From 90520b4b3854293a793e4dcef628fee18d26b7ea Mon Sep 17 00:00:00 2001 From: Andrew Tibbetts Date: Wed, 5 Jan 2022 07:32:19 -0600 Subject: [PATCH 01/17] prettier formatted --- css/looks.css | 2604 +++++++++-------- index.html | 34 +- js/main.js | 7602 ++++++++++++++++++++++++++----------------------- 3 files changed, 5420 insertions(+), 4820 deletions(-) diff --git a/css/looks.css b/css/looks.css index 4211a6a..55165e1 100644 --- a/css/looks.css +++ b/css/looks.css @@ -1,116 +1,116 @@ @-moz-keyframes spin { - from { - -moz-transform: rotate(0deg); - } + from { + -moz-transform: rotate(0deg); + } - to { - -moz-transform: rotate(359deg); - } + to { + -moz-transform: rotate(359deg); + } } @-webkit-keyframes spin { - from { - -webkit-transform: rotate(0deg); - } + from { + -webkit-transform: rotate(0deg); + } - to { - -webkit-transform: rotate(359deg); - } + to { + -webkit-transform: rotate(359deg); + } } @keyframes spin { - from { - transform: rotate(0deg); - } + from { + transform: rotate(0deg); + } - to { - transform: rotate(359deg); - } + to { + transform: rotate(359deg); + } } @keyframes MoveUpDown { - 50% { - -webkit-transform: translateY(5px); - transform: translateY(5px); - will-change: transform; - } + 50% { + -webkit-transform: translateY(5px); + transform: translateY(5px); + will-change: transform; + } } @keyframes MoveSideSide { - 5% { - transform: rotate(0deg) translateY(0.1rem); - } + 5% { + transform: rotate(0deg) translateY(0.1rem); + } - 25% { - transform: rotate(-3deg) translateY(0.1rem); - } + 25% { + transform: rotate(-3deg) translateY(0.1rem); + } - 45% { - transform: rotate(0deg) translateY(0.1rem); - } + 45% { + transform: rotate(0deg) translateY(0.1rem); + } - 55% { - transform: rotate(0deg) translateY(0.1rem); - } + 55% { + transform: rotate(0deg) translateY(0.1rem); + } - 75% { - transform: rotate(3deg) translateY(0.1rem); - } + 75% { + transform: rotate(3deg) translateY(0.1rem); + } - 95% { - transform: rotate(0deg) translateY(0.1rem); - } + 95% { + transform: rotate(0deg) translateY(0.1rem); + } } @keyframes pulse { - from { - opacity: 1; - } + from { + opacity: 1; + } - to { - opacity: 0.5; - } + to { + opacity: 0.5; + } } :root { - --orange: #F4810B; - --orangelite: #F6993C; - --orange66: #F4810B66; - --orange33: #F4810B33; - --pad1: 0.25rem; - --pad2: 0.5rem; - --pad3: 1rem; - --pad4: 1.5rem; - --pad5: 4rem; - --pad6: 8rem; + --orange: #f4810b; + --orangelite: #f6993c; + --orange66: #f4810b66; + --orange33: #f4810b33; + --pad1: 0.25rem; + --pad2: 0.5rem; + --pad3: 1rem; + --pad4: 1.5rem; + --pad5: 4rem; + --pad6: 8rem; } *, *:before, *:after { - -webkit-box-sizing: border-box; - -moz-box-sizing: border-box; - -ms-box-sizing: border-box; - box-sizing: border-box; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + -ms-box-sizing: border-box; + box-sizing: border-box; } html { - font-size: 14px; + font-size: 14px; } html, body { - height: 100%; + height: 100%; } body { - display: flex; - flex-direction: column; - margin: 0; - font-size: 1rem; - color: #bbbbbb; - background-color: #000; - overflow: hidden; + display: flex; + flex-direction: column; + margin: 0; + font-size: 1rem; + color: #bbbbbb; + background-color: #000; + overflow: hidden; } body, @@ -118,338 +118,338 @@ input, select, textarea, button { - font-family: "Open Sans", helvetica, arial, sans-serif; + font-family: "Open Sans", helvetica, arial, sans-serif; } h2 { - font-size: 1.2rem; - font-weight: 700; - color: white; + font-size: 1.2rem; + font-weight: 700; + color: white; } a { - color: var(--orange); - text-decoration: none; + color: var(--orange); + text-decoration: none; } [role="button"] { - cursor: pointer; + cursor: pointer; } :focus { - outline: none; - box-shadow: 0 0 0.5rem var(--orange); + outline: none; + box-shadow: 0 0 0.5rem var(--orange); } input:not([type="checkbox"]):not([type="radio"]), select, button { - height: 2rem; - padding: 0.25rem 0.5rem; - font-size: 0.9rem; - font-family: "Open Sans", helvetica, arial, sans-serif; - color: #eee; - background-color: rgba(123, 123, 123, 0.15); - border: 1px solid transparent; - border-bottom-color: rgba(0, 0, 0, 0.85); - border-top-color: rgba(255, 255, 255, 0.15); - border-radius: 0.25rem; - box-shadow: 0 0.25rem 0.5rem -0.25rem black; + height: 2rem; + padding: 0.25rem 0.5rem; + font-size: 0.9rem; + font-family: "Open Sans", helvetica, arial, sans-serif; + color: #eee; + background-color: rgba(123, 123, 123, 0.15); + border: 1px solid transparent; + border-bottom-color: rgba(0, 0, 0, 0.85); + border-top-color: rgba(255, 255, 255, 0.15); + border-radius: 0.25rem; + box-shadow: 0 0.25rem 0.5rem -0.25rem black; } input:not([type="checkbox"]):not([type="radio"]) { - border-top-color: rgba(0, 0, 0, 0.85); - border-bottom-color: rgba(255, 255, 255, 0.15); - transform: translateY(-1px); - box-shadow: none; + border-top-color: rgba(0, 0, 0, 0.85); + border-bottom-color: rgba(255, 255, 255, 0.15); + transform: translateY(-1px); + box-shadow: none; } input[type="checkbox"], input[type="radio"] { - margin-right: var(--pad1); + margin-right: var(--pad1); } -input[type="checkbox"]+label, -input[type="radio"]+label { - display: inline-block; - margin-right: var(--pad3); +input[type="checkbox"] + label, +input[type="radio"] + label { + display: inline-block; + margin-right: var(--pad3); } option { - background-color: #333; + background-color: #333; } input:focus, select:focus, textarea:focus, button:focus { - outline: none; + outline: none; } code { - display: inline-block; - padding: 0.1em 0.2em; - font: 12px Monaco, Consolas, "Andale Mono", "DejaVu Sans Mono", monospace; - color: #bbb; - background-color: rgba(123, 123, 123, 0.5); + display: inline-block; + padding: 0.1em 0.2em; + font: 12px Monaco, Consolas, "Andale Mono", "DejaVu Sans Mono", monospace; + color: #bbb; + background-color: rgba(123, 123, 123, 0.5); } .simplebar-scrollbar:before { - background: var(--orange); + background: var(--orange); } .previewicon { - display: flex; - align-items: center; - justify-content: center; - width: 1.5rem; - height: 1.5rem; - text-align: center; - font-size: 1rem; - color: white; - background-color: black; - border-radius: 999px; - opacity: 0.66; + display: flex; + align-items: center; + justify-content: center; + width: 1.5rem; + height: 1.5rem; + text-align: center; + font-size: 1rem; + color: white; + background-color: black; + border-radius: 999px; + opacity: 0.66; } .track-warning { - color: var(--orange); - cursor: pointer; + color: var(--orange); + cursor: pointer; } .track-warning:empty { - display: none; + display: none; } -.utitle, .prsnJoined { - color: rgba(255, 255, 255, 0.4); - font-size: 0.66rem; - font-weight: 700; - text-transform: uppercase; - letter-spacing: 0.1em; +.utitle, +.prsnJoined { + color: rgba(255, 255, 255, 0.4); + font-size: 0.66rem; + font-weight: 700; + text-transform: uppercase; + letter-spacing: 0.1em; } .herecon { - color: #43b581; - background-color: #282828; - height: calc(0.75rem + 4px); - width: calc(0.75rem + 4px); - padding: 2px; - font-size: 0.75em; - display: block; - position: absolute; - bottom: 0; - border-radius: 999px; - right: -0.25rem; + color: #43b581; + background-color: #282828; + height: calc(0.75rem + 4px); + width: calc(0.75rem + 4px); + padding: 2px; + font-size: 0.75em; + display: block; + position: absolute; + bottom: 0; + border-radius: 999px; + right: -0.25rem; } .herecon.idle { - color: #faa61a; + color: #faa61a; } .notice { - margin: 0; - background-color: #222; - overflow: hidden; - height: 100%; - padding: var(--pad4); + margin: 0; + background-color: #222; + overflow: hidden; + height: 100%; + padding: var(--pad4); } .notice#notice { - display: block; + display: block; } .notice p { - margin-bottom: 15px; - margin-top: 15px; + margin-bottom: 15px; + margin-top: 15px; } .flexpacer { - flex: 1; + flex: 1; } .tabs { - display: flex; - background-color: black; + display: flex; + background-color: black; } .tab { - flex: 1; - margin: 5px 0 0 0; - padding: 0.5em; - font-size: 0.85rem; - color: #999; - text-align: center; - background-color: #222; - border: 0; - border-top: 1px solid rgba(255, 255, 255, 0.1); - border-radius: 0; - box-shadow: 0 -0.2rem 0.5rem -0.1rem black, inset 0 -0.2rem 0.4rem -0.2rem black; - clip-path: - polygon(0% 0%, - 0% 0%, - calc(100% - var(--pad3)) 0%, - 100% var(--pad3), - 100% 100%, - 100% 100%, - 0% 100%, - 0% 0%); + flex: 1; + margin: 5px 0 0 0; + padding: 0.5em; + font-size: 0.85rem; + color: #999; + text-align: center; + background-color: #222; + border: 0; + border-top: 1px solid rgba(255, 255, 255, 0.1); + border-radius: 0; + box-shadow: 0 -0.2rem 0.5rem -0.1rem black, inset 0 -0.2rem 0.4rem -0.2rem black; + clip-path: polygon( + 0% 0%, + 0% 0%, + calc(100% - var(--pad3)) 0%, + 100% var(--pad3), + 100% 100%, + 100% 100%, + 0% 100%, + 0% 0% + ); } .tab.on { - color: #ffffff; - background-color: #282828; - box-shadow: 0 -0.2rem 0.5rem -0.2rem black; + color: #ffffff; + background-color: #282828; + box-shadow: 0 -0.2rem 0.5rem -0.2rem black; } .tab:not(:first-child) { - margin-left: 5px; - ; + margin-left: 5px; } span.removemeIcon.material-icons { - font-size: 1rem; - vertical-align: middle; + font-size: 1rem; + vertical-align: middle; } .butt { - display: flex; - justify-content: center; - align-items: center; - margin: 0; - color: white; - font-size: 0.75rem; - font-weight: 700; - text-transform: uppercase; - letter-spacing: 0.1em; - text-decoration: none; - background-color: var(--orange); - border-top: 1px solid rgba(255, 255, 255, 0.3); - border-bottom: 1px solid black; - transition: all 100ms ease-in-out; - user-select: none; + display: flex; + justify-content: center; + align-items: center; + margin: 0; + color: white; + font-size: 0.75rem; + font-weight: 700; + text-transform: uppercase; + letter-spacing: 0.1em; + text-decoration: none; + background-color: var(--orange); + border-top: 1px solid rgba(255, 255, 255, 0.3); + border-bottom: 1px solid black; + transition: all 100ms ease-in-out; + user-select: none; } .butt:hover { - background-color: var(--orangelite); + background-color: var(--orangelite); } .graybutt { - color: #888; - background: rgba(255, 255, 255, 0.066); + color: #888; + background: rgba(255, 255, 255, 0.066); } .graybutt:hover { - background: #444; + background: #444; } .graybutt:focus { - background: rgba(255, 255, 255, 0.1); + background: rgba(255, 255, 255, 0.1); } .redbutt { - background: #c43; + background: #c43; } .redbutt:hover { - background: #b54; + background: #b54; } .iconbutt { - width: 2rem; - padding: 0; - font-size: 0.5rem; + width: 2rem; + padding: 0; + font-size: 0.5rem; } .iconbutt.on { - color: var(--orange); - background: rgba(255, 255, 255, 0.033); - border-top: 1px solid black; - border-bottom: 1px solid var(--orange66); - box-shadow: inset 0 0 1rem var(--orange33); + color: var(--orange); + background: rgba(255, 255, 255, 0.033); + border-top: 1px solid black; + border-bottom: 1px solid var(--orange66); + box-shadow: inset 0 0 1rem var(--orange33); } .iconbutt .material-icons { - font-size: 1.43rem; + font-size: 1.43rem; } .scrollWrap { - height: 100%; - overflow: auto; + height: 100%; + overflow: auto; } .pvbarWrap { - display: flex; + display: flex; } .working .material-icons { - animation: spin 3s infinite linear; + animation: spin 3s infinite linear; } .ps { - position: relative; + position: relative; } .ps__rail-x { - display: none !important; + display: none !important; } .ps__rail-y { - z-index: 2; + z-index: 2; } .ps .ps__rail-y:focus, .ps .ps__rail-y:hover, .ps .ps__rail-y.ps--clicking { - background-color: transparent; - outline: none; + background-color: transparent; + outline: none; } .ps__thumb-y, -.ps__rail-y:hover>.ps__thumb-y, -.ps__rail-y:focus>.ps__thumb-y, +.ps__rail-y:hover > .ps__thumb-y, +.ps__rail-y:focus > .ps__thumb-y, .ps__rail-y.ps--clicking .ps__thumb-y { - width: 0.5rem; - background-color: rgba(123, 123, 123, 0.5); - border-radius: 0.2rem; + width: 0.5rem; + background-color: rgba(123, 123, 123, 0.5); + border-radius: 0.2rem; } /* Top Bar / Site Header */ #topbar { - display: flex; - align-items: center; - padding: var(--pad1) var(--pad3); - color: #fff; - background-color: black; + display: flex; + align-items: center; + padding: var(--pad1) var(--pad3); + color: #fff; + background-color: black; } .ftlogo { - margin-right: 0.5rem; - color: var(--orange); - font-size: 1.25rem; - font-weight: 700; + margin-right: 0.5rem; + color: var(--orange); + font-size: 1.25rem; + font-weight: 700; } #idtitle { - display: none; - font-size: 1rem; - color: #fff; - font-weight: 700; - margin-right:1rem; + display: none; + font-size: 1rem; + color: #fff; + font-weight: 700; + margin-right: 1rem; } -#socialthings{ +#socialthings { } -a.sociallogo{ - text-decoration:none; - display:none; - margin: 0.2rem 0.2rem 0 0.2rem; - +a.sociallogo { + text-decoration: none; + display: none; + margin: 0.2rem 0.2rem 0 0.2rem; } -.sociallogo svg:hover{ - fill: #fff; +.sociallogo svg:hover { + fill: #fff; } .sociallogo svg { width: 1.2rem; @@ -462,109 +462,104 @@ div#sociallthings { } div#roomlogo { - background-image: url(../img/idlogo2.png); - background-size: contain; - width: 3rem; - height: 3rem; - margin-right: 1.5rem; - background-repeat: no-repeat; - background-position: center; + background-image: url(../img/idlogo2.png); + background-size: contain; + width: 3rem; + height: 3rem; + margin-right: 1.5rem; + background-repeat: no-repeat; + background-position: center; } #loggedInName { - margin: 0 var(--pad2); - cursor: pointer; - text-decoration: none; + margin: 0 var(--pad2); + cursor: pointer; + text-decoration: none; } #loggedInName:hover { - text-decoration: underline; + text-decoration: underline; } .header_icon { - margin: 0.2rem 0.2rem 0 0.2rem; - padding: 0; - font-size: 0; - line-height: 0; - background: none; - border: 0; + margin: 0.2rem 0.2rem 0 0.2rem; + padding: 0; + font-size: 0; + line-height: 0; + background: none; + border: 0; } .header_icon .material-icons { - font-size: 1.2rem; - color: #888; + font-size: 1.2rem; + color: #888; } /*------------------------------------ MAIN BODY THINGS */ #mainGrid { - grid-area: main; - flex: 1; - display: grid; - grid-template-rows: auto auto auto 1fr; - grid-template-columns: 1fr; - grid-template-areas: - "stage" - "main" - "main" - "main" - ; - overflow: hidden; + grid-area: main; + flex: 1; + display: grid; + grid-template-rows: auto auto auto 1fr; + grid-template-columns: 1fr; + grid-template-areas: + "stage" + "main" + "main" + "main"; + overflow: hidden; } #mainGrid.mmusrs { - grid-template-areas: - "stage" - "theme" - "mmopts" - "users" - ; + grid-template-areas: + "stage" + "theme" + "mmopts" + "users"; } #mainGrid.mmusrs #queuebox, #mainGrid.mmusrs #thehistoryWrap, #mainGrid.mmusrs #actualChat, #mainGrid.mmusrs #login { - display: none; + display: none; } #mainGrid.mmqueue { - grid-template-areas: - "stage" - "theme" - "mmopts" - "queues" - ; + grid-template-areas: + "stage" + "theme" + "mmopts" + "queues"; } #mainGrid.mmqueue #usersbox, #mainGrid.mmqueue #thehistoryWrap, #mainGrid.mmqueue #actualChat, #mainGrid.mmqueue #login { - display: none; + display: none; } #mainGrid.mmchat { - grid-template-areas: - "stage" - "theme" - "mmopts" - "chat" - ; + grid-template-areas: + "stage" + "theme" + "mmopts" + "chat"; } #mainGrid.mmchat #usersbox, #mainGrid.mmchat #queuebox, #mainGrid.mmchat #thehistoryWrap, #mainGrid.mmchat #login { - display: none; + display: none; } #mainGrid.login { - grid-template-rows: auto 1fr; - grid-template-areas: - "stage" - "login" - ; + grid-template-rows: auto 1fr; + grid-template-areas: + "stage" + "login"; } #mainGrid.login #usersbox, @@ -575,1444 +570,1455 @@ div#roomlogo { #mainGrid.login #actualChat, #mainGrid.login #voteActions, #mainGrid.login #minimodeoptions { - display: none; + display: none; } /* MiniMode Nav */ #minimodeoptions { - grid-area: mmopts; + grid-area: mmopts; } #minimodeoptions .tab { - background-color: #1b1b1b; + background-color: #1b1b1b; } #minimodeoptions .tab.on { - background-color: #222; + background-color: #222; } /* Users Lists */ #usersbox { - grid-area: users; - display: flex; - flex-direction: column; - overflow: hidden; + grid-area: users; + display: flex; + flex-direction: column; + overflow: hidden; } #userslist { - flex: 1; - background-color: #282828; - overflow: hidden; + flex: 1; + background-color: #282828; + overflow: hidden; } #usertabs { - padding-top: 0.5rem; - background-color: #222; + padding-top: 0.5rem; + background-color: #222; } .usersWrap { - overflow: auto; + overflow: auto; } #allusers, #justwaitlist { - padding-top: 1rem; + padding-top: 1rem; } #allusersWrap, #justwaitWrap { - height: 100%; + height: 100%; } .prson { - display: flex; - align-items: center; - min-width: 0; - padding: var(--pad1) var(--pad4); + display: flex; + align-items: center; + min-width: 0; + padding: var(--pad1) var(--pad4); } #allusersWrap .prson { - cursor: pointer; - -webkit-touch-callout: none; - -webkit-user-select: none; - -khtml-user-select: none; - -moz-user-select: none; - -ms-user-select: none; - user-select: none; + cursor: pointer; + -webkit-touch-callout: none; + -webkit-user-select: none; + -khtml-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; } .prsnName { - flex: 1; - font-size: 0.9rem; - font-weight: 700; - line-height: 1.5; - white-space: nowrap; - overflow-x: hidden; - text-overflow: ellipsis; + flex: 1; + font-size: 0.9rem; + font-weight: 700; + line-height: 1.5; + white-space: nowrap; + overflow-x: hidden; + text-overflow: ellipsis; } #userslist .utitle { - margin-left: auto; + margin-left: auto; } #userslist .prson .prsnJoined { - display: none; + display: none; } #userslist .prson:hover .prsnJoined { - display: block; + display: block; } #userslist .prson:hover .utitle { - display: none; + display: none; } -#userslist .prson.blockd .prsnName, #userslist .prson.blockd .prsnJoined, #userslist .prson.blockd .utitle { - opacity: 0.5; +#userslist .prson.blockd .prsnName, +#userslist .prson.blockd .prsnJoined, +#userslist .prson.blockd .utitle { + opacity: 0.5; } .botson { - background-color: #000; - background-size: auto 100%; - border-radius: 999px; - background-position: center 55%; - background-repeat: no-repeat; - border-bottom: 1px solid #888; + background-color: #000; + background-size: auto 100%; + border-radius: 999px; + background-position: center 55%; + background-repeat: no-repeat; + border-bottom: 1px solid #888; } #userslist .botson { - margin-right: 0.75rem; - width: 2rem; - height: 2rem; - position: relative; + margin-right: 0.75rem; + width: 2rem; + height: 2rem; + position: relative; } span.block { - width: 2rem; - height: 2rem; - font-size: 2rem; - background-color: rgba(0, 0, 0, 0.50); - color: var(--orange); - border-radius: 999px; + width: 2rem; + height: 2rem; + font-size: 2rem; + background-color: rgba(0, 0, 0, 0.5); + color: var(--orange); + border-radius: 999px; } span.block:empty { - display: none; + display: none; } /* The Stage */ #stage { - grid-area: stage; - display: flex; - flex-direction: column; - position: relative; - overflow: hidden; + grid-area: stage; + display: flex; + flex-direction: column; + position: relative; + overflow: hidden; } #stage::before { - content: ""; - position: absolute; - z-index: 1; - top: 0; - right: 0; - bottom: 0; - left: 0; - background: linear-gradient(rgba(0, 0, 0, 0.75), rgba(0, 0, 0, 0.55)); + content: ""; + position: absolute; + z-index: 1; + top: 0; + right: 0; + bottom: 0; + left: 0; + background: linear-gradient(rgba(0, 0, 0, 0.75), rgba(0, 0, 0, 0.55)); } -#stage>div:not(#screenBox) { - position: relative; - z-index: 2; +#stage > div:not(#screenBox) { + position: relative; + z-index: 2; } #djStage { - display: flex; - flex-direction: column; - justify-content: space-between; + display: flex; + flex-direction: column; + justify-content: space-between; } #nowplaying { - position: relative; - display: grid; - grid-template-columns: auto 1fr auto; - grid-template-rows: auto auto 1fr; - grid-template-areas: - "art track timr" - "art artist artist" - "art plays plays" - ; - margin-bottom: auto; - padding: var(--pad4); + position: relative; + display: grid; + grid-template-columns: auto 1fr auto; + grid-template-rows: auto auto 1fr; + grid-template-areas: + "art track timr" + "art artist artist" + "art plays plays"; + margin-bottom: auto; + padding: var(--pad4); } #nowplaying::before { - content: ''; - position: absolute; - top: 3rem; - left: 3rem; - width: 15rem; - height: 5rem; - background-color: rgba(0, 0, 0, 0.25); - box-shadow: 0 0 10rem 10rem rgba(0, 0, 0, 0.25); + content: ""; + position: absolute; + top: 3rem; + left: 3rem; + width: 15rem; + height: 5rem; + background-color: rgba(0, 0, 0, 0.25); + box-shadow: 0 0 10rem 10rem rgba(0, 0, 0, 0.25); } #albumArt { - grid-area: art; - margin: var(--pad1) var(--pad3) 0 var(--pad1); - width: 4rem; - height: 4rem; - background-size: cover; - background-position: center center; + grid-area: art; + margin: var(--pad1) var(--pad3) 0 var(--pad1); + width: 4rem; + height: 4rem; + background-size: cover; + background-position: center center; } #track { - grid-area: track; - font-size: 1.25rem; - color: white; + grid-area: track; + font-size: 1.25rem; + color: white; } #timr { - grid-area: timr; - padding-top: 0.15em; - font-size: 0.85rem; - font-weight: 700; - letter-spacing: 0.05em; - text-align: right; + grid-area: timr; + padding-top: 0.15em; + font-size: 0.85rem; + font-weight: 700; + letter-spacing: 0.05em; + text-align: right; } #artist { - grid-area: artist; - margin-bottom: var(--pad2); - font-size: 1rem; - font-weight: 400; - overflow: hidden; - color: white; - white-space: nowrap; - text-overflow: ellipsis; + grid-area: artist; + margin-bottom: var(--pad2); + font-size: 1rem; + font-weight: 400; + overflow: hidden; + color: white; + white-space: nowrap; + text-overflow: ellipsis; } #source, #plays { - font-size: 0.75rem; - font-weight: 700; - color: rgba(255, 255, 255, 0.8); + font-size: 0.75rem; + font-weight: 700; + color: rgba(255, 255, 255, 0.8); } #plays { - grid-area: plays; + grid-area: plays; } #playCount, #firstPlay { - margin-right: var(--pad2); + margin-right: var(--pad2); } #playCount:empty, #firstPlay:empty, #lastPlay:empty { - display: none; + display: none; } #deck { - grid-area: deck; - display: flex; - max-width: 100vw; + grid-area: deck; + display: flex; + max-width: 100vw; } #deck.dance .avtr:not(.animate) { - transform-origin: bottom; - animation: MoveSideSide 2s linear infinite; + transform-origin: bottom; + animation: MoveSideSide 2s linear infinite; } .spot { - flex: 1; - position: relative; - display: flex; - flex-direction: column; - justify-content: flex-end; - margin-right: var(--pad2); - margin-left: var(--pad2); - min-width: 0; - height: 1.5rem; + flex: 1; + position: relative; + display: flex; + flex-direction: column; + justify-content: flex-end; + margin-right: var(--pad2); + margin-left: var(--pad2); + min-width: 0; + height: 1.5rem; } .spot.empty .djplaque { - flex-direction: column; - color: rgba(255, 2556, 255, 0.5); + flex-direction: column; + color: rgba(255, 2556, 255, 0.5); } .avtr { - display: none; - position: absolute; - z-index: 0; - bottom: 1.4rem; - width: 100%; - height: 8rem; - background-size: contain; - background-repeat: no-repeat; - background-position: center bottom; + display: none; + position: absolute; + z-index: 0; + bottom: 1.4rem; + width: 100%; + height: 8rem; + background-size: contain; + background-repeat: no-repeat; + background-position: center bottom; } .avtr.animate { - animation: MoveUpDown 1s linear infinite; + animation: MoveUpDown 1s linear infinite; } .djplaque { - position: relative; - z-index: 1; - display: flex; - justify-content: space-between; - min-width: 0; - padding: 0 var(--pad3); - font-size: 0.75rem; - line-height: 1.5rem; - text-align: center; - background-color: #151515; - border-top-left-radius: 0.5rem; - border-top-right-radius: 0.5rem; + position: relative; + z-index: 1; + display: flex; + justify-content: space-between; + min-width: 0; + padding: 0 var(--pad3); + font-size: 0.75rem; + line-height: 1.5rem; + text-align: center; + background-color: #151515; + border-top-left-radius: 0.5rem; + border-top-right-radius: 0.5rem; } .djname { - text-overflow: ellipsis; - overflow: hidden; - white-space: nowrap; - padding-right: 5px; + text-overflow: ellipsis; + overflow: hidden; + white-space: nowrap; + padding-right: 5px; } .playcount { - white-space: nowrap; + white-space: nowrap; } #prgbar { - width: 100%; - height: 0.5rem; - border-bottom: 1px solid #333; - margin-bottom: 0; + width: 100%; + height: 0.5rem; + border-bottom: 1px solid #333; + margin-bottom: 0; } #volandthings, #queueControls { - display: flex; - align-items: center; - justify-content: space-between; - padding: 0.4rem 0.5rem; - white-space: nowrap; + display: flex; + align-items: center; + justify-content: space-between; + padding: 0.4rem 0.5rem; + white-space: nowrap; } #volandthings { - position: relative; - background-color: #151515; - border-bottom: 1px solid #333; + position: relative; + background-color: #151515; + border-bottom: 1px solid #333; } #songthings, #voteActions, #songActions, #playerControls { - display: flex; - white-space: nowrap; + display: flex; + white-space: nowrap; } #songthings, #playerControls { - flex: 1; + flex: 1; } #songthings .iconbutt { - margin-right: 0.25rem; + margin-right: 0.25rem; } #voteActions { - margin-right: 1rem; + margin-right: 1rem; } #playerControls { - margin-left: 1rem; + margin-left: 1rem; } #volplace { - flex: 1; - display: flex; - align-items: center; - padding-left: 0.25rem; - padding-right: 0.25rem; - white-space: nowrap; + flex: 1; + display: flex; + align-items: center; + padding-left: 0.25rem; + padding-right: 0.25rem; + white-space: nowrap; } #volstatus { - cursor: pointer; + cursor: pointer; } #volplace .material-icons { - font-size: 16px !important; + font-size: 16px !important; } #shareinfo { - text-align: right; + text-align: right; } #stealContain { - display: none; - position: fixed; - z-index: 59; - top: 50%; - left: 50%; - box-shadow: 0 0.25rem 1rem -0.25rem black; + display: none; + position: fixed; + z-index: 59; + top: 50%; + left: 50%; + box-shadow: 0 0.25rem 1rem -0.25rem black; } #stealBox { - width: 16rem; - padding: var(--pad3); - background-color: #333; - border-radius: 5px; + width: 16rem; + padding: var(--pad3); + background-color: #333; + border-radius: 5px; } #stealArrow { - margin-top: -0.5rem; - margin-left: 1.15rem; - width: 0; - height: 0; - border-left: 1rem solid transparent; - border-right: 1rem solid transparent; - border-bottom: 1rem solid #333; + margin-top: -0.5rem; + margin-left: 1.15rem; + width: 0; + height: 0; + border-left: 1rem solid transparent; + border-right: 1rem solid transparent; + border-bottom: 1rem solid #333; } select#stealpicker { - width: 100%; - font-size: 1rem; - border: none; - font-family: "Open Sans", helvetica, arial, sans-serif; - background-color: #222; - padding-left: var(--pad1); - color: #eee; - white-space: nowrap; + width: 100%; + font-size: 1rem; + border: none; + font-family: "Open Sans", helvetica, arial, sans-serif; + background-color: #222; + padding-left: var(--pad1); + color: #eee; + white-space: nowrap; } #addToQueueBttn { - margin-left: 1em; - color: white; - background-color: var(--orange); - border-right: 1px solid black; - border-left: 1px solid black; - border-radius: 5px; + margin-left: 1em; + color: white; + background-color: var(--orange); + border-right: 1px solid black; + border-left: 1px solid black; + border-radius: 5px; } #slider { - margin-right: var(--pad2); - width: 100%; - max-width: 10rem; + margin-right: var(--pad2); + width: 100%; + max-width: 10rem; } .ui-slider { - position: relative; - text-align: left; + position: relative; + text-align: left; } .ui-slider .ui-slider-handle { - position: absolute; - z-index: 2; - width: 1.25rem; - height: 1.25rem; - cursor: grab; - background-color: #bbb; - border: 0.2rem solid #666; - border-radius: 999px; - -ms-touch-action: none; - touch-action: none; + position: absolute; + z-index: 2; + width: 1.25rem; + height: 1.25rem; + cursor: grab; + background-color: #bbb; + border: 0.2rem solid #666; + border-radius: 999px; + -ms-touch-action: none; + touch-action: none; } .ui-slider .ui-slider-range { - position: absolute; - z-index: 1; - font-size: .7em; - display: block; - border: 0; - background-position: 0 0; + position: absolute; + z-index: 1; + font-size: 0.7em; + display: block; + border: 0; + background-position: 0 0; } .ui-slider-horizontal .ui-slider-range-min { - left: 0; - background-color: var(--orange); + left: 0; + background-color: var(--orange); } .ui-slider-horizontal { - height: 3px; + height: 3px; } .ui-slider-horizontal .ui-slider-handle { - top: -0.6rem; - margin-left: -.6em; + top: -0.6rem; + margin-left: -0.6em; } .ui-slider-horizontal .ui-slider-range { - top: 0; - height: 100%; + top: 0; + height: 100%; } .ui-slider-horizontal .ui-slider-range-min { - left: 0; + left: 0; } .ui-slider-horizontal .ui-slider-range-max { - right: 0; + right: 0; } -.ui-state-default, .ui-widget-content .ui-state-default, .ui-widget-header .ui-state-default, .ui-button, html .ui-button.ui-state-disabled:hover, html .ui-button.ui-state-disabled:active { - font-weight: normal; +.ui-state-default, +.ui-widget-content .ui-state-default, +.ui-widget-header .ui-state-default, +.ui-button, +html .ui-button.ui-state-disabled:hover, +html .ui-button.ui-state-disabled:active { + font-weight: normal; } .ui-widget.ui-widget-content { - border: none; - background-color: rgba(204, 204, 204, 0.43137254901960786); + border: none; + background-color: rgba(204, 204, 204, 0.43137254901960786); } .ui-widget-content { - border: 1px solid #dddddd; - background: #ffffff; - color: #333333; + border: 1px solid #dddddd; + background: #ffffff; + color: #333333; } #screenBox { - position: absolute; - top: -100%; - width: 100%; - height: 100%; - z-index: 0; - transition: top 2s ease-in-out; - pointer-events: none; + position: absolute; + top: -100%; + width: 100%; + height: 100%; + z-index: 0; + transition: top 2s ease-in-out; + pointer-events: none; } #scScreen, #playerArea { - position: absolute; - top: 0; - left: 0; - height: 100%; - width: 100% + position: absolute; + top: 0; + left: 0; + height: 100%; + width: 100%; } #screenover { - position: absolute; - top: 0; - width: 100%; - height: 100%; - background: linear-gradient(rgba(0, 0, 0, 0.5), rgba(255, 255, 255, 0.1)); + position: absolute; + top: 0; + width: 100%; + height: 100%; + background: linear-gradient(rgba(0, 0, 0, 0.5), rgba(255, 255, 255, 0.1)); } /* Queues / Playlists */ #queuebox { - grid-area: queues; - display: flex; - flex-direction: column; - position: relative; - overflow: hidden; + grid-area: queues; + display: flex; + flex-direction: column; + position: relative; + overflow: hidden; } input#queueFilter { - width: 100%; + width: 100%; } div#filterMachine { - padding: var(--pad2); + padding: var(--pad2); } #mainqueue .material-icons { - margin: 0 var(--pad1); - font-size: 1.2rem; + margin: 0 var(--pad1); + font-size: 1.2rem; } #queuelist .pvbar { - padding: var(--pad2) var(--pad2); - background-color: #222; - border-bottom: 1px solid black; - cursor: move; - /* fallback if grab cursor is unsupported */ - cursor: grab; - cursor: -moz-grab; - cursor: -webkit-grab; + padding: var(--pad2) var(--pad2); + background-color: #222; + border-bottom: 1px solid black; + cursor: move; + /* fallback if grab cursor is unsupported */ + cursor: grab; + cursor: -moz-grab; + cursor: -webkit-grab; } #queuelist .pvbarWrap { - align-items: center; + align-items: center; } #queuelist .pvbar.editing .edittags { - display: none; + display: none; } #queuelist .pvbar.editing .closeeditor { - display: block; + display: block; } #queuelist .listwords { - flex: 1; - margin-left: var(--pad2); + flex: 1; + margin-left: var(--pad2); } #listpickerWrap { - flex: 1; + flex: 1; } select#listpicker { - width: 100%; - background-color: #333; - padding-left: var(--pad2); + width: 100%; + background-color: #333; + padding-left: var(--pad2); } #queueControls, #filterMachine { - background-color: #222; + background-color: #222; } #qControlButtons { - display: flex; + display: flex; } button#cancelqsearch { - display: none; - margin-left: var(--pad2); + display: none; + margin-left: var(--pad2); } #addbox, #plmanager { - display: none; - flex-direction: column; - height: 100%; - background-color: #222; - overflow: hidden; + display: none; + flex-direction: column; + height: 100%; + background-color: #222; + overflow: hidden; } #plmanager { - padding: var(--pad2); + padding: var(--pad2); } .ortxt { - padding: var(--pad1); - text-align: center; + padding: var(--pad1); + text-align: center; } input#qsearch { - margin: var(--pad2); + margin: var(--pad2); } #queuelist { - flex: 1; - height: 100%; - background-color: #181818; - overflow: auto; + flex: 1; + height: 100%; + background-color: #181818; + overflow: auto; } #searchResults { - flex: 1; - background-color: #282828; + flex: 1; + background-color: #282828; } #mergeContain { - display: none; - position: absolute; - z-index: 15; - width: calc(100% - 30px); - margin: 0.5rem 15px 0 15px; - box-shadow: 0 0.25rem 0.5rem -0.125rem black; + display: none; + position: absolute; + z-index: 15; + width: calc(100% - 30px); + margin: 0.5rem 15px 0 15px; + box-shadow: 0 0.25rem 0.5rem -0.125rem black; } #mergeArrow { - position: absolute; - bottom: 100%; - right: 1.85rem; - width: 0; - height: 0; - border-left: 1rem solid transparent; - border-right: 1rem solid transparent; - border-bottom: 1rem solid #333; + position: absolute; + bottom: 100%; + right: 1.85rem; + width: 0; + height: 0; + border-left: 1rem solid transparent; + border-right: 1rem solid transparent; + border-bottom: 1rem solid #333; } #mergeBox { - background-color: #333; - padding: 15px; - border-radius: 5px; + background-color: #333; + padding: 15px; + border-radius: 5px; } #mergeSetup { - display: flex; - align-items: center; - justify-content: space-between; + display: flex; + align-items: center; + justify-content: space-between; } #mergeHappening { - display: none; + display: none; } -select#mergepicker, #mergepicker2 { - width: calc(50% - 50px); - font-size: 14px; - border: none; - font-family: "Open Sans", helvetica, arial, sans-serif; - background-color: #222; - padding-left: 5px; - color: #eee; - height: 24px; - white-space: nowrap; +select#mergepicker, +#mergepicker2 { + width: calc(50% - 50px); + font-size: 14px; + border: none; + font-family: "Open Sans", helvetica, arial, sans-serif; + background-color: #222; + padding-left: 5px; + color: #eee; + height: 24px; + white-space: nowrap; } #mergeBox i.material-icons { - font-size: 17px !important; - font-weight: 700; - cursor: default; - vertical-align: middle; + font-size: 17px !important; + font-weight: 700; + cursor: default; + vertical-align: middle; } #mergeCompleted { - display: none; + display: none; } .importResult { - display: flex; - align-items: center; - padding: var(--pad1) 0; - border-bottom: 1px solid #111; + display: flex; + align-items: center; + padding: var(--pad1) 0; + border-bottom: 1px solid #111; } .importResult .material-icons { - margin-left: var(--pad2); - font-size: 1rem; + margin-left: var(--pad2); + font-size: 1rem; } a.importLinkCheck { - color: #eee; - line-height: 0; + color: #eee; + line-height: 0; } .imtxt { - flex: 1; - padding-right: var(--pad2); + flex: 1; + padding-right: var(--pad2); } .tagPromptBox { - padding: var(--pad3) 0 var(--pad1) var(--pad2); - font-size: .875rem; + padding: var(--pad3) 0 var(--pad1) var(--pad2); + font-size: 0.875rem; } .tagPromptBox .closebutt { - float: right; + float: right; } .closeeditor { - display: none; + display: none; } .tagsNlink { - display: flex; + display: flex; } .tagsNlink input.tagMachine { - flex: 1; - margin: 0; + flex: 1; + margin: 0; } .tagSongLink { - padding: var(--pad1) var(--pad1) var(--pad1) var(--pad2); + padding: var(--pad1) var(--pad1) var(--pad1) var(--pad2); } #mainqueue .material-icons.tracklink { - font-size: 1.5rem; + font-size: 1.5rem; } /* Theme */ #themebox { - grid-area: theme; - z-index: 15; - padding: var(--pad2); - font-size: 1rem; - text-align: center; - white-space: nowrap; - text-overflow: ellipsis; - background-color: #333; - border-bottom: 1px solid #111; - overflow: hidden; + grid-area: theme; + z-index: 15; + padding: var(--pad2); + font-size: 1rem; + text-align: center; + white-space: nowrap; + text-overflow: ellipsis; + background-color: #333; + border-bottom: 1px solid #111; + overflow: hidden; } #currentTheme { - text-overflow: ellipsis; - white-space: nowrap; - overflow: hidden; - width: 100%; + text-overflow: ellipsis; + white-space: nowrap; + overflow: hidden; + width: 100%; } /* Chat */ #actualChat { - grid-area: chat; - display: flex; - flex-direction: column; - overflow: hidden; - background-color: #181818; + grid-area: chat; + display: flex; + flex-direction: column; + overflow: hidden; + background-color: #181818; } #chatsWrap { - flex: 1; - height: 100%; - overflow: auto; + flex: 1; + height: 100%; + overflow: auto; } #newchat { - width: 100%; + width: 100%; } #newchatForm { - flex: 1; - margin-right: var(--pad1); + flex: 1; + margin-right: var(--pad1); } .newChat { - position: relative; - display: flex; - justify-content: space-between; - margin: var(--pad3); - color: #ddd; - word-break: break-word; - background-color: #181818; + position: relative; + display: flex; + justify-content: space-between; + margin: var(--pad3); + color: #ddd; + word-break: break-word; + background-color: #181818; } .newChat .botson { - position: relative; - z-index: 2; - margin: 0.15rem var(--pad3) 0 0; - width: 2rem; - height: 2rem; - cursor: pointer; + position: relative; + z-index: 2; + margin: 0.15rem var(--pad3) 0 0; + width: 2rem; + height: 2rem; + cursor: pointer; } #actualChat.avatarsOff .botson { - display: none; + display: none; } .nowplayn { - position: relative; - background-color: transparent; + position: relative; + background-color: transparent; } .newChat.nowplayn, -.nowplayn+.newChat { - border: 0; +.nowplayn + .newChat { + border: 0; } -.npmsg, .lcrsp { - position: relative; - z-index: 2; - width: 100%; - padding: var(--pad1); - color: #888; - font-size: 0.75rem; - text-align: center; - background-color: #282828; - border-radius: var(--pad1); +.npmsg, +.lcrsp { + position: relative; + z-index: 2; + width: 100%; + padding: var(--pad1); + color: #888; + font-size: 0.75rem; + text-align: center; + background-color: #282828; + border-radius: var(--pad1); } .chatContent { - flex: 1; - padding: 0 var(--pad4) 0 0; + flex: 1; + padding: 0 var(--pad4) 0 0; } .chatContent .utitle { - line-height: 1.6; - color: rgba(255, 255, 255, 0.25); + line-height: 1.6; + color: rgba(255, 255, 255, 0.25); } .chatHead { - display: flex; + display: flex; } .chatName { - margin-right: var(--pad2); - font-size: 0.75rem; - font-weight: 700; - color: rgba(255, 255, 255, 0.4); - cursor: pointer; + margin-right: var(--pad2); + font-size: 0.75rem; + font-weight: 700; + color: rgba(255, 255, 255, 0.4); + cursor: pointer; } .modDelete { - position: absolute; - right: 0; - top: 0; - font-size: 1rem; - padding-left: 0.25rem; - font-weight: 400; - padding-right: 0.25rem; - background-color: #333; - cursor: pointer; - display: none; - color: #999; + position: absolute; + right: 0; + top: 0; + font-size: 1rem; + padding-left: 0.25rem; + font-weight: 400; + padding-right: 0.25rem; + background-color: #333; + cursor: pointer; + display: none; + color: #999; } .modDelete:hover { - background-color: #dd2e44; - color: #ddd; + background-color: #dd2e44; + color: #ddd; } .chatText { - position: relative; + position: relative; } .chatText.deleteMe:hover { - background-color: #282828; + background-color: #282828; } .chatText.deleteMe:hover .modDelete { - display: block; + display: block; } .chatText a { - position: relative; - display: block; + position: relative; + display: block; } .chatText .inlineImage { - display: block; - max-width: 100%; + display: block; + max-width: 100%; } .chatText .hideImage { - position: absolute; - z-index: 2; - top: 0.5rem; - right: 0.5rem; - display: flex; - justify-content: center; - align-items: center; - width: 1.5rem; - height: 1.5rem; - font-size: 1.25rem; - color: white; - background-color: rgba(255, 0, 0, 0.5); - border-radius: 999px; + position: absolute; + z-index: 2; + top: 0.5rem; + right: 0.5rem; + display: flex; + justify-content: center; + align-items: center; + width: 1.5rem; + height: 1.5rem; + font-size: 1.25rem; + color: white; + background-color: rgba(255, 0, 0, 0.5); + border-radius: 999px; } .chatText.hideImg .inlineImage { - position: relative; - height: 2.5rem; - opacity: 0; - visibility: hidden; + position: relative; + height: 2.5rem; + opacity: 0; + visibility: hidden; } .chatText.hideImg a.inlineImgLink::after { - content: 'image hidden'; - position: absolute; - z-index: 1; - top: 0; - right: 0; - bottom: 0; - left: 0; - display: flex; - justify-content: center; - align-items: center; - font-size: 0.66rem; - text-transform: uppercase; - letter-spacing: 0.1em; - ; - color: rgba(255, 255, 255, 0.5); - background-color: black; + content: "image hidden"; + position: absolute; + z-index: 1; + top: 0; + right: 0; + bottom: 0; + left: 0; + display: flex; + justify-content: center; + align-items: center; + font-size: 0.66rem; + text-transform: uppercase; + letter-spacing: 0.1em; + color: rgba(255, 255, 255, 0.5); + background-color: black; } .chatText.hideImg .hideImage { - background-color: #444; - transform: rotate(45deg); + background-color: #444; + transform: rotate(45deg); } .badoop::before { - content: ""; - display: flex; - align-items: flex-start; - justify-content: center; - position: absolute; - z-index: 1; - top: calc(var(--pad3) * -1); - left: calc(var(--pad3) * -1); - bottom: calc(var(--pad3) * -1); - width: 0; - padding: 0.2rem; - font-size: 1.2rem; - font-weight: 700; - line-height: 3.2; - color: #777; - background-color: #444; - border-top-right-radius: 999px; - border-bottom-right-radius: 999px; + content: ""; + display: flex; + align-items: flex-start; + justify-content: center; + position: absolute; + z-index: 1; + top: calc(var(--pad3) * -1); + left: calc(var(--pad3) * -1); + bottom: calc(var(--pad3) * -1); + width: 0; + padding: 0.2rem; + font-size: 1.2rem; + font-weight: 700; + line-height: 3.2; + color: #777; + background-color: #444; + border-top-right-radius: 999px; + border-bottom-right-radius: 999px; } .chatTime { - position: relative; - z-index: 2; - font-weight: 400; - font-size: 0.66rem; - color: rgba(255, 255, 255, 0.47); - white-space: nowrap; + position: relative; + z-index: 2; + font-weight: 400; + font-size: 0.66rem; + color: rgba(255, 255, 255, 0.47); + white-space: nowrap; } .chatCard { - display: block; - margin: var(--pad2) 0; + display: block; + margin: var(--pad2) 0; } #chatbottom { - position: relative; - display: flex; - align-items: center; - padding: var(--pad1); - background-color: #282828; + position: relative; + display: flex; + align-items: center; + padding: var(--pad1); + background-color: #282828; } #pickerResults { - overflow-y: auto; - height: 33vh; - overflow-x: hidden; - text-align: center; + overflow-y: auto; + height: 33vh; + overflow-x: hidden; + text-align: center; } .pickerSecSelected { - color: #fff; - border-bottom: 2px solid #fff; + color: #fff; + border-bottom: 2px solid #fff; } #pickerResults span { - margin-right: var(--pad2); - display: inline-block; - margin-bottom: var(--pad2); + margin-right: var(--pad2); + display: inline-block; + margin-bottom: var(--pad2); } #pickerResults h3 { - margin-bottom: var(--pad3); - margin-top: var(--pad2); - text-align: center; + margin-bottom: var(--pad3); + margin-top: var(--pad2); + text-align: center; } #emojiPicker { - position: absolute; - z-index: 99; - left: 0; - bottom: 100%; - width: 100%; - padding: var(--pad3); - background-color: #2d2d2d; - box-shadow: 0 -0.25rem 0.5rem black; + position: absolute; + z-index: 99; + left: 0; + bottom: 100%; + width: 100%; + padding: var(--pad3); + background-color: #2d2d2d; + box-shadow: 0 -0.25rem 0.5rem black; } #pickerNav { - overflow: hidden; - white-space: nowrap; - text-align: center; + overflow: hidden; + white-space: nowrap; + text-align: center; } input#pickerSearch { - width: 100%; - margin: var(--pad2) 0; + width: 100%; + margin: var(--pad2) 0; } #pickerNav span { - margin-right: var(--pad2); - cursor: pointer; - filter: saturate(0); - opacity: .6; + margin-right: var(--pad2); + cursor: pointer; + filter: saturate(0); + opacity: 0.6; } #pickerNav .on { - color: var(--orange); - filter: none; - opacity: 1; + color: var(--orange); + filter: none; + opacity: 1; } img.emoji { - height: 1.25em; - width: 1.25em; - margin: 0 .05em 0 .1em; - vertical-align: -0.1em; + height: 1.25em; + width: 1.25em; + margin: 0 0.05em 0 0.1em; + vertical-align: -0.1em; } .rohnmoji { - height: 1.25em; - width: 1.25em; - margin: 0 .05em 0 .1em; - vertical-align: -0.1em; - background-image: url(../img/rohn.png); - display: inline-block; - background-size: cover; + height: 1.25em; + width: 1.25em; + margin: 0 0.05em 0 0.1em; + vertical-align: -0.1em; + background-image: url(../img/rohn.png); + display: inline-block; + background-size: cover; } #morechats { - position: absolute; - bottom: 100%; - display: none; - justify-content: center; - width: 100%; - pointer-events: none; + position: absolute; + bottom: 100%; + display: none; + justify-content: center; + width: 100%; + pointer-events: none; } #morechats.show { - display: flex; + display: flex; } #morechats .butt { - height: auto; - padding: 0.2em; - font-size: 0.7rem; - background-color: #222; - border-bottom-color: #222; - border-bottom-left-radius: 0; - border-bottom-right-radius: 0; - box-shadow: none; - pointer-events: auto; + height: auto; + padding: 0.2em; + font-size: 0.7rem; + background-color: #222; + border-bottom-color: #222; + border-bottom-left-radius: 0; + border-bottom-right-radius: 0; + box-shadow: none; + pointer-events: auto; } #morechats .butt i { - margin: 0 0.25em; - font-size: 0.85rem; - font-weight: bold; + margin: 0 0.25em; + font-size: 0.85rem; + font-weight: bold; } #atPicker { - display: none; - position: absolute; - z-index: 2; - bottom: 100%; - left: 1.5rem; - padding: 0.25rem; - background: #222; - box-shadow: 0 -0.25rem 0.5rem black; + display: none; + position: absolute; + z-index: 2; + bottom: 100%; + left: 1.5rem; + padding: 0.25rem; + background: #222; + box-shadow: 0 -0.25rem 0.5rem black; } #atPicker.show { - display: block; + display: block; } #atPicker .butt { - margin: 0.25rem; - height: auto; - padding: 0.25em 0.5em; - text-transform: none; - letter-spacing: 0; + margin: 0.25rem; + height: auto; + padding: 0.25em 0.5em; + text-transform: none; + letter-spacing: 0; } #atPicker i { - margin: 0.25rem; - padding: 0.25em 0.5em; - font-size: 0.75rem; - color: #888; + margin: 0.25rem; + padding: 0.25em 0.5em; + font-size: 0.75rem; + color: #888; } /* History */ #thehistoryWrap { - position: absolute; - z-index: 15; - bottom: 0; - left: 0; - width: 100%; - height: 100%; - background: #222; - overflow: auto; + position: absolute; + z-index: 15; + bottom: 0; + left: 0; + width: 100%; + height: 100%; + background: #222; + overflow: auto; } #thehistory .pvbar { - padding: var(--pad2) var(--pad2) var(--pad2) var(--pad2); - border-bottom: 1px solid black; + padding: var(--pad2) var(--pad2) var(--pad2) var(--pad2); + border-bottom: 1px solid black; } .histmoreinfo { - font-size: 0.8rem; - color: #888; + font-size: 0.8rem; + color: #888; } .histart { - display: flex; - align-items: center; - justify-content: center; - margin-right: var(--pad2); - height: 2.5rem; - width: 2.5rem; - background-size: cover; - background-position: center; + display: flex; + align-items: center; + justify-content: center; + margin-right: var(--pad2); + height: 2.5rem; + width: 2.5rem; + background-size: cover; + background-position: center; } .histlink { - color: #bbb; + color: #bbb; } .qtxt { - flex: 1; + flex: 1; } /* Login */ #welcomeInfo { - padding-bottom: var(--pad4); + padding-bottom: var(--pad4); } #login { - grid-area: login; - padding: var(--pad5); - background: #151515; - overflow: auto; - height: 100%; + grid-area: login; + padding: var(--pad5); + background: #151515; + overflow: auto; + height: 100%; } #login h4 { - font-size: 1.25rem; - font-weight: 300; + font-size: 1.25rem; + font-weight: 300; } #login h4 a { - font-size: 1rem; + font-size: 1rem; } #login .butt { - width: auto; + width: auto; } .inputline { - display: flex; - flex-direction: column; - margin-top: 1rem; - max-width: 20rem; + display: flex; + flex-direction: column; + margin-top: 1rem; + max-width: 20rem; } -.inputline>* { - margin: var(--pad1) 0; +.inputline > * { + margin: var(--pad1) 0; } .formlinks { - margin-top: var(--pad3); + margin-top: var(--pad3); } #login .formlinks a { - margin-right: 15px; - cursor: pointer; + margin-right: 15px; + cursor: pointer; } #login .formlinks a:last-child { - margin-right: 0; + margin-right: 0; } #login .formlinks .selected { - display: none; + display: none; } /* Modals */ #overlay { - position: fixed; - top: 0; - left: 0; - z-index: 100; - display: flex; - align-items: center; - justify-content: center; - width: 100%; - height: 100%; - background: rgba(0, 0, 0, 0.85); - overflow: hidden; + position: fixed; + top: 0; + left: 0; + z-index: 100; + display: flex; + align-items: center; + justify-content: center; + width: 100%; + height: 100%; + background: rgba(0, 0, 0, 0.85); + overflow: hidden; } #overlay.show { - display: flex; + display: flex; } .modalThing { - display: none; - flex-direction: column; - max-width: 80vw; - max-height: 80vh; - background-color: #111; - overflow: hidden; + display: none; + flex-direction: column; + max-width: 80vw; + max-height: 80vh; + background-color: #111; + overflow: hidden; } .modalThing.show { - display: flex; + display: flex; } .modalHeader { - display: flex; - align-items: center; - justify-content: space-between; - padding: var(--pad4); - color: #eee; - background-color: #222; + display: flex; + align-items: center; + justify-content: space-between; + padding: var(--pad4); + color: #eee; + background-color: #222; } .closeModal { - margin-left: var(--pad3); + margin-left: var(--pad3); } .modalContent { - flex: 1; - display: flex; - padding: var(--pad4); - overflow: auto; + flex: 1; + display: flex; + padding: var(--pad4); + overflow: auto; } -#settingsBox .modalContent, #accountSettingsBox .modalContent { - min-width: 320px; +#settingsBox .modalContent, +#accountSettingsBox .modalContent { + min-width: 320px; } #cardsBox .modalContent { - width: 80vw; - height: 80vh; + width: 80vw; + height: 80vh; } .settingline { - margin-bottom: var(--pad2); + margin-bottom: var(--pad2); } #cardsMain { - display: grid; - grid-gap: 10px; - grid-template-columns: repeat(auto-fill, minmax(225px, 1fr)); + display: grid; + grid-gap: 10px; + grid-template-columns: repeat(auto-fill, minmax(225px, 1fr)); } .caseCard { - margin: var(--pad2); + margin: var(--pad2); } .caseCardSpot { - position: relative; + position: relative; } .caseCardSpot canvas { - border: 1px solid #333; + border: 1px solid #333; } span.cardShareChat { - position: absolute; - right: 85px; - bottom: 160px; - font-size: 14px; - background-color: #000; - padding: 5px; - border-radius: 5px; - display: none; + position: absolute; + right: 85px; + bottom: 160px; + font-size: 14px; + background-color: #000; + padding: 5px; + border-radius: 5px; + display: none; } span.cardGiftChat { - position: absolute; - right: 100px; - bottom: 200px; - font-size: 14px; - background-color: #000; - padding: 5px; - border-radius: 5px; - display: none; + position: absolute; + right: 100px; + bottom: 200px; + font-size: 14px; + background-color: #000; + padding: 5px; + border-radius: 5px; + display: none; } span.cardGiftChat:hover, span.cardShareChat:hover { - background-color: var(--orange); - color: #000; + background-color: var(--orange); + color: #000; } .caseCardSpot:hover .cardShareChat, .caseCardSpot:hover .cardGiftChat { - display: block; + display: block; } #importDubContent { - display: none; + display: none; } -input.tagMachine, #supercopSearch, #changeUsername { - width: 100%; - padding: 5px; - margin-bottom: 15px; +input.tagMachine, +#supercopSearch, +#changeUsername { + width: 100%; + padding: 5px; + margin-bottom: 15px; } #songlink { - border-radius: 0.25rem; + border-radius: 0.25rem; } #songlink svg { - width: 100%; - height: 100%; - padding: 4px; + width: 100%; + height: 100%; + padding: 4px; } #importSources { - padding: 0; - background: none; + padding: 0; + background: none; } -#importContent, #importDubContent { - padding: var(--pad2) var(--pad3); - background-color: #282828; +#importContent, +#importDubContent { + padding: var(--pad2) var(--pad3); + background-color: #282828; } #dubimportButton { - margin-top: 10px; - display: none; + margin-top: 10px; + display: none; } #byId { - display: flex; - align-items: center; + display: flex; + align-items: center; } #byId .butt:disabled { - opacity: 0.4; + opacity: 0.4; } input#plMachine, input#plMachineById { - line-height: 30px; - width: 100%; - padding: 5px; + line-height: 30px; + width: 100%; + padding: 5px; } input#plMachineById { - padding-right: 40px; + padding-right: 40px; } .responseBox { - background-color: var(--orange); - margin: 10px; - padding: 5px; - color: #000; - border-radius: 0.25rem; + background-color: var(--orange); + margin: 10px; + padding: 5px; + color: #000; + border-radius: 0.25rem; } .responseBox:empty { - display: none; + display: none; } #mergeLists, #shuffleQueue { - margin-left: 1em; + margin-left: 1em; } /* Unauthenticated state */ #reconnecting { - display: none; - padding: var(--pad1) var(--pad2); - background-color: #bb4433; - animation: pulse 500ms infinite linear alternate; + display: none; + padding: var(--pad1) var(--pad2); + background-color: #bb4433; + animation: pulse 500ms infinite linear alternate; } body.disconnected #queuebox, body.disconnected #newchat { - opacity: 0.5; - pointer-events: none; - user-select: none; + opacity: 0.5; + pointer-events: none; + user-select: none; } body.disconnected #reconnecting { - display: block; + display: block; } body.disconnected #logOutButton { - display: none; + display: none; } /* About page */ body.blog { - overflow: auto; + overflow: auto; } div#blog { - flex: 1; - padding: var(--pad5); - background-color: #282828; + flex: 1; + padding: var(--pad5); + background-color: #282828; } /* Initially hidden stuff */ @@ -2023,268 +2029,260 @@ div#blog { .notice, #emojiPicker, #overlay { - display: none; + display: none; } /*------------------------------------ Media Queries -*/ @media only screen and (max-width: 799px) { - #allusersWrap .prson { - cursor: none; - pointer-events: none; - } + #allusersWrap .prson { + cursor: none; + pointer-events: none; + } } @media only screen and (min-width: 800px) { - body.screen #albumArt { - display: none; - } - - body.screen #screenBox { - top: 0; - } - - #mainGrid { - gap: 0 5px; - } - - #importPromptBox .modalContent { - width: 480px; - } - - #mainGrid, - #mainGrid.mmchat { - grid-template-rows: auto auto auto 1fr; - grid-template-columns: 60vw 40vw; - grid-template-areas: - "stage theme" - "stage chat" - "mmopts chat" - "users chat" - ; - } - - #mainGrid.mmusrs { - grid-template-areas: - "stage theme" - "stage chat" - "mmopts chat" - "users chat" - ; - } - - #mainGrid.mmqueue { - grid-template-areas: - "stage theme" - "stage chat" - "mmopts chat" - "queues chat" - ; - } - - #mainGrid.mmchat #usersbox, - #mainGrid.mmqueue #actualChat, - #mainGrid.mmusrs #actualChat { - display: flex; - } - - #mainGrid.login { - grid-template-areas: - "stage queues" - "login queues" - ; - } - - #mainGrid.login #queuebox, - #mainGrid.login #thehistoryWrap { - display: block; - } - - #mainGrid.login .histeal, - #mmchat { - display: none; - } - - #idtitle { - display: block; - } - - #thehistoryWrap { - grid-area: queues; - display: block; - position: relative; - top: auto !important; - bottom: auto; - } - - #mainGrid.mmqueue #thehistoryWrap { - grid-area: mmopts / queues; - } + body.screen #albumArt { + display: none; + } + + body.screen #screenBox { + top: 0; + } + + #mainGrid { + gap: 0 5px; + } + + #importPromptBox .modalContent { + width: 480px; + } + + #mainGrid, + #mainGrid.mmchat { + grid-template-rows: auto auto auto 1fr; + grid-template-columns: 60vw 40vw; + grid-template-areas: + "stage theme" + "stage chat" + "mmopts chat" + "users chat"; + } + + #mainGrid.mmusrs { + grid-template-areas: + "stage theme" + "stage chat" + "mmopts chat" + "users chat"; + } + + #mainGrid.mmqueue { + grid-template-areas: + "stage theme" + "stage chat" + "mmopts chat" + "queues chat"; + } + + #mainGrid.mmchat #usersbox, + #mainGrid.mmqueue #actualChat, + #mainGrid.mmusrs #actualChat { + display: flex; + } + + #mainGrid.login { + grid-template-areas: + "stage queues" + "login queues"; + } + + #mainGrid.login #queuebox, + #mainGrid.login #thehistoryWrap { + display: block; + } + + #mainGrid.login .histeal, + #mmchat { + display: none; + } + + #idtitle { + display: block; + } + + #thehistoryWrap { + grid-area: queues; + display: block; + position: relative; + top: auto !important; + bottom: auto; + } + + #mainGrid.mmqueue #thehistoryWrap { + grid-area: mmopts / queues; + } } @media only screen and (min-width: 1200px) { - - #mainGrid, - #mainGrid.mmqueue, - #mainGrid.mmchat, - #mainGrid.mmusrs { - grid-template-rows: auto auto 1fr; - grid-template-columns: minmax(16rem, 20rem) minmax(40vw, 100vw) minmax(24rem, 36rem); - grid-template-areas: - "users stage theme" - "users stage chat" - "users queues chat" - ; - } - - #mainGrid.mmusrs #queuebox, - #mainGrid.mmusrs #actualChat, - #mainGrid.mmchat #usersbox, - #mainGrid.mmchat #queuebox, - #mainGrid.mmqueue #usersbox, - #mainGrid.mmqueue #actualChat { - display: flex; - } - - #mainGrid.login { - grid-template-areas: - "stage stage queues" - "login login queues" - ; - } - - #mainGrid #minimodeoptions { - display: none; - } - - #usertabs { - padding-top: 0; - background-color: black; - } - - #djStage { - height: 30vh; - min-height: 15rem; - } - - .avtr { - display: block; - } - - #albumArt { - height: 6.5rem; - width: 6.5rem; - } - - #deck { - margin-left: 5vw !important; - margin-right: 5vw !important; - } - - .djplaque { - justify-content: space-between; - } - - #thehistoryWrap { - grid-area: queues !important; - } + #mainGrid, + #mainGrid.mmqueue, + #mainGrid.mmchat, + #mainGrid.mmusrs { + grid-template-rows: auto auto 1fr; + grid-template-columns: minmax(16rem, 20rem) minmax(40vw, 100vw) minmax(24rem, 36rem); + grid-template-areas: + "users stage theme" + "users stage chat" + "users queues chat"; + } + + #mainGrid.mmusrs #queuebox, + #mainGrid.mmusrs #actualChat, + #mainGrid.mmchat #usersbox, + #mainGrid.mmchat #queuebox, + #mainGrid.mmqueue #usersbox, + #mainGrid.mmqueue #actualChat { + display: flex; + } + + #mainGrid.login { + grid-template-areas: + "stage stage queues" + "login login queues"; + } + + #mainGrid #minimodeoptions { + display: none; + } + + #usertabs { + padding-top: 0; + background-color: black; + } + + #djStage { + height: 30vh; + min-height: 15rem; + } + + .avtr { + display: block; + } + + #albumArt { + height: 6.5rem; + width: 6.5rem; + } + + #deck { + margin-left: 5vw !important; + margin-right: 5vw !important; + } + + .djplaque { + justify-content: space-between; + } + + #thehistoryWrap { + grid-area: queues !important; + } } @media only screen and (min-width: 1680px) { - - #mainGrid, - #mainGrid.mmqueue, - #mainGrid.mmchat, - #mainGrid.mmusrs { - grid-template-columns: minmax(16rem, 20rem) minmax(50vw, 100vw) minmax(24rem, 36rem); - } - - #mainGrid.login { - grid-template-areas: - "stage stage queues" - "login login queues" - ; - } - - #mainGrid.login #queuebox { - display: none; - } - - #mainGrid.login #thehistoryWrap { - width: 100%; - } - - #history { - position: absolute; - left: 60%; - bottom: -1px; - width: auto; - padding: 0 var(--pad2); - border-left: var(--pad1) solid #151515; - border-bottom-color: #222; - border-radius: 0; - clip-path: - polygon(0% 0%, - 0% 0%, - calc(100% - var(--pad2)) 0%, - 100% var(--pad2), - 100% 100%, - 100% 100%, - 0% 100%, - 0% 0%); - pointer-events: none; - } - - #history::after { - content: "Recent Plays"; - margin-left: var(--pad1); - font-size: 0.85rem; - font-weight: 400; - text-transform: none; - letter-spacing: 0; - } - - #queuebox { - width: 60%; - } - - #thehistoryWrap { - display: block !important; - grid-area: queues; - justify-self: flex-end; - z-index: auto; - width: 40%; - border-left: var(--pad1) solid #151515; - } + #mainGrid, + #mainGrid.mmqueue, + #mainGrid.mmchat, + #mainGrid.mmusrs { + grid-template-columns: minmax(16rem, 20rem) minmax(50vw, 100vw) minmax(24rem, 36rem); + } + + #mainGrid.login { + grid-template-areas: + "stage stage queues" + "login login queues"; + } + + #mainGrid.login #queuebox { + display: none; + } + + #mainGrid.login #thehistoryWrap { + width: 100%; + } + + #history { + position: absolute; + left: 60%; + bottom: -1px; + width: auto; + padding: 0 var(--pad2); + border-left: var(--pad1) solid #151515; + border-bottom-color: #222; + border-radius: 0; + clip-path: polygon( + 0% 0%, + 0% 0%, + calc(100% - var(--pad2)) 0%, + 100% var(--pad2), + 100% 100%, + 100% 100%, + 0% 100%, + 0% 0% + ); + pointer-events: none; + } + + #history::after { + content: "Recent Plays"; + margin-left: var(--pad1); + font-size: 0.85rem; + font-weight: 400; + text-transform: none; + letter-spacing: 0; + } + + #queuebox { + width: 60%; + } + + #thehistoryWrap { + display: block !important; + grid-area: queues; + justify-self: flex-end; + z-index: auto; + width: 40%; + border-left: var(--pad1) solid #151515; + } } @media only screen and (min-width: 2100px) { - html { - font-size: calc(0.4em + 0.4vw); - } - - #mainGrid, - #mainGrid.mmqueue, - #mainGrid.mmchat, - #mainGrid.mmusrs { - grid-template-columns: minmax(24rem, 36rem) minmax(40vw, 100vw) minmax(24rem, 36rem); - } - - #usersbox .tab { - color: #ffffff; - background-color: #282828; - box-shadow: 0 -0.2rem 0.5rem -0.2rem black; - } - - #userslist { - display: flex; - } - - .usersWrap { - display: block !important; - flex: 1; - } - - #allusersWrap { - border-right: 5px solid black; - } + html { + font-size: calc(0.4em + 0.4vw); + } + + #mainGrid, + #mainGrid.mmqueue, + #mainGrid.mmchat, + #mainGrid.mmusrs { + grid-template-columns: minmax(24rem, 36rem) minmax(40vw, 100vw) minmax(24rem, 36rem); + } + + #usersbox .tab { + color: #ffffff; + background-color: #282828; + box-shadow: 0 -0.2rem 0.5rem -0.2rem black; + } + + #userslist { + display: flex; + } + + .usersWrap { + display: block !important; + flex: 1; + } + + #allusersWrap { + border-right: 5px solid black; + } } diff --git a/index.html b/index.html index d5a1374..fd60513 100644 --- a/index.html +++ b/index.html @@ -19,30 +19,36 @@
-
diff --git a/js/main.js b/js/main.js index 2c85fb0..d950c69 100644 --- a/js/main.js +++ b/js/main.js @@ -1,60 +1,61 @@ var firetable = { - started: false, - loggedIn: false, - uid: null, - uname: null, - pvCount: 0, - playdex: 0, - users: {}, - queue: false, - preview: false, - movePvBar: null, - moveBar: null, - song: null, - playBadoop: true, - idle: false, - idleChanged: null, - sbhowImages: false, - screenControl: "sync", - lights: false, - screenSyncPos: false, - scSeek: false, - desktopNotifyMentions: false, - orange: "#F4810B", - color: "#F4810B", - countcolor: "#fff", - ytLoaded: null, - scLoaded: null, - listShowing: null, - parser: null, - songToEdit: null, - scwidget: null, - searchSelectsChoice: 1, - importSelectsChoice: 1, - dtImportName: null, - dtImportList: [], - lastChatPerson: false, - lastChatId: false, - tagUpdate: null, - nonpmsg: true, - playlimit: 2, - scImg: "", - superCopBanUpdates: null, - loginForm: null, - emojiMap: null, - pickerInit: false, - atLand: false, - atUsers: [], - atUsersFiltered: [], - atString: "", - debug: false -} + started: false, + loggedIn: false, + uid: null, + uname: null, + pvCount: 0, + playdex: 0, + users: {}, + queue: false, + preview: false, + movePvBar: null, + moveBar: null, + song: null, + playBadoop: true, + idle: false, + idleChanged: null, + sbhowImages: false, + screenControl: "sync", + lights: false, + screenSyncPos: false, + scSeek: false, + desktopNotifyMentions: false, + orange: "#F4810B", + color: "#F4810B", + countcolor: "#fff", + ytLoaded: null, + scLoaded: null, + listShowing: null, + parser: null, + songToEdit: null, + scwidget: null, + searchSelectsChoice: 1, + importSelectsChoice: 1, + dtImportName: null, + dtImportList: [], + lastChatPerson: false, + lastChatId: false, + tagUpdate: null, + nonpmsg: true, + playlimit: 2, + scImg: "", + superCopBanUpdates: null, + loginForm: null, + emojiMap: null, + pickerInit: false, + atLand: false, + atUsers: [], + atUsersFiltered: [], + atString: "", + debug: false, +}; -if (typeof ftconfigs == "undefined") throw "config.js is missing! Copy config.js.example and rename to config.js. Edit this file and add your own app's information."; +if (typeof ftconfigs == "undefined") + throw "config.js is missing! Copy config.js.example and rename to config.js. Edit this file and add your own app's information."; -var chatScroll = new SimpleBar(document.getElementById('chatsWrap')); -chatScroll.getScrollElement().addEventListener('scroll', function() { - if (firetable.utilities.isChatPrettyMuchAtBottom()) $('#morechats').removeClass('show'); +var chatScroll = new SimpleBar(document.getElementById("chatsWrap")); +chatScroll.getScrollElement().addEventListener("scroll", function () { + if (firetable.utilities.isChatPrettyMuchAtBottom()) $("#morechats").removeClass("show"); }); firetable.version = "01.09.04"; @@ -62,113 +63,111 @@ firetable.version = "01.09.04"; var player, $playlistItemTemplate; var idlejs = new IdleJs({ - idle: 5 * 60000, - events: ['mousemove', 'keydown', 'mousedown', 'touchstart'], - onIdle: function() { - ftapi.actions.changeIdleStatus(true, 1); - }, - onActive: function() { - ftapi.actions.changeIdleStatus(false, 1); - }, - onHide: function() { - ftapi.actions.changeIdleStatus(true, 1); - firetable.debug && console.log("hide"); - }, - onShow: function() { - ftapi.actions.changeIdleStatus(false, 1); - }, - keepTracking: true, - startAtIdle: false + idle: 5 * 60000, + events: ["mousemove", "keydown", "mousedown", "touchstart"], + onIdle: function () { + ftapi.actions.changeIdleStatus(true, 1); + }, + onActive: function () { + ftapi.actions.changeIdleStatus(false, 1); + }, + onHide: function () { + ftapi.actions.changeIdleStatus(true, 1); + firetable.debug && console.log("hide"); + }, + onShow: function () { + ftapi.actions.changeIdleStatus(false, 1); + }, + keepTracking: true, + startAtIdle: false, }); idlejs.start(); function onYouTubeIframeAPIReady() { - player = new YT.Player('playerArea', { - width: $('#djStage').outerHeight() * 1.7777, - height: $('#djStage').outerHeight(), - playerVars: { - 'autoplay': 1, - 'controls': 0 - }, - videoId: '0', - events: { - onReady: initialize, - onStateChange: function() { - $('#reloadtrack').removeClass('on working'); - } - } - }); + player = new YT.Player("playerArea", { + width: $("#djStage").outerHeight() * 1.7777, + height: $("#djStage").outerHeight(), + playerVars: { + autoplay: 1, + controls: 0, + }, + videoId: "0", + events: { + onReady: initialize, + onStateChange: function () { + $("#reloadtrack").removeClass("on working"); + }, + }, + }); } function initialize(event) { - firetable.ytLoaded = true; - var vol = localStorage["firetableVol"]; - if (typeof vol == "undefined") { - vol = 80; - localStorage["firetableVol"] = 80; - } - player.setVolume(vol); - - var muted = localStorage["firetableMute"]; - if (typeof muted == "undefined") { - localStorage["firetableMute"] = false; - muted = "false"; - $("#volstatus").removeClass('on'); - } - - if (muted != "false") { - var icon = ""; - $("#volstatus i").html(icon); - $("#volstatus").addClass('on'); - } - - $("#slider").slider({ - orientation: "horizontal", - range: "min", - min: 0, - max: 100, - value: vol, - step: 5, - slide: function(event, ui) { - player.setVolume(ui.value); - firetable.scwidget.setVolume(ui.value); - localStorage["firetableVol"] = ui.value; - var muted = localStorage["firetableMute"]; - if (muted != "false") { + firetable.ytLoaded = true; + var vol = localStorage["firetableVol"]; + if (typeof vol == "undefined") { + vol = 80; + localStorage["firetableVol"] = 80; + } + player.setVolume(vol); + + var muted = localStorage["firetableMute"]; + if (typeof muted == "undefined") { localStorage["firetableMute"] = false; - var icon = ""; - $("#volstatus i").html(icon); - $("#volstatus").removeClass('on'); - } else if (ui.value == 0) { - firetable.actions.muteToggle(true); - $("#volstatus").addClass('on'); - } + muted = "false"; + $("#volstatus").removeClass("on"); } - }); - if (firetable.song) { - var data = firetable.song; - var nownow = Date.now(); - var timeSince = nownow - data.started; - if (timeSince <= 0) timeSince = 0; - - var secSince = Math.floor(timeSince / 1000); - var timeLeft = data.duration - secSince; - if (data.type == 1) { - if (!firetable.preview) { - if (!firetable.disableMediaPlayback) player.loadVideoById(data.cid, secSince, "large") - } + + if (muted != "false") { + var icon = ""; + $("#volstatus i").html(icon); + $("#volstatus").addClass("on"); } - } + $("#slider").slider({ + orientation: "horizontal", + range: "min", + min: 0, + max: 100, + value: vol, + step: 5, + slide: function (event, ui) { + player.setVolume(ui.value); + firetable.scwidget.setVolume(ui.value); + localStorage["firetableVol"] = ui.value; + var muted = localStorage["firetableMute"]; + if (muted != "false") { + localStorage["firetableMute"] = false; + var icon = ""; + $("#volstatus i").html(icon); + $("#volstatus").removeClass("on"); + } else if (ui.value == 0) { + firetable.actions.muteToggle(true); + $("#volstatus").addClass("on"); + } + }, + }); + if (firetable.song) { + var data = firetable.song; + var nownow = Date.now(); + var timeSince = nownow - data.started; + if (timeSince <= 0) timeSince = 0; + var secSince = Math.floor(timeSince / 1000); + var timeLeft = data.duration - secSince; + if (data.type == 1) { + if (!firetable.preview) { + if (!firetable.disableMediaPlayback) player.loadVideoById(data.cid, secSince, "large"); + } + } + } } function onPlayerStateChange(event) { - //state changed thanks + //state changed thanks } -firetable.init = function() { - console.log(` +firetable.init = function () { + console.log(` ( ) ) ( )\\ ) ( ( ( ( /( ) ( /( )\\ ( (()/( )\\ )( ))\\ )\\()) ( /( )\\()) (_)) ))\\ @@ -177,3172 +176,3655 @@ firetable.init = function() { | _| | | | '_| / -_) | _| / _' | | '_ \\ | | / -_) |_| |_| |_| \\___| \\__| \\__,_| |_.__/ |_| \\___| `); - firetable.started = true; - - $("#idtitle").text(ftconfigs.roomName); - $("#welcomeName").text(ftconfigs.roomName); - -if (ftconfigs.facebookURL){ - $(".sociallogo.facebook").attr("href", ftconfigs.facebookURL); - $(".sociallogo.facebook").css("display", "inline-block"); -} - -if (ftconfigs.redditURL){ - $(".sociallogo.reddit").attr("href", ftconfigs.redditURL); - $(".sociallogo.reddit").css("display", "inline-block"); -} - -if (ftconfigs.lastfmURL){ - $(".sociallogo.lastfm").attr("href", ftconfigs.lastfmURL); - $(".sociallogo.lastfm").css("display", "inline-block"); -} - -if (ftconfigs.discordURL){ - $(".sociallogo.discord").attr("href", ftconfigs.discordURL); - $(".sociallogo.discord").css("display", "inline-block"); -} + firetable.started = true; -if (ftconfigs.soundcloudURL){ - $(".sociallogo.soundcloud").attr("href", ftconfigs.soundcloudURL); - $(".sociallogo.soundcloud").css("display", "inline-block"); -} + $("#idtitle").text(ftconfigs.roomName); + $("#welcomeName").text(ftconfigs.roomName); - if (ftconfigs.logoImage) $("#roomlogo").css("background-image", "url("+ftconfigs.logoImage+")") - document.title = ftconfigs.roomName + " | firetable"; - if (ftconfigs.roomInfoUrl.length) $("#roomInfo").attr("href", ftconfigs.roomInfoUrl); - $("#version").text("You're running firetable v" + firetable.version + "."); - firetable.utilities.getEmojiMap(); - firetable.parser = new DOMParser(); - $(window).resize(firetable.utilities.debounce(function() { - // This will execute whenever the window is resized - $("#thehistory").css('top', $('#stage').outerHeight() + $('#topbar').outerHeight()); - $('#playerArea,#scScreen').width($('#djStage').outerWidth()).height($('#djStage').outerHeight()); - $("#stealContain").css({ - 'top': $('#grab').offset().top + $('#grab').height(), - 'left': $('#grab').offset().left - 16 - }); - setup(); - }, 500)); - firetable.utilities.scrollToBottom(); - var widgetIframe = document.getElementById('sc-widget'); - firetable.scwidget = SC.Widget(widgetIframe); - firetable.scwidget.bind(SC.Widget.Events.READY, function() { - firetable.scwidget.bind(SC.Widget.Events.PLAY, function() { - var vol = localStorage["firetableVol"]; - if (!vol) { - vol = 80; - localStorage["firetableVol"] = 80; - } - firetable.scwidget.setVolume(vol); - if (firetable.scSeek) firetable.scwidget.seekTo(firetable.scSeek); - }); - if (firetable.song) { - var data = firetable.song; - var nownow = Date.now(); - var timeSince = nownow - data.started; - if (timeSince <= 0) timeSince = 0; - - var secSince = Math.floor(timeSince / 1000); - var timeLeft = data.duration - secSince; - if (data.type == 2) { - if (!firetable.preview) { - firetable.scSeek = timeSince; - if (!firetable.disableMediaPlayback) firetable.scwidget.load("http://api.soundcloud.com/tracks/" + data.cid, { - auto_play: true - }); - } - } + if (ftconfigs.facebookURL) { + $(".sociallogo.facebook").attr("href", ftconfigs.facebookURL); + $(".sociallogo.facebook").css("display", "inline-block"); } - firetable.scLoaded = true; - }); - - $playlistItemTemplate = $('#mainqueue .pvbar').remove(); - $tagEditorTemplate = $('.tagPromptBox').remove(); - - ftapi.init(ftconfigs.firebase); - - SC.initialize({ - client_id: ftconfigs.soundcloudKey - }); - - ftapi.events.on("loggedIn", function(data) { - firetable.actions.loggedIn(data); - }); - - ftapi.events.on("loggedOut", firetable.actions.showLoginScreen); - - ftapi.events.on("authReconnected", function() { - firetable.debug && console.log('reconnected'); - $('body').removeClass('disconnected'); - $('#newchat').prop('disabled', false).focus(); - }); - - ftapi.events.on("authDisconnected", function() { - firetable.debug && console.log('disconnected'); - $('body').addClass('disconnected'); - $('#newchat').prop('disabled', true).blur(); - }); - - ftapi.events.on("userBanned", function() { - firetable.debug && console.log("ban detected."); - if (document.getElementById("notice") == null) { - var usrname2use = ftapi.uid; - if (ftapi.users[ftapi.uid]) { - if (ftapi.users[ftapi.uid].username) usrname2use = ftapi.users[ftapi.uid].username; - } - $('.notice').attr('id', 'notice'); - $("#troublemaker").text(usrname2use); - } - }); - - ftapi.events.on("userUnbanned", function() { - window.location.reload(); - }); - - firetable.ui.init(); -}; - -firetable.actions = { - dubtrackImport: function() { - $("#importDubResults").html("importing (0/" + firetable.dtImportList.length + ")..."); - $("#dubimportButton").hide(); - var listid = ftapi.actions.createList(firetable.dtImportName); - var name = firetable.dtImportName; - - $("#listpicker").append(""); - var trackarray = firetable.dtImportList; - for (var e = 0; e < trackarray.length; e++) { - var thetype = 1; - if (trackarray[e].type == "soundcloud") thetype = 2; - var numbo = e + 1; - $("#importDubResults").html("importing (" + numbo + "/" + firetable.dtImportList.length + ")..."); - if (numbo == firetable.dtImportList.length) $("#importDubResults").html("Import complete! You can now select another file if you'd like to do another!"); - ftapi.actions.addToList(thetype, trackarray[e].name, trackarray[e].cid, listid); - } - }, - localChatResponse: function(txt) { - if (txt.length) { - $("#chats").append("
" + txt + "
"); - firetable.utilities.scrollToBottom(); - } - }, - logOut: function() { - ftapi.actions.logOut(); - firetable.debug && console.log("logout"); - }, - showLoginScreen: function() { - $("#cardCaseButton").hide(); - $("#loggedInName").hide(); - $("#logOutButton").hide().off(); - $('#mainGrid').removeClass().addClass('login'); - $("#grab").css("display", "none"); - if (firetable.loginForm && !$("#login").html()) { - $("#mainGrid").append("
" + firetable.loginForm + "
"); - - firetable.ui.loginEventsInit(); - } - }, - logIn: function(email, password) { - firetable.debug && console.log("login"); - ftapi.actions.logIn(email, password, function(error) { - var errorCode = error.code; - var errorMessage = error.message; - if (errorCode === 'auth/wrong-password') { - alert('Wrong password.'); - } else { - alert(errorMessage); - } - firetable.debug && console.log("log in error:", error); - }); - }, - loggedIn: function(user) { - firetable.debug && console.log("user signed in!"); - if ($("#login").html()) { - firetable.loginForm = $("#login").html(); - firetable.ui.loginEventsDestroy(); - $("#login").remove(); + if (ftconfigs.redditURL) { + $(".sociallogo.reddit").attr("href", ftconfigs.redditURL); + $(".sociallogo.reddit").css("display", "inline-block"); } - if (ftapi.users[ftapi.uid]) { - if (ftapi.users[ftapi.uid].username) { - $("#loggedInName").text(ftapi.users[ftapi.uid].username); - } else { - $("#loggedInName").text(user.uid); - } - } else { - $("#loggedInName").text(user.uid); + if (ftconfigs.lastfmURL) { + $(".sociallogo.lastfm").attr("href", ftconfigs.lastfmURL); + $(".sociallogo.lastfm").css("display", "inline-block"); } - ftapi.lookup.allLists(function(allPlaylists) { - $("#listpicker").off("change"); - $("#listpicker").html(""); - for (var key in allPlaylists) { - if (allPlaylists.hasOwnProperty(key)) { - $("#listpicker").append(""); - } - } - ftapi.lookup.selectedList(function(selectedList) { - $("#listpicker").val(selectedList).change(); - $("#listpicker").change(function() { - var val = $("#listpicker").val(); - if (val == "1") { - //ADD PLAYLIST SCREEN - $("#mainqueuestuff").css("display", "none"); - $("#filterMachine").css("display", "none"); - $("#addbox").css("display", "none"); - $("#cancelqsearch").hide(); - $("#qControlButtons").hide(); - - $("#plmanager").css("display", "flex"); + if (ftconfigs.discordURL) { + $(".sociallogo.discord").attr("href", ftconfigs.discordURL); + $(".sociallogo.discord").css("display", "inline-block"); + } - } else if (val != ftapi.selectedListThing) { - //LOAD SELECTED LIST - //change selected list in user obj - $("#mainqueuestuff").css("display", "block"); - $("#filterMachine").css("display", "block"); - $("#addbox").css("display", "none"); - $("#cancelqsearch").hide(); - $("#qControlButtons").show(); + if (ftconfigs.soundcloudURL) { + $(".sociallogo.soundcloud").attr("href", ftconfigs.soundcloudURL); + $(".sociallogo.soundcloud").css("display", "inline-block"); + } - $("#plmanager").css("display", "none"); - ftapi.actions.switchList(val); - } else { - //you selected the thing you already had selected. - $("#mainqueuestuff").css("display", "block"); - $("#filterMachine").css("display", "block"); - $("#addbox").css("display", "none"); - $("#cancelqsearch").hide(); - $("#qControlButtons").show(); - $("#plmanager").css("display", "none"); - } + if (ftconfigs.logoImage) $("#roomlogo").css("background-image", "url(" + ftconfigs.logoImage + ")"); + document.title = ftconfigs.roomName + " | firetable"; + if (ftconfigs.roomInfoUrl.length) $("#roomInfo").attr("href", ftconfigs.roomInfoUrl); + $("#version").text("You're running firetable v" + firetable.version + "."); + firetable.utilities.getEmojiMap(); + firetable.parser = new DOMParser(); + $(window).resize( + firetable.utilities.debounce(function () { + // This will execute whenever the window is resized + $("#thehistory").css("top", $("#stage").outerHeight() + $("#topbar").outerHeight()); + $("#playerArea,#scScreen").width($("#djStage").outerWidth()).height($("#djStage").outerHeight()); + $("#stealContain").css({ + top: $("#grab").offset().top + $("#grab").height(), + left: $("#grab").offset().left - 16, + }); + setup(); + }, 500) + ); + firetable.utilities.scrollToBottom(); + var widgetIframe = document.getElementById("sc-widget"); + firetable.scwidget = SC.Widget(widgetIframe); + firetable.scwidget.bind(SC.Widget.Events.READY, function () { + firetable.scwidget.bind(SC.Widget.Events.PLAY, function () { + var vol = localStorage["firetableVol"]; + if (!vol) { + vol = 80; + localStorage["firetableVol"] = 80; + } + firetable.scwidget.setVolume(vol); + if (firetable.scSeek) firetable.scwidget.seekTo(firetable.scSeek); }); - - }); - }); - $("#cardCaseButton").show(); - $("#loggedInName").show(); - $("#logOutButton").show().on('click', firetable.actions.logOut); - firetable.debug && console.log('remove login class from mainGrid'); - $('#mainGrid').removeClass().addClass('mmusrs'); - $("#grab").css("display", "inline-block"); - }, - cardCase: function() { - $("#cardsMain").html(""); - ftapi.lookup.cardCollection(function(data) { - for (var key in data) { - var childData = data[key]; - firetable.debug && console.log('card:', childData); - $("#cardsMain").append("Gift to DJShare In Chat"); - firetable.actions.displayCard(childData, key); - } + if (firetable.song) { + var data = firetable.song; + var nownow = Date.now(); + var timeSince = nownow - data.started; + if (timeSince <= 0) timeSince = 0; + + var secSince = Math.floor(timeSince / 1000); + var timeLeft = data.duration - secSince; + if (data.type == 2) { + if (!firetable.preview) { + firetable.scSeek = timeSince; + if (!firetable.disableMediaPlayback) + firetable.scwidget.load("http://api.soundcloud.com/tracks/" + data.cid, { + auto_play: true, + }); + } + } + } + firetable.scLoaded = true; }); - }, - chatCard: function(cardid) { - ftapi.actions.sendChat("Check out my card...", cardid); - }, - giftCard: function(cardid) { - ftapi.actions.sendChat("!giftcard :gift:", cardid); - $("#caseCardSpot" + cardid).remove(); - }, - displayCard: function(data, chatid) { - firetable.debug && console.log("display card"); - var defaultScheme = false; - if (data.colors) { - if (data.colors.color == "#fff" || data.colors.color == "#7f7f7f") { - data.colors.color = firetable.orange; - data.colors.txt = "#000"; - defaultScheme = true; - } - } - - if (data.image == "img/idlogo.png" && ftconfigs.defaultAlbumArtUrl.length) data.image = ftconfigs.defaultAlbumArtUrl; - var canvas = document.getElementById('cardMaker' + chatid); - - if (canvas.getContext) { - var ctx = canvas.getContext('2d'); - ctx.clearRect(0, 0, canvas.width, canvas.height); - ctx.fillStyle = "#000"; - ctx.fillRect(0, 0, 225, 300); - - ctx.fillStyle = data.colors.color; - if (defaultScheme) ctx.fillStyle = "#fff"; - ctx.fillRect(1, 30, 223, 175); - - var grd = ctx.createLinearGradient(0, 0, 0, 175); - grd.addColorStop(0, "rgba(0, 0, 0, 0.75)"); - grd.addColorStop(1, "rgba(0, 0, 0, 0.55)"); - - // Fill with gradient - ctx.fillStyle = grd; - ctx.fillRect(1, 30, 223, 175); - - ctx.fillStyle = data.colors.color; - ctx.fillRect(1, 205, 223, 10); - - ctx.fillStyle = "#333333"; - //ctx.fillRect(1, 205, 223, 1); - // ctx.fillRect(1, 215, 223, 1); - - ctx.fillStyle = "#151515"; - ctx.fillRect(1, 216, 223, 75); - - //text go - ctx.fillStyle = "#eee"; - ctx.font = "700 11px Helvetica, Arial, sans-serif"; - ctx.fillText(data.djname, 10, 20); - - - ctx.font = "400 8px Helvetica, Arial, sans-serif"; - ctx.textAlign = "center"; - ctx.fillText("Printed " + firetable.utilities.format_date(data.date) + " | " + ftconfigs.roomNameShort, 112.5, 299); - - ctx.font = "700 10px Helvetica, Arial, sans-serif"; - ctx.textAlign = "left"; - var linez = firetable.utilities.wrapText(ctx, data.title, 66, 240, 160, 15); - firetable.debug && console.log('linez:', linez); - ctx.font = "400 8px Helvetica, Arial, sans-serif"; - ctx.textAlign = "left"; - firetable.utilities.wrapText(ctx, data.artist, 66, 253 + (15 * linez), 160, 15); - - ctx.fillStyle = data.colors.txt; - ctx.font = "400 9px Helvetica, Arial, sans-serif"; - ctx.textAlign = "center"; - ctx.fillText("Card No. " + data.cardnum + " | DJ Card | Max Operating Temp " + data.temp + "°", 112.5, 214); - ctx.beginPath(); - ctx.arc(205, 15, 12, 0, 2 * Math.PI, false); - ctx.fillStyle = data.colors.color; - ctx.fill(); - - ctx.fillStyle = data.colors.txt; - ctx.font = "700 15px Helvetica, Arial, sans-serif"; - ctx.textAlign = "left"; - ctx.fillText(data.num, 200.5, 20); - - var doImages = function() { - var picboy = new Image; - picboy.xvalue = 0; - picboy.onload = function() { - ctx.drawImage(this, 20, 30, 175, 175); - var picboy2 = new Image; - picboy2.xvalue = 0; - picboy2.onload = function() { - var heighta = 50; - if (data.image.match(/ytimg.com/g)) heighta = 28; - ctx.drawImage(this, 10, 230, 50, heighta); - ctx = null; - }; - picboy2.src = data.image; - }; - picboy.src = 'https://indiediscotheque.com/robots/' + data.djid + data.djname + '.png?size=175x175'; + $playlistItemTemplate = $("#mainqueue .pvbar").remove(); + $tagEditorTemplate = $(".tagPromptBox").remove(); - }; + ftapi.init(ftconfigs.firebase); - // special styles - - if (data.special) { - if (data.special == "id8") { - ctx.fillStyle = data.colors.color; - ctx.fillRect(1, 30, 223, 10); + SC.initialize({ + client_id: ftconfigs.soundcloudKey, + }); - ctx.fillStyle = "#333333"; - // ctx.fillRect(1, 29, 223, 1); - // ctx.fillRect(1, 40, 223, 1); + ftapi.events.on("loggedIn", function (data) { + firetable.actions.loggedIn(data); + }); - ctx.fillStyle = data.colors.txt; - ctx.font = "400 10px Helvetica, Arial, sans-serif"; - ctx.textAlign = "center"; - ctx.fillText("Celebrating 8 Years of Indie Discotheque", 112.5, 38); + ftapi.events.on("loggedOut", firetable.actions.showLoginScreen); - var cake = new Image; - cake.xvalue = 0; - cake.onload = function() { - ctx.drawImage(this, 10, 50, 35, 35); - var eight = new Image; - eight.xvalue = 0; - eight.onload = function() { - ctx.drawImage(this, 180, 50, 35, 35); - doImages(); - }; - eight.src = 'img/8.png'; - }; - cake.src = 'img/cake.png'; - - } else if (data.special == "id9") { - ctx.fillStyle = data.colors.color; - ctx.fillRect(1, 30, 223, 10); - - ctx.fillStyle = "#333333"; - // ctx.fillRect(1, 29, 223, 1); - // ctx.fillRect(1, 40, 223, 1); - - ctx.fillStyle = data.colors.txt; - ctx.font = "400 10px Helvetica, Arial, sans-serif"; - ctx.textAlign = "center"; - ctx.fillText("Celebrating 9 Years of Indie Discotheque", 112.5, 38); - - var cake = new Image; - cake.xvalue = 0; - cake.onload = function() { - ctx.drawImage(this, 5, 50, 45, 45); - var eight = new Image; - eight.xvalue = 0; - eight.onload = function() { - ctx.save(); - ctx.translate(75 * 0.5, 75 * 0.5); - ctx.rotate(0.959931); - ctx.translate(-75 * 0.5, -75 * 0.5); - - ctx.drawImage(this, 125, -81, 75, 75); - ctx.restore(); - var picboy = new Image; - picboy.xvalue = 0; - picboy.onload = function() { - ctx.drawImage(this, 25, 40, 170, 170); - var picboy2 = new Image; - picboy2.xvalue = 0; - picboy2.onload = function() { - var heighta = 50; - if (data.image.match(/ytimg.com/g)) heighta = 28; - ctx.drawImage(this, 10, 230, 50, heighta); - ctx = null; - }; - picboy2.src = data.image; - }; - picboy.src = 'img/id9.png'; - }; - eight.src = 'https://indiediscotheque.com/robots/' + data.djid + data.djname + '.png?size=110x110'; - }; - cake.src = 'img/arnold.png'; - } - } else { - doImages(); - } - } - }, - showCard: function(cardid, chatid) { - // let's SHOW A CARD - ftapi.lookup.card(cardid, function(data) { - firetable.actions.displayCard(data, chatid); + ftapi.events.on("authReconnected", function () { + firetable.debug && console.log("reconnected"); + $("body").removeClass("disconnected"); + $("#newchat").prop("disabled", false).focus(); }); - }, - filterQueue: function(val) { - if (val.length == 0) { - $("#mainqueue .pvbar").show(); - return - } else { - } - val = val.toLowerCase(); - $("#mainqueue .pvbar").each(function(p, q) { - var txt = $(q).find(".listwords").text(); - var regex = new RegExp(val, 'ig'); - if (txt.match(regex)) { - $(q).show() - } else { - $(q).hide() - } + ftapi.events.on("authDisconnected", function () { + firetable.debug && console.log("disconnected"); + $("body").addClass("disconnected"); + $("#newchat").prop("disabled", true).blur(); }); - }, - muteToggle: function(zeroMute) { - var muted = localStorage["firetableMute"]; - var icon = ""; - firetable.debug && console.log('muted:', muted); - if (zeroMute) { - icon = ""; - muted = 0; - - } else if (typeof muted !== 'undefined') { - if (muted != "false") { - - if (muted == 0) { - $("#slider").slider("value", 80); - player.setVolume(80); - firetable.scwidget.setVolume(80); - localStorage["firetableVol"] = 80; - } else { - muted = parseInt(muted); - $("#slider").slider("value", muted); - player.setVolume(muted); - firetable.scwidget.setVolume(muted); - localStorage["firetableVol"] = muted; + ftapi.events.on("userBanned", function () { + firetable.debug && console.log("ban detected."); + if (document.getElementById("notice") == null) { + var usrname2use = ftapi.uid; + if (ftapi.users[ftapi.uid]) { + if (ftapi.users[ftapi.uid].username) usrname2use = ftapi.users[ftapi.uid].username; + } + $(".notice").attr("id", "notice"); + $("#troublemaker").text(usrname2use); } - muted = false; - } else { - icon = ""; + }); - muted = $("#slider").slider("value"); - $("#slider").slider('value', 0); - player.setVolume(0); - firetable.scwidget.setVolume(0); - localStorage["firetableVol"] = 0; + ftapi.events.on("userUnbanned", function () { + window.location.reload(); + }); - } - } else { - icon = ""; - - muted = $("#slider").slider("value"); - $("#slider").slider('value', 0); - player.setVolume(0); - firetable.scwidget.setVolume(0); - localStorage["firetableVol"] = 0; - } + firetable.ui.init(); +}; - if (muted) $("#volstatus").addClass('on'); - else $("#volstatus").removeClass('on'); - $("#volstatus i").html(icon); - localStorage["firetableMute"] = muted; - }, - pview: function(id, fromSearch, type, fromHist) { - if (firetable.preview == id) { - //already previewing this. stop and resume regular song - clearTimeout(firetable.ptimeout); - firetable.ptimeout = null; - $("#pv" + firetable.preview).html(""); - $("#pvbar" + firetable.preview).css("background-image", "none"); - clearInterval(firetable.movePvBar); - firetable.movePvBar = null; - firetable.preview = false; - //start regular song - var nownow = Date.now(); - var timeSince = nownow - firetable.song.started; - if (timeSince <= 0) timeSince = 0; - - var secSince = Math.floor(timeSince / 1000); - var timeLeft = firetable.song.duration - secSince; - if (firetable.song.type == 1) { - if (!firetable.preview) { - if (firetable.scLoaded) firetable.scwidget.pause(); - if (!firetable.disableMediaPlayback) player.loadVideoById(firetable.song.cid, secSince, "large"); +firetable.actions = { + dubtrackImport: function () { + $("#importDubResults").html("importing (0/" + firetable.dtImportList.length + ")..."); + $("#dubimportButton").hide(); + var listid = ftapi.actions.createList(firetable.dtImportName); + var name = firetable.dtImportName; + + $("#listpicker").append('"); + var trackarray = firetable.dtImportList; + for (var e = 0; e < trackarray.length; e++) { + var thetype = 1; + if (trackarray[e].type == "soundcloud") thetype = 2; + var numbo = e + 1; + $("#importDubResults").html("importing (" + numbo + "/" + firetable.dtImportList.length + ")..."); + if (numbo == firetable.dtImportList.length) + $("#importDubResults").html( + "Import complete! You can now select another file if you'd like to do another!" + ); + ftapi.actions.addToList(thetype, trackarray[e].name, trackarray[e].cid, listid); } - } else if (firetable.song.type == 2) { - if (!firetable.preview) { - if (firetable.ytLoaded) player.stopVideo(); - firetable.scSeek = timeSince; - if (!firetable.disableMediaPlayback) firetable.scwidget.load("http://api.soundcloud.com/tracks/" + firetable.song.cid, { - auto_play: true - }); + }, + localChatResponse: function (txt) { + if (txt.length) { + $("#chats").append('
' + txt + "
"); + firetable.utilities.scrollToBottom(); } - } - } else { - if (firetable.preview) { - $("#pv" + firetable.preview).html(""); - $("#pvbar" + firetable.preview).css("background-image", "none"); - } - - firetable.preview = id; - if (fromSearch) { - var cid = id.slice(5); - } else { - var cid = firetable.queue[id].cid; - } - - if (firetable.ptimeout != null) { - clearTimeout(firetable.ptimeout); - firetable.ptimeout = null; - } - if (firetable.movePvBar != null) { - clearInterval(firetable.movePvBar); - firetable.movePvBar = null; - } - firetable.pvCount = 0; - firetable.ptimeout = setTimeout(function() { - firetable.ptimeout = null; - $("#pv" + firetable.preview).html(""); - $("#pvbar" + firetable.preview).css("background-image", "none"); - clearInterval(firetable.movePvBar); - firetable.movePvBar = null; - firetable.pvCount = 0; - firetable.preview = false; - - //start regular song - var nownow = Date.now(); - var timeSince = nownow - firetable.song.started; - if (timeSince <= 0) timeSince = 0; - - var secSince = Math.floor(timeSince / 1000); - var timeLeft = firetable.song.duration - secSince; - if (firetable.song.type == 1) { - if (!firetable.preview) { - if (firetable.scLoaded) firetable.scwidget.pause(); - if (!firetable.disableMediaPlayback) player.loadVideoById(firetable.song.cid, secSince, "large"); - } - } else if (firetable.song.type == 2) { - if (!firetable.preview) { - if (firetable.ytLoaded) player.stopVideo(); - firetable.scSeek = timeSince; - if (!firetable.disableMediaPlayback) firetable.scwidget.load("http://api.soundcloud.com/tracks/" + firetable.song.cid, { - auto_play: true - }); - } + }, + logOut: function () { + ftapi.actions.logOut(); + firetable.debug && console.log("logout"); + }, + showLoginScreen: function () { + $("#cardCaseButton").hide(); + $("#loggedInName").hide(); + $("#logOutButton").hide().off(); + $("#mainGrid").removeClass().addClass("login"); + $("#grab").css("display", "none"); + if (firetable.loginForm && !$("#login").html()) { + $("#mainGrid").append('
' + firetable.loginForm + "
"); + + firetable.ui.loginEventsInit(); } - }, 30 * 1000); - $("#pv" + id).html(""); - firetable.movePvBar = setInterval(function() { - var pcnt = (firetable.pvCount / 29) * 100; - firetable.pvCount += 0.2; - var pvcolr = "#222"; - if (fromHist) pvcolr = "#222"; - $("#pvbar" + firetable.preview).css("background-image", "linear-gradient(90deg, rgba(244, 129, 11, 0.267) " + pcnt + "%, " + pvcolr + " " + pcnt + "%)"); - }, 200); - if (type == 1) { - if (firetable.scLoaded) firetable.scwidget.pause(); - if (!firetable.disableMediaPlayback) player.loadVideoById(cid, 0, "large") - } else if (type == 2) { - if (firetable.ytLoaded) player.stopVideo(); - firetable.scSeek = 0; - if (!firetable.disableMediaPlayback) firetable.scwidget.load("http://api.soundcloud.com/tracks/" + cid, { - auto_play: true + }, + logIn: function (email, password) { + firetable.debug && console.log("login"); + ftapi.actions.logIn(email, password, function (error) { + var errorCode = error.code; + var errorMessage = error.message; + if (errorCode === "auth/wrong-password") { + alert("Wrong password."); + } else { + alert(errorMessage); + } + firetable.debug && console.log("log in error:", error); }); - } - - - } + }, + loggedIn: function (user) { + firetable.debug && console.log("user signed in!"); + if ($("#login").html()) { + firetable.loginForm = $("#login").html(); + firetable.ui.loginEventsDestroy(); + $("#login").remove(); + } - }, - mergeLists: function(source, dest, sourceName) { - if (source == dest) { - //source and dest are the same, let's remove the duplicates - firetable.actions.removeDupesFromQueue(); - return; - } - if (dest == -1) { - // create new list if needed - var newname = firetable.utilities.format_date(Date.now()) + " Copy of " + sourceName; - var dest = ftapi.actions.createList(newname); - $("#listpicker").append(""); - } - ftapi.actions.mergeLists(source, dest, function() { - $("#mergeCompleted").show(); - $("#mergeHappening").hide(); - }); - }, - queueFromLink: function(link) { - if (link.match(/youtube.com\/watch/)) { - //youtube - firetable.debug && console.log("yt"); - - function getQueryStringValue(str, key) { - return unescape(str.replace(new RegExp("^(?:.*[&\\?]" + escape(key).replace(/[\.\+\*]/g, "\\$&") + "(?:\\=([^&]*))?)?.*$", "i"), "$1")); - } - var therealid = getQueryStringValue(link, "v"); - if (therealid) { - function keyWordsearch() { - gapi.client.setApiKey(ftconfigs.youtubeKey); - gapi.client.load('youtube', 'v3', function() { - makeRequest(); - }); + if (ftapi.users[ftapi.uid]) { + if (ftapi.users[ftapi.uid].username) { + $("#loggedInName").text(ftapi.users[ftapi.uid].username); + } else { + $("#loggedInName").text(user.uid); + } + } else { + $("#loggedInName").text(user.uid); } - function makeRequest() { - var request = gapi.client.youtube.videos.list({ - id: therealid, - part: 'snippet', - maxResults: 1 - }); - request.execute(function(response) { - firetable.debug && console.log('queue from link:', response); - if (response.result) { - if (response.result.items) { - if (response.result.items.length) { - var goodtitle = response.result.items[0].snippet.title; - var yargo = response.result.items[0].snippet.title.split(" - "); - var sartist = yargo[0]; - var stitle = yargo[1]; - if (!stitle) { - // yt title not formatted artist - title. use uploader name instead as artist - stitle = sartist; - sartist = response.result.items[0].snippet.channelTitle.replace(" - Topic", ""); - } - goodtitle = sartist + " - " + stitle; - firetable.actions.queueTrack(response.result.items[0].id, goodtitle, 1); + ftapi.lookup.allLists(function (allPlaylists) { + $("#listpicker").off("change"); + $("#listpicker").html( + '' + ); + for (var key in allPlaylists) { + if (allPlaylists.hasOwnProperty(key)) { + $("#listpicker").append( + '" + ); } - } } - }) - } - keyWordsearch(); - } - } else if (link.match(/soundcloud.com/)) { - //soundcloud - firetable.debug && console.log("sc"); - var getComments = function(track) { - return SC.get("tracks/" + track.id); - }; - - firetable.actions.resolveSCLink(link, function(tracks) { - if (tracks) { - var yargo = tracks.title.split(" - "); - var sartist = yargo[0]; - var stitle = yargo[1]; - if (!stitle) { - stitle = sartist; - sartist = tracks.user.username; - } - var goodTitle = sartist + " - " + stitle; - firetable.actions.queueTrack(tracks.id, goodTitle, 2); + ftapi.lookup.selectedList(function (selectedList) { + $("#listpicker").val(selectedList).change(); + $("#listpicker").change(function () { + var val = $("#listpicker").val(); + if (val == "1") { + //ADD PLAYLIST SCREEN + $("#mainqueuestuff").css("display", "none"); + $("#filterMachine").css("display", "none"); + $("#addbox").css("display", "none"); + $("#cancelqsearch").hide(); + $("#qControlButtons").hide(); + + $("#plmanager").css("display", "flex"); + } else if (val != ftapi.selectedListThing) { + //LOAD SELECTED LIST + //change selected list in user obj + $("#mainqueuestuff").css("display", "block"); + $("#filterMachine").css("display", "block"); + $("#addbox").css("display", "none"); + $("#cancelqsearch").hide(); + $("#qControlButtons").show(); + + $("#plmanager").css("display", "none"); + ftapi.actions.switchList(val); + } else { + //you selected the thing you already had selected. + $("#mainqueuestuff").css("display", "block"); + $("#filterMachine").css("display", "block"); + $("#addbox").css("display", "none"); + $("#cancelqsearch").hide(); + $("#qControlButtons").show(); + $("#plmanager").css("display", "none"); + } + }); + }); + }); + $("#cardCaseButton").show(); + $("#loggedInName").show(); + $("#logOutButton").show().on("click", firetable.actions.logOut); + firetable.debug && console.log("remove login class from mainGrid"); + $("#mainGrid").removeClass().addClass("mmusrs"); + $("#grab").css("display", "inline-block"); + }, + cardCase: function () { + $("#cardsMain").html(""); + ftapi.lookup.cardCollection(function (data) { + for (var key in data) { + var childData = data[key]; + firetable.debug && console.log("card:", childData); + $("#cardsMain").append( + 'Gift to DJShare In Chat' + ); + firetable.actions.displayCard(childData, key); + } + }); + }, + chatCard: function (cardid) { + ftapi.actions.sendChat("Check out my card...", cardid); + }, + giftCard: function (cardid) { + ftapi.actions.sendChat("!giftcard :gift:", cardid); + $("#caseCardSpot" + cardid).remove(); + }, + displayCard: function (data, chatid) { + firetable.debug && console.log("display card"); + var defaultScheme = false; + if (data.colors) { + if (data.colors.color == "#fff" || data.colors.color == "#7f7f7f") { + data.colors.color = firetable.orange; + data.colors.txt = "#000"; + defaultScheme = true; + } } - }); - //SC.resolve(link).then(getComments).then(listComments); - } - }, - resolveSCLink: function(link, callback) { - var importantStuff = link.replace("https://soundcloud.com/", ""); - importantStuff = importantStuff.replace("http://soundcloud.com/", ""); - $.ajax({ - url: "https://thompsn.com/resolvesc/?q=" + importantStuff, - type: 'GET', - dataType: 'json', - success: function(res) { - console.log(res); - callback(res.response); - } - }); - }, - scGet: function(type, q, callback) { - $.ajax({ - url: "https://thompsn.com/soundcloud/?type=" + type + "&q=" + q, - type: 'GET', - dataType: 'json', - success: function(res) { - console.log(res); - callback(res.response); - } - }); - }, - updateQueue: function() { - //this fires when someone drags a song to a new spot in the queue - var arr = $('#mainqueue > div').map(function() { - var theid = this.id; - var idraw = theid.slice(5); - return idraw; - }).get(); - ftapi.actions.reorderList(arr, firetable.preview, function(changePV) { - if (changePV) firetable.preview = changePV; - }); - }, - shuffleQueue: function() { - ftapi.actions.shuffleList(firetable.preview, function(changePV) { - if (changePV) firetable.preview = changePV; - }); - }, - removeDupesFromQueue: function() { - ftapi.actions.removeDuplicatesFromList(); - $("#mergeCompleted").show(); - $("#mergeHappening").hide(); - }, - editTagsPrompt: function(songid) { - var song = firetable.queue[songid]; - var $pvbar = $('#mainqueue .pvbar[data-key="' + songid + '"]'); - $('#mainqueue .pvbar.editing').removeClass('editing'); - $('.tagPromptBox').remove(); - $pvbar.addClass('editing'); - var $tags = $tagEditorTemplate.clone().appendTo($pvbar); - $tags.find(".tagMachine").val(song.name); - if (song.type == 1) { - $tags.find(".tagSongLink").attr("href", "https://youtube.com/watch?v=" + song.cid); - } else if (song.type == 2) { - firetable.actions.scGet('tracks', song.cid, function(tracks) { - if (tracks.permalink_url) { - $tags.find(".tagSongLink").attr("href", tracks.permalink_url); - } else { - $tags.find(".tagSongLink").attr("href", "http://howtojointheindiediscothequewaitlist.com/ThisSongIsBroken?thanks=true"); - } - }); - } + if (data.image == "img/idlogo.png" && ftconfigs.defaultAlbumArtUrl.length) + data.image = ftconfigs.defaultAlbumArtUrl; + var canvas = document.getElementById("cardMaker" + chatid); + + if (canvas.getContext) { + var ctx = canvas.getContext("2d"); + ctx.clearRect(0, 0, canvas.width, canvas.height); + ctx.fillStyle = "#000"; + ctx.fillRect(0, 0, 225, 300); + + ctx.fillStyle = data.colors.color; + if (defaultScheme) ctx.fillStyle = "#fff"; + ctx.fillRect(1, 30, 223, 175); + + var grd = ctx.createLinearGradient(0, 0, 0, 175); + grd.addColorStop(0, "rgba(0, 0, 0, 0.75)"); + grd.addColorStop(1, "rgba(0, 0, 0, 0.55)"); + + // Fill with gradient + ctx.fillStyle = grd; + ctx.fillRect(1, 30, 223, 175); + + ctx.fillStyle = data.colors.color; + ctx.fillRect(1, 205, 223, 10); + + ctx.fillStyle = "#333333"; + //ctx.fillRect(1, 205, 223, 1); + // ctx.fillRect(1, 215, 223, 1); + + ctx.fillStyle = "#151515"; + ctx.fillRect(1, 216, 223, 75); + + //text go + ctx.fillStyle = "#eee"; + ctx.font = "700 11px Helvetica, Arial, sans-serif"; + ctx.fillText(data.djname, 10, 20); + + ctx.font = "400 8px Helvetica, Arial, sans-serif"; + ctx.textAlign = "center"; + ctx.fillText( + "Printed " + firetable.utilities.format_date(data.date) + " | " + ftconfigs.roomNameShort, + 112.5, + 299 + ); + + ctx.font = "700 10px Helvetica, Arial, sans-serif"; + ctx.textAlign = "left"; + var linez = firetable.utilities.wrapText(ctx, data.title, 66, 240, 160, 15); + firetable.debug && console.log("linez:", linez); + ctx.font = "400 8px Helvetica, Arial, sans-serif"; + ctx.textAlign = "left"; + firetable.utilities.wrapText(ctx, data.artist, 66, 253 + 15 * linez, 160, 15); + + ctx.fillStyle = data.colors.txt; + ctx.font = "400 9px Helvetica, Arial, sans-serif"; + ctx.textAlign = "center"; + ctx.fillText("Card No. " + data.cardnum + " | DJ Card | Max Operating Temp " + data.temp + "°", 112.5, 214); + ctx.beginPath(); + ctx.arc(205, 15, 12, 0, 2 * Math.PI, false); + ctx.fillStyle = data.colors.color; + ctx.fill(); + + ctx.fillStyle = data.colors.txt; + ctx.font = "700 15px Helvetica, Arial, sans-serif"; + ctx.textAlign = "left"; + ctx.fillText(data.num, 200.5, 20); + + var doImages = function () { + var picboy = new Image(); + picboy.xvalue = 0; + picboy.onload = function () { + ctx.drawImage(this, 20, 30, 175, 175); + var picboy2 = new Image(); + picboy2.xvalue = 0; + picboy2.onload = function () { + var heighta = 50; + if (data.image.match(/ytimg.com/g)) heighta = 28; + ctx.drawImage(this, 10, 230, 50, heighta); + ctx = null; + }; + picboy2.src = data.image; + }; + picboy.src = "https://indiediscotheque.com/robots/" + data.djid + data.djname + ".png?size=175x175"; + }; - firetable.debug && console.log('edit tags song id:', songid); - firetable.songToEdit = { - song: song, - key: songid - }; - }, - importList(id, name, type) { - //time to IMPORT SOME LISTS! - $("#overlay").removeClass('show'); - $("#importResults").html(""); - $("#plMachine").val(""); - if (type == 1) { - //youtube - var finalList = []; - - function keyWordsearch(pg) { - gapi.client.setApiKey(ftconfigs.youtubeKey); - gapi.client.load('youtube', 'v3', function() { - makeRequest(pg); + // special styles + + if (data.special) { + if (data.special == "id8") { + ctx.fillStyle = data.colors.color; + ctx.fillRect(1, 30, 223, 10); + + ctx.fillStyle = "#333333"; + // ctx.fillRect(1, 29, 223, 1); + // ctx.fillRect(1, 40, 223, 1); + + ctx.fillStyle = data.colors.txt; + ctx.font = "400 10px Helvetica, Arial, sans-serif"; + ctx.textAlign = "center"; + ctx.fillText("Celebrating 8 Years of Indie Discotheque", 112.5, 38); + + var cake = new Image(); + cake.xvalue = 0; + cake.onload = function () { + ctx.drawImage(this, 10, 50, 35, 35); + var eight = new Image(); + eight.xvalue = 0; + eight.onload = function () { + ctx.drawImage(this, 180, 50, 35, 35); + doImages(); + }; + eight.src = "img/8.png"; + }; + cake.src = "img/cake.png"; + } else if (data.special == "id9") { + ctx.fillStyle = data.colors.color; + ctx.fillRect(1, 30, 223, 10); + + ctx.fillStyle = "#333333"; + // ctx.fillRect(1, 29, 223, 1); + // ctx.fillRect(1, 40, 223, 1); + + ctx.fillStyle = data.colors.txt; + ctx.font = "400 10px Helvetica, Arial, sans-serif"; + ctx.textAlign = "center"; + ctx.fillText("Celebrating 9 Years of Indie Discotheque", 112.5, 38); + + var cake = new Image(); + cake.xvalue = 0; + cake.onload = function () { + ctx.drawImage(this, 5, 50, 45, 45); + var eight = new Image(); + eight.xvalue = 0; + eight.onload = function () { + ctx.save(); + ctx.translate(75 * 0.5, 75 * 0.5); + ctx.rotate(0.959931); + ctx.translate(-75 * 0.5, -75 * 0.5); + + ctx.drawImage(this, 125, -81, 75, 75); + ctx.restore(); + var picboy = new Image(); + picboy.xvalue = 0; + picboy.onload = function () { + ctx.drawImage(this, 25, 40, 170, 170); + var picboy2 = new Image(); + picboy2.xvalue = 0; + picboy2.onload = function () { + var heighta = 50; + if (data.image.match(/ytimg.com/g)) heighta = 28; + ctx.drawImage(this, 10, 230, 50, heighta); + ctx = null; + }; + picboy2.src = data.image; + }; + picboy.src = "img/id9.png"; + }; + eight.src = + "https://indiediscotheque.com/robots/" + data.djid + data.djname + ".png?size=110x110"; + }; + cake.src = "img/arnold.png"; + } + } else { + doImages(); + } + } + }, + showCard: function (cardid, chatid) { + // let's SHOW A CARD + ftapi.lookup.card(cardid, function (data) { + firetable.actions.displayCard(data, chatid); }); - } - - function makeRequest(pg) { - if (pg) { - var request = gapi.client.youtube.playlistItems.list({ - playlistId: id, - maxResults: 50, - part: "snippet", - pageToken: pg - }); + }, + filterQueue: function (val) { + if (val.length == 0) { + $("#mainqueue .pvbar").show(); + return; } else { - var request = gapi.client.youtube.playlistItems.list({ - playlistId: id, - maxResults: 50, - part: "snippet" - }); } - request.execute(function(response) { - if (response.items.length) { - for (var idx = 0; idx < response.items.length; idx++) { - finalList.push(response.items[idx]); - } - } - if (response.nextPageToken) { - if (response.nextPageToken != "") keyWordsearch(response.nextPageToken); - } else { - firetable.debug && console.log(finalList); - var listid = ftapi.actions.createList(name); - $("#listpicker").append(""); - for (var i = 0; i < finalList.length; i++) { - var goodTitle = finalList[i].snippet.title; - // can't use youtube uploader name to fix tags here because YOUTUBE DECIDED NOT TO INCLUDE THAT INFORMATION >:o - if (goodTitle !== "Private video" && goodTitle !== "Deleted video") { - ftapi.actions.addToList(1, goodTitle, finalList[i].snippet.resourceId.videoId, listid); - } + val = val.toLowerCase(); + $("#mainqueue .pvbar").each(function (p, q) { + var txt = $(q).find(".listwords").text(); + var regex = new RegExp(val, "ig"); + if (txt.match(regex)) { + $(q).show(); + } else { + $(q).hide(); } - } - }) - } - keyWordsearch(); - - } else if (type == 2) { - firetable.actions.scGet('playlists', id, function(listinfo) { - firetable.debug && console.log('sc tracks:', listinfo.tracks); - var listid = ftapi.actions.createList(name); - $("#listpicker").append(""); - for (var i = 0; i < listinfo.tracks.length; i++) { - if (listinfo.tracks[i].title) { - var yargo = listinfo.tracks[i].title.split(" - "); - var sartist = yargo[0]; - var stitle = yargo[1]; - if (!stitle) { - stitle = sartist; - sartist = listinfo.tracks[i].user.username; - } - var goodTitle = sartist + " - " + stitle; - } else { - var goodTitle = "Unknown"; - } - - ftapi.actions.addToList(2, goodTitle, listinfo.tracks[i].id, listid); - } - }); - } - }, - bumpSongInQueue: function(songid) { - ftapi.actions.moveTrackToTop(songid, firetable.preview, function(changePV) { - if (changePV) firetable.preview = changePV; - }); - }, - signUp: function(email, password, username) { - firetable.debug && console.log("signup"); - ftapi.actions.signUp(email, password, username, function(error) { - alert(error); - }); - }, - deleteSong: function(id) { - ftapi.actions.deleteTrack(id); - }, - uidLookup: function(name) { - var match = false; - var usrs = ftapi.users; - for (var key in usrs) { - if (usrs.hasOwnProperty(key)) { - if (ftapi.users[key].username) { - if (ftapi.users[key].username == name) { - match = key; - } - } - } - } - if (!match && ftapi.users[name]) match = name; - return match; - }, - grab: function() { - if (firetable.song.cid != 0) { - var title = firetable.song.artist + " - " + firetable.song.title; - firetable.actions.queueTrack(firetable.song.cid, title, firetable.song.type, true); - } - }, - unban: function(userid) { - ftapi.actions.unbanUser(userid); - }, - reloadtrack: function() { - $('#reloadtrack').addClass('on working'); - //start regular song - var nownow = Date.now(); - var timeSince = nownow - firetable.song.started; - if (timeSince <= 0) timeSince = 0; - var secSince = Math.floor(timeSince / 1000); - var timeLeft = firetable.song.duration - secSince; - if (firetable.song.type == 1) { - if (!firetable.preview) { - if (firetable.scLoaded) firetable.scwidget.pause(); - if (!firetable.disableMediaPlayback) player.loadVideoById(firetable.song.cid, secSince, "large"); - } - } else if (firetable.song.type == 2) { - if (!firetable.preview) { - if (firetable.ytLoaded) player.stopVideo(); - firetable.scSeek = timeSince; - if (!firetable.disableMediaPlayback) firetable.scwidget.load("http://api.soundcloud.com/tracks/" + firetable.song.cid, { - auto_play: true - }, function() { - $('#reloadtrack').removeClass('on working'); }); - } - } - }, - queueTrack: function(cid, name, type, tobottom) { - var info = { - type: type, - name: name, - cid: cid - }; - $("#apv" + type + cid).find(".material-icons").text("check"); - $("#apv" + type + cid).css("color", firetable.orange); - $("#apv" + type + cid).css("pointer-events", "none"); - setTimeout(function() { - $("#apv" + type + cid).find(".material-icons").text("playlist_add"); - $("#apv" + type + cid).removeAttr("style"); - }, 3000); - - var cuteid = ftapi.actions.addToList(type, name, cid, false, function() { - firetable.debug && console.log('queue track id:', cuteid); - if (!tobottom) firetable.actions.bumpSongInQueue(cuteid); - }); + }, + muteToggle: function (zeroMute) { + var muted = localStorage["firetableMute"]; + var icon = ""; + firetable.debug && console.log("muted:", muted); + if (zeroMute) { + icon = ""; + muted = 0; + } else if (typeof muted !== "undefined") { + if (muted != "false") { + if (muted == 0) { + $("#slider").slider("value", 80); + player.setVolume(80); + firetable.scwidget.setVolume(80); + localStorage["firetableVol"] = 80; + } else { + muted = parseInt(muted); + $("#slider").slider("value", muted); + player.setVolume(muted); + firetable.scwidget.setVolume(muted); + localStorage["firetableVol"] = muted; + } + muted = false; + } else { + icon = ""; - if (firetable.preview) { - if (firetable.preview.slice(0, 5) == "ytcid" || firetable.preview.slice(0, 5) == "sccid") { - $("#pv" + firetable.preview).html(""); - clearTimeout(firetable.ptimeout); - firetable.ptimeout = null; - $("#pvbar" + firetable.preview).css("background-image", "none"); - clearInterval(firetable.movePvBar); - firetable.movePvBar = null; - firetable.preview = false; - //start regular song - var nownow = Date.now(); - var timeSince = nownow - firetable.song.started; - if (timeSince <= 0) timeSince = 0; - var secSince = Math.floor(timeSince / 1000); - var timeLeft = firetable.song.duration - secSince; - if (firetable.song.type == 1) { - if (!firetable.preview) { - if (firetable.scLoaded) firetable.scwidget.pause(); - if (!firetable.disableMediaPlayback) player.loadVideoById(firetable.song.cid, secSince, "large"); - } - } else if (firetable.song.type == 2) { - if (!firetable.preview) { - if (firetable.ytLoaded) player.stopVideo(); - firetable.scSeek = timeSince; - if (!firetable.disableMediaPlayback) firetable.scwidget.load("http://api.soundcloud.com/tracks/" + firetable.song.cid, { - auto_play: true - }); - } + muted = $("#slider").slider("value"); + $("#slider").slider("value", 0); + player.setVolume(0); + firetable.scwidget.setVolume(0); + localStorage["firetableVol"] = 0; + } + } else { + icon = ""; + + muted = $("#slider").slider("value"); + $("#slider").slider("value", 0); + player.setVolume(0); + firetable.scwidget.setVolume(0); + localStorage["firetableVol"] = 0; } - } - } - $("#mainqueuestuff").css("display", "block"); - $("#filterMachine").css("display", "block"); - $("#addbox").css("display", "none"); - $("#cancelqsearch").hide(); - $("#qControlButtons").show(); - } -}; -firetable.emojis = { - h: function() { - $(".pickerResult").show(); - $("#pickerResults h3").show(); - }, - n: function(p, q) { - var e = p.attr("data-alternative-name"); - return ($(p).text().toLowerCase().indexOf(q) >= 0) || (e != null && e.toLowerCase().indexOf(q) >= 0) - }, - sec: function(sec) { - firetable.debug && console.log('emoji sec:', sec); - var selectedSec = $("#pickerNav > .on"); - var thething = sec.substr(1); - console.log(thething); - if (selectedSec.length) { - firetable.debug && console.log("already selected sec"); - if (selectedSec[0].id == sec) { - firetable.debug && console.log("toggle selected... back to FULL LIST"); - $("#" + selectedSec[0].id).removeClass("on"); - $("#pickerContents div").show(); - } else { - //new sec selected - $("#" + selectedSec[0].id).removeClass("on"); - $("#" + selectedSec[0].id.substr(1)).hide(); - $("#" + sec).addClass("on"); - $("#" + thething).show(); - } - } else { - firetable.debug && console.log("first select"); - $("#" + sec).addClass("on"); - $("#pickerContents div").hide(); - $("#" + thething).show(); - } - }, - niceSearch: function(val) { - if (val.length == 0) { - firetable.emojis.h(); - return - } else { - var isvisible = $("#pickerResults h3").is(":visible"); - if (isvisible) $("#pickerResults h3").hide(); - } - val = val.toLowerCase(); - $(".pickerResult").each(function(p, q) { - if (firetable.emojis.n($(q), val)) { - $(q).show() - } else { - $(q).hide() - } - }); - // simplebar scroll update? - } -}; + if (muted) $("#volstatus").addClass("on"); + else $("#volstatus").removeClass("on"); + $("#volstatus i").html(icon); + localStorage["firetableMute"] = muted; + }, + pview: function (id, fromSearch, type, fromHist) { + if (firetable.preview == id) { + //already previewing this. stop and resume regular song + clearTimeout(firetable.ptimeout); + firetable.ptimeout = null; + $("#pv" + firetable.preview).html(""); + $("#pvbar" + firetable.preview).css("background-image", "none"); + clearInterval(firetable.movePvBar); + firetable.movePvBar = null; + firetable.preview = false; + //start regular song + var nownow = Date.now(); + var timeSince = nownow - firetable.song.started; + if (timeSince <= 0) timeSince = 0; + + var secSince = Math.floor(timeSince / 1000); + var timeLeft = firetable.song.duration - secSince; + if (firetable.song.type == 1) { + if (!firetable.preview) { + if (firetable.scLoaded) firetable.scwidget.pause(); + if (!firetable.disableMediaPlayback) player.loadVideoById(firetable.song.cid, secSince, "large"); + } + } else if (firetable.song.type == 2) { + if (!firetable.preview) { + if (firetable.ytLoaded) player.stopVideo(); + firetable.scSeek = timeSince; + if (!firetable.disableMediaPlayback) + firetable.scwidget.load("http://api.soundcloud.com/tracks/" + firetable.song.cid, { + auto_play: true, + }); + } + } + } else { + if (firetable.preview) { + $("#pv" + firetable.preview).html(""); + $("#pvbar" + firetable.preview).css("background-image", "none"); + } -firetable.utilities = { - getEmojiMap: function() { - firetable.emojiMap = {}; - (async function() { - urls = [ - "https://unpkg.com/unicode-emoji-json@0.3.0/data-by-group.json", - "https://unpkg.com/emojilib@3.0.4/dist/emoji-en-US.json" - ]; - duhdoymojis = { - '☕': ['coffee'], - '🚩': ['triangular_flag_on_post'], - '👋': ['wave'], - '🆔': ['id'], - '📈': ['chart_with_upwards_trend'], - '🚨': ['rotating_light'], - '🌧️': ['rain'], - '✅': ['white_check_mark'], - '🛰️': ['artificial_satellite'], - '🍵': ['tea'], - '❤️': ['heart'], - '🌊': ['ocean'], - '💦': ['splash'], - '💩': ['poop'], - '💯': ['100'], - '💨': ['dash'], - '🤡': ['clown'], - '🥱': ['yawn'], - '🙂': ['smile'], - '👌': ['ok'], - '💥': ['boom'], - '🍺': ['beer'], - '🍻': ['beers'], - '🥃': ['whiskey'], - '🌨️': ['snow'], - }; - try { - const requests = urls.map((url) => fetch(url)); - const responses = await Promise.all(requests); - const promises = responses.map((response) => response.json()); - const data = await Promise.all(promises); - for (const [category, emojisArr] of Object.entries(data[0])) { - let catid = category.replace(/[\s&]+/g, '_').toLowerCase(); - $('#pickerNav').append('' + emojisArr[0].emoji + ''); - $('#pickerContents').append('

' + category + '

'); - for (let i in emojisArr) { - firetable.emojiMap[emojisArr[i].slug] = emojisArr[i].emoji; - var words = ""; - words += (data[1][emojisArr[i].emoji] !== undefined) ? data[1][emojisArr[i].emoji].join(',') : ""; - words += (duhdoymojis[emojisArr[i].emoji] !== undefined) ? ','+duhdoymojis[emojisArr[i].emoji].join(',') : ""; - $("#picker" + catid).append('' + emojisArr[i].emoji + ''); - } - for (let i in duhdoymojis) { - for (let j in duhdoymojis[i]) { - firetable.emojiMap[duhdoymojis[i][j]] = i; - } - } - } - twemoji.parse(document.getElementById("pickerNav")); - } catch (err) {}; - })() - }, - hexToRGB: function(hex) { - var shorthandRegex = /^#?([a-f\d])([a-f\d])([a-f\d])$/i; - hex = hex.replace(shorthandRegex, function(m, r, g, b) { - return r + r + g + g + b + b; - }); + firetable.preview = id; + if (fromSearch) { + var cid = id.slice(5); + } else { + var cid = firetable.queue[id].cid; + } - var result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex); - return result ? { - r: parseInt(result[1], 16), - g: parseInt(result[2], 16), - b: parseInt(result[3], 16) - } : null; - }, - wrapText: function(context, text, x, y, maxWidth, lineHeight) { - var words = text.split(' '); - var line = ''; - var lines = 0; - - for (var n = 0; n < words.length; n++) { - var testLine = line + words[n] + ' '; - var metrics = context.measureText(testLine); - var testWidth = metrics.width; - if (testWidth > maxWidth && n > 0) { - context.fillText(line, x, y); - line = words[n] + ' '; - y += lineHeight; - lines++; - } else { - line = testLine; - } - } - context.fillText(line, x, y); - return lines; - }, - emojiShortnamestoUnicode: function(str) { - var res = str.replace(/\:(.*?)\:/g, function(x) { - var response = x; - var shortname = x.replace(/\:/g, ""); - if (firetable.emojiMap[shortname]) { - response = "" + firetable.emojiMap[shortname] + ""; - } else if (shortname == "rohn") { - response = ""; - } - return response; - }); - return res; - }, - playSound: function(filename) { - if (firetable.playBadoop) { - document.getElementById("audilert").setAttribute('src', filename + ".mp3"); - } - }, - desktopNotify: function(chatData, namebo) { - if (Notification) { - if (Notification.permission !== "granted") { - Notification.requestPermission(); - } else { - var notification = new Notification(namebo, { - icon: "https://indiediscotheque.com/robots/" + chatData.id + namebo + ".png?size=110x110", - body: chatData.txt, - }); - } - } - }, - screenUp: function() { - $('body').removeClass('screen'); - }, - screenDown: function() { - $('body').addClass('screen'); - }, - isChatPrettyMuchAtBottom: function() { - var scrollable = chatScroll.contentEl.scrollHeight - chatScroll.el.clientHeight; - var scrolled = chatScroll.contentWrapperEl.scrollTop; - console.log('near bottom?', scrollable, scrolled); - return (Math.abs(scrollable - scrolled) <= 25); - }, - scrollToBottom: function() { - chatScroll.contentWrapperEl.scrollTop = chatScroll.contentEl.scrollHeight; - }, - htmlEscape: function(s, preserveCR) { - preserveCR = preserveCR ? ' ' : '\n'; - return ('' + s) /* Forces the conversion to string. */ - .replace(/&/g, '&') /* This MUST be the 1st replacement. */ - .replace(/'/g, '\\'') /* The 4 other predefined entities, required. */ - .replace(/"/g, '"') - .replace(//g, '>') - /* - You may add other replacements here for HTML only - (but it's not necessary). - Or for XML, only if the named entities are defined in its DTD. - */ - .replace(/\r\n/g, preserveCR) /* Must be before the next replacement. */ - .replace(/[\r\n]/g, preserveCR);; - }, - format_date: function(d) { - - var date = new Date(d); - - var month = date.getMonth() + 1; - var day = date.getDate(); - var year = date.getFullYear(); - - var formatted_date = month + "/" + day + "/" + year; - return formatted_date; - }, - format_time: function(d) { - var date = new Date(d); - - var hours1 = date.getHours(); - var ampm = "am"; - var hours = hours1; - if (hours1 >= 12) { - ampm = "pm"; - if (hours !== 12) hours = hours1 - 12; - } - if (hours == 0) hours = 12; - var minutes = date.getMinutes(); - var min = ""; - if (minutes > 9) { - min += minutes; - } else { - min += "0" + minutes; - } - return hours + ":" + min + "" + ampm; - }, - debounce(func, wait, immediate) { - var timeout; - return function() { - var context = this, - args = arguments; - var later = function() { - timeout = null; - if (!immediate) func.apply(context, args); - }; - var callNow = immediate && !timeout; - clearTimeout(timeout); - timeout = setTimeout(later, wait); - if (callNow) func.apply(context, args); - }; - }, - chatAt: function(element) { - element.bind("click", function() { - console.log(element); - var nameToAt; - if (element[0].className == "prson") { - nameToAt = $(this).find(".prsnName").text(); - } else if (element[0].className == "botson") { - nameToAt = $(this).next(".chatContent").find(".chatName").text(); - } else if (element[0].className == "chatName") { - nameToAt = $(this).text(); - } - $("#newchat").val(function(i, val) { - return val + "@" + nameToAt + " "; - }).focus(); - }) - }, - initAtLand: function() { - firetable.atLand = true; - firetable.atString = ""; - firetable.atUsers = ["everyone"]; - for (var user in ftapi.users) { - firetable.atUsers.push(ftapi.users[user].username); - } - firetable.atUsersFiltered = firetable.atUsers.sort(); - }, - updateAtLand: function() { - firetable.atUsersFiltered = firetable.atUsers.filter(user => user.toLowerCase().startsWith(firetable.atString.toLowerCase())).sort(); - $('#atPicker').html(''); - if (firetable.atUsersFiltered.length) { - for (var user of firetable.atUsersFiltered) { - $('
').appendTo('#atPicker'); - } - } else { - $('
No users match
').appendTo('#atPicker'); - } - }, - chooseAt: function(atPeep) { - var $chatText = $('#newchat'); - if (firetable.atString.length > 0) $chatText.val($chatText.val().slice(0, firetable.atString.length * -1)); - $chatText.val($chatText.val() + atPeep + " "); - firetable.utilities.exitAtLand(); - }, - exitAtLand: function() { - firetable.atLand = false; - firetable.atUsersFiltered = []; - firetable.atString = ""; - $('#atPicker').removeClass('show').html(''); - } -}; + if (firetable.ptimeout != null) { + clearTimeout(firetable.ptimeout); + firetable.ptimeout = null; + } + if (firetable.movePvBar != null) { + clearInterval(firetable.movePvBar); + firetable.movePvBar = null; + } + firetable.pvCount = 0; + firetable.ptimeout = setTimeout(function () { + firetable.ptimeout = null; + $("#pv" + firetable.preview).html(""); + $("#pvbar" + firetable.preview).css("background-image", "none"); + clearInterval(firetable.movePvBar); + firetable.movePvBar = null; + firetable.pvCount = 0; + firetable.preview = false; -firetable.ui = { - textToLinks: function(text, themeBox) { - var re = /(\b(https?|ftp|file):\/\/[-A-Z0-9+&@#\/%?=~_|!:,.;]*[-A-Z0-9+&@#\/%=~_|])/ig; - if (firetable.showImages && !themeBox) re = /(https?:\/\/(?![/|.|\w|\s|-]*(?:jpe?g|png|gif))[^" ]+)/g; - return text.replace(re, "$1"); - - return text; - }, - dubtrackImportFileSelect: function(evt) { - var files = evt.target.files; // FileList object - var file = files[0]; - // read the file contents - var reader = new FileReader(); - reader.readAsText(file); - reader.onload = function(event) { - try { - var allthestuff = event.currentTarget.result; - console.log(allthestuff); - firetable.dtImportName = firetable.ui.strip(allthestuff.split('

')[1].split('

')[0]); - var hams = allthestuff.split('
  • (.*)<\/li>/gm; - var matches = thingsRegex.exec(hams[i]) - var type = matches[2]; - var cid = matches[4]; - var name = firetable.ui.strip(matches[5]); - firetable.dtImportList.push({ - type: type, - cid: cid, - name: name - }); + //start regular song + var nownow = Date.now(); + var timeSince = nownow - firetable.song.started; + if (timeSince <= 0) timeSince = 0; + + var secSince = Math.floor(timeSince / 1000); + var timeLeft = firetable.song.duration - secSince; + if (firetable.song.type == 1) { + if (!firetable.preview) { + if (firetable.scLoaded) firetable.scwidget.pause(); + if (!firetable.disableMediaPlayback) + player.loadVideoById(firetable.song.cid, secSince, "large"); + } + } else if (firetable.song.type == 2) { + if (!firetable.preview) { + if (firetable.ytLoaded) player.stopVideo(); + firetable.scSeek = timeSince; + if (!firetable.disableMediaPlayback) + firetable.scwidget.load("http://api.soundcloud.com/tracks/" + firetable.song.cid, { + auto_play: true, + }); + } + } + }, 30 * 1000); + $("#pv" + id).html(""); + firetable.movePvBar = setInterval(function () { + var pcnt = (firetable.pvCount / 29) * 100; + firetable.pvCount += 0.2; + var pvcolr = "#222"; + if (fromHist) pvcolr = "#222"; + $("#pvbar" + firetable.preview).css( + "background-image", + "linear-gradient(90deg, rgba(244, 129, 11, 0.267) " + pcnt + "%, " + pvcolr + " " + pcnt + "%)" + ); + }, 200); + if (type == 1) { + if (firetable.scLoaded) firetable.scwidget.pause(); + if (!firetable.disableMediaPlayback) player.loadVideoById(cid, 0, "large"); + } else if (type == 2) { + if (firetable.ytLoaded) player.stopVideo(); + firetable.scSeek = 0; + if (!firetable.disableMediaPlayback) + firetable.scwidget.load("http://api.soundcloud.com/tracks/" + cid, { + auto_play: true, + }); + } } - console.log(firetable.dtImportList); - console.log(firetable.dtImportName); - if (firetable.dtImportList.length) { - $("#importDubResults").text("Ok... import " + firetable.dtImportName + " (" + firetable.dtImportList.length + " tracks)?") - $("#dubimportButton").show(); - } else { - $("#importDubResults").text("ERROR... NO TRAX?") - $("#dubimportButton").hide(); + }, + mergeLists: function (source, dest, sourceName) { + if (source == dest) { + //source and dest are the same, let's remove the duplicates + firetable.actions.removeDupesFromQueue(); + return; } - } catch (e) { - console.log(e); - $("#importDubResults").text("ERROR") - $("#dubimportButton").hide(); - } + if (dest == -1) { + // create new list if needed + var newname = firetable.utilities.format_date(Date.now()) + " Copy of " + sourceName; + var dest = ftapi.actions.createList(newname); + $("#listpicker").append('"); + } + ftapi.actions.mergeLists(source, dest, function () { + $("#mergeCompleted").show(); + $("#mergeHappening").hide(); + }); + }, + queueFromLink: function (link) { + if (link.match(/youtube.com\/watch/)) { + //youtube + firetable.debug && console.log("yt"); + function getQueryStringValue(str, key) { + return unescape( + str.replace( + new RegExp( + "^(?:.*[&\\?]" + escape(key).replace(/[\.\+\*]/g, "\\$&") + "(?:\\=([^&]*))?)?.*$", + "i" + ), + "$1" + ) + ); + } + var therealid = getQueryStringValue(link, "v"); + if (therealid) { + function keyWordsearch() { + gapi.client.setApiKey(ftconfigs.youtubeKey); + gapi.client.load("youtube", "v3", function () { + makeRequest(); + }); + } - }; - - }, - strip: function(html) { - var doc = firetable.parser.parseFromString(html, 'text/html'); - return doc.body.textContent || ""; - }, - hidePlayerControls: function() { - $("head").append(""); - }, - showPlayerControls: function() { - $(".playerControlsHider").remove(); - }, - showImages: function(chatTxt) { - if (firetable.showImages) { - var imageUrlRegex = /((http(s?):)([/|.|\w|\s|-])*\.(?:jpe?g|gif|png))/g; - var hasImage = chatTxt.search(imageUrlRegex) >= 0; - if (hasImage) { - chatTxt = chatTxt.replace(imageUrlRegex, function(imageUrl) { - var chatImage = new Image(); - chatImage.onload = function() { - if (firetable.utilities.isChatPrettyMuchAtBottom()) firetable.utilities.scrollToBottom(); - } - chatImage.src = imageUrl; - return '×' - }); + function makeRequest() { + var request = gapi.client.youtube.videos.list({ + id: therealid, + part: "snippet", + maxResults: 1, + }); + request.execute(function (response) { + firetable.debug && console.log("queue from link:", response); + if (response.result) { + if (response.result.items) { + if (response.result.items.length) { + var goodtitle = response.result.items[0].snippet.title; + var yargo = response.result.items[0].snippet.title.split(" - "); + var sartist = yargo[0]; + var stitle = yargo[1]; + if (!stitle) { + // yt title not formatted artist - title. use uploader name instead as artist + stitle = sartist; + sartist = response.result.items[0].snippet.channelTitle.replace(" - Topic", ""); + } + goodtitle = sartist + " - " + stitle; + firetable.actions.queueTrack(response.result.items[0].id, goodtitle, 1); + } + } + } + }); + } + keyWordsearch(); + } + } else if (link.match(/soundcloud.com/)) { + //soundcloud + firetable.debug && console.log("sc"); + var getComments = function (track) { + return SC.get("tracks/" + track.id); + }; - } - } - return chatTxt; - }, - loginLinkToggle: function(id) { - $("#formlinks").find(".selected").removeClass("selected"); - $("#" + id).addClass("selected"); - }, - loginEventsInit: function() { - $("#resetpass").bind("click", function() { - $("#logscreen").css("display", "none"); - $("#createscreen").css("display", "none"); - $("#resetscreen").css("display", "block"); - firetable.ui.loginLinkToggle($(this).attr('id')); - }); - $("#loginlink").bind("click", function() { - $("#logscreen").css("display", "block"); - $("#createscreen").css("display", "none"); - $("#resetscreen").css("display", "none"); - firetable.ui.loginLinkToggle($(this).attr('id')); - }); - $("#signuplink").bind("click", function() { - $("#logscreen").css("display", "none"); - $("#createscreen").css("display", "block"); - $("#resetscreen").css("display", "none"); - firetable.ui.loginLinkToggle($(this).attr('id')); - }); - $("#loginpass").bind("keyup", function(e) { - if (e.which == 13) { - var email = $("#loginemail").val(); - var pass = $("#loginpass").val(); - $("#loginemail").val(""); - $("#loginpass").val(""); - firetable.actions.logIn(email, pass); - } - }); - $("#newpass2").bind("keyup", function(e) { - if (e.which == 13) { - var email = $("#newemail").val(); - var pass = $("#newpass").val(); - var pass2 = $("#newpass2").val(); - var username = $("#newusername").val(); - if (pass == pass2) { - firetable.actions.signUp(email, pass, username); - } else { - alert("Those passwords do not match!"); + firetable.actions.resolveSCLink(link, function (tracks) { + if (tracks) { + var yargo = tracks.title.split(" - "); + var sartist = yargo[0]; + var stitle = yargo[1]; + if (!stitle) { + stitle = sartist; + sartist = tracks.user.username; + } + var goodTitle = sartist + " - " + stitle; + firetable.actions.queueTrack(tracks.id, goodTitle, 2); + } + }); + //SC.resolve(link).then(getComments).then(listComments); } - } - }); - $("#theAddress").bind("keyup", function(e) { - if (e.which == 13) { - var email = $("#theAddress").val(); - firetable.debug && console.log("reset email return"); - ftapi.actions.resetPassword(email, function(error) { - var errorCode = error.code; - var errorMessage = error.message; - if (errorCode === 'auth/wrong-password') { - alert('Wrong password.'); - } else { - alert(errorMessage); - } - firetable.debug && console.log('send pass reset error:', error); - }); - alert("Reset email sent. Click the reset link when it arrives thanks."); - } - }); - $("#createAccountBttn").bind("click", function() { - var email = $("#newemail").val(); - var pass = $("#newpass").val(); - var pass2 = $("#newpass2").val(); - var termsAgreedTo = $("#agreetoterms").is(":checked"); - var username = $("#newusername").val(); - if (!termsAgreedTo) { - alert("You must read and agree to the Terms of Service and Privacy Policy before you can create an account."); - } else if (pass != pass2) { - alert("Those passwords do not match!"); - } else { - firetable.actions.signUp(email, pass, username); - } - }); - $("#resetPassBttn").bind("click", function() { - var email = $("#theAddress").val(); - firetable.debug && console.log("reset email click button"); - ftapi.actions.resetPassword(email, function(error) { - var errorCode = error.code; - var errorMessage = error.message; - if (errorCode === 'auth/wrong-password') { - alert('Wrong password.'); - } else { - alert(errorMessage); + }, + resolveSCLink: function (link, callback) { + var importantStuff = link.replace("https://soundcloud.com/", ""); + importantStuff = importantStuff.replace("http://soundcloud.com/", ""); + $.ajax({ + url: "https://thompsn.com/resolvesc/?q=" + importantStuff, + type: "GET", + dataType: "json", + success: function (res) { + console.log(res); + callback(res.response); + }, + }); + }, + scGet: function (type, q, callback) { + $.ajax({ + url: "https://thompsn.com/soundcloud/?type=" + type + "&q=" + q, + type: "GET", + dataType: "json", + success: function (res) { + console.log(res); + callback(res.response); + }, + }); + }, + updateQueue: function () { + //this fires when someone drags a song to a new spot in the queue + var arr = $("#mainqueue > div") + .map(function () { + var theid = this.id; + var idraw = theid.slice(5); + return idraw; + }) + .get(); + ftapi.actions.reorderList(arr, firetable.preview, function (changePV) { + if (changePV) firetable.preview = changePV; + }); + }, + shuffleQueue: function () { + ftapi.actions.shuffleList(firetable.preview, function (changePV) { + if (changePV) firetable.preview = changePV; + }); + }, + removeDupesFromQueue: function () { + ftapi.actions.removeDuplicatesFromList(); + $("#mergeCompleted").show(); + $("#mergeHappening").hide(); + }, + editTagsPrompt: function (songid) { + var song = firetable.queue[songid]; + var $pvbar = $('#mainqueue .pvbar[data-key="' + songid + '"]'); + $("#mainqueue .pvbar.editing").removeClass("editing"); + $(".tagPromptBox").remove(); + $pvbar.addClass("editing"); + var $tags = $tagEditorTemplate.clone().appendTo($pvbar); + $tags.find(".tagMachine").val(song.name); + if (song.type == 1) { + $tags.find(".tagSongLink").attr("href", "https://youtube.com/watch?v=" + song.cid); + } else if (song.type == 2) { + firetable.actions.scGet("tracks", song.cid, function (tracks) { + if (tracks.permalink_url) { + $tags.find(".tagSongLink").attr("href", tracks.permalink_url); + } else { + $tags + .find(".tagSongLink") + .attr("href", "http://howtojointheindiediscothequewaitlist.com/ThisSongIsBroken?thanks=true"); + } + }); } - firetable.debug && console.log('send pass reset error:', error); - }); - alert("Reset email sent. Click the reset link when it arrives thanks."); - }); - $("#loginBttn").bind("click", function() { - var email = $("#loginemail").val(); - var pass = $("#loginpass").val(); - $("#loginemail").val(""); - $("#loginpass").val(""); - firetable.actions.logIn(email, pass); - }); - }, - loginEventsDestroy: function() { - $("#resetpass").off("click"); - $("#loginlink").off("click"); - $("#signuplink").off("click"); - $("#loginpass").off("keyup"); - $("#newpass2").off("keyup"); - $("#theAddress").off("keyup"); - $("#createAccountBttn").off("click"); - $("#resetPassBttn").off("click"); - $("#loginBttn").off("click"); - }, - init: function() { - - $('#mainqueue').sortable({ - start: function(event, ui) { - var start_pos = ui.item.index(); - ui.item.data('start_pos', start_pos); - }, - change: function(event, ui) { - - }, - update: function(event, ui) { - firetable.debug && console.log("UPDATE"); - firetable.actions.updateQueue(); - } - }); - //GET SETTINGS FROM LOCALSTORAGE - var disableMediaPlayback = localStorage["firetableDisableMedia"]; - if (typeof disableMediaPlayback == "undefined") { - localStorage["disableMediaPlayback"] = false; - firetable.disableMediaPlayback = false; - $("#mediaDisableToggle").prop("checked", false); - } else { - disableMediaPlayback = JSON.parse(disableMediaPlayback); - firetable.disableMediaPlayback = disableMediaPlayback; - $("#mediaDisableToggle").prop("checked", disableMediaPlayback); - if (disableMediaPlayback) { - firetable.ui.hidePlayerControls(); - } - } - var showImages = localStorage["firetableShowImages"]; - if (typeof showImages == "undefined") { - localStorage["firetableShowImages"] = false; - firetable.showImages = false; - $("#showImagesToggle").prop("checked", false); - } else { - showImages = JSON.parse(showImages); - firetable.showImages = showImages; - $("#showImagesToggle").prop("checked", showImages); - } - var showAvatars = localStorage["firetableShowAvatars"]; - if (typeof showAvatars == "undefined") { - localStorage["firetableShowAvatars"] = true; - firetable.showAvatars = true; - $("#showAvatarsToggle").prop("checked", true); - } else { - showAvatars = JSON.parse(showAvatars); - firetable.showAvatars = showAvatars; - $("#showAvatarsToggle").prop("checked", showAvatars); - if (showAvatars == false) { - document.getElementById("actualChat").classList.add("avatarsOff"); - } - } - var playBadoop = localStorage["firetableBadoop"]; - if (typeof playBadoop == "undefined") { - localStorage["firetableBadoop"] = true; - firetable.playBadoop = true; - $("#badoopToggle").prop("checked", true); - } else { - playBadoop = JSON.parse(playBadoop); - firetable.playBadoop = playBadoop; - $("#badoopToggle").prop("checked", playBadoop); - } - var dtnmt = localStorage["firetableDTNM"]; - if (typeof dtnmt == "undefined") { - localStorage["firetableDTNM"] = false; - firetable.desktopNotifyMentions = false; - $("#desktopNotifyMentionsToggle").prop("checked", false); - } else { - dtnmt = JSON.parse(dtnmt); - firetable.desktopNotifyMentions = dtnmt; - $("#desktopNotifyMentionsToggle").prop("checked", dtnmt); - } - var screenControl = localStorage["firetableScreenControl"]; - if (typeof screenControl == "undefined") { - localStorage["firetableScreenControl"] = "sync"; - firetable.screenControl = "sync"; - $("#screenControlTog" + firetable.screenControl).prop("checked", true); - } else { - firetable.screenControl = screenControl; - $("#screenControlTog" + firetable.screenControl).prop("checked", true); - if (screenControl == "on") { - firetable.utilities.screenDown(); - } else if (screenControl == "off") { - firetable.utilities.screenUp(); - } else if (screenControl == "sync") { - if (firetable.screenSyncPos) { - firetable.utilities.screenDown(); - } else { - firetable.utilities.screenUp(); - } - } - } - var $historyItem = $('#thehistory .pvbar').remove(); - ftapi.events.on('newHistory', function(data) { - if (data.img == "img/idlogo.png" && ftconfigs.defaultAlbumArtUrl.length) data.img = ftconfigs.defaultAlbumArtUrl; - var firstpart = "yt"; - if (data.type == 2) firstpart == "sc"; - var pkey = firstpart + "cid" + data.cid; - var $histItem = $historyItem.clone(); - $histItem.attr('id', "pvbar" + pkey); - $histItem.attr("data-key", pkey); - $histItem.attr("data-cid", data.cid); - $histItem.attr("data-type", data.type); - - $histItem.find('.previewicon').attr('id', "pv" + pkey).on('click', function() { - firetable.actions.pview( - $(this).closest('.pvbar').attr('data-key'), - true, - $(this).closest('.pvbar').attr('data-type'), - true - ); - }); - $histItem.find('.histlink').attr({ - 'href': data.url, - 'tabindex': "-1" - }).text(data.artist + " - " + data.title); - $histItem.find('.histdj').text(data.dj); - $histItem.find('.histdate').text(firetable.utilities.format_date(data.when)); - $histItem.find('.histtime').text(firetable.utilities.format_time(data.when)); - $histItem.find('.histeal').attr('id', "apv" + data.type + data.cid).on('click', function() { - firetable.actions.queueTrack( - $(this).closest('.pvbar').attr('data-cid'), - firetable.utilities.htmlEscape($(this).closest('.pvbar').find('.histlink').text()), - $(this).closest('.pvbar').attr('data-type'), - true - ); - }); - $histItem.find('.histart').css('background-image', 'url(' + data.img + ')'); - $histItem.prependTo("#thehistory"); - // simplebar scroll update? - }); - ftapi.events.on("newTheme", function(data) { - if (!data) { - //no theme - $("#currentTheme").text("!suggest a theme"); - } else { - var txtOut = firetable.ui.strip(data); - txtOut = firetable.ui.textToLinks(txtOut, true); - txtOut = firetable.utilities.emojiShortnamestoUnicode(txtOut); - txtOut = txtOut.replace(/\`(.*?)\`/g, function(x) { - return "" + x.replace(/\`/g, "") + ""; - }); - $("#currentTheme").html(txtOut); - twemoji.parse(document.getElementById("currentTheme")); - } - }); - ftapi.events.on("tagUpdate", function(data) { - firetable.debug && console.log("TAG UPDATE", data); - firetable.tagUpdate = data; - if (firetable.song) { - if (firetable.song.cid == data.cid && data.adamData.track_name) { - $("#track").text(firetable.ui.strip(data.adamData.track_name)); - $("#artist").text(firetable.ui.strip(data.adamData.artist)); - var nicename = firetable.song.djname; - var showPlaycount = false; - if (data.adamData.playcount) { - if (data.adamData.playcount > 0) { - showPlaycount = true; - } - } - if (data.adamData.last_play) { - $("#lastPlay").text("last " + firetable.utilities.format_date(data.adamData.last_play) + " by " + data.adamData.last_play_dj); - } else { - $("#lastPlay").text(""); - } - if (data.adamData.first_play) { - $("#firstPlay").text("first " + firetable.utilities.format_date(data.adamData.first_play) + " by " + data.adamData.first_play_dj); - } else { - $("#firstPlay").text(""); - } - var doTheScrollThing = firetable.utilities.isChatPrettyMuchAtBottom(); - if (showPlaycount) { - $("#playCount").text(data.adamData.playcount + " plays"); - $(".npmsg" + data.cid).last().html("
    DJ " + nicename + " started playing " + data.adamData.track_name + " by " + data.adamData.artist + "
    This song has been played " + data.adamData.playcount + " times.
    "); - } else { - $("#playCount").text(""); - $(".npmsg" + data.cid).last().html("
    DJ " + nicename + " started playing " + data.adamData.track_name + " by " + data.adamData.artist + "
    "); - } - if (doTheScrollThing) firetable.utilities.scrollToBottom(); - } - } - }); + firetable.debug && console.log("edit tags song id:", songid); + firetable.songToEdit = { + song: song, + key: songid, + }; + }, + importList(id, name, type) { + //time to IMPORT SOME LISTS! + $("#overlay").removeClass("show"); + $("#importResults").html(""); + $("#plMachine").val(""); + if (type == 1) { + //youtube + var finalList = []; + + function keyWordsearch(pg) { + gapi.client.setApiKey(ftconfigs.youtubeKey); + gapi.client.load("youtube", "v3", function () { + makeRequest(pg); + }); + } + function makeRequest(pg) { + if (pg) { + var request = gapi.client.youtube.playlistItems.list({ + playlistId: id, + maxResults: 50, + part: "snippet", + pageToken: pg, + }); + } else { + var request = gapi.client.youtube.playlistItems.list({ + playlistId: id, + maxResults: 50, + part: "snippet", + }); + } + request.execute(function (response) { + if (response.items.length) { + for (var idx = 0; idx < response.items.length; idx++) { + finalList.push(response.items[idx]); + } + } + if (response.nextPageToken) { + if (response.nextPageToken != "") keyWordsearch(response.nextPageToken); + } else { + firetable.debug && console.log(finalList); + var listid = ftapi.actions.createList(name); + $("#listpicker").append( + '" + ); + for (var i = 0; i < finalList.length; i++) { + var goodTitle = finalList[i].snippet.title; + // can't use youtube uploader name to fix tags here because YOUTUBE DECIDED NOT TO INCLUDE THAT INFORMATION >:o + if (goodTitle !== "Private video" && goodTitle !== "Deleted video") { + ftapi.actions.addToList(1, goodTitle, finalList[i].snippet.resourceId.videoId, listid); + } + } + } + }); + } + keyWordsearch(); + } else if (type == 2) { + firetable.actions.scGet("playlists", id, function (listinfo) { + firetable.debug && console.log("sc tracks:", listinfo.tracks); + var listid = ftapi.actions.createList(name); + $("#listpicker").append( + '" + ); + for (var i = 0; i < listinfo.tracks.length; i++) { + if (listinfo.tracks[i].title) { + var yargo = listinfo.tracks[i].title.split(" - "); + var sartist = yargo[0]; + var stitle = yargo[1]; + if (!stitle) { + stitle = sartist; + sartist = listinfo.tracks[i].user.username; + } + var goodTitle = sartist + " - " + stitle; + } else { + var goodTitle = "Unknown"; + } - ftapi.events.on('newSong', function(data) { - $("#playCount").text(""); - $("#lastPlay").text(""); - $("#firstPlay").text(""); - window.dispatchEvent(new Event('resize')); - $("#cloud_with_rain").removeClass("on"); - $("#fire").removeClass("on"); - $("#timr").countdown("destroy"); - if (firetable.moveBar != null) { - clearInterval(firetable.moveBar); - firetable.moveBar = null; - } - if (data.image == "img/idlogo.png" && ftconfigs.defaultAlbumArtUrl.length) data.image = ftconfigs.defaultAlbumArtUrl; - $("#prgbar").css("background", "#151515"); - var showPlaycount = false; - if (firetable.tagUpdate) { - if (data.cid == firetable.tagUpdate.cid && firetable.tagUpdate.adamData.track_name) { - data.title = firetable.tagUpdate.adamData.track_name; - data.artist = firetable.tagUpdate.adamData.artist; - if (firetable.tagUpdate.adamData.last_play) { - $("#lastPlay").text("last " + firetable.utilities.format_date(firetable.tagUpdate.adamData.last_play) + " by " + firetable.tagUpdate.adamData.last_play_dj); - } - if (firetable.tagUpdate.adamData.first_play) { - $("#firstPlay").text("first " + firetable.utilities.format_date(firetable.tagUpdate.adamData.first_play) + " by " + firetable.tagUpdate.adamData.first_play_dj); - } - if (firetable.tagUpdate.adamData.playcount) { - if (firetable.tagUpdate.adamData.playcount > 0) { - showPlaycount = true; - $("#playCount").text(firetable.tagUpdate.adamData.playcount + " plays"); - } - } + ftapi.actions.addToList(2, goodTitle, listinfo.tracks[i].id, listid); + } + }); } - } - $("#track").text(firetable.ui.strip(data.title)); - $("#artist").text(firetable.ui.strip(data.artist)); - $("#songlink").attr("href", data.url); - $("#albumArt").css("background-image", "url(" + data.image + ")"); - var nownow = Date.now(); - var timeSince = nownow - data.started; - if (timeSince <= 0) timeSince = 0; - var secSince = Math.floor(timeSince / 1000); - var timeLeft = data.duration - secSince; - firetable.song = data; - firetable.debug && console.log("NEW TRACK", data); - firetable.debug && console.log('time since:', timeSince); - if (data.type == 1) { - $("#scScreen").hide(); - $("#songlink").html(''); - } else if (data.type == 2) { - $("#scScreen").show(); - $("#songlink").html(''); - var biggerImg = data.image.replace('-large', '-t500x500'); - firetable.scImg = biggerImg; - $("#albumArt").css("background-image", "url(" + biggerImg + ")") - try { - setup(biggerImg); - } catch (e) { - firetable.debug && console.log('big image error:', e) + }, + bumpSongInQueue: function (songid) { + ftapi.actions.moveTrackToTop(songid, firetable.preview, function (changePV) { + if (changePV) firetable.preview = changePV; + }); + }, + signUp: function (email, password, username) { + firetable.debug && console.log("signup"); + ftapi.actions.signUp(email, password, username, function (error) { + alert(error); + }); + }, + deleteSong: function (id) { + ftapi.actions.deleteTrack(id); + }, + uidLookup: function (name) { + var match = false; + var usrs = ftapi.users; + for (var key in usrs) { + if (usrs.hasOwnProperty(key)) { + if (ftapi.users[key].username) { + if (ftapi.users[key].username == name) { + match = key; + } + } + } } - } - if (data.type == 1 && firetable.ytLoaded) { - if (!firetable.preview) { - if (firetable.scLoaded) firetable.scwidget.pause(); - if (!firetable.disableMediaPlayback) player.loadVideoById(data.cid, secSince, "large"); - var thevolactual = $("#slider").slider("value"); - player.setVolume(thevolactual); - firetable.scwidget.setVolume(thevolactual); + if (!match && ftapi.users[name]) match = name; + return match; + }, + grab: function () { + if (firetable.song.cid != 0) { + var title = firetable.song.artist + " - " + firetable.song.title; + firetable.actions.queueTrack(firetable.song.cid, title, firetable.song.type, true); } - } else if (data.type == 2 && firetable.scLoaded) { - if (!firetable.preview) { - if (firetable.ytLoaded) player.stopVideo(); - firetable.scSeek = timeSince; - if (!firetable.disableMediaPlayback) firetable.scwidget.load("http://api.soundcloud.com/tracks/" + data.cid, { - auto_play: true, - single_active: false, - callback: function() { - var thevolactual = localStorage["firetableVol"]; - player.setVolume(thevolactual); - firetable.scwidget.setVolume(thevolactual); - } - }); + }, + unban: function (userid) { + ftapi.actions.unbanUser(userid); + }, + reloadtrack: function () { + $("#reloadtrack").addClass("on working"); + //start regular song + var nownow = Date.now(); + var timeSince = nownow - firetable.song.started; + if (timeSince <= 0) timeSince = 0; + var secSince = Math.floor(timeSince / 1000); + var timeLeft = firetable.song.duration - secSince; + if (firetable.song.type == 1) { + if (!firetable.preview) { + if (firetable.scLoaded) firetable.scwidget.pause(); + if (!firetable.disableMediaPlayback) player.loadVideoById(firetable.song.cid, secSince, "large"); + } + } else if (firetable.song.type == 2) { + if (!firetable.preview) { + if (firetable.ytLoaded) player.stopVideo(); + firetable.scSeek = timeSince; + if (!firetable.disableMediaPlayback) + firetable.scwidget.load( + "http://api.soundcloud.com/tracks/" + firetable.song.cid, + { + auto_play: true, + }, + function () { + $("#reloadtrack").removeClass("on working"); + } + ); + } } - } - if (data.cid != 0) { - var nicename = data.djid; - if (ftapi.users[data.djid]) { - if (ftapi.users[data.djid].username) nicename = ftapi.users[data.djid].username; + }, + queueTrack: function (cid, name, type, tobottom) { + var info = { + type: type, + name: name, + cid: cid, + }; + $("#apv" + type + cid) + .find(".material-icons") + .text("check"); + $("#apv" + type + cid).css("color", firetable.orange); + $("#apv" + type + cid).css("pointer-events", "none"); + setTimeout(function () { + $("#apv" + type + cid) + .find(".material-icons") + .text("playlist_add"); + $("#apv" + type + cid).removeAttr("style"); + }, 3000); + + var cuteid = ftapi.actions.addToList(type, name, cid, false, function () { + firetable.debug && console.log("queue track id:", cuteid); + if (!tobottom) firetable.actions.bumpSongInQueue(cuteid); + }); + + if (firetable.preview) { + if (firetable.preview.slice(0, 5) == "ytcid" || firetable.preview.slice(0, 5) == "sccid") { + $("#pv" + firetable.preview).html(""); + clearTimeout(firetable.ptimeout); + firetable.ptimeout = null; + $("#pvbar" + firetable.preview).css("background-image", "none"); + clearInterval(firetable.movePvBar); + firetable.movePvBar = null; + firetable.preview = false; + //start regular song + var nownow = Date.now(); + var timeSince = nownow - firetable.song.started; + if (timeSince <= 0) timeSince = 0; + var secSince = Math.floor(timeSince / 1000); + var timeLeft = firetable.song.duration - secSince; + if (firetable.song.type == 1) { + if (!firetable.preview) { + if (firetable.scLoaded) firetable.scwidget.pause(); + if (!firetable.disableMediaPlayback) + player.loadVideoById(firetable.song.cid, secSince, "large"); + } + } else if (firetable.song.type == 2) { + if (!firetable.preview) { + if (firetable.ytLoaded) player.stopVideo(); + firetable.scSeek = timeSince; + if (!firetable.disableMediaPlayback) + firetable.scwidget.load("http://api.soundcloud.com/tracks/" + firetable.song.cid, { + auto_play: true, + }); + } + } + } } - if (firetable.nonpmsg) { - firetable.nonpmsg = false; + $("#mainqueuestuff").css("display", "block"); + $("#filterMachine").css("display", "block"); + $("#addbox").css("display", "none"); + $("#cancelqsearch").hide(); + $("#qControlButtons").show(); + }, +}; + +firetable.emojis = { + h: function () { + $(".pickerResult").show(); + $("#pickerResults h3").show(); + }, + n: function (p, q) { + var e = p.attr("data-alternative-name"); + return $(p).text().toLowerCase().indexOf(q) >= 0 || (e != null && e.toLowerCase().indexOf(q) >= 0); + }, + sec: function (sec) { + firetable.debug && console.log("emoji sec:", sec); + var selectedSec = $("#pickerNav > .on"); + var thething = sec.substr(1); + console.log(thething); + if (selectedSec.length) { + firetable.debug && console.log("already selected sec"); + if (selectedSec[0].id == sec) { + firetable.debug && console.log("toggle selected... back to FULL LIST"); + $("#" + selectedSec[0].id).removeClass("on"); + $("#pickerContents div").show(); + } else { + //new sec selected + $("#" + selectedSec[0].id).removeClass("on"); + $("#" + selectedSec[0].id.substr(1)).hide(); + $("#" + sec).addClass("on"); + $("#" + thething).show(); + } } else { - var doTheScrollThing = firetable.utilities.isChatPrettyMuchAtBottom(); - if (showPlaycount) { - $("#chats").append("
    DJ " + nicename + " started playing " + data.title + " by " + data.artist + "
    This song has been played " + firetable.tagUpdate.adamData.playcount + " times.
    ") - } else { - $("#chats").append("
    DJ " + nicename + " started playing " + data.title + " by " + data.artist + "
    ") - } - if (doTheScrollThing) firetable.utilities.scrollToBottom(); - firetable.lastChatPerson = false; - firetable.lastChatId = false; + firetable.debug && console.log("first select"); + $("#" + sec).addClass("on"); + $("#pickerContents div").hide(); + $("#" + thething).show(); } - - } - $("#timr").countdown({ - until: timeLeft, - compact: true, - description: "", - format: "MS" - }); - firetable.moveBar = setInterval(function() { - var now = Date.now(); - var sofar = now - firetable.song.started; - var pcnt = (sofar / (firetable.song.duration * 1000)) * 100; - $("#prgbar").css("background", "linear-gradient(90deg, " + firetable.color + " " + pcnt + "%, #151515 " + pcnt + "%)"); - }, 500); - }); - ftapi.events.on("screenStateChanged", function(data) { - firetable.debug && console.log('thescreen:', data); - firetable.screenSyncPos = data; - if (firetable.screenControl == "sync") { - if (data) { - firetable.utilities.screenDown(); + }, + niceSearch: function (val) { + if (val.length == 0) { + firetable.emojis.h(); + return; } else { - firetable.utilities.screenUp(); + var isvisible = $("#pickerResults h3").is(":visible"); + if (isvisible) $("#pickerResults h3").hide(); } - } - }); - ftapi.events.on("danceStateChanged", function(data) { - firetable.debug && console.log('dance check:', data); - if (data) { - $("#deck").addClass("dance"); - } else { - $("#deck").removeClass("dance"); - } - }); - ftapi.events.on("lightsChanged", function(data) { - firetable.debug && console.log('lights check:', data); - if (data) { - firetable.lights = true; - $('.festiveLights').remove(); - var colorThing = firetable.utilities.hexToRGB(firetable.color); - var style = ""; - $("head").append(style); - } else { - $('.festiveLights').remove(); - firetable.lights = false; - } - }); - ftapi.events.on("waitlistChanged", function(data) { - var ok1 = ""; - var cnt = "0"; - if (data) { - var countr = 1; - for (var key in data) { - firetable.debug && console.log('waitlist', data); - if (data.hasOwnProperty(key)) { - cnt = countr; - var removeMe = ""; - if (data[key].removeAfter) removeMe = "departure_board" - ok1 += "
    " + countr + ". " + data[key].name + " " + removeMe + "
    "; - countr++; - } + val = val.toLowerCase(); + $(".pickerResult").each(function (p, q) { + if (firetable.emojis.n($(q), val)) { + $(q).show(); + } else { + $(q).hide(); + } + }); + // simplebar scroll update? + }, +}; + +firetable.utilities = { + getEmojiMap: function () { + firetable.emojiMap = {}; + (async function () { + urls = [ + "https://unpkg.com/unicode-emoji-json@0.3.0/data-by-group.json", + "https://unpkg.com/emojilib@3.0.4/dist/emoji-en-US.json", + ]; + duhdoymojis = { + "☕": ["coffee"], + "🚩": ["triangular_flag_on_post"], + "👋": ["wave"], + "🆔": ["id"], + "📈": ["chart_with_upwards_trend"], + "🚨": ["rotating_light"], + "🌧️": ["rain"], + "✅": ["white_check_mark"], + "🛰️": ["artificial_satellite"], + "🍵": ["tea"], + "❤️": ["heart"], + "🌊": ["ocean"], + "💦": ["splash"], + "💩": ["poop"], + "💯": ["100"], + "💨": ["dash"], + "🤡": ["clown"], + "🥱": ["yawn"], + "🙂": ["smile"], + "👌": ["ok"], + "💥": ["boom"], + "🍺": ["beer"], + "🍻": ["beers"], + "🥃": ["whiskey"], + "🌨️": ["snow"], + }; + try { + const requests = urls.map((url) => fetch(url)); + const responses = await Promise.all(requests); + const promises = responses.map((response) => response.json()); + const data = await Promise.all(promises); + for (const [category, emojisArr] of Object.entries(data[0])) { + let catid = category.replace(/[\s&]+/g, "_").toLowerCase(); + $("#pickerNav").append( + '' + emojisArr[0].emoji + "" + ); + $("#pickerContents").append('

    ' + category + "

    "); + for (let i in emojisArr) { + firetable.emojiMap[emojisArr[i].slug] = emojisArr[i].emoji; + var words = ""; + words += data[1][emojisArr[i].emoji] !== undefined ? data[1][emojisArr[i].emoji].join(",") : ""; + words += + duhdoymojis[emojisArr[i].emoji] !== undefined + ? "," + duhdoymojis[emojisArr[i].emoji].join(",") + : ""; + $("#picker" + catid).append( + '' + + emojisArr[i].emoji + + "" + ); + } + for (let i in duhdoymojis) { + for (let j in duhdoymojis[i]) { + firetable.emojiMap[duhdoymojis[i][j]] = i; + } + } + } + twemoji.parse(document.getElementById("pickerNav")); + } catch (err) {} + })(); + }, + hexToRGB: function (hex) { + var shorthandRegex = /^#?([a-f\d])([a-f\d])([a-f\d])$/i; + hex = hex.replace(shorthandRegex, function (m, r, g, b) { + return r + r + g + g + b + b; + }); + + var result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex); + return result + ? { + r: parseInt(result[1], 16), + g: parseInt(result[2], 16), + b: parseInt(result[3], 16), + } + : null; + }, + wrapText: function (context, text, x, y, maxWidth, lineHeight) { + var words = text.split(" "); + var line = ""; + var lines = 0; + + for (var n = 0; n < words.length; n++) { + var testLine = line + words[n] + " "; + var metrics = context.measureText(testLine); + var testWidth = metrics.width; + if (testWidth > maxWidth && n > 0) { + context.fillText(line, x, y); + line = words[n] + " "; + y += lineHeight; + lines++; + } else { + line = testLine; + } } - } - $("#label2 .count").text(" (" + cnt + ")"); - $("#justwaitlist").html(ok1); - }); - ftapi.events.on("tableChanged", function(data) { - var ok1 = ""; - if (data) { - var countr = 0; - for (var key in data) { - if (data.hasOwnProperty(key)) { - var removeMe = ""; - if (data[key].removeAfter) removeMe = "departure_board" - - ok1 += "
    " + removeMe + " " + data[key].name + "
    " + data[key].plays + "/" + firetable.playlimit + "
    "; - countr++; - } + context.fillText(line, x, y); + return lines; + }, + emojiShortnamestoUnicode: function (str) { + var res = str.replace(/\:(.*?)\:/g, function (x) { + var response = x; + var shortname = x.replace(/\:/g, ""); + if (firetable.emojiMap[shortname]) { + response = '' + firetable.emojiMap[shortname] + ""; + } else if (shortname == "rohn") { + response = ''; + } + return response; + }); + return res; + }, + playSound: function (filename) { + if (firetable.playBadoop) { + document.getElementById("audilert").setAttribute("src", filename + ".mp3"); } - if (countr < 4) { - ok1 += "
    !addme
    "; - countr++; - for (var i = countr; i < 4; i++) { - ok1 += "
     
    "; - } + }, + desktopNotify: function (chatData, namebo) { + if (Notification) { + if (Notification.permission !== "granted") { + Notification.requestPermission(); + } else { + var notification = new Notification(namebo, { + icon: "https://indiediscotheque.com/robots/" + chatData.id + namebo + ".png?size=110x110", + body: chatData.txt, + }); + } } + }, + screenUp: function () { + $("body").removeClass("screen"); + }, + screenDown: function () { + $("body").addClass("screen"); + }, + isChatPrettyMuchAtBottom: function () { + var scrollable = chatScroll.contentEl.scrollHeight - chatScroll.el.clientHeight; + var scrolled = chatScroll.contentWrapperEl.scrollTop; + firetable.debug && console.log("near bottom?", scrollable, scrolled); + return Math.abs(scrollable - scrolled) <= 25; + }, + scrollToBottom: function () { + chatScroll.contentWrapperEl.scrollTop = chatScroll.contentEl.scrollHeight; + }, + htmlEscape: function (s, preserveCR) { + preserveCR = preserveCR ? " " : "\n"; + return ( + ("" + s) /* Forces the conversion to string. */ + .replace(/&/g, "&") /* This MUST be the 1st replacement. */ + .replace(/'/g, "\\'") /* The 4 other predefined entities, required. */ + .replace(/"/g, """) + .replace(//g, ">") + /* + You may add other replacements here for HTML only + (but it's not necessary). + Or for XML, only if the named entities are defined in its DTD. + */ + .replace(/\r\n/g, preserveCR) /* Must be before the next replacement. */ + .replace(/[\r\n]/g, preserveCR) + ); + }, + format_date: function (d) { + var date = new Date(d); - } else { - ok1 += "
    !addme
    "; - for (var i = 0; i < 3; i++) { - ok1 += "
     
    "; - } - } - $("#deck").html(ok1); - for (var i = 0; i < 4; i++) { - if (i != firetable.playdex) { - $("#avtr" + i).removeClass("animate"); - $("#djthing" + i).removeClass("djActive"); + var month = date.getMonth() + 1; + var day = date.getDate(); + var year = date.getFullYear(); - } else { - $("#avtr" + i).addClass("animate"); - $("#djthing" + i).addClass("djActive"); + var formatted_date = month + "/" + day + "/" + year; + return formatted_date; + }, + format_time: function (d) { + var date = new Date(d); + + var hours1 = date.getHours(); + var ampm = "am"; + var hours = hours1; + if (hours1 >= 12) { + ampm = "pm"; + if (hours !== 12) hours = hours1 - 12; } - } - }); - ftapi.events.on("spotlightStateChanged", function(data) { - firetable.playdex = data; - for (var i = 0; i < 4; i++) { - if (i != data) { - $("#avtr" + i).removeClass("animate"); - $("#djthing" + i).removeClass("djActive"); - + if (hours == 0) hours = 12; + var minutes = date.getMinutes(); + var min = ""; + if (minutes > 9) { + min += minutes; } else { - $("#avtr" + i).addClass("animate"); - $("#djthing" + i).addClass("djActive"); + min += "0" + minutes; } - } - }); - ftapi.events.on("playLimitChanged", function(data) { - firetable.playlimit = data; - for (var i = 0; i < 4; i++) { - $("#plimit" + i).text(data); - } - }); - ftapi.events.on("banListChanged", function(data) { - $("#activeSuspentions").html(""); - for (key in data) { - if (data[key]) { - ftapi.lookup.userByName(key, function(person) { - $("#activeSuspentions").append("
    " + person.username + "
    "); - }); + return hours + ":" + min + "" + ampm; + }, + debounce(func, wait, immediate) { + var timeout; + return function () { + var context = this, + args = arguments; + var later = function () { + timeout = null; + if (!immediate) func.apply(context, args); + }; + var callNow = immediate && !timeout; + clearTimeout(timeout); + timeout = setTimeout(later, wait); + if (callNow) func.apply(context, args); + }; + }, + chatAt: function (element) { + element.bind("click", function () { + console.log(element); + var nameToAt; + if (element[0].className == "prson") { + nameToAt = $(this).find(".prsnName").text(); + } else if (element[0].className == "botson") { + nameToAt = $(this).next(".chatContent").find(".chatName").text(); + } else if (element[0].className == "chatName") { + nameToAt = $(this).text(); + } + $("#newchat") + .val(function (i, val) { + return val + "@" + nameToAt + " "; + }) + .focus(); + }); + }, + initAtLand: function () { + firetable.atLand = true; + firetable.atString = ""; + firetable.atUsers = ["everyone"]; + for (var user in ftapi.users) { + firetable.atUsers.push(ftapi.users[user].username); } - } - }); - ftapi.events.on("userJoined", function(data) { - console.log(data); - var user = data; - var block = ""; - var blockcon = ""; - var herecon = "lens"; - var isIdle = ""; - if (data.idle) { - if (data.idle.isIdle && !data.hostbot) isIdle = "idle"; - if (data.idle.audio == 2) { - herecon = "label_important"; + firetable.atUsersFiltered = firetable.atUsers.sort(); + }, + updateAtLand: function () { + firetable.atUsersFiltered = firetable.atUsers + .filter((user) => user.toLowerCase().startsWith(firetable.atString.toLowerCase())) + .sort(); + $("#atPicker").html(""); + if (firetable.atUsersFiltered.length) { + for (var user of firetable.atUsersFiltered) { + $( + '
    " + ).appendTo("#atPicker"); + } + } else { + $('
    No users match
    ').appendTo("#atPicker"); } - } - if (data.blocked) { - block = "blockd"; - blockcon = "block"; - } - - if (!data.username) data.username = data.userid; + }, + chooseAt: function (atPeep) { + var $chatText = $("#newchat"); + if (firetable.atString.length > 0) $chatText.val($chatText.val().slice(0, firetable.atString.length * -1)); + $chatText.val($chatText.val() + atPeep + " "); + firetable.utilities.exitAtLand(); + }, + exitAtLand: function () { + firetable.atLand = false; + firetable.atUsersFiltered = []; + firetable.atString = ""; + $("#atPicker").removeClass("show").html(""); + }, +}; - var destination = "#usersRegular"; - var rolename = ""; - if (data.mod) { - rolename = "mod"; - destination = "#usersMod"; - } - if (data.supermod) { - rolename = "supermod"; - destination = "#usersSuper"; - } - if (data.hostbot) { - rolename = "robot"; - destination = "#usersBot"; - } +firetable.ui = { + textToLinks: function (text, themeBox) { + var re = /(\b(https?|ftp|file):\/\/[-A-Z0-9+&@#\/%?=~_|!:,.;]*[-A-Z0-9+&@#\/%=~_|])/gi; + if (firetable.showImages && !themeBox) re = /(https?:\/\/(?![/|.|\w|\s|-]*(?:jpe?g|png|gif))[^" ]+)/g; + return text.replace(re, '$1'); - var newUserToAddX = $("
    "); - newUserToAddX.addClass("prson " + block); - newUserToAddX.attr("id", "user" + data.userid); - newUserToAddX.html("
    " + blockcon + "" + herecon + "
    " + data.username + "" + rolename + "joined " + firetable.utilities.format_date(data.joined) + ""); - firetable.utilities.chatAt(newUserToAddX); // adds the click event to @ the user - $(destination).append(newUserToAddX); - }); - ftapi.events.on("userLeft", function(data) { - $("#user" + data.userid).remove(); - }); - ftapi.events.on("userChanged", function(data) { - var user = data; - var block = ""; - var blockcon = ""; - var herecon = "lens"; - var isIdle = ""; - console.log("CHANGE", data) - if (data.idle) { - if (data.idle.isIdle && !data.hostbot) isIdle = "idle"; - if (data.idle.audio == 2) { - herecon = "label_important"; + return text; + }, + dubtrackImportFileSelect: function (evt) { + var files = evt.target.files; // FileList object + var file = files[0]; + // read the file contents + var reader = new FileReader(); + reader.readAsText(file); + reader.onload = function (event) { + try { + var allthestuff = event.currentTarget.result; + console.log(allthestuff); + firetable.dtImportName = firetable.ui.strip(allthestuff.split("

    ")[1].split("

    ")[0]); + var hams = allthestuff.split('
  • (.*)<\/li>/gm; + var matches = thingsRegex.exec(hams[i]); + var type = matches[2]; + var cid = matches[4]; + var name = firetable.ui.strip(matches[5]); + firetable.dtImportList.push({ + type: type, + cid: cid, + name: name, + }); + } + console.log(firetable.dtImportList); + console.log(firetable.dtImportName); + if (firetable.dtImportList.length) { + $("#importDubResults").text( + "Ok... import " + firetable.dtImportName + " (" + firetable.dtImportList.length + " tracks)?" + ); + $("#dubimportButton").show(); + } else { + $("#importDubResults").text("ERROR... NO TRAX?"); + $("#dubimportButton").hide(); + } + } catch (e) { + console.log(e); + $("#importDubResults").text("ERROR"); + $("#dubimportButton").hide(); + } + }; + }, + strip: function (html) { + var doc = firetable.parser.parseFromString(html, "text/html"); + return doc.body.textContent || ""; + }, + hidePlayerControls: function () { + $("head").append( + "" + ); + }, + showPlayerControls: function () { + $(".playerControlsHider").remove(); + }, + showImages: function (chatTxt) { + if (firetable.showImages) { + var imageUrlRegex = /((http(s?):)([/|.|\w|\s|-])*\.(?:jpe?g|gif|png))/g; + var hasImage = chatTxt.search(imageUrlRegex) >= 0; + if (hasImage) { + chatTxt = chatTxt.replace(imageUrlRegex, function (imageUrl) { + var chatImage = new Image(); + chatImage.onload = function () { + if (firetable.utilities.isChatPrettyMuchAtBottom()) firetable.utilities.scrollToBottom(); + }; + chatImage.src = imageUrl; + return ( + '×' + ); + }); + } + } + return chatTxt; + }, + loginLinkToggle: function (id) { + $("#formlinks").find(".selected").removeClass("selected"); + $("#" + id).addClass("selected"); + }, + loginEventsInit: function () { + $("#resetpass").bind("click", function () { + $("#logscreen").css("display", "none"); + $("#createscreen").css("display", "none"); + $("#resetscreen").css("display", "block"); + firetable.ui.loginLinkToggle($(this).attr("id")); + }); + $("#loginlink").bind("click", function () { + $("#logscreen").css("display", "block"); + $("#createscreen").css("display", "none"); + $("#resetscreen").css("display", "none"); + firetable.ui.loginLinkToggle($(this).attr("id")); + }); + $("#signuplink").bind("click", function () { + $("#logscreen").css("display", "none"); + $("#createscreen").css("display", "block"); + $("#resetscreen").css("display", "none"); + firetable.ui.loginLinkToggle($(this).attr("id")); + }); + $("#loginpass").bind("keyup", function (e) { + if (e.which == 13) { + var email = $("#loginemail").val(); + var pass = $("#loginpass").val(); + $("#loginemail").val(""); + $("#loginpass").val(""); + firetable.actions.logIn(email, pass); + } + }); + $("#newpass2").bind("keyup", function (e) { + if (e.which == 13) { + var email = $("#newemail").val(); + var pass = $("#newpass").val(); + var pass2 = $("#newpass2").val(); + var username = $("#newusername").val(); + if (pass == pass2) { + firetable.actions.signUp(email, pass, username); + } else { + alert("Those passwords do not match!"); + } + } + }); + $("#theAddress").bind("keyup", function (e) { + if (e.which == 13) { + var email = $("#theAddress").val(); + firetable.debug && console.log("reset email return"); + ftapi.actions.resetPassword(email, function (error) { + var errorCode = error.code; + var errorMessage = error.message; + if (errorCode === "auth/wrong-password") { + alert("Wrong password."); + } else { + alert(errorMessage); + } + firetable.debug && console.log("send pass reset error:", error); + }); + alert("Reset email sent. Click the reset link when it arrives thanks."); + } + }); + $("#createAccountBttn").bind("click", function () { + var email = $("#newemail").val(); + var pass = $("#newpass").val(); + var pass2 = $("#newpass2").val(); + var termsAgreedTo = $("#agreetoterms").is(":checked"); + var username = $("#newusername").val(); + if (!termsAgreedTo) { + alert( + "You must read and agree to the Terms of Service and Privacy Policy before you can create an account." + ); + } else if (pass != pass2) { + alert("Those passwords do not match!"); + } else { + firetable.actions.signUp(email, pass, username); + } + }); + $("#resetPassBttn").bind("click", function () { + var email = $("#theAddress").val(); + firetable.debug && console.log("reset email click button"); + ftapi.actions.resetPassword(email, function (error) { + var errorCode = error.code; + var errorMessage = error.message; + if (errorCode === "auth/wrong-password") { + alert("Wrong password."); + } else { + alert(errorMessage); + } + firetable.debug && console.log("send pass reset error:", error); + }); + alert("Reset email sent. Click the reset link when it arrives thanks."); + }); + $("#loginBttn").bind("click", function () { + var email = $("#loginemail").val(); + var pass = $("#loginpass").val(); + $("#loginemail").val(""); + $("#loginpass").val(""); + firetable.actions.logIn(email, pass); + }); + }, + loginEventsDestroy: function () { + $("#resetpass").off("click"); + $("#loginlink").off("click"); + $("#signuplink").off("click"); + $("#loginpass").off("keyup"); + $("#newpass2").off("keyup"); + $("#theAddress").off("keyup"); + $("#createAccountBttn").off("click"); + $("#resetPassBttn").off("click"); + $("#loginBttn").off("click"); + }, + init: function () { + $("#mainqueue").sortable({ + start: function (event, ui) { + var start_pos = ui.item.index(); + ui.item.data("start_pos", start_pos); + }, + change: function (event, ui) {}, + update: function (event, ui) { + firetable.debug && console.log("UPDATE"); + firetable.actions.updateQueue(); + }, + }); + //GET SETTINGS FROM LOCALSTORAGE + var disableMediaPlayback = localStorage["firetableDisableMedia"]; + if (typeof disableMediaPlayback == "undefined") { + localStorage["disableMediaPlayback"] = false; + firetable.disableMediaPlayback = false; + $("#mediaDisableToggle").prop("checked", false); + } else { + disableMediaPlayback = JSON.parse(disableMediaPlayback); + firetable.disableMediaPlayback = disableMediaPlayback; + $("#mediaDisableToggle").prop("checked", disableMediaPlayback); + if (disableMediaPlayback) { + firetable.ui.hidePlayerControls(); + } } - } - if (data.blocked) { - block = "blockd"; - blockcon = "block"; - } - - if (!data.username) data.username = data.userid; - - var destination = "#usersRegular"; - var rolename = ""; - if (data.mod) { - rolename = "mod"; - destination = "#usersMod"; - } - if (data.supermod) { - rolename = "supermod"; - destination = "#usersSuper"; - } - if (data.hostbot) { - rolename = "robot"; - destination = "#usersBot"; - } - $("#user" + data.userid).html("
    " + blockcon + "" + herecon + "
    " + data.username + "" + rolename + "joined " + firetable.utilities.format_date(data.joined) + ""); - }); - ftapi.events.on("usersChanged", function(okdata) { - if ($("#loggedInName").text() == ftapi.uid) { - if (ftapi.users[ftapi.uid]) { - if (ftapi.users[ftapi.uid].username) { - $("#loggedInName").text(ftapi.users[ftapi.uid].username); - } + var showImages = localStorage["firetableShowImages"]; + if (typeof showImages == "undefined") { + localStorage["firetableShowImages"] = false; + firetable.showImages = false; + $("#showImagesToggle").prop("checked", false); + } else { + showImages = JSON.parse(showImages); + firetable.showImages = showImages; + $("#showImagesToggle").prop("checked", showImages); } - } - if (ftapi.uid) { - if (ftapi.users[ftapi.uid]) { - if (ftapi.users[ftapi.uid].supermod) { - if ($("#ftSuperCopButton").is(":hidden")) { - $("#ftSuperCopButton").show(); + var showAvatars = localStorage["firetableShowAvatars"]; + if (typeof showAvatars == "undefined") { + localStorage["firetableShowAvatars"] = true; + firetable.showAvatars = true; + $("#showAvatarsToggle").prop("checked", true); + } else { + showAvatars = JSON.parse(showAvatars); + firetable.showAvatars = showAvatars; + $("#showAvatarsToggle").prop("checked", showAvatars); + if (showAvatars == false) { + document.getElementById("actualChat").classList.add("avatarsOff"); } - } } - } - var count = Object.keys(okdata).length; - $("#label1 .count").text(" (" + count + ")"); - firetable.debug && console.log('users:', okdata); - }); - var $chatTemplate = $('#chatKEY').remove(); - ftapi.events.on("newChat", function(chatData) { - var namebo = chatData.id; - var utitle = ""; - - var atBottom = false; - if (firetable.utilities.isChatPrettyMuchAtBottom()) atBottom = true; - - var you = ftapi.uid; - if (ftapi.users[ftapi.uid]) { - if (ftapi.users[ftapi.uid].username) you = ftapi.users[ftapi.uid].username; - } - - if (ftapi.users[chatData.id]) { - if (ftapi.users[chatData.id].username) namebo = ftapi.users[chatData.id].username; - if (ftapi.users[chatData.id].mod) utitle = "mod"; - if (ftapi.users[chatData.id].supermod) utitle = "supermod"; - if (ftapi.users[chatData.id].hostbot) utitle = "robot"; - } else if (chatData.name) { - namebo = chatData.name; - } + var playBadoop = localStorage["firetableBadoop"]; + if (typeof playBadoop == "undefined") { + localStorage["firetableBadoop"] = true; + firetable.playBadoop = true; + $("#badoopToggle").prop("checked", true); + } else { + playBadoop = JSON.parse(playBadoop); + firetable.playBadoop = playBadoop; + $("#badoopToggle").prop("checked", playBadoop); + } + var dtnmt = localStorage["firetableDTNM"]; + if (typeof dtnmt == "undefined") { + localStorage["firetableDTNM"] = false; + firetable.desktopNotifyMentions = false; + $("#desktopNotifyMentionsToggle").prop("checked", false); + } else { + dtnmt = JSON.parse(dtnmt); + firetable.desktopNotifyMentions = dtnmt; + $("#desktopNotifyMentionsToggle").prop("checked", dtnmt); + } + var screenControl = localStorage["firetableScreenControl"]; + if (typeof screenControl == "undefined") { + localStorage["firetableScreenControl"] = "sync"; + firetable.screenControl = "sync"; + $("#screenControlTog" + firetable.screenControl).prop("checked", true); + } else { + firetable.screenControl = screenControl; + $("#screenControlTog" + firetable.screenControl).prop("checked", true); + if (screenControl == "on") { + firetable.utilities.screenDown(); + } else if (screenControl == "off") { + firetable.utilities.screenUp(); + } else if (screenControl == "sync") { + if (firetable.screenSyncPos) { + firetable.utilities.screenDown(); + } else { + firetable.utilities.screenUp(); + } + } + } + var $historyItem = $("#thehistory .pvbar").remove(); + ftapi.events.on("newHistory", function (data) { + if (data.img == "img/idlogo.png" && ftconfigs.defaultAlbumArtUrl.length) + data.img = ftconfigs.defaultAlbumArtUrl; + var firstpart = "yt"; + if (data.type == 2) firstpart == "sc"; + var pkey = firstpart + "cid" + data.cid; + var $histItem = $historyItem.clone(); + $histItem.attr("id", "pvbar" + pkey); + $histItem.attr("data-key", pkey); + $histItem.attr("data-cid", data.cid); + $histItem.attr("data-type", data.type); + + $histItem + .find(".previewicon") + .attr("id", "pv" + pkey) + .on("click", function () { + firetable.actions.pview( + $(this).closest(".pvbar").attr("data-key"), + true, + $(this).closest(".pvbar").attr("data-type"), + true + ); + }); + $histItem + .find(".histlink") + .attr({ + href: data.url, + tabindex: "-1", + }) + .text(data.artist + " - " + data.title); + $histItem.find(".histdj").text(data.dj); + $histItem.find(".histdate").text(firetable.utilities.format_date(data.when)); + $histItem.find(".histtime").text(firetable.utilities.format_time(data.when)); + $histItem + .find(".histeal") + .attr("id", "apv" + data.type + data.cid) + .on("click", function () { + firetable.actions.queueTrack( + $(this).closest(".pvbar").attr("data-cid"), + firetable.utilities.htmlEscape($(this).closest(".pvbar").find(".histlink").text()), + $(this).closest(".pvbar").attr("data-type"), + true + ); + }); + $histItem.find(".histart").css("background-image", "url(" + data.img + ")"); + $histItem.prependTo("#thehistory"); + // simplebar scroll update? + }); + ftapi.events.on("newTheme", function (data) { + if (!data) { + //no theme + $("#currentTheme").text("!suggest a theme"); + } else { + var txtOut = firetable.ui.strip(data); + txtOut = firetable.ui.textToLinks(txtOut, true); + txtOut = firetable.utilities.emojiShortnamestoUnicode(txtOut); + txtOut = txtOut.replace(/\`(.*?)\`/g, function (x) { + return "" + x.replace(/\`/g, "") + ""; + }); + $("#currentTheme").html(txtOut); + twemoji.parse(document.getElementById("currentTheme")); + } + }); + ftapi.events.on("tagUpdate", function (data) { + firetable.debug && console.log("TAG UPDATE", data); + firetable.tagUpdate = data; + if (firetable.song) { + if (firetable.song.cid == data.cid && data.adamData.track_name) { + $("#track").text(firetable.ui.strip(data.adamData.track_name)); + $("#artist").text(firetable.ui.strip(data.adamData.artist)); + var nicename = firetable.song.djname; + var showPlaycount = false; + if (data.adamData.playcount) { + if (data.adamData.playcount > 0) { + showPlaycount = true; + } + } + if (data.adamData.last_play) { + $("#lastPlay").text( + "last " + + firetable.utilities.format_date(data.adamData.last_play) + + " by " + + data.adamData.last_play_dj + ); + } else { + $("#lastPlay").text(""); + } + if (data.adamData.first_play) { + $("#firstPlay").text( + "first " + + firetable.utilities.format_date(data.adamData.first_play) + + " by " + + data.adamData.first_play_dj + ); + } else { + $("#firstPlay").text(""); + } + var doTheScrollThing = firetable.utilities.isChatPrettyMuchAtBottom(); + if (showPlaycount) { + $("#playCount").text(data.adamData.playcount + " plays"); + $(".npmsg" + data.cid) + .last() + .html( + '
    DJ ' + + nicename + + " started playing " + + data.adamData.track_name + + " by " + + data.adamData.artist + + "
    This song has been played " + + data.adamData.playcount + + " times.
    " + ); + } else { + $("#playCount").text(""); + $(".npmsg" + data.cid) + .last() + .html( + '
    DJ ' + + nicename + + " started playing " + + data.adamData.track_name + + " by " + + data.adamData.artist + + "
    " + ); + } + if (doTheScrollThing) firetable.utilities.scrollToBottom(); + } + } + }); - var badoop = false; - if (chatData.txt.match("@" + you, 'i') || chatData.txt.match(/\@everyone/)) { - var oknow = Date.now(); - if (oknow - chatData.time < (10 * 1000)) { - firetable.utilities.playSound("sound"); - if (firetable.desktopNotifyMentions) firetable.utilities.desktopNotify(chatData, namebo); - badoop = true; - } - } - if (chatData.id == firetable.lastChatPerson && !badoop) { - $("#chat" + firetable.lastChatId + " .chatContent").append("
    "); - $("#chatTime" + firetable.lastChatId).text(firetable.utilities.format_time(chatData.time)); - var txtOut = firetable.ui.strip(chatData.txt); - txtOut = firetable.ui.showImages(txtOut); - txtOut = firetable.ui.textToLinks(txtOut); - txtOut = firetable.utilities.emojiShortnamestoUnicode(txtOut); - txtOut = txtOut.replace(/\`(.*?)\`/g, function(x) { - return "" + x.replace(/\`/g, "") + ""; - }); - if (chatData.hidden) txtOut = "[message removed]"; - $("#chattxt" + chatData.chatID).html(txtOut); - var canBeDeleted = false; - if (ftapi.users[ftapi.uid].mod || ftapi.users[ftapi.uid].supermod) { - if (ftapi.users[chatData.id]) { - if (!ftapi.users[chatData.id].mod && !ftapi.users[chatData.id].supermod) { - canBeDeleted = true; - } - } else { - canBeDeleted = true; - } - if (canBeDeleted && !chatData.hidden) { - // add delete button - $("#chattxt" + chatData.chatID).addClass("deleteMe"); - $("#chattxt" + chatData.chatID).append("
    x
    "); - $("#chattxt" + chatData.chatID).find(".modDelete").on('click', function() { - console.log("DELETE CHAT", chatData); - ftapi.actions.deleteChat(chatData.feedID); + ftapi.events.on("newSong", function (data) { + $("#playCount").text(""); + $("#lastPlay").text(""); + $("#firstPlay").text(""); + window.dispatchEvent(new Event("resize")); + $("#cloud_with_rain").removeClass("on"); + $("#fire").removeClass("on"); + $("#timr").countdown("destroy"); + if (firetable.moveBar != null) { + clearInterval(firetable.moveBar); + firetable.moveBar = null; + } + if (data.image == "img/idlogo.png" && ftconfigs.defaultAlbumArtUrl.length) + data.image = ftconfigs.defaultAlbumArtUrl; + $("#prgbar").css("background", "#151515"); + var showPlaycount = false; + if (firetable.tagUpdate) { + if (data.cid == firetable.tagUpdate.cid && firetable.tagUpdate.adamData.track_name) { + data.title = firetable.tagUpdate.adamData.track_name; + data.artist = firetable.tagUpdate.adamData.artist; + if (firetable.tagUpdate.adamData.last_play) { + $("#lastPlay").text( + "last " + + firetable.utilities.format_date(firetable.tagUpdate.adamData.last_play) + + " by " + + firetable.tagUpdate.adamData.last_play_dj + ); + } + if (firetable.tagUpdate.adamData.first_play) { + $("#firstPlay").text( + "first " + + firetable.utilities.format_date(firetable.tagUpdate.adamData.first_play) + + " by " + + firetable.tagUpdate.adamData.first_play_dj + ); + } + if (firetable.tagUpdate.adamData.playcount) { + if (firetable.tagUpdate.adamData.playcount > 0) { + showPlaycount = true; + $("#playCount").text(firetable.tagUpdate.adamData.playcount + " plays"); + } + } + } + } + $("#track").text(firetable.ui.strip(data.title)); + $("#artist").text(firetable.ui.strip(data.artist)); + $("#songlink").attr("href", data.url); + $("#albumArt").css("background-image", "url(" + data.image + ")"); + var nownow = Date.now(); + var timeSince = nownow - data.started; + if (timeSince <= 0) timeSince = 0; + var secSince = Math.floor(timeSince / 1000); + var timeLeft = data.duration - secSince; + firetable.song = data; + firetable.debug && console.log("NEW TRACK", data); + firetable.debug && console.log("time since:", timeSince); + if (data.type == 1) { + $("#scScreen").hide(); + $("#songlink").html( + '' + ); + } else if (data.type == 2) { + $("#scScreen").show(); + $("#songlink").html( + '' + ); + var biggerImg = data.image.replace("-large", "-t500x500"); + firetable.scImg = biggerImg; + $("#albumArt").css("background-image", "url(" + biggerImg + ")"); + try { + setup(biggerImg); + } catch (e) { + firetable.debug && console.log("big image error:", e); + } + } + if (data.type == 1 && firetable.ytLoaded) { + if (!firetable.preview) { + if (firetable.scLoaded) firetable.scwidget.pause(); + if (!firetable.disableMediaPlayback) player.loadVideoById(data.cid, secSince, "large"); + var thevolactual = $("#slider").slider("value"); + player.setVolume(thevolactual); + firetable.scwidget.setVolume(thevolactual); + } + } else if (data.type == 2 && firetable.scLoaded) { + if (!firetable.preview) { + if (firetable.ytLoaded) player.stopVideo(); + firetable.scSeek = timeSince; + if (!firetable.disableMediaPlayback) + firetable.scwidget.load("http://api.soundcloud.com/tracks/" + data.cid, { + auto_play: true, + single_active: false, + callback: function () { + var thevolactual = localStorage["firetableVol"]; + player.setVolume(thevolactual); + firetable.scwidget.setVolume(thevolactual); + }, + }); + } + } + if (data.cid != 0) { + var nicename = data.djid; + if (ftapi.users[data.djid]) { + if (ftapi.users[data.djid].username) nicename = ftapi.users[data.djid].username; + } + if (firetable.nonpmsg) { + firetable.nonpmsg = false; + } else { + var doTheScrollThing = firetable.utilities.isChatPrettyMuchAtBottom(); + if (showPlaycount) { + $("#chats").append( + '
    DJ ' + + nicename + + " started playing " + + data.title + + " by " + + data.artist + + "
    This song has been played " + + firetable.tagUpdate.adamData.playcount + + " times.
    " + ); + } else { + $("#chats").append( + '
    DJ ' + + nicename + + " started playing " + + data.title + + " by " + + data.artist + + "
    " + ); + } + if (doTheScrollThing) firetable.utilities.scrollToBottom(); + firetable.lastChatPerson = false; + firetable.lastChatId = false; + } + } + $("#timr").countdown({ + until: timeLeft, + compact: true, + description: "", + format: "MS", }); - } - } - twemoji.parse(document.getElementById("chattxt" + chatData.chatID)); - - } else { - var $chatthing = $chatTemplate.clone(); - $chatthing.attr('id', "chat" + chatData.chatID); - $chatthing.find('.botson').css('background-image', "url(https://indiediscotheque.com/robots/" + chatData.id + namebo + ".png?size=110x110"); - $chatthing.find('.utitle').html(utitle); - $chatthing.find('.chatTime').attr('id', "chatTime" + chatData.chatID).html(firetable.utilities.format_time(chatData.time)); - if (badoop) $chatthing.addClass('badoop'); - var txtOut = firetable.ui.strip(chatData.txt); - txtOut = firetable.ui.showImages(txtOut); - txtOut = firetable.ui.textToLinks(txtOut); - txtOut = firetable.utilities.emojiShortnamestoUnicode(txtOut); - txtOut = txtOut.replace(/\`(.*?)\`/g, function(x) { - return "" + x.replace(/\`/g, "") + ""; - }); - if (chatData.hidden) txtOut = "[message removed]"; - $chatthing.find(".chatText").html(txtOut).attr('id', "chattxt" + chatData.chatID); - console.log(chatData); - - $chatthing.find(".chatName").text(namebo); - firetable.utilities.chatAt($chatthing.find('.botson')); // adds the click event to @ the user - firetable.utilities.chatAt($chatthing.find('.chatName')); // adds the click event to @ the user - twemoji.parse($chatthing.find(".chatText")[0]); - $chatthing.appendTo("#chats"); - try { - if (ftapi.users[ftapi.uid].mod || ftapi.users[ftapi.uid].supermod) { - var canBeDeleted = false; - if (ftapi.users[chatData.id]) { - if (!ftapi.users[chatData.id].mod && !ftapi.users[chatData.id].supermod && !chatData.hidden) { - canBeDeleted = true; - } + firetable.moveBar = setInterval(function () { + var now = Date.now(); + var sofar = now - firetable.song.started; + var pcnt = (sofar / (firetable.song.duration * 1000)) * 100; + $("#prgbar").css( + "background", + "linear-gradient(90deg, " + firetable.color + " " + pcnt + "%, #151515 " + pcnt + "%)" + ); + }, 500); + }); + ftapi.events.on("screenStateChanged", function (data) { + firetable.debug && console.log("thescreen:", data); + firetable.screenSyncPos = data; + if (firetable.screenControl == "sync") { + if (data) { + firetable.utilities.screenDown(); + } else { + firetable.utilities.screenUp(); + } + } + }); + ftapi.events.on("danceStateChanged", function (data) { + firetable.debug && console.log("dance check:", data); + if (data) { + $("#deck").addClass("dance"); } else { - canBeDeleted = true; - } - if (canBeDeleted && !chatData.hidden) { - // add delete button - $chatthing.find(".chatText").addClass("deleteMe"); - $chatthing.find(".chatText").append("
    x
    "); - $chatthing.find(".modDelete").on('click', function() { - ftapi.actions.deleteChat(chatData.feedID); - }); - } - } - } catch (e) { - console.log(e) - } - firetable.lastChatPerson = chatData.id; - firetable.lastChatId = chatData.chatID; - } - - if (chatData.card) { - $("#chattxt" + chatData.chatID).append(""); - - firetable.actions.showCard(chatData.card, chatData.chatID); - firetable.debug && console.log("showin card"); - } + $("#deck").removeClass("dance"); + } + }); + ftapi.events.on("lightsChanged", function (data) { + firetable.debug && console.log("lights check:", data); + if (data) { + firetable.lights = true; + $(".festiveLights").remove(); + var colorThing = firetable.utilities.hexToRGB(firetable.color); + var style = + ""; + $("head").append(style); + } else { + $(".festiveLights").remove(); + firetable.lights = false; + } + }); + ftapi.events.on("waitlistChanged", function (data) { + var ok1 = ""; + var cnt = "0"; + if (data) { + var countr = 1; + for (var key in data) { + firetable.debug && console.log("waitlist", data); + if (data.hasOwnProperty(key)) { + cnt = countr; + var removeMe = ""; + if (data[key].removeAfter) removeMe = "departure_board"; + ok1 += + '
    ' + + countr + + ". " + + data[key].name + + ' ' + + removeMe + + "
    "; + countr++; + } + } + } + $("#label2 .count").text(" (" + cnt + ")"); + $("#justwaitlist").html(ok1); + }); + ftapi.events.on("tableChanged", function (data) { + var ok1 = ""; + if (data) { + var countr = 0; + for (var key in data) { + if (data.hasOwnProperty(key)) { + var removeMe = ""; + if (data[key].removeAfter) removeMe = "departure_board"; + + ok1 += + '
    ' + + removeMe + + " " + + data[key].name + + '
    ' + + data[key].plays + + '/' + + firetable.playlimit + + "
    "; + countr++; + } + } + if (countr < 4) { + ok1 += + '
    !addme
    '; + countr++; + for (var i = countr; i < 4; i++) { + ok1 += '
     
    '; + } + } + } else { + ok1 += + '
    !addme
    '; + for (var i = 0; i < 3; i++) { + ok1 += '
     
    '; + } + } + $("#deck").html(ok1); + for (var i = 0; i < 4; i++) { + if (i != firetable.playdex) { + $("#avtr" + i).removeClass("animate"); + $("#djthing" + i).removeClass("djActive"); + } else { + $("#avtr" + i).addClass("animate"); + $("#djthing" + i).addClass("djActive"); + } + } + }); + ftapi.events.on("spotlightStateChanged", function (data) { + firetable.playdex = data; + for (var i = 0; i < 4; i++) { + if (i != data) { + $("#avtr" + i).removeClass("animate"); + $("#djthing" + i).removeClass("djActive"); + } else { + $("#avtr" + i).addClass("animate"); + $("#djthing" + i).addClass("djActive"); + } + } + }); + ftapi.events.on("playLimitChanged", function (data) { + firetable.playlimit = data; + for (var i = 0; i < 4; i++) { + $("#plimit" + i).text(data); + } + }); + ftapi.events.on("banListChanged", function (data) { + $("#activeSuspentions").html(""); + for (key in data) { + if (data[key]) { + ftapi.lookup.userByName(key, function (person) { + $("#activeSuspentions").append( + '
    ' + + person.username + + '
    ' + ); + }); + } + } + }); + ftapi.events.on("userJoined", function (data) { + console.log(data); + var user = data; + var block = ""; + var blockcon = ""; + var herecon = "lens"; + var isIdle = ""; + if (data.idle) { + if (data.idle.isIdle && !data.hostbot) isIdle = "idle"; + if (data.idle.audio == 2) { + herecon = "label_important"; + } + } + if (data.blocked) { + block = "blockd"; + blockcon = "block"; + } - if (atBottom || ftapi.uid == chatData.id) firetable.utilities.scrollToBottom(); - else $('#morechats').addClass('show'); - }); + if (!data.username) data.username = data.userid; - ftapi.events.on("chatRemoved", function(data) { - console.log("CHAT DELETED", data); - $("#chattxt" + data.chatID).text("[message removed]"); - if (ftapi.users[ftapi.uid].mod || ftapi.users[ftapi.uid].supermod) $("#chattxt" + data.chatID).removeClass("deleteMe"); - }); + var destination = "#usersRegular"; + var rolename = ""; + if (data.mod) { + rolename = "mod"; + destination = "#usersMod"; + } + if (data.supermod) { + rolename = "supermod"; + destination = "#usersSuper"; + } + if (data.hostbot) { + rolename = "robot"; + destination = "#usersBot"; + } - ftapi.events.on("playlistChanged", function(okdata, listID) { - firetable.queue = okdata; - $('#mainqueue').html(""); - for (var key in okdata) { - if (okdata.hasOwnProperty(key)) { - var $newli = $playlistItemTemplate.clone(); - var thisone = okdata[key]; - var psign = ""; - if (key == firetable.preview) { - psign = ""; - } - $newli.attr('id', "pvbar" + key); - $newli.attr("data-key", key); - $newli.attr("data-type", thisone.type); - $newli.find('.previewicon').attr('id', "pv" + key).on('click', function() { - firetable.actions.pview($(this).closest('.pvbar').attr('data-key'), false, $(this).closest('.pvbar').attr('data-type')); - }).html(psign); - $newli.find('.listwords').html(thisone.name); - $newli.find('.bumpsongs').on('click', function() { - firetable.actions.bumpSongInQueue($(this).closest('.pvbar').attr('data-key')) - }); - $newli.find('.bottomsongs').on('click', function() { - var oldID = $(this).closest('.pvbar').attr('data-key'); - ftapi.actions.moveTrackToBottom($(this).closest('.pvbar').attr('data-key'), function(newID) { - if (firetable.preview) { - // visually update preview in the new location if applicable - if (firetable.preview == oldID) { - firetable.preview = newID; - $("#pv" + newID).html(""); + var newUserToAddX = $("
    "); + newUserToAddX.addClass("prson " + block); + newUserToAddX.attr("id", "user" + data.userid); + newUserToAddX.html( + '
    ' + + blockcon + + '' + + herecon + + '
    ' + + data.username + + '' + + rolename + + 'joined ' + + firetable.utilities.format_date(data.joined) + + "" + ); + firetable.utilities.chatAt(newUserToAddX); // adds the click event to @ the user + $(destination).append(newUserToAddX); + }); + ftapi.events.on("userLeft", function (data) { + $("#user" + data.userid).remove(); + }); + ftapi.events.on("userChanged", function (data) { + var user = data; + var block = ""; + var blockcon = ""; + var herecon = "lens"; + var isIdle = ""; + console.log("CHANGE", data); + if (data.idle) { + if (data.idle.isIdle && !data.hostbot) isIdle = "idle"; + if (data.idle.audio == 2) { + herecon = "label_important"; } - } - }); - }); - if (thisone.flagged) { - var flagLabel = "broken"; - var flagIcon = "warning"; - if (thisone.flagged.code == 7) { - flagLabel = "age restricted"; - } else if (thisone.flagged.code >= 8) { - if (thisone.flagged.code == 8) { - // manual broken flagged by mod - flagLabel = "broken (manual)"; - } else if (thisone.flagged.code == 9) { - // low quality - flagLabel = "low audio quality"; - flagIcon = "disc_full"; - } else if (thisone.flagged.code == 10) { - // offtheme - flagLabel = "offtheme"; - flagIcon = "flag"; - } } - $newli.find('.track-warning').html(" " + flagIcon + " "); - $newli.find('.track-warning').prop('title', 'Flagged as ' + flagLabel + ' on ' + firetable.utilities.format_date(thisone.flagged.date) + '. Click to remove flag.'); - $newli.find('.track-warning').on('click', function() { - ftapi.actions.unflagTrack($(this).closest('.pvbar').attr('data-key')); - $(this).html(""); - }); + if (data.blocked) { + block = "blockd"; + blockcon = "block"; + } - } - $newli.find('.edittags').on('click', function() { - firetable.actions.editTagsPrompt($(this).closest('.pvbar').attr('data-key')) - }); - $newli.find('.deletesong').on('click', function() { - firetable.actions.deleteSong($(this).closest('.pvbar').attr('data-key')) - }); - $('#mainqueue').append($newli); - } - } - }); + if (!data.username) data.username = data.userid; - firetable.ui.LinkGrabber.start(); + var destination = "#usersRegular"; + var rolename = ""; + if (data.mod) { + rolename = "mod"; + destination = "#usersMod"; + } + if (data.supermod) { + rolename = "supermod"; + destination = "#usersSuper"; + } + if (data.hostbot) { + rolename = "robot"; + destination = "#usersBot"; + } + + $("#user" + data.userid).html( + '
    ' + + blockcon + + '' + + herecon + + '
    ' + + data.username + + '' + + rolename + + 'joined ' + + firetable.utilities.format_date(data.joined) + + "" + ); + }); + ftapi.events.on("usersChanged", function (okdata) { + if ($("#loggedInName").text() == ftapi.uid) { + if (ftapi.users[ftapi.uid]) { + if (ftapi.users[ftapi.uid].username) { + $("#loggedInName").text(ftapi.users[ftapi.uid].username); + } + } + } + if (ftapi.uid) { + if (ftapi.users[ftapi.uid]) { + if (ftapi.users[ftapi.uid].supermod) { + if ($("#ftSuperCopButton").is(":hidden")) { + $("#ftSuperCopButton").show(); + } + } + } + } + var count = Object.keys(okdata).length; + $("#label1 .count").text(" (" + count + ")"); + firetable.debug && console.log("users:", okdata); + }); + var $chatTemplate = $("#chatKEY").remove(); + ftapi.events.on("newChat", function (chatData) { + var namebo = chatData.id; + var utitle = ""; - $("#label1").bind("click.lb1tab", firetable.ui.usertab1); - $("#label2").bind("click.lb2tab", firetable.ui.usertab2); - $("#addToQueueBttn").bind("click", function() { - $("#mainqueuestuff").css("display", "none"); - $("#filterMachine").css("display", "none"); - $("#addbox").css("display", "flex"); - $("#cancelqsearch").show(); - $("#qControlButtons").hide(); + var atBottom = false; + if (firetable.utilities.isChatPrettyMuchAtBottom()) atBottom = true; - $("#plmanager").css("display", "none"); - }); + var you = ftapi.uid; + if (ftapi.users[ftapi.uid]) { + if (ftapi.users[ftapi.uid].username) you = ftapi.users[ftapi.uid].username; + } - $("#minimodeoptions .tab").bind("click", function(event) { - $("#mainGrid").removeClass().addClass($(this).attr('id')); - $("#minimodeoptions .tab").removeClass('on'); - $(this).addClass('on'); - }); + if (ftapi.users[chatData.id]) { + if (ftapi.users[chatData.id].username) namebo = ftapi.users[chatData.id].username; + if (ftapi.users[chatData.id].mod) utitle = "mod"; + if (ftapi.users[chatData.id].supermod) utitle = "supermod"; + if (ftapi.users[chatData.id].hostbot) utitle = "robot"; + } else if (chatData.name) { + namebo = chatData.name; + } - $("#plmaker").bind("keyup", function(e) { - if (e.which == 13) { - var val = $("#plmaker").val(); - if (val != "") { - var listid = ftapi.actions.createList(val); - $("#listpicker").append(""); - $("#listpicker").val(listid).change(); - ftapi.actions.switchList(listid); - } - } - }); - $("#cancelqsearch").bind("click", function() { - $("#mainqueuestuff").css("display", "block"); - $("#filterMachine").css("display", "block"); - $("#cancelqsearch").hide(); - $("#qControlButtons").show(); - - $("#addbox").css("display", "none"); - if (firetable.preview) { - if (firetable.preview.slice(0, 5) == "ytcid" || firetable.preview.slice(0, 5) == "sccid") { - $("#pv" + firetable.preview).html(""); - clearTimeout(firetable.ptimeout); - firetable.ptimeout = null; - $("#pvbar" + firetable.preview).css("background-image", "none"); - clearInterval(firetable.movePvBar); - firetable.movePvBar = null; - firetable.preview = false; - //start regular song - var nownow = Date.now(); - var timeSince = nownow - firetable.song.started; - if (timeSince <= 0) timeSince = 0; - - var secSince = Math.floor(timeSince / 1000); - var timeLeft = firetable.song.duration - secSince; - if (firetable.song.type == 1) { - if (!firetable.preview) { - if (firetable.scLoaded) firetable.scwidget.pause(); - if (!firetable.disableMediaPlayback) player.loadVideoById(firetable.song.cid, secSince, "large"); + var badoop = false; + if (chatData.txt.match("@" + you, "i") || chatData.txt.match(/\@everyone/)) { + var oknow = Date.now(); + if (oknow - chatData.time < 10 * 1000) { + firetable.utilities.playSound("sound"); + if (firetable.desktopNotifyMentions) firetable.utilities.desktopNotify(chatData, namebo); + badoop = true; + } } - } else if (firetable.song.type == 2) { - if (!firetable.preview) { - if (firetable.ytLoaded) player.stopVideo(); - firetable.scSeek = timeSince; - if (!firetable.disableMediaPlayback) firetable.scwidget.load("http://api.soundcloud.com/tracks/" + firetable.song.cid, { - auto_play: true - }); + if (chatData.id == firetable.lastChatPerson && !badoop) { + $("#chat" + firetable.lastChatId + " .chatContent").append( + '
    ' + ); + $("#chatTime" + firetable.lastChatId).text(firetable.utilities.format_time(chatData.time)); + var txtOut = firetable.ui.strip(chatData.txt); + txtOut = firetable.ui.showImages(txtOut); + txtOut = firetable.ui.textToLinks(txtOut); + txtOut = firetable.utilities.emojiShortnamestoUnicode(txtOut); + txtOut = txtOut.replace(/\`(.*?)\`/g, function (x) { + return "" + x.replace(/\`/g, "") + ""; + }); + if (chatData.hidden) txtOut = "[message removed]"; + $("#chattxt" + chatData.chatID).html(txtOut); + var canBeDeleted = false; + if (ftapi.users[ftapi.uid].mod || ftapi.users[ftapi.uid].supermod) { + if (ftapi.users[chatData.id]) { + if (!ftapi.users[chatData.id].mod && !ftapi.users[chatData.id].supermod) { + canBeDeleted = true; + } + } else { + canBeDeleted = true; + } + if (canBeDeleted && !chatData.hidden) { + // add delete button + $("#chattxt" + chatData.chatID).addClass("deleteMe"); + $("#chattxt" + chatData.chatID).append('
    x
    '); + $("#chattxt" + chatData.chatID) + .find(".modDelete") + .on("click", function () { + console.log("DELETE CHAT", chatData); + ftapi.actions.deleteChat(chatData.feedID); + }); + } + } + twemoji.parse(document.getElementById("chattxt" + chatData.chatID)); + } else { + var $chatthing = $chatTemplate.clone(); + $chatthing.attr("id", "chat" + chatData.chatID); + $chatthing + .find(".botson") + .css( + "background-image", + "url(https://indiediscotheque.com/robots/" + chatData.id + namebo + ".png?size=110x110" + ); + $chatthing.find(".utitle").html(utitle); + $chatthing + .find(".chatTime") + .attr("id", "chatTime" + chatData.chatID) + .html(firetable.utilities.format_time(chatData.time)); + if (badoop) $chatthing.addClass("badoop"); + var txtOut = firetable.ui.strip(chatData.txt); + txtOut = firetable.ui.showImages(txtOut); + txtOut = firetable.ui.textToLinks(txtOut); + txtOut = firetable.utilities.emojiShortnamestoUnicode(txtOut); + txtOut = txtOut.replace(/\`(.*?)\`/g, function (x) { + return "" + x.replace(/\`/g, "") + ""; + }); + if (chatData.hidden) txtOut = "[message removed]"; + $chatthing + .find(".chatText") + .html(txtOut) + .attr("id", "chattxt" + chatData.chatID); + console.log(chatData); + + $chatthing.find(".chatName").text(namebo); + firetable.utilities.chatAt($chatthing.find(".botson")); // adds the click event to @ the user + firetable.utilities.chatAt($chatthing.find(".chatName")); // adds the click event to @ the user + twemoji.parse($chatthing.find(".chatText")[0]); + $chatthing.appendTo("#chats"); + try { + if (ftapi.users[ftapi.uid].mod || ftapi.users[ftapi.uid].supermod) { + var canBeDeleted = false; + if (ftapi.users[chatData.id]) { + if ( + !ftapi.users[chatData.id].mod && + !ftapi.users[chatData.id].supermod && + !chatData.hidden + ) { + canBeDeleted = true; + } + } else { + canBeDeleted = true; + } + if (canBeDeleted && !chatData.hidden) { + // add delete button + $chatthing.find(".chatText").addClass("deleteMe"); + $chatthing.find(".chatText").append('
    x
    '); + $chatthing.find(".modDelete").on("click", function () { + ftapi.actions.deleteChat(chatData.feedID); + }); + } + } + } catch (e) { + console.log(e); + } + firetable.lastChatPerson = chatData.id; + firetable.lastChatId = chatData.chatID; } - } - } - } - }); - $("#grab").bind("click", function() { - var isHidden = $("#stealContain").is(":hidden"); - if (isHidden) { - ftapi.lookup.allLists(function(allPlaylists) { - $("#stealpicker").html(""); - for (var key in allPlaylists) { - if (allPlaylists.hasOwnProperty(key)) { - $("#stealpicker").append(""); - } - } - $('#grab').addClass('on'); - $("#stealContain").css({ - 'top': $('#grab').offset().top + $('#grab').height(), - 'left': $('#grab').offset().left - 16 - }).show(); - }); - - } else { - $('#grab').removeClass('on'); - $("#stealContain").hide(); - } - }); - $("#shuffleQueue").bind("click", firetable.actions.shuffleQueue); - $("#history").bind("click", function() { - $("#thehistoryWrap").slideToggle().css('top', $('#stage').outerHeight() + $('#topbar').outerHeight()); - $(this).toggleClass('on'); - }); - $("#startMerge").bind("click", function() { - var source = $("#mergepicker").val(); - var sourceName = $("#mergepicker option:selected").text(); - var dest = $("#mergepicker2").val(); - var destName = $("#mergepicker2 option:selected").text(); - $("#mergeSetup").hide(); - $("#mergeHappening").show(); - firetable.debug && console.log(sourceName + " -> " + destName); - firetable.actions.mergeLists(source, dest, sourceName); - }); - $("#mergeOK").bind("click", function() { - $("#mergeSetup").show(); - $("#mergeCompleted").hide(); - $("#mergeHappening").hide(); - $("#mergeContain").hide(); - }); - $("#mergeLists").bind("click", function() { - var $this = $(this); - var isHidden = $("#mergeContain").is(":hidden"); - if (isHidden) { - ftapi.lookup.allLists(function(allPlaylists) { - $("#mergepicker").html(""); - $("#mergepicker2").html(""); - for (var key in allPlaylists) { - if (allPlaylists.hasOwnProperty(key)) { - $("#mergepicker").append(""); - $("#mergepicker2").append(""); - } - } - if (ftapi.users[ftapi.uid]) { - if (ftapi.users[ftapi.uid].selectedList) { - $("#mergepicker").val(ftapi.users[ftapi.uid].selectedList).change(); - $("#mergepicker2").val(-1).change(); - } - } - $("#mergeContain").show(); - $this.addClass('on'); - }); - - } else { - $("#mergeContain").hide(); - $this.removeClass('on'); - } - }); - $("#reloadtrack").bind("click", firetable.actions.reloadtrack); + if (chatData.card) { + $("#chattxt" + chatData.chatID).append( + '' + ); - $("#importDubGo").bind("click", firetable.actions.dubtrackImport); + firetable.actions.showCard(chatData.card, chatData.chatID); + firetable.debug && console.log("showin card"); + } - $("#volstatus").bind("click", function() { - firetable.actions.muteToggle(); - }); - $(".openModal").bind("click", function() { - var modalContentID = $(this).attr('data-modal'); - $(".modalThing").removeClass('show'); - $("#overlay").addClass('show'); - $("#" + modalContentID).addClass('show'); - }); - $(".closeModal").bind("click", function() { - $("#overlay").removeClass('show'); - $("#deletepicker").html(""); - firetable.actions.cardCase(); - $("#plMachine").val(""); - }); - $(document).on("click", ".closeeditor", function() { - $(this).closest('.pvbar').removeClass('editing').find('.tagPromptBox').remove(); - firetable.songToEdit = null; - }); - $("#cardCaseButton").bind("click", function() { - firetable.actions.cardCase(); - $("#cardsOverlay").show(); - }); - $("#pickerNav").on("click", "span", function() { - try { - var sec = $(this)[0].id; - firetable.emojis.sec(sec); - } catch (s) {} - }); - $("#pickEmoji").bind("click", function() { - //toggle emoji picker - if ($("#emojiPicker").is(":hidden")) { - $(this).addClass('on'); - $("#emojiPicker").slideDown(function() { - $('#pickerSearch').focus(); + if (atBottom || ftapi.uid == chatData.id) firetable.utilities.scrollToBottom(); + else $("#morechats").addClass("show"); }); - if (!firetable.pickerInit) { - const makeRequest = async () => { - twemoji.parse(document.getElementById("pickerResults")); - return true; - } + ftapi.events.on("chatRemoved", function (data) { + console.log("CHAT DELETED", data); + $("#chattxt" + data.chatID).text("[message removed]"); + if (ftapi.users[ftapi.uid].mod || ftapi.users[ftapi.uid].supermod) + $("#chattxt" + data.chatID).removeClass("deleteMe"); + }); - makeRequest() - } - } else { - $(this).removeClass('on'); - $("#emojiPicker").slideUp(function() { - $('#pickerSearch').val('').trigger('change'); - $('#newchat').focus(); + ftapi.events.on("playlistChanged", function (okdata, listID) { + firetable.queue = okdata; + $("#mainqueue").html(""); + for (var key in okdata) { + if (okdata.hasOwnProperty(key)) { + var $newli = $playlistItemTemplate.clone(); + var thisone = okdata[key]; + var psign = ""; + if (key == firetable.preview) { + psign = ""; + } + $newli.attr("id", "pvbar" + key); + $newli.attr("data-key", key); + $newli.attr("data-type", thisone.type); + $newli + .find(".previewicon") + .attr("id", "pv" + key) + .on("click", function () { + firetable.actions.pview( + $(this).closest(".pvbar").attr("data-key"), + false, + $(this).closest(".pvbar").attr("data-type") + ); + }) + .html(psign); + $newli.find(".listwords").html(thisone.name); + $newli.find(".bumpsongs").on("click", function () { + firetable.actions.bumpSongInQueue($(this).closest(".pvbar").attr("data-key")); + }); + $newli.find(".bottomsongs").on("click", function () { + var oldID = $(this).closest(".pvbar").attr("data-key"); + ftapi.actions.moveTrackToBottom($(this).closest(".pvbar").attr("data-key"), function (newID) { + if (firetable.preview) { + // visually update preview in the new location if applicable + if (firetable.preview == oldID) { + firetable.preview = newID; + $("#pv" + newID).html(""); + } + } + }); + }); + if (thisone.flagged) { + var flagLabel = "broken"; + var flagIcon = "warning"; + if (thisone.flagged.code == 7) { + flagLabel = "age restricted"; + } else if (thisone.flagged.code >= 8) { + if (thisone.flagged.code == 8) { + // manual broken flagged by mod + flagLabel = "broken (manual)"; + } else if (thisone.flagged.code == 9) { + // low quality + flagLabel = "low audio quality"; + flagIcon = "disc_full"; + } else if (thisone.flagged.code == 10) { + // offtheme + flagLabel = "offtheme"; + flagIcon = "flag"; + } + } + $newli.find(".track-warning").html(' ' + flagIcon + " "); + $newli + .find(".track-warning") + .prop( + "title", + "Flagged as " + + flagLabel + + " on " + + firetable.utilities.format_date(thisone.flagged.date) + + ". Click to remove flag." + ); + $newli.find(".track-warning").on("click", function () { + ftapi.actions.unflagTrack($(this).closest(".pvbar").attr("data-key")); + $(this).html(""); + }); + } + $newli.find(".edittags").on("click", function () { + firetable.actions.editTagsPrompt($(this).closest(".pvbar").attr("data-key")); + }); + $newli.find(".deletesong").on("click", function () { + firetable.actions.deleteSong($(this).closest(".pvbar").attr("data-key")); + }); + $("#mainqueue").append($newli); + } + } }); - } - }); - $("#morechats .butt").bind("click", function() { - firetable.utilities.scrollToBottom(); - }); + firetable.ui.LinkGrabber.start(); - $("#fire").bind("click", function() { - ftapi.actions.sendChat(":fire:"); - $("#cloud_with_rain").removeClass("on"); - $("#fire").addClass("on"); - }); + $("#label1").bind("click.lb1tab", firetable.ui.usertab1); + $("#label2").bind("click.lb2tab", firetable.ui.usertab2); + $("#addToQueueBttn").bind("click", function () { + $("#mainqueuestuff").css("display", "none"); + $("#filterMachine").css("display", "none"); + $("#addbox").css("display", "flex"); + $("#cancelqsearch").show(); + $("#qControlButtons").hide(); - $("#cloud_with_rain").bind("click", function() { - ftapi.actions.sendChat(":cloud_with_rain:"); - $("#cloud_with_rain").addClass("on"); - $("#fire").removeClass("on"); - }); + $("#plmanager").css("display", "none"); + }); + $("#minimodeoptions .tab").bind("click", function (event) { + $("#mainGrid").removeClass().addClass($(this).attr("id")); + $("#minimodeoptions .tab").removeClass("on"); + $(this).addClass("on"); + }); - //SETTINGS TOGGLES - $('#badoopToggle').change(function() { - if (this.checked) { - firetable.debug && console.log("badoop on"); - localStorage["firetableBadoop"] = true; - firetable.playBadoop = true; - } else { - firetable.debug && console.log("badoop off"); - localStorage["firetableBadoop"] = false; - firetable.playBadoop = false; + $("#plmaker").bind("keyup", function (e) { + if (e.which == 13) { + var val = $("#plmaker").val(); + if (val != "") { + var listid = ftapi.actions.createList(val); + $("#listpicker").append( + '" + ); + $("#listpicker").val(listid).change(); + ftapi.actions.switchList(listid); + } + } + }); + $("#cancelqsearch").bind("click", function () { + $("#mainqueuestuff").css("display", "block"); + $("#filterMachine").css("display", "block"); + $("#cancelqsearch").hide(); + $("#qControlButtons").show(); - } - }); - $('#showImagesToggle').change(function() { - if (this.checked) { - firetable.debug && console.log("show images on"); - localStorage["firetableShowImages"] = true; - firetable.showImages = true; - } else { - firetable.debug && console.log("show images off"); - localStorage["firetableShowImages"] = false; - firetable.showImages = false; + $("#addbox").css("display", "none"); + if (firetable.preview) { + if (firetable.preview.slice(0, 5) == "ytcid" || firetable.preview.slice(0, 5) == "sccid") { + $("#pv" + firetable.preview).html(""); + clearTimeout(firetable.ptimeout); + firetable.ptimeout = null; + $("#pvbar" + firetable.preview).css("background-image", "none"); + clearInterval(firetable.movePvBar); + firetable.movePvBar = null; + firetable.preview = false; + //start regular song + var nownow = Date.now(); + var timeSince = nownow - firetable.song.started; + if (timeSince <= 0) timeSince = 0; + + var secSince = Math.floor(timeSince / 1000); + var timeLeft = firetable.song.duration - secSince; + if (firetable.song.type == 1) { + if (!firetable.preview) { + if (firetable.scLoaded) firetable.scwidget.pause(); + if (!firetable.disableMediaPlayback) + player.loadVideoById(firetable.song.cid, secSince, "large"); + } + } else if (firetable.song.type == 2) { + if (!firetable.preview) { + if (firetable.ytLoaded) player.stopVideo(); + firetable.scSeek = timeSince; + if (!firetable.disableMediaPlayback) + firetable.scwidget.load("http://api.soundcloud.com/tracks/" + firetable.song.cid, { + auto_play: true, + }); + } + } + } + } + }); - } - }); - $('#mediaDisableToggle').change(function() { - if (this.checked) { - firetable.debug && console.log("media disable on"); - localStorage["firetableDisableMedia"] = true; - firetable.disableMediaPlayback = true; - if (firetable.scLoaded) firetable.scwidget.pause(); - if (firetable.ytLoaded) player.stopVideo(); - firetable.ui.hidePlayerControls(); - } else { - firetable.debug && console.log("media disable off"); - localStorage["firetableDisableMedia"] = false; - firetable.disableMediaPlayback = false; - firetable.ui.showPlayerControls(); - firetable.actions.reloadtrack(); - } - }); - $('#showAvatarsToggle').change(function() { - if (this.checked) { - firetable.debug && console.log("show avatars on"); - localStorage["firetableShowAvatars"] = true; - firetable.showAvatars = true; - document.getElementById("actualChat").classList.remove("avatarsOff"); - } else { - firetable.debug && console.log("show avatars off"); - localStorage["firetableShowAvatars"] = false; - firetable.showAvatars = false; - document.getElementById("actualChat").classList.add("avatarsOff"); - } - }); - $(document).on('click', '.hideImage', function(e) { - e.stopPropagation(); - e.preventDefault(); - $(this).closest('.chatText').toggleClass('hideImg'); - }); - $('#desktopNotifyMentionsToggle').change(function() { - if (this.checked) { - firetable.debug && console.log("dtnm on"); - localStorage["firetableDTNM"] = true; - firetable.desktopNotifyMentions = true; - if (Notification) { - if (Notification.permission !== "granted") { - Notification.requestPermission(); - } - } - } else { - firetable.debug && console.log("dtnm off"); - localStorage["firetableDTNM"] = false; - firetable.desktopNotifyMentions = false; + $("#grab").bind("click", function () { + var isHidden = $("#stealContain").is(":hidden"); + if (isHidden) { + ftapi.lookup.allLists(function (allPlaylists) { + $("#stealpicker").html( + '' + ); + for (var key in allPlaylists) { + if (allPlaylists.hasOwnProperty(key)) { + $("#stealpicker").append( + '" + ); + } + } + $("#grab").addClass("on"); + $("#stealContain") + .css({ + top: $("#grab").offset().top + $("#grab").height(), + left: $("#grab").offset().left - 16, + }) + .show(); + }); + } else { + $("#grab").removeClass("on"); + $("#stealContain").hide(); + } + }); + $("#shuffleQueue").bind("click", firetable.actions.shuffleQueue); + $("#history").bind("click", function () { + $("#thehistoryWrap") + .slideToggle() + .css("top", $("#stage").outerHeight() + $("#topbar").outerHeight()); + $(this).toggleClass("on"); + }); + $("#startMerge").bind("click", function () { + var source = $("#mergepicker").val(); + var sourceName = $("#mergepicker option:selected").text(); + var dest = $("#mergepicker2").val(); + var destName = $("#mergepicker2 option:selected").text(); + $("#mergeSetup").hide(); + $("#mergeHappening").show(); + firetable.debug && console.log(sourceName + " -> " + destName); + firetable.actions.mergeLists(source, dest, sourceName); + }); + $("#mergeOK").bind("click", function () { + $("#mergeSetup").show(); + $("#mergeCompleted").hide(); + $("#mergeHappening").hide(); + $("#mergeContain").hide(); + }); + $("#mergeLists").bind("click", function () { + var $this = $(this); + var isHidden = $("#mergeContain").is(":hidden"); + if (isHidden) { + ftapi.lookup.allLists(function (allPlaylists) { + $("#mergepicker").html(''); + $("#mergepicker2").html( + '' + ); + for (var key in allPlaylists) { + if (allPlaylists.hasOwnProperty(key)) { + $("#mergepicker").append( + '" + ); + $("#mergepicker2").append( + '" + ); + } + } + if (ftapi.users[ftapi.uid]) { + if (ftapi.users[ftapi.uid].selectedList) { + $("#mergepicker").val(ftapi.users[ftapi.uid].selectedList).change(); + $("#mergepicker2").val(-1).change(); + } + } + $("#mergeContain").show(); + $this.addClass("on"); + }); + } else { + $("#mergeContain").hide(); + $this.removeClass("on"); + } + }); + $("#reloadtrack").bind("click", firetable.actions.reloadtrack); - } - }); + $("#importDubGo").bind("click", firetable.actions.dubtrackImport); - $('input[type=radio][name=screenControl]').change(function() { - firetable.debug && console.log('screen control:', this.value); - localStorage["firetableScreenControl"] = this.value; - firetable.screenControl = this.value; - if (this.value == "off") { - firetable.utilities.screenUp(); - } else if (this.value == "on") { - firetable.utilities.screenDown(); - } else if (this.value == "sync") { - if (firetable.screenSyncPos) { - firetable.utilities.screenDown(); - } else { - firetable.utilities.screenUp(); - } - } - }); + $("#volstatus").bind("click", function () { + firetable.actions.muteToggle(); + }); + $(".openModal").bind("click", function () { + var modalContentID = $(this).attr("data-modal"); + $(".modalThing").removeClass("show"); + $("#overlay").addClass("show"); + $("#" + modalContentID).addClass("show"); + }); + $(".closeModal").bind("click", function () { + $("#overlay").removeClass("show"); + $("#deletepicker").html(""); + firetable.actions.cardCase(); + $("#plMachine").val(""); + }); + $(document).on("click", ".closeeditor", function () { + $(this).closest(".pvbar").removeClass("editing").find(".tagPromptBox").remove(); + firetable.songToEdit = null; + }); + $("#cardCaseButton").bind("click", function () { + firetable.actions.cardCase(); + $("#cardsOverlay").show(); + }); + $("#pickerNav").on("click", "span", function () { + try { + var sec = $(this)[0].id; + firetable.emojis.sec(sec); + } catch (s) {} + }); + $("#pickEmoji").bind("click", function () { + //toggle emoji picker + if ($("#emojiPicker").is(":hidden")) { + $(this).addClass("on"); + $("#emojiPicker").slideDown(function () { + $("#pickerSearch").focus(); + }); + if (!firetable.pickerInit) { + const makeRequest = async () => { + twemoji.parse(document.getElementById("pickerResults")); + return true; + }; - $("#stealpicker").change(function() { - var dest = $("#stealpicker").val(); - if (dest == "-1") return; - if (firetable.song.cid != 0) { - var title = firetable.song.artist + " - " + firetable.song.title; - $("#grab").removeClass('on'); - ftapi.actions.addToList(firetable.song.type, title, firetable.song.cid, dest); - $("#stealContain").hide(); - } - }); + makeRequest(); + } + } else { + $(this).removeClass("on"); + $("#emojiPicker").slideUp(function () { + $("#pickerSearch").val("").trigger("change"); + $("#newchat").focus(); + }); + } + }); - $("#pldeleteButton").bind("click", function() { - var val = $("#deletepicker").val(); - firetable.debug && console.log('playlist delete:', val); - if (ftapi.users[ftapi.uid]) { - if (ftapi.users[ftapi.uid].selectedList) { - if (ftapi.users[ftapi.uid].selectedList == val) { - $("#listpicker").val("0").change(); - } - } - } - ftapi.actions.deleteList(val); - $("#pdopt" + val).remove(); - $("#overlay").removeClass('show'); - }); - $("#plimportLauncher").bind("click", function() { - $("#overlay").addClass('show'); - $(".modalThing").removeClass('show'); - $('#importPromptBox').addClass('show'); - }); - $("#pldeleteLauncher").bind("click", function() { - ftapi.lookup.allLists(function(allPlaylists) { - $("#deletepicker").html(""); - for (var key in allPlaylists) { - if (allPlaylists.hasOwnProperty(key)) { - $("#deletepicker").append(""); - } - } - $("#overlay").addClass('show'); - $(".modalThing").removeClass('show'); - $('#deletePromptBox').addClass('show'); - }); - }); - $("#pickerSearch").on("change paste keyup", function() { - firetable.emojis.niceSearch($("#pickerSearch").val()); - }); - $("#queueFilter").on("change paste keyup", function() { - firetable.actions.filterQueue($("#queueFilter").val()); - }); - $("#pickerResults").on("click", "span", function() { - try { - var oldval = $("#newchat").val(); - var newval = oldval + ":" + $(this).attr("title").trim() + ":"; - $("#newchat").focus().val(newval); + $("#morechats .butt").bind("click", function () { + firetable.utilities.scrollToBottom(); + }); - } catch (s) {} - }); + $("#fire").bind("click", function () { + ftapi.actions.sendChat(":fire:"); + $("#cloud_with_rain").removeClass("on"); + $("#fire").addClass("on"); + }); - firetable.ui.loginEventsInit(); + $("#cloud_with_rain").bind("click", function () { + ftapi.actions.sendChat(":cloud_with_rain:"); + $("#cloud_with_rain").addClass("on"); + $("#fire").removeClass("on"); + }); - $("#ytsearchSelect").bind("click", function() { - $("#scsearchSelect").removeClass("on"); - $("#ytsearchSelect").addClass("on"); - firetable.searchSelectsChoice = 1; - }); - $("#scsearchSelect").bind("click", function() { - $("#ytsearchSelect").removeClass("on"); - $("#scsearchSelect").addClass("on"); - firetable.searchSelectsChoice = 2; - }); - $("#ytimportchoice").bind("click", function() { - firetable.debug && console.log("yt import"); - firetable.importSelectsChoice = 1; - }); - $("#scimportchoice").bind("click", function() { - firetable.debug && console.log("sc import"); - firetable.importSelectsChoice = 2; - }); - $("#dtimportchoice").bind("click", function() { - firetable.debug && console.log("dt import"); - firetable.importSelectsChoice = 3; - }); - $(document).on("keyup", ".tagMachine", function(e) { - if (e.which == 13) { - var songKey = $(this).closest('.tagPromptBox').prev('.pvbar').attr('data-key'); - if (firetable.songToEdit) { - var val = $(this).val(); - if (val != "") { - var obj = firetable.songToEdit; - ftapi.actions.editTrackTag(obj.key, obj.song.cid, val); - firetable.songToEdit = null; - $(this).closest('.editing').removeClass('editing').next('.tagPromptBox').remove(); - } - } - } - }); - $("#changeUsername").bind("keyup", function(e) { - if (e.which == 13) { - var oldDjName = ftapi.users[ftapi.uid].username; - var newDjName = $("#changeUsername").val(); - $("#usernameResponse").html(""); - if (newDjName != "") { - // try to change name - ftapi.actions.changeName(newDjName, function(error) { - if (error) { - alert(error); - $("#usernameResponse").text(error); + //SETTINGS TOGGLES + $("#badoopToggle").change(function () { + if (this.checked) { + firetable.debug && console.log("badoop on"); + localStorage["firetableBadoop"] = true; + firetable.playBadoop = true; } else { - $("#usernameResponse").text("Great job! Your name is now " + newDjName); - $("#loggedInName").text(newDjName); + firetable.debug && console.log("badoop off"); + localStorage["firetableBadoop"] = false; + firetable.playBadoop = false; } - }); - } - } - }); - $('#dubtrackimportfile').bind('change', firetable.ui.dubtrackImportFileSelect); - $("#supercopSearch").bind("keyup", function(e) { - if (e.which == 13) { - var val = $("#supercopSearch").val(); - $("#supercopResponse").html(""); - if (val != "") { - //begin user search... - ftapi.lookup.userByName(val, function(person) { - //check search results - if (person) { - //found something! - if (!person.supermod) { - ftapi.actions.banUser(person.userid); - $("#supercopResponse").html(person.username + " suspended."); - - } else { - $("#supercopResponse").text("Can not suspend that (or any) supermod."); - } + }); + $("#showImagesToggle").change(function () { + if (this.checked) { + firetable.debug && console.log("show images on"); + localStorage["firetableShowImages"] = true; + firetable.showImages = true; + } else { + firetable.debug && console.log("show images off"); + localStorage["firetableShowImages"] = false; + firetable.showImages = false; + } + }); + $("#mediaDisableToggle").change(function () { + if (this.checked) { + firetable.debug && console.log("media disable on"); + localStorage["firetableDisableMedia"] = true; + firetable.disableMediaPlayback = true; + if (firetable.scLoaded) firetable.scwidget.pause(); + if (firetable.ytLoaded) player.stopVideo(); + firetable.ui.hidePlayerControls(); + } else { + firetable.debug && console.log("media disable off"); + localStorage["firetableDisableMedia"] = false; + firetable.disableMediaPlayback = false; + firetable.ui.showPlayerControls(); + firetable.actions.reloadtrack(); + } + }); + $("#showAvatarsToggle").change(function () { + if (this.checked) { + firetable.debug && console.log("show avatars on"); + localStorage["firetableShowAvatars"] = true; + firetable.showAvatars = true; + document.getElementById("actualChat").classList.remove("avatarsOff"); } else { - $("#supercopResponse").text(val + " not found..."); + firetable.debug && console.log("show avatars off"); + localStorage["firetableShowAvatars"] = false; + firetable.showAvatars = false; + document.getElementById("actualChat").classList.add("avatarsOff"); } - }); - } - } - }); - $("#importSources .tab").bind("click", function(e) { - var searchFrom = firetable.importSelectsChoice; - if (searchFrom == 3) { - $("#importDubContent").show(); - $("#importContent").hide(); - } else { - $("#importDubContent").hide(); - $("#importContent").show(); - } - $(this).siblings().removeClass('on'); - $(this).addClass('on'); - }); - $("#plMachine").bind("keyup", function(e) { - if (e.which == 13) { - var val = $("#plMachine").val(); - if (val != "") { - $("#importResults").html(""); - $("#plMachine").val(""); - var searchFrom = firetable.importSelectsChoice; - if (searchFrom == 1) { - var listID; - var directLink = false; - //see if this is a particular list's url... - if (val.match(/youtube.com\/watch/) || val.match(/youtube.com\/playlist/)) { - function getQueryStringValue(str, key) { - return unescape(str.replace(new RegExp("^(?:.*[&\\?]" + escape(key).replace(/[\.\+\*]/g, "\\$&") + "(?:\\=([^&]*))?)?.*$", "i"), "$1")); - } - listID = getQueryStringValue(val, "list"); - if (listID) directLink = true; - } - - if (directLink) { - function keyWordsearch() { - gapi.client.setApiKey(ftconfigs.youtubeKey); - gapi.client.load('youtube', 'v3', function() { - makeRequest(); - }); - } - - function makeRequest() { - var request = gapi.client.youtube.playlists.list({ - id: listID, - part: 'snippet' - }); - request.execute(function(response) { - if (response.result) { - if (response.result.items) { - if (response.result.items.length === 1) { - var item = response.result.items[0]; - vidTitle = item.snippet.title; - $("#importResults").append("
    " + item.snippet.title + " by " + item.snippet.channelTitle + "
    "); - } else { - // no result - } + }); + $(document).on("click", ".hideImage", function (e) { + e.stopPropagation(); + e.preventDefault(); + $(this).closest(".chatText").toggleClass("hideImg"); + }); + $("#desktopNotifyMentionsToggle").change(function () { + if (this.checked) { + firetable.debug && console.log("dtnm on"); + localStorage["firetableDTNM"] = true; + firetable.desktopNotifyMentions = true; + if (Notification) { + if (Notification.permission !== "granted") { + Notification.requestPermission(); } - } - }) - } - keyWordsearch(); + } } else { - //youtube - function keyWordsearch() { - gapi.client.setApiKey(ftconfigs.youtubeKey); - gapi.client.load('youtube', 'v3', function() { - makeRequest(); - }); - } - - function makeRequest() { - var request = gapi.client.youtube.search.list({ - q: val, - type: 'playlist', - part: 'snippet', - maxResults: 15 - }); - request.execute(function(response) { - var srchItems = response.result.items; - firetable.debug && console.log('import search results:', response); - $.each(srchItems, function(index, item) { - vidTitle = item.snippet.title; - $("#importResults").append("
    " + item.snippet.title + " by " + item.snippet.channelTitle + "
    "); - }) - }) - } - keyWordsearch(); + firetable.debug && console.log("dtnm off"); + localStorage["firetableDTNM"] = false; + firetable.desktopNotifyMentions = false; } + }); - } else if (searchFrom == 2) { - var listData; - var directLink = false; - //see if this is a particular list's url... - console.log(val); - if (val.match(/.*\/\/soundcloud\.com\/.*\/sets\/.*/)) { - firetable.actions.resolveSCLink(val, function(item) { - if (item) { - if (item.sharing == "public" && item.kind == "playlist") { - $("#importResults").append("
    " + item.title + " by " + item.user.username + " (" + item.track_count + " songs)
    "); - } + $("input[type=radio][name=screenControl]").change(function () { + firetable.debug && console.log("screen control:", this.value); + localStorage["firetableScreenControl"] = this.value; + firetable.screenControl = this.value; + if (this.value == "off") { + firetable.utilities.screenUp(); + } else if (this.value == "on") { + firetable.utilities.screenDown(); + } else if (this.value == "sync") { + if (firetable.screenSyncPos) { + firetable.utilities.screenDown(); + } else { + firetable.utilities.screenUp(); } - }); - //var getList = SC.resolve(val).then(finishUp); + } + }); - } else { - //cloud sound world dot com - SC.get('/playlists', { - q: val - }).then(function(lists) { - for (var i = 0; i < lists.length; i++) { - var item = lists[i]; - if (item.sharing == "public") { - $("#importResults").append("
    " + item.title + " by " + item.user.username + " (" + item.track_count + " songs)
    "); - } - } - }); - } - } - } - } - }); - var $searchItemTemplate = $('#searchResults .pvbar').remove(); - $("#qsearch").bind("keyup", function(e) { - if (e.which == 13) { - var txt = $("#qsearch").val(); - if (firetable.searchSelectsChoice == 1) { - var showResults = function(response) { - firetable.debug && console.log('queue search:', response); - // $("#qsearch").val(""); - $('#searchResults').html(""); + $("#stealpicker").change(function () { + var dest = $("#stealpicker").val(); + if (dest == "-1") return; + if (firetable.song.cid != 0) { + var title = firetable.song.artist + " - " + firetable.song.title; + $("#grab").removeClass("on"); + ftapi.actions.addToList(firetable.song.type, title, firetable.song.cid, dest); + $("#stealContain").hide(); + } + }); - if (firetable.preview) { - if (firetable.preview.slice(0, 5) == "ytcid" || firetable.preview.slice(0, 5) == "sccid") { - $("#pv" + firetable.preview).html(""); - clearTimeout(firetable.ptimeout); - firetable.ptimeout = null; - $("#pvbar" + firetable.preview).css("background-image", "none"); - clearInterval(firetable.movePvBar); - firetable.movePvBar = null; - firetable.preview = false; - //start regular song - var nownow = Date.now(); - var timeSince = nownow - firetable.song.started; - if (timeSince <= 0) timeSince = 0; + $("#pldeleteButton").bind("click", function () { + var val = $("#deletepicker").val(); + firetable.debug && console.log("playlist delete:", val); + if (ftapi.users[ftapi.uid]) { + if (ftapi.users[ftapi.uid].selectedList) { + if (ftapi.users[ftapi.uid].selectedList == val) { + $("#listpicker").val("0").change(); + } + } + } + ftapi.actions.deleteList(val); + $("#pdopt" + val).remove(); + $("#overlay").removeClass("show"); + }); + $("#plimportLauncher").bind("click", function () { + $("#overlay").addClass("show"); + $(".modalThing").removeClass("show"); + $("#importPromptBox").addClass("show"); + }); + $("#pldeleteLauncher").bind("click", function () { + ftapi.lookup.allLists(function (allPlaylists) { + $("#deletepicker").html(""); + for (var key in allPlaylists) { + if (allPlaylists.hasOwnProperty(key)) { + $("#deletepicker").append( + '" + ); + } + } + $("#overlay").addClass("show"); + $(".modalThing").removeClass("show"); + $("#deletePromptBox").addClass("show"); + }); + }); + $("#pickerSearch").on("change paste keyup", function () { + firetable.emojis.niceSearch($("#pickerSearch").val()); + }); + $("#queueFilter").on("change paste keyup", function () { + firetable.actions.filterQueue($("#queueFilter").val()); + }); + $("#pickerResults").on("click", "span", function () { + try { + var oldval = $("#newchat").val(); + var newval = oldval + ":" + $(this).attr("title").trim() + ":"; + $("#newchat").focus().val(newval); + } catch (s) {} + }); - var secSince = Math.floor(timeSince / 1000); - var timeLeft = firetable.song.duration - secSince; - if (firetable.song.type == 1) { - if (!firetable.preview) { - if (firetable.scLoaded) firetable.scwidget.pause(); - if (!firetable.disableMediaPlayback) player.loadVideoById(firetable.song.cid, secSince, "large"); - } - } else if (firetable.song.type == 2) { - if (!firetable.preview) { - if (firetable.ytLoaded) player.stopVideo(); - firetable.scSeek = timeSince; - if (!firetable.disableMediaPlayback) firetable.scwidget.load("http://api.soundcloud.com/tracks/" + firetable.song.cid, { - auto_play: true + firetable.ui.loginEventsInit(); + + $("#ytsearchSelect").bind("click", function () { + $("#scsearchSelect").removeClass("on"); + $("#ytsearchSelect").addClass("on"); + firetable.searchSelectsChoice = 1; + }); + $("#scsearchSelect").bind("click", function () { + $("#ytsearchSelect").removeClass("on"); + $("#scsearchSelect").addClass("on"); + firetable.searchSelectsChoice = 2; + }); + $("#ytimportchoice").bind("click", function () { + firetable.debug && console.log("yt import"); + firetable.importSelectsChoice = 1; + }); + $("#scimportchoice").bind("click", function () { + firetable.debug && console.log("sc import"); + firetable.importSelectsChoice = 2; + }); + $("#dtimportchoice").bind("click", function () { + firetable.debug && console.log("dt import"); + firetable.importSelectsChoice = 3; + }); + $(document).on("keyup", ".tagMachine", function (e) { + if (e.which == 13) { + var songKey = $(this).closest(".tagPromptBox").prev(".pvbar").attr("data-key"); + if (firetable.songToEdit) { + var val = $(this).val(); + if (val != "") { + var obj = firetable.songToEdit; + ftapi.actions.editTrackTag(obj.key, obj.song.cid, val); + firetable.songToEdit = null; + $(this).closest(".editing").removeClass("editing").next(".tagPromptBox").remove(); + } + } + } + }); + $("#changeUsername").bind("keyup", function (e) { + if (e.which == 13) { + var oldDjName = ftapi.users[ftapi.uid].username; + var newDjName = $("#changeUsername").val(); + $("#usernameResponse").html(""); + if (newDjName != "") { + // try to change name + ftapi.actions.changeName(newDjName, function (error) { + if (error) { + alert(error); + $("#usernameResponse").text(error); + } else { + $("#usernameResponse").text("Great job! Your name is now " + newDjName); + $("#loggedInName").text(newDjName); + } }); - } } - } } - var srchItems = response.result.items; - $.each(srchItems, function(index, item) { - console.log(item); - var thecid; - if (item.kind == "youtube#searchResult") { - thecid = item.id.videoId; - } else if (item.kind == "youtube#video") { - thecid = item.id; - } - vidtitle = item.snippet.title; - var yargo = item.snippet.title.split(" - "); - var sartist = yargo[0]; - var stitle = yargo[1]; - if (!stitle) { - // yt title not formatted artist - title. use uploader name instead as artist - stitle = sartist; - sartist = item.snippet.channelTitle.replace(" - Topic", ""); - } - vidTitle = sartist + " - " + stitle; - var pkey = "ytcid" + thecid; - var $srli = $searchItemTemplate.clone(); - $srli.attr('id', "pvbar" + pkey); - $srli.attr("data-key", pkey); - $srli.attr("data-cid", thecid); - $srli.find('.previewicon').attr('id', "pv" + pkey).on('click', function() { - firetable.actions.pview($(this).closest('.pvbar').attr('data-key'), true, 1); - }); - $srli.find('.listwords').html(vidTitle); - $srli.find('.queuetrack').on('click', function() { - firetable.actions.queueTrack( - $(this).closest('.pvbar').attr('data-cid'), - firetable.utilities.htmlEscape($(this).closest('.pvbar').find('.listwords').text()), - 1 - ); - }); - $("#searchResults").append($srli); - }) - }; - var directLink = false; - var thecid = false; - //see if this is a particular track's url... - if (txt.match(/youtube.com\/watch/)) { - function getQueryStringValue(str, key) { - return unescape(str.replace(new RegExp("^(?:.*[&\\?]" + escape(key).replace(/[\.\+\*]/g, "\\$&") + "(?:\\=([^&]*))?)?.*$", "i"), "$1")); - } - thecid = getQueryStringValue(txt, "v"); - if (thecid) directLink = true; - } - if (directLink) { - firetable.debug && console.log("direct yt link found"); - - function keyWordsearch() { - gapi.client.setApiKey(ftconfigs.youtubeKey); - gapi.client.load('youtube', 'v3', function() { - makeRequest(); - }); - } - - function makeRequest() { - var request = gapi.client.youtube.videos.list({ - id: thecid, - part: 'snippet', - maxResults: 1 - }); - request.execute(function(response) { - console.log(response); - showResults(response); - }) + }); + $("#dubtrackimportfile").bind("change", firetable.ui.dubtrackImportFileSelect); + $("#supercopSearch").bind("keyup", function (e) { + if (e.which == 13) { + var val = $("#supercopSearch").val(); + $("#supercopResponse").html(""); + if (val != "") { + //begin user search... + ftapi.lookup.userByName(val, function (person) { + //check search results + if (person) { + //found something! + if (!person.supermod) { + ftapi.actions.banUser(person.userid); + $("#supercopResponse").html(person.username + " suspended."); + } else { + $("#supercopResponse").text("Can not suspend that (or any) supermod."); + } + } else { + $("#supercopResponse").text(val + " not found..."); + } + }); + } } - keyWordsearch(); - } else { - function keyWordsearch() { - gapi.client.setApiKey(ftconfigs.youtubeKey); - gapi.client.load('youtube', 'v3', function() { - makeRequest(); - }); - } - - function makeRequest() { - var q = $('#qsearch').val(); - $('#searchResults').html("Searching..."); - - var request = gapi.client.youtube.search.list({ - q: q, - type: 'video', - part: 'snippet', - maxResults: 15 - }); - request.execute(function(response) { - showResults(response); - }) + }); + $("#importSources .tab").bind("click", function (e) { + var searchFrom = firetable.importSelectsChoice; + if (searchFrom == 3) { + $("#importDubContent").show(); + $("#importContent").hide(); + } else { + $("#importDubContent").hide(); + $("#importContent").show(); } - keyWordsearch(); - } - } else if (firetable.searchSelectsChoice == 2) { - var q = $('#qsearch').val(); - var showResults = function(tracks) { - firetable.debug && console.log('sc tracks:', tracks); - // $("#qsearch").val(""); - $('#searchResults').html(""); - - if (firetable.preview) { - if (firetable.preview.slice(0, 5) == "ytcid" || firetable.preview.slice(0, 5) == "sccid") { - $("#pv" + firetable.preview).html(""); - clearTimeout(firetable.ptimeout); - firetable.ptimeout = null - $("#pvbar" + firetable.preview).css("background-image", "none"); - clearInterval(firetable.movePvBar); - firetable.movePvBar = null; - firetable.preview = false; - //start regular song - var nownow = Date.now(); - var timeSince = nownow - firetable.song.started; - if (timeSince <= 0) timeSince = 0; - - var secSince = Math.floor(timeSince / 1000); - var timeLeft = firetable.song.duration - secSince; - if (firetable.song.type == 1) { - if (!firetable.preview) { - firetable.scwidget.pause(); - if (!firetable.disableMediaPlayback) player.loadVideoById(firetable.song.cid, secSince, "large"); - } - } else if (firetable.song.type == 2) { - if (!firetable.preview) { - if (firetable.ytLoaded) player.stopVideo(); - firetable.scSeek = timeSince; - if (!firetable.disableMediaPlayback) firetable.scwidget.load("http://api.soundcloud.com/tracks/" + firetable.song.cid, { - auto_play: true + $(this).siblings().removeClass("on"); + $(this).addClass("on"); + }); + $("#plMachine").bind("keyup", function (e) { + if (e.which == 13) { + var val = $("#plMachine").val(); + if (val != "") { + $("#importResults").html(""); + $("#plMachine").val(""); + var searchFrom = firetable.importSelectsChoice; + if (searchFrom == 1) { + var listID; + var directLink = false; + //see if this is a particular list's url... + if (val.match(/youtube.com\/watch/) || val.match(/youtube.com\/playlist/)) { + function getQueryStringValue(str, key) { + return unescape( + str.replace( + new RegExp( + "^(?:.*[&\\?]" + + escape(key).replace(/[\.\+\*]/g, "\\$&") + + "(?:\\=([^&]*))?)?.*$", + "i" + ), + "$1" + ) + ); + } + listID = getQueryStringValue(val, "list"); + if (listID) directLink = true; + } + + if (directLink) { + function keyWordsearch() { + gapi.client.setApiKey(ftconfigs.youtubeKey); + gapi.client.load("youtube", "v3", function () { + makeRequest(); + }); + } + + function makeRequest() { + var request = gapi.client.youtube.playlists.list({ + id: listID, + part: "snippet", + }); + request.execute(function (response) { + if (response.result) { + if (response.result.items) { + if (response.result.items.length === 1) { + var item = response.result.items[0]; + vidTitle = item.snippet.title; + $("#importResults").append( + '
    ' + + item.snippet.title + + " by " + + item.snippet.channelTitle + + '
    ' + ); + } else { + // no result + } + } + } + }); + } + keyWordsearch(); + } else { + //youtube + function keyWordsearch() { + gapi.client.setApiKey(ftconfigs.youtubeKey); + gapi.client.load("youtube", "v3", function () { + makeRequest(); + }); + } + + function makeRequest() { + var request = gapi.client.youtube.search.list({ + q: val, + type: "playlist", + part: "snippet", + maxResults: 15, + }); + request.execute(function (response) { + var srchItems = response.result.items; + firetable.debug && console.log("import search results:", response); + $.each(srchItems, function (index, item) { + vidTitle = item.snippet.title; + $("#importResults").append( + '
    ' + + item.snippet.title + + " by " + + item.snippet.channelTitle + + '
    ' + ); + }); + }); + } + keyWordsearch(); + } + } else if (searchFrom == 2) { + var listData; + var directLink = false; + //see if this is a particular list's url... + console.log(val); + if (val.match(/.*\/\/soundcloud\.com\/.*\/sets\/.*/)) { + firetable.actions.resolveSCLink(val, function (item) { + if (item) { + if (item.sharing == "public" && item.kind == "playlist") { + $("#importResults").append( + '
    ' + + item.title + + " by " + + item.user.username + + " (" + + item.track_count + + ' songs)
    ' + ); + } + } + }); + //var getList = SC.resolve(val).then(finishUp); + } else { + //cloud sound world dot com + SC.get("/playlists", { + q: val, + }).then(function (lists) { + for (var i = 0; i < lists.length; i++) { + var item = lists[i]; + if (item.sharing == "public") { + $("#importResults").append( + '
    ' + + item.title + + " by " + + item.user.username + + " (" + + item.track_count + + ' songs)
    ' + ); + } + } + }); + } + } + } + } + }); + var $searchItemTemplate = $("#searchResults .pvbar").remove(); + $("#qsearch").bind("keyup", function (e) { + if (e.which == 13) { + var txt = $("#qsearch").val(); + if (firetable.searchSelectsChoice == 1) { + var showResults = function (response) { + firetable.debug && console.log("queue search:", response); + // $("#qsearch").val(""); + $("#searchResults").html(""); + + if (firetable.preview) { + if (firetable.preview.slice(0, 5) == "ytcid" || firetable.preview.slice(0, 5) == "sccid") { + $("#pv" + firetable.preview).html(""); + clearTimeout(firetable.ptimeout); + firetable.ptimeout = null; + $("#pvbar" + firetable.preview).css("background-image", "none"); + clearInterval(firetable.movePvBar); + firetable.movePvBar = null; + firetable.preview = false; + //start regular song + var nownow = Date.now(); + var timeSince = nownow - firetable.song.started; + if (timeSince <= 0) timeSince = 0; + + var secSince = Math.floor(timeSince / 1000); + var timeLeft = firetable.song.duration - secSince; + if (firetable.song.type == 1) { + if (!firetable.preview) { + if (firetable.scLoaded) firetable.scwidget.pause(); + if (!firetable.disableMediaPlayback) + player.loadVideoById(firetable.song.cid, secSince, "large"); + } + } else if (firetable.song.type == 2) { + if (!firetable.preview) { + if (firetable.ytLoaded) player.stopVideo(); + firetable.scSeek = timeSince; + if (!firetable.disableMediaPlayback) + firetable.scwidget.load( + "http://api.soundcloud.com/tracks/" + firetable.song.cid, + { + auto_play: true, + } + ); + } + } + } + } + var srchItems = response.result.items; + $.each(srchItems, function (index, item) { + console.log(item); + var thecid; + if (item.kind == "youtube#searchResult") { + thecid = item.id.videoId; + } else if (item.kind == "youtube#video") { + thecid = item.id; + } + vidtitle = item.snippet.title; + var yargo = item.snippet.title.split(" - "); + var sartist = yargo[0]; + var stitle = yargo[1]; + if (!stitle) { + // yt title not formatted artist - title. use uploader name instead as artist + stitle = sartist; + sartist = item.snippet.channelTitle.replace(" - Topic", ""); + } + vidTitle = sartist + " - " + stitle; + var pkey = "ytcid" + thecid; + var $srli = $searchItemTemplate.clone(); + $srli.attr("id", "pvbar" + pkey); + $srli.attr("data-key", pkey); + $srli.attr("data-cid", thecid); + $srli + .find(".previewicon") + .attr("id", "pv" + pkey) + .on("click", function () { + firetable.actions.pview($(this).closest(".pvbar").attr("data-key"), true, 1); + }); + $srli.find(".listwords").html(vidTitle); + $srli.find(".queuetrack").on("click", function () { + firetable.actions.queueTrack( + $(this).closest(".pvbar").attr("data-cid"), + firetable.utilities.htmlEscape($(this).closest(".pvbar").find(".listwords").text()), + 1 + ); + }); + $("#searchResults").append($srli); + }); + }; + var directLink = false; + var thecid = false; + //see if this is a particular track's url... + if (txt.match(/youtube.com\/watch/)) { + function getQueryStringValue(str, key) { + return unescape( + str.replace( + new RegExp( + "^(?:.*[&\\?]" + + escape(key).replace(/[\.\+\*]/g, "\\$&") + + "(?:\\=([^&]*))?)?.*$", + "i" + ), + "$1" + ) + ); + } + thecid = getQueryStringValue(txt, "v"); + if (thecid) directLink = true; + } + if (directLink) { + firetable.debug && console.log("direct yt link found"); + + function keyWordsearch() { + gapi.client.setApiKey(ftconfigs.youtubeKey); + gapi.client.load("youtube", "v3", function () { + makeRequest(); + }); + } + + function makeRequest() { + var request = gapi.client.youtube.videos.list({ + id: thecid, + part: "snippet", + maxResults: 1, + }); + request.execute(function (response) { + console.log(response); + showResults(response); + }); + } + keyWordsearch(); + } else { + function keyWordsearch() { + gapi.client.setApiKey(ftconfigs.youtubeKey); + gapi.client.load("youtube", "v3", function () { + makeRequest(); + }); + } + + function makeRequest() { + var q = $("#qsearch").val(); + $("#searchResults").html("Searching..."); + + var request = gapi.client.youtube.search.list({ + q: q, + type: "video", + part: "snippet", + maxResults: 15, + }); + request.execute(function (response) { + showResults(response); + }); + } + keyWordsearch(); + } + } else if (firetable.searchSelectsChoice == 2) { + var q = $("#qsearch").val(); + var showResults = function (tracks) { + firetable.debug && console.log("sc tracks:", tracks); + // $("#qsearch").val(""); + $("#searchResults").html(""); + + if (firetable.preview) { + if (firetable.preview.slice(0, 5) == "ytcid" || firetable.preview.slice(0, 5) == "sccid") { + $("#pv" + firetable.preview).html(""); + clearTimeout(firetable.ptimeout); + firetable.ptimeout = null; + $("#pvbar" + firetable.preview).css("background-image", "none"); + clearInterval(firetable.movePvBar); + firetable.movePvBar = null; + firetable.preview = false; + //start regular song + var nownow = Date.now(); + var timeSince = nownow - firetable.song.started; + if (timeSince <= 0) timeSince = 0; + + var secSince = Math.floor(timeSince / 1000); + var timeLeft = firetable.song.duration - secSince; + if (firetable.song.type == 1) { + if (!firetable.preview) { + firetable.scwidget.pause(); + if (!firetable.disableMediaPlayback) + player.loadVideoById(firetable.song.cid, secSince, "large"); + } + } else if (firetable.song.type == 2) { + if (!firetable.preview) { + if (firetable.ytLoaded) player.stopVideo(); + firetable.scSeek = timeSince; + if (!firetable.disableMediaPlayback) + firetable.scwidget.load( + "http://api.soundcloud.com/tracks/" + firetable.song.cid, + { + auto_play: true, + } + ); + } + } + } + } + var srchItems = tracks; + $.each(srchItems, function (index, item) { + vidTitle = item.title; + var yargo = item.title.split(" - "); + var sartist = yargo[0]; + var stitle = yargo[1]; + if (!stitle) { + stitle = sartist; + sartist = item.user.username; + } + vidTitle = sartist + " - " + stitle; + var pkey = "sccid" + item.id; + var $srli = $searchItemTemplate.clone(); + $srli.attr("id", "pvbar" + pkey); + $srli.attr("data-key", pkey); + $srli.attr("data-cid", item.id); + $srli + .find(".previewicon") + .attr("id", "pv" + pkey) + .on("click", function () { + firetable.actions.pview($(this).closest(".pvbar").attr("data-key"), true, 2); + }); + $srli.find(".listwords").html(vidTitle); + $srli.find(".queuetrack").on("click", function () { + firetable.actions.queueTrack( + $(this).closest(".pvbar").attr("data-cid"), + firetable.utilities.htmlEscape($(this).closest(".pvbar").find(".listwords").text()), + 2 + ); + }); + $("#searchResults").append($srli); + }); + }; + var directLink = false; + if (q.match(/:\/\/soundcloud\.com\//)) { + directLink = true; + } + $("#searchResults").html("Searching..."); + if (directLink) { + firetable.debug && console.log("sc direct link found"); + firetable.actions.resolveSCLink(q, function (item) { + var items = []; + if (item.kind == "track") items.push(item); + showResults(items); + }); + //var getList = SC.resolve(q).then(finishUp); + } else { + SC.get("/tracks", { + q: q, + }).then(function (tracks) { + showResults(tracks); + }); + } + } + } + }); + $("#newchat").bind("keypress", function (e) { + firetable.debug && console.log("chat key", e.key); + if (e.key == "Enter") { + var txt = $("#newchat").val(); + if (txt == "") return; + var matches = txt.match(/^(?:[\/])(\w+)\s*(.*)/i); + if (txt == ":fire:" || txt == "🔥") { + $("#cloud_with_rain").removeClass("on"); + $("#fire").addClass("on"); + } else if (txt == ":cloud_with_rain:" || txt == "🌧") { + $("#cloud_with_rain").addClass("on"); + $("#fire").removeClass("on"); + } + if (matches) { + var command = matches[1].toLowerCase(); + var args = matches[2]; + if (command == "mod") { + var personToMod = firetable.actions.uidLookup(args); + if (personToMod) { + ftapi.actions.modUser(personToMod); + } + } else if (command == "unmod") { + var personToMod = firetable.actions.uidLookup(args); + if (personToMod) { + ftapi.actions.unmodUser(personToMod); + } + } else if (command == "block") { + if (args) { + ftapi.actions.blockUser(args, function (response) { + console.log(response); + firetable.actions.localChatResponse(response); + }); + } + } else if (command == "unblock") { + if (args) { + ftapi.actions.unblockUser(args, function (response) { + console.log(response); + + firetable.actions.localChatResponse(response); + }); + } + } else if (command == "hot") { + ftapi.actions.sendChat(":fire:"); + $("#cloud_with_rain").removeClass("on"); + $("#fire").addClass("on"); + } else if (command == "storm") { + ftapi.actions.sendChat(":cloud_with_rain:"); + $("#cloud_with_rain").addClass("on"); + $("#fire").removeClass("on"); + } else if (command == "shrug") { + var thingtosay = "¯\\_(ツ)_/¯"; + if (args) thingtosay = args + " ¯\\_(ツ)_/¯"; + ftapi.actions.sendChat(thingtosay); + } else if (command == "tableflip") { + var thingtosay = "(╯°□°)╯︵ ┻━┻"; + if (args) thingtosay = args + " (╯°□°)╯︵ ┻━┻"; + ftapi.actions.sendChat(thingtosay); + } else if (command == "unflip") { + var thingtosay = "┬─┬ ノ( ゜-゜ノ)"; + if (args) thingtosay = args + " ┬─┬ ノ( ゜-゜ノ)"; + ftapi.actions.sendChat(thingtosay); + } + } else { + ftapi.actions.sendChat(txt); + } + $("#newchat").val(""); + $("#emojiPicker").slideUp(); + $("#pickEmoji").removeClass("on"); + firetable.utilities.exitAtLand(); + } else if (e.key == "@") { + // open the door to @ land + if (firetable.atLand) { + // double @@ + firetable.utilities.exitAtLand(); + } else { + // first step into @ land + firetable.utilities.initAtLand(); + for (var user of firetable.atUsersFiltered) { + $("#atPicker").addClass("show"); + $( + '
    " + ).appendTo("#atPicker"); + } + } + } else if (firetable.atLand) { + // we're in @ land + if (e.key == " " || e.key == "Spacebar") { + // we've got what we want + firetable.utilities.exitAtLand(); + } else if (!e.key.match(/[0-9a-zA-Z_]/)) { + // not possibly a characer from a name + firetable.atString += e.key; + $("#atPicker").html(""); + $('
    Usernames cannot contain "' + e.key + '"
    ').appendTo( + "#atPicker" + ); + } else { + // we're still in @ land + firetable.atString += e.key; + firetable.utilities.updateAtLand(); + } + } + }); + $("#newchat").bind("keyup", function (e) { + if (firetable.atLand) { + // we're in @ land + if (e.key == "Backspace") { + if (!firetable.atString) { + // deleting the @, exit @ land + firetable.utilities.exitAtLand(); + } else { + // still got someone we're lookin for + firetable.atString = firetable.atString.slice(0, -1); + firetable.utilities.updateAtLand(); + } + } else if (e.key == "ArrowUp") { + // i see my @, go up! + $("#atPicker .butt:last").focus(); + } else if (e.key == "ArrowDown") { + // i see my @, go down! + $("#atPicker .butt:first").focus(); + } + } + }); + $("#newchat").bind("keydown", function (e) { + if (e.key == "Tab") { + if (firetable.atUsersFiltered.length === 1) { + $("#newchat").one("blur", function (e) { + $("#newchat").focus().val($("#newchat").val()); }); - } + firetable.utilities.chooseAt(firetable.atUsersFiltered[0]); + } else { + firetable.utilities.exitAtLand(); } - } } - var srchItems = tracks; - $.each(srchItems, function(index, item) { - vidTitle = item.title; - var yargo = item.title.split(" - "); - var sartist = yargo[0]; - var stitle = yargo[1]; - if (!stitle) { - stitle = sartist; - sartist = item.user.username; - } - vidTitle = sartist + " - " + stitle; - var pkey = "sccid" + item.id; - var $srli = $searchItemTemplate.clone(); - $srli.attr('id', "pvbar" + pkey); - $srli.attr("data-key", pkey); - $srli.attr("data-cid", item.id); - $srli.find('.previewicon').attr('id', "pv" + pkey).on('click', function() { - firetable.actions.pview( - $(this).closest('.pvbar').attr('data-key'), - true, - 2 - ); - }); - $srli.find('.listwords').html(vidTitle); - $srli.find('.queuetrack').on('click', function() { - firetable.actions.queueTrack( - $(this).closest('.pvbar').attr('data-cid'), - firetable.utilities.htmlEscape($(this).closest('.pvbar').find('.listwords').text()), - 2 - ); - }); - $("#searchResults").append($srli); - }) - } - var directLink = false; - if (q.match(/:\/\/soundcloud\.com\//)) { - directLink = true; - } - $('#searchResults').html("Searching..."); - if (directLink) { - firetable.debug && console.log("sc direct link found"); - firetable.actions.resolveSCLink(q, function(item) { - var items = []; - if (item.kind == "track") items.push(item); - showResults(items); - }); - //var getList = SC.resolve(q).then(finishUp); - } else { - SC.get('/tracks', { - q: q - }).then(function(tracks) { - showResults(tracks); - }); - } - } - } - }); - $("#newchat").bind("keypress", function(e) { - firetable.debug && console.log('chat key', e.key); - if (e.key == "Enter") { - var txt = $("#newchat").val(); - if (txt == "") return; - var matches = txt.match(/^(?:[\/])(\w+)\s*(.*)/i); - if (txt == ":fire:" || txt == "🔥") { - $("#cloud_with_rain").removeClass("on"); - $("#fire").addClass("on"); - } else if (txt == ":cloud_with_rain:" || txt == "🌧") { - $("#cloud_with_rain").addClass("on"); - $("#fire").removeClass("on"); - } - if (matches) { - var command = matches[1].toLowerCase(); - var args = matches[2]; - if (command == "mod") { - var personToMod = firetable.actions.uidLookup(args); - if (personToMod) { - ftapi.actions.modUser(personToMod); - } - } else if (command == "unmod") { - var personToMod = firetable.actions.uidLookup(args); - if (personToMod) { - ftapi.actions.unmodUser(personToMod); - } - } else if (command == "block") { - if (args) { - ftapi.actions.blockUser(args, function(response) { - console.log(response); - firetable.actions.localChatResponse(response); - }); - } - } else if (command == "unblock") { - if (args) { - ftapi.actions.unblockUser(args, function(response) { - console.log(response); - - firetable.actions.localChatResponse(response); - }); - } - } else if (command == "hot") { - ftapi.actions.sendChat(":fire:"); - $("#cloud_with_rain").removeClass("on"); - $("#fire").addClass("on"); - } else if (command == "storm") { - ftapi.actions.sendChat(":cloud_with_rain:"); - $("#cloud_with_rain").addClass("on"); - $("#fire").removeClass("on"); - } else if (command == "shrug") { - var thingtosay = "¯\\_(ツ)_/¯"; - if (args) thingtosay = args + " ¯\\_(ツ)_/¯"; - ftapi.actions.sendChat(thingtosay); - } else if (command == "tableflip") { - var thingtosay = "(╯°□°)╯︵ ┻━┻"; - if (args) thingtosay = args + " (╯°□°)╯︵ ┻━┻"; - ftapi.actions.sendChat(thingtosay); - } else if (command == "unflip") { - var thingtosay = "┬─┬ ノ( ゜-゜ノ)"; - if (args) thingtosay = args + " ┬─┬ ノ( ゜-゜ノ)"; - ftapi.actions.sendChat(thingtosay); - } - } else { - ftapi.actions.sendChat(txt); - } - $("#newchat").val(""); - $("#emojiPicker").slideUp(); - $("#pickEmoji").removeClass("on"); - firetable.utilities.exitAtLand(); - } else if (e.key == "@") { // open the door to @ land - if (firetable.atLand) { // double @@ - firetable.utilities.exitAtLand(); - } else { // first step into @ land - firetable.utilities.initAtLand(); - for (var user of firetable.atUsersFiltered) { - $('#atPicker').addClass('show'); - $('
    ').appendTo('#atPicker'); - } - } - } else if (firetable.atLand) { // we're in @ land - if (e.key == " " || e.key == "Spacebar") { // we've got what we want - firetable.utilities.exitAtLand(); - } else if (!e.key.match(/[0-9a-zA-Z_]/)) { // not possibly a characer from a name - firetable.atString += e.key; - $('#atPicker').html(''); - $('
    Usernames cannot contain "' + e.key + '"
    ').appendTo('#atPicker'); - } else { // we're still in @ land - firetable.atString += e.key; - firetable.utilities.updateAtLand(); - } - } - }); - $("#newchat").bind("keyup", function(e) { - if (firetable.atLand) { // we're in @ land - if (e.key == "Backspace") { - if (!firetable.atString) { // deleting the @, exit @ land - firetable.utilities.exitAtLand(); - } else { // still got someone we're lookin for - firetable.atString = firetable.atString.slice(0, -1); - firetable.utilities.updateAtLand(); - } - } else if (e.key == "ArrowUp") { // i see my @, go up! - $('#atPicker .butt:last').focus(); - } else if (e.key == "ArrowDown") { // i see my @, go down! - $('#atPicker .butt:first').focus(); - } - } - }); - $("#newchat").bind("keydown", function(e) { - if (e.key == "Tab") { - if (firetable.atUsersFiltered.length === 1) { - $("#newchat").one("blur", function(e) { - $("#newchat").focus().val($("#newchat").val()); - }); - firetable.utilities.chooseAt(firetable.atUsersFiltered[0]); - } else { - firetable.utilities.exitAtLand(); - } - } - }); - $(document).on('click', '#atPicker .butt', function(e) { - e.preventDefault(); - firetable.utilities.chooseAt($(this).text().replace("@", "")); - setTimeout(() => { - var tempText = $("#newchat").val(); - $('#newchat').focus().val(''); - $('#newchat').val(tempText); - }, 250); - }); - $(document).on('keyup', '#atPicker .butt:focus', function(e) { - if (e.key == "ArrowUp") { - if ($('#atPicker .butt:focus').parent().prev().length) { - $('#atPicker .butt:focus').parent().prev().find('.butt').focus(); - } else { - $('#atPicker .butt:last').focus(); - } - } else if (e.key == "ArrowDown") { - if ($('#atPicker .butt:focus').parent().next().length) { - $('#atPicker .butt:focus').parent().next().find('.butt').focus(); - } else { - $('#atPicker .butt:first').focus(); - } - } - }); + }); + $(document).on("click", "#atPicker .butt", function (e) { + e.preventDefault(); + firetable.utilities.chooseAt($(this).text().replace("@", "")); + setTimeout(() => { + var tempText = $("#newchat").val(); + $("#newchat").focus().val(""); + $("#newchat").val(tempText); + }, 250); + }); + $(document).on("keyup", "#atPicker .butt:focus", function (e) { + if (e.key == "ArrowUp") { + if ($("#atPicker .butt:focus").parent().prev().length) { + $("#atPicker .butt:focus").parent().prev().find(".butt").focus(); + } else { + $("#atPicker .butt:last").focus(); + } + } else if (e.key == "ArrowDown") { + if ($("#atPicker .butt:focus").parent().next().length) { + $("#atPicker .butt:focus").parent().next().find(".butt").focus(); + } else { + $("#atPicker .butt:first").focus(); + } + } + }); - ftapi.events.on("colorsChanged", function(data) { - firetable.debug && console.log("COLOR CHANGE!", data); - - firetable.color = data.color; - firetable.countcolor = data.txt; - if (data.color == "#fff" || data.color == "#7f7f7f") { - firetable.color = firetable.orange; - firetable.countcolor = "#fff"; - $("#stage").css("background-color", "#fff"); - } else { - $("#stage").css("background-color", data.color); - } - /* + ftapi.events.on("colorsChanged", function (data) { + firetable.debug && console.log("COLOR CHANGE!", data); + + firetable.color = data.color; + firetable.countcolor = data.txt; + if (data.color == "#fff" || data.color == "#7f7f7f") { + firetable.color = firetable.orange; + firetable.countcolor = "#fff"; + $("#stage").css("background-color", "#fff"); + } else { + $("#stage").css("background-color", data.color); + } + /* if (firetable.countcolor == "#fff"){ firetable.countcolor = "#ffffffc9"; } else if (firetable.countcolor == "#000"){ @@ -3351,393 +3833,507 @@ firetable.ui = { } $("#stage").css("color", firetable.countcolor); */ - $('.customColorStyles').remove(); - - $('.festiveLights').remove(); - - var colorThing = firetable.utilities.hexToRGB(firetable.color); - $("head").append(""); + $(".customColorStyles").remove(); + + $(".festiveLights").remove(); + + var colorThing = firetable.utilities.hexToRGB(firetable.color); + $("head").append( + "" + ); + + if (firetable.lights) { + var style = + ""; + $("head").append(style); + } + }); + }, + usertab1: function () { + $("#allusersWrap").css("display", "block"); + $("#justwaitWrap").css("display", "none"); + $("#usertabs").find(".on").removeClass("on"); + $("#label1").addClass("on"); + }, + usertab2: function () { + $("#usertabs").find(".on").removeClass("on"); + $("#label2").addClass("on"); + $("#allusersWrap").css("display", "none"); + $("#justwaitWrap").css("display", "block"); + }, + LinkGrabber: { + textarea: null, - if (firetable.lights) { - var style = ""; - $("head").append(style); - } + /* Textarea Management */ - }); - }, - usertab1: function() { - $("#allusersWrap").css("display", "block"); - $("#justwaitWrap").css("display", "none"); - $("#usertabs").find(".on").removeClass("on"); - $("#label1").addClass("on"); - }, - usertab2: function() { - $("#usertabs").find(".on").removeClass("on"); - $("#label2").addClass("on"); - $("#allusersWrap").css("display", "none"); - $("#justwaitWrap").css("display", "block"); + attach_ta: function (event) { + if (!$.contains(document.getElementById("queuelist"), event.target)) return; + if (firetable.ui.LinkGrabber.textarea != null) return; - }, - LinkGrabber: { - textarea: null, + var textarea = (firetable.ui.LinkGrabber.textarea = document.createElement("textarea")); + textarea.setAttribute( + "style", + "position: fixed; width: 100%; margin: 0; top: 0; bottom: 0; right: 0; left: 0; z-index: 99999999" + ); + textarea.style.opacity = "0.000000000000000001"; - /* Textarea Management */ + var body = document.getElementsByTagName("body")[0]; + body.appendChild(textarea); - attach_ta: function(event) { - if (!$.contains(document.getElementById("queuelist"), event.target)) return; - if (firetable.ui.LinkGrabber.textarea != null) return; + textarea.oninput = firetable.ui.LinkGrabber.evt_got_link; + }, - var textarea = firetable.ui.LinkGrabber.textarea = document.createElement("textarea"); - textarea.setAttribute("style", "position: fixed; width: 100%; margin: 0; top: 0; bottom: 0; right: 0; left: 0; z-index: 99999999"); - textarea.style.opacity = "0.000000000000000001"; + detach_ta: function () { + if (firetable.ui.LinkGrabber.textarea == null) return; + var textarea = firetable.ui.LinkGrabber.textarea; - var body = document.getElementsByTagName("body")[0]; - body.appendChild(textarea); + textarea.parentNode.removeChild(textarea); + firetable.ui.LinkGrabber.textarea = null; + }, - textarea.oninput = firetable.ui.LinkGrabber.evt_got_link; - }, + /* Event Handlers */ - detach_ta: function() { - if (firetable.ui.LinkGrabber.textarea == null) return; - var textarea = firetable.ui.LinkGrabber.textarea; + evt_drag_over: function (event) { + firetable.ui.LinkGrabber.attach_ta(event); //Create TA overlay + }, - textarea.parentNode.removeChild(textarea); - firetable.ui.LinkGrabber.textarea = null; - }, + evt_got_link: function () { + /* THIS IS WHERE WE HANDLE THE RECEIVED LINK */ - /* Event Handlers */ + var link = firetable.ui.LinkGrabber.textarea.value; + firetable.debug && console.log("NEW LINK RECEIVED VIA THE DRAGON'S DROP. " + link); + firetable.actions.queueFromLink(link); - evt_drag_over: function(event) { - firetable.ui.LinkGrabber.attach_ta(event); //Create TA overlay - }, + firetable.ui.LinkGrabber.detach_ta(); + }, - evt_got_link: function() { - /* THIS IS WHERE WE HANDLE THE RECEIVED LINK */ + evt_drag_out: function (e) { + if (e.target == firetable.ui.LinkGrabber.textarea) firetable.ui.LinkGrabber.detach_ta(); + }, - var link = firetable.ui.LinkGrabber.textarea.value; - firetable.debug && console.log("NEW LINK RECEIVED VIA THE DRAGON'S DROP. " + link); - firetable.actions.queueFromLink(link); + /* Start/Stop */ - firetable.ui.LinkGrabber.detach_ta(); - }, + start: function () { + document.addEventListener("dragover", firetable.ui.LinkGrabber.evt_drag_over, false); + document.addEventListener("dragenter", firetable.ui.LinkGrabber.evt_drag_over, false); - evt_drag_out: function(e) { - if (e.target == firetable.ui.LinkGrabber.textarea) firetable.ui.LinkGrabber.detach_ta(); - }, + document.addEventListener("mouseup", firetable.ui.LinkGrabber.evt_drag_out, false); + document.addEventListener("dragleave", firetable.ui.LinkGrabber.evt_drag_out, false); + }, - /* Start/Stop */ + stop: function () { + document.removeEventListener("dragover", firetable.ui.LinkGrabber.evt_drag_over); + document.removeEventListener("dragenter", firetable.ui.LinkGrabber.evt_drag_over); - start: function() { - document.addEventListener("dragover", firetable.ui.LinkGrabber.evt_drag_over, false); - document.addEventListener("dragenter", firetable.ui.LinkGrabber.evt_drag_over, false); + document.removeEventListener("mouseup", firetable.ui.LinkGrabber.evt_drag_out); + document.removeEventListener("dragleave", firetable.ui.LinkGrabber.evt_drag_out); - document.addEventListener("mouseup", firetable.ui.LinkGrabber.evt_drag_out, false); - document.addEventListener("dragleave", firetable.ui.LinkGrabber.evt_drag_out, false); + firetable.ui.LinkGrabber.detach_ta(); + }, }, - - stop: function() { - document.removeEventListener("dragover", firetable.ui.LinkGrabber.evt_drag_over); - document.removeEventListener("dragenter", firetable.ui.LinkGrabber.evt_drag_over); - - document.removeEventListener("mouseup", firetable.ui.LinkGrabber.evt_drag_out); - document.removeEventListener("dragleave", firetable.ui.LinkGrabber.evt_drag_out); - - firetable.ui.LinkGrabber.detach_ta(); - } - } - - -} - - +}; let isLoaded = false; let glitch; -let imgSrc = ''; +let imgSrc = ""; function setup(useThis) { - if (!useThis) useThis = firetable.scImg; - background(0); - let cnv = createCanvas($('#djStage').outerWidth(), $('#djStage').outerHeight()); - cnv.parent('scScreen'); - loadImage(useThis, function(img) { - glitch = new Glitch(img); - isLoaded = true; - var $can = $('#scScreen canvas'); - var canrat = $can.width() / $can.height(); - $can.data('ratio', canrat); - }); + if (!useThis) useThis = firetable.scImg; + background(0); + let cnv = createCanvas($("#djStage").outerWidth(), $("#djStage").outerHeight()); + cnv.parent("scScreen"); + loadImage(useThis, function (img) { + glitch = new Glitch(img); + isLoaded = true; + var $can = $("#scScreen canvas"); + var canrat = $can.width() / $can.height(); + $can.data("ratio", canrat); + }); } function draw() { - clear(); - background(0); + clear(); + background(0); - if (isLoaded) { - glitch.show(); - } - - // fill(255, 255, 255); - // textSize(14); - // text('FPS: ' + floor(frameRate()), 20, 30); + if (isLoaded) { + glitch.show(); + } + // fill(255, 255, 255); + // textSize(14); + // text('FPS: ' + floor(frameRate()), 20, 30); } class Glitch { - constructor(img) { - this.channelLen = 4; - this.imgOrigin = img; - this.imgOrigin.loadPixels(); - this.copyData = []; - this.flowLineImgs = []; - this.shiftLineImgs = []; - this.shiftRGBs = []; - this.scatImgs = []; - this.throughFlag = true; - this.copyData = new Uint8ClampedArray(this.imgOrigin.pixels); - - // flow line - for (let i = 0; i < 1; i++) { - let o = { - pixels: null, - t1: floor(random(0, 1000)), - speed: floor(random(4, 24)), - randX: floor(random(24, 80)) - }; - this.flowLineImgs.push(o); - } + constructor(img) { + this.channelLen = 4; + this.imgOrigin = img; + this.imgOrigin.loadPixels(); + this.copyData = []; + this.flowLineImgs = []; + this.shiftLineImgs = []; + this.shiftRGBs = []; + this.scatImgs = []; + this.throughFlag = true; + this.copyData = new Uint8ClampedArray(this.imgOrigin.pixels); + + // flow line + for (let i = 0; i < 1; i++) { + let o = { + pixels: null, + t1: floor(random(0, 1000)), + speed: floor(random(4, 24)), + randX: floor(random(24, 80)), + }; + this.flowLineImgs.push(o); + } - // shift line - for (let i = 0; i < 6; i++) { - let o = null; - this.shiftLineImgs.push(o); - } + // shift line + for (let i = 0; i < 6; i++) { + let o = null; + this.shiftLineImgs.push(o); + } - // shift RGB - for (let i = 0; i < 1; i++) { - let o = null; - this.shiftRGBs.push(o); - } + // shift RGB + for (let i = 0; i < 1; i++) { + let o = null; + this.shiftRGBs.push(o); + } - // scat imgs - for (let i = 0; i < 3; i++) { - let scatImg = { - img: null, - x: 0, - y: 0 - }; - this.scatImgs.push(scatImg); - } - } - - replaceData(destImg, srcPixels) { - for (let y = 0; y < destImg.height; y++) { - for (let x = 0; x < destImg.width; x++) { - let r, g, b, a; - let index; - index = (y * destImg.width + x) * this.channelLen; - r = index; - g = index + 1; - b = index + 2; - a = index + 3; - destImg.pixels[r] = srcPixels[r]; - destImg.pixels[g] = srcPixels[g]; - destImg.pixels[b] = srcPixels[b]; - destImg.pixels[a] = srcPixels[a]; - } + // scat imgs + for (let i = 0; i < 3; i++) { + let scatImg = { + img: null, + x: 0, + y: 0, + }; + this.scatImgs.push(scatImg); + } } - destImg.updatePixels(); - } - - flowLine(srcImg, obj) { - - let destPixels, - tempY; - destPixels = new Uint8ClampedArray(srcImg.pixels); - obj.t1 %= srcImg.height; - obj.t1 += obj.speed; - //tempY = floor(noise(obj.t1) * srcImg.height); - tempY = floor(obj.t1); - for (let y = 0; y < srcImg.height; y++) { - if (tempY === y) { - for (let x = 0; x < srcImg.width; x++) { - let r, g, b, a; - let index; - index = (y * srcImg.width + x) * this.channelLen; - r = index; - g = index + 1; - b = index + 2; - a = index + 3; - destPixels[r] = srcImg.pixels[r] + obj.randX; - destPixels[g] = srcImg.pixels[g] + obj.randX; - destPixels[b] = srcImg.pixels[b] + obj.randX; - destPixels[a] = srcImg.pixels[a]; + + replaceData(destImg, srcPixels) { + for (let y = 0; y < destImg.height; y++) { + for (let x = 0; x < destImg.width; x++) { + let r, g, b, a; + let index; + index = (y * destImg.width + x) * this.channelLen; + r = index; + g = index + 1; + b = index + 2; + a = index + 3; + destImg.pixels[r] = srcPixels[r]; + destImg.pixels[g] = srcPixels[g]; + destImg.pixels[b] = srcPixels[b]; + destImg.pixels[a] = srcPixels[a]; + } } - } + destImg.updatePixels(); } - return destPixels; - } - - shiftLine(srcImg) { - - let offsetX; - let rangeMin, rangeMax; - let destPixels; - let rangeH; - - destPixels = new Uint8ClampedArray(srcImg.pixels); - rangeH = srcImg.height; - rangeMin = floor(random(0, rangeH)); - rangeMax = rangeMin + floor(random(1, rangeH - rangeMin)); - offsetX = this.channelLen * floor(random(-40, 40)); - - for (let y = 0; y < srcImg.height; y++) { - if (y > rangeMin && y < rangeMax) { - for (let x = 0; x < srcImg.width; x++) { - let r, g, b, a; - let r2, g2, b2, a2; - let index; - - index = (y * srcImg.width + x) * this.channelLen; - r = index; - g = index + 1; - b = index + 2; - a = index + 3; - r2 = r + offsetX; - g2 = g + offsetX; - b2 = b + offsetX; - destPixels[r] = srcImg.pixels[r2]; - destPixels[g] = srcImg.pixels[g2]; - destPixels[b] = srcImg.pixels[b2]; - destPixels[a] = srcImg.pixels[a]; + + flowLine(srcImg, obj) { + let destPixels, tempY; + destPixels = new Uint8ClampedArray(srcImg.pixels); + obj.t1 %= srcImg.height; + obj.t1 += obj.speed; + //tempY = floor(noise(obj.t1) * srcImg.height); + tempY = floor(obj.t1); + for (let y = 0; y < srcImg.height; y++) { + if (tempY === y) { + for (let x = 0; x < srcImg.width; x++) { + let r, g, b, a; + let index; + index = (y * srcImg.width + x) * this.channelLen; + r = index; + g = index + 1; + b = index + 2; + a = index + 3; + destPixels[r] = srcImg.pixels[r] + obj.randX; + destPixels[g] = srcImg.pixels[g] + obj.randX; + destPixels[b] = srcImg.pixels[b] + obj.randX; + destPixels[a] = srcImg.pixels[a]; + } + } } - } + return destPixels; } - return destPixels; - } - - shiftRGB(srcImg) { - - let randR, randG, randB; - let destPixels; - let range; - - range = 16; - destPixels = new Uint8ClampedArray(srcImg.pixels); - randR = (floor(random(-range, range)) * srcImg.width + floor(random(-range, range))) * this.channelLen; - randG = (floor(random(-range, range)) * srcImg.width + floor(random(-range, range))) * this.channelLen; - randB = (floor(random(-range, range)) * srcImg.width + floor(random(-range, range))) * this.channelLen; - - for (let y = 0; y < srcImg.height; y++) { - for (let x = 0; x < srcImg.width; x++) { - let r, g, b, a; - let r2, g2, b2, a2; - let index; - - index = (y * srcImg.width + x) * this.channelLen; - r = index; - g = index + 1; - b = index + 2; - a = index + 3; - r2 = (r + randR) % srcImg.pixels.length; - g2 = (g + randG) % srcImg.pixels.length; - b2 = (b + randB) % srcImg.pixels.length; - destPixels[r] = srcImg.pixels[r2]; - destPixels[g] = srcImg.pixels[g2]; - destPixels[b] = srcImg.pixels[b2]; - destPixels[a] = srcImg.pixels[a]; - } + + shiftLine(srcImg) { + let offsetX; + let rangeMin, rangeMax; + let destPixels; + let rangeH; + + destPixels = new Uint8ClampedArray(srcImg.pixels); + rangeH = srcImg.height; + rangeMin = floor(random(0, rangeH)); + rangeMax = rangeMin + floor(random(1, rangeH - rangeMin)); + offsetX = this.channelLen * floor(random(-40, 40)); + + for (let y = 0; y < srcImg.height; y++) { + if (y > rangeMin && y < rangeMax) { + for (let x = 0; x < srcImg.width; x++) { + let r, g, b, a; + let r2, g2, b2, a2; + let index; + + index = (y * srcImg.width + x) * this.channelLen; + r = index; + g = index + 1; + b = index + 2; + a = index + 3; + r2 = r + offsetX; + g2 = g + offsetX; + b2 = b + offsetX; + destPixels[r] = srcImg.pixels[r2]; + destPixels[g] = srcImg.pixels[g2]; + destPixels[b] = srcImg.pixels[b2]; + destPixels[a] = srcImg.pixels[a]; + } + } + } + return destPixels; } - return destPixels; - } - - getRandomRectImg(srcImg) { - let startX; - let startY; - let rectW; - let rectH; - let destImg; - startX = floor(random(0, srcImg.width - 30)); - startY = floor(random(0, srcImg.height - 50)); - rectW = floor(random(30, srcImg.width - startX)); - rectH = floor(random(1, 50)); - destImg = srcImg.get(startX, startY, rectW, rectH); - destImg.loadPixels(); - return destImg; - } - - show() { - - // restore the original state - this.replaceData(this.imgOrigin, this.copyData); - - // sometimes pass without effect processing - let n = floor(random(100)); - if (n > 75 && this.throughFlag) { - this.throughFlag = false; - setTimeout(() => { - this.throughFlag = true; - }, floor(random(200, 1500))); + shiftRGB(srcImg) { + let randR, randG, randB; + let destPixels; + let range; + + range = 16; + destPixels = new Uint8ClampedArray(srcImg.pixels); + randR = (floor(random(-range, range)) * srcImg.width + floor(random(-range, range))) * this.channelLen; + randG = (floor(random(-range, range)) * srcImg.width + floor(random(-range, range))) * this.channelLen; + randB = (floor(random(-range, range)) * srcImg.width + floor(random(-range, range))) * this.channelLen; + + for (let y = 0; y < srcImg.height; y++) { + for (let x = 0; x < srcImg.width; x++) { + let r, g, b, a; + let r2, g2, b2, a2; + let index; + + index = (y * srcImg.width + x) * this.channelLen; + r = index; + g = index + 1; + b = index + 2; + a = index + 3; + r2 = (r + randR) % srcImg.pixels.length; + g2 = (g + randG) % srcImg.pixels.length; + b2 = (b + randB) % srcImg.pixels.length; + destPixels[r] = srcImg.pixels[r2]; + destPixels[g] = srcImg.pixels[g2]; + destPixels[b] = srcImg.pixels[b2]; + destPixels[a] = srcImg.pixels[a]; + } + } + + return destPixels; } - if (!this.throughFlag) { - push(); - translate((width - this.imgOrigin.width) / 2, (height - this.imgOrigin.height) / 2); - image(this.imgOrigin, 0, 0); - pop(); - return; + + getRandomRectImg(srcImg) { + let startX; + let startY; + let rectW; + let rectH; + let destImg; + startX = floor(random(0, srcImg.width - 30)); + startY = floor(random(0, srcImg.height - 50)); + rectW = floor(random(30, srcImg.width - startX)); + rectH = floor(random(1, 50)); + destImg = srcImg.get(startX, startY, rectW, rectH); + destImg.loadPixels(); + return destImg; } - // flow line - this.flowLineImgs.forEach((v, i, arr) => { - arr[i].pixels = this.flowLine(this.imgOrigin, v); - if (arr[i].pixels) { - this.replaceData(this.imgOrigin, arr[i].pixels); - } - }) - - // shift line - this.shiftLineImgs.forEach((v, i, arr) => { - if (floor(random(100)) > 50) { - arr[i] = this.shiftLine(this.imgOrigin); - this.replaceData(this.imgOrigin, arr[i]); - } else { - if (arr[i]) { - this.replaceData(this.imgOrigin, arr[i]); + show() { + // restore the original state + this.replaceData(this.imgOrigin, this.copyData); + + // sometimes pass without effect processing + let n = floor(random(100)); + if (n > 75 && this.throughFlag) { + this.throughFlag = false; + setTimeout(() => { + this.throughFlag = true; + }, floor(random(200, 1500))); + } + if (!this.throughFlag) { + push(); + translate((width - this.imgOrigin.width) / 2, (height - this.imgOrigin.height) / 2); + image(this.imgOrigin, 0, 0); + pop(); + return; } - } - }) - // shift rgb - this.shiftRGBs.forEach((v, i, arr) => { - if (floor(random(100)) > 65) { - arr[i] = this.shiftRGB(this.imgOrigin); - this.replaceData(this.imgOrigin, arr[i]); - } - }) - - push(); - translate((width - this.imgOrigin.width) / 2, (height - this.imgOrigin.height) / 2); - image(this.imgOrigin, 0, 0); - pop(); - - // scat image - this.scatImgs.forEach((obj) => { - push(); - translate((width - this.imgOrigin.width) / 2, (height - this.imgOrigin.height) / 2); - if (floor(random(100)) > 80) { - obj.x = floor(random(-this.imgOrigin.width * 0.3, this.imgOrigin.width * 0.7)); - obj.y = floor(random(-this.imgOrigin.height * 0.1, this.imgOrigin.height)); - obj.img = this.getRandomRectImg(this.imgOrigin); - } - if (obj.img) { - image(obj.img, obj.x, obj.y); - } - pop(); - }) + // flow line + this.flowLineImgs.forEach((v, i, arr) => { + arr[i].pixels = this.flowLine(this.imgOrigin, v); + if (arr[i].pixels) { + this.replaceData(this.imgOrigin, arr[i].pixels); + } + }); + + // shift line + this.shiftLineImgs.forEach((v, i, arr) => { + if (floor(random(100)) > 50) { + arr[i] = this.shiftLine(this.imgOrigin); + this.replaceData(this.imgOrigin, arr[i]); + } else { + if (arr[i]) { + this.replaceData(this.imgOrigin, arr[i]); + } + } + }); - } + // shift rgb + this.shiftRGBs.forEach((v, i, arr) => { + if (floor(random(100)) > 65) { + arr[i] = this.shiftRGB(this.imgOrigin); + this.replaceData(this.imgOrigin, arr[i]); + } + }); + push(); + translate((width - this.imgOrigin.width) / 2, (height - this.imgOrigin.height) / 2); + image(this.imgOrigin, 0, 0); + pop(); + + // scat image + this.scatImgs.forEach((obj) => { + push(); + translate((width - this.imgOrigin.width) / 2, (height - this.imgOrigin.height) / 2); + if (floor(random(100)) > 80) { + obj.x = floor(random(-this.imgOrigin.width * 0.3, this.imgOrigin.width * 0.7)); + obj.y = floor(random(-this.imgOrigin.height * 0.1, this.imgOrigin.height)); + obj.img = this.getRandomRectImg(this.imgOrigin); + } + if (obj.img) { + image(obj.img, obj.x, obj.y); + } + pop(); + }); + } } if (!firetable.started) firetable.init(); From 0e2bcdedeb5d257837081bede5b5701d67060522 Mon Sep 17 00:00:00 2001 From: Andrew Tibbetts Date: Thu, 6 Jan 2022 01:15:42 -0600 Subject: [PATCH 02/17] robot account avatar, tuck away logout --- css/looks.css | 26 +- index.html | 1169 ++++++++++++++++++++++++++----------------------- js/main.js | 75 ++-- 3 files changed, 698 insertions(+), 572 deletions(-) diff --git a/css/looks.css b/css/looks.css index 55165e1..b5278f4 100644 --- a/css/looks.css +++ b/css/looks.css @@ -440,6 +440,7 @@ span.removemeIcon.material-icons { } #socialthings { + display: flex; } a.sociallogo { @@ -448,6 +449,10 @@ a.sociallogo { margin: 0.2rem 0.2rem 0 0.2rem; } +a.sociallogo[href] { + display: block; +} + .sociallogo svg:hover { fill: #fff; } @@ -457,11 +462,7 @@ a.sociallogo { fill: #888; } -div#sociallthings { - margin-top: 0.25rem; -} - -div#roomlogo { +#roomlogo { background-image: url(../img/idlogo2.png); background-size: contain; width: 3rem; @@ -471,13 +472,18 @@ div#roomlogo { background-position: center; } -#loggedInName { - margin: 0 var(--pad2); +#loggedInUser { + margin-left: var(--pad2); cursor: pointer; text-decoration: none; } -#loggedInName:hover { +#loggedInUser .botson { + width: 2rem; + height: 2rem; +} + +#loggedInUser:hover { text-decoration: underline; } @@ -1836,6 +1842,10 @@ img.emoji { background-color: #222; } +.modalHeader h2 { + margin-right: auto; +} + .closeModal { margin-left: var(--pad3); } diff --git a/index.html b/index.html index d29cd48..694ef1f 100644 --- a/index.html +++ b/index.html @@ -1,561 +1,656 @@ + + + firetable + + + + + + + + + - - - firetable - - - - - - - - - + +
    + +
    + +
    + + + + +
    +
    +
    +
    Reconnecting...
    +
    - -
    - -
    - -
    - - - - -
    -
    Reconnecting...
    - -
    +
    +
    +
    +
    +
    +
    +
    +
      +
    • +
    • +
    • +
    • +
    • +
    • +
    • +
    • +
    • +
    • +
    • +
    • +
    • +
    • +
    • +
    • +
    • +
    • +
    • +
    • +
    • +
    • +
    • +
    • +
    • +
    • +
    • +
    • +
    • +
    • +
    • +
    • +
    • +
    • +
    • +
    • +
    • +
    • +
    • +
    • +
    • +
    • +
    +
    +
    +
    +
    Loading firetable...
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + + +
    +
    + + + link + +
    +
    + + +
    +
    +
    +
    +
    + +
    +
    -
    +
    +
    +
    -
    -
    -
    -
    -
    -
    -
      -
    • -
    • -
    • -
    • -
    • -
    • -
    • -
    • -
    • -
    • -
    • -
    • -
    • -
    • -
    • -
    • -
    • -
    • -
    • -
    • -
    • -
    • -
    • -
    • -
    • -
    • -
    • -
    • -
    • -
    • -
    • -
    • -
    • -
    • -
    • -
    • -
    • -
    • -
    • -
    • -
    • -
    • -
    -
    -
    -
    -
    Loading firetable...
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    - - -
    -
    - - - link - -
    -
    - - -
    -
    -
    -
    -
    - -
    -
    +
    + + + +
    -
    -
    -
    +
    +
    +
    +
    + +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +
    + +
    +
    + +
    +
    +
    +
    +
    + +
    +
    +
    +
    -
    - - - -
    +
    +
    + + +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    Loading waitlist...
    +
    +
    +
    -
    -
    -
    -
    - -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    - -
    - -
    -
    - -
    -
    -
    -
    -
    - -
    -
    -
    -
    +
    +
    +
    +
    + +
    +
    + + + +
    + +
    +
    +
    +
    +
    + + arrow_right_alt + + +
    +
    Merge in progress... Please stand by...
    +
    + Congratulations! Merge completed. + +
    +
    +
    +
    +
    + +
    +
    +
    +
    +
    +
    +
    + +
    + + +
    + vertical_align_top + vertical_align_bottom + edit + close + delete +
    +
    +
    + + + Edit the song tag + hit enter to save
    +
    + Rohn Standard Notation
    + Standard: Artist - Song Title
    + Remix: Artist - Song Title (Remixartist Remix)
    + Featuring: Artist - Song Title feat. Subartist
    + Featuring + Remix: Artist - Song Title feat. Subartist (Remixartist Remix) +
    +
    +
    +
    + +
    or...
    + + +
    +
    + +
    + YoutubeSoundcloud +
    +
    +
    + +
    + +
    + +
    +
    +
    +
    +
    +
    -
    -
    - - -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    - Loading waitlist... -
    -
    -
    -
    +
    +
    +
    +
    +
    + +
    +
    +
    +
    + played by on at + +
    +
    +
    +
    +
    +
    +
    -
    -
    -
    -
    - -
    -
    - - - -
    - -
    -
    -
    -
    -
    - arrow_right_alt -
    -
    Merge in progress... Please stand by...
    -
    Congratulations! Merge completed.
    -
    -
    -
    -
    - -
    -
    -
    -
    -
    -
    -
    - -
    - - -
    - vertical_align_top - vertical_align_bottom - edit - close - delete -
    -
    -
    - - - Edit the song tag + hit enter to save
    -
    - Rohn Standard Notation
    - Standard: Artist - Song Title
    - Remix: Artist - Song Title (Remixartist Remix)
    - Featuring: Artist - Song Title feat. Subartist
    - Featuring + Remix: Artist - Song Title feat. Subartist (Remixartist Remix) -
    -
    -
    -
    - -
    or...
    - - -
    -
    - -
    - YoutubeSoundcloud -
    -
    -
    - -
    - -
    - -
    -
    -
    -
    -
    -
    +
    +
    +

    Welcome To Firetable!

    +

    + Want to jump on the DJ table and pick some h0t tunes to play? Just want to join the + conversation? +

    +
    + +
    +

    Create an Account

    +
    Email:
    +
    Username:
    +
    Password:
    +
    Repeat Password:
    +
    + I have read, understand, and agree to the + Terms of Service and + Privacy Policy. +
    +
    + +
    +
    + + +
    +
    -
    -
    -

    Welcome To Firetable!

    -

    Want to jump on the DJ table and pick some h0t tunes to play? Just want to join the conversation?

    -
    - -
    -

    Create an Account

    -
    - Email: -
    -
    - Username: -
    -
    - Password: -
    -
    - Repeat Password: -
    -
    - I have read, understand, and agree to the Terms of Service and Privacy Policy. -
    -
    - -
    -
    - +
    +
    +

    Your Card Case

    + +
    +
    +
    loading your cards...
    +
    +
    -
    +
    +
    +

    Your Account

    + + +
    +
    +
    + Change your username to some other thing...

    + +
    +
    +
    -
    +
    +
    +

    Supermod Control Panel

    + +
    +
    + Suspend a user's account, or manage active suspentions.

    + +
    +

    Active Suspentions

    +
    +
    +
    -
    -
    -

    Playlist Deletion Zone

    - -
    -
    - So you want to delete a playlist huh? This action is irreversible.
    If you're super sure you want to do this, pick a playlist to delete below. -

    -
    - - -
    -
    -
    +
    +
    +

    Playlist Import Machine

    + +
    +
    +
    + +
    +
    + +
    +
    +
    +
    + Select an "interactive webpage" .html file from + Dubtrack. +
    + +
    +
    +
    +
    +
    +
    -
    -
    -

    Setthings

    - -
    -
    - Set all of the things here on the table. -

    -
    - - -
    -
    - - -
    -
    - -
    - - -
    -
    - -
    -
    -
    - -
    - -
    - - -
    -
    -
    -
    +
    +
    +
    + +
    +
    -
    -
    -

    Your Card Case

    - -
    -
    -
    loading your cards...
    -
    -
    + -
    -
    -

    Your Account

    - -
    -
    -
    - Change your username to some other thing...

    - -
    -
    -
    + -
    -
    -

    Supermod Control Panel

    - -
    -
    - Suspend a user's account, or manage active suspentions.

    - -
    -

    Active Suspentions

    -
    -
    -
    - -
    -
    -

    Playlist Import Machine

    - -
    -
    -
    - -
    -
    - -
    -
    -
    -
    - Select an "interactive webpage" .html file from Dubtrack. -
    - -
    -
    -
    -
    -
    - -
    - -
    -
    -
    - -
    -
    - - - - - - - - - - - - - - - - - - - - - - - - - -
    -

    Your account has been suspended due to a perceived violation of our Terms of Service.

    -

    We reserve the right to modify, suspend, or terminate the Service for any reason, without notice, at any time.

    -
    + + + + + + + + + + + + + + + + + + +
    +

    Your account has been suspended due to a perceived violation of our Terms of Service.

    +

    + We reserve the right to modify, suspend, or terminate the Service for any reason, without notice, at any + time. +

    +
    diff --git a/js/main.js b/js/main.js index db8eabc..bbfb426 100644 --- a/js/main.js +++ b/js/main.js @@ -47,7 +47,7 @@ var firetable = { atUsers: [], atUsersFiltered: [], atString: "", - debug: false, + debug: true, }; if (typeof ftconfigs == "undefined") @@ -183,27 +183,22 @@ firetable.init = function () { if (ftconfigs.facebookURL) { $(".sociallogo.facebook").attr("href", ftconfigs.facebookURL); - $(".sociallogo.facebook").css("display", "inline-block"); } if (ftconfigs.redditURL) { $(".sociallogo.reddit").attr("href", ftconfigs.redditURL); - $(".sociallogo.reddit").css("display", "inline-block"); } if (ftconfigs.lastfmURL) { $(".sociallogo.lastfm").attr("href", ftconfigs.lastfmURL); - $(".sociallogo.lastfm").css("display", "inline-block"); } if (ftconfigs.discordURL) { $(".sociallogo.discord").attr("href", ftconfigs.discordURL); - $(".sociallogo.discord").css("display", "inline-block"); } if (ftconfigs.soundcloudURL) { $(".sociallogo.soundcloud").attr("href", ftconfigs.soundcloudURL); - $(".sociallogo.soundcloud").css("display", "inline-block"); } if (ftconfigs.logoImage) $("#roomlogo").css("background-image", "url(" + ftconfigs.logoImage + ")"); @@ -337,7 +332,7 @@ firetable.actions = { }, showLoginScreen: function () { $("#cardCaseButton").hide(); - $("#loggedInName").hide(); + $("#loggedInUser").hide(); $("#logOutButton").hide().off(); $("#mainGrid").removeClass().addClass("login"); $("#grab").css("display", "none"); @@ -361,7 +356,7 @@ firetable.actions = { }); }, loggedIn: function (user) { - firetable.debug && console.log("user signed in!"); + firetable.debug && console.log("user signed in!", user.uid, ftapi.uid, ftapi.users); if ($("#login").html()) { firetable.loginForm = $("#login").html(); firetable.ui.loginEventsDestroy(); @@ -370,12 +365,26 @@ firetable.actions = { if (ftapi.users[ftapi.uid]) { if (ftapi.users[ftapi.uid].username) { - $("#loggedInName").text(ftapi.users[ftapi.uid].username); + $("#loggedInUser .botson").css( + "background-image", + "url(https://indiediscotheque.com/robots/" + + ftapi.uid + + ftapi.users[ftapi.uid].username + + ".png?size=175x175)" + ); } else { - $("#loggedInName").text(user.uid); + $("#loggedInUser .botson").data("uid", user.uid); + $("#loggedInUser .botson").css( + "background-image", + "url(https://indiediscotheque.com/robots/" + user.uid + ".png?size=175x175)" + ); } } else { - $("#loggedInName").text(user.uid); + $("#loggedInUser .botson").data("uid", user.uid); + $("#loggedInUser .botson").css( + "background-image", + "url(https://indiediscotheque.com/robots/" + user.uid + ".png?size=175x175)" + ); } ftapi.lookup.allLists(function (allPlaylists) { @@ -427,7 +436,7 @@ firetable.actions = { }); }); $("#cardCaseButton").show(); - $("#loggedInName").show(); + $("#loggedInUser").show(); $("#logOutButton").show().on("click", firetable.actions.logOut); firetable.debug && console.log("remove login class from mainGrid"); $("#mainGrid").removeClass().addClass("mmusrs"); @@ -1394,10 +1403,10 @@ firetable.utilities = { .replace(//g, ">") /* - You may add other replacements here for HTML only - (but it's not necessary). - Or for XML, only if the named entities are defined in its DTD. - */ + You may add other replacements here for HTML only + (but it's not necessary). + Or for XML, only if the named entities are defined in its DTD. + */ .replace(/\r\n/g, preserveCR) /* Must be before the next replacement. */ .replace(/[\r\n]/g, preserveCR) ); @@ -2467,10 +2476,16 @@ firetable.ui = { ); }); ftapi.events.on("usersChanged", function (okdata) { - if ($("#loggedInName").text() == ftapi.uid) { + if ($("#loggedInUser .botson").data("uid") == ftapi.uid) { if (ftapi.users[ftapi.uid]) { if (ftapi.users[ftapi.uid].username) { - $("#loggedInName").text(ftapi.users[ftapi.uid].username); + $("#loggedInUser .botson").css( + "background-image", + "url(https://indiediscotheque.com/robots/" + + ftapi.uid + + ftapi.users[ftapi.uid].username + + ".png?size=175x175)" + ); } } } @@ -3159,7 +3174,13 @@ firetable.ui = { $("#usernameResponse").text(error); } else { $("#usernameResponse").text("Great job! Your name is now " + newDjName); - $("#loggedInName").text(newDjName); + $("#loggedInUser .botson").css( + "background-image", + "url(https://indiediscotheque.com/robots/" + + ftapi.uid + + newDjName + + ".png?size=175x175)" + ); } }); } @@ -3798,14 +3819,14 @@ firetable.ui = { $("#stage").css("background-color", data.color); } /* - if (firetable.countcolor == "#fff"){ - firetable.countcolor = "#ffffffc9"; - } else if (firetable.countcolor == "#000"){ - firetable.debug && console.log("a") - firetable.countcolor = "#000000c9"; - } - $("#stage").css("color", firetable.countcolor); - */ + if (firetable.countcolor == "#fff"){ + firetable.countcolor = "#ffffffc9"; + } else if (firetable.countcolor == "#000"){ + firetable.debug && console.log("a") + firetable.countcolor = "#000000c9"; + } + $("#stage").css("color", firetable.countcolor); + */ $(".customColorStyles").remove(); $(".festiveLights").remove(); From 753846983f95e8f89f05395939718cf9c5944144 Mon Sep 17 00:00:00 2001 From: Andrew Tibbetts Date: Sun, 9 Jan 2022 15:15:50 -0600 Subject: [PATCH 03/17] combined all users and waitlist --- css/looks.css | 144 ++++++++++++++++++++++------------------- index.html | 37 +++++++---- js/main.js | 175 +++++++++++++++++++------------------------------- 3 files changed, 167 insertions(+), 189 deletions(-) diff --git a/css/looks.css b/css/looks.css index b5278f4..1ac49a8 100644 --- a/css/looks.css +++ b/css/looks.css @@ -221,31 +221,38 @@ code { display: none; } -.utitle, -.prsnJoined { - color: rgba(255, 255, 255, 0.4); +.prsnRole { + font-size: 1.125rem; + color: #43b581; +} + +.utitle { font-size: 0.66rem; font-weight: 700; text-transform: uppercase; letter-spacing: 0.1em; + color: rgba(255, 255, 255, 0.4); } -.herecon { - color: #43b581; - background-color: #282828; +.imptcon { + display: none; + position: absolute; + right: -0.25rem; height: calc(0.75rem + 4px); width: calc(0.75rem + 4px); padding: 2px; font-size: 0.75em; - display: block; - position: absolute; - bottom: 0; + color: var(--orange); + background-color: #282828; border-radius: 999px; - right: -0.25rem; } -.herecon.idle { - color: #faa61a; +.prson.idle .prsnRole { + color: var(--orange); +} + +.prson.idlething .imptcon { + display: block; } .notice { @@ -597,31 +604,47 @@ a.sociallogo[href] { grid-area: users; display: flex; flex-direction: column; + background-color: #181818; overflow: hidden; } -#userslist { - flex: 1; +.usersWrap { + overflow: auto; +} + +#usersHeader { + display: flex; + align-items: center; + gap: 0.5rem; + padding: var(--pad2) var(--pad3); + color: #888; background-color: #282828; - overflow: hidden; } -#usertabs { - padding-top: 0.5rem; - background-color: #222; +#usersHeader h2 { + margin-right: auto; + font-size: 1rem; + color: inherit; } -.usersWrap { - overflow: auto; +#usersHeader .listenerType { + /* display: flex;*/ + display: none; + align-items: center; + font-size: 0.875rem; + font-weight: 700; +} + +#usersHeader .material-icons { + font-size: 1rem; + color: #888; } -#allusers, -#justwaitlist { +#allUsers { padding-top: 1rem; } -#allusersWrap, -#justwaitWrap { +#allUsersWrap { height: 100%; } @@ -632,8 +655,7 @@ a.sociallogo[href] { padding: var(--pad1) var(--pad4); } -#allusersWrap .prson { - cursor: pointer; +#allUsersWrap .prson { -webkit-touch-callout: none; -webkit-user-select: none; -khtml-user-select: none; @@ -646,33 +668,39 @@ a.sociallogo[href] { flex: 1; font-size: 0.9rem; font-weight: 700; - line-height: 1.5; + line-height: 2; white-space: nowrap; overflow-x: hidden; text-overflow: ellipsis; } -#userslist .utitle { - margin-left: auto; -} - -#userslist .prson .prsnJoined { - display: none; +.djOrder { + display: flex; + align-items: center; + margin-right: 0.75rem; + font-size: 0.85rem; + font-weight: 700; + line-height: 1; + color: #888; } -#userslist .prson:hover .prsnJoined { - display: block; +.djOrder .material-icons-outlined { + margin-right: 0.15rem; + font-size: 1.25rem; + color: #888; } -#userslist .prson:hover .utitle { - display: none; +#usersbox .prsnRole { + margin-left: auto; } -#userslist .prson.blockd .prsnName, -#userslist .prson.blockd .prsnJoined, -#userslist .prson.blockd .utitle { +#usersbox .prson.blockd .prsnName, +#usersbox .prson.blockd .prsnRole { opacity: 0.5; } +#usersbox .prson.blockd .blockon { + display: block; +} .botson { background-color: #000; @@ -683,14 +711,17 @@ a.sociallogo[href] { border-bottom: 1px solid #888; } -#userslist .botson { +#usersbox .botson { + display: flex; + align-items: center; margin-right: 0.75rem; width: 2rem; height: 2rem; position: relative; } -span.block { +span.blockon { + display: none; width: 2rem; height: 2rem; font-size: 2rem; @@ -699,7 +730,7 @@ span.block { border-radius: 999px; } -span.block:empty { +span.blockon:empty { display: none; } @@ -747,17 +778,6 @@ span.block:empty { padding: var(--pad4); } -#nowplaying::before { - content: ""; - position: absolute; - top: 3rem; - left: 3rem; - width: 15rem; - height: 5rem; - background-color: rgba(0, 0, 0, 0.25); - box-shadow: 0 0 10rem 10rem rgba(0, 0, 0, 0.25); -} - #albumArt { grid-area: art; margin: var(--pad1) var(--pad3) 0 var(--pad1); @@ -1335,7 +1355,7 @@ a.importLinkCheck { text-align: center; white-space: nowrap; text-overflow: ellipsis; - background-color: #333; + background-color: #282828; border-bottom: 1px solid #111; overflow: hidden; } @@ -2035,7 +2055,6 @@ div#blog { #ftSuperCopButton, #audilert, #sc-widget, -#justwaitWrap, .notice, #emojiPicker, #overlay { @@ -2044,7 +2063,7 @@ div#blog { /*------------------------------------ Media Queries -*/ @media only screen and (max-width: 799px) { - #allusersWrap .prson { + #allUsersWrap .prson { cursor: none; pointer-events: none; } @@ -2165,11 +2184,6 @@ div#blog { display: none; } - #usertabs { - padding-top: 0; - background-color: black; - } - #djStage { height: 30vh; min-height: 15rem; @@ -2283,16 +2297,12 @@ div#blog { box-shadow: 0 -0.2rem 0.5rem -0.2rem black; } - #userslist { - display: flex; - } - .usersWrap { display: block !important; flex: 1; } - #allusersWrap { + #allUsersWrap { border-right: 5px solid black; } } diff --git a/index.html b/index.html index 694ef1f..2365fb8 100644 --- a/index.html +++ b/index.html @@ -10,6 +10,7 @@ + @@ -245,21 +246,31 @@
    -
    - - +
    +

    Users

    +
    + room +
    +
    + radio +
    -
    -
    -
    -
    -
    -
    -
    +
    +
    +
    + +
    + block + label_important +
    + + +
    -
    -
    -
    Loading waitlist...
    +
    +
    +
    +
    diff --git a/js/main.js b/js/main.js index bbfb426..2937041 100644 --- a/js/main.js +++ b/js/main.js @@ -2234,39 +2234,34 @@ firetable.ui = { } }); ftapi.events.on("waitlistChanged", function (data) { - var ok1 = ""; - var cnt = "0"; + /* + { + id: [string], // user id + name: [string], // user name + plays: [int], // number of songs played as dj + removeAfter: [bool] // has the user requested the bot to remove them after? + } + */ if (data) { - var countr = 1; + $("#allUsers .djOrder.waitlist").html("").removeClass("waitlist"); for (var key in data) { - firetable.debug && console.log("waitlist", data); + var position = parseInt(key + 1); + firetable.debug && console.log("waitlist", position, data); if (data.hasOwnProperty(key)) { - cnt = countr; - var removeMe = ""; - if (data[key].removeAfter) removeMe = "departure_board"; - ok1 += - '
    ' + - countr + - ". " + - data[key].name + - ' ' + - removeMe + - "
    "; - countr++; + $("#user" + data[key].id + " .djOrder") + .html('pending ' + position) + .removeClass("ondeck") + .addClass("waitlist"); } } } - $("#label2 .count").text(" (" + cnt + ")"); - $("#justwaitlist").html(ok1); }); ftapi.events.on("tableChanged", function (data) { var ok1 = ""; if (data) { + console.log("table changed", data); var countr = 0; + $("#allUsers .djOrder.ondeck").html("").removeClass("ondeck"); for (var key in data) { if (data.hasOwnProperty(key)) { var removeMe = ""; @@ -2295,6 +2290,10 @@ firetable.ui = { firetable.playlimit + "
    "; countr++; + $("#user" + data[key].id + " .djOrder") + .html('album ' + countr) + .removeClass("waitlist") + .addClass("ondeck"); } } if (countr < 4) { @@ -2357,123 +2356,95 @@ firetable.ui = { } } }); + var $userTemplate = $("#userKEY").remove(); ftapi.events.on("userJoined", function (data) { - console.log(data); - var user = data; - var block = ""; - var blockcon = ""; - var herecon = "lens"; - var isIdle = ""; + console.log("user joined", data); + var $newUser = $userTemplate.clone(); + if (!data.username) data.username = data.userid; if (data.idle) { - if (data.idle.isIdle && !data.hostbot) isIdle = "idle"; - if (data.idle.audio == 2) { - herecon = "label_important"; - } + if (data.idle.isIdle && !data.hostbot) $newUser.addClass("idle"); + if (data.idle.audio == 2) $newUser.addClass("idlething"); } if (data.blocked) { - block = "blockd"; - blockcon = "block"; + $newUser.addClass("blockd"); } - if (!data.username) data.username = data.userid; - var destination = "#usersRegular"; var rolename = ""; + var rolecon = ""; if (data.mod) { rolename = "mod"; + rolecon = "shield"; destination = "#usersMod"; } if (data.supermod) { rolename = "supermod"; + rolecon = "local_police"; destination = "#usersSuper"; } if (data.hostbot) { rolename = "robot"; + rolecon = "smart_toy"; destination = "#usersBot"; } - var newUserToAddX = $("
    "); - newUserToAddX.addClass("prson " + block); - newUserToAddX.attr("id", "user" + data.userid); - newUserToAddX.html( - '
    ' + - blockcon + - '' + - herecon + - '
    ' + - data.username + - '' + - rolename + - 'joined ' + - firetable.utilities.format_date(data.joined) + - "" - ); - firetable.utilities.chatAt(newUserToAddX); // adds the click event to @ the user - $(destination).append(newUserToAddX); + $newUser.attr("id", "user" + data.userid); + $newUser + .find(".botson") + .css( + "background-image", + "url(https://indiediscotheque.com/robots/" + data.userid + data.username + ".png?size=110x110)" + ); + $newUser.find(".prsnName").text(data.username); + $newUser.find(".prsnRole").text(rolecon).prop("title", rolename); + $newUser.prop("title", "joined " + firetable.utilities.format_date(data.joined)); + firetable.utilities.chatAt($newUser); // adds the click event to @ the user + $(destination).append($newUser); }); ftapi.events.on("userLeft", function (data) { $("#user" + data.userid).remove(); }); ftapi.events.on("userChanged", function (data) { - var user = data; - var block = ""; - var blockcon = ""; - var herecon = "lens"; - var isIdle = ""; - console.log("CHANGE", data); + console.log("user changed", data); + $thisUser = $("#user" + data.userid); + $thisUser.removeClass("idle"); + $thisUser.removeClass("blockd"); + if (!data.username) data.username = data.userid; if (data.idle) { - if (data.idle.isIdle && !data.hostbot) isIdle = "idle"; - if (data.idle.audio == 2) { - herecon = "label_important"; - } + if (data.idle.isIdle && !data.hostbot) $thisUser.addClass("idle"); + if (data.idle.audio == 2) $thisUser.addClass("idlething"); } if (data.blocked) { - block = "blockd"; - blockcon = "block"; + $thisUser.addClass("blockd"); } - if (!data.username) data.username = data.userid; - var destination = "#usersRegular"; var rolename = ""; + var rolecon = ""; if (data.mod) { rolename = "mod"; + rolecon = "shield"; destination = "#usersMod"; } if (data.supermod) { rolename = "supermod"; + rolecon = "local_police"; destination = "#usersSuper"; } if (data.hostbot) { rolename = "robot"; + rolecon = "smart_toy"; destination = "#usersBot"; } - $("#user" + data.userid).html( - '
    ' + - blockcon + - '' + - herecon + - '
    ' + - data.username + - '' + - rolename + - 'joined ' + - firetable.utilities.format_date(data.joined) + - "" - ); + $thisUser + .find(".botson") + .css( + "background-image", + "url(https://indiediscotheque.com/robots/" + data.userid + data.username + ".png?size=110x110)" + ); + $thisUser.find(".prsnName").text(data.username); + $thisUser.find(".prsnRole").text(rolecon).prop("title", rolename); }); ftapi.events.on("usersChanged", function (okdata) { if ($("#loggedInUser .botson").data("uid") == ftapi.uid) { @@ -2499,8 +2470,8 @@ firetable.ui = { } } var count = Object.keys(okdata).length; - $("#label1 .count").text(" (" + count + ")"); - firetable.debug && console.log("users:", okdata); + $("#numListeners, #numRoomListeners").text(count); + firetable.debug && console.log("users changed:", okdata); }); var $chatTemplate = $("#chatKEY").remove(); ftapi.events.on("newChat", function (chatData) { @@ -2742,8 +2713,6 @@ firetable.ui = { firetable.ui.LinkGrabber.start(); - $("#label1").bind("click.lb1tab", firetable.ui.usertab1); - $("#label2").bind("click.lb2tab", firetable.ui.usertab2); $("#addToQueueBttn").bind("click", function () { $("#mainqueuestuff").css("display", "none"); $("#filterMachine").css("display", "none"); @@ -3965,18 +3934,6 @@ firetable.ui = { } }); }, - usertab1: function () { - $("#allusersWrap").css("display", "block"); - $("#justwaitWrap").css("display", "none"); - $("#usertabs").find(".on").removeClass("on"); - $("#label1").addClass("on"); - }, - usertab2: function () { - $("#usertabs").find(".on").removeClass("on"); - $("#label2").addClass("on"); - $("#allusersWrap").css("display", "none"); - $("#justwaitWrap").css("display", "block"); - }, LinkGrabber: { textarea: null, From a2e4d9fb6fd10ecf609391248676b7248e80f13b Mon Sep 17 00:00:00 2001 From: Andrew Tibbetts Date: Mon, 10 Jan 2022 22:59:09 -0600 Subject: [PATCH 04/17] dot for non-mods to show idle, new waitlist icon --- css/looks.css | 11 ++++--- index.html | 4 +-- js/main.js | 88 +++++++++++++++++++++++++++++++-------------------- 3 files changed, 61 insertions(+), 42 deletions(-) diff --git a/css/looks.css b/css/looks.css index 1ac49a8..5e1059b 100644 --- a/css/looks.css +++ b/css/looks.css @@ -225,6 +225,11 @@ code { font-size: 1.125rem; color: #43b581; } +.smallIcon .prsnRole { + margin-right: 0.15rem; + margin-left: 0.15rem; + font-size: 0.75rem; +} .utitle { font-size: 0.66rem; @@ -684,16 +689,12 @@ a.sociallogo[href] { color: #888; } -.djOrder .material-icons-outlined { +.djOrder .material-icons { margin-right: 0.15rem; font-size: 1.25rem; color: #888; } -#usersbox .prsnRole { - margin-left: auto; -} - #usersbox .prson.blockd .prsnName, #usersbox .prson.blockd .prsnRole { opacity: 0.5; diff --git a/index.html b/index.html index 2365fb8..0406f2b 100644 --- a/index.html +++ b/index.html @@ -555,7 +555,7 @@

    Your Card Case

    -

    Your Account

    +

    Account

    @@ -565,7 +565,7 @@

    Your Account

    - Change your username to some other thing...

    + Change your username, , to some other thing...

    diff --git a/js/main.js b/js/main.js index 2937041..c6dc50b 100644 --- a/js/main.js +++ b/js/main.js @@ -365,13 +365,16 @@ firetable.actions = { if (ftapi.users[ftapi.uid]) { if (ftapi.users[ftapi.uid].username) { - $("#loggedInUser .botson").css( - "background-image", - "url(https://indiediscotheque.com/robots/" + - ftapi.uid + - ftapi.users[ftapi.uid].username + - ".png?size=175x175)" - ); + $("#loggedInUser .botson") + .css( + "background-image", + "url(https://indiediscotheque.com/robots/" + + ftapi.uid + + ftapi.users[ftapi.uid].username + + ".png?size=175x175)" + ) + .prop("title", "You're logged in as " + ftapi.users[ftapi.uid].username); + $("#loggedInUsername").text(ftapi.users[ftapi.uid].username); } else { $("#loggedInUser .botson").data("uid", user.uid); $("#loggedInUser .botson").css( @@ -2234,14 +2237,6 @@ firetable.ui = { } }); ftapi.events.on("waitlistChanged", function (data) { - /* - { - id: [string], // user id - name: [string], // user name - plays: [int], // number of songs played as dj - removeAfter: [bool] // has the user requested the bot to remove them after? - } - */ if (data) { $("#allUsers .djOrder.waitlist").html("").removeClass("waitlist"); for (var key in data) { @@ -2249,7 +2244,12 @@ firetable.ui = { firetable.debug && console.log("waitlist", position, data); if (data.hasOwnProperty(key)) { $("#user" + data[key].id + " .djOrder") - .html('pending ' + position) + .html( + 'pending_actions ' + + position + ) .removeClass("ondeck") .addClass("waitlist"); } @@ -2291,7 +2291,7 @@ firetable.ui = { "
    "; countr++; $("#user" + data[key].id + " .djOrder") - .html('album ' + countr) + .html('album ' + countr) .removeClass("waitlist") .addClass("ondeck"); } @@ -2371,20 +2371,24 @@ firetable.ui = { var destination = "#usersRegular"; var rolename = ""; - var rolecon = ""; + var rolecon = "lens"; + var smallcon = true; if (data.mod) { rolename = "mod"; rolecon = "shield"; + smallcon = false; destination = "#usersMod"; } if (data.supermod) { rolename = "supermod"; rolecon = "local_police"; + smallcon = false; destination = "#usersSuper"; } if (data.hostbot) { rolename = "robot"; rolecon = "smart_toy"; + smallcon = false; destination = "#usersBot"; } @@ -2396,7 +2400,9 @@ firetable.ui = { "url(https://indiediscotheque.com/robots/" + data.userid + data.username + ".png?size=110x110)" ); $newUser.find(".prsnName").text(data.username); - $newUser.find(".prsnRole").text(rolecon).prop("title", rolename); + $newUser.find(".prsnRole").text(rolecon); + if (rolename) $newUser.find(".prsnRole").prop("title", rolename); + if (smallcon) $newUser.addClass("smallIcon"); $newUser.prop("title", "joined " + firetable.utilities.format_date(data.joined)); firetable.utilities.chatAt($newUser); // adds the click event to @ the user $(destination).append($newUser); @@ -2420,20 +2426,24 @@ firetable.ui = { var destination = "#usersRegular"; var rolename = ""; - var rolecon = ""; + var rolecon = "lens"; + var smallcon = true; if (data.mod) { rolename = "mod"; rolecon = "shield"; + smallcon = false; destination = "#usersMod"; } if (data.supermod) { rolename = "supermod"; rolecon = "local_police"; + smallcon = false; destination = "#usersSuper"; } if (data.hostbot) { rolename = "robot"; rolecon = "smart_toy"; + smallcon = false; destination = "#usersBot"; } @@ -2444,19 +2454,24 @@ firetable.ui = { "url(https://indiediscotheque.com/robots/" + data.userid + data.username + ".png?size=110x110)" ); $thisUser.find(".prsnName").text(data.username); - $thisUser.find(".prsnRole").text(rolecon).prop("title", rolename); + $thisUser.find(".prsnRole").text(rolecon); + if (rolename) $thisUser.find(".prsnRole").prop("title", rolename); + if (smallcon) $thisUser.addClass("smallIcon"); }); ftapi.events.on("usersChanged", function (okdata) { if ($("#loggedInUser .botson").data("uid") == ftapi.uid) { if (ftapi.users[ftapi.uid]) { if (ftapi.users[ftapi.uid].username) { - $("#loggedInUser .botson").css( - "background-image", - "url(https://indiediscotheque.com/robots/" + - ftapi.uid + - ftapi.users[ftapi.uid].username + - ".png?size=175x175)" - ); + $("#loggedInUser .botson") + .css( + "background-image", + "url(https://indiediscotheque.com/robots/" + + ftapi.uid + + ftapi.users[ftapi.uid].username + + ".png?size=175x175)" + ) + .prop("title", "You're logged in as " + ftapi.users[ftapi.uid].username); + $("#loggedInUsername").text(ftapi.users[ftapi.uid].username); } } } @@ -3143,13 +3158,16 @@ firetable.ui = { $("#usernameResponse").text(error); } else { $("#usernameResponse").text("Great job! Your name is now " + newDjName); - $("#loggedInUser .botson").css( - "background-image", - "url(https://indiediscotheque.com/robots/" + - ftapi.uid + - newDjName + - ".png?size=175x175)" - ); + $("#loggedInUser .botson") + .css( + "background-image", + "url(https://indiediscotheque.com/robots/" + + ftapi.uid + + newDjName + + ".png?size=175x175)" + ) + .prop("title", "You're logged in as " + ftapi.users[ftapi.uid].username); + $("#loggedInUsername").text(ftapi.users[ftapi.uid].username); } }); } From fae6b45916f53064258dc4ffc389cfde37b1be45 Mon Sep 17 00:00:00 2001 From: Andrew Tibbetts Date: Tue, 11 Jan 2022 00:52:44 -0600 Subject: [PATCH 05/17] FOOTER --- css/looks.css | 94 ++++++++++++++++++++++++++++++--------------------- index.html | 19 +++++------ 2 files changed, 65 insertions(+), 48 deletions(-) diff --git a/css/looks.css b/css/looks.css index 5e1059b..a3bdcc7 100644 --- a/css/looks.css +++ b/css/looks.css @@ -83,6 +83,7 @@ --pad4: 1.5rem; --pad5: 4rem; --pad6: 8rem; + --height-footer: 3rem; } *, @@ -433,7 +434,7 @@ span.removemeIcon.material-icons { align-items: center; padding: var(--pad1) var(--pad3); color: #fff; - background-color: black; + background-color: #111; } .ftlogo { @@ -518,7 +519,7 @@ a.sociallogo[href] { grid-area: main; flex: 1; display: grid; - grid-template-rows: auto auto auto 1fr; + grid-template-rows: auto auto 1fr auto; grid-template-columns: 1fr; grid-template-areas: "stage" @@ -531,9 +532,9 @@ a.sociallogo[href] { #mainGrid.mmusrs { grid-template-areas: "stage" - "theme" "mmopts" - "users"; + "users" + "theme"; } #mainGrid.mmusrs #queuebox, @@ -546,9 +547,9 @@ a.sociallogo[href] { #mainGrid.mmqueue { grid-template-areas: "stage" - "theme" "mmopts" - "queues"; + "queues" + "theme"; } #mainGrid.mmqueue #usersbox, @@ -561,9 +562,9 @@ a.sociallogo[href] { #mainGrid.mmchat { grid-template-areas: "stage" - "theme" "mmopts" - "chat"; + "chat" + "theme"; } #mainGrid.mmchat #usersbox, @@ -609,7 +610,7 @@ a.sociallogo[href] { grid-area: users; display: flex; flex-direction: column; - background-color: #181818; + background-color: #222; overflow: hidden; } @@ -617,22 +618,23 @@ a.sociallogo[href] { overflow: auto; } -#usersHeader { +#usersFooter { display: flex; align-items: center; + justify-content: center; gap: 0.5rem; - padding: var(--pad2) var(--pad3); + padding: var(--pad2) var(--pad3) 0 var(--pad3); color: #888; - background-color: #282828; + background-color: #111; } -#usersHeader h2 { - margin-right: auto; - font-size: 1rem; +#usersFooter h2 { + padding: 0.2rem 0; + font-size: 0.75rem; color: inherit; } -#usersHeader .listenerType { +#usersFooter .listenerType { /* display: flex;*/ display: none; align-items: center; @@ -640,7 +642,7 @@ a.sociallogo[href] { font-weight: 700; } -#usersHeader .material-icons { +#usersFooter .material-icons { font-size: 1rem; color: #888; } @@ -1351,13 +1353,15 @@ a.importLinkCheck { #themebox { grid-area: theme; z-index: 15; + display: flex; + align-items: center; + height: var(--height-footer); padding: var(--pad2); font-size: 1rem; text-align: center; white-space: nowrap; text-overflow: ellipsis; - background-color: #282828; - border-bottom: 1px solid #111; + background-color: #111; overflow: hidden; } @@ -1374,7 +1378,7 @@ a.importLinkCheck { display: flex; flex-direction: column; overflow: hidden; - background-color: #181818; + background-color: #222; } #chatsWrap { @@ -1399,7 +1403,6 @@ a.importLinkCheck { margin: var(--pad3); color: #ddd; word-break: break-word; - background-color: #181818; } .newChat .botson { @@ -1586,8 +1589,9 @@ a.importLinkCheck { position: relative; display: flex; align-items: center; + height: var(--height-footer); padding: var(--pad1); - background-color: #282828; + background-color: #111; } #pickerResults { @@ -1844,8 +1848,8 @@ img.emoji { .modalThing { display: none; flex-direction: column; - max-width: 80vw; - max-height: 80vh; + max-width: 100vw; + max-height: 100vh; background-color: #111; overflow: hidden; } @@ -2079,39 +2083,35 @@ div#blog { top: 0; } - #mainGrid { - gap: 0 5px; - } - #importPromptBox .modalContent { width: 480px; } #mainGrid, #mainGrid.mmchat { - grid-template-rows: auto auto auto 1fr; + grid-template-rows: auto auto 1fr auto; grid-template-columns: 60vw 40vw; grid-template-areas: - "stage theme" "stage chat" "mmopts chat" - "users chat"; + "users chat" + "theme chat"; } #mainGrid.mmusrs { grid-template-areas: - "stage theme" "stage chat" "mmopts chat" - "users chat"; + "users chat" + "theme chat"; } #mainGrid.mmqueue { grid-template-areas: - "stage theme" "stage chat" "mmopts chat" - "queues chat"; + "queues chat" + "theme chat"; } #mainGrid.mmchat #usersbox, @@ -2151,6 +2151,10 @@ div#blog { #mainGrid.mmqueue #thehistoryWrap { grid-area: mmopts / queues; } + + #actualChat { + background-color: #181818; + } } @media only screen and (min-width: 1200px) { @@ -2158,12 +2162,12 @@ div#blog { #mainGrid.mmqueue, #mainGrid.mmchat, #mainGrid.mmusrs { - grid-template-rows: auto auto 1fr; + grid-template-rows: auto 1fr auto; grid-template-columns: minmax(16rem, 20rem) minmax(40vw, 100vw) minmax(24rem, 36rem); grid-template-areas: - "users stage theme" "users stage chat" - "users queues chat"; + "users queues chat" + "users theme chat"; } #mainGrid.mmusrs #queuebox, @@ -2211,6 +2215,20 @@ div#blog { #thehistoryWrap { grid-area: queues !important; } + + #usersbox { + background-color: #181818; + } + + #themebox { + text-align: right; + } + + #usersFooter { + justify-content: flex-start; + height: var(--height-footer); + padding: var(--pad3); + } } @media only screen and (min-width: 1680px) { diff --git a/index.html b/index.html index 0406f2b..1b50f6e 100644 --- a/index.html +++ b/index.html @@ -10,7 +10,6 @@ - @@ -246,15 +245,6 @@
    -
    -

    Users

    -
    - room -
    -
    - radio -
    -
    @@ -273,6 +263,15 @@

    Users

    +
    +

    Users

    +
    + room +
    +
    + radio +
    +
    From 5534371167da42cddd6a381798abd07054c1cf54 Mon Sep 17 00:00:00 2001 From: Andrew Tibbetts Date: Fri, 14 Jan 2022 00:44:43 -0600 Subject: [PATCH 06/17] too much? --- css/looks.css | 392 ++++++++++++++++++++++++-------------------------- index.html | 145 +++++++++---------- js/main.js | 14 +- 3 files changed, 270 insertions(+), 281 deletions(-) diff --git a/css/looks.css b/css/looks.css index a3bdcc7..bc49053 100644 --- a/css/looks.css +++ b/css/looks.css @@ -73,10 +73,15 @@ } :root { - --orange: #f4810b; - --orangelite: #f6993c; - --orange66: #f4810b66; - --orange33: #f4810b33; + --color-bg: #181818; + --color-bg-s1: #151515; + --color-bg-s2: #111111; + --color-bg-t1: #222222; + --color-bg-t2: #282828; + --color-orange: #f4810b; + --color-orangelite: #f6993c; + --color-orange66: #f4810b66; + --color-orange33: #f4810b33; --pad1: 0.25rem; --pad2: 0.5rem; --pad3: 1rem; @@ -110,7 +115,7 @@ body { margin: 0; font-size: 1rem; color: #bbbbbb; - background-color: #000; + background: var(--color-bg); overflow: hidden; } @@ -129,7 +134,7 @@ h2 { } a { - color: var(--orange); + color: var(--color-orange); text-decoration: none; } @@ -139,7 +144,7 @@ a { :focus { outline: none; - box-shadow: 0 0 0.5rem var(--orange); + box-shadow: 0 0 0.5rem var(--color-orange); } input:not([type="checkbox"]):not([type="radio"]), @@ -150,7 +155,7 @@ button { font-size: 0.9rem; font-family: "Open Sans", helvetica, arial, sans-serif; color: #eee; - background-color: rgba(123, 123, 123, 0.15); + background-color: rgba(255, 255, 255, 0.05); border: 1px solid transparent; border-bottom-color: rgba(0, 0, 0, 0.85); border-top-color: rgba(255, 255, 255, 0.15); @@ -159,6 +164,7 @@ button { } input:not([type="checkbox"]):not([type="radio"]) { + background-color: rgba(0, 0, 0, 0.5); border-top-color: rgba(0, 0, 0, 0.85); border-bottom-color: rgba(255, 255, 255, 0.15); transform: translateY(-1px); @@ -196,7 +202,7 @@ code { } .simplebar-scrollbar:before { - background: var(--orange); + background: var(--color-orange); } .previewicon { @@ -214,7 +220,7 @@ code { } .track-warning { - color: var(--orange); + color: var(--color-orange); cursor: pointer; } @@ -223,7 +229,10 @@ code { } .prsnRole { + z-index: 2; + margin-right: 0.5rem; font-size: 1.125rem; + line-height: 0; color: #43b581; } .smallIcon .prsnRole { @@ -248,13 +257,13 @@ code { width: calc(0.75rem + 4px); padding: 2px; font-size: 0.75em; - color: var(--orange); - background-color: #282828; + color: var(--color-orange); + background-color: var(--color-bg-t2); border-radius: 999px; } .prson.idle .prsnRole { - color: var(--orange); + color: rgba(255, 255, 255, 0.33); } .prson.idlething .imptcon { @@ -263,7 +272,7 @@ code { .notice { margin: 0; - background-color: #222; + background-color: var(--color-bg-t1); overflow: hidden; height: 100%; padding: var(--pad4); @@ -284,7 +293,6 @@ code { .tabs { display: flex; - background-color: black; } .tab { @@ -294,7 +302,7 @@ code { font-size: 0.85rem; color: #999; text-align: center; - background-color: #222; + background-color: var(--color-bg); border: 0; border-top: 1px solid rgba(255, 255, 255, 0.1); border-radius: 0; @@ -313,7 +321,7 @@ code { .tab.on { color: #ffffff; - background-color: #282828; + background: linear-gradient(var(--color-bg-t1), var(--color-bg)); box-shadow: 0 -0.2rem 0.5rem -0.2rem black; } @@ -337,7 +345,7 @@ span.removemeIcon.material-icons { text-transform: uppercase; letter-spacing: 0.1em; text-decoration: none; - background-color: var(--orange); + background-color: var(--color-orange); border-top: 1px solid rgba(255, 255, 255, 0.3); border-bottom: 1px solid black; transition: all 100ms ease-in-out; @@ -345,12 +353,12 @@ span.removemeIcon.material-icons { } .butt:hover { - background-color: var(--orangelite); + background-color: var(--color-orangelite); } .graybutt { color: #888; - background: rgba(255, 255, 255, 0.066); + background: rgba(255, 255, 255, 0.05); } .graybutt:hover { @@ -376,11 +384,11 @@ span.removemeIcon.material-icons { } .iconbutt.on { - color: var(--orange); + color: var(--color-orange); background: rgba(255, 255, 255, 0.033); border-top: 1px solid black; - border-bottom: 1px solid var(--orange66); - box-shadow: inset 0 0 1rem var(--orange33); + border-bottom: 1px solid var(--color-orange66); + box-shadow: inset 0 0 1rem var(--color-orange33); } .iconbutt .material-icons { @@ -430,25 +438,26 @@ span.removemeIcon.material-icons { /* Top Bar / Site Header */ #topbar { + grid-area: head; display: flex; align-items: center; padding: var(--pad1) var(--pad3); color: #fff; - background-color: #111; } .ftlogo { margin-right: 0.5rem; - color: var(--orange); + color: var(--color-orange); font-size: 1.25rem; font-weight: 700; } #idtitle { display: none; + margin-top: 0.5em; font-size: 1rem; color: #fff; - font-weight: 700; + font-weight: 100; margin-right: 1rem; } @@ -459,7 +468,7 @@ span.removemeIcon.material-icons { a.sociallogo { text-decoration: none; display: none; - margin: 0.2rem 0.2rem 0 0.2rem; + margin: 0.5rem 0.2rem 0 0.2rem; } a.sociallogo[href] { @@ -480,7 +489,7 @@ a.sociallogo[href] { background-size: contain; width: 3rem; height: 3rem; - margin-right: 1.5rem; + margin-right: 1rem; background-repeat: no-repeat; background-position: center; } @@ -501,12 +510,13 @@ a.sociallogo[href] { } .header_icon { - margin: 0.2rem 0.2rem 0 0.2rem; + margin: 0.5rem 0.2rem 0 0.2rem; padding: 0; font-size: 0; line-height: 0; background: none; border: 0; + box-shadow: none; } .header_icon .material-icons { @@ -519,22 +529,25 @@ a.sociallogo[href] { grid-area: main; flex: 1; display: grid; - grid-template-rows: auto auto 1fr auto; + grid-template-rows: auto auto auto auto 1fr; grid-template-columns: 1fr; grid-template-areas: + "head" "stage" "main" "main" "main"; + justify-content: center; overflow: hidden; } #mainGrid.mmusrs { grid-template-areas: + "head" + "theme" "stage" "mmopts" - "users" - "theme"; + "users"; } #mainGrid.mmusrs #queuebox, @@ -546,10 +559,11 @@ a.sociallogo[href] { #mainGrid.mmqueue { grid-template-areas: + "head" + "theme" "stage" "mmopts" - "queues" - "theme"; + "queues"; } #mainGrid.mmqueue #usersbox, @@ -561,10 +575,11 @@ a.sociallogo[href] { #mainGrid.mmchat { grid-template-areas: + "head" + "theme" "stage" "mmopts" - "chat" - "theme"; + "chat"; } #mainGrid.mmchat #usersbox, @@ -575,8 +590,9 @@ a.sociallogo[href] { } #mainGrid.login { - grid-template-rows: auto 1fr; + grid-template-rows: auto auto 1fr; grid-template-areas: + "head" "stage" "login"; } @@ -598,11 +614,11 @@ a.sociallogo[href] { } #minimodeoptions .tab { - background-color: #1b1b1b; + background-color: var(--color-bg-s2); } #minimodeoptions .tab.on { - background-color: #222; + background-color: var(--color-bg); } /* Users Lists */ @@ -610,31 +626,32 @@ a.sociallogo[href] { grid-area: users; display: flex; flex-direction: column; - background-color: #222; + width: 100%; overflow: hidden; } .usersWrap { + max-width: 20rem; overflow: auto; } -#usersFooter { - display: flex; +#usersStats { + /* display: flex;*/ + display: none; align-items: center; justify-content: center; gap: 0.5rem; padding: var(--pad2) var(--pad3) 0 var(--pad3); color: #888; - background-color: #111; } -#usersFooter h2 { +#usersStats h2 { padding: 0.2rem 0; font-size: 0.75rem; color: inherit; } -#usersFooter .listenerType { +#usersStats .listenerType { /* display: flex;*/ display: none; align-items: center; @@ -642,17 +659,19 @@ a.sociallogo[href] { font-weight: 700; } -#usersFooter .material-icons { +#usersStats .material-icons { font-size: 1rem; color: #888; } #allUsers { - padding-top: 1rem; + padding-top: var(--height-footer); } #allUsersWrap { height: 100%; + width: 100%; + align-self: center; } .prson { @@ -673,9 +692,10 @@ a.sociallogo[href] { .prsnName { flex: 1; - font-size: 0.9rem; - font-weight: 700; + font-size: 1rem; + font-weight: 400; line-height: 2; + color: #bbb; white-space: nowrap; overflow-x: hidden; text-overflow: ellipsis; @@ -684,16 +704,19 @@ a.sociallogo[href] { .djOrder { display: flex; align-items: center; + justify-content: flex-end; margin-right: 0.75rem; + width: 2.5rem; font-size: 0.85rem; font-weight: 700; - line-height: 1; + line-height: 0; color: #888; } .djOrder .material-icons { - margin-right: 0.15rem; + margin-left: 0.25rem; font-size: 1.25rem; + line-height: 0; color: #888; } @@ -729,7 +752,7 @@ span.blockon { height: 2rem; font-size: 2rem; background-color: rgba(0, 0, 0, 0.5); - color: var(--orange); + color: var(--color-orange); border-radius: 999px; } @@ -746,17 +769,6 @@ span.blockon:empty { overflow: hidden; } -#stage::before { - content: ""; - position: absolute; - z-index: 1; - top: 0; - right: 0; - bottom: 0; - left: 0; - background: linear-gradient(rgba(0, 0, 0, 0.75), rgba(0, 0, 0, 0.55)); -} - #stage > div:not(#screenBox) { position: relative; z-index: 2; @@ -766,6 +778,7 @@ span.blockon:empty { display: flex; flex-direction: column; justify-content: space-between; + box-shadow: inset 0 -4rem 4rem -3rem rgba(0, 0, 0, 0.25), inset 0 4rem 4rem -3rem rgba(0, 0, 0, 0.25); } #nowplaying { @@ -779,6 +792,7 @@ span.blockon:empty { "art plays plays"; margin-bottom: auto; padding: var(--pad4); + text-shadow: 1px 1px 0 rgba(0, 0, 0, 0.15); } #albumArt { @@ -803,13 +817,14 @@ span.blockon:empty { font-weight: 700; letter-spacing: 0.05em; text-align: right; + color: rgba(255, 255, 255, 0.66); } #artist { grid-area: artist; margin-bottom: var(--pad2); - font-size: 1rem; - font-weight: 400; + font-size: 0.875rem; + font-weight: 700; overflow: hidden; color: white; white-space: nowrap; @@ -892,7 +907,7 @@ span.blockon:empty { font-size: 0.75rem; line-height: 1.5rem; text-align: center; - background-color: #151515; + background-color: var(--color-bg-s1); border-top-left-radius: 0.5rem; border-top-right-radius: 0.5rem; } @@ -926,8 +941,10 @@ span.blockon:empty { #volandthings { position: relative; - background-color: #151515; + background-color: var(--color-bg-s1); border-bottom: 1px solid #333; + border-bottom-left-radius: 0.5rem; + border-bottom-right-radius: 0.5rem; } #songthings, @@ -1007,7 +1024,7 @@ select#stealpicker { font-size: 1rem; border: none; font-family: "Open Sans", helvetica, arial, sans-serif; - background-color: #222; + background-color: var(--color-bg-t1); padding-left: var(--pad1); color: #eee; white-space: nowrap; @@ -1016,7 +1033,7 @@ select#stealpicker { #addToQueueBttn { margin-left: 1em; color: white; - background-color: var(--orange); + background-color: var(--color-orange); border-right: 1px solid black; border-left: 1px solid black; border-radius: 5px; @@ -1057,7 +1074,7 @@ select#stealpicker { .ui-slider-horizontal .ui-slider-range-min { left: 0; - background-color: var(--orange); + background-color: var(--color-orange); } .ui-slider-horizontal { @@ -1143,7 +1160,7 @@ input#queueFilter { } div#filterMachine { - padding: var(--pad2); + padding: 0 var(--pad2) var(--pad2) var(--pad2); } #mainqueue .material-icons { @@ -1153,7 +1170,7 @@ div#filterMachine { #queuelist .pvbar { padding: var(--pad2) var(--pad2); - background-color: #222; + background-color: var(--color-bg-t1); border-bottom: 1px solid black; cursor: move; /* fallback if grab cursor is unsupported */ @@ -1185,15 +1202,9 @@ div#filterMachine { select#listpicker { width: 100%; - background-color: #333; padding-left: var(--pad2); } -#queueControls, -#filterMachine { - background-color: #222; -} - #qControlButtons { display: flex; } @@ -1208,7 +1219,6 @@ button#cancelqsearch { display: none; flex-direction: column; height: 100%; - background-color: #222; overflow: hidden; } @@ -1228,13 +1238,12 @@ input#qsearch { #queuelist { flex: 1; height: 100%; - background-color: #181818; overflow: auto; } #searchResults { flex: 1; - background-color: #282828; + background-color: var(--color-bg-t2); } #mergeContain { @@ -1254,11 +1263,11 @@ input#qsearch { height: 0; border-left: 1rem solid transparent; border-right: 1rem solid transparent; - border-bottom: 1rem solid #333; + border-bottom: 1rem solid var(--color-bg-t2); } #mergeBox { - background-color: #333; + background-color: var(--color-bg-t2); padding: 15px; border-radius: 5px; } @@ -1279,10 +1288,8 @@ select#mergepicker, font-size: 14px; border: none; font-family: "Open Sans", helvetica, arial, sans-serif; - background-color: #222; padding-left: 5px; color: #eee; - height: 24px; white-space: nowrap; } @@ -1301,7 +1308,7 @@ select#mergepicker, display: flex; align-items: center; padding: var(--pad1) 0; - border-bottom: 1px solid #111; + border-bottom: 1px solid var(--color-bg-s2); } .importResult .material-icons { @@ -1355,14 +1362,13 @@ a.importLinkCheck { z-index: 15; display: flex; align-items: center; - height: var(--height-footer); padding: var(--pad2); font-size: 1rem; text-align: center; - white-space: nowrap; - text-overflow: ellipsis; - background-color: #111; - overflow: hidden; + background-color: var(--color-bg-s1); + border-top: 2px solid black; + border-top-left-radius: 0.5rem; + border-top-right-radius: 0.5rem; } #currentTheme { @@ -1378,7 +1384,6 @@ a.importLinkCheck { display: flex; flex-direction: column; overflow: hidden; - background-color: #222; } #chatsWrap { @@ -1392,8 +1397,8 @@ a.importLinkCheck { } #newchatForm { - flex: 1; - margin-right: var(--pad1); + grid-column: 2/3; + width: 100%; } .newChat { @@ -1401,14 +1406,15 @@ a.importLinkCheck { display: flex; justify-content: space-between; margin: var(--pad3); - color: #ddd; + font-size: 0.875rem; + color: #bbb; word-break: break-word; } .newChat .botson { position: relative; z-index: 2; - margin: 0.15rem var(--pad3) 0 0; + margin: 0 var(--pad3) 0 0; width: 2rem; height: 2rem; cursor: pointer; @@ -1437,13 +1443,13 @@ a.importLinkCheck { color: #888; font-size: 0.75rem; text-align: center; - background-color: #282828; + background-color: var(--color-bg-t1); border-radius: var(--pad1); } .chatContent { flex: 1; - padding: 0 var(--pad4) 0 0; + padding: 0 var(--pad3) 0 0; } .chatContent .utitle { @@ -1487,7 +1493,7 @@ a.importLinkCheck { } .chatText.deleteMe:hover { - background-color: #282828; + background-color: var(--color-bg-t2); } .chatText.deleteMe:hover .modDelete { @@ -1574,9 +1580,11 @@ a.importLinkCheck { .chatTime { position: relative; z-index: 2; + width: 3rem; font-weight: 400; font-size: 0.66rem; color: rgba(255, 255, 255, 0.47); + text-align: right; white-space: nowrap; } @@ -1587,11 +1595,15 @@ a.importLinkCheck { #chatbottom { position: relative; - display: flex; - align-items: center; + display: grid; + grid-template-columns: 3.5rem 1fr var(--pad3); + justify-items: center; height: var(--height-footer); - padding: var(--pad1); - background-color: #111; +} + +#pickEmoji { + grid-column: 1/2; + margin-left: var(--pad2); } #pickerResults { @@ -1621,12 +1633,13 @@ a.importLinkCheck { #emojiPicker { position: absolute; z-index: 99; - left: 0; - bottom: 100%; - width: 100%; + left: 1.5rem; + right: 1.5rem; + bottom: calc(100% + 0.5rem); padding: var(--pad3); - background-color: #2d2d2d; - box-shadow: 0 -0.25rem 0.5rem black; + background-color: var(--color-bg-t2); + box-shadow: 0 0.25rem 1rem black; + border-radius: 0.5rem; } #pickerNav { @@ -1648,7 +1661,7 @@ input#pickerSearch { } #pickerNav .on { - color: var(--orange); + color: var(--color-orange); filter: none; opacity: 1; } @@ -1687,8 +1700,8 @@ img.emoji { height: auto; padding: 0.2em; font-size: 0.7rem; - background-color: #222; - border-bottom-color: #222; + background-color: var(--color-bg-t1); + border-bottom-color: var(--color-bg-t1); border-bottom-left-radius: 0; border-bottom-right-radius: 0; box-shadow: none; @@ -1708,7 +1721,7 @@ img.emoji { bottom: 100%; left: 1.5rem; padding: 0.25rem; - background: #222; + background: var(--color-bg-t1); box-shadow: 0 -0.25rem 0.5rem black; } @@ -1739,7 +1752,7 @@ img.emoji { left: 0; width: 100%; height: 100%; - background: #222; + background: var(--color-bg-t1); overflow: auto; } @@ -1780,7 +1793,7 @@ img.emoji { #login { grid-area: login; padding: var(--pad5); - background: #151515; + background: var(--color-bg-s1); overflow: auto; height: 100%; } @@ -1850,7 +1863,7 @@ img.emoji { flex-direction: column; max-width: 100vw; max-height: 100vh; - background-color: #111; + background-color: var(--color-bg-s2); overflow: hidden; } @@ -1864,7 +1877,7 @@ img.emoji { justify-content: space-between; padding: var(--pad4); color: #eee; - background-color: #222; + background-color: var(--color-bg-t1); } .modalHeader h2 { @@ -1938,7 +1951,7 @@ span.cardGiftChat { span.cardGiftChat:hover, span.cardShareChat:hover { - background-color: var(--orange); + background-color: var(--color-orange); color: #000; } @@ -1977,7 +1990,7 @@ input.tagMachine, #importContent, #importDubContent { padding: var(--pad2) var(--pad3); - background-color: #282828; + background-color: var(--color-bg-t2); } #dubimportButton { @@ -2006,7 +2019,7 @@ input#plMachineById { } .responseBox { - background-color: var(--orange); + background-color: var(--color-orange); margin: 10px; padding: 5px; color: #000; @@ -2053,7 +2066,7 @@ body.blog { div#blog { flex: 1; padding: var(--pad5); - background-color: #282828; + background-color: var(--color-bg-t2); } /* Initially hidden stuff */ @@ -2089,29 +2102,32 @@ div#blog { #mainGrid, #mainGrid.mmchat { - grid-template-rows: auto auto 1fr auto; + grid-template-rows: auto auto auto auto 1fr; grid-template-columns: 60vw 40vw; grid-template-areas: + "head chat" + "theme chat" "stage chat" "mmopts chat" - "users chat" - "theme chat"; + "users chat"; } #mainGrid.mmusrs { grid-template-areas: + "head chat" + "theme chat" "stage chat" "mmopts chat" - "users chat" - "theme chat"; + "users chat"; } #mainGrid.mmqueue { grid-template-areas: + "head chat" + "theme chat" "stage chat" "mmopts chat" - "queues chat" - "theme chat"; + "queues chat"; } #mainGrid.mmchat #usersbox, @@ -2122,6 +2138,7 @@ div#blog { #mainGrid.login { grid-template-areas: + "head queues" "stage queues" "login queues"; } @@ -2136,10 +2153,36 @@ div#blog { display: none; } + #themebox, + #stage, + #minimodeoptions { + justify-self: center; + width: 100%; + max-width: 48rem; + } + + #topbar, + #queuebox, + #thehistoryWrap { + justify-self: center; + width: 100%; + max-width: 40rem; + } + + #topbar { + padding-right: var(--pad4); + padding-left: var(--pad4); + } + #idtitle { display: block; } + #actualChat { + min-width: 20rem; + max-width: 30rem; + } + #thehistoryWrap { grid-area: queues; display: block; @@ -2151,23 +2194,24 @@ div#blog { #mainGrid.mmqueue #thehistoryWrap { grid-area: mmopts / queues; } - - #actualChat { - background-color: #181818; - } } @media only screen and (min-width: 1200px) { + body { + background: linear-gradient(var(--color-bg-t1), var(--color-bg)); + } + #mainGrid, #mainGrid.mmqueue, #mainGrid.mmchat, #mainGrid.mmusrs { - grid-template-rows: auto 1fr auto; - grid-template-columns: minmax(16rem, 20rem) minmax(40vw, 100vw) minmax(24rem, 36rem); + grid-template-rows: auto auto auto 1fr; + grid-template-columns: minmax(20rem, 20vw) minmax(30rem, 48rem) 30vw; grid-template-areas: + "users head chat" + "users theme chat" "users stage chat" - "users queues chat" - "users theme chat"; + "users queues chat"; } #mainGrid.mmusrs #queuebox, @@ -2189,6 +2233,10 @@ div#blog { display: none; } + #topbar { + padding: 0; + } + #djStage { height: 30vh; min-height: 15rem; @@ -2216,35 +2264,17 @@ div#blog { grid-area: queues !important; } - #usersbox { - background-color: #181818; - } - - #themebox { - text-align: right; + #allUsersWrap { + align-self: flex-end; } - #usersFooter { + #usersStats { justify-content: flex-start; - height: var(--height-footer); - padding: var(--pad3); + padding: var(--pad3) var(--pad4); } } -@media only screen and (min-width: 1680px) { - #mainGrid, - #mainGrid.mmqueue, - #mainGrid.mmchat, - #mainGrid.mmusrs { - grid-template-columns: minmax(16rem, 20rem) minmax(50vw, 100vw) minmax(24rem, 36rem); - } - - #mainGrid.login { - grid-template-areas: - "stage stage queues" - "login login queues"; - } - +@media only screen and (min-width: 1370px) { #mainGrid.login #queuebox { display: none; } @@ -2252,50 +2282,6 @@ div#blog { #mainGrid.login #thehistoryWrap { width: 100%; } - - #history { - position: absolute; - left: 60%; - bottom: -1px; - width: auto; - padding: 0 var(--pad2); - border-left: var(--pad1) solid #151515; - border-bottom-color: #222; - border-radius: 0; - clip-path: polygon( - 0% 0%, - 0% 0%, - calc(100% - var(--pad2)) 0%, - 100% var(--pad2), - 100% 100%, - 100% 100%, - 0% 100%, - 0% 0% - ); - pointer-events: none; - } - - #history::after { - content: "Recent Plays"; - margin-left: var(--pad1); - font-size: 0.85rem; - font-weight: 400; - text-transform: none; - letter-spacing: 0; - } - - #queuebox { - width: 60%; - } - - #thehistoryWrap { - display: block !important; - grid-area: queues; - justify-self: flex-end; - z-index: auto; - width: 40%; - border-left: var(--pad1) solid #151515; - } } @media only screen and (min-width: 2100px) { @@ -2312,7 +2298,7 @@ div#blog { #usersbox .tab { color: #ffffff; - background-color: #282828; + background-color: var(--color-bg-t2); box-shadow: 0 -0.2rem 0.5rem -0.2rem black; } diff --git a/index.html b/index.html index 1b50f6e..8f83ba2 100644 --- a/index.html +++ b/index.html @@ -8,70 +8,71 @@ - + -
    - -
    - -
    - - - - -
    -
    -
    -
    Reconnecting...
    -
    -
    +
    + +
    + +
    + + + + +
    +
    +
    +
    Reconnecting...
    +
    @@ -213,6 +214,9 @@
    +
    -
    @@ -245,17 +246,26 @@
    +
    +

    People

    +
    + room +
    +
    + radio +
    +
    + +
    block label_important
    - -
    @@ -263,15 +273,6 @@
    -
    -

    Users

    -
    - room -
    -
    - radio -
    -
    diff --git a/js/main.js b/js/main.js index c6dc50b..50c9e81 100644 --- a/js/main.js +++ b/js/main.js @@ -2245,10 +2245,10 @@ firetable.ui = { if (data.hasOwnProperty(key)) { $("#user" + data[key].id + " .djOrder") .html( - 'pending_actions ' + - position + ' on the waitlist">pending_actions' ) .removeClass("ondeck") .addClass("waitlist"); @@ -2291,7 +2291,7 @@ firetable.ui = { "
    "; countr++; $("#user" + data[key].id + " .djOrder") - .html('album ' + countr) + .html(countr + ' album') .removeClass("waitlist") .addClass("ondeck"); } @@ -2403,6 +2403,7 @@ firetable.ui = { $newUser.find(".prsnRole").text(rolecon); if (rolename) $newUser.find(".prsnRole").prop("title", rolename); if (smallcon) $newUser.addClass("smallIcon"); + else $newUser.removeClass("smallIcon"); $newUser.prop("title", "joined " + firetable.utilities.format_date(data.joined)); firetable.utilities.chatAt($newUser); // adds the click event to @ the user $(destination).append($newUser); @@ -2457,6 +2458,7 @@ firetable.ui = { $thisUser.find(".prsnRole").text(rolecon); if (rolename) $thisUser.find(".prsnRole").prop("title", rolename); if (smallcon) $thisUser.addClass("smallIcon"); + else $thisUser.removeClass("smallIcon"); }); ftapi.events.on("usersChanged", function (okdata) { if ($("#loggedInUser .botson").data("uid") == ftapi.uid) { @@ -3801,9 +3803,9 @@ firetable.ui = { if (data.color == "#fff" || data.color == "#7f7f7f") { firetable.color = firetable.orange; firetable.countcolor = "#fff"; - $("#stage").css("background-color", "#fff"); + $("#djStage").css("background-color", "#fff"); } else { - $("#stage").css("background-color", data.color); + $("#djStage").css("background-color", data.color); } /* if (firetable.countcolor == "#fff"){ From d5480d83636b2c59f6e9d4ab7fe20fcc086a4848 Mon Sep 17 00:00:00 2001 From: Andrew Tibbetts Date: Fri, 14 Jan 2022 01:02:52 -0600 Subject: [PATCH 07/17] oh ya, login screen --- css/looks.css | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/css/looks.css b/css/looks.css index bc49053..3b98e88 100644 --- a/css/looks.css +++ b/css/looks.css @@ -590,9 +590,10 @@ a.sociallogo[href] { } #mainGrid.login { - grid-template-rows: auto auto 1fr; + grid-template-rows: auto auto auto 1fr; grid-template-areas: "head" + "theme" "stage" "login"; } @@ -601,7 +602,6 @@ a.sociallogo[href] { #mainGrid.login #queuebox, #mainGrid.login #history, #mainGrid.login #thehistoryWrap, -#mainGrid.login #themebox, #mainGrid.login #actualChat, #mainGrid.login #voteActions, #mainGrid.login #minimodeoptions { @@ -1752,13 +1752,15 @@ img.emoji { left: 0; width: 100%; height: 100%; - background: var(--color-bg-t1); + background-image: linear-gradient(var(--color-bg-t1), var(--color-bg)); overflow: auto; } -#thehistory .pvbar { - padding: var(--pad2) var(--pad2) var(--pad2) var(--pad2); - border-bottom: 1px solid black; +#thehistory { + display: flex; + flex-direction: column; + gap: 1rem; + padding: 1rem; } .histmoreinfo { @@ -1793,8 +1795,8 @@ img.emoji { #login { grid-area: login; padding: var(--pad5); - background: var(--color-bg-s1); overflow: auto; + max-width: 40rem; height: 100%; } @@ -2139,6 +2141,7 @@ div#blog { #mainGrid.login { grid-template-areas: "head queues" + "theme queues" "stage queues" "login queues"; } @@ -2155,7 +2158,8 @@ div#blog { #themebox, #stage, - #minimodeoptions { + #minimodeoptions, + #login { justify-self: center; width: 100%; max-width: 48rem; @@ -2225,6 +2229,8 @@ div#blog { #mainGrid.login { grid-template-areas: + "head head queues" + "theme theme queues" "stage stage queues" "login login queues"; } From 1e0aee1f36fc7b9c00f7c96440192ecf9ea54d69 Mon Sep 17 00:00:00 2001 From: Matt Thompson Date: Wed, 26 Jan 2022 06:00:11 -0600 Subject: [PATCH 08/17] continue seperation of song and room elements in player --- css/looks.css | 39 +++++++++++------------ index.html | 87 +++++++++++++++++++++++---------------------------- js/main.js | 6 ++-- 3 files changed, 63 insertions(+), 69 deletions(-) diff --git a/css/looks.css b/css/looks.css index 3b98e88..6e5cf68 100644 --- a/css/looks.css +++ b/css/looks.css @@ -236,7 +236,6 @@ code { color: #43b581; } .smallIcon .prsnRole { - margin-right: 0.15rem; margin-left: 0.15rem; font-size: 0.75rem; } @@ -263,7 +262,7 @@ code { } .prson.idle .prsnRole { - color: rgba(255, 255, 255, 0.33); + color: #faa61a; } .prson.idlething .imptcon { @@ -544,8 +543,8 @@ a.sociallogo[href] { #mainGrid.mmusrs { grid-template-areas: "head" - "theme" "stage" + "theme" "mmopts" "users"; } @@ -560,8 +559,8 @@ a.sociallogo[href] { #mainGrid.mmqueue { grid-template-areas: "head" - "theme" "stage" + "theme" "mmopts" "queues"; } @@ -576,8 +575,8 @@ a.sociallogo[href] { #mainGrid.mmchat { grid-template-areas: "head" - "theme" "stage" + "theme" "mmopts" "chat"; } @@ -593,8 +592,8 @@ a.sociallogo[href] { grid-template-rows: auto auto auto 1fr; grid-template-areas: "head" - "theme" "stage" + "theme" "login"; } @@ -767,6 +766,9 @@ span.blockon:empty { flex-direction: column; position: relative; overflow: hidden; + border-top: 2px solid black; +border-top-left-radius: 0.5rem; +border-top-right-radius: 0.5rem; } #stage > div:not(#screenBox) { @@ -792,7 +794,6 @@ span.blockon:empty { "art plays plays"; margin-bottom: auto; padding: var(--pad4); - text-shadow: 1px 1px 0 rgba(0, 0, 0, 0.15); } #albumArt { @@ -873,7 +874,7 @@ span.blockon:empty { margin-right: var(--pad2); margin-left: var(--pad2); min-width: 0; - height: 1.5rem; + height: 1rem; } .spot.empty .djplaque { @@ -942,9 +943,7 @@ span.blockon:empty { #volandthings { position: relative; background-color: var(--color-bg-s1); - border-bottom: 1px solid #333; - border-bottom-left-radius: 0.5rem; - border-bottom-right-radius: 0.5rem; + border-bottom: 1px solid #333 } #songthings, @@ -1366,9 +1365,9 @@ a.importLinkCheck { font-size: 1rem; text-align: center; background-color: var(--color-bg-s1); - border-top: 2px solid black; - border-top-left-radius: 0.5rem; - border-top-right-radius: 0.5rem; + border-bottom: 2px solid black; + border-bottom-left-radius: 0.5rem; + border-bottom-right-radius: 0.5rem; } #currentTheme { @@ -2108,8 +2107,8 @@ div#blog { grid-template-columns: 60vw 40vw; grid-template-areas: "head chat" - "theme chat" "stage chat" + "theme chat" "mmopts chat" "users chat"; } @@ -2117,8 +2116,8 @@ div#blog { #mainGrid.mmusrs { grid-template-areas: "head chat" - "theme chat" "stage chat" + "theme chat" "mmopts chat" "users chat"; } @@ -2126,8 +2125,8 @@ div#blog { #mainGrid.mmqueue { grid-template-areas: "head chat" - "theme chat" "stage chat" + "theme chat" "mmopts chat" "queues chat"; } @@ -2141,8 +2140,8 @@ div#blog { #mainGrid.login { grid-template-areas: "head queues" - "theme queues" "stage queues" + "theme queues" "login queues"; } @@ -2213,8 +2212,8 @@ div#blog { grid-template-columns: minmax(20rem, 20vw) minmax(30rem, 48rem) 30vw; grid-template-areas: "users head chat" - "users theme chat" "users stage chat" + "users theme chat" "users queues chat"; } @@ -2230,8 +2229,8 @@ div#blog { #mainGrid.login { grid-template-areas: "head head queues" - "theme theme queues" "stage stage queues" + "theme theme queues" "login login queues"; } diff --git a/index.html b/index.html index 8f83ba2..e27c7c6 100644 --- a/index.html +++ b/index.html @@ -124,6 +124,42 @@
  • +
    +
    +
    + + + link + +
    +
    + + +
    +
    +
    +
    +
    + +
    Loading firetable...
    @@ -137,57 +173,14 @@
    -
    -
    -
    -
    - - -
    -
    - - - link - -
    -
    - - -
    -
    -
    -
    -
    - -
    +
    +
    diff --git a/js/main.js b/js/main.js index 50c9e81..1fdba39 100644 --- a/js/main.js +++ b/js/main.js @@ -3803,7 +3803,7 @@ firetable.ui = { if (data.color == "#fff" || data.color == "#7f7f7f") { firetable.color = firetable.orange; firetable.countcolor = "#fff"; - $("#djStage").css("background-color", "#fff"); + $("#djStage").css("background-color", "#ccc"); } else { $("#djStage").css("background-color", data.color); } @@ -3828,7 +3828,9 @@ firetable.ui = { firetable.color + "; color: " + firetable.countcolor + - "; } .iconbutt.on { color: " + + "; } #nowplaying #track, #nowplaying #artist, #nowplaying #plays, #nowplaying #timr { color: " + + firetable.countcolor + + "} .iconbutt.on { color: " + firetable.color + "; border-bottom: 1px solid " + firetable.color + From 8f81309ee959c5f0777b414d943fd84a63329072 Mon Sep 17 00:00:00 2001 From: Andrew Tibbetts Date: Thu, 27 Jan 2022 02:38:24 -0600 Subject: [PATCH 09/17] user list tweaks --- css/looks.css | 154 ++++++++++++++++++++++++++------------------------ index.html | 21 ++++--- js/main.js | 121 +++++++++++++++------------------------ 3 files changed, 142 insertions(+), 154 deletions(-) diff --git a/css/looks.css b/css/looks.css index 6e5cf68..be8ab72 100644 --- a/css/looks.css +++ b/css/looks.css @@ -228,18 +228,6 @@ code { display: none; } -.prsnRole { - z-index: 2; - margin-right: 0.5rem; - font-size: 1.125rem; - line-height: 0; - color: #43b581; -} -.smallIcon .prsnRole { - margin-left: 0.15rem; - font-size: 0.75rem; -} - .utitle { font-size: 0.66rem; font-weight: 700; @@ -248,27 +236,6 @@ code { color: rgba(255, 255, 255, 0.4); } -.imptcon { - display: none; - position: absolute; - right: -0.25rem; - height: calc(0.75rem + 4px); - width: calc(0.75rem + 4px); - padding: 2px; - font-size: 0.75em; - color: var(--color-orange); - background-color: var(--color-bg-t2); - border-radius: 999px; -} - -.prson.idle .prsnRole { - color: #faa61a; -} - -.prson.idlething .imptcon { - display: block; -} - .notice { margin: 0; background-color: var(--color-bg-t1); @@ -474,7 +441,7 @@ a.sociallogo[href] { display: block; } -.sociallogo svg:hover { +.sociallogo:hover svg { fill: #fff; } .sociallogo svg { @@ -523,6 +490,10 @@ a.sociallogo[href] { color: #888; } +.header_icon:hover .material-icons { + color: white; +} + /*------------------------------------ MAIN BODY THINGS */ #mainGrid { grid-area: main; @@ -664,7 +635,16 @@ a.sociallogo[href] { } #allUsers { - padding-top: var(--height-footer); + display: grid; + grid-template-columns: auto 1fr auto; + align-items: center; + gap: var(--pad2); + padding: var(--height-footer) var(--pad4) var(--pad5) var(--pad4); +} + +#allUsers > *, +#allUsers .prson { + display: contents; } #allUsersWrap { @@ -673,39 +653,71 @@ a.sociallogo[href] { align-self: center; } -.prson { - display: flex; - align-items: center; - min-width: 0; - padding: var(--pad1) var(--pad4); +.prson.idlething .imptcon { + display: block; } -#allUsersWrap .prson { - -webkit-touch-callout: none; - -webkit-user-select: none; - -khtml-user-select: none; - -moz-user-select: none; - -ms-user-select: none; - user-select: none; +.prsnStatus { + position: absolute; + z-index: 1; + bottom: 0; + right: -0.2rem; + font-size: 0.75rem; + color: #43b581; + transform: rotate(-45deg); +} +.prson.idle .prsnStatus { + color: var(--color-orange); } -.prsnName { - flex: 1; +.prsnNameRole { + display: flex; + align-items: center; font-size: 1rem; font-weight: 400; - line-height: 2; + line-height: 2.2rem; +} +.prson.blockd .prsnNameRole { + opacity: 0.5; +} + +.prsnName { + font-weight: 700; color: #bbb; white-space: nowrap; overflow-x: hidden; text-overflow: ellipsis; } +.prsnRole { + z-index: 2; + margin-left: 0.5rem; + font-size: 1em; + line-height: 1; + color: rgba(255, 255, 255, 0.33); +} +.smallIcon .prsnRole { + margin-left: 0.15rem; + font-size: 0.5em; +} + +.imptcon { + display: none; + position: absolute; + right: -0.25rem; + height: calc(0.75rem + 4px); + width: calc(0.75rem + 4px); + padding: 2px; + font-size: 0.75em; + color: var(--color-orange); + background-color: var(--color-bg-t2); + border-radius: 999px; +} + .djOrder { display: flex; align-items: center; justify-content: flex-end; - margin-right: 0.75rem; - width: 2.5rem; font-size: 0.85rem; font-weight: 700; line-height: 0; @@ -719,14 +731,6 @@ a.sociallogo[href] { color: #888; } -#usersbox .prson.blockd .prsnName, -#usersbox .prson.blockd .prsnRole { - opacity: 0.5; -} -#usersbox .prson.blockd .blockon { - display: block; -} - .botson { background-color: #000; background-size: auto 100%; @@ -734,28 +738,32 @@ a.sociallogo[href] { background-position: center 55%; background-repeat: no-repeat; border-bottom: 1px solid #888; + clip-path: circle(60px at center); } -#usersbox .botson { +#allUsers .botson { display: flex; align-items: center; - margin-right: 0.75rem; - width: 2rem; - height: 2rem; position: relative; + margin-right: 0.5rem; + width: 2.2rem; + height: 2.2rem; + background-position: -33%; } -span.blockon { +.blockon { display: none; - width: 2rem; - height: 2rem; - font-size: 2rem; + width: 2.2rem; + height: 2.2rem; + font-size: 2.2rem; background-color: rgba(0, 0, 0, 0.5); color: var(--color-orange); border-radius: 999px; } - -span.blockon:empty { +.prson.blockd .blockon { + display: block; +} +.blockon:empty { display: none; } @@ -767,8 +775,8 @@ span.blockon:empty { position: relative; overflow: hidden; border-top: 2px solid black; -border-top-left-radius: 0.5rem; -border-top-right-radius: 0.5rem; + border-top-left-radius: 0.5rem; + border-top-right-radius: 0.5rem; } #stage > div:not(#screenBox) { @@ -943,7 +951,7 @@ border-top-right-radius: 0.5rem; #volandthings { position: relative; background-color: var(--color-bg-s1); - border-bottom: 1px solid #333 + border-bottom: 1px solid #333; } #songthings, diff --git a/index.html b/index.html index e27c7c6..0f4f039 100644 --- a/index.html +++ b/index.html @@ -130,7 +130,13 @@ - + link
    @@ -158,7 +164,6 @@ -
    @@ -173,7 +178,6 @@
    -
    @@ -252,13 +256,16 @@

    People

    - -
    block - label_important + radio + lens
    - +
    + + +
    +
    diff --git a/js/main.js b/js/main.js index 1fdba39..f29112f 100644 --- a/js/main.js +++ b/js/main.js @@ -2357,108 +2357,81 @@ firetable.ui = { } }); var $userTemplate = $("#userKEY").remove(); - ftapi.events.on("userJoined", function (data) { - console.log("user joined", data); - var $newUser = $userTemplate.clone(); + function userBits(data) { if (!data.username) data.username = data.userid; if (data.idle) { - if (data.idle.isIdle && !data.hostbot) $newUser.addClass("idle"); - if (data.idle.audio == 2) $newUser.addClass("idlething"); + if (data.idle.isIdle && !data.hostbot) + data.html.addClass("idle").find(".prsnStatus").text("nightlight"); + if (data.idle.audio == 2) data.html.addClass("idlething"); } if (data.blocked) { - $newUser.addClass("blockd"); + data.html.addClass("blockd"); } - var destination = "#usersRegular"; - var rolename = ""; - var rolecon = "lens"; - var smallcon = true; + data.destination = "#usersRegular"; + data.rolename = ""; + data.rolecon = ""; + data.smallcon = true; if (data.mod) { - rolename = "mod"; - rolecon = "shield"; - smallcon = false; - destination = "#usersMod"; + data.rolename = "mod"; + data.rolecon = "star"; + data.smallcon = false; + data.destination = "#usersMod"; } if (data.supermod) { - rolename = "supermod"; - rolecon = "local_police"; - smallcon = false; - destination = "#usersSuper"; + data.rolename = "supermod"; + data.rolecon = "local_police"; + data.smallcon = false; + data.destination = "#usersSuper"; } if (data.hostbot) { - rolename = "robot"; - rolecon = "smart_toy"; - smallcon = false; - destination = "#usersBot"; + data.rolename = "robot"; + data.rolecon = "smart_toy"; + data.smallcon = false; + data.destination = "#usersBot"; } - - $newUser.attr("id", "user" + data.userid); - $newUser + return data; + } + ftapi.events.on("userJoined", function (data) { + console.log("user joined", data); + data.html = $userTemplate.clone(); + data = userBits(data); + data.html.attr("id", "user" + data.userid); + data.html .find(".botson") .css( "background-image", "url(https://indiediscotheque.com/robots/" + data.userid + data.username + ".png?size=110x110)" ); - $newUser.find(".prsnName").text(data.username); - $newUser.find(".prsnRole").text(rolecon); - if (rolename) $newUser.find(".prsnRole").prop("title", rolename); - if (smallcon) $newUser.addClass("smallIcon"); - else $newUser.removeClass("smallIcon"); - $newUser.prop("title", "joined " + firetable.utilities.format_date(data.joined)); - firetable.utilities.chatAt($newUser); // adds the click event to @ the user - $(destination).append($newUser); + data.html.find(".prsnName").text(data.username); + data.html.find(".prsnRole").text(data.rolecon); + if (data.rolename) data.html.find(".prsnRole").prop("title", data.rolename); + if (data.smallcon) data.html.addClass("smallIcon"); + else data.html.removeClass("smallIcon"); + data.html.prop("title", "joined " + firetable.utilities.format_date(data.joined)); + firetable.utilities.chatAt(data.html); // adds the click event to @ the user + $(data.destination).append(data.html); }); ftapi.events.on("userLeft", function (data) { $("#user" + data.userid).remove(); }); ftapi.events.on("userChanged", function (data) { console.log("user changed", data); - $thisUser = $("#user" + data.userid); - $thisUser.removeClass("idle"); - $thisUser.removeClass("blockd"); - if (!data.username) data.username = data.userid; - if (data.idle) { - if (data.idle.isIdle && !data.hostbot) $thisUser.addClass("idle"); - if (data.idle.audio == 2) $thisUser.addClass("idlething"); - } - if (data.blocked) { - $thisUser.addClass("blockd"); - } - - var destination = "#usersRegular"; - var rolename = ""; - var rolecon = "lens"; - var smallcon = true; - if (data.mod) { - rolename = "mod"; - rolecon = "shield"; - smallcon = false; - destination = "#usersMod"; - } - if (data.supermod) { - rolename = "supermod"; - rolecon = "local_police"; - smallcon = false; - destination = "#usersSuper"; - } - if (data.hostbot) { - rolename = "robot"; - rolecon = "smart_toy"; - smallcon = false; - destination = "#usersBot"; - } - - $thisUser + data.html = $("#user" + data.userid); + data.html.removeClass("idle").find(".prsnStatus").text("lens"); + data.html.removeClass("blockd"); + data = userBits(data); + data.html .find(".botson") .css( "background-image", "url(https://indiediscotheque.com/robots/" + data.userid + data.username + ".png?size=110x110)" ); - $thisUser.find(".prsnName").text(data.username); - $thisUser.find(".prsnRole").text(rolecon); - if (rolename) $thisUser.find(".prsnRole").prop("title", rolename); - if (smallcon) $thisUser.addClass("smallIcon"); - else $thisUser.removeClass("smallIcon"); + data.html.find(".prsnName").text(data.username); + data.html.find(".prsnRole").text(data.rolecon); + if (data.rolename) data.html.find(".prsnRole").prop("title", data.rolename); + if (data.smallcon) data.html.addClass("smallIcon"); + else data.html.removeClass("smallIcon"); }); ftapi.events.on("usersChanged", function (okdata) { if ($("#loggedInUser .botson").data("uid") == ftapi.uid) { From 48d4e17c4d70b612f474fe1bf046f23bd441e51c Mon Sep 17 00:00:00 2001 From: Andrew Tibbetts Date: Fri, 28 Jan 2022 00:54:26 -0600 Subject: [PATCH 10/17] better breaks + layout --- css/looks.css | 129 ++++++++++++++++++++------------------------------ js/main.js | 4 +- 2 files changed, 54 insertions(+), 79 deletions(-) diff --git a/css/looks.css b/css/looks.css index be8ab72..b715442 100644 --- a/css/looks.css +++ b/css/looks.css @@ -259,6 +259,7 @@ code { .tabs { display: flex; + align-items: flex-end; } .tab { @@ -788,7 +789,7 @@ a.sociallogo[href] { display: flex; flex-direction: column; justify-content: space-between; - box-shadow: inset 0 -4rem 4rem -3rem rgba(0, 0, 0, 0.25), inset 0 4rem 4rem -3rem rgba(0, 0, 0, 0.25); + box-shadow: inset 0 -4rem 4rem -3rem rgba(0, 0, 0, 0.5), inset 0 4rem 4rem -3rem rgba(0, 0, 0, 0.5); } #nowplaying { @@ -896,7 +897,7 @@ a.sociallogo[href] { z-index: 0; bottom: 1.4rem; width: 100%; - height: 8rem; + height: 6rem; background-size: contain; background-repeat: no-repeat; background-position: center bottom; @@ -912,7 +913,7 @@ a.sociallogo[href] { display: flex; justify-content: space-between; min-width: 0; - padding: 0 var(--pad3); + padding: 0 var(--pad2); font-size: 0.75rem; line-height: 1.5rem; text-align: center; @@ -1373,7 +1374,7 @@ a.importLinkCheck { font-size: 1rem; text-align: center; background-color: var(--color-bg-s1); - border-bottom: 2px solid black; + border-bottom: 2px solid rgba(255, 255, 255, 0.125); border-bottom-left-radius: 0.5rem; border-bottom-right-radius: 0.5rem; } @@ -2089,60 +2090,24 @@ div#blog { } /*------------------------------------ Media Queries -*/ -@media only screen and (max-width: 799px) { - #allUsersWrap .prson { - cursor: none; - pointer-events: none; - } -} - -@media only screen and (min-width: 800px) { - body.screen #albumArt { - display: none; - } - - body.screen #screenBox { - top: 0; - } - - #importPromptBox .modalContent { - width: 480px; - } - +@media only screen and (min-width: 640px) { #mainGrid, #mainGrid.mmchat { - grid-template-rows: auto auto auto auto 1fr; - grid-template-columns: 60vw 40vw; + grid-template-rows: auto auto auto 1fr; + grid-template-columns: minmax(28rem, 1fr) minmax(18rem, 24rem); grid-template-areas: - "head chat" + "head mmopts" "stage chat" "theme chat" - "mmopts chat" - "users chat"; + "queues chat"; } #mainGrid.mmusrs { grid-template-areas: - "head chat" - "stage chat" - "theme chat" - "mmopts chat" - "users chat"; - } - - #mainGrid.mmqueue { - grid-template-areas: - "head chat" - "stage chat" - "theme chat" - "mmopts chat" - "queues chat"; - } - - #mainGrid.mmchat #usersbox, - #mainGrid.mmqueue #actualChat, - #mainGrid.mmusrs #actualChat { - display: flex; + "head mmopts" + "stage users" + "theme users" + "queues users"; } #mainGrid.login { @@ -2154,30 +2119,50 @@ div#blog { } #mainGrid.login #queuebox, - #mainGrid.login #thehistoryWrap { - display: block; + #mainGrid.login #thehistoryWrap, + #mainGrid.mmchat #actualChat, + #mainGrid.mmusrs #usersbox, + #mainGrid.mmchat #queuebox, + #mainGrid.mmusrs #queuebox { + display: flex; } #mainGrid.login .histeal, - #mmchat { + #mmqueue { display: none; } - #themebox, - #stage, - #minimodeoptions, - #login { - justify-self: center; - width: 100%; - max-width: 48rem; + #thehistoryWrap { + grid-area: queues; + display: block; + position: relative; + top: auto !important; + bottom: auto; } - #topbar, - #queuebox, - #thehistoryWrap { - justify-self: center; - width: 100%; - max-width: 40rem; + #mainGrid.mmqueue #thehistoryWrap { + grid-area: queues; + } +} + +@media only screen and (max-width: 799px) { + #allUsersWrap .prson { + cursor: none; + pointer-events: none; + } +} + +@media only screen and (min-width: 800px) { + body.screen #albumArt { + display: none; + } + + body.screen #screenBox { + top: 0; + } + + #importPromptBox .modalContent { + width: 480px; } #topbar { @@ -2193,21 +2178,9 @@ div#blog { min-width: 20rem; max-width: 30rem; } - - #thehistoryWrap { - grid-area: queues; - display: block; - position: relative; - top: auto !important; - bottom: auto; - } - - #mainGrid.mmqueue #thehistoryWrap { - grid-area: mmopts / queues; - } } -@media only screen and (min-width: 1200px) { +@media only screen and (min-width: 1000px) { body { background: linear-gradient(var(--color-bg-t1), var(--color-bg)); } diff --git a/js/main.js b/js/main.js index f29112f..1a83d8e 100644 --- a/js/main.js +++ b/js/main.js @@ -3801,7 +3801,9 @@ firetable.ui = { firetable.color + "; color: " + firetable.countcolor + - "; } #nowplaying #track, #nowplaying #artist, #nowplaying #plays, #nowplaying #timr { color: " + + "; } .djActive { background-image: linear-gradient(to bottom, var(--color-bg-s1) -200%, " + + firetable.color + + "); } #nowplaying #track, #nowplaying #artist, #nowplaying #plays, #nowplaying #timr { color: " + firetable.countcolor + "} .iconbutt.on { color: " + firetable.color + From 8058a945624c724c3809e9fdca3192522f9ad5c7 Mon Sep 17 00:00:00 2001 From: Andrew Tibbetts Date: Sun, 13 Feb 2022 15:09:23 -0600 Subject: [PATCH 11/17] more grid work, header to left side --- css/looks.css | 526 ++++++++++++++++++++++++++++++-------------------- index.html | 271 +++++++++++++------------- js/main.js | 67 ++++--- 3 files changed, 489 insertions(+), 375 deletions(-) diff --git a/css/looks.css b/css/looks.css index b715442..ae181ff 100644 --- a/css/looks.css +++ b/css/looks.css @@ -229,10 +229,8 @@ code { } .utitle { - font-size: 0.66rem; - font-weight: 700; - text-transform: uppercase; - letter-spacing: 0.1em; + font-size: 1rem; + font-weight: 100; color: rgba(255, 255, 255, 0.4); } @@ -315,6 +313,7 @@ span.removemeIcon.material-icons { background-color: var(--color-orange); border-top: 1px solid rgba(255, 255, 255, 0.3); border-bottom: 1px solid black; + box-shadow: 0 0.25rem 0.5rem -0.25rem black; transition: all 100ms ease-in-out; user-select: none; } @@ -428,14 +427,9 @@ span.removemeIcon.material-icons { margin-right: 1rem; } -#socialthings { - display: flex; -} - a.sociallogo { text-decoration: none; display: none; - margin: 0.5rem 0.2rem 0 0.2rem; } a.sociallogo[href] { @@ -479,6 +473,7 @@ a.sociallogo[href] { .header_icon { margin: 0.5rem 0.2rem 0 0.2rem; padding: 0; + height: auto; font-size: 0; line-height: 0; background: none; @@ -500,11 +495,12 @@ a.sociallogo[href] { grid-area: main; flex: 1; display: grid; - grid-template-rows: auto auto auto auto 1fr; + grid-template-rows: auto auto auto auto auto 1fr; grid-template-columns: 1fr; grid-template-areas: "head" - "stage" + "player" + "playingstage" "main" "main" "main"; @@ -515,7 +511,8 @@ a.sociallogo[href] { #mainGrid.mmusrs { grid-template-areas: "head" - "stage" + "player" + "playingstage" "theme" "mmopts" "users"; @@ -531,7 +528,8 @@ a.sociallogo[href] { #mainGrid.mmqueue { grid-template-areas: "head" - "stage" + "player" + "playingstage" "theme" "mmopts" "queues"; @@ -547,7 +545,8 @@ a.sociallogo[href] { #mainGrid.mmchat { grid-template-areas: "head" - "stage" + "player" + "playingstage" "theme" "mmopts" "chat"; @@ -564,7 +563,7 @@ a.sociallogo[href] { grid-template-rows: auto auto auto 1fr; grid-template-areas: "head" - "stage" + "playingstage" "theme" "login"; } @@ -602,7 +601,7 @@ a.sociallogo[href] { } .usersWrap { - max-width: 20rem; + max-width: 18rem; overflow: auto; } @@ -640,7 +639,7 @@ a.sociallogo[href] { grid-template-columns: auto 1fr auto; align-items: center; gap: var(--pad2); - padding: var(--height-footer) var(--pad4) var(--pad5) var(--pad4); + padding: var(--pad4); } #allUsers > *, @@ -651,7 +650,6 @@ a.sociallogo[href] { #allUsersWrap { height: 100%; width: 100%; - align-self: center; } .prson.idlething .imptcon { @@ -674,9 +672,10 @@ a.sociallogo[href] { .prsnNameRole { display: flex; align-items: center; - font-size: 1rem; + justify-content: flex-start; + margin-left: 0.25rem; + font-size: 0.875rem; font-weight: 400; - line-height: 2.2rem; } .prson.blockd .prsnNameRole { opacity: 0.5; @@ -693,7 +692,7 @@ a.sociallogo[href] { .prsnRole { z-index: 2; margin-left: 0.5rem; - font-size: 1em; + font-size: 1.125rem; line-height: 1; color: rgba(255, 255, 255, 0.33); } @@ -724,19 +723,25 @@ a.sociallogo[href] { line-height: 0; color: #888; } - .djOrder .material-icons { - margin-left: 0.25rem; + margin-right: 0.25rem; font-size: 1.25rem; line-height: 0; + color: rgba(123, 123, 123, 0.5); +} +.djOrder.ondeck .material-icons, +.djOrder.waitlist .material-icons { color: #888; } +.djOrder span { + width: 1rem; +} .botson { background-color: #000; - background-size: auto 100%; + background-size: auto 125%; border-radius: 999px; - background-position: center 55%; + background-position: center 66%; background-repeat: no-repeat; border-bottom: 1px solid #888; clip-path: circle(60px at center); @@ -747,9 +752,9 @@ a.sociallogo[href] { align-items: center; position: relative; margin-right: 0.5rem; - width: 2.2rem; - height: 2.2rem; - background-position: -33%; + width: 2rem; + height: 2rem; + background-position: 66% 72%; } .blockon { @@ -768,31 +773,9 @@ a.sociallogo[href] { display: none; } -/* The Stage */ -#stage { - grid-area: stage; - display: flex; - flex-direction: column; - position: relative; - overflow: hidden; - border-top: 2px solid black; - border-top-left-radius: 0.5rem; - border-top-right-radius: 0.5rem; -} - -#stage > div:not(#screenBox) { - position: relative; - z-index: 2; -} - -#djStage { - display: flex; - flex-direction: column; - justify-content: space-between; - box-shadow: inset 0 -4rem 4rem -3rem rgba(0, 0, 0, 0.5), inset 0 4rem 4rem -3rem rgba(0, 0, 0, 0.5); -} - +/* Now Playing */ #nowplaying { + grid-area: playingstage; position: relative; display: grid; grid-template-columns: auto 1fr auto; @@ -802,7 +785,7 @@ a.sociallogo[href] { "art artist artist" "art plays plays"; margin-bottom: auto; - padding: var(--pad4); + padding: var(--pad4) var(--pad4) 2.5rem var(--pad4); } #albumArt { @@ -863,96 +846,21 @@ a.sociallogo[href] { display: none; } -#deck { - grid-area: deck; - display: flex; - max-width: 100vw; -} - -#deck.dance .avtr:not(.animate) { - transform-origin: bottom; - animation: MoveSideSide 2s linear infinite; -} - -.spot { - flex: 1; - position: relative; - display: flex; - flex-direction: column; - justify-content: flex-end; - margin-right: var(--pad2); - margin-left: var(--pad2); - min-width: 0; - height: 1rem; -} - -.spot.empty .djplaque { - flex-direction: column; - color: rgba(255, 2556, 255, 0.5); -} - -.avtr { - display: none; - position: absolute; - z-index: 0; - bottom: 1.4rem; - width: 100%; - height: 6rem; - background-size: contain; - background-repeat: no-repeat; - background-position: center bottom; -} - -.avtr.animate { - animation: MoveUpDown 1s linear infinite; -} - -.djplaque { - position: relative; - z-index: 1; - display: flex; - justify-content: space-between; - min-width: 0; - padding: 0 var(--pad2); - font-size: 0.75rem; - line-height: 1.5rem; - text-align: center; - background-color: var(--color-bg-s1); - border-top-left-radius: 0.5rem; - border-top-right-radius: 0.5rem; -} - -.djname { - text-overflow: ellipsis; - overflow: hidden; - white-space: nowrap; - padding-right: 5px; -} - -.playcount { - white-space: nowrap; -} - -#prgbar { - width: 100%; - height: 0.5rem; - border-bottom: 1px solid #333; - margin-bottom: 0; -} - +/* Player */ #volandthings, #queueControls { display: flex; align-items: center; - justify-content: space-between; + justify-content: center; padding: 0.4rem 0.5rem; white-space: nowrap; } #volandthings { + grid-area: player; position: relative; - background-color: var(--color-bg-s1); - border-bottom: 1px solid #333; + display: flex; + justify-content: space-between; } #songthings, @@ -963,7 +871,6 @@ a.sociallogo[href] { white-space: nowrap; } -#songthings, #playerControls { flex: 1; } @@ -976,10 +883,6 @@ a.sociallogo[href] { margin-right: 1rem; } -#playerControls { - margin-left: 1rem; -} - #volplace { flex: 1; display: flex; @@ -1048,9 +951,10 @@ select#stealpicker { } #slider { - margin-right: var(--pad2); + margin-right: var(--pad1); width: 100%; max-width: 10rem; + min-width: 6rem; } .ui-slider { @@ -1065,8 +969,8 @@ select#stealpicker { height: 1.25rem; cursor: grab; background-color: #bbb; - border: 0.2rem solid #666; - border-radius: 999px; + border-radius: 0.4rem; + box-shadow: 0 0.5rem 0.5rem -0.25rem black; -ms-touch-action: none; touch-action: none; } @@ -1127,6 +1031,106 @@ html .ui-button.ui-state-disabled:active { color: #333333; } +/* The Stage */ +#djStage > div:not(#screenBox) { + position: relative; + z-index: 2; +} + +#djStage { + grid-area: playingstage; + display: flex; + flex-direction: column; + justify-content: flex-end; + position: relative; + overflow: hidden; + border-top-left-radius: 0.5rem; + border-top-right-radius: 0.5rem; + box-shadow: inset 0 -4rem 4rem -3rem rgba(0, 0, 0, 0.5), inset 0 16rem 32rem -3rem rgba(0, 0, 0, 0.75), + inset 0 4rem 2rem -4rem black; +} + +#deck { + grid-area: deck; + display: flex; + max-width: 100vw; +} + +#deck.dance .avtr:not(.animate) { + transform-origin: bottom; + animation: MoveSideSide 2s linear infinite; +} + +.spot { + flex: 1; + position: relative; + display: flex; + flex-direction: column; + justify-content: flex-end; + margin-right: var(--pad2); + margin-left: var(--pad2); + min-width: 0; +} + +.spot.empty .djplaque { + flex-direction: column; + color: rgba(255, 2556, 255, 0.5); +} + +.avtr { + display: none; + position: absolute; + z-index: 0; + bottom: 1.4rem; + width: 100%; + height: 6rem; + background-size: contain; + background-repeat: no-repeat; + background-position: center bottom; +} + +.avtr.animate { + animation: MoveUpDown 1s linear infinite; +} + +.djplaque { + position: relative; + z-index: 1; + display: flex; + justify-content: space-between; + min-width: 0; + padding: 0 var(--pad2); + font-size: 0.75rem; + line-height: 1.5rem; + text-align: center; + color: #fff; + background-color: var(--color-bg-s1); + background-image: linear-gradient(to bottom, rgba(0, 0, 0, 0.125), rgba(0, 0, 0, 0)); + border-top-left-radius: 0.5rem; + border-top-right-radius: 0.5rem; +} +.djActive { + border-top: 1px solid rgba(0, 0, 0, 0.2); + background-color: var(--color-bg-s1) !important; +} + +.djname { + text-overflow: ellipsis; + overflow: hidden; + white-space: nowrap; + padding-right: 5px; +} + +.playcount { + white-space: nowrap; +} + +#prgbar { + width: 100%; + height: 0.5rem; + border-bottom: 1px solid #333; + margin-bottom: 0; +} #screenBox { position: absolute; top: -100%; @@ -1151,7 +1155,7 @@ html .ui-button.ui-state-disabled:active { top: 0; width: 100%; height: 100%; - background: linear-gradient(rgba(0, 0, 0, 0.5), rgba(255, 255, 255, 0.1)); + background: linear-gradient(rgba(0, 0, 0, 0.5), rgba(255, 255, 255, 0.15)); } /* Queues / Playlists */ @@ -1163,12 +1167,45 @@ html .ui-button.ui-state-disabled:active { overflow: hidden; } +.queuetop { + background-color: var(--color-bg-t1); + border-top: 1px solid rgba(255, 255, 255, 0.1); + border-bottom: 1px solid rgba(255, 255, 255, 0.1); + border-bottom-left-radius: 0.5rem; + border-bottom-right-radius: 0.5rem; +} + input#queueFilter { width: 100%; + border-bottom: 0; + border-top-left-radius: 0; + border-top-right-radius: 0; } div#filterMachine { - padding: 0 var(--pad2) var(--pad2) var(--pad2); + padding: 0; +} + +#mainqueue { + padding: var(--pad1) var(--pad2); +} +#mainqueue.emptyList:before, +#mainqueue.loading:before, +#mainqueue.overFiltered:before { + display: flex; + justify-content: center; + padding: var(--pad2); + font-style: italic; + color: rgba(255, 255, 255, 0.4); +} +#mainqueue.emptyList:before { + content: "this playlist is empty"; +} +#mainqueue.loading:before { + content: "loading..."; +} +#mainqueue.overFiltered:before { + content: "no tracks meet your search criteria"; } #mainqueue .material-icons { @@ -1177,9 +1214,12 @@ div#filterMachine { } #queuelist .pvbar { + margin: var(--pad1) 0; padding: var(--pad2) var(--pad2); background-color: var(--color-bg-t1); - border-bottom: 1px solid black; + border-top: 1px solid var(--color-bg-t2); + border-bottom: 1px solid var(--color-bg); + border-radius: 0.5rem; cursor: move; /* fallback if grab cursor is unsupported */ cursor: grab; @@ -1370,13 +1410,12 @@ a.importLinkCheck { z-index: 15; display: flex; align-items: center; + max-width: 100vw; padding: var(--pad2); font-size: 1rem; text-align: center; + color: #fff; background-color: var(--color-bg-s1); - border-bottom: 2px solid rgba(255, 255, 255, 0.125); - border-bottom-left-radius: 0.5rem; - border-bottom-right-radius: 0.5rem; } #currentTheme { @@ -1414,7 +1453,6 @@ a.importLinkCheck { display: flex; justify-content: space-between; margin: var(--pad3); - font-size: 0.875rem; color: #bbb; word-break: break-word; } @@ -1422,7 +1460,7 @@ a.importLinkCheck { .newChat .botson { position: relative; z-index: 2; - margin: 0 var(--pad3) 0 0; + margin: 0.1rem var(--pad3) 0 0; width: 2rem; height: 2rem; cursor: pointer; @@ -1461,18 +1499,23 @@ a.importLinkCheck { } .chatContent .utitle { - line-height: 1.6; - color: rgba(255, 255, 255, 0.25); + position: absolute; + z-index: 2; + left: -1.5rem; + color: #666; } .chatHead { + position: relative; display: flex; + align-items: center; } .chatName { - margin-right: var(--pad2); + margin-right: var(--pad1); font-size: 0.75rem; font-weight: 700; + letter-spacing: 0.05em; color: rgba(255, 255, 255, 0.4); cursor: pointer; } @@ -1498,6 +1541,7 @@ a.importLinkCheck { .chatText { position: relative; + font-size: 1rem; } .chatText.deleteMe:hover { @@ -1599,19 +1643,22 @@ a.importLinkCheck { .chatCard { display: block; margin: var(--pad2) 0; + max-width: 100%; } #chatbottom { position: relative; display: grid; - grid-template-columns: 3.5rem 1fr var(--pad3); + grid-template-columns: auto 1fr; justify-items: center; + gap: 0.5rem; height: var(--height-footer); + padding-right: var(--pad3); } #pickEmoji { grid-column: 1/2; - margin-left: var(--pad2); + margin-left: var(--pad3); } #pickerResults { @@ -2093,11 +2140,12 @@ div#blog { @media only screen and (min-width: 640px) { #mainGrid, #mainGrid.mmchat { - grid-template-rows: auto auto auto 1fr; + grid-template-rows: auto auto auto auto 1fr; grid-template-columns: minmax(28rem, 1fr) minmax(18rem, 24rem); grid-template-areas: "head mmopts" - "stage chat" + "player chat" + "playingstage chat" "theme chat" "queues chat"; } @@ -2105,7 +2153,8 @@ div#blog { #mainGrid.mmusrs { grid-template-areas: "head mmopts" - "stage users" + "player users" + "playingstage users" "theme users" "queues users"; } @@ -2113,7 +2162,8 @@ div#blog { #mainGrid.login { grid-template-areas: "head queues" - "stage queues" + "player queues" + "playingstage queues" "theme queues" "login queues"; } @@ -2143,6 +2193,10 @@ div#blog { #mainGrid.mmqueue #thehistoryWrap { grid-area: queues; } + + #queueControls { + padding: var(--pad3) var(--pad4); + } } @media only screen and (max-width: 799px) { @@ -2161,26 +2215,83 @@ div#blog { top: 0; } + #mainGrid, + #mainGrid.mmchat { + grid-template-rows: auto auto auto 1fr; + grid-template-columns: auto minmax(28rem, 1fr) minmax(18rem, 24rem); + grid-template-areas: + "head player mmopts" + "head playingstage chat" + "head theme chat" + "head queues chat"; + } + + #mainGrid.mmusrs { + grid-template-areas: + "head player mmopts" + "head playingstage users" + "head theme users" + "head queues users"; + } + + #mainGrid.login { + grid-template-areas: + "head player queues" + "head playingstage queues" + "head theme queues" + "head login queues"; + } + #importPromptBox .modalContent { width: 480px; } #topbar { - padding-right: var(--pad4); - padding-left: var(--pad4); + flex-direction: column; + justify-content: center; + padding-top: var(--pad2); + padding-bottom: var(--pad4); } - #idtitle { + .header_icon { + margin-bottom: 0.5rem; + } + + #loggedInUser, + #roomlogo { + margin-right: 0; + margin-left: 0; + } + + #loggedInUser { + margin-top: 0.5rem; + } + + #roomlogo { + margin-bottom: 0.5rem; + } + + /* #idtitle { display: block; + } */ + + #volandthings { + padding-top: var(--pad4); + padding-bottom: var(--pad2); } #actualChat { min-width: 20rem; - max-width: 30rem; + max-width: 28rem; + } + + #deck { + margin-left: 2.5vw; + margin-right: 2.5vw; } } -@media only screen and (min-width: 1000px) { +@media only screen and (min-width: 1024px) { body { background: linear-gradient(var(--color-bg-t1), var(--color-bg)); } @@ -2190,12 +2301,12 @@ div#blog { #mainGrid.mmchat, #mainGrid.mmusrs { grid-template-rows: auto auto auto 1fr; - grid-template-columns: minmax(20rem, 20vw) minmax(30rem, 48rem) 30vw; + grid-template-columns: auto 9fr min-content 12fr; grid-template-areas: - "users head chat" - "users stage chat" - "users theme chat" - "users queues chat"; + "head users player chat" + "head users playingstage chat" + "head users theme chat" + "head users queues chat"; } #mainGrid.mmusrs #queuebox, @@ -2209,18 +2320,22 @@ div#blog { #mainGrid.login { grid-template-areas: - "head head queues" - "stage stage queues" - "theme theme queues" - "login login queues"; + "head player player queues" + "head playingstage playingstage queues" + "head theme theme queues" + "head login login queues"; } #mainGrid #minimodeoptions { display: none; } - #topbar { - padding: 0; + #djStage, + #themebox, + #queuebox { + width: 46vw; + max-width: 56rem; + justify-self: flex-end; } #djStage { @@ -2237,11 +2352,6 @@ div#blog { width: 6.5rem; } - #deck { - margin-left: 5vw !important; - margin-right: 5vw !important; - } - .djplaque { justify-content: space-between; } @@ -2254,6 +2364,36 @@ div#blog { align-self: flex-end; } + #allUsers { + grid-auto-flow: dense; + padding-top: var(--pad5); + } + #allUsers .botson { + grid-column: -2 / -1; + } + #allUsers .djOrder { + grid-column: 1 / 2; + } + #allUsers .prsnNameRole { + grid-column: 2 / 3; + justify-content: flex-end; + } + #allUsers .prsnName { + order: 2; + } + #allUsers .prsnRole { + margin-left: 0; + margin-right: 0.5rem; + } + + .newChat { + margin-left: var(--pad4); + } + + #pickEmoji { + margin-left: var(--pad4); + } + #usersStats { justify-content: flex-start; padding: var(--pad3) var(--pad4); @@ -2269,31 +2409,3 @@ div#blog { width: 100%; } } - -@media only screen and (min-width: 2100px) { - html { - font-size: calc(0.4em + 0.4vw); - } - - #mainGrid, - #mainGrid.mmqueue, - #mainGrid.mmchat, - #mainGrid.mmusrs { - grid-template-columns: minmax(24rem, 36rem) minmax(40vw, 100vw) minmax(24rem, 36rem); - } - - #usersbox .tab { - color: #ffffff; - background-color: var(--color-bg-t2); - box-shadow: 0 -0.2rem 0.5rem -0.2rem black; - } - - .usersWrap { - display: block !important; - flex: 1; - } - - #allUsersWrap { - border-right: 5px solid black; - } -} diff --git a/index.html b/index.html index 0f4f039..74f43cd 100644 --- a/index.html +++ b/index.html @@ -18,43 +18,41 @@
    - + + + + +
    Reconnecting...
    -
    -
    -
    -
    -
    -
    -
      -
    • -
    • -
    • -
    • -
    • -
    • -
    • -
    • -
    • -
    • -
    • -
    • -
    • -
    • -
    • -
    • -
    • -
    • -
    • -
    • -
    • -
    • -
    • -
    • -
    • -
    • -
    • -
    • -
    • -
    • -
    • -
    • -
    • -
    • -
    • -
    • -
    • -
    • -
    • -
    • -
    • -
    • -
    -
    -
    -
    -
    - - - link - -
    -
    - - -
    -
    -
    -
    + +
    +
    +
    + + +
    +
    -
    -
    -
    Loading firetable...
    -
    -
    -
    -
    -
    -
    -
    +
    + + + link +
    -
    +
    + +
    + +
    +
    +
    +
    +
    +
      +
    • +
    • +
    • +
    • +
    • +
    • +
    • +
    • +
    • +
    • +
    • +
    • +
    • +
    • +
    • +
    • +
    • +
    • +
    • +
    • +
    • +
    • +
    • +
    • +
    • +
    • +
    • +
    • +
    • +
    • +
    • +
    • +
    • +
    • +
    • +
    • +
    • +
    • +
    • +
    • +
    • +
    • +
    +
    +
    +
    + +
    +
    +
    Loading firetable...
    +
    +
    +
    +
    +
    +
    -
    @@ -201,8 +188,8 @@
    +
    -
    diff --git a/js/main.js b/js/main.js index 1a83d8e..e6803fe 100644 --- a/js/main.js +++ b/js/main.js @@ -210,7 +210,7 @@ firetable.init = function () { $(window).resize( firetable.utilities.debounce(function () { // This will execute whenever the window is resized - $("#thehistory").css("top", $("#stage").outerHeight() + $("#topbar").outerHeight()); + $("#thehistory").css("top", $("#djStage").outerHeight() + $("#topbar").outerHeight()); $("#playerArea,#scScreen").width($("#djStage").outerWidth()).height($("#djStage").outerHeight()); $("#stealContain").css({ top: $("#grab").offset().top + $("#grab").height(), @@ -660,19 +660,31 @@ firetable.actions = { filterQueue: function (val) { if (val.length == 0) { $("#mainqueue .pvbar").show(); + $("#mainqueue").removeClass("overFiltered"); return; - } else { } val = val.toLowerCase(); - $("#mainqueue .pvbar").each(function (p, q) { - var txt = $(q).find(".listwords").text(); - var regex = new RegExp(val, "ig"); - if (txt.match(regex)) { - $(q).show(); - } else { - $(q).hide(); - } - }); + numresults = 0; + $("#mainqueue .pvbar") + .each(function (p, q) { + var txt = $(q).find(".listwords").text(); + var regex = new RegExp(val, "ig"); + if (txt.match(regex)) { + $(q).show(); + numresults++; + } else { + $(q).hide(); + } + }) + .promise() + .done(function () { + console.log(numresults); + if (!numresults) { + $("#mainqueue").addClass("overFiltered"); + } else { + $("#mainqueue").removeClass("overFiltered"); + } + }); }, muteToggle: function (zeroMute) { var muted = localStorage["firetableMute"]; @@ -1718,6 +1730,7 @@ firetable.ui = { }, init: function () { $("#mainqueue").sortable({ + axis: "y", start: function (event, ui) { var start_pos = ui.item.index(); ui.item.data("start_pos", start_pos); @@ -2245,10 +2258,10 @@ firetable.ui = { if (data.hasOwnProperty(key)) { $("#user" + data[key].id + " .djOrder") .html( - position + - ' pending_actions' + ' on the waitlist">pending_actions ' + + position ) .removeClass("ondeck") .addClass("waitlist"); @@ -2291,7 +2304,7 @@ firetable.ui = { "
    "; countr++; $("#user" + data[key].id + " .djOrder") - .html(countr + ' album') + .html('album ' + countr) .removeClass("waitlist") .addClass("ondeck"); } @@ -2478,9 +2491,9 @@ firetable.ui = { if (ftapi.users[chatData.id]) { if (ftapi.users[chatData.id].username) namebo = ftapi.users[chatData.id].username; - if (ftapi.users[chatData.id].mod) utitle = "mod"; - if (ftapi.users[chatData.id].supermod) utitle = "supermod"; - if (ftapi.users[chatData.id].hostbot) utitle = "robot"; + if (ftapi.users[chatData.id].mod) utitle = "stars"; + if (ftapi.users[chatData.id].supermod) utitle = "local_police"; + if (ftapi.users[chatData.id].hostbot) utitle = "smart_toy"; } else if (chatData.name) { namebo = chatData.name; } @@ -2615,8 +2628,11 @@ firetable.ui = { }); ftapi.events.on("playlistChanged", function (okdata, listID) { + firetable.debug && console.log(okdata.length); firetable.queue = okdata; - $("#mainqueue").html(""); + $("#mainqueue").html("").removeClass("emptyList overFiltered").addClass("loading"); + $("#queueFilterForm")[0].reset(); + if ($.isEmptyObject(okdata)) $("#mainqueue").addClass("emptyList"); for (var key in okdata) { if (okdata.hasOwnProperty(key)) { var $newli = $playlistItemTemplate.clone(); @@ -2699,6 +2715,7 @@ firetable.ui = { $("#mainqueue").append($newli); } } + $("#mainqueue").removeClass("loading"); }); firetable.ui.LinkGrabber.start(); @@ -2806,7 +2823,7 @@ firetable.ui = { $("#history").bind("click", function () { $("#thehistoryWrap") .slideToggle() - .css("top", $("#stage").outerHeight() + $("#topbar").outerHeight()); + .css("top", $("#djStage").outerHeight() + $("#topbar").outerHeight()); $(this).toggleClass("on"); }); $("#startMerge").bind("click", function () { @@ -3772,7 +3789,7 @@ firetable.ui = { firetable.debug && console.log("COLOR CHANGE!", data); firetable.color = data.color; - firetable.countcolor = data.txt; + //firetable.countcolor = data.txt; if (data.color == "#fff" || data.color == "#7f7f7f") { firetable.color = firetable.orange; firetable.countcolor = "#fff"; @@ -3787,7 +3804,7 @@ firetable.ui = { firetable.debug && console.log("a") firetable.countcolor = "#000000c9"; } - $("#stage").css("color", firetable.countcolor); + $("#djStage").css("color", firetable.countcolor); */ $(".customColorStyles").remove(); @@ -3797,13 +3814,11 @@ firetable.ui = { $("head").append( "` + ); + next(); + }); }); ftapi.events.on("screenStateChanged", function (data) { firetable.debug && console.log("thescreen:", data); @@ -2251,12 +2262,32 @@ firetable.ui = { }); ftapi.events.on("waitlistChanged", function (data) { if (data) { - $("#allUsers .djOrder.waitlist").html("").removeClass("waitlist"); + $("#allUsers .djOrder.waitlist").each(function () { + $(this).html("").removeClass("waitlist"); + var $prson = $(this).closest(".prson"); + switch (true) { + case $prson.hasClass("robot"): + $prson.remove().appendTo("#usersBot"); + break; + case $prson.hasClass("supermod"): + $prson.remove().appendTo("#usersSuper"); + break; + case $prson.hasClass("mod"): + $prson.remove().appendTo("#usersMod"); + break; + default: + $prson.remove().appendTo("#usersRegular"); + break; + } + }); for (var key in data) { var position = parseInt(key + 1); firetable.debug && console.log("waitlist", position, data); if (data.hasOwnProperty(key)) { - $("#user" + data[key].id + " .djOrder") + $("#user" + data[key].id) + .remove() + .appendTo("#usersWaitlist") + .find(".djOrder") .html( '
    "; countr++; - $("#user" + data[key].id + " .djOrder") + $("#user" + data[key].id) + .remove() + .appendTo("#usersDJs") + .find(".djOrder") .html('album ' + countr) .removeClass("waitlist") .addClass("ondeck"); @@ -2332,6 +2383,17 @@ firetable.ui = { } else { $("#avtr" + i).addClass("animate"); $("#djthing" + i).addClass("djActive"); + $(".djactive-style").remove(); + $("head") + .delay(500) + .queue(function (next) { + $(this).append( + `` + ); + next(); + }); } } }); @@ -2344,6 +2406,17 @@ firetable.ui = { } else { $("#avtr" + i).addClass("animate"); $("#djthing" + i).addClass("djActive"); + $(".djactive-style").remove(); + $("head") + .delay(500) + .queue(function (next) { + $(this).append( + `` + ); + next(); + }); } } }); @@ -2406,10 +2479,10 @@ firetable.ui = { return data; } ftapi.events.on("userJoined", function (data) { - console.log("user joined", data); + firetable.debug && console.log("user joined", data); data.html = $userTemplate.clone(); data = userBits(data); - data.html.attr("id", "user" + data.userid); + data.html.attr("id", "user" + data.userid).addClass(data.rolename); data.html .find(".botson") .css( From bd11f8e074b729d5e2ffdf01946e1a3879bb798c Mon Sep 17 00:00:00 2001 From: Andrew Tibbetts Date: Thu, 9 Apr 2026 23:26:16 -0500 Subject: [PATCH 13/17] 2026 BETA 1 --- css/looks.css | 1349 ++++++++----- index.html | 1259 +++++++------ js/cards.js | 259 +++ js/chat.js | 439 +++++ js/constants.js | 73 + js/emoji.js | 172 ++ js/firetable.js | 21 + js/glitch.js | 313 ++++ js/helpers.js | 412 ++++ js/init.js | 175 ++ js/lastfm.js | 187 ++ js/main.js | 4375 ------------------------------------------- js/main.original.js | 4171 +++++++++++++++++++++++++++++++++++++++++ js/md5.js | 213 +++ js/player.js | 269 +++ js/playlist.js | 659 +++++++ js/room.js | 631 +++++++ js/search.js | 285 +++ js/state.js | 224 +++ js/twemoji.js | 2 + js/ui.js | 663 +++++++ js/users.js | 474 +++++ serve.json | 7 + 23 files changed, 11120 insertions(+), 5512 deletions(-) create mode 100644 js/cards.js create mode 100644 js/chat.js create mode 100644 js/constants.js create mode 100644 js/emoji.js create mode 100644 js/glitch.js create mode 100644 js/helpers.js create mode 100644 js/init.js create mode 100644 js/lastfm.js delete mode 100644 js/main.js create mode 100644 js/main.original.js create mode 100644 js/md5.js create mode 100644 js/player.js create mode 100644 js/playlist.js create mode 100644 js/room.js create mode 100644 js/search.js create mode 100644 js/state.js create mode 100644 js/twemoji.js create mode 100644 js/ui.js create mode 100644 js/users.js create mode 100644 serve.json diff --git a/css/looks.css b/css/looks.css index e947af2..92343c3 100644 --- a/css/looks.css +++ b/css/looks.css @@ -1,78 +1,36 @@ -@-moz-keyframes spin { - from { - -moz-transform: rotate(0deg); - } - - to { - -moz-transform: rotate(359deg); - } -} - -@-webkit-keyframes spin { - from { - -webkit-transform: rotate(0deg); - } - - to { - -webkit-transform: rotate(359deg); - } -} +/* ============================================ + Keyframes + ============================================ */ @keyframes spin { - from { - transform: rotate(0deg); - } - - to { - transform: rotate(359deg); - } + from { transform: rotate(0deg); } + to { transform: rotate(359deg); } } @keyframes MoveUpDown { 50% { - -webkit-transform: translateY(5px); transform: translateY(5px); will-change: transform; } } @keyframes MoveSideSide { - 5% { - transform: rotate(0deg) translateY(0.1rem); - } - - 25% { - transform: rotate(-3deg) translateY(0.1rem); - } - - 45% { - transform: rotate(0deg) translateY(0.1rem); - } - - 55% { - transform: rotate(0deg) translateY(0.1rem); - } - - 75% { - transform: rotate(3deg) translateY(0.1rem); - } - - 95% { - transform: rotate(0deg) translateY(0.1rem); - } + 5%, 45%, 55%, 95% { transform: rotate(0deg) translateY(0.1rem); } + 25% { transform: rotate(-3deg) translateY(0.1rem); } + 75% { transform: rotate(3deg) translateY(0.1rem); } } @keyframes pulse { - from { - opacity: 1; - } - - to { - opacity: 0.5; - } + from { opacity: 1; } + to { opacity: 0.5; } } +/* ============================================ + Custom Properties + ============================================ */ + :root { + /* Colors */ --color-bg: #181818; --color-bg-s1: #151515; --color-bg-s2: #111111; @@ -82,28 +40,45 @@ --color-orangelite: #f6993c; --color-orange66: #f4810b66; --color-orange33: #f4810b33; + --color-accent: var(--color-orange); + --color-text: #bbbbbb; + --color-text-light: #eee; + --color-text-muted: #888; + --color-text-dim: #666; + + /* Spacing */ --pad1: 0.25rem; --pad2: 0.5rem; --pad3: 1rem; --pad4: 1.5rem; --pad5: 4rem; --pad6: 8rem; + + /* Layout */ --height-footer: 3rem; + + /* Radii */ + --radius-sm: 0.25rem; + --radius-md: 0.5rem; + --radius-pill: 999px; + + /* Typography */ + --font-family: "Open Sans", helvetica, arial, sans-serif; + + /* Shadows */ + --shadow-drop: 0 0.25rem 0.5rem -0.25rem black; } +/* ============================================ + Base / Reset + ============================================ */ + *, *:before, *:after { - -webkit-box-sizing: border-box; - -moz-box-sizing: border-box; - -ms-box-sizing: border-box; box-sizing: border-box; } -html { - font-size: 14px; -} - html, body { height: 100%; @@ -114,8 +89,9 @@ body { flex-direction: column; margin: 0; font-size: 1rem; - color: #bbbbbb; + color: var(--color-text); background: var(--color-bg); + background: linear-gradient(var(--color-bg-t1), var(--color-bg)); overflow: hidden; } @@ -124,7 +100,7 @@ input, select, textarea, button { - font-family: "Open Sans", helvetica, arial, sans-serif; + font-family: var(--font-family); } h2 { @@ -151,16 +127,16 @@ input:not([type="checkbox"]):not([type="radio"]), select, button { height: 2rem; - padding: 0.25rem 0.5rem; + padding: var(--pad1) var(--pad2); font-size: 0.9rem; - font-family: "Open Sans", helvetica, arial, sans-serif; - color: #eee; + font-family: var(--font-family); + color: var(--color-text-light); background-color: rgba(255, 255, 255, 0.05); border: 1px solid transparent; border-bottom-color: rgba(0, 0, 0, 0.85); border-top-color: rgba(255, 255, 255, 0.15); - border-radius: 0.25rem; - box-shadow: 0 0.25rem 0.5rem -0.25rem black; + border-radius: var(--radius-sm); + box-shadow: var(--shadow-drop); } input:not([type="checkbox"]):not([type="radio"]) { @@ -197,13 +173,13 @@ code { display: inline-block; padding: 0.1em 0.2em; font: 12px Monaco, Consolas, "Andale Mono", "DejaVu Sans Mono", monospace; - color: #bbb; + color: var(--color-text); background-color: rgba(123, 123, 123, 0.5); } -.simplebar-scrollbar:before { - background: var(--color-orange); -} +/* ============================================ + Shared Components + ============================================ */ .previewicon { display: flex; @@ -215,7 +191,7 @@ code { font-size: 1rem; color: white; background-color: black; - border-radius: 999px; + border-radius: var(--radius-pill); opacity: 0.66; } @@ -229,7 +205,7 @@ code { } .utitle { - font-size: 1rem; + font-size: 0.875rem; font-weight: 100; color: rgba(255, 255, 255, 0.4); } @@ -247,8 +223,7 @@ code { } .notice p { - margin-bottom: 15px; - margin-top: 15px; + margin: 15px 0; } .flexpacer { @@ -257,53 +232,74 @@ code { .tabs { display: flex; - align-items: flex-end; + align-items: center; + width: 100%; + border-radius: 20px; + overflow: hidden; } .tab { - flex: 1; - margin: 5px 0 0 0; + flex: 1 1 auto; + height: auto; + min-height: 2.5rem; padding: 0.5em; - font-size: 0.85rem; + font-size: 0.7rem; + font-weight: 700; + line-height: 1; + text-transform: uppercase; + letter-spacing: .05em; color: #999; text-align: center; background-color: var(--color-bg); border: 0; - border-top: 1px solid rgba(255, 255, 255, 0.1); - border-radius: 0; - box-shadow: 0 -0.2rem 0.5rem -0.1rem black, inset 0 -0.2rem 0.4rem -0.2rem black; - clip-path: polygon( - 0% 0%, - 0% 0%, - calc(100% - var(--pad3)) 0%, - 100% var(--pad3), - 100% 100%, - 100% 100%, - 0% 100%, - 0% 0% - ); + box-shadow: none; } .tab.on { + z-index: 1; color: #ffffff; - background: linear-gradient(var(--color-bg-t1), var(--color-bg)); - box-shadow: 0 -0.2rem 0.5rem -0.2rem black; + background: linear-gradient(var(--color-bg), black); } -.tab:not(:first-child) { - margin-left: 5px; +.logoutButt { + margin-left: var(--pad2); } -span.removemeIcon.material-icons { +.removemeIcon { font-size: 1rem; vertical-align: middle; } +.djplaque .deckRemoveBtn { + display: flex; + align-items: center; + justify-content: center; + flex-shrink: 0; + width: 1.5rem; + height: 1.5rem; + padding: 0; + background: transparent; + border: none; + color: rgba(255, 255, 255, 0.4); + cursor: pointer; + border-radius: var(--radius-sm); +} +.djplaque .deckRemoveBtn:hover { + color: #fff; + background: rgba(255, 255, 255, 0.12); +} +.djplaque .deckRemoveBtn i { + font-size: 0.9rem; + pointer-events: none; +} + .butt { display: flex; justify-content: center; align-items: center; margin: 0; + min-width: 2rem; + min-height: 2rem; color: white; font-size: 0.75rem; font-weight: 700; @@ -313,9 +309,16 @@ span.removemeIcon.material-icons { background-color: var(--color-orange); border-top: 1px solid rgba(255, 255, 255, 0.3); border-bottom: 1px solid black; - box-shadow: 0 0.25rem 0.5rem -0.25rem black; + box-shadow: var(--shadow-drop); transition: all 100ms ease-in-out; user-select: none; + + &.small { + height: min-content; + min-height: 0; + padding: 0.5em 0.75em; + font-size: 0.65rem; + } } .butt:hover { @@ -323,12 +326,12 @@ span.removemeIcon.material-icons { } .graybutt { - color: #888; + color: var(--color-text-muted); background: rgba(255, 255, 255, 0.05); } .graybutt:hover { - background: #444; + background: rgba(255, 255, 255, 0.1); } .graybutt:focus { @@ -357,52 +360,106 @@ span.removemeIcon.material-icons { box-shadow: inset 0 0 1rem var(--color-orange33); } -.iconbutt .material-icons { +#fire.on { + color: white; +} + +.iconbutt .material-icons, +.iconbutt .material-symbols-outlined { font-size: 1.43rem; } +.material-symbols-outlined { + font-variation-settings: 'FILL' 1, 'wght' 400, 'GRAD' 0, 'opsz' 24; +} + +.accent { + color: white; + background-color: var(--color-orange); + background-image: radial-gradient(circle at 50% 15%, rgba(0, 0, 0, 0.2), rgba(0, 0, 0, 0.4)); + border-right: 1px solid rgba(123, 123, 123, 0.5); + border-left: 1px solid rgba(123, 123, 123, 0.5); + border-radius: var(--radius-md); +} + +.accent:hover { + background-color: var(--color-orangelite); +} + .scrollWrap { height: 100%; overflow: auto; } +/* Native scrollbar styling - invisible by default, visible on hover */ +.scroll-view, +.scrollWrap, +.modalContent { + scrollbar-gutter: stable; + scrollbar-width: thin; + scrollbar-color: transparent transparent; + transition: scrollbar-color 200ms ease-in-out; +} -.pvbarWrap { - display: flex; +.scroll-view:hover, +.scrollWrap:hover, +.modalContent:hover { + scrollbar-color: color-mix(in oklch, var(--color-accent) 50%, transparent) transparent; } -.working .material-icons { - animation: spin 3s infinite linear; +/* WebKit browsers (Chrome, Safari, Edge) - default invisible scrollbars */ +.scroll-view::-webkit-scrollbar, +.scrollWrap::-webkit-scrollbar, +.modalContent::-webkit-scrollbar { + width: 8px; } -.ps { - position: relative; +.scroll-view::-webkit-scrollbar-track, +.scrollWrap::-webkit-scrollbar-track, +.modalContent::-webkit-scrollbar-track { + background: transparent; } -.ps__rail-x { - display: none !important; +.scroll-view::-webkit-scrollbar-thumb, +.scrollWrap::-webkit-scrollbar-thumb, +.modalContent::-webkit-scrollbar-thumb { + background-color: transparent; + border-radius: 4px; + transition: background-color 200ms ease-in-out; } -.ps__rail-y { - z-index: 2; +/* Show scrollbar thumb on container hover */ +.scroll-view:hover::-webkit-scrollbar-thumb, +.scrollWrap:hover::-webkit-scrollbar-thumb, +.modalContent:hover::-webkit-scrollbar-thumb { + background-color: var(--color-accent); } -.ps .ps__rail-y:focus, -.ps .ps__rail-y:hover, -.ps .ps__rail-y.ps--clicking { - background-color: transparent; - outline: none; +.pvbarWrap { + display: flex; + + &:hover { + background-color: var(--color-bg-t1); + } } -.ps__thumb-y, -.ps__rail-y:hover > .ps__thumb-y, -.ps__rail-y:focus > .ps__thumb-y, -.ps__rail-y.ps--clicking .ps__thumb-y { - width: 0.5rem; - background-color: rgba(123, 123, 123, 0.5); - border-radius: 0.2rem; +.working .material-icons { + animation: spin 3s infinite linear; +} + + +#viewnav { + display: flex; + align-items: center; + margin-left: var(--pad3); } -/* Top Bar / Site Header */ +#viewnav .header_icon .material-icons { + font-size: 1.75rem; +} + +/* ============================================ + Top Bar / Site Header + ============================================ */ #topbar { grid-area: head; display: flex; @@ -442,7 +499,7 @@ a.sociallogo[href] { .sociallogo svg { width: 1.2rem; height: 1.2rem; - fill: #888; + fill: var(--color-text-muted); } #roomlogo { @@ -483,117 +540,177 @@ a.sociallogo[href] { .header_icon .material-icons { font-size: 1.2rem; - color: #888; + color: var(--color-text-muted); } .header_icon:hover .material-icons { color: white; } -/*------------------------------------ MAIN BODY THINGS */ +.header_icon.on .material-icons { + color: white; +} + +.header_icon[title] { + position: relative; +} + +.header_icon[title]::after { + content: attr(title); + position: absolute; + left: calc(100% + 0.75rem); + top: 50%; + transform: translateY(-50%); + padding: 0.2rem 0.5rem; + font-size: 0.7rem; + font-family: var(--font-family); + white-space: nowrap; + color: var(--color-text-light); + background: var(--color-bg-t2); + border-radius: var(--radius-sm); + pointer-events: none; + opacity: 0; + transition: opacity 150ms ease; + z-index: 100; +} + +.header_icon[title]:hover::after { + opacity: 1; +} + +.iconbutt[title] { + position: relative; +} + +.iconbutt[title]::after { + content: attr(title); + position: absolute; + bottom: calc(100% + 0.5rem); + left: 50%; + transform: translateX(-50%); + padding: 0.2rem 0.5rem; + font-size: 0.7rem; + font-family: var(--font-family); + font-weight: 400; + text-transform: none; + letter-spacing: 0; + white-space: nowrap; + color: var(--color-text-light); + background: var(--color-bg-t2); + border-radius: var(--radius-sm); + pointer-events: none; + opacity: 0; + transition: opacity 150ms ease; + z-index: 100; +} + +.iconbutt[title]:hover::after { + opacity: 1; +} + +/* ============================================ + Main Grid Layout + ============================================ */ #mainGrid { grid-area: main; flex: 1; display: grid; - grid-template-rows: auto auto auto auto auto 1fr; + grid-template-rows: auto auto auto 1fr; grid-template-columns: 1fr; grid-template-areas: "head" - "player" - "playingstage" - "main" - "main" - "main"; - justify-content: center; + "stage" + "nav" + "content"; + width: 100%; overflow: hidden; } -#mainGrid.mmusrs { - grid-template-areas: - "head" - "player" - "playingstage" - "theme" - "mmopts" - "users"; +#appShell { + display: contents; } -#mainGrid.mmusrs #queuebox, -#mainGrid.mmusrs #thehistoryWrap, -#mainGrid.mmusrs #actualChat, -#mainGrid.mmusrs #login { - display: none; +/* Suppress flash during Firebase auth check */ +#mainGrid.pre-auth > * { + visibility: hidden; } -#mainGrid.mmqueue { - grid-template-areas: - "head" - "player" - "playingstage" - "theme" - "mmopts" - "queues"; -} +/* Mobile: show only the active content panel per tab */ + +/* View panels are hidden by default; a view-class on #mainGrid reveals the active one. + At mobile they only show when the grid is in mmqueue mode. */ +#mainGrid.mmqueue.view-playlists #queuebox { display: flex; } +#mainGrid.mmqueue.view-history #thehistoryWrap { display: flex; } +#mainGrid.mmqueue.view-cards #cardsWrap { display: block; } + +#mainGrid.mmusrs #actualChat { display: none; } +#mainGrid.mmusrs #usersbox { display: flex; } #mainGrid.mmqueue #usersbox, -#mainGrid.mmqueue #thehistoryWrap, -#mainGrid.mmqueue #actualChat, -#mainGrid.mmqueue #login { - display: none; -} +#mainGrid.mmqueue #actualChat { display: none; } + +#mainGrid.mmchat #usersbox { display: none; } +#mainGrid.mmchat #actualChat { display: flex; } -#mainGrid.mmchat { +#mainGrid.login { + grid-template-rows: auto auto 1fr; grid-template-areas: "head" - "player" - "playingstage" - "theme" - "mmopts" - "chat"; + "stage" + "login"; } -#mainGrid.mmchat #usersbox, -#mainGrid.mmchat #queuebox, -#mainGrid.mmchat #thehistoryWrap, -#mainGrid.mmchat #login { - display: none; +#mainGrid.login #stage { + max-width: 36rem; + max-height: 40vh; + width: 100%; + margin: 0 auto; } -#mainGrid.login { - grid-template-rows: auto auto auto 1fr; - grid-template-areas: - "head" - "playingstage" - "theme" - "login"; +#mainGrid.login #login { + max-width: 36rem; + margin: 0 auto; +} + +#mainGrid.login .spot.empty .djname { + color: transparent; } -#mainGrid.login #usersbox, -#mainGrid.login #queuebox, -#mainGrid.login #history, -#mainGrid.login #thehistoryWrap, -#mainGrid.login #actualChat, -#mainGrid.login #voteActions, -#mainGrid.login #minimodeoptions { +#mainGrid.login #loggedInUser, +#mainGrid.login #viewnav { display: none; } -/* MiniMode Nav */ -#minimodeoptions { - grid-area: mmopts; +/* On very short viewports (landscape mobile) hide the login form, show just the stage */ +@media only screen and (max-height: 400px) { + #mainGrid.login { + grid-template-rows: auto 1fr; + grid-template-areas: + "head" + "stage"; + } + #mainGrid.login #login { display: none; } } -#minimodeoptions .tab { - background-color: var(--color-bg-s2); +/* Mobile: view selector lives in the mini-nav, not the header */ +#playlists, +#history, +#cardcase { + display: none; } -#minimodeoptions .tab.on { - background-color: var(--color-bg); +/* ============================================ + MiniMode Nav + ============================================ */ +#minimodeoptions { + grid-area: nav; } -/* Users Lists */ +/* ============================================ + Users Lists + ============================================ */ #usersbox { - grid-area: users; + grid-area: content; display: flex; flex-direction: column; width: 100%; @@ -606,13 +723,12 @@ a.sociallogo[href] { } #usersStats { - /* display: flex;*/ display: none; align-items: center; justify-content: center; gap: 0.5rem; padding: var(--pad2) var(--pad3) 0 var(--pad3); - color: #888; + color: var(--color-text-muted); } #usersStats h2 { @@ -622,7 +738,6 @@ a.sociallogo[href] { } #usersStats .listenerType { - /* display: flex;*/ display: none; align-items: center; font-size: 0.875rem; @@ -631,14 +746,14 @@ a.sociallogo[href] { #usersStats .material-icons { font-size: 1rem; - color: #888; + color: var(--color-text-muted); } #allUsers { display: grid; - grid-template-columns: auto 1fr auto; + grid-template-columns: auto auto 1fr; align-items: center; - gap: var(--pad2); + gap: var(--pad3) var(--pad2); padding: var(--pad4); } @@ -647,34 +762,28 @@ a.sociallogo[href] { display: contents; } +#allUsers #userKEY.prson { + display: none !important; +} + #allUsersWrap { height: 100%; width: 100%; } - .prson.idlething .imptcon { + overflow: auto; display: block; } .prsnStatus { - position: absolute; - z-index: 1; - bottom: 0; - right: -0.2rem; - font-size: 0.75rem; - color: #43b581; - transform: rotate(-45deg); -} -.prson.idle .prsnStatus { - color: var(--color-orange); + display: none; } .prsnNameRole { display: flex; align-items: center; - justify-content: flex-start; margin-left: 0.25rem; - font-size: 0.875rem; + font-size: 1rem; font-weight: 400; } .prson.blockd .prsnNameRole { @@ -682,23 +791,20 @@ a.sociallogo[href] { } .prsnName { - font-weight: 700; - color: #bbb; + font-weight: 500; + color: var(--color-text); white-space: nowrap; overflow-x: hidden; text-overflow: ellipsis; } .prsnRole { - z-index: 2; - margin-left: 0.5rem; - font-size: 1.125rem; + font-size: 1.2rem; + color: #43b581; line-height: 1; - color: rgba(255, 255, 255, 0.33); } -.smallIcon .prsnRole { - margin-left: 0.15rem; - font-size: 0.5em; +.prson.idle .prsnRole { + color: var(--color-orange); } .imptcon { @@ -711,7 +817,7 @@ a.sociallogo[href] { font-size: 0.75em; color: var(--color-orange); background-color: var(--color-bg-t2); - border-radius: 999px; + border-radius: var(--radius-pill); } .djOrder { @@ -721,7 +827,7 @@ a.sociallogo[href] { font-size: 0.85rem; font-weight: 700; line-height: 0; - color: #888; + color: var(--color-text-muted); } .djOrder .material-icons { margin-right: 0.25rem; @@ -731,19 +837,25 @@ a.sociallogo[href] { } .djOrder.ondeck .material-icons, .djOrder.waitlist .material-icons { - color: #888; + color: var(--color-text-muted); } .djOrder span { width: 1rem; } +.prsnJoined { + font-size: 0.75rem; + color: var(--color-text-dim); + white-space: nowrap; +} + .botson { background-color: #000; background-size: auto 125%; - border-radius: 999px; + border-radius: var(--radius-pill); background-position: center 66%; background-repeat: no-repeat; - border-bottom: 1px solid #888; + border-bottom: 1px solid var(--color-text-muted); clip-path: circle(60px at center); } @@ -751,10 +863,9 @@ a.sociallogo[href] { display: flex; align-items: center; position: relative; - margin-right: 0.5rem; width: 2rem; height: 2rem; - background-position: 66% 72%; + background-position: 50% 72%; } .blockon { @@ -764,7 +875,7 @@ a.sociallogo[href] { font-size: 2.2rem; background-color: rgba(0, 0, 0, 0.5); color: var(--color-orange); - border-radius: 999px; + border-radius: var(--radius-pill); } .prson.blockd .blockon { display: block; @@ -773,17 +884,20 @@ a.sociallogo[href] { display: none; } -/* Now Playing */ +/* ============================================ + Now Playing + ============================================ */ #nowplaying { - grid-area: playingstage; + grid-area: visual; + align-self: start; position: relative; display: grid; grid-template-columns: auto 1fr auto; grid-template-rows: auto auto 1fr; grid-template-areas: - "art track timr" - "art artist artist" - "art plays plays"; + "art track timrvol" + "art artist timrvol" + "art plays timrvol"; margin-bottom: auto; padding: var(--pad4) var(--pad4) 2.5rem var(--pad4); } @@ -804,7 +918,7 @@ a.sociallogo[href] { } #timr { - grid-area: timr; + grid-area: timrvol; padding-top: 0.15em; font-size: 0.85rem; font-weight: 700; @@ -846,23 +960,17 @@ a.sociallogo[href] { display: none; } -/* Player */ -#volandthings, +/* ============================================ + Player Controls + ============================================ */ #queueControls { display: flex; align-items: center; justify-content: center; - padding: 0.4rem 0.5rem; + padding: 0.8rem 1rem; white-space: nowrap; } -#volandthings { - grid-area: player; - position: relative; - display: flex; - justify-content: space-between; -} - #songthings, #voteActions, #songActions, @@ -884,22 +992,18 @@ a.sociallogo[href] { } #volplace { - flex: 1; + grid-area: timrvol; display: flex; - align-items: center; - padding-left: 0.25rem; - padding-right: 0.25rem; - white-space: nowrap; + align-items: flex-start; + justify-content: center; + margin-top: 2rem; + max-height: 3rem; } #volstatus { cursor: pointer; } -#volplace .material-icons { - font-size: 16px !important; -} - #shareinfo { text-align: right; } @@ -917,7 +1021,7 @@ a.sociallogo[href] { width: 16rem; padding: var(--pad3); background-color: #333; - border-radius: 5px; + border-radius: var(--radius-md); } #stealArrow { @@ -930,37 +1034,30 @@ a.sociallogo[href] { border-bottom: 1rem solid #333; } -select#stealpicker { +#stealpicker { width: 100%; font-size: 1rem; border: none; - font-family: "Open Sans", helvetica, arial, sans-serif; + font-family: var(--font-family); background-color: var(--color-bg-t1); padding-left: var(--pad1); - color: #eee; + color: var(--color-text-light); white-space: nowrap; } #addToQueueBttn { margin-left: 1em; - color: white; - background-color: var(--color-orange); - background-image: radial-gradient(circle at 50% 75%, rgba(0, 0, 0, 0.2), rgba(0, 0, 0, 0.4)); - border-right: 1px solid black; - border-left: 1px solid black; - border-radius: 5px; } #slider { - margin-right: var(--pad1); - width: 100%; - max-width: 10rem; - min-width: 6rem; + height: 100%; + width: 3px; } .ui-slider { position: relative; - text-align: left; + display: flex; + justify-content: center; } .ui-slider .ui-slider-handle { @@ -969,10 +1066,9 @@ select#stealpicker { width: 1.25rem; height: 1.25rem; cursor: grab; - background-color: #bbb; + background-color: var(--color-text); border-radius: 0.4rem; box-shadow: 0 0.5rem 0.5rem -0.25rem black; - -ms-touch-action: none; touch-action: none; } @@ -990,25 +1086,60 @@ select#stealpicker { background-color: var(--color-orange); } -.ui-slider-horizontal { - height: 3px; +/* Vertical volume slider */ +.ui-slider-vertical { + width: 3px; + height: 5rem; + background-color: rgba(204, 204, 204, 0.2); +} + +.ui-slider-vertical .ui-slider-handle { + margin-bottom: -0.1875rem; + width: 2rem; + height: 0.5rem; + border-radius: 2px; + background-color: var(--color-text); + box-shadow: 0 1px 4px -1px black; +} + +.ui-slider-vertical .ui-slider-range { + left: 0; + width: 100%; + bottom: 0; } -.ui-slider-horizontal .ui-slider-handle { - top: -0.6rem; - margin-left: -0.6em; +.ui-slider-vertical .ui-slider-range-min { + bottom: 0; + background-color: var(--color-accent); } -.ui-slider-horizontal .ui-slider-range { +/* Tick marks on either side of the track */ +#volplace { + position: relative; +} + +#volplace::before, +#volplace::after { + content: ""; + position: absolute; top: 0; - height: 100%; + bottom: 0; + width: 10px; + background-image: repeating-linear-gradient( + to bottom, + rgba(255,255,255,0.35) 0px, + rgba(255,255,255,0.35) 1px, + transparent 1px, + transparent 5px + ); + pointer-events: none; } -.ui-slider-horizontal .ui-slider-range-min { +#volplace::before { left: 0; } -.ui-slider-horizontal .ui-slider-range-max { +#volplace::after { right: 0; } @@ -1023,7 +1154,7 @@ html .ui-button.ui-state-disabled:active { .ui-widget.ui-widget-content { border: none; - background-color: rgba(204, 204, 204, 0.43137254901960786); + background-color: rgba(204, 204, 204, 0.43); } .ui-widget-content { @@ -1032,21 +1163,35 @@ html .ui-button.ui-state-disabled:active { color: #333333; } -/* The Stage */ +/* ============================================ + Stage Wrapper + ============================================ */ +#stage { + grid-area: stage; + display: grid; + grid-template-rows: 1fr auto; + grid-template-areas: + "visual" + "controls"; +} + +/* ============================================ + The Stage + ============================================ */ #djStage > div:not(#screenBox) { position: relative; z-index: 2; } #djStage { - grid-area: playingstage; + grid-area: visual; display: flex; flex-direction: column; justify-content: flex-end; position: relative; overflow: hidden; - border-top-left-radius: 0.5rem; - border-top-right-radius: 0.5rem; + border-top-left-radius: 20px; + border-top-right-radius: 20px; box-shadow: inset 0 -4rem 4rem -3rem rgba(0, 0, 0, 0.5), inset 0 16rem 32rem -3rem rgba(0, 0, 0, 0.75), inset 0 4rem 2rem -4rem black; } @@ -1075,7 +1220,7 @@ html .ui-button.ui-state-disabled:active { .spot.empty .djplaque { flex-direction: column; - color: rgba(255, 2556, 255, 0.5); + color: rgba(255, 255, 255, 0.5); } .avtr { @@ -1105,38 +1250,62 @@ html .ui-button.ui-state-disabled:active { line-height: 1.5rem; text-align: center; color: #fff; - background-image: linear-gradient(to bottom, rgba(0, 0, 0, 0.7), rgba(0, 0, 0, 0.6)); - border-top-left-radius: 0.5rem; - border-top-right-radius: 0.5rem; + background-image: linear-gradient(to bottom, var(--color-bg-t2), rgb(21,21,21)); + border-top: 1px solid rgba(255,255,255,0.25); + border-top-left-radius: var(--radius-md); + border-top-right-radius: var(--radius-md); } .djActive { - border-top: 1px solid rgba(0, 0, 0, 0.2); - background-color: var(--color-bg-s1) !important; - background-image: none; + background-image: linear-gradient(to top, var(--color-bg-t2), var(--color-accent)); } .djname { + flex: 1 1 auto; + min-width: 0; text-overflow: ellipsis; overflow: hidden; white-space: nowrap; - padding-right: 5px; } .playcount { + position: absolute; + bottom: calc(100% + 0.4rem); + left: 50%; + transform: translateX(-50%); + padding: 0.2rem 0.5rem; + font-size: 0.7rem; white-space: nowrap; + color: var(--color-text-light); + background: var(--color-bg-t2); + border-radius: var(--radius-sm); + pointer-events: none; + opacity: 0; + transition: opacity 150ms ease; + z-index: 10; +} + +.djplaque:hover .playcount { + opacity: 1; +} + +.addmeButt { + display: inline-block; + background-color: transparent; + border: none; } #prgbar { - position: fixed; - bottom: 0; + position: absolute; + top: 0; left: 0; right: 0; - height: 1px; + height: 2px; + z-index: 1; } #prgbarbar { position: absolute; left: 0; - height: 1px; + height: 2px; } #screenBox { position: absolute; @@ -1165,12 +1334,15 @@ html .ui-button.ui-state-disabled:active { background: linear-gradient(rgba(0, 0, 0, 0.5), rgba(255, 255, 255, 0.15)); } -/* Queues / Playlists */ +/* ============================================ + View Panel (Playlists / History / Cards) + ============================================ */ #queuebox { - grid-area: queues; - display: flex; + grid-area: content; + display: none; flex-direction: column; position: relative; + padding-top: var(--pad4); overflow: hidden; } @@ -1178,23 +1350,23 @@ html .ui-button.ui-state-disabled:active { background-color: var(--color-bg-t1); border-top: 1px solid rgba(255, 255, 255, 0.1); border-bottom: 1px solid rgba(255, 255, 255, 0.1); - border-bottom-left-radius: 0.5rem; - border-bottom-right-radius: 0.5rem; + border-radius: 20px 20px var(--radius-md) var(--radius-md); } -input#queueFilter { +#queueFilter { width: 100%; border-bottom: 0; border-top-left-radius: 0; border-top-right-radius: 0; } -div#filterMachine { +#filterMachine { padding: 0; } #mainqueue { - padding: var(--pad1) var(--pad2); + padding: var(--pad1) 0.75rem; + padding-right: 0; } #mainqueue.emptyList:before, #mainqueue.loading:before, @@ -1222,16 +1394,12 @@ div#filterMachine { #queuelist .pvbar { margin: var(--pad1) 0; - padding: var(--pad2) var(--pad2); + padding: var(--pad2); background-color: var(--color-bg-t1); border-top: 1px solid var(--color-bg-t2); border-bottom: 1px solid var(--color-bg); - border-radius: 0.5rem; - cursor: move; - /* fallback if grab cursor is unsupported */ + border-radius: var(--radius-md); cursor: grab; - cursor: -moz-grab; - cursor: -webkit-grab; } #queuelist .pvbarWrap { @@ -1255,7 +1423,7 @@ div#filterMachine { flex: 1; } -select#listpicker { +#listpicker { width: 100%; padding-left: var(--pad2); } @@ -1264,7 +1432,7 @@ select#listpicker { display: flex; } -button#cancelqsearch { +#cancelqsearch { display: none; margin-left: var(--pad2); } @@ -1286,13 +1454,14 @@ button#cancelqsearch { text-align: center; } -input#qsearch { +#qsearch { margin: var(--pad2); } #queuelist { flex: 1; height: 100%; + background-color: var(--color-bg-s1); overflow: auto; } @@ -1300,8 +1469,8 @@ input#qsearch { flex: 1; background-color: var(--color-bg-t2); } - #mergeContain { + overflow: auto; display: none; position: absolute; z-index: 15; @@ -1324,7 +1493,7 @@ input#qsearch { #mergeBox { background-color: var(--color-bg-t2); padding: 15px; - border-radius: 5px; + border-radius: var(--radius-md); } #mergeSetup { @@ -1337,18 +1506,18 @@ input#qsearch { display: none; } -select#mergepicker, +#mergepicker, #mergepicker2 { width: calc(50% - 50px); font-size: 14px; border: none; - font-family: "Open Sans", helvetica, arial, sans-serif; + font-family: var(--font-family); padding-left: 5px; - color: #eee; + color: var(--color-text-light); white-space: nowrap; } -#mergeBox i.material-icons { +#mergeBox .material-icons { font-size: 17px !important; font-weight: 700; cursor: default; @@ -1372,7 +1541,7 @@ select#mergepicker, } a.importLinkCheck { - color: #eee; + color: var(--color-text-light); line-height: 0; } @@ -1411,31 +1580,65 @@ a.importLinkCheck { font-size: 1.5rem; } -/* Theme */ +/* ============================================ + Theme Bar + ============================================ */ #themebox { - grid-area: theme; + grid-area: controls; z-index: 15; position: relative; display: flex; align-items: center; max-width: 100vw; - padding: var(--pad2); + padding: var(--pad2) var(--pad3); font-size: 1rem; text-align: center; color: #fff; background-color: var(--color-bg-s1); + border-bottom-left-radius: 20px; + border-bottom-right-radius: 20px; +} + +.themeLeft, +.themeRight { + display: flex; + align-items: center; + flex-shrink: 0; + gap: var(--pad2); +} + +.themeCenter { + flex: 1; + min-width: 0; + overflow: hidden; +} + +@keyframes theme-ticker { + from { transform: translateX(0); } + to { transform: translateX(-50%); } } #currentTheme { - text-overflow: ellipsis; white-space: nowrap; overflow: hidden; - width: 100%; } -/* Chat */ +#currentTheme.is-ticker .ticker-run { + display: inline-block; + white-space: nowrap; + animation: theme-ticker 14s linear infinite; +} + +.ticker-sep { + padding: 0 0.5em; + opacity: 0.4; +} + +/* ============================================ + Chat + ============================================ */ #actualChat { - grid-area: chat; + grid-area: content; display: flex; flex-direction: column; overflow: hidden; @@ -1444,9 +1647,15 @@ a.importLinkCheck { #chatsWrap { flex: 1; height: 100%; + padding-inline: var(--pad2); overflow: auto; } +#chats { + margin: 0 auto; + max-width: 54ch; +} + #newchat { width: 100%; } @@ -1458,17 +1667,18 @@ a.importLinkCheck { .newChat { position: relative; - display: flex; - justify-content: space-between; + display: grid; + grid-template-columns: auto 1fr auto; + grid-template-rows: auto auto; + gap: var(--pad2); margin: var(--pad3); - color: #bbb; + color: var(--color-text); word-break: break-word; } .newChat .botson { position: relative; z-index: 2; - margin: 0.1rem var(--pad3) 0 0; width: 2rem; height: 2rem; cursor: pointer; @@ -1494,23 +1704,39 @@ a.importLinkCheck { z-index: 2; width: 100%; padding: var(--pad1); - color: #888; + color: var(--color-text-muted); font-size: 0.75rem; text-align: center; background-color: var(--color-bg-t1); border-radius: var(--pad1); } +.npmsg { + grid-column: 1 / -1; +} + +.npmsg-fires { + position: absolute; + z-index: 2; + right: 0; + top: 0; + font-size: 10px; + line-height: 0; + pointer-events: none; +} + .chatContent { - flex: 1; - padding: 0 var(--pad3) 0 0; + grid-column: 2 / -1; + grid-row: 1 / -1; + display: grid; + grid-template-columns: subgrid; } .chatContent .utitle { position: absolute; z-index: 2; left: -1.5rem; - color: #666; + color: var(--color-text-dim); } .chatHead { @@ -1519,10 +1745,18 @@ a.importLinkCheck { align-items: center; } +.chatHead::after { + content: ""; + flex: 1; + height: 1px; + background: rgba(255, 255, 255, 0.1); + margin-left: var(--pad1); +} + .chatName { margin-right: var(--pad1); font-size: 0.75rem; - font-weight: 700; + font-weight: 500; letter-spacing: 0.05em; color: rgba(255, 255, 255, 0.4); cursor: pointer; @@ -1533,9 +1767,8 @@ a.importLinkCheck { right: 0; top: 0; font-size: 1rem; - padding-left: 0.25rem; + padding: 0 0.25rem; font-weight: 400; - padding-right: 0.25rem; background-color: #333; cursor: pointer; display: none; @@ -1549,7 +1782,8 @@ a.importLinkCheck { .chatText { position: relative; - font-size: 1rem; + font-size: 0.875rem; + grid-column: 1 / -1; } .chatText.deleteMe:hover { @@ -1583,7 +1817,7 @@ a.importLinkCheck { font-size: 1.25rem; color: white; background-color: rgba(255, 0, 0, 0.5); - border-radius: 999px; + border-radius: var(--radius-pill); } .chatText.hideImg .inlineImage { @@ -1633,16 +1867,18 @@ a.importLinkCheck { line-height: 3.2; color: #777; background-color: #444; - border-top-right-radius: 999px; - border-bottom-right-radius: 999px; + border-top-right-radius: var(--radius-pill); + border-bottom-right-radius: var(--radius-pill); } .chatTime { + grid-column: 3 / -1; + grid-row: 1 / 2; position: relative; z-index: 2; - width: 3rem; - font-weight: 400; font-size: 0.66rem; + font-weight: 400; + line-height: 1.8; color: rgba(255, 255, 255, 0.47); text-align: right; white-space: nowrap; @@ -1702,7 +1938,7 @@ a.importLinkCheck { padding: var(--pad3); background-color: var(--color-bg-t2); box-shadow: 0 0.25rem 1rem black; - border-radius: 0.5rem; + border-radius: var(--radius-md); } #pickerNav { @@ -1711,7 +1947,7 @@ a.importLinkCheck { text-align: center; } -input#pickerSearch { +#pickerSearch { width: 100%; margin: var(--pad2) 0; } @@ -1729,7 +1965,8 @@ input#pickerSearch { opacity: 1; } -img.emoji { +img.emoji, +.rohnmoji { height: 1.25em; width: 1.25em; margin: 0 0.05em 0 0.1em; @@ -1737,10 +1974,6 @@ img.emoji { } .rohnmoji { - height: 1.25em; - width: 1.25em; - margin: 0 0.05em 0 0.1em; - vertical-align: -0.1em; background-image: url(../img/rohn.png); display: inline-block; background-size: cover; @@ -1804,31 +2037,66 @@ img.emoji { margin: 0.25rem; padding: 0.25em 0.5em; font-size: 0.75rem; - color: #888; + color: var(--color-text-muted); } -/* History */ +/* ============================================ + History + ============================================ */ #thehistoryWrap { - position: absolute; - z-index: 15; - bottom: 0; - left: 0; - width: 100%; - height: 100%; - background-image: linear-gradient(var(--color-bg-t1), var(--color-bg)); + grid-area: content; + display: none; + flex-direction: column; + overflow: hidden; +} + +/* ============================================ + Card Case + ============================================ */ +#cardsWrap { + grid-area: content; + display: none; overflow: auto; + padding: 1rem; +} + +#histFilterBar { + flex-shrink: 0; + padding: var(--pad3) var(--pad3) 0 var(--pad3); +} + +#histFilter { + width: 100%; + height: auto; + padding: var(--pad2) var(--pad3); + color: var(--color-text); + font-size: 0.875rem; + background: var(--color-bg-s2); + border: 1px solid var(--color-border); + border-radius: var(--radius-md); + outline: none; + box-sizing: border-box; +} + +#histFilter:focus { + border-color: var(--color-accent); } #thehistory { display: flex; flex-direction: column; - gap: 1rem; - padding: 1rem; + flex: 1; + overflow-y: auto; + padding-top: 0; + + .pvbarWrap { + padding: var(--pad2) var(--pad3); + } } .histmoreinfo { font-size: 0.8rem; - color: #888; + color: var(--color-text-muted); } .histart { @@ -1843,21 +2111,27 @@ img.emoji { } .histlink { - color: #bbb; + color: var(--color-text); + + &:hover { + background-color: var(--color-bg-t1); + } } .qtxt { flex: 1; } -/* Login */ +/* ============================================ + Login + ============================================ */ #welcomeInfo { padding-bottom: var(--pad4); } #login { grid-area: login; - padding: var(--pad5); + padding: var(--pad4); overflow: auto; max-width: 40rem; height: 100%; @@ -1894,6 +2168,11 @@ img.emoji { #login .formlinks a { margin-right: 15px; cursor: pointer; + color: var(--color-accent); +} + +#login a { + color: var(--color-accent); } #login .formlinks a:last-child { @@ -1904,13 +2183,15 @@ img.emoji { display: none; } -/* Modals */ +/* ============================================ + Modals + ============================================ */ #overlay { position: fixed; top: 0; left: 0; z-index: 100; - display: flex; + display: none; align-items: center; justify-content: center; width: 100%; @@ -1941,7 +2222,7 @@ img.emoji { align-items: center; justify-content: space-between; padding: var(--pad4); - color: #eee; + color: var(--color-text-light); background-color: var(--color-bg-t1); } @@ -1950,7 +2231,7 @@ img.emoji { } .closeModal { - margin-left: var(--pad3); + margin-right: var(--pad1); } .modalContent { @@ -1960,14 +2241,43 @@ img.emoji { overflow: auto; } -#settingsBox .modalContent, +#accountSettingsBox { + width: 420px; + max-width: 100vw; + min-height: 440px; + max-height: 100vh; +} + #accountSettingsBox .modalContent { - min-width: 320px; + flex-direction: column; +} + +#accountSettingsTabs { + border-radius: 0; + border-bottom: 1px solid var(--color-bg-t1); + background-color: var(--color-bg-t1); +} + +#accountSettingsTabs .tab { + border-radius: 0; + flex: none; + padding: var(--pad2) var(--pad4); +} + +#accountSettingsClose { + margin-left: auto; + border-radius: 0; +} + +.tabPanel { + display: none; + flex-direction: column; + flex: 1; + width: 100%; } -#cardsBox .modalContent { - width: 80vw; - height: 80vh; +.tabPanel.active { + display: flex; } .settingline { @@ -1977,41 +2287,40 @@ img.emoji { #cardsMain { display: grid; grid-gap: 10px; - grid-template-columns: repeat(auto-fill, minmax(225px, 1fr)); -} - -.caseCard { - margin: var(--pad2); + grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); } .caseCardSpot { position: relative; + display: flex; + align-items: center; + justify-content: center; + width: 100%; } .caseCardSpot canvas { + display: block; + width: 100%; + height: auto; border: 1px solid #333; } -span.cardShareChat { +span.cardShareChat, +span.cardGiftChat { position: absolute; - right: 85px; - bottom: 160px; font-size: 14px; background-color: #000; padding: 5px; - border-radius: 5px; + border-radius: var(--radius-md); display: none; } +span.cardShareChat { + top: 33%; +} + span.cardGiftChat { - position: absolute; - right: 100px; - bottom: 200px; - font-size: 14px; - background-color: #000; - padding: 5px; - border-radius: 5px; - display: none; + bottom: 33%; } span.cardGiftChat:hover, @@ -2038,7 +2347,7 @@ input.tagMachine, } #songlink { - border-radius: 0.25rem; + border-radius: var(--radius-sm); } #songlink svg { @@ -2072,14 +2381,14 @@ input.tagMachine, opacity: 0.4; } -input#plMachine, -input#plMachineById { +#plMachine, +#plMachineById { line-height: 30px; width: 100%; padding: 5px; } -input#plMachineById { +#plMachineById { padding-right: 40px; } @@ -2088,7 +2397,7 @@ input#plMachineById { margin: 10px; padding: 5px; color: #000; - border-radius: 0.25rem; + border-radius: var(--radius-sm); } .responseBox:empty { @@ -2100,7 +2409,9 @@ input#plMachineById { margin-left: 1em; } -/* Unauthenticated state */ +/* ============================================ + Unauthenticated / Disconnected State + ============================================ */ #reconnecting { display: none; padding: var(--pad1) var(--pad2); @@ -2123,100 +2434,111 @@ body.disconnected #logOutButton { display: none; } -/* About page */ +/* ============================================ + About Page + ============================================ */ body.blog { overflow: auto; } -div#blog { +#blog { flex: 1; padding: var(--pad5); background-color: var(--color-bg-t2); } -/* Initially hidden stuff */ +/* ============================================ + Initially Hidden Elements + ============================================ */ #ftSuperCopButton, +#modTab, #audilert, #sc-widget, .notice, -#emojiPicker, -#overlay { +#emojiPicker { display: none; } -/*------------------------------------ Media Queries -*/ +/* ============================================ + Media Queries + ============================================ */ + +/* ── 640px: 2-column layout, header gains view nav buttons ── */ @media only screen and (min-width: 640px) { + /* Promote content panels to their semantic grid areas */ + #usersbox { grid-area: people; } + #actualChat { grid-area: chat; } + #queuebox, + #thehistoryWrap, + #cardsWrap { grid-area: view; } + + /* Header view buttons are now visible */ + #playlists, + #history, + #cardcase { display: block; } + + /* Mini-nav view tabs are redundant at this size */ + #mm-playlists, + #mm-history, + #mm-cards { display: none; } + + /* Grid templates */ #mainGrid, #mainGrid.mmqueue, #mainGrid.mmchat { - grid-template-rows: auto auto auto auto 1fr; - grid-template-columns: minmax(28rem, 1fr) minmax(18rem, 24rem); + grid-template-rows: auto auto 1fr; + grid-template-columns: 3fr 2fr; grid-template-areas: - "head mmopts" - "player chat" - "playingstage chat" - "theme chat" - "queues chat"; + "head nav" + "stage chat" + "view chat"; } #mainGrid.mmusrs { grid-template-areas: - "head mmopts" - "player users" - "playingstage users" - "theme users" - "queues users"; + "head nav" + "stage people" + "view people"; } #mainGrid.login { + grid-template-columns: 1fr; + grid-template-rows: auto auto 1fr; grid-template-areas: - "head queues" - "player queues" - "playingstage queues" - "theme queues" - "login queues"; - } - - #mainGrid.login #queuebox, - #mainGrid.login #thehistoryWrap, - #mainGrid.mmchat #actualChat, - #mainGrid.mmqueue #actualChat, - #mainGrid.mmusrs #usersbox, - #mainGrid.mmchat #queuebox, - #mainGrid.mmusrs #queuebox { - display: flex; + "head" + "stage" + "login"; } - #mainGrid.login .histeal, - #mmqueue { - display: none; - } + /* At 640px+: active view panel shows via view-class regardless of layout mode */ + #mainGrid.view-playlists #queuebox { display: flex; } + #mainGrid.view-history #thehistoryWrap { display: flex; } + #mainGrid.view-cards #cardsWrap { display: block; } - #thehistoryWrap { - grid-area: queues; - display: block; - position: relative; - top: auto !important; - bottom: auto; - } + /* Re-show secondary panels suppressed by mobile rules */ + #mainGrid.mmqueue #actualChat, + #mainGrid.mmchat #actualChat { display: flex; } - #mainGrid.mmqueue #thehistoryWrap { - grid-area: queues; - } + #mainGrid.mmusrs #usersbox { display: flex; } - #queueControls, - #volandthings { - padding: var(--pad2) var(--pad4); - } + .newChat { margin-left: var(--pad2); } } +/* ── max 799px: touch-only users list ── */ @media only screen and (max-width: 799px) { #allUsersWrap .prson { cursor: none; pointer-events: none; } + + .header_icon[title]::after { + left: 50%; + top: calc(100% + 0.5rem); + transform: translateX(-50%); + } } +/* ── 800px: 3-column layout, header becomes vertical sidebar ── */ @media only screen and (min-width: 800px) { body.screen #albumArt { display: none; @@ -2227,30 +2549,36 @@ div#blog { } #mainGrid, + #mainGrid.mmqueue, #mainGrid.mmchat { - grid-template-rows: auto auto auto 1fr; + grid-template-rows: auto auto 1fr; grid-template-columns: auto minmax(28rem, 1fr) minmax(18rem, 24rem); grid-template-areas: - "head player mmopts" - "head playingstage chat" - "head theme chat" - "head queues chat"; + "head . nav" + "head stage chat" + "head view chat"; } #mainGrid.mmusrs { grid-template-areas: - "head player mmopts" - "head playingstage users" - "head theme users" - "head queues users"; + "head . nav" + "head stage people" + "head view people"; } #mainGrid.login { + grid-template-columns: 1fr; + grid-template-rows: auto auto 1fr; grid-template-areas: - "head player queues" - "head playingstage queues" - "head theme queues" - "head login queues"; + "head" + "stage" + "login"; + } + + #mainGrid.login #topbar { + flex-direction: row; + align-items: center; + padding: var(--pad1) var(--pad3); } #importPromptBox .modalContent { @@ -2264,6 +2592,11 @@ div#blog { padding-bottom: var(--pad4); } + #viewnav { + flex-direction: column; + margin-left: 0; + } + .header_icon { margin-bottom: 0.5rem; } @@ -2286,10 +2619,6 @@ div#blog { display: block; } */ - #volandthings { - padding: var(--pad4) var(--pad4) var(--pad2) var(--pad4); - } - #actualChat { min-width: 20rem; max-width: 28rem; @@ -2299,55 +2628,72 @@ div#blog { margin-left: 2.5vw; margin-right: 2.5vw; } -} -@media only screen and (min-width: 1024px) { - body { - background: linear-gradient(var(--color-bg-t1), var(--color-bg)); + .chatText { + grid-column: 1 / 2; } +} +/* ── 1024px: 4-column layout, all panels always visible ── */ +@media only screen and (min-width: 1024px) { #mainGrid, #mainGrid.mmqueue, #mainGrid.mmchat, #mainGrid.mmusrs { - grid-template-rows: auto auto auto 1fr; - grid-template-columns: auto 9fr min-content 12fr; + grid-template-rows: auto 1fr; + grid-template-columns: min-content minmax(min-content, auto) min-content minmax(min-content, auto); grid-template-areas: - "head users player chat" - "head users playingstage chat" - "head users theme chat" - "head users queues chat"; + "head people stage chat" + "head people view chat"; } - #mainGrid.mmusrs #queuebox, - #mainGrid.mmusrs #actualChat, - #mainGrid.mmchat #usersbox, - #mainGrid.mmchat #queuebox, + /* At 1024px: chat/people panels always visible regardless of nav class. + The active view panel is still governed by view-class (inherited from 640px rules). */ + #mainGrid.mmusrs #actualChat, + #mainGrid.mmchat #usersbox, #mainGrid.mmqueue #usersbox, #mainGrid.mmqueue #actualChat { display: flex; } #mainGrid.login { + grid-template-columns: 1fr; + grid-template-rows: auto auto 1fr; grid-template-areas: - "head player player queues" - "head playingstage playingstage queues" - "head theme theme queues" - "head login login queues"; + "head" + "stage" + "login"; + } + + #mainGrid.login #topbar { + flex-direction: row; + } + + #mainGrid.login #stage { + width: 100%; + max-width: 36rem; + justify-self: center; } #mainGrid #minimodeoptions { display: none; } - #djStage, - #themebox, - #queuebox { + /* Stage wrapper + view panels: right-column sizing */ + #stage, + #queuebox, + #thehistoryWrap, + #cardsWrap { width: 46vw; max-width: 56rem; justify-self: flex-end; } + #nowplaying, + #djStage { + margin-top: 20px; + } + #djStage { height: 30vh; min-height: 15rem; @@ -2366,35 +2712,32 @@ div#blog { justify-content: space-between; } - #thehistoryWrap { - grid-area: queues !important; - } - #allUsersWrap { align-self: flex-end; + transform: translateX(1vw); } #allUsers { + grid-template-columns: 1fr auto auto; grid-auto-flow: dense; padding-top: var(--pad5); } #allUsers .botson { grid-column: -2 / -1; } + #allUsers .prsnRole { + grid-column: -3 / -2; + } #allUsers .djOrder { grid-column: 1 / 2; } #allUsers .prsnNameRole { - grid-column: 2 / 3; + grid-column: 1 / 2; justify-content: flex-end; } #allUsers .prsnName { order: 2; } - #allUsers .prsnRole { - margin-left: 0; - margin-right: 0.5rem; - } .newChat { margin-left: var(--pad4); diff --git a/index.html b/index.html index 080699f..b80d6a2 100644 --- a/index.html +++ b/index.html @@ -1,660 +1,651 @@ - - - firetable - - - - - - - - - - -
    -
    - -
    - - - - - -
    - - - - -
    -
    -
    -
    Reconnecting...
    -
    + + + firetable + + + + + + + + + + -
    -
    -
    - - -
    -
    -
    -
    -
    - - - link - -
    -
    - -
    + +
    +
    + +
    + + + + + + +
    + +
    +
    +
    +
    Reconnecting...
    +
    -
    -
    -
    -
    -
    -
      -
    • -
    • -
    • -
    • -
    • -
    • -
    • -
    • -
    • -
    • -
    • -
    • -
    • -
    • -
    • -
    • -
    • -
    • -
    • -
    • -
    • -
    • -
    • -
    • -
    • -
    • -
    • -
    • -
    • -
    • -
    • -
    • -
    • -
    • -
    • -
    • -
    • -
    • -
    • -
    • -
    • -
    • -
    -
    -
    -
    +
    +
    +
    +
    +
    +
    +
      +
    • +
    • +
    • +
    • +
    • +
    • +
    • +
    • +
    • +
    • +
    • +
    • +
    • +
    • +
    • +
    • +
    • +
    • +
    • +
    • +
    • +
    • +
    • +
    • +
    • +
    • +
    • +
    • +
    • +
    • +
    • +
    • +
    • +
    • +
    • +
    • +
    • +
    • +
    • +
    • +
    • +
    • +
    +
    +
    +
    -
    -
    -
    Loading firetable...
    -
    -
    -
    -
    -
    -
    -
    -
    +
    +
    +
    Loading firetable...
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    -
    -
    -
    +
    +
    +
    + + + link + + +
    +
    +
    +
    +
    + + + +
    +
    +
    -
    - - - -
    +
    -
    -
    -
    -
    - -
    -
    -
    - -
    -
    -
    -
    -
    -
    -
    -
    -
    - -
    - -
    -
    -
    - -
    -
    -
    -
    -
    - -
    -
    -
    -
    +
    + + + + + +
    -
    -
    -

    People

    -
    - room -
    -
    - radio -
    -
    -
    -
    -
    - -
    - block - radio - lens -
    -
    - - -
    - -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    +
    +
    +
    +
    + +
    +
    +
    + +
    +
    +
    +
    +
    +
    +
    +
    +
    + +
    + +
    +
    +
    + +
    +
    +
    +
    +
    + +
    +
    +
    +
    -
    -
    -
    -
    - -
    -
    - - - -
    - -
    -
    -
    -
    -
    - - arrow_right_alt - - -
    -
    Merge in progress... Please stand by...
    -
    - Congratulations! Merge completed. - -
    -
    -
    -
    -
    - -
    -
    -
    -
    -
    -
    -
    - -
    - - -
    - vertical_align_top - vertical_align_bottom - edit - close - delete -
    -
    -
    - - - Edit the song tag + hit enter to save
    -
    - Rohn Standard Notation
    - Standard: Artist - Song Title
    - Remix: Artist - Song Title (Remixartist Remix)
    - Featuring: Artist - Song Title feat. Subartist
    - Featuring + Remix: Artist - Song Title feat. Subartist (Remixartist Remix) -
    -
    -
    -
    - -
    or...
    - - -
    -
    - -
    - YoutubeSoundcloud -
    -
    -
    - -
    - -
    - -
    -
    -
    -
    -
    -
    +
    +
    +

    People

    +
    + room +
    +
    + radio +
    +
    +
    +
    +
    + +
    + block + radio + lens +
    +
    + + +
    + +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    -
    -
    -
    -
    -
    - -
    -
    -
    -
    - played by on at - -
    -
    -
    -
    -
    -
    -
    +
    +
    +
    +
    + +
    +
    + + + +
    + +
    +
    +
    +
    +
    + + arrow_right_alt + + +
    +
    Merge in progress... Please stand by...
    +
    + Congratulations! Merge completed. + +
    +
    +
    +
    +
    + +
    +
    +
    +
    +
    +
    +
    + +
    + + +
    + vertical_align_top + vertical_align_bottom + edit + close + playlist_add + delete +
    +
    +
    + + + Edit the song tag + hit enter to save
    +
    + Rohn Standard Notation
    + Standard: Artist - Song Title
    + Remix: Artist - Song Title (Remixartist Remix)
    + Featuring: Artist - Song Title feat. Subartist
    + Featuring + Remix: Artist - Song Title feat. Subartist (Remixartist Remix) +
    +
    +
    +
    + +
    or...
    + + +
    +
    + +
    + YoutubeSoundcloud +
    +
    +
    + +
    + +
    + +
    +
    +
    +
    +
    +
    -
    -
    -

    Welcome To Firetable!

    -

    - Want to jump on the DJ table and pick some h0t tunes to play? Just want to join the - conversation? -

    -
    - -
    -

    Create an Account

    -
    Email:
    -
    Username:
    -
    Password:
    -
    Repeat Password:
    -
    - I have read, understand, and agree to the - Terms of Service and - Privacy Policy. -
    -
    - -
    -
    - - -
    -
    +
    +
    loading your cards...
    +
    -
    -
    -
    +
    -
    -
    -
    -

    Playlist Deletion Zone

    - -
    -
    - So you want to delete a playlist huh? This action is irreversible.
    - If you're super sure you want to do this, pick a playlist to delete below.

    -
    - - -
    -
    -
    +
    +
    +

    Welcome To Firetable!

    +

    + Want to jump on the DJ table and pick some h0t tunes to play? Just want to join the + conversation? +

    +
    + +
    +
    +

    Create an Account

    +
    Email:
    +
    Username:
    +
    Password:
    +
    Repeat Password:
    +
    + I have read, understand, and + agree to the + Terms of Service and + Privacy Policy. +
    +
    + +
    +
    +
    + + +
    +
    -
    -
    -

    Your Card Case

    - -
    -
    -
    loading your cards...
    -
    -
    +
    -
    -
    -

    Account

    - - -
    -
    -
    - Change your username, , to some other thing...

    - -
    -
    -
    +
    +
    +

    Playlist Deletion Zone

    + +
    +
    + So you want to delete a playlist huh? This action is irreversible.
    If you're + super sure you want to do this, pick a playlist to delete below. +

    +
    + + +
    +
    +
    -
    -
    -

    Supermod Control Panel

    - -
    -
    - Suspend a user's account, or manage active suspentions.

    - -
    -

    Active Suspentions

    -
    -
    -
    +
    +
    + + + + + +
    +
    +
    +
    + Change your username to some other thing...

    + +
    +
    + Avatar:

    + +

    +
    +
    + Set all of the things here on the table. +

    +
    + + +
    +
    + + +
    +
    + +
    +
    +
    + + +
    +
    + + +
    +
    -
    -
    -

    Playlist Import Machine

    - -
    -
    -
    - -
    -
    - -
    -
    -
    -
    - Select an "interactive webpage" .html file from - Dubtrack. -
    - -
    -
    -
    -
    -
    -
    + +
    +
    +
    +
    + Suspend a user's account, or manage active suspentions.

    + +
    +

    Active Suspentions

    +
    +
    +
    +
    -
    -
    -
    - -
    -
    +
    +
    +

    Playlist Import Machine

    + +
    +
    +
    + +
    +
    + +
    +
    +
    +
    + Select an "interactive webpage" .html file from Dubtrack. +
    + +
    +
    +
    +
    +
    - +
    - +
    +
    +
    + +
    +
    - - - - - - - - - - - - - - - - - - + -
    -

    Your account has been suspended due to a perceived violation of our Terms of Service.

    -

    - We reserve the right to modify, suspend, or terminate the Service for any reason, without notice, at any - time. -

    -
    - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    +

    Your account has been suspended due to a perceived violation of our Terms of Service.

    +

    We reserve the right to modify, suspend, or terminate the Service for any reason, without notice, at any time. +

    +
    + +
    +

    Your account has been suspended due to a perceived violation of our Terms of Service.

    +

    + We reserve the right to modify, suspend, or terminate the Service for any reason, without notice, at any + time. +

    +
    + + \ No newline at end of file diff --git a/js/cards.js b/js/cards.js new file mode 100644 index 0000000..c2270c8 --- /dev/null +++ b/js/cards.js @@ -0,0 +1,259 @@ +/** + * cards.js — DJ card rendering on HTML5 Canvas. + * + * DJ cards are collectible items showing a song that was played, the DJ who + * played it, album art, and a unique card number. They can be shared in chat + * or gifted to the current DJ. + * + * Special edition cards (id8, id9) have custom artwork for anniversary events. + */ + +firetable.actions = firetable.actions || {}; + +/** + * Open the card case modal and render all cards the user owns. + */ +firetable.actions.cardCase = function () { + $("#cardsMain").html(""); + ftapi.lookup.cardCollection(function (data) { + for (var key in data) { + if (!data.hasOwnProperty(key)) continue; + var childData = data[key]; + firetable.debug && console.log('card:', childData); + $("#cardsMain").append( + '' + + '' + + 'Gift to DJ' + + 'Share In Chat' + + '' + ); + firetable.actions.displayCard(childData, key); + } + }); +}; + +/** + * Share a card in chat (sends a message with the card ID attached). + * @param {string} cardid - Card key + */ +firetable.actions.chatCard = function (cardid) { + ftapi.actions.sendChat("Check out my card...", cardid); +}; + +/** + * Gift a card to the current DJ (removes it from your collection). + * @param {string} cardid - Card key + */ +firetable.actions.giftCard = function (cardid) { + ftapi.actions.sendChat("!giftcard :gift:", cardid); + $("#caseCardSpot" + cardid).remove(); +}; + +/** + * Fetch card data from the server and render it on a canvas. + * Used when a card is shared in chat. + * @param {string} cardid - Card key + * @param {string} chatid - Chat element ID (for the canvas target) + */ +firetable.actions.showCard = function (cardid, chatid) { + ftapi.lookup.card(cardid, function (data) { + firetable.actions.displayCard(data, chatid); + }); +}; + +/** + * Render a DJ card onto a canvas element. + * + * Layout (225×300px): + * - Top bar: DJ name + coloured circle with card number + * - Centre: Avatar image overlaid on gradient + * - Accent stripe: Card metadata (number, temperature) + * - Bottom: Song title, artist, album art thumbnail, date + * + * @param {Object} data - Card data from the server + * @param {string} data.djname - DJ's display name + * @param {string} data.djid - DJ's user ID + * @param {string} data.title - Song title + * @param {string} data.artist - Artist name + * @param {string} data.image - Album art URL + * @param {Object} data.colors - {color, txt} accent colours + * @param {number} data.cardnum - Unique card serial number + * @param {string} data.num - Display number (single digit on circle badge) + * @param {number} data.temp - "Max operating temperature" gag value + * @param {number} data.date - Timestamp when the card was created + * @param {string} [data.set] - Robohash set override + * @param {string} [data.special] - Special edition identifier ("id8", "id9") + * @param {string} chatid - Suffix for the canvas element ID ("cardMaker" + chatid) + */ +firetable.actions.displayCard = function (data, chatid) { + firetable.debug && console.log("display card"); + + // ── Normalize colours ── + var defaultScheme = false; + if (data.colors) { + if (data.colors.color === "#fff" || data.colors.color === "#7f7f7f") { + data.colors.color = firetable.orange; + data.colors.txt = "#000"; + defaultScheme = true; + } + } + + // ── Default album art fallback ── + if (data.image === "img/idlogo.png" && ftconfigs.defaultAlbumArtUrl.length) { + data.image = ftconfigs.defaultAlbumArtUrl; + } + + var set = data.set || "set1"; + var canvas = document.getElementById('cardMaker' + chatid); + if (!canvas || !canvas.getContext) return; + + var ctx = canvas.getContext('2d'); + ctx.clearRect(0, 0, canvas.width, canvas.height); + + // ── Background ── + ctx.fillStyle = "#000"; + ctx.fillRect(0, 0, 225, 300); + + // ── Centre image area with gradient overlay ── + ctx.fillStyle = defaultScheme ? "#fff" : data.colors.color; + ctx.fillRect(1, 30, 223, 175); + + var grd = ctx.createLinearGradient(0, 0, 0, 175); + grd.addColorStop(0, "rgba(0, 0, 0, 0.75)"); + grd.addColorStop(1, "rgba(0, 0, 0, 0.55)"); + ctx.fillStyle = grd; + ctx.fillRect(1, 30, 223, 175); + + // ── Accent stripe ── + ctx.fillStyle = data.colors.color; + ctx.fillRect(1, 205, 223, 10); + + // ── Bottom info area ── + ctx.fillStyle = "#151515"; + ctx.fillRect(1, 216, 223, 75); + + // ── DJ name (top left) ── + ctx.fillStyle = "#eee"; + ctx.font = "700 11px Helvetica, Arial, sans-serif"; + ctx.fillText(data.djname, 10, 20); + + // ── Print date + room name (bottom centre) ── + ctx.font = "400 8px Helvetica, Arial, sans-serif"; + ctx.textAlign = "center"; + ctx.fillText( + "Printed " + firetable.utilities.format_date(data.date) + " | " + ftconfigs.roomNameShort, + 112.5, 299 + ); + + // ── Song title (wraps) ── + ctx.font = "700 10px Helvetica, Arial, sans-serif"; + ctx.textAlign = "left"; + var linez = firetable.utilities.wrapText(ctx, data.title, 66, 240, 160, 15); + + // ── Artist ── + ctx.font = "400 8px Helvetica, Arial, sans-serif"; + ctx.textAlign = "left"; + firetable.utilities.wrapText(ctx, data.artist, 66, 253 + (15 * linez), 160, 15); + + // ── Metadata stripe text ── + ctx.fillStyle = data.colors.txt; + ctx.font = "400 9px Helvetica, Arial, sans-serif"; + ctx.textAlign = "center"; + ctx.fillText("Card No. " + data.cardnum + " | DJ Card | Max Operating Temp " + data.temp + "°", 112.5, 214); + + // ── Number badge circle (top right) ── + ctx.beginPath(); + ctx.arc(205, 15, 12, 0, 2 * Math.PI, false); + ctx.fillStyle = data.colors.color; + ctx.fill(); + + ctx.fillStyle = data.colors.txt; + ctx.font = "700 15px Helvetica, Arial, sans-serif"; + ctx.textAlign = "left"; + ctx.fillText(data.num, 200.5, 20); + + // ── Image Drawing ───────────────────────────────────────────────────── + /** + * Default image loader: draws avatar + album art thumbnail. + */ + var doImages = function () { + var avatarImg = new Image(); + avatarImg.onload = function () { + ctx.drawImage(this, 20, 30, 175, 175); + var albumImg = new Image(); + albumImg.onload = function () { + var height = data.image.match(/ytimg.com/) ? 28 : 50; + ctx.drawImage(this, 10, 230, 50, height); + ctx = null; // release context + }; + albumImg.src = data.image; + }; + avatarImg.src = firetable.utilities.avatarURL(data.djid, data.djname, "175x175"); + }; + + // ── Special Edition Cards ───────────────────────────────────────────── + if (data.special === "id8") { + // 8th anniversary card + ctx.fillStyle = data.colors.color; + ctx.fillRect(1, 30, 223, 10); + ctx.fillStyle = data.colors.txt; + ctx.font = "400 10px Helvetica, Arial, sans-serif"; + ctx.textAlign = "center"; + ctx.fillText("Celebrating 8 Years of Indie Discotheque", 112.5, 38); + + var cake = new Image(); + cake.onload = function () { + ctx.drawImage(this, 10, 50, 35, 35); + var eight = new Image(); + eight.onload = function () { + ctx.drawImage(this, 180, 50, 35, 35); + doImages(); + }; + eight.src = 'img/8.png'; + }; + cake.src = 'img/cake.png'; + + } else if (data.special === "id9") { + // 9th anniversary card — rotated robot avatar + custom background + ctx.fillStyle = data.colors.color; + ctx.fillRect(1, 30, 223, 10); + ctx.fillStyle = data.colors.txt; + ctx.font = "400 10px Helvetica, Arial, sans-serif"; + ctx.textAlign = "center"; + ctx.fillText("Celebrating 9 Years of Indie Discotheque", 112.5, 38); + + var arnold = new Image(); + arnold.onload = function () { + ctx.drawImage(this, 5, 50, 45, 45); + var avatar = new Image(); + avatar.onload = function () { + // Draw rotated avatar + ctx.save(); + ctx.translate(75 * 0.5, 75 * 0.5); + ctx.rotate(0.959931); // ~55 degrees + ctx.translate(-75 * 0.5, -75 * 0.5); + ctx.drawImage(this, 125, -81, 75, 75); + ctx.restore(); + + var bgImg = new Image(); + bgImg.onload = function () { + ctx.drawImage(this, 25, 40, 170, 170); + var albumImg = new Image(); + albumImg.onload = function () { + var height = data.image.match(/ytimg.com/) ? 28 : 50; + ctx.drawImage(this, 10, 230, 50, height); + ctx = null; + }; + albumImg.src = data.image; + }; + bgImg.src = 'img/id9.png'; + }; + avatar.src = firetable.utilities.avatarURL(data.djid, data.djname); + }; + arnold.src = 'img/arnold.png'; + + } else { + // Standard card + doImages(); + } +}; diff --git a/js/chat.js b/js/chat.js new file mode 100644 index 0000000..4e48484 --- /dev/null +++ b/js/chat.js @@ -0,0 +1,439 @@ +/** + * chat.js — Chat message rendering, slash commands, and @-mention keyboard UI. + * + * Handles: + * - Rendering incoming chat messages (newChat event) + * - Grouping consecutive messages from the same user + * - Inline image rendering (when setting is enabled) + * - Text → link conversion + * - Emoji shortname conversion + twemoji parsing + * - Mod delete button on messages + * - Slash commands (/mod, /block, /shrug, /tableflip, etc.) + * - @-mention autocomplete keyboard navigation + * - Chat removal (chatRemoved event) + */ + +firetable.actions = firetable.actions || {}; + +/** + * Display a local-only response in chat (not sent to server). + * Used for command feedback like block/unblock confirmations. + * @param {string} txt - Message text + */ +firetable.actions.localChatResponse = function (txt) { + if (txt.length) { + $("#chats").append('
    ' + txt + '
    '); + firetable.utilities.scrollToBottom(); + } +}; + +// ─── Text Processing Helpers ───────────────────────────────────────────────── + +firetable.ui = firetable.ui || {}; + +/** + * Convert URLs in text to clickable links. + * When showImages is enabled and themeBox is false, image URLs are excluded + * (they get handled separately by showImages()). + * @param {string} text - Raw text + * @param {boolean} [themeBox=false] - True when processing theme text (always linkify all) + * @returns {string} Text with URLs wrapped in anchor tags + */ +firetable.ui.textToLinks = function (text, themeBox) { + var re = /(\b(https?|ftp|file):\/\/[-A-Z0-9+&@#\/%?=~_|!:,.;]*[-A-Z0-9+&@#\/%=~_|])/ig; + if (firetable.showImages && !themeBox) { + // Exclude image URLs — those are rendered inline by showImages() + re = /(https?:\/\/(?![/|.|\w|\s|-]*(?:jpe?g|png|gif))[^" ]+)/g; + } + return text.replace(re, '$1'); +}; + +/** + * Find image URLs in chat text and replace them with inline tags. + * Auto-scrolls chat if user was already at the bottom when the image loads. + * @param {string} chatTxt - Chat message text + * @returns {string} Text with image URLs replaced by inline images + */ +firetable.ui.showImages = function (chatTxt) { + if (!firetable.showImages) return chatTxt; + + var imageUrlRegex = /((http(s?):)([/|.|\w|\s|-])*\.(?:jpe?g|gif|png))/g; + if (chatTxt.search(imageUrlRegex) >= 0) { + chatTxt = chatTxt.replace(imageUrlRegex, function (imageUrl) { + // Pre-load image to auto-scroll after it renders + var chatImage = new Image(); + chatImage.onload = function () { + if (firetable.utilities.isChatPrettyMuchAtBottom()) { + firetable.utilities.scrollToBottom(); + } + }; + chatImage.src = imageUrl; + return '' + + '' + + '×'; + }); + } + return chatTxt; +}; + +/** + * Strip HTML from a string using DOMParser. + * @param {string} html - Raw HTML string + * @returns {string} Plain text content + */ +firetable.ui.strip = function (html) { + var doc = firetable.parser.parseFromString(html, 'text/html'); + return doc.body.textContent || ""; +}; + +/** + * Process raw chat text through the full formatting pipeline: + * strip HTML → inline images → linkify → emoji → backtick code + * @param {string} rawTxt - Unprocessed chat text + * @returns {string} Formatted HTML string safe for insertion + */ +firetable.ui.formatChatText = function (rawTxt) { + var txt = firetable.ui.strip(rawTxt); + txt = firetable.ui.showImages(txt); + txt = firetable.ui.textToLinks(txt); + txt = firetable.utilities.emojiShortnamestoUnicode(txt); + // Backtick → blocks + txt = txt.replace(/\`(.*?)\`/g, function (x) { + return "" + x.replace(/\`/g, "") + ""; + }); + return txt; +}; + +// ─── Chat Event Binding ────────────────────────────────────────────────────── + +/** + * Set up all chat-related ftapi event listeners and keyboard handlers. + * Called once from firetable.ui.init(). + */ +firetable.ui.setupChatEvents = function () { + var $chatTemplate = $('#chatKEY').remove(); + + // ── Incoming Chat Messages ── + ftapi.events.on("newChat", function (chatData) { + if (chatData.botCmd) return; + var namebo = chatData.id; + var utitle = ""; + var atBottom = firetable.utilities.isChatPrettyMuchAtBottom(); + + // Resolve current user's display name for @-mention detection + var you = ftapi.uid; + if (ftapi.users[ftapi.uid] && ftapi.users[ftapi.uid].username) { + you = ftapi.users[ftapi.uid].username; + } + + // Resolve sender's display name and role + if (ftapi.users[chatData.id]) { + if (ftapi.users[chatData.id].username) namebo = ftapi.users[chatData.id].username; + if (ftapi.users[chatData.id].mod) utitle = "mod"; + if (ftapi.users[chatData.id].supermod) utitle = "supermod"; + if (ftapi.users[chatData.id].hostbot) utitle = "robot"; + } else if (chatData.name) { + namebo = chatData.name; + } + + // ── @-mention detection ── + var badoop = false; + if (chatData.txt.match("@" + you, 'i') || chatData.txt.match(/\@everyone/)) { + var timeSinceMessage = Date.now() - chatData.time; + if (timeSinceMessage < 10 * 1000) { + firetable.utilities.playSound("sound"); + if (firetable.desktopNotifyMentions) { + firetable.utilities.desktopNotify(chatData, namebo); + } + badoop = true; + } + } + + // ── Check if we can delete this message (mod powers) ── + var canDelete = function () { + try { + if (!ftapi.users[ftapi.uid].mod && !ftapi.users[ftapi.uid].supermod) return false; + if (ftapi.users[chatData.id]) { + if (ftapi.users[chatData.id].mod || ftapi.users[chatData.id].supermod) return false; + } + return !chatData.hidden; + } catch (e) { + return false; + } + }; + + // Format the message text + var txtOut = firetable.ui.formatChatText(chatData.txt); + if (chatData.hidden) txtOut = "[message removed]"; + + if (chatData.id === firetable.lastChatPerson && !badoop) { + // ── Group with previous message from same user ── + $("#chat" + firetable.lastChatId + " .chatContent").append( + '
    ' + ); + $("#chatTime" + firetable.lastChatId).text(firetable.utilities.format_time(chatData.time)); + $("#chattxt" + chatData.chatID).html(txtOut); + + if (canDelete()) { + $("#chattxt" + chatData.chatID).addClass("deleteMe"); + $("#chattxt" + chatData.chatID).append('
    x
    '); + $("#chattxt" + chatData.chatID).find(".modDelete").on('click', function () { + ftapi.actions.deleteChat(chatData.feedID); + }); + } + twemoji.parse(document.getElementById("chattxt" + chatData.chatID)); + + } else { + // ── New message block (different user or @-mention break) ── + var $chatthing = $chatTemplate.clone(); + $chatthing.attr('id', "chat" + chatData.chatID); + $chatthing.find('.botson').css( + 'background-image', + "url(" + firetable.utilities.avatarURL(chatData.id, namebo) + ")" + ); + $chatthing.find('.utitle').html(utitle); + $chatthing.find('.chatTime') + .attr('id', "chatTime" + chatData.chatID) + .html(firetable.utilities.format_time(chatData.time)); + if (badoop) $chatthing.addClass('badoop'); + + $chatthing.find(".chatText").html(txtOut).attr('id', "chattxt" + chatData.chatID); + $chatthing.find(".chatName").text(namebo); + + // Click-to-@ on avatar and name + firetable.utilities.chatAt($chatthing.find('.botson')); + firetable.utilities.chatAt($chatthing.find('.chatName')); + twemoji.parse($chatthing.find(".chatText")[0]); + $chatthing.appendTo("#chats"); + + if (canDelete()) { + $chatthing.find(".chatText").addClass("deleteMe"); + $chatthing.find(".chatText").append('
    x
    '); + $chatthing.find(".modDelete").on('click', function () { + ftapi.actions.deleteChat(chatData.feedID); + }); + } + + firetable.lastChatPerson = chatData.id; + firetable.lastChatId = chatData.chatID; + } + + // ── Inline card rendering ── + if (chatData.card) { + $("#chattxt" + chatData.chatID).append( + '' + ); + firetable.actions.showCard(chatData.card, chatData.chatID); + } + + // Auto-scroll if user was at bottom or is the sender + if (atBottom || ftapi.uid === chatData.id) { + firetable.utilities.scrollToBottom(); + } + }); + + // ── Chat Removal (mod delete) ── + ftapi.events.on("chatRemoved", function (data) { + $("#chattxt" + data.chatID).text("[message removed]"); + try { + if (ftapi.users[ftapi.uid].mod || ftapi.users[ftapi.uid].supermod) { + $("#chattxt" + data.chatID).removeClass("deleteMe"); + } + } catch (e) {} + }); + + // ── Hide inline image button ── + $(document).on('click', '.hideImage', function (e) { + e.stopPropagation(); + e.preventDefault(); + $(this).closest('.chatText').toggleClass('hideImg'); + }); + + // ── Chat Input: Send Message + Slash Commands ── + $("#newchat").bind("keypress", function (e) { + if (e.key === "Enter") { + var txt = $("#newchat").val(); + if (txt === "") return; + + // Hot/Rain emoji toggle for quick reactions + if (txt === ":fire:" || txt === "🔥") { + $("#cloud_with_rain").removeClass("on"); + $("#fire").addClass("on"); + } else if (txt === ":cloud_with_rain:" || txt === "🌧") { + $("#cloud_with_rain").addClass("on"); + $("#fire").removeClass("on"); + } + + // ── Slash Commands ── + var matches = txt.match(/^(?:[\/])(\w+)\s*(.*)/i); + if (matches) { + var command = matches[1].toLowerCase(); + var args = matches[2]; + + switch (command) { + case "mod": + var personToMod = firetable.actions.uidLookup(args); + if (personToMod) ftapi.actions.modUser(personToMod); + break; + case "unmod": + var personToUnmod = firetable.actions.uidLookup(args); + if (personToUnmod) ftapi.actions.unmodUser(personToUnmod); + break; + case "block": + if (args) { + ftapi.actions.blockUser(args, function (response) { + firetable.actions.localChatResponse(response); + }); + } + break; + case "unblock": + if (args) { + ftapi.actions.unblockUser(args, function (response) { + firetable.actions.localChatResponse(response); + }); + } + break; + case "hot": + ftapi.actions.sendChat(":fire:"); + $("#cloud_with_rain").removeClass("on"); + $("#fire").addClass("on"); + break; + case "storm": + ftapi.actions.sendChat(":cloud_with_rain:"); + $("#cloud_with_rain").addClass("on"); + $("#fire").removeClass("on"); + break; + case "shrug": + ftapi.actions.sendChat((args ? args + " " : "") + "¯\\_(ツ)_/¯"); + break; + case "tableflip": + ftapi.actions.sendChat((args ? args + " " : "") + "(╯°□°)╯︵ ┻━┻"); + break; + case "unflip": + ftapi.actions.sendChat((args ? args + " " : "") + "┬─┬ ノ( ゜-゜ノ)"); + break; + } + } else { + // Regular chat message + ftapi.actions.sendChat(txt); + } + + $("#newchat").val(""); + $("#emojiPicker").slideUp(); + $("#pickEmoji").removeClass("on"); + firetable.utilities.exitAtLand(); + + } else if (e.key === "@") { + // ── @-mention autocomplete trigger ── + if (firetable.atLand) { + firetable.utilities.exitAtLand(); // double @@ cancels + } else { + firetable.utilities.initAtLand(); + $('#atPicker').addClass('show'); + for (var i = 0; i < firetable.atUsersFiltered.length; i++) { + $('
    ').appendTo('#atPicker'); + } + } + + } else if (firetable.atLand) { + // ── @-mention: filter as user types ── + if (e.key === " " || e.key === "Spacebar") { + firetable.utilities.exitAtLand(); + } else if (!e.key.match(/[0-9a-zA-Z_]/)) { + firetable.atString += e.key; + $('#atPicker').html(''); + $('
    Usernames cannot contain "' + e.key + '"
    ').appendTo('#atPicker'); + } else { + firetable.atString += e.key; + firetable.utilities.updateAtLand(); + } + } + }); + + // ── @-mention: backspace/arrow navigation ── + $("#newchat").bind("keyup", function (e) { + if (!firetable.atLand) return; + if (e.key === "Backspace") { + if (!firetable.atString) { + firetable.utilities.exitAtLand(); + } else { + firetable.atString = firetable.atString.slice(0, -1); + firetable.utilities.updateAtLand(); + } + } else if (e.key === "ArrowUp") { + $('#atPicker .butt:last').focus(); + } else if (e.key === "ArrowDown") { + $('#atPicker .butt:first').focus(); + } + }); + + // ── @-mention: Tab to auto-complete ── + $("#newchat").bind("keydown", function (e) { + if (e.key === "Tab") { + if (firetable.atUsersFiltered.length === 1) { + $("#newchat").one("blur", function () { + $("#newchat").focus().val($("#newchat").val()); + }); + firetable.utilities.chooseAt(firetable.atUsersFiltered[0]); + } else { + firetable.utilities.exitAtLand(); + } + } + }); + + // ── @-mention: click on dropdown item ── + $(document).on('click', '#atPicker .butt', function (e) { + e.preventDefault(); + firetable.utilities.chooseAt($(this).text().replace("@", "")); + setTimeout(function () { + var tempText = $("#newchat").val(); + $('#newchat').focus().val(''); + $('#newchat').val(tempText); + }, 250); + }); + + // ── @-mention: arrow keys within dropdown ── + $(document).on('keyup', '#atPicker .butt:focus', function (e) { + if (e.key === "ArrowUp") { + var $prev = $('#atPicker .butt:focus').parent().prev(); + if ($prev.length) { + $prev.find('.butt').focus(); + } else { + $('#atPicker .butt:last').focus(); + } + } else if (e.key === "ArrowDown") { + var $next = $('#atPicker .butt:focus').parent().next(); + if ($next.length) { + $next.find('.butt').focus(); + } else { + $('#atPicker .butt:first').focus(); + } + } + }); + + // ── "More chats" scroll-to-bottom button ── + $("#morechats .butt").bind("click", function () { + firetable.utilities.scrollToBottom(); + }); + + // ── Fire / Rain reaction buttons ── + $("#fire").bind("click", function () { + if (firetable.song) { + var $fires = $(".npmsg" + firetable.song.cid).last().find(".npmsg-fires"); + if ($fires.text() === "") { + $fires.text("🔥").css("font-size", "10px"); + } else { + var currentSize = parseInt($fires.css("font-size")) || 10; + $fires.css("font-size", (currentSize + 3) + "px"); + } + if (firetable.utilities.isChatPrettyMuchAtBottom()) firetable.utilities.scrollToBottom(); + } + $("#fire").addClass("on"); + }); + $("#cloud_with_rain").bind("click", function () { + ftapi.actions.sendChat(":cloud_with_rain:"); + $("#cloud_with_rain").addClass("on"); + $("#fire").removeClass("on"); + }); +}; diff --git a/js/constants.js b/js/constants.js new file mode 100644 index 0000000..5f06ff2 --- /dev/null +++ b/js/constants.js @@ -0,0 +1,73 @@ +/** + * constants.js — Named constants used across the firetable application. + * + * Replaces magic numbers and hardcoded strings scattered throughout the + * codebase with a single, documented source of truth. + */ + +// ─── Media Types ───────────────────────────────────────────────────────────── +/** YouTube video */ +var MEDIA_YOUTUBE = 1; +/** SoundCloud track */ +var MEDIA_SOUNDCLOUD = 2; + +// ─── Defaults ──────────────────────────────────────────────────────────────── +/** Default player volume (0–100) */ +var DEFAULT_VOLUME = 80; +/** How long before a user is marked idle (ms) — 5 minutes */ +var IDLE_TIMEOUT = 5 * 60000; +/** How long a track preview plays before auto-stopping (ms) — 30 seconds */ +var PREVIEW_DURATION = 30 * 1000; +/** 16:9 aspect ratio used for the YouTube player sizing */ +var ASPECT_RATIO = 16 / 9; +/** Default brand colour (firetable orange) */ +var COLOR_ORANGE = "#F4810B"; + +// ─── localStorage Keys ────────────────────────────────────────────────────── +/** + * All localStorage key strings in one place. + * Usage: localStorage[STORAGE.volume] instead of localStorage["firetableVol"] + */ +var STORAGE = { + volume: "firetableVol", + mute: "firetableMute", + disableMedia: "firetableDisableMedia", + showImages: "firetableShowImages", + showAvatars: "firetableShowAvatars", + badoop: "firetableBadoop", + desktopNotify: "firetableDTNM", + screenControl: "firetableScreenControl", + avatarStyle: "firetableAvatarStyle", + lastfmSession: "ftLastfmSession" +}; + +// ─── External Service URLs ────────────────────────────────────────────────── +/** Avatar / Robohash image base URL */ +var AVATAR_BASE_URL = "https://indiediscotheque.com/robots/"; +/** SoundCloud link resolver proxy */ +var SC_RESOLVE_URL = "https://thompsn.com/resolvesc/"; +/** SoundCloud general proxy */ +var SC_PROXY_URL = "https://thompsn.com/soundcloud/"; +/** SoundCloud API track base URL */ +var SC_API_TRACK_URL = "http://api.soundcloud.com/tracks/"; +/** Last.fm API base URL */ +var LASTFM_API_URL = "https://ws.audioscrobbler.com/2.0/"; + +// ─── Emoji Data Sources ───────────────────────────────────────────────────── +var EMOJI_URLS = [ + "https://unpkg.com/unicode-emoji-json@0.3.0/data-by-group.json", + "https://unpkg.com/emojilib@2.4.0/emojis.json", + "https://unpkg.com/emojilib@3.0.4/dist/emoji-en-US.json" +]; + +// ─── Search Result Limits ─────────────────────────────────────────────────── +/** Max results returned from YouTube / SoundCloud searches */ +var SEARCH_MAX_RESULTS = 15; +/** Max results per page for YouTube playlist import pagination */ +var IMPORT_PAGE_SIZE = 50; + +// ─── Preview Bar Update Interval ──────────────────────────────────────────── +/** How often the preview progress bar updates (ms) */ +var PREVIEW_BAR_INTERVAL = 200; +/** How often the song progress bar updates (ms) */ +var PROGRESS_BAR_INTERVAL = 500; diff --git a/js/emoji.js b/js/emoji.js new file mode 100644 index 0000000..a7a3cd1 --- /dev/null +++ b/js/emoji.js @@ -0,0 +1,172 @@ +/** + * emoji.js — Emoji picker, search, and shortname-to-unicode conversion. + * + * Loads emoji data from unpkg CDN (unicode-emoji-json + emojilib), + * builds the picker DOM, and converts :shortname: strings to unicode + * characters in chat messages. + */ + +firetable.emojis = { + + /** + * Show all emoji results and section headers (resets any active search filter). + */ + h: function () { + $(".pickerResult").show(); + $("#pickerResults h3").show(); + }, + + /** + * Test whether a picker emoji element matches a search query. + * Checks both the visible text (the emoji itself) and the data-alternative-name attribute. + * @param {jQuery} $el - The emoji span element + * @param {string} query - Lowercase search term + * @returns {boolean} + */ + n: function ($el, query) { + var altName = $el.attr("data-alternative-name"); + return ($el.text().toLowerCase().indexOf(query) >= 0) || + (altName != null && altName.toLowerCase().indexOf(query) >= 0); + }, + + /** + * Toggle an emoji category section in the picker. + * Clicking the same section twice resets to showing all sections. + * @param {string} sec - The section button's element ID (e.g. "bpickersmileys_emotion") + */ + sec: function (sec) { + var selectedSec = $("#pickerNav > .on"); + var contentId = sec.substr(1); // strip the "b" prefix to get the content div ID + + if (selectedSec.length) { + if (selectedSec[0].id === sec) { + // Toggle off — back to full list + $("#" + selectedSec[0].id).removeClass("on"); + $("#pickerContents div").show(); + } else { + // Switch to new section + $("#" + selectedSec[0].id).removeClass("on"); + $("#" + selectedSec[0].id.substr(1)).hide(); + $("#" + sec).addClass("on"); + $("#" + contentId).show(); + } + } else { + // First selection + $("#" + sec).addClass("on"); + $("#pickerContents div").hide(); + $("#" + contentId).show(); + } + }, + + /** + * Filter the emoji picker results by search text. + * Hides section headers during search, shows all when cleared. + * @param {string} val - Search text + */ + niceSearch: function (val) { + if (val.length === 0) { + firetable.emojis.h(); + return; + } + // Hide headers during filtered search + if ($("#pickerResults h3").is(":visible")) { + $("#pickerResults h3").hide(); + } + val = val.toLowerCase(); + $(".pickerResult").each(function (i, el) { + if (firetable.emojis.n($(el), val)) { + $(el).show(); + } else { + $(el).hide(); + } + }); + } +}; + +// ─── Emoji Map Loading & Conversion (extends firetable.utilities) ──────────── + +/** + * Fetch emoji data from CDN and build: + * 1. firetable.emojiMap — shortname → unicode lookup + * 2. Picker DOM — category nav + emoji grid in #pickerNav / #pickerContents + */ +firetable.utilities.getEmojiMap = function () { + firetable.emojiMap = {}; + (async function () { + try { + var requests = EMOJI_URLS.map(function (url) { return fetch(url); }); + var responses = await Promise.all(requests); + var promises = responses.map(function (response) { return response.json(); }); + var data = await Promise.all(promises); + + // Build a reverse map from emojilib v2: emoji_char → old_shortname + var oldmojis = {}; + for (var oldSlug in data[1]) { + if (data[1].hasOwnProperty(oldSlug)) { + oldmojis[data[1][oldSlug].char] = oldSlug; + } + } + + // Walk the grouped emoji data and build picker DOM + emojiMap + for (var category in data[0]) { + if (!data[0].hasOwnProperty(category)) continue; + var emojisArr = data[0][category]; + var catid = category.replace(/[\s&]+/g, '_').toLowerCase(); + + // Category nav button (uses first emoji as icon) + $('#pickerNav').append( + '' + emojisArr[0].emoji + '' + ); + // Category content section + $('#pickerContents').append( + '

    ' + category + '

    ' + ); + + for (var i = 0; i < emojisArr.length; i++) { + firetable.emojiMap[emojisArr[i].slug] = emojisArr[i].emoji; + + // Gather alternative search keywords from emojilib v3 + v2 + var words = ""; + if (data[2][emojisArr[i].emoji] !== undefined) { + words += data[2][emojisArr[i].emoji].join(','); + } + if (oldmojis[emojisArr[i].emoji] !== undefined) { + words += ',' + oldmojis[emojisArr[i].emoji]; + } + $("#picker" + catid).append( + '' + emojisArr[i].emoji + '' + ); + } + + // Also add old emojilib v2 shortnames to the map + for (var emoji in oldmojis) { + if (oldmojis.hasOwnProperty(emoji)) { + firetable.emojiMap[oldmojis[emoji]] = emoji; + } + } + } + twemoji.parse(document.getElementById("pickerNav")); + } catch (err) { + console.error("Failed to load emoji data:", err); + } + })(); +}; + +/** + * Replace :shortname: emoji codes in a string with unicode characters. + * Special-cases the custom :rohn: emoji. + * @param {string} str - Text containing :shortname: codes + * @returns {string} Text with shortnames replaced by unicode or custom HTML + */ +firetable.utilities.emojiShortnamestoUnicode = function (str) { + return str.replace(/\:(.*?)\:/g, function (match) { + var shortname = match.replace(/\:/g, ""); + if (firetable.emojiMap[shortname]) { + return '' + firetable.emojiMap[shortname] + ''; + } else if (shortname === "rohn") { + return ''; + } + return match; // unknown shortname — leave as-is + }); +}; diff --git a/js/firetable.js b/js/firetable.js index 3fb7e84..6a5dc04 100644 --- a/js/firetable.js +++ b/js/firetable.js @@ -217,6 +217,10 @@ ftapi.init = function(firebaseConfig) { if (data) { returnData.user = data; if (data.username) ftapi.uname = data.username; + if (data.avatarStyle) { + firetable.avatarStyle = data.avatarStyle; + localStorage[STORAGE.avatarStyle] = data.avatarStyle; + } } ftapi.events.emit("loggedIn", returnData); @@ -376,6 +380,23 @@ ftapi.actions = { }); }); }, + sendBotCommand: function(txt) { + var chatFeed = firebase.app("firetable").database().ref("chatFeed"); + var chatData = firebase.app("firetable").database().ref("chatData"); + var data = { + time: firebase.database.ServerValue.TIMESTAMP, + id: ftapi.uid, + txt: txt, + name: ftapi.uname, + botCmd: true + }; + var chatItem = chatData.push(data, function() { + var feedObj = { chatID: chatItem.key }; + var feedItem = chatFeed.push(feedObj, function() { + chatItem.child("feedID").set(feedItem.key); + }); + }); + }, switchList: function(listID) { var uref = firebase.app("firetable").database().ref("users/" + ftapi.uid + "/selectedList"); uref.set(listID); diff --git a/js/glitch.js b/js/glitch.js new file mode 100644 index 0000000..366ca95 --- /dev/null +++ b/js/glitch.js @@ -0,0 +1,313 @@ +/** + * glitch.js — p5.js album art glitch effect for SoundCloud tracks. + * + * Uses p5.js in global mode to create a canvas inside #scScreen. + * When a SoundCloud track loads, setup(biggerImg) is called from room.js + * to load the high-res album art and apply real-time pixel effects. + * + * Effects applied: + * - Flow Line: A horizontal bright stripe that scrolls downward + * - Shift Line: Random horizontal slices shifted left/right + * - Shift RGB: Random per-channel offset (chromatic aberration) + * - Scat Image: Random rectangular fragments repositioned + * + * Globals exposed (required by p5.js global mode): + * setup(useThis) — called automatically by p5 and from room.js newSong + * draw() — called every frame by p5 + * Glitch — class instantiated in setup() + * + * Depends on: p5.js (loaded from CDN), firetable.scImg (set by room.js) + */ + +/* jshint esversion: 6 */ + +// ─── State ─────────────────────────────────────────────────────────────────── + +/** @type {boolean} Whether the image has been loaded and Glitch is ready */ +let isLoaded = false; + +/** @type {Glitch|null} Current glitch instance */ +let glitch = null; + +/** @type {string} URL of the current SoundCloud album art */ +let imgSrc = ''; + +// ─── p5.js Entry Points ───────────────────────────────────────────────────── + +/** + * p5.js setup — creates the canvas and loads the album art. + * Also called directly from the newSong handler when a SC track starts. + * @param {string} [useThis] - Image URL to load (defaults to firetable.scImg) + */ +function setup(useThis) { + // Guard: p5.js may not have bound its globals yet (e.g. if called from + // a Firebase event before p5 auto-initializes). Bail out — p5 will call + // setup() itself once ready. + if (typeof createCanvas === 'undefined') return; + + if (!useThis) useThis = firetable.scImg; + background(0); + + var cnv = createCanvas($('#djStage').outerWidth(), $('#djStage').outerHeight()); + cnv.parent('scScreen'); + + loadImage(useThis, function (img) { + glitch = new Glitch(img); + isLoaded = true; + var $can = $('#scScreen canvas'); + var canrat = $can.width() / $can.height(); + $can.data('ratio', canrat); + }); +} + +/** + * p5.js draw loop — runs every frame. + * Clears the canvas and renders the glitch effect if the image is loaded. + */ +function draw() { + clear(); + background(0); + if (isLoaded) { + glitch.show(); + } +} + +// ─── Glitch Class ──────────────────────────────────────────────────────────── + +/** + * Pixel-level glitch effect engine. + * Operates on the raw pixel buffer of a p5.Image to create visual distortions. + */ +class Glitch { + /** + * @param {p5.Image} img - The source image to apply effects to + */ + constructor(img) { + /** Number of channels per pixel (RGBA) */ + this.channelLen = 4; + + /** The p5 image being manipulated */ + this.imgOrigin = img; + this.imgOrigin.loadPixels(); + + /** Pristine copy of the original pixel data for restoration each frame */ + this.copyData = new Uint8ClampedArray(this.imgOrigin.pixels); + + /** Flow line effect objects — each one is a scrolling bright stripe */ + this.flowLineImgs = []; + for (let i = 0; i < 1; i++) { + this.flowLineImgs.push({ + pixels: null, + t1: floor(random(0, 1000)), + speed: floor(random(4, 24)), + randX: floor(random(24, 80)) + }); + } + + /** Shift line effect buffers — horizontal slice displacement */ + this.shiftLineImgs = []; + for (let i = 0; i < 6; i++) { + this.shiftLineImgs.push(null); + } + + /** Shift RGB effect buffers — per-channel offset (chromatic aberration) */ + this.shiftRGBs = []; + for (let i = 0; i < 1; i++) { + this.shiftRGBs.push(null); + } + + /** Scattered image fragments — random rectangles repositioned */ + this.scatImgs = []; + for (let i = 0; i < 3; i++) { + this.scatImgs.push({ img: null, x: 0, y: 0 }); + } + + /** When false, effects are temporarily paused (shows clean image) */ + this.throughFlag = true; + } + + /** + * Overwrite pixel data of a destination image with source pixels. + * @param {p5.Image} destImg - Image whose pixels are replaced + * @param {Uint8ClampedArray} srcPixels - Source pixel buffer + */ + replaceData(destImg, srcPixels) { + for (let y = 0; y < destImg.height; y++) { + for (let x = 0; x < destImg.width; x++) { + let index = (y * destImg.width + x) * this.channelLen; + destImg.pixels[index] = srcPixels[index]; // R + destImg.pixels[index + 1] = srcPixels[index + 1]; // G + destImg.pixels[index + 2] = srcPixels[index + 2]; // B + destImg.pixels[index + 3] = srcPixels[index + 3]; // A + } + } + destImg.updatePixels(); + } + + /** + * Flow Line effect — adds brightness to a single horizontal scan line + * that scrolls vertically through the image. + * @param {p5.Image} srcImg - Source image + * @param {Object} obj - Effect state (t1, speed, randX) + * @returns {Uint8ClampedArray} Modified pixel buffer + */ + flowLine(srcImg, obj) { + let destPixels = new Uint8ClampedArray(srcImg.pixels); + obj.t1 %= srcImg.height; + obj.t1 += obj.speed; + let tempY = floor(obj.t1); + + for (let y = 0; y < srcImg.height; y++) { + if (tempY === y) { + for (let x = 0; x < srcImg.width; x++) { + let index = (y * srcImg.width + x) * this.channelLen; + destPixels[index] = srcImg.pixels[index] + obj.randX; // R + destPixels[index + 1] = srcImg.pixels[index + 1] + obj.randX; // G + destPixels[index + 2] = srcImg.pixels[index + 2] + obj.randX; // B + // A stays the same + } + } + } + return destPixels; + } + + /** + * Shift Line effect — displaces a random horizontal band left or right. + * @param {p5.Image} srcImg - Source image + * @returns {Uint8ClampedArray} Modified pixel buffer + */ + shiftLine(srcImg) { + let destPixels = new Uint8ClampedArray(srcImg.pixels); + let rangeH = srcImg.height; + let rangeMin = floor(random(0, rangeH)); + let rangeMax = rangeMin + floor(random(1, rangeH - rangeMin)); + let offsetX = this.channelLen * floor(random(-40, 40)); + + for (let y = 0; y < srcImg.height; y++) { + if (y > rangeMin && y < rangeMax) { + for (let x = 0; x < srcImg.width; x++) { + let index = (y * srcImg.width + x) * this.channelLen; + destPixels[index] = srcImg.pixels[index + offsetX]; // R + destPixels[index + 1] = srcImg.pixels[index + 1 + offsetX]; // G + destPixels[index + 2] = srcImg.pixels[index + 2 + offsetX]; // B + // A stays the same + } + } + } + return destPixels; + } + + /** + * Shift RGB effect — offsets each color channel independently to create + * a chromatic aberration / color-split look. + * @param {p5.Image} srcImg - Source image + * @returns {Uint8ClampedArray} Modified pixel buffer + */ + shiftRGB(srcImg) { + let range = 16; + let destPixels = new Uint8ClampedArray(srcImg.pixels); + + let randR = (floor(random(-range, range)) * srcImg.width + floor(random(-range, range))) * this.channelLen; + let randG = (floor(random(-range, range)) * srcImg.width + floor(random(-range, range))) * this.channelLen; + let randB = (floor(random(-range, range)) * srcImg.width + floor(random(-range, range))) * this.channelLen; + + for (let y = 0; y < srcImg.height; y++) { + for (let x = 0; x < srcImg.width; x++) { + let index = (y * srcImg.width + x) * this.channelLen; + destPixels[index] = srcImg.pixels[(index + randR) % srcImg.pixels.length]; // R + destPixels[index + 1] = srcImg.pixels[(index + 1 + randG) % srcImg.pixels.length]; // G + destPixels[index + 2] = srcImg.pixels[(index + 2 + randB) % srcImg.pixels.length]; // B + // A stays the same + } + } + return destPixels; + } + + /** + * Extract a random rectangular region from the source image. + * @param {p5.Image} srcImg - Source image + * @returns {p5.Image} Cropped sub-image + */ + getRandomRectImg(srcImg) { + let startX = floor(random(0, srcImg.width - 30)); + let startY = floor(random(0, srcImg.height - 50)); + let rectW = floor(random(30, srcImg.width - startX)); + let rectH = floor(random(1, 50)); + let destImg = srcImg.get(startX, startY, rectW, rectH); + destImg.loadPixels(); + return destImg; + } + + /** + * Main render — called every frame from draw(). + * Restores original pixels, randomly applies effects, then draws to canvas. + */ + show() { + // Restore pristine pixel data + this.replaceData(this.imgOrigin, this.copyData); + + // Randomly pause effects for short intervals (creates a "clean" flash) + let n = floor(random(100)); + if (n > 75 && this.throughFlag) { + this.throughFlag = false; + setTimeout(() => { + this.throughFlag = true; + }, floor(random(200, 1500))); + } + + if (!this.throughFlag) { + push(); + translate((width - this.imgOrigin.width) / 2, (height - this.imgOrigin.height) / 2); + image(this.imgOrigin, 0, 0); + pop(); + return; + } + + // Apply flow line + this.flowLineImgs.forEach((v, i, arr) => { + arr[i].pixels = this.flowLine(this.imgOrigin, v); + if (arr[i].pixels) { + this.replaceData(this.imgOrigin, arr[i].pixels); + } + }); + + // Apply shift line + this.shiftLineImgs.forEach((v, i, arr) => { + if (floor(random(100)) > 50) { + arr[i] = this.shiftLine(this.imgOrigin); + this.replaceData(this.imgOrigin, arr[i]); + } else if (arr[i]) { + this.replaceData(this.imgOrigin, arr[i]); + } + }); + + // Apply shift RGB + this.shiftRGBs.forEach((v, i, arr) => { + if (floor(random(100)) > 65) { + arr[i] = this.shiftRGB(this.imgOrigin); + this.replaceData(this.imgOrigin, arr[i]); + } + }); + + // Draw the processed image centered on the canvas + push(); + translate((width - this.imgOrigin.width) / 2, (height - this.imgOrigin.height) / 2); + image(this.imgOrigin, 0, 0); + pop(); + + // Scatter random rectangular fragments + this.scatImgs.forEach((obj) => { + push(); + translate((width - this.imgOrigin.width) / 2, (height - this.imgOrigin.height) / 2); + if (floor(random(100)) > 80) { + obj.x = floor(random(-this.imgOrigin.width * 0.3, this.imgOrigin.width * 0.7)); + obj.y = floor(random(-this.imgOrigin.height * 0.1, this.imgOrigin.height)); + obj.img = this.getRandomRectImg(this.imgOrigin); + } + if (obj.img) { + image(obj.img, obj.x, obj.y); + } + pop(); + }); + } +} diff --git a/js/helpers.js b/js/helpers.js new file mode 100644 index 0000000..052efb5 --- /dev/null +++ b/js/helpers.js @@ -0,0 +1,412 @@ +/** + * helpers.js — General-purpose utility functions for firetable. + * + * Contains formatting helpers, HTML escaping, debounce, notification sound, + * desktop notifications, chat scroll management, avatar URL builder, + * and the critical `resumeCurrentSong()` helper that replaces 8+ duplicated + * "calculate elapsed → load YT or SC" blocks from the original code. + */ + +firetable.utilities = { + + // ─── Avatar URL ────────────────────────────────────────────────────────── + + /** + * Build an avatar URL for a user. + * Supports Robohash (via proxy) and DiceBear (SVG). + * Style lookup order: explicit `style` param → ftapi.users[uid].avatarStyle + * → firetable.avatarStyle (own user cache) → "robohash:set1" fallback. + * @param {string} uid - User ID + * @param {string} username - Display name + * @param {string|null} [size="110x110"] - Dimensions (Robohash only) + * @param {string} [style] - Style key e.g. "robohash:set1" or "dicebear:pixel-art" + * @returns {string} Full avatar URL + */ + avatarURL: function (uid, username, size, style) { + size = size || "110x110"; + if (!style) { + style = (ftapi.users && ftapi.users[uid] && ftapi.users[uid].avatarStyle) + || (uid === ftapi.uid ? firetable.avatarStyle : null) + || "robohash:set1"; + } + if (style.indexOf("dicebear:") === 0) { + return "https://api.dicebear.com/9.x/" + style.slice(9) + "/svg?seed=" + encodeURIComponent(uid + username); + } + var set = style.indexOf("robohash:") === 0 ? style.slice(9) : firetable.avatarset; + return AVATAR_BASE_URL + uid + username + ".png?size=" + size + "&set=" + set; + }, + + // ─── Song Title Parsing ────────────────────────────────────────────────── + + /** + * Split a "Artist - Title" string into { artist, title }. + * Falls back: if no " - " separator, artist = fallbackArtist, title = the string. + * @param {string} raw - The raw title string (e.g. "Radiohead - Creep") + * @param {string} [fallbackArtist=""] - Used when title has no " - " separator + * @returns {{artist: string, title: string}} + */ + parseArtistTitle: function (raw, fallbackArtist) { + var parts = raw.split(" - "); + var artist = parts[0]; + var title = parts[1]; + if (!title) { + title = artist; + artist = fallbackArtist || ""; + } + return { artist: artist, title: title }; + }, + + // ─── Hex ↔ RGB ─────────────────────────────────────────────────────────── + + /** + * Convert a hex colour string to an {r, g, b} object. + * Handles both 3-char (#abc) and 6-char (#aabbcc) formats. + * @param {string} hex + * @returns {{r: number, g: number, b: number}|null} + */ + hexToRGB: function (hex) { + var shorthandRegex = /^#?([a-f\d])([a-f\d])([a-f\d])$/i; + hex = hex.replace(shorthandRegex, function (m, r, g, b) { + return r + r + g + g + b + b; + }); + var result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex); + return result ? { + r: parseInt(result[1], 16), + g: parseInt(result[2], 16), + b: parseInt(result[3], 16) + } : null; + }, + + // ─── Canvas Text Wrapping ──────────────────────────────────────────────── + + /** + * Draw word-wrapped text onto a canvas context. + * @param {CanvasRenderingContext2D} context + * @param {string} text + * @param {number} x - Starting X position + * @param {number} y - Starting Y position + * @param {number} maxWidth - Maximum line width in pixels + * @param {number} lineHeight - Vertical spacing between lines + * @returns {number} Number of extra lines drawn (0 if text fit on one line) + */ + wrapText: function (context, text, x, y, maxWidth, lineHeight) { + var words = text.split(' '); + var line = ''; + var lines = 0; + for (var n = 0; n < words.length; n++) { + var testLine = line + words[n] + ' '; + var metrics = context.measureText(testLine); + if (metrics.width > maxWidth && n > 0) { + context.fillText(line, x, y); + line = words[n] + ' '; + y += lineHeight; + lines++; + } else { + line = testLine; + } + } + context.fillText(line, x, y); + return lines; + }, + + // ─── Sound / Notifications ─────────────────────────────────────────────── + + /** + * Play an audio notification (the "badoop" sound on @-mentions). + * Respects the user's playBadoop setting. + * @param {string} filename - Audio file path without extension (adds .mp3) + */ + playSound: function (filename) { + if (firetable.playBadoop) { + document.getElementById("audilert").setAttribute('src', filename + ".mp3"); + } + }, + + /** + * Show a browser desktop notification (if permission granted). + * @param {Object} chatData - Chat message data {id, txt} + * @param {string} namebo - Display name of the sender + */ + desktopNotify: function (chatData, namebo) { + if (Notification) { + if (Notification.permission !== "granted") { + Notification.requestPermission(); + } else { + new Notification(namebo, { + icon: firetable.utilities.avatarURL(chatData.id, namebo), + body: chatData.txt + }); + } + } + }, + + // ─── Screen (Video Stage) ──────────────────────────────────────────────── + + /** Slide the video stage up (hide it) */ + screenUp: function () { + $('body').removeClass('screen'); + }, + + /** Slide the video stage down (show it) */ + screenDown: function () { + $('body').addClass('screen'); + }, + + // ─── Chat Scroll ───────────────────────────────────────────────────────── + + /** + * Check if the chat is scrolled to (or very near) the bottom. + * Used to decide whether to auto-scroll on new messages. + * @returns {boolean} + */ + isChatPrettyMuchAtBottom: function () { + if (!chatScroll || !chatScroll.contentWrapperEl) { + return true; + } + + var scrollable = chatScroll.contentEl.scrollHeight - chatScroll.el.clientHeight; + var scrolled = chatScroll.contentWrapperEl.scrollTop; + return (Math.abs(scrollable - scrolled) <= 25); + }, + + /** Scroll the chat container to the very bottom. */ + scrollToBottom: function () { + if (!chatScroll || !chatScroll.contentWrapperEl) { + return; + } + + chatScroll.contentWrapperEl.scrollTop = chatScroll.contentEl.scrollHeight; + }, + + // ─── HTML / Text Processing ────────────────────────────────────────────── + + /** + * Escape HTML special characters for safe insertion into the DOM. + * @param {string} s - Raw string + * @param {boolean} [preserveCR=false] - If true, preserves carriage returns as + * @returns {string} Escaped string + */ + htmlEscape: function (s, preserveCR) { + preserveCR = preserveCR ? ' ' : '\n'; + return ('' + s) + .replace(/&/g, '&') + .replace(/'/g, '\\'') + .replace(/"/g, '"') + .replace(//g, '>') + .replace(/\r\n/g, preserveCR) + .replace(/[\r\n]/g, preserveCR); + }, + + // ─── Date / Time Formatting ────────────────────────────────────────────── + + /** + * Format a timestamp as a short date string: M/D/YYYY + * @param {number|string|Date} d - Timestamp or Date + * @returns {string} + */ + format_date: function (d) { + var date = new Date(d); + return (date.getMonth() + 1) + "/" + date.getDate() + "/" + date.getFullYear(); + }, + + /** + * Format a timestamp as a 12-hour time string: H:MMam/pm + * @param {number|string|Date} d - Timestamp or Date + * @returns {string} + */ + format_time: function (d) { + var date = new Date(d); + var hours = date.getHours(); + var ampm = hours >= 12 ? "pm" : "am"; + if (hours > 12) hours -= 12; + if (hours === 0) hours = 12; + var minutes = date.getMinutes(); + var min = minutes > 9 ? "" + minutes : "0" + minutes; + return hours + ":" + min + ampm; + }, + + // ─── Debounce ──────────────────────────────────────────────────────────── + + /** + * Create a debounced version of a function that delays invocation until + * `wait` ms have elapsed since the last call. + * @param {Function} func + * @param {number} wait - Delay in milliseconds + * @param {boolean} [immediate=false] - Trigger on leading edge instead of trailing + * @returns {Function} + */ + debounce: function (func, wait, immediate) { + var timeout; + return function () { + var context = this; + var args = arguments; + var later = function () { + timeout = null; + if (!immediate) func.apply(context, args); + }; + var callNow = immediate && !timeout; + clearTimeout(timeout); + timeout = setTimeout(later, wait); + if (callNow) func.apply(context, args); + }; + }, + + // ─── Chat @-Mention Click Handler ──────────────────────────────────────── + + /** + * Attach a click handler to an element that inserts "@username " into the chat input. + * Works on .prson (user list), .botson (chat avatar), and .chatName elements. + * @param {jQuery} element - jQuery-wrapped DOM element + */ + chatAt: function (element) { + element.bind("click", function () { + var nameToAt; + if (element.hasClass("prson")) { + nameToAt = $(this).find(".prsnName").text(); + } else if (element.hasClass("botson")) { + nameToAt = $(this).next(".chatContent").find(".chatName").text(); + } else if (element.hasClass("chatName")) { + nameToAt = $(this).text(); + } + $("#newchat").val(function (i, val) { + return val + "@" + nameToAt + " "; + }).focus(); + }); + }, + + // ─── @-Mention Autocomplete ────────────────────────────────────────────── + + /** + * Enter @-mention mode: populate the list of all usernames + "everyone". + */ + initAtLand: function () { + firetable.atLand = true; + firetable.atString = ""; + firetable.atUsers = ["everyone"]; + for (var user in ftapi.users) { + firetable.atUsers.push(ftapi.users[user].username); + } + firetable.atUsersFiltered = firetable.atUsers.sort(); + }, + + /** + * Filter the @-mention dropdown to match the characters typed so far. + */ + updateAtLand: function () { + firetable.atUsersFiltered = firetable.atUsers + .filter(function (user) { + return user.toLowerCase().startsWith(firetable.atString.toLowerCase()); + }) + .sort(); + $('#atPicker').html(''); + if (firetable.atUsersFiltered.length) { + for (var i = 0; i < firetable.atUsersFiltered.length; i++) { + $('
    ').appendTo('#atPicker'); + } + } else { + $('
    No users match
    ').appendTo('#atPicker'); + } + }, + + /** + * Accept an @-mention selection: insert the name, close the picker. + * @param {string} atPeep - Username to insert (without the @) + */ + chooseAt: function (atPeep) { + var $chatText = $('#newchat'); + // Remove the partial string the user typed after @ + if (firetable.atString.length > 0) { + $chatText.val($chatText.val().slice(0, firetable.atString.length * -1)); + } + $chatText.val($chatText.val() + atPeep + " "); + firetable.utilities.exitAtLand(); + }, + + /** + * Exit @-mention mode: reset state and hide the picker. + */ + exitAtLand: function () { + firetable.atLand = false; + firetable.atUsersFiltered = []; + firetable.atString = ""; + $('#atPicker').removeClass('show').html(''); + }, + + // ─── Cancel Search/Queue Preview ─────────────────────────────────────── + + /** + * If a search-result or queue preview is playing, stop it and resume + * the current room song. This pattern was duplicated 6+ times across + * search handlers, cancelqsearch, queueTrack, etc. + * + * Returns true if a preview was actually cancelled, false otherwise. + * @returns {boolean} + */ + cancelSearchPreview: function () { + if (!firetable.preview) return false; + var prefix = String(firetable.preview).slice(0, 5); + if (prefix !== "ytcid" && prefix !== "sccid") return false; + + $("#pv" + firetable.preview).html(""); + clearTimeout(firetable.ptimeout); + firetable.ptimeout = null; + $("#pvbar" + firetable.preview).css("background-image", "none"); + clearInterval(firetable.movePvBar); + firetable.movePvBar = null; + firetable.preview = false; + firetable.utilities.resumeCurrentSong(); + return true; + }, + + // ─── Resume Current Song (Deduplication Helper) ────────────────────────── + + /** + * Resume playback of the current song at the correct elapsed position. + * + * This replaces the identical block of code that was copy-pasted 8+ times + * across the original codebase (in pview, reloadtrack, queueTrack, + * cancelqsearch, search handlers, newSong, initialize, SC READY, etc.). + * + * Calculates how much time has elapsed since the song started, + * then loads either the YouTube player or SoundCloud widget at that offset. + * + * @param {Object} [opts] - Optional overrides + * @param {boolean} [opts.forceVolume=false] - Also set volume after loading + */ + resumeCurrentSong: function (opts) { + opts = opts || {}; + if (!firetable.song) return; + if (firetable.preview) return; // don't interrupt a preview + + var data = firetable.song; + var nownow = Date.now(); + var timeSince = nownow - data.started; + if (timeSince <= 0) timeSince = 0; + + var secSince = Math.floor(timeSince / 1000); + + if (data.type === MEDIA_YOUTUBE) { + if (firetable.scLoaded) firetable.scwidget.pause(); + if (!firetable.disableMediaPlayback) { + player.loadVideoById(data.cid, secSince, "large"); + } + if (opts.forceVolume) { + var vol = $("#slider").slider("value"); + player.setVolume(vol); + firetable.scwidget.setVolume(vol); + } + } else if (data.type === MEDIA_SOUNDCLOUD) { + if (firetable.ytLoaded) player.stopVideo(); + firetable.scSeek = timeSince; + if (!firetable.disableMediaPlayback) { + firetable.scwidget.load(SC_API_TRACK_URL + data.cid, { auto_play: true }); + } + if (opts.forceVolume) { + var vol = $("#slider").slider("value"); + player.setVolume(vol); + firetable.scwidget.setVolume(vol); + } + } + } +}; diff --git a/js/init.js b/js/init.js new file mode 100644 index 0000000..964e899 --- /dev/null +++ b/js/init.js @@ -0,0 +1,175 @@ +/** + * init.js — Application bootstrap. + * + * This is the last module loaded. It defines firetable.init() which: + * 1. Marks the app as started + * 2. Applies room branding from ftconfigs (title, logo, social links) + * 3. Sets up the DOMParser for HTML stripping + * 4. Binds the window resize handler (debounced) + * 5. Initializes the SoundCloud widget and binds its READY/PLAY events + * 6. Removes playlist/tag editor DOM templates for later cloning + * 7. Boots Firebase via ftapi.init() + * 8. Initializes the SoundCloud SDK + * 9. Binds auth lifecycle events (login, logout, reconnect, ban) + * 10. Calls firetable.ui.init() to wire up the rest of the UI + * + * The final line auto-starts the app: `if (!firetable.started) firetable.init();` + * + * Depends on: All other modules must be loaded before this file. + */ + +// ─── Bootstrap ─────────────────────────────────────────────────────────────── + +firetable.init = function () { + console.log( + "\n" + + " ( ) ) (\n" + + " )\\ ) ( ( ( ( /( ) ( /( )\\ (\n" + + "(()/( )\\ )( ))\\ )\\()) ( /( )\\()) (_)) ))\\\n" + + " /(_)) ((_) (()\\ /((_) (_))/ )(_)) ((_)\\ | | /((_)\n" + + "(_) _| (_) ((_) (_)) | |_ ((_)_ | |(_) | | (_))\n" + + " | _| | | | '_| / -_) | _| / _` | | '_ \\ | | / -_)\n" + + " |_| |_| |_| \\___| \\__| \\__,_| |_.__/ |_| \\___|" + ); + + firetable.started = true; + + // ── Room Branding ── + $("#idtitle").text(ftconfigs.roomName); + $("#welcomeName").text(ftconfigs.roomName); + + if (ftconfigs.avatarset) firetable.avatarset = ftconfigs.avatarset; + + // Social links — show each icon only if the URL is configured + var socialLinks = [ + { cls: "facebook", url: ftconfigs.facebookURL }, + { cls: "reddit", url: ftconfigs.redditURL }, + { cls: "lastfm", url: ftconfigs.lastfmURL }, + { cls: "discord", url: ftconfigs.discordURL }, + { cls: "soundcloud", url: ftconfigs.soundcloudURL } + ]; + socialLinks.forEach(function (link) { + if (link.url) { + $(".sociallogo." + link.cls) + .attr("href", link.url) + .css("display", "inline-block"); + } + }); + + if (ftconfigs.logoImage) { + $("#roomlogo").css("background-image", "url(" + ftconfigs.logoImage + ")"); + } + document.title = ftconfigs.roomName + " | firetable"; + if (ftconfigs.roomInfoUrl.length) { + $("#roomInfo").attr("href", ftconfigs.roomInfoUrl); + } + $("#version").text("You're running firetable v" + firetable.version + "."); + + // ── Utilities Setup ── + firetable.utilities.getEmojiMap(); + firetable.parser = new DOMParser(); + + // ── Window Resize Handler (debounced) ── + $(window).resize(firetable.utilities.debounce(function () { + $("#thehistory").css('top', $('#stage').outerHeight() + $('#topbar').outerHeight()); + $('#playerArea, #scScreen') + .width($('#djStage').outerWidth()) + .height($('#djStage').outerHeight()); + $("#stealContain").css({ + 'top': $('#grab').offset().top + $('#grab').height(), + 'left': $('#grab').offset().left - 16 + }); + setup(); // Re-create the p5.js canvas at the new size + }, 500)); + + firetable.utilities.scrollToBottom(); + + // ── SoundCloud Widget ── + var widgetIframe = document.getElementById('sc-widget'); + firetable.scwidget = SC.Widget(widgetIframe); + + firetable.scwidget.bind(SC.Widget.Events.READY, function () { + // When a SC track starts playing, apply volume + seek + firetable.scwidget.bind(SC.Widget.Events.PLAY, function () { + var vol = localStorage[STORAGE.volume]; + if (!vol) { + vol = DEFAULT_VOLUME; + localStorage[STORAGE.volume] = DEFAULT_VOLUME; + } + firetable.scwidget.setVolume(vol); + if (firetable.scSeek) firetable.scwidget.seekTo(firetable.scSeek); + }); + + // If a SC song was already loaded before the widget was ready, start it now + if (firetable.song && firetable.song.type == MEDIA_SOUNDCLOUD) { + var data = firetable.song; + var timeSince = Date.now() - data.started; + if (timeSince <= 0) timeSince = 0; + if (!firetable.preview) { + firetable.scSeek = timeSince; + if (!firetable.disableMediaPlayback) { + firetable.scwidget.load(SC_API_TRACK_URL + data.cid, { auto_play: true }); + } + } + } + firetable.scLoaded = true; + }); + + // ── DOM Templates ── + $playlistItemTemplate = $('#mainqueue .pvbar').remove(); + $tagEditorTemplate = $('.tagPromptBox').remove(); + + // ── Firebase Init ── + ftapi.init(ftconfigs.firebase); + + // ── SoundCloud SDK Init ── + SC.initialize({ client_id: ftconfigs.soundcloudKey }); + + // ── Auth Lifecycle Events ── + + /** User successfully logged in */ + ftapi.events.on("loggedIn", function (data) { + firetable.actions.loggedIn(data); + }); + + /** User logged out */ + ftapi.events.on("loggedOut", firetable.actions.showLoginScreen); + + /** Firebase reconnected after a network drop */ + ftapi.events.on("authReconnected", function () { + firetable.debug && console.log('reconnected'); + $('body').removeClass('disconnected'); + $('#newchat').prop('disabled', false).focus(); + }); + + /** Firebase connection lost */ + ftapi.events.on("authDisconnected", function () { + firetable.debug && console.log('disconnected'); + $('body').addClass('disconnected'); + $('#newchat').prop('disabled', true).blur(); + }); + + /** Current user was banned */ + ftapi.events.on("userBanned", function () { + firetable.debug && console.log("ban detected."); + if (document.getElementById("notice") == null) { + var usrname2use = ftapi.uid; + if (ftapi.users[ftapi.uid] && ftapi.users[ftapi.uid].username) { + usrname2use = ftapi.users[ftapi.uid].username; + } + $('.notice').attr('id', 'notice'); + $("#troublemaker").text(usrname2use); + } + }); + + /** Current user was un-banned */ + ftapi.events.on("userUnbanned", function () { + window.location.reload(); + }); + + // ── UI Init (wires everything up) ── + firetable.ui.init(); +}; + +// ─── Auto-start ────────────────────────────────────────────────────────────── +if (!firetable.started) firetable.init(); diff --git a/js/lastfm.js b/js/lastfm.js new file mode 100644 index 0000000..456e04e --- /dev/null +++ b/js/lastfm.js @@ -0,0 +1,187 @@ +/** + * lastfm.js — Last.fm scrobbling integration. + * + * Handles: session management (connect/disconnect), nowPlaying updates, + * scrobble submissions, and track.love. Uses the MD5 implementation from + * md5.js to sign API requests. + * + * The Last.fm API key and secret are defined here. Session keys are stored + * in localStorage so scrobbling persists across page reloads. + */ + +firetable.lastfm = { + + /** @type {string|false} Last.fm session key, or false if not connected */ + sk: false, + + /** @type {string} Last.fm API key */ + key: "e86f3b80e48769c03f2b4e0609e12924", + + /** @type {string} Last.fm API shared secret (used for signing requests) */ + secret: "838d63e62b556f74176656640b75e33e", + + /** @type {number|null} Unix timestamp (seconds) when the current song started */ + songStart: null, + + /** @type {number|null} Duration of the current song in seconds */ + duration: null, + + /** @type {number|null} setTimeout ID for the scrobble timer */ + timer: null, + + // ─── Session Management ──────────────────────────────────────────────── + + /** + * Disconnect the Last.fm session and clear stored credentials. + * Re-renders the settings link to allow re-connecting. + */ + killSession: function () { + firetable.lastfm.sk = false; + localStorage[STORAGE.lastfmSession] = firetable.lastfm.sk; + $("#scrobtoggle").html( + 'Set up last.fm scrobbling' + ); + }, + + /** + * Handle a successful auth.getSession response from Last.fm. + * Parses the session key from the XHR response and stores it. + * @param {XMLHttpRequest} xhr - The completed XHR object + * @returns {Function} onload callback + */ + newSession: function (xhr) { + return function () { + var jsonResponse = JSON.parse(xhr.responseText); + firetable.lastfm.sk = jsonResponse.session.key; + localStorage[STORAGE.lastfmSession] = firetable.lastfm.sk; + $("#scrobtoggle").html( + 'Disconnect Lastfm Scrobbling' + ); + }; + }, + + // ─── API Calls ───────────────────────────────────────────────────────── + + /** + * Submit a scrobble (track.scrobble) for the current song. + * Called automatically after ~(duration - 3s) of playback. + */ + scrobble: function () { + var song = firetable.song; + var track = firetable.lastfm._cleanForScrobble(song.title); + + var params = { + artist: song.artist, + track: track, + timestamp: firetable.lastfm.songStart, + api_key: firetable.lastfm.key, + sk: firetable.lastfm.sk, + method: "track.scrobble" + }; + + params.api_sig = firetable.lastfm.getApiSignature(params); + var requestUrl = LASTFM_API_URL + '?' + serialize(params); + + var xhr = new XMLHttpRequest(); + xhr.open('POST', requestUrl, true); + xhr.onload = function () { console.log("scrobbled"); }; + xhr.onerror = firetable.lastfm._onAjaxError; + xhr.send(); + }, + + /** + * Send a track.love for the current song. + */ + love: function () { + var song = firetable.song; + var track = firetable.lastfm._cleanForScrobble(song.title); + + var params = { + artist: song.artist, + track: track, + api_key: firetable.lastfm.key, + sk: firetable.lastfm.sk, + method: "track.love" + }; + + params.api_sig = firetable.lastfm.getApiSignature(params); + var requestUrl = LASTFM_API_URL + '?' + serialize(params); + + var xhr = new XMLHttpRequest(); + xhr.open('POST', requestUrl, true); + xhr.onload = function () { console.log("loved"); }; + xhr.onerror = firetable.lastfm._onAjaxError; + xhr.send(); + }, + + /** + * Send a track.updateNowPlaying to Last.fm. + * Called immediately when a new song starts. + */ + nowPlaying: function () { + var song = firetable.song; + var track = firetable.lastfm._cleanForScrobble(song.title); + + var params = { + artist: song.artist, + track: track, + duration: firetable.lastfm.duration, + api_key: firetable.lastfm.key, + sk: firetable.lastfm.sk, + method: "track.updateNowPlaying" + }; + + params.api_sig = firetable.lastfm.getApiSignature(params); + var requestUrl = LASTFM_API_URL + '?' + serialize(params); + + var xhr = new XMLHttpRequest(); + xhr.open('POST', requestUrl, true); + xhr.onload = function () { console.log("nowplaying updated"); }; + xhr.onerror = firetable.lastfm._onAjaxError; + xhr.send(); + }, + + // ─── Internal Helpers ────────────────────────────────────────────────── + + /** + * Build the api_sig parameter for a Last.fm request. + * Sorts params alphabetically, concatenates key+value pairs, + * appends the shared secret, and returns the MD5 hash. + * @param {Object} params - API parameters (excluding api_sig) + * @returns {string} 32-char hex MD5 signature + */ + getApiSignature: function (params) { + var keys = []; + for (var key in params) { + if (params.hasOwnProperty(key)) keys.push(key); + } + keys.sort(); + + var paramString = ""; + for (var i = 0; i < keys.length; i++) { + paramString += keys[i] + params[keys[i]]; + } + return calcMD5(paramString + firetable.lastfm.secret); + }, + + /** + * Strip common YouTube title suffixes that pollute scrobble data. + * e.g. " (Official Audio)", " (Official Video)" + * @param {string} title + * @returns {string} Cleaned title + */ + _cleanForScrobble: function (title) { + return title.replace(/ \([oO]fficial (?:[aA]udio|[vV]ideo)\)/, ""); + }, + + /** + * Log Last.fm API errors to the console. + * @param {XMLHttpRequest} xhr + * @param {string} status + * @param {string} error + */ + _onAjaxError: function (xhr, status, error) { + console.error("Last.fm API error:", xhr, status, error); + } +}; diff --git a/js/main.js b/js/main.js deleted file mode 100644 index dc15f06..0000000 --- a/js/main.js +++ /dev/null @@ -1,4375 +0,0 @@ -var firetable = { - started: false, - loggedIn: false, - uid: null, - uname: null, - pvCount: 0, - playdex: 0, - users: {}, - queue: false, - preview: false, - movePvBar: null, - moveBar: null, - song: null, - playBadoop: true, - idle: false, - idleChanged: null, - sbhowImages: false, - screenControl: "sync", - lights: false, - screenSyncPos: false, - scSeek: false, - desktopNotifyMentions: false, - orange: "#F4810B", - color: "#F4810B", - countcolor: "#fff", - ytLoaded: null, - scLoaded: null, - listShowing: null, - parser: null, - songToEdit: null, - scwidget: null, - searchSelectsChoice: 1, - importSelectsChoice: 1, - dtImportName: null, - dtImportList: [], - lastChatPerson: false, - lastChatId: false, - tagUpdate: null, - nonpmsg: true, - playlimit: 2, - scImg: "", - superCopBanUpdates: null, - loginForm: null, - emojiMap: null, - pickerInit: false, - atLand: false, - atUsers: [], - atUsersFiltered: [], - atString: "", - debug: true, -}; - -if (typeof ftconfigs == "undefined") - throw "config.js is missing! Copy config.js.example and rename to config.js. Edit this file and add your own app's information."; - -var chatScroll = new SimpleBar(document.getElementById("chatsWrap")); -chatScroll.getScrollElement().addEventListener("scroll", function () { - if (firetable.utilities.isChatPrettyMuchAtBottom()) $("#morechats").removeClass("show"); -}); - -firetable.version = "01.09.05"; - -var player, $playlistItemTemplate; - -var idlejs = new IdleJs({ - idle: 5 * 60000, - events: ["mousemove", "keydown", "mousedown", "touchstart"], - onIdle: function () { - ftapi.actions.changeIdleStatus(true, 1); - }, - onActive: function () { - ftapi.actions.changeIdleStatus(false, 1); - }, - onHide: function () { - ftapi.actions.changeIdleStatus(true, 1); - firetable.debug && console.log("hide"); - }, - onShow: function () { - ftapi.actions.changeIdleStatus(false, 1); - }, - keepTracking: true, - startAtIdle: false, -}); -idlejs.start(); - -function onYouTubeIframeAPIReady() { - player = new YT.Player("playerArea", { - width: $("#djStage").outerHeight() * 1.7777, - height: $("#djStage").outerHeight(), - playerVars: { - autoplay: 1, - controls: 0, - }, - videoId: "0", - events: { - onReady: initialize, - onStateChange: function () { - $("#reloadtrack").removeClass("on working"); - }, - }, - }); -} - -function initialize(event) { - firetable.ytLoaded = true; - var vol = localStorage["firetableVol"]; - if (typeof vol == "undefined") { - vol = 80; - localStorage["firetableVol"] = 80; - } - player.setVolume(vol); - - var muted = localStorage["firetableMute"]; - if (typeof muted == "undefined") { - localStorage["firetableMute"] = false; - muted = "false"; - $("#volstatus").removeClass("on"); - } - - if (muted != "false") { - var icon = ""; - $("#volstatus i").html(icon); - $("#volstatus").addClass("on"); - } - - $("#slider").slider({ - orientation: "horizontal", - range: "min", - min: 0, - max: 100, - value: vol, - step: 5, - slide: function (event, ui) { - player.setVolume(ui.value); - firetable.scwidget.setVolume(ui.value); - localStorage["firetableVol"] = ui.value; - var muted = localStorage["firetableMute"]; - if (muted != "false") { - localStorage["firetableMute"] = false; - var icon = ""; - $("#volstatus i").html(icon); - $("#volstatus").removeClass("on"); - } else if (ui.value == 0) { - firetable.actions.muteToggle(true); - $("#volstatus").addClass("on"); - } - }, - }); - if (firetable.song) { - var data = firetable.song; - var nownow = Date.now(); - var timeSince = nownow - data.started; - if (timeSince <= 0) timeSince = 0; - - var secSince = Math.floor(timeSince / 1000); - var timeLeft = data.duration - secSince; - if (data.type == 1) { - if (!firetable.preview) { - if (!firetable.disableMediaPlayback) player.loadVideoById(data.cid, secSince, "large"); - } - } - } -} - -function onPlayerStateChange(event) { - //state changed thanks -} - -firetable.init = function () { - console.log(` - ( ) ) ( - )\\ ) ( ( ( ( /( ) ( /( )\\ ( -(()/( )\\ )( ))\\ )\\()) ( /( )\\()) (_)) ))\\ - /(_)) ((_) (()\\ /((_) (_))/ )(_)) ((_)\\ | | /((_) -(_) _| (_) ((_) (_)) | |_ ((_)_ | |(_) | | (_)) - | _| | | | '_| / -_) | _| / _' | | '_ \\ | | / -_) - |_| |_| |_| \\___| \\__| \\__,_| |_.__/ |_| \\___| -`); - firetable.started = true; - - $("#idtitle").text(ftconfigs.roomName); - $("#welcomeName").text(ftconfigs.roomName); - - if (ftconfigs.facebookURL) { - $(".sociallogo.facebook").attr("href", ftconfigs.facebookURL); - } - - if (ftconfigs.redditURL) { - $(".sociallogo.reddit").attr("href", ftconfigs.redditURL); - } - - if (ftconfigs.lastfmURL) { - $(".sociallogo.lastfm").attr("href", ftconfigs.lastfmURL); - } - - if (ftconfigs.discordURL) { - $(".sociallogo.discord").attr("href", ftconfigs.discordURL); - } - - if (ftconfigs.soundcloudURL) { - $(".sociallogo.soundcloud").attr("href", ftconfigs.soundcloudURL); - } - - if (ftconfigs.logoImage) $("#roomlogo").css("background-image", "url(" + ftconfigs.logoImage + ")"); - document.title = ftconfigs.roomName + " | firetable"; - if (ftconfigs.roomInfoUrl.length) $("#roomInfo").attr("href", ftconfigs.roomInfoUrl); - $("#version").text("You're running firetable v" + firetable.version + "."); - firetable.utilities.getEmojiMap(); - firetable.parser = new DOMParser(); - $(window).resize( - firetable.utilities.debounce(function () { - // This will execute whenever the window is resized - $("#thehistory").css("top", $("#djStage").outerHeight() + $("#topbar").outerHeight()); - $("#playerArea,#scScreen").width($("#djStage").outerWidth()).height($("#djStage").outerHeight()); - $("#stealContain").css({ - top: $("#grab").offset().top + $("#grab").height(), - left: $("#grab").offset().left - 16, - }); - setup(); - }, 500) - ); - firetable.utilities.scrollToBottom(); - var widgetIframe = document.getElementById("sc-widget"); - firetable.scwidget = SC.Widget(widgetIframe); - firetable.scwidget.bind(SC.Widget.Events.READY, function () { - firetable.scwidget.bind(SC.Widget.Events.PLAY, function () { - var vol = localStorage["firetableVol"]; - if (!vol) { - vol = 80; - localStorage["firetableVol"] = 80; - } - firetable.scwidget.setVolume(vol); - if (firetable.scSeek) firetable.scwidget.seekTo(firetable.scSeek); - }); - if (firetable.song) { - var data = firetable.song; - var nownow = Date.now(); - var timeSince = nownow - data.started; - if (timeSince <= 0) timeSince = 0; - - var secSince = Math.floor(timeSince / 1000); - var timeLeft = data.duration - secSince; - if (data.type == 2) { - if (!firetable.preview) { - firetable.scSeek = timeSince; - if (!firetable.disableMediaPlayback) - firetable.scwidget.load("http://api.soundcloud.com/tracks/" + data.cid, { - auto_play: true, - }); - } - } - } - firetable.scLoaded = true; - }); - - $playlistItemTemplate = $("#mainqueue .pvbar").remove(); - $tagEditorTemplate = $(".tagPromptBox").remove(); - - ftapi.init(ftconfigs.firebase); - - SC.initialize({ - client_id: ftconfigs.soundcloudKey, - }); - - ftapi.events.on("loggedIn", function (data) { - firetable.actions.loggedIn(data); - }); - - ftapi.events.on("loggedOut", firetable.actions.showLoginScreen); - - ftapi.events.on("authReconnected", function () { - firetable.debug && console.log("reconnected"); - $("body").removeClass("disconnected"); - $("#newchat").prop("disabled", false).focus(); - }); - - ftapi.events.on("authDisconnected", function () { - firetable.debug && console.log("disconnected"); - $("body").addClass("disconnected"); - $("#newchat").prop("disabled", true).blur(); - }); - - ftapi.events.on("userBanned", function () { - firetable.debug && console.log("ban detected."); - if (document.getElementById("notice") == null) { - var usrname2use = ftapi.uid; - if (ftapi.users[ftapi.uid]) { - if (ftapi.users[ftapi.uid].username) usrname2use = ftapi.users[ftapi.uid].username; - } - $(".notice").attr("id", "notice"); - $("#troublemaker").text(usrname2use); - } - }); - - ftapi.events.on("userUnbanned", function () { - window.location.reload(); - }); - - firetable.ui.init(); -}; - -firetable.actions = { - dubtrackImport: function () { - $("#importDubResults").html("importing (0/" + firetable.dtImportList.length + ")..."); - $("#dubimportButton").hide(); - var listid = ftapi.actions.createList(firetable.dtImportName); - var name = firetable.dtImportName; - - $("#listpicker").append('"); - var trackarray = firetable.dtImportList; - for (var e = 0; e < trackarray.length; e++) { - var thetype = 1; - if (trackarray[e].type == "soundcloud") thetype = 2; - var numbo = e + 1; - $("#importDubResults").html("importing (" + numbo + "/" + firetable.dtImportList.length + ")..."); - if (numbo == firetable.dtImportList.length) - $("#importDubResults").html( - "Import complete! You can now select another file if you'd like to do another!" - ); - ftapi.actions.addToList(thetype, trackarray[e].name, trackarray[e].cid, listid); - } - }, - localChatResponse: function (txt) { - if (txt.length) { - $("#chats").append('
    ' + txt + "
    "); - firetable.utilities.scrollToBottom(); - } - }, - logOut: function () { - ftapi.actions.logOut(); - firetable.debug && console.log("logout"); - }, - showLoginScreen: function () { - $("#cardCaseButton").hide(); - $("#loggedInUser").hide(); - $("#logOutButton").hide().off(); - $("#mainGrid").removeClass().addClass("login"); - $("#grab").css("display", "none"); - if (firetable.loginForm && !$("#login").html()) { - $("#mainGrid").append('
    ' + firetable.loginForm + "
    "); - - firetable.ui.loginEventsInit(); - } - }, - logIn: function (email, password) { - firetable.debug && console.log("login"); - ftapi.actions.logIn(email, password, function (error) { - var errorCode = error.code; - var errorMessage = error.message; - if (errorCode === "auth/wrong-password") { - alert("Wrong password."); - } else { - alert(errorMessage); - } - firetable.debug && console.log("log in error:", error); - }); - }, - loggedIn: function (user) { - firetable.debug && console.log("user signed in!", user.uid, ftapi.uid, ftapi.users); - if ($("#login").html()) { - firetable.loginForm = $("#login").html(); - firetable.ui.loginEventsDestroy(); - $("#login").remove(); - } - - if (ftapi.users[ftapi.uid]) { - if (ftapi.users[ftapi.uid].username) { - $("#loggedInUser .botson") - .css( - "background-image", - "url(https://indiediscotheque.com/robots/" + - ftapi.uid + - ftapi.users[ftapi.uid].username + - ".png?size=175x175)" - ) - .prop("title", "You're logged in as " + ftapi.users[ftapi.uid].username); - $("#loggedInUsername").text(ftapi.users[ftapi.uid].username); - } else { - $("#loggedInUser .botson").data("uid", user.uid); - $("#loggedInUser .botson").css( - "background-image", - "url(https://indiediscotheque.com/robots/" + user.uid + ".png?size=175x175)" - ); - } - } else { - $("#loggedInUser .botson").data("uid", user.uid); - $("#loggedInUser .botson").css( - "background-image", - "url(https://indiediscotheque.com/robots/" + user.uid + ".png?size=175x175)" - ); - } - - ftapi.lookup.allLists(function (allPlaylists) { - $("#listpicker").off("change"); - $("#listpicker").html( - '' - ); - for (var key in allPlaylists) { - if (allPlaylists.hasOwnProperty(key)) { - $("#listpicker").append( - '" - ); - } - } - ftapi.lookup.selectedList(function (selectedList) { - $("#listpicker").val(selectedList).change(); - $("#listpicker").change(function () { - var val = $("#listpicker").val(); - if (val == "1") { - //ADD PLAYLIST SCREEN - $("#mainqueuestuff").css("display", "none"); - $("#filterMachine").css("display", "none"); - $("#addbox").css("display", "none"); - $("#cancelqsearch").hide(); - $("#qControlButtons").hide(); - - $("#plmanager").css("display", "flex"); - } else if (val != ftapi.selectedListThing) { - //LOAD SELECTED LIST - //change selected list in user obj - $("#mainqueuestuff").css("display", "block"); - $("#filterMachine").css("display", "block"); - $("#addbox").css("display", "none"); - $("#cancelqsearch").hide(); - $("#qControlButtons").show(); - - $("#plmanager").css("display", "none"); - ftapi.actions.switchList(val); - } else { - //you selected the thing you already had selected. - $("#mainqueuestuff").css("display", "block"); - $("#filterMachine").css("display", "block"); - $("#addbox").css("display", "none"); - $("#cancelqsearch").hide(); - $("#qControlButtons").show(); - $("#plmanager").css("display", "none"); - } - }); - }); - }); - $("#cardCaseButton").show(); - $("#loggedInUser").show(); - $("#logOutButton").show().on("click", firetable.actions.logOut); - firetable.debug && console.log("remove login class from mainGrid"); - $("#mainGrid").removeClass().addClass("mmusrs"); - $("#grab").css("display", "inline-block"); - }, - cardCase: function () { - $("#cardsMain").html(""); - ftapi.lookup.cardCollection(function (data) { - for (var key in data) { - var childData = data[key]; - firetable.debug && console.log("card:", childData); - $("#cardsMain").append( - 'Gift to DJShare In Chat' - ); - firetable.actions.displayCard(childData, key); - } - }); - }, - chatCard: function (cardid) { - ftapi.actions.sendChat("Check out my card...", cardid); - }, - giftCard: function (cardid) { - ftapi.actions.sendChat("!giftcard :gift:", cardid); - $("#caseCardSpot" + cardid).remove(); - }, - displayCard: function (data, chatid) { - firetable.debug && console.log("display card"); - var defaultScheme = false; - if (data.colors) { - if (data.colors.color == "#fff" || data.colors.color == "#7f7f7f") { - data.colors.color = firetable.orange; - data.colors.txt = "#000"; - defaultScheme = true; - } - } - - if (data.image == "img/idlogo.png" && ftconfigs.defaultAlbumArtUrl.length) - data.image = ftconfigs.defaultAlbumArtUrl; - var canvas = document.getElementById("cardMaker" + chatid); - - if (canvas.getContext) { - var ctx = canvas.getContext("2d"); - ctx.clearRect(0, 0, canvas.width, canvas.height); - ctx.fillStyle = "#000"; - ctx.fillRect(0, 0, 225, 300); - - ctx.fillStyle = data.colors.color; - if (defaultScheme) ctx.fillStyle = "#fff"; - ctx.fillRect(1, 30, 223, 175); - - var grd = ctx.createLinearGradient(0, 0, 0, 175); - grd.addColorStop(0, "rgba(0, 0, 0, 0.75)"); - grd.addColorStop(1, "rgba(0, 0, 0, 0.55)"); - - // Fill with gradient - ctx.fillStyle = grd; - ctx.fillRect(1, 30, 223, 175); - - ctx.fillStyle = data.colors.color; - ctx.fillRect(1, 205, 223, 10); - - ctx.fillStyle = "#333333"; - //ctx.fillRect(1, 205, 223, 1); - // ctx.fillRect(1, 215, 223, 1); - - ctx.fillStyle = "#151515"; - ctx.fillRect(1, 216, 223, 75); - - //text go - ctx.fillStyle = "#eee"; - ctx.font = "700 11px Helvetica, Arial, sans-serif"; - ctx.fillText(data.djname, 10, 20); - - ctx.font = "400 8px Helvetica, Arial, sans-serif"; - ctx.textAlign = "center"; - ctx.fillText( - "Printed " + firetable.utilities.format_date(data.date) + " | " + ftconfigs.roomNameShort, - 112.5, - 299 - ); - - ctx.font = "700 10px Helvetica, Arial, sans-serif"; - ctx.textAlign = "left"; - var linez = firetable.utilities.wrapText(ctx, data.title, 66, 240, 160, 15); - firetable.debug && console.log("linez:", linez); - ctx.font = "400 8px Helvetica, Arial, sans-serif"; - ctx.textAlign = "left"; - firetable.utilities.wrapText(ctx, data.artist, 66, 253 + 15 * linez, 160, 15); - - ctx.fillStyle = data.colors.txt; - ctx.font = "400 9px Helvetica, Arial, sans-serif"; - ctx.textAlign = "center"; - ctx.fillText("Card No. " + data.cardnum + " | DJ Card | Max Operating Temp " + data.temp + "°", 112.5, 214); - ctx.beginPath(); - ctx.arc(205, 15, 12, 0, 2 * Math.PI, false); - ctx.fillStyle = data.colors.color; - ctx.fill(); - - ctx.fillStyle = data.colors.txt; - ctx.font = "700 15px Helvetica, Arial, sans-serif"; - ctx.textAlign = "left"; - ctx.fillText(data.num, 200.5, 20); - - var doImages = function () { - var picboy = new Image(); - picboy.xvalue = 0; - picboy.onload = function () { - ctx.drawImage(this, 20, 30, 175, 175); - var picboy2 = new Image(); - picboy2.xvalue = 0; - picboy2.onload = function () { - var heighta = 50; - if (data.image.match(/ytimg.com/g)) heighta = 28; - ctx.drawImage(this, 10, 230, 50, heighta); - ctx = null; - }; - picboy2.src = data.image; - }; - picboy.src = "https://indiediscotheque.com/robots/" + data.djid + data.djname + ".png?size=175x175"; - }; - - // special styles - - if (data.special) { - if (data.special == "id8") { - ctx.fillStyle = data.colors.color; - ctx.fillRect(1, 30, 223, 10); - - ctx.fillStyle = "#333333"; - // ctx.fillRect(1, 29, 223, 1); - // ctx.fillRect(1, 40, 223, 1); - - ctx.fillStyle = data.colors.txt; - ctx.font = "400 10px Helvetica, Arial, sans-serif"; - ctx.textAlign = "center"; - ctx.fillText("Celebrating 8 Years of Indie Discotheque", 112.5, 38); - - var cake = new Image(); - cake.xvalue = 0; - cake.onload = function () { - ctx.drawImage(this, 10, 50, 35, 35); - var eight = new Image(); - eight.xvalue = 0; - eight.onload = function () { - ctx.drawImage(this, 180, 50, 35, 35); - doImages(); - }; - eight.src = "img/8.png"; - }; - cake.src = "img/cake.png"; - } else if (data.special == "id9") { - ctx.fillStyle = data.colors.color; - ctx.fillRect(1, 30, 223, 10); - - ctx.fillStyle = "#333333"; - // ctx.fillRect(1, 29, 223, 1); - // ctx.fillRect(1, 40, 223, 1); - - ctx.fillStyle = data.colors.txt; - ctx.font = "400 10px Helvetica, Arial, sans-serif"; - ctx.textAlign = "center"; - ctx.fillText("Celebrating 9 Years of Indie Discotheque", 112.5, 38); - - var cake = new Image(); - cake.xvalue = 0; - cake.onload = function () { - ctx.drawImage(this, 5, 50, 45, 45); - var eight = new Image(); - eight.xvalue = 0; - eight.onload = function () { - ctx.save(); - ctx.translate(75 * 0.5, 75 * 0.5); - ctx.rotate(0.959931); - ctx.translate(-75 * 0.5, -75 * 0.5); - - ctx.drawImage(this, 125, -81, 75, 75); - ctx.restore(); - var picboy = new Image(); - picboy.xvalue = 0; - picboy.onload = function () { - ctx.drawImage(this, 25, 40, 170, 170); - var picboy2 = new Image(); - picboy2.xvalue = 0; - picboy2.onload = function () { - var heighta = 50; - if (data.image.match(/ytimg.com/g)) heighta = 28; - ctx.drawImage(this, 10, 230, 50, heighta); - ctx = null; - }; - picboy2.src = data.image; - }; - picboy.src = "img/id9.png"; - }; - eight.src = - "https://indiediscotheque.com/robots/" + data.djid + data.djname + ".png?size=110x110"; - }; - cake.src = "img/arnold.png"; - } - } else { - doImages(); - } - } - }, - showCard: function (cardid, chatid) { - // let's SHOW A CARD - ftapi.lookup.card(cardid, function (data) { - firetable.actions.displayCard(data, chatid); - }); - }, - filterQueue: function (val) { - if (val.length == 0) { - $("#mainqueue .pvbar").show(); - $("#mainqueue").removeClass("overFiltered"); - return; - } - val = val.toLowerCase(); - numresults = 0; - $("#mainqueue .pvbar") - .each(function (p, q) { - var txt = $(q).find(".listwords").text(); - var regex = new RegExp(val, "ig"); - if (txt.match(regex)) { - $(q).show(); - numresults++; - } else { - $(q).hide(); - } - }) - .promise() - .done(function () { - console.log(numresults); - if (!numresults) { - $("#mainqueue").addClass("overFiltered"); - } else { - $("#mainqueue").removeClass("overFiltered"); - } - }); - }, - muteToggle: function (zeroMute) { - var muted = localStorage["firetableMute"]; - var icon = ""; - firetable.debug && console.log("muted:", muted); - if (zeroMute) { - icon = ""; - muted = 0; - } else if (typeof muted !== "undefined") { - if (muted != "false") { - if (muted == 0) { - $("#slider").slider("value", 80); - player.setVolume(80); - firetable.scwidget.setVolume(80); - localStorage["firetableVol"] = 80; - } else { - muted = parseInt(muted); - $("#slider").slider("value", muted); - player.setVolume(muted); - firetable.scwidget.setVolume(muted); - localStorage["firetableVol"] = muted; - } - muted = false; - } else { - icon = ""; - - muted = $("#slider").slider("value"); - $("#slider").slider("value", 0); - player.setVolume(0); - firetable.scwidget.setVolume(0); - localStorage["firetableVol"] = 0; - } - } else { - icon = ""; - - muted = $("#slider").slider("value"); - $("#slider").slider("value", 0); - player.setVolume(0); - firetable.scwidget.setVolume(0); - localStorage["firetableVol"] = 0; - } - - if (muted) $("#volstatus").addClass("on"); - else $("#volstatus").removeClass("on"); - $("#volstatus i").html(icon); - localStorage["firetableMute"] = muted; - }, - pview: function (id, fromSearch, type, fromHist) { - if (firetable.preview == id) { - //already previewing this. stop and resume regular song - clearTimeout(firetable.ptimeout); - firetable.ptimeout = null; - $("#pv" + firetable.preview).html(""); - $("#pvbar" + firetable.preview).css("background-image", "none"); - clearInterval(firetable.movePvBar); - firetable.movePvBar = null; - firetable.preview = false; - //start regular song - var nownow = Date.now(); - var timeSince = nownow - firetable.song.started; - if (timeSince <= 0) timeSince = 0; - - var secSince = Math.floor(timeSince / 1000); - var timeLeft = firetable.song.duration - secSince; - if (firetable.song.type == 1) { - if (!firetable.preview) { - if (firetable.scLoaded) firetable.scwidget.pause(); - if (!firetable.disableMediaPlayback) player.loadVideoById(firetable.song.cid, secSince, "large"); - } - } else if (firetable.song.type == 2) { - if (!firetable.preview) { - if (firetable.ytLoaded) player.stopVideo(); - firetable.scSeek = timeSince; - if (!firetable.disableMediaPlayback) - firetable.scwidget.load("http://api.soundcloud.com/tracks/" + firetable.song.cid, { - auto_play: true, - }); - } - } - } else { - if (firetable.preview) { - $("#pv" + firetable.preview).html(""); - $("#pvbar" + firetable.preview).css("background-image", "none"); - } - - firetable.preview = id; - if (fromSearch) { - var cid = id.slice(5); - } else { - var cid = firetable.queue[id].cid; - } - - if (firetable.ptimeout != null) { - clearTimeout(firetable.ptimeout); - firetable.ptimeout = null; - } - if (firetable.movePvBar != null) { - clearInterval(firetable.movePvBar); - firetable.movePvBar = null; - } - firetable.pvCount = 0; - firetable.ptimeout = setTimeout(function () { - firetable.ptimeout = null; - $("#pv" + firetable.preview).html(""); - $("#pvbar" + firetable.preview).css("background-image", "none"); - clearInterval(firetable.movePvBar); - firetable.movePvBar = null; - firetable.pvCount = 0; - firetable.preview = false; - - //start regular song - var nownow = Date.now(); - var timeSince = nownow - firetable.song.started; - if (timeSince <= 0) timeSince = 0; - - var secSince = Math.floor(timeSince / 1000); - var timeLeft = firetable.song.duration - secSince; - if (firetable.song.type == 1) { - if (!firetable.preview) { - if (firetable.scLoaded) firetable.scwidget.pause(); - if (!firetable.disableMediaPlayback) - player.loadVideoById(firetable.song.cid, secSince, "large"); - } - } else if (firetable.song.type == 2) { - if (!firetable.preview) { - if (firetable.ytLoaded) player.stopVideo(); - firetable.scSeek = timeSince; - if (!firetable.disableMediaPlayback) - firetable.scwidget.load("http://api.soundcloud.com/tracks/" + firetable.song.cid, { - auto_play: true, - }); - } - } - }, 30 * 1000); - $("#pv" + id).html(""); - firetable.movePvBar = setInterval(function () { - var pcnt = (firetable.pvCount / 29) * 100; - firetable.pvCount += 0.2; - var pvcolr = "#222"; - if (fromHist) pvcolr = "#222"; - $("#pvbar" + firetable.preview).css( - "background-image", - "linear-gradient(90deg, rgba(244, 129, 11, 0.267) " + pcnt + "%, " + pvcolr + " " + pcnt + "%)" - ); - }, 200); - if (type == 1) { - if (firetable.scLoaded) firetable.scwidget.pause(); - if (!firetable.disableMediaPlayback) player.loadVideoById(cid, 0, "large"); - } else if (type == 2) { - if (firetable.ytLoaded) player.stopVideo(); - firetable.scSeek = 0; - if (!firetable.disableMediaPlayback) - firetable.scwidget.load("http://api.soundcloud.com/tracks/" + cid, { - auto_play: true, - }); - } - } - }, - mergeLists: function (source, dest, sourceName) { - if (source == dest) { - //source and dest are the same, let's remove the duplicates - firetable.actions.removeDupesFromQueue(); - return; - } - if (dest == -1) { - // create new list if needed - var newname = firetable.utilities.format_date(Date.now()) + " Copy of " + sourceName; - var dest = ftapi.actions.createList(newname); - $("#listpicker").append('"); - } - ftapi.actions.mergeLists(source, dest, function () { - $("#mergeCompleted").show(); - $("#mergeHappening").hide(); - }); - }, - queueFromLink: function (link) { - if (link.match(/youtube.com\/watch/)) { - //youtube - firetable.debug && console.log("yt"); - - function getQueryStringValue(str, key) { - return unescape( - str.replace( - new RegExp( - "^(?:.*[&\\?]" + escape(key).replace(/[\.\+\*]/g, "\\$&") + "(?:\\=([^&]*))?)?.*$", - "i" - ), - "$1" - ) - ); - } - var therealid = getQueryStringValue(link, "v"); - if (therealid) { - function keyWordsearch() { - gapi.client.setApiKey(ftconfigs.youtubeKey); - gapi.client.load("youtube", "v3", function () { - makeRequest(); - }); - } - - function makeRequest() { - var request = gapi.client.youtube.videos.list({ - id: therealid, - part: "snippet", - maxResults: 1, - }); - request.execute(function (response) { - firetable.debug && console.log("queue from link:", response); - if (response.result) { - if (response.result.items) { - if (response.result.items.length) { - var goodtitle = response.result.items[0].snippet.title; - var yargo = response.result.items[0].snippet.title.split(" - "); - var sartist = yargo[0]; - var stitle = yargo[1]; - if (!stitle) { - // yt title not formatted artist - title. use uploader name instead as artist - stitle = sartist; - sartist = response.result.items[0].snippet.channelTitle.replace(" - Topic", ""); - } - goodtitle = sartist + " - " + stitle; - firetable.actions.queueTrack(response.result.items[0].id, goodtitle, 1); - } - } - } - }); - } - keyWordsearch(); - } - } else if (link.match(/soundcloud.com/)) { - //soundcloud - firetable.debug && console.log("sc"); - var getComments = function (track) { - return SC.get("tracks/" + track.id); - }; - - firetable.actions.resolveSCLink(link, function (tracks) { - if (tracks) { - var yargo = tracks.title.split(" - "); - var sartist = yargo[0]; - var stitle = yargo[1]; - if (!stitle) { - stitle = sartist; - sartist = tracks.user.username; - } - var goodTitle = sartist + " - " + stitle; - firetable.actions.queueTrack(tracks.id, goodTitle, 2); - } - }); - //SC.resolve(link).then(getComments).then(listComments); - } - }, - resolveSCLink: function (link, callback) { - var importantStuff = link.replace("https://soundcloud.com/", ""); - importantStuff = importantStuff.replace("http://soundcloud.com/", ""); - $.ajax({ - url: "https://thompsn.com/resolvesc/?q=" + importantStuff, - type: "GET", - dataType: "json", - success: function (res) { - console.log(res); - callback(res.response); - }, - }); - }, - scGet: function (type, q, callback) { - $.ajax({ - url: "https://thompsn.com/soundcloud/?type=" + type + "&q=" + q, - type: "GET", - dataType: "json", - success: function (res) { - console.log(res); - callback(res.response); - }, - }); - }, - updateQueue: function () { - //this fires when someone drags a song to a new spot in the queue - var arr = $("#mainqueue > div") - .map(function () { - var theid = this.id; - var idraw = theid.slice(5); - return idraw; - }) - .get(); - ftapi.actions.reorderList(arr, firetable.preview, function (changePV) { - if (changePV) firetable.preview = changePV; - }); - }, - shuffleQueue: function () { - ftapi.actions.shuffleList(firetable.preview, function (changePV) { - if (changePV) firetable.preview = changePV; - }); - }, - removeDupesFromQueue: function () { - ftapi.actions.removeDuplicatesFromList(); - $("#mergeCompleted").show(); - $("#mergeHappening").hide(); - }, - editTagsPrompt: function (songid) { - var song = firetable.queue[songid]; - var $pvbar = $('#mainqueue .pvbar[data-key="' + songid + '"]'); - $("#mainqueue .pvbar.editing").removeClass("editing"); - $(".tagPromptBox").remove(); - $pvbar.addClass("editing"); - var $tags = $tagEditorTemplate.clone().appendTo($pvbar); - $tags.find(".tagMachine").val(song.name); - if (song.type == 1) { - $tags.find(".tagSongLink").attr("href", "https://youtube.com/watch?v=" + song.cid); - } else if (song.type == 2) { - firetable.actions.scGet("tracks", song.cid, function (tracks) { - if (tracks.permalink_url) { - $tags.find(".tagSongLink").attr("href", tracks.permalink_url); - } else { - $tags - .find(".tagSongLink") - .attr("href", "http://howtojointheindiediscothequewaitlist.com/ThisSongIsBroken?thanks=true"); - } - }); - } - - firetable.debug && console.log("edit tags song id:", songid); - firetable.songToEdit = { - song: song, - key: songid, - }; - }, - importList(id, name, type) { - //time to IMPORT SOME LISTS! - $("#overlay").removeClass("show"); - $("#importResults").html(""); - $("#plMachine").val(""); - if (type == 1) { - //youtube - var finalList = []; - - function keyWordsearch(pg) { - gapi.client.setApiKey(ftconfigs.youtubeKey); - gapi.client.load("youtube", "v3", function () { - makeRequest(pg); - }); - } - - function makeRequest(pg) { - if (pg) { - var request = gapi.client.youtube.playlistItems.list({ - playlistId: id, - maxResults: 50, - part: "snippet", - pageToken: pg, - }); - } else { - var request = gapi.client.youtube.playlistItems.list({ - playlistId: id, - maxResults: 50, - part: "snippet", - }); - } - request.execute(function (response) { - if (response.items.length) { - for (var idx = 0; idx < response.items.length; idx++) { - finalList.push(response.items[idx]); - } - } - if (response.nextPageToken) { - if (response.nextPageToken != "") keyWordsearch(response.nextPageToken); - } else { - firetable.debug && console.log(finalList); - var listid = ftapi.actions.createList(name); - $("#listpicker").append( - '" - ); - for (var i = 0; i < finalList.length; i++) { - var goodTitle = finalList[i].snippet.title; - // can't use youtube uploader name to fix tags here because YOUTUBE DECIDED NOT TO INCLUDE THAT INFORMATION >:o - if (goodTitle !== "Private video" && goodTitle !== "Deleted video") { - ftapi.actions.addToList(1, goodTitle, finalList[i].snippet.resourceId.videoId, listid); - } - } - } - }); - } - keyWordsearch(); - } else if (type == 2) { - firetable.actions.scGet("playlists", id, function (listinfo) { - firetable.debug && console.log("sc tracks:", listinfo.tracks); - var listid = ftapi.actions.createList(name); - $("#listpicker").append( - '" - ); - for (var i = 0; i < listinfo.tracks.length; i++) { - if (listinfo.tracks[i].title) { - var yargo = listinfo.tracks[i].title.split(" - "); - var sartist = yargo[0]; - var stitle = yargo[1]; - if (!stitle) { - stitle = sartist; - sartist = listinfo.tracks[i].user.username; - } - var goodTitle = sartist + " - " + stitle; - } else { - var goodTitle = "Unknown"; - } - - ftapi.actions.addToList(2, goodTitle, listinfo.tracks[i].id, listid); - } - }); - } - }, - bumpSongInQueue: function (songid) { - ftapi.actions.moveTrackToTop(songid, firetable.preview, function (changePV) { - if (changePV) firetable.preview = changePV; - }); - }, - signUp: function (email, password, username) { - firetable.debug && console.log("signup"); - ftapi.actions.signUp(email, password, username, function (error) { - alert(error); - }); - }, - deleteSong: function (id) { - ftapi.actions.deleteTrack(id); - }, - uidLookup: function (name) { - var match = false; - var usrs = ftapi.users; - for (var key in usrs) { - if (usrs.hasOwnProperty(key)) { - if (ftapi.users[key].username) { - if (ftapi.users[key].username == name) { - match = key; - } - } - } - } - if (!match && ftapi.users[name]) match = name; - return match; - }, - grab: function () { - if (firetable.song.cid != 0) { - var title = firetable.song.artist + " - " + firetable.song.title; - firetable.actions.queueTrack(firetable.song.cid, title, firetable.song.type, true); - } - }, - unban: function (userid) { - ftapi.actions.unbanUser(userid); - }, - reloadtrack: function () { - $("#reloadtrack").addClass("on working"); - //start regular song - var nownow = Date.now(); - var timeSince = nownow - firetable.song.started; - if (timeSince <= 0) timeSince = 0; - var secSince = Math.floor(timeSince / 1000); - var timeLeft = firetable.song.duration - secSince; - if (firetable.song.type == 1) { - if (!firetable.preview) { - if (firetable.scLoaded) firetable.scwidget.pause(); - if (!firetable.disableMediaPlayback) player.loadVideoById(firetable.song.cid, secSince, "large"); - } - } else if (firetable.song.type == 2) { - if (!firetable.preview) { - if (firetable.ytLoaded) player.stopVideo(); - firetable.scSeek = timeSince; - if (!firetable.disableMediaPlayback) - firetable.scwidget.load( - "http://api.soundcloud.com/tracks/" + firetable.song.cid, - { - auto_play: true, - }, - function () { - $("#reloadtrack").removeClass("on working"); - } - ); - } - } - }, - queueTrack: function (cid, name, type, tobottom) { - var info = { - type: type, - name: name, - cid: cid, - }; - $("#apv" + type + cid) - .find(".material-icons") - .text("check"); - $("#apv" + type + cid).css("color", firetable.orange); - $("#apv" + type + cid).css("pointer-events", "none"); - setTimeout(function () { - $("#apv" + type + cid) - .find(".material-icons") - .text("playlist_add"); - $("#apv" + type + cid).removeAttr("style"); - }, 3000); - - var cuteid = ftapi.actions.addToList(type, name, cid, false, function () { - firetable.debug && console.log("queue track id:", cuteid); - if (!tobottom) firetable.actions.bumpSongInQueue(cuteid); - }); - - if (firetable.preview) { - if (firetable.preview.slice(0, 5) == "ytcid" || firetable.preview.slice(0, 5) == "sccid") { - $("#pv" + firetable.preview).html(""); - clearTimeout(firetable.ptimeout); - firetable.ptimeout = null; - $("#pvbar" + firetable.preview).css("background-image", "none"); - clearInterval(firetable.movePvBar); - firetable.movePvBar = null; - firetable.preview = false; - //start regular song - var nownow = Date.now(); - var timeSince = nownow - firetable.song.started; - if (timeSince <= 0) timeSince = 0; - var secSince = Math.floor(timeSince / 1000); - var timeLeft = firetable.song.duration - secSince; - if (firetable.song.type == 1) { - if (!firetable.preview) { - if (firetable.scLoaded) firetable.scwidget.pause(); - if (!firetable.disableMediaPlayback) - player.loadVideoById(firetable.song.cid, secSince, "large"); - } - } else if (firetable.song.type == 2) { - if (!firetable.preview) { - if (firetable.ytLoaded) player.stopVideo(); - firetable.scSeek = timeSince; - if (!firetable.disableMediaPlayback) - firetable.scwidget.load("http://api.soundcloud.com/tracks/" + firetable.song.cid, { - auto_play: true, - }); - } - } - } - } - $("#mainqueuestuff").css("display", "block"); - $("#filterMachine").css("display", "block"); - $("#addbox").css("display", "none"); - $("#cancelqsearch").hide(); - $("#qControlButtons").show(); - }, -}; - -firetable.emojis = { - h: function () { - $(".pickerResult").show(); - $("#pickerResults h3").show(); - }, - n: function (p, q) { - var e = p.attr("data-alternative-name"); - return $(p).text().toLowerCase().indexOf(q) >= 0 || (e != null && e.toLowerCase().indexOf(q) >= 0); - }, - sec: function (sec) { - firetable.debug && console.log("emoji sec:", sec); - var selectedSec = $("#pickerNav > .on"); - var thething = sec.substr(1); - console.log(thething); - if (selectedSec.length) { - firetable.debug && console.log("already selected sec"); - if (selectedSec[0].id == sec) { - firetable.debug && console.log("toggle selected... back to FULL LIST"); - $("#" + selectedSec[0].id).removeClass("on"); - $("#pickerContents div").show(); - } else { - //new sec selected - $("#" + selectedSec[0].id).removeClass("on"); - $("#" + selectedSec[0].id.substr(1)).hide(); - $("#" + sec).addClass("on"); - $("#" + thething).show(); - } - } else { - firetable.debug && console.log("first select"); - $("#" + sec).addClass("on"); - $("#pickerContents div").hide(); - $("#" + thething).show(); - } - }, - niceSearch: function (val) { - if (val.length == 0) { - firetable.emojis.h(); - return; - } else { - var isvisible = $("#pickerResults h3").is(":visible"); - if (isvisible) $("#pickerResults h3").hide(); - } - val = val.toLowerCase(); - $(".pickerResult").each(function (p, q) { - if (firetable.emojis.n($(q), val)) { - $(q).show(); - } else { - $(q).hide(); - } - }); - // simplebar scroll update? - }, -}; - -firetable.utilities = { - getEmojiMap: function () { - firetable.emojiMap = {}; - (async function () { - urls = [ - "https://unpkg.com/unicode-emoji-json@0.3.0/data-by-group.json", - "https://unpkg.com/emojilib@2.4.0/emojis.json", - "https://unpkg.com/emojilib@3.0.4/dist/emoji-en-US.json", - ]; - try { - const requests = urls.map((url) => fetch(url)); - const responses = await Promise.all(requests); - const promises = responses.map((response) => response.json()); - const data = await Promise.all(promises); - let oldmojis = {}; - for (const [oldSlug, emojiObj] of Object.entries(data[1])) { - oldmojis[emojiObj.char] = oldSlug; - } - for (const [category, emojisArr] of Object.entries(data[0])) { - let catid = category.replace(/[\s&]+/g, "_").toLowerCase(); - $("#pickerNav").append( - '' + emojisArr[0].emoji + "" - ); - $("#pickerContents").append('

    ' + category + "

    "); - for (let i in emojisArr) { - firetable.emojiMap[emojisArr[i].slug] = emojisArr[i].emoji; - var words = ""; - words += data[2][emojisArr[i].emoji] !== undefined ? data[2][emojisArr[i].emoji].join(",") : ""; - words += oldmojis[emojisArr[i].emoji] !== undefined ? "," + oldmojis[emojisArr[i].emoji] : ""; - $("#picker" + catid).append( - '' + - emojisArr[i].emoji + - "" - ); - } - for (let i in oldmojis) { - firetable.emojiMap[oldmojis[i]] = i; - } - } - twemoji.parse(document.getElementById("pickerNav")); - } catch (err) {} - })(); - }, - hexToRGB: function (hex) { - var shorthandRegex = /^#?([a-f\d])([a-f\d])([a-f\d])$/i; - hex = hex.replace(shorthandRegex, function (m, r, g, b) { - return r + r + g + g + b + b; - }); - - var result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex); - return result - ? { - r: parseInt(result[1], 16), - g: parseInt(result[2], 16), - b: parseInt(result[3], 16), - } - : null; - }, - wrapText: function (context, text, x, y, maxWidth, lineHeight) { - var words = text.split(" "); - var line = ""; - var lines = 0; - - for (var n = 0; n < words.length; n++) { - var testLine = line + words[n] + " "; - var metrics = context.measureText(testLine); - var testWidth = metrics.width; - if (testWidth > maxWidth && n > 0) { - context.fillText(line, x, y); - line = words[n] + " "; - y += lineHeight; - lines++; - } else { - line = testLine; - } - } - context.fillText(line, x, y); - return lines; - }, - emojiShortnamestoUnicode: function (str) { - var res = str.replace(/\:(.*?)\:/g, function (x) { - var response = x; - var shortname = x.replace(/\:/g, ""); - if (firetable.emojiMap[shortname]) { - response = '' + firetable.emojiMap[shortname] + ""; - } else if (shortname == "rohn") { - response = ''; - } - return response; - }); - return res; - }, - playSound: function (filename) { - if (firetable.playBadoop) { - document.getElementById("audilert").setAttribute("src", filename + ".mp3"); - } - }, - desktopNotify: function (chatData, namebo) { - if (Notification) { - if (Notification.permission !== "granted") { - Notification.requestPermission(); - } else { - var notification = new Notification(namebo, { - icon: "https://indiediscotheque.com/robots/" + chatData.id + namebo + ".png?size=110x110", - body: chatData.txt, - }); - } - } - }, - screenUp: function () { - $("body").removeClass("screen"); - }, - screenDown: function () { - $("body").addClass("screen"); - }, - isChatPrettyMuchAtBottom: function () { - var scrollable = chatScroll.contentEl.scrollHeight - chatScroll.el.clientHeight; - var scrolled = chatScroll.contentWrapperEl.scrollTop; - firetable.debug && console.log("near bottom?", scrollable, scrolled); - return Math.abs(scrollable - scrolled) <= 25; - }, - scrollToBottom: function () { - chatScroll.contentWrapperEl.scrollTop = chatScroll.contentEl.scrollHeight; - }, - htmlEscape: function (s, preserveCR) { - preserveCR = preserveCR ? " " : "\n"; - return ( - ("" + s) /* Forces the conversion to string. */ - .replace(/&/g, "&") /* This MUST be the 1st replacement. */ - .replace(/'/g, "\\'") /* The 4 other predefined entities, required. */ - .replace(/"/g, """) - .replace(//g, ">") - /* - You may add other replacements here for HTML only - (but it's not necessary). - Or for XML, only if the named entities are defined in its DTD. - */ - .replace(/\r\n/g, preserveCR) /* Must be before the next replacement. */ - .replace(/[\r\n]/g, preserveCR) - ); - }, - format_date: function (d) { - var date = new Date(d); - - var month = date.getMonth() + 1; - var day = date.getDate(); - var year = date.getFullYear(); - - var formatted_date = month + "/" + day + "/" + year; - return formatted_date; - }, - format_time: function (d) { - var date = new Date(d); - - var hours1 = date.getHours(); - var ampm = "am"; - var hours = hours1; - if (hours1 >= 12) { - ampm = "pm"; - if (hours !== 12) hours = hours1 - 12; - } - if (hours == 0) hours = 12; - var minutes = date.getMinutes(); - var min = ""; - if (minutes > 9) { - min += minutes; - } else { - min += "0" + minutes; - } - return hours + ":" + min + "" + ampm; - }, - debounce(func, wait, immediate) { - var timeout; - return function () { - var context = this, - args = arguments; - var later = function () { - timeout = null; - if (!immediate) func.apply(context, args); - }; - var callNow = immediate && !timeout; - clearTimeout(timeout); - timeout = setTimeout(later, wait); - if (callNow) func.apply(context, args); - }; - }, - chatAt: function (element) { - element.bind("click", function () { - console.log(element); - var nameToAt; - if (element[0].className == "prson") { - nameToAt = $(this).find(".prsnName").text(); - } else if (element[0].className == "botson") { - nameToAt = $(this).next(".chatContent").find(".chatName").text(); - } else if (element[0].className == "chatName") { - nameToAt = $(this).text(); - } - $("#newchat") - .val(function (i, val) { - return val + "@" + nameToAt + " "; - }) - .focus(); - }); - }, - initAtLand: function () { - firetable.atLand = true; - firetable.atString = ""; - firetable.atUsers = ["everyone"]; - for (var user in ftapi.users) { - firetable.atUsers.push(ftapi.users[user].username); - } - firetable.atUsersFiltered = firetable.atUsers.sort(); - }, - updateAtLand: function () { - firetable.atUsersFiltered = firetable.atUsers - .filter((user) => user.toLowerCase().startsWith(firetable.atString.toLowerCase())) - .sort(); - $("#atPicker").html(""); - if (firetable.atUsersFiltered.length) { - for (var user of firetable.atUsersFiltered) { - $( - '
    " - ).appendTo("#atPicker"); - } - } else { - $('
    No users match
    ').appendTo("#atPicker"); - } - }, - chooseAt: function (atPeep) { - var $chatText = $("#newchat"); - if (firetable.atString.length > 0) $chatText.val($chatText.val().slice(0, firetable.atString.length * -1)); - $chatText.val($chatText.val() + atPeep + " "); - firetable.utilities.exitAtLand(); - }, - exitAtLand: function () { - firetable.atLand = false; - firetable.atUsersFiltered = []; - firetable.atString = ""; - $("#atPicker").removeClass("show").html(""); - }, -}; - -firetable.ui = { - textToLinks: function (text, themeBox) { - var re = /(\b(https?|ftp|file):\/\/[-A-Z0-9+&@#\/%?=~_|!:,.;]*[-A-Z0-9+&@#\/%=~_|])/gi; - if (firetable.showImages && !themeBox) re = /(https?:\/\/(?![/|.|\w|\s|-]*(?:jpe?g|png|gif))[^" ]+)/g; - return text.replace(re, '$1'); - - return text; - }, - dubtrackImportFileSelect: function (evt) { - var files = evt.target.files; // FileList object - var file = files[0]; - // read the file contents - var reader = new FileReader(); - reader.readAsText(file); - reader.onload = function (event) { - try { - var allthestuff = event.currentTarget.result; - console.log(allthestuff); - firetable.dtImportName = firetable.ui.strip(allthestuff.split("

    ")[1].split("

    ")[0]); - var hams = allthestuff.split('
  • (.*)<\/li>/gm; - var matches = thingsRegex.exec(hams[i]); - var type = matches[2]; - var cid = matches[4]; - var name = firetable.ui.strip(matches[5]); - firetable.dtImportList.push({ - type: type, - cid: cid, - name: name, - }); - } - console.log(firetable.dtImportList); - console.log(firetable.dtImportName); - if (firetable.dtImportList.length) { - $("#importDubResults").text( - "Ok... import " + firetable.dtImportName + " (" + firetable.dtImportList.length + " tracks)?" - ); - $("#dubimportButton").show(); - } else { - $("#importDubResults").text("ERROR... NO TRAX?"); - $("#dubimportButton").hide(); - } - } catch (e) { - console.log(e); - $("#importDubResults").text("ERROR"); - $("#dubimportButton").hide(); - } - }; - }, - strip: function (html) { - var doc = firetable.parser.parseFromString(html, "text/html"); - return doc.body.textContent || ""; - }, - hidePlayerControls: function () { - $("head").append( - "" - ); - }, - showPlayerControls: function () { - $(".playerControlsHider").remove(); - }, - showImages: function (chatTxt) { - if (firetable.showImages) { - var imageUrlRegex = /((http(s?):)([/|.|\w|\s|-])*\.(?:jpe?g|gif|png))/g; - var hasImage = chatTxt.search(imageUrlRegex) >= 0; - if (hasImage) { - chatTxt = chatTxt.replace(imageUrlRegex, function (imageUrl) { - var chatImage = new Image(); - chatImage.onload = function () { - if (firetable.utilities.isChatPrettyMuchAtBottom()) firetable.utilities.scrollToBottom(); - }; - chatImage.src = imageUrl; - return ( - '×' - ); - }); - } - } - return chatTxt; - }, - loginLinkToggle: function (id) { - $("#formlinks").find(".selected").removeClass("selected"); - $("#" + id).addClass("selected"); - }, - loginEventsInit: function () { - $("#resetpass").bind("click", function () { - $("#logscreen").css("display", "none"); - $("#createscreen").css("display", "none"); - $("#resetscreen").css("display", "block"); - firetable.ui.loginLinkToggle($(this).attr("id")); - }); - $("#loginlink").bind("click", function () { - $("#logscreen").css("display", "block"); - $("#createscreen").css("display", "none"); - $("#resetscreen").css("display", "none"); - firetable.ui.loginLinkToggle($(this).attr("id")); - }); - $("#signuplink").bind("click", function () { - $("#logscreen").css("display", "none"); - $("#createscreen").css("display", "block"); - $("#resetscreen").css("display", "none"); - firetable.ui.loginLinkToggle($(this).attr("id")); - }); - $("#loginpass").bind("keyup", function (e) { - if (e.which == 13) { - var email = $("#loginemail").val(); - var pass = $("#loginpass").val(); - $("#loginemail").val(""); - $("#loginpass").val(""); - firetable.actions.logIn(email, pass); - } - }); - $("#newpass2").bind("keyup", function (e) { - if (e.which == 13) { - var email = $("#newemail").val(); - var pass = $("#newpass").val(); - var pass2 = $("#newpass2").val(); - var username = $("#newusername").val(); - if (pass == pass2) { - firetable.actions.signUp(email, pass, username); - } else { - alert("Those passwords do not match!"); - } - } - }); - $("#theAddress").bind("keyup", function (e) { - if (e.which == 13) { - var email = $("#theAddress").val(); - firetable.debug && console.log("reset email return"); - ftapi.actions.resetPassword(email, function (error) { - var errorCode = error.code; - var errorMessage = error.message; - if (errorCode === "auth/wrong-password") { - alert("Wrong password."); - } else { - alert(errorMessage); - } - firetable.debug && console.log("send pass reset error:", error); - }); - alert("Reset email sent. Click the reset link when it arrives thanks."); - } - }); - $("#createAccountBttn").bind("click", function () { - var email = $("#newemail").val(); - var pass = $("#newpass").val(); - var pass2 = $("#newpass2").val(); - var termsAgreedTo = $("#agreetoterms").is(":checked"); - var username = $("#newusername").val(); - if (!termsAgreedTo) { - alert( - "You must read and agree to the Terms of Service and Privacy Policy before you can create an account." - ); - } else if (pass != pass2) { - alert("Those passwords do not match!"); - } else { - firetable.actions.signUp(email, pass, username); - } - }); - $("#resetPassBttn").bind("click", function () { - var email = $("#theAddress").val(); - firetable.debug && console.log("reset email click button"); - ftapi.actions.resetPassword(email, function (error) { - var errorCode = error.code; - var errorMessage = error.message; - if (errorCode === "auth/wrong-password") { - alert("Wrong password."); - } else { - alert(errorMessage); - } - firetable.debug && console.log("send pass reset error:", error); - }); - alert("Reset email sent. Click the reset link when it arrives thanks."); - }); - $("#loginBttn").bind("click", function () { - var email = $("#loginemail").val(); - var pass = $("#loginpass").val(); - $("#loginemail").val(""); - $("#loginpass").val(""); - firetable.actions.logIn(email, pass); - }); - }, - loginEventsDestroy: function () { - $("#resetpass").off("click"); - $("#loginlink").off("click"); - $("#signuplink").off("click"); - $("#loginpass").off("keyup"); - $("#newpass2").off("keyup"); - $("#theAddress").off("keyup"); - $("#createAccountBttn").off("click"); - $("#resetPassBttn").off("click"); - $("#loginBttn").off("click"); - }, - init: function () { - $("#mainqueue").sortable({ - axis: "y", - start: function (event, ui) { - var start_pos = ui.item.index(); - ui.item.data("start_pos", start_pos); - }, - change: function (event, ui) {}, - update: function (event, ui) { - firetable.debug && console.log("UPDATE"); - firetable.actions.updateQueue(); - }, - }); - //GET SETTINGS FROM LOCALSTORAGE - var disableMediaPlayback = localStorage["firetableDisableMedia"]; - if (typeof disableMediaPlayback == "undefined") { - localStorage["disableMediaPlayback"] = false; - firetable.disableMediaPlayback = false; - $("#mediaDisableToggle").prop("checked", false); - } else { - disableMediaPlayback = JSON.parse(disableMediaPlayback); - firetable.disableMediaPlayback = disableMediaPlayback; - $("#mediaDisableToggle").prop("checked", disableMediaPlayback); - if (disableMediaPlayback) { - firetable.ui.hidePlayerControls(); - } - } - - var showImages = localStorage["firetableShowImages"]; - if (typeof showImages == "undefined") { - localStorage["firetableShowImages"] = false; - firetable.showImages = false; - $("#showImagesToggle").prop("checked", false); - } else { - showImages = JSON.parse(showImages); - firetable.showImages = showImages; - $("#showImagesToggle").prop("checked", showImages); - } - var showAvatars = localStorage["firetableShowAvatars"]; - if (typeof showAvatars == "undefined") { - localStorage["firetableShowAvatars"] = true; - firetable.showAvatars = true; - $("#showAvatarsToggle").prop("checked", true); - } else { - showAvatars = JSON.parse(showAvatars); - firetable.showAvatars = showAvatars; - $("#showAvatarsToggle").prop("checked", showAvatars); - if (showAvatars == false) { - document.getElementById("actualChat").classList.add("avatarsOff"); - } - } - var playBadoop = localStorage["firetableBadoop"]; - if (typeof playBadoop == "undefined") { - localStorage["firetableBadoop"] = true; - firetable.playBadoop = true; - $("#badoopToggle").prop("checked", true); - } else { - playBadoop = JSON.parse(playBadoop); - firetable.playBadoop = playBadoop; - $("#badoopToggle").prop("checked", playBadoop); - } - var dtnmt = localStorage["firetableDTNM"]; - if (typeof dtnmt == "undefined") { - localStorage["firetableDTNM"] = false; - firetable.desktopNotifyMentions = false; - $("#desktopNotifyMentionsToggle").prop("checked", false); - } else { - dtnmt = JSON.parse(dtnmt); - firetable.desktopNotifyMentions = dtnmt; - $("#desktopNotifyMentionsToggle").prop("checked", dtnmt); - } - var screenControl = localStorage["firetableScreenControl"]; - if (typeof screenControl == "undefined") { - localStorage["firetableScreenControl"] = "sync"; - firetable.screenControl = "sync"; - $("#screenControlTog" + firetable.screenControl).prop("checked", true); - } else { - firetable.screenControl = screenControl; - $("#screenControlTog" + firetable.screenControl).prop("checked", true); - if (screenControl == "on") { - firetable.utilities.screenDown(); - } else if (screenControl == "off") { - firetable.utilities.screenUp(); - } else if (screenControl == "sync") { - if (firetable.screenSyncPos) { - firetable.utilities.screenDown(); - } else { - firetable.utilities.screenUp(); - } - } - } - var $historyItem = $("#thehistory .pvbar").remove(); - ftapi.events.on("newHistory", function (data) { - if (data.img == "img/idlogo.png" && ftconfigs.defaultAlbumArtUrl.length) - data.img = ftconfigs.defaultAlbumArtUrl; - var firstpart = "yt"; - if (data.type == 2) firstpart == "sc"; - var pkey = firstpart + "cid" + data.cid; - var $histItem = $historyItem.clone(); - $histItem.attr("id", "pvbar" + pkey); - $histItem.attr("data-key", pkey); - $histItem.attr("data-cid", data.cid); - $histItem.attr("data-type", data.type); - - $histItem - .find(".previewicon") - .attr("id", "pv" + pkey) - .on("click", function () { - firetable.actions.pview( - $(this).closest(".pvbar").attr("data-key"), - true, - $(this).closest(".pvbar").attr("data-type"), - true - ); - }); - $histItem - .find(".histlink") - .attr({ - href: data.url, - tabindex: "-1", - }) - .text(data.artist + " - " + data.title); - $histItem.find(".histdj").text(data.dj); - $histItem.find(".histdate").text(firetable.utilities.format_date(data.when)); - $histItem.find(".histtime").text(firetable.utilities.format_time(data.when)); - $histItem - .find(".histeal") - .attr("id", "apv" + data.type + data.cid) - .on("click", function () { - firetable.actions.queueTrack( - $(this).closest(".pvbar").attr("data-cid"), - firetable.utilities.htmlEscape($(this).closest(".pvbar").find(".histlink").text()), - $(this).closest(".pvbar").attr("data-type"), - true - ); - }); - $histItem.find(".histart").css("background-image", "url(" + data.img + ")"); - $histItem.prependTo("#thehistory"); - // simplebar scroll update? - }); - ftapi.events.on("newTheme", function (data) { - if (!data) { - //no theme - $("#currentTheme").text("!suggest a theme"); - } else { - var txtOut = firetable.ui.strip(data); - txtOut = firetable.ui.textToLinks(txtOut, true); - txtOut = firetable.utilities.emojiShortnamestoUnicode(txtOut); - txtOut = txtOut.replace(/\`(.*?)\`/g, function (x) { - return "" + x.replace(/\`/g, "") + ""; - }); - $("#currentTheme").html(txtOut); - twemoji.parse(document.getElementById("currentTheme")); - } - }); - ftapi.events.on("tagUpdate", function (data) { - firetable.debug && console.log("TAG UPDATE", data); - firetable.tagUpdate = data; - if (firetable.song) { - if (firetable.song.cid == data.cid && data.adamData.track_name) { - $("#track").text(firetable.ui.strip(data.adamData.track_name)); - $("#artist").text(firetable.ui.strip(data.adamData.artist)); - var nicename = firetable.song.djname; - var showPlaycount = false; - if (data.adamData.playcount) { - if (data.adamData.playcount > 0) { - showPlaycount = true; - } - } - if (data.adamData.last_play) { - $("#lastPlay").text( - "last " + - firetable.utilities.format_date(data.adamData.last_play) + - " by " + - data.adamData.last_play_dj - ); - } else { - $("#lastPlay").text(""); - } - if (data.adamData.first_play) { - $("#firstPlay").text( - "first " + - firetable.utilities.format_date(data.adamData.first_play) + - " by " + - data.adamData.first_play_dj - ); - } else { - $("#firstPlay").text(""); - } - var doTheScrollThing = firetable.utilities.isChatPrettyMuchAtBottom(); - if (showPlaycount) { - $("#playCount").text(data.adamData.playcount + " plays"); - $(".npmsg" + data.cid) - .last() - .html( - '
    DJ ' + - nicename + - " started playing " + - data.adamData.track_name + - " by " + - data.adamData.artist + - "
    This song has been played " + - data.adamData.playcount + - " times.
    " - ); - } else { - $("#playCount").text(""); - $(".npmsg" + data.cid) - .last() - .html( - '
    DJ ' + - nicename + - " started playing " + - data.adamData.track_name + - " by " + - data.adamData.artist + - "
    " - ); - } - if (doTheScrollThing) firetable.utilities.scrollToBottom(); - } - } - }); - - ftapi.events.on("newSong", function (data) { - $("#playCount").text(""); - $("#lastPlay").text(""); - $("#firstPlay").text(""); - window.dispatchEvent(new Event("resize")); - $("#cloud_with_rain").removeClass("on"); - $("#fire").removeClass("on"); - $("#timr").countdown("destroy"); - if (firetable.moveBar != null) { - clearInterval(firetable.moveBar); - firetable.moveBar = null; - } - if (data.image == "img/idlogo.png" && ftconfigs.defaultAlbumArtUrl.length) - data.image = ftconfigs.defaultAlbumArtUrl; - var showPlaycount = false; - if (firetable.tagUpdate) { - if (data.cid == firetable.tagUpdate.cid && firetable.tagUpdate.adamData.track_name) { - data.title = firetable.tagUpdate.adamData.track_name; - data.artist = firetable.tagUpdate.adamData.artist; - if (firetable.tagUpdate.adamData.last_play) { - $("#lastPlay").text( - "last " + - firetable.utilities.format_date(firetable.tagUpdate.adamData.last_play) + - " by " + - firetable.tagUpdate.adamData.last_play_dj - ); - } - if (firetable.tagUpdate.adamData.first_play) { - $("#firstPlay").text( - "first " + - firetable.utilities.format_date(firetable.tagUpdate.adamData.first_play) + - " by " + - firetable.tagUpdate.adamData.first_play_dj - ); - } - if (firetable.tagUpdate.adamData.playcount) { - if (firetable.tagUpdate.adamData.playcount > 0) { - showPlaycount = true; - $("#playCount").text(firetable.tagUpdate.adamData.playcount + " plays"); - } - } - } - } - $("#track").text(firetable.ui.strip(data.title)); - $("#artist").text(firetable.ui.strip(data.artist)); - $("#songlink").attr("href", data.url); - $("#albumArt").css("background-image", "url(" + data.image + ")"); - var nownow = Date.now(); - var timeSince = nownow - data.started; - if (timeSince <= 0) timeSince = 0; - var secSince = Math.floor(timeSince / 1000); - var timeLeft = data.duration - secSince; - firetable.song = data; - firetable.debug && console.log("NEW TRACK", data); - firetable.debug && console.log("time since:", timeSince); - if (data.type == 1) { - $("#scScreen").hide(); - $("#songlink").html( - '' - ); - } else if (data.type == 2) { - $("#scScreen").show(); - $("#songlink").html( - '' - ); - var biggerImg = data.image.replace("-large", "-t500x500"); - firetable.scImg = biggerImg; - $("#albumArt").css("background-image", "url(" + biggerImg + ")"); - try { - setup(biggerImg); - } catch (e) { - firetable.debug && console.log("big image error:", e); - } - } - if (data.type == 1 && firetable.ytLoaded) { - if (!firetable.preview) { - if (firetable.scLoaded) firetable.scwidget.pause(); - if (!firetable.disableMediaPlayback) player.loadVideoById(data.cid, secSince, "large"); - var thevolactual = $("#slider").slider("value"); - player.setVolume(thevolactual); - firetable.scwidget.setVolume(thevolactual); - } - } else if (data.type == 2 && firetable.scLoaded) { - if (!firetable.preview) { - if (firetable.ytLoaded) player.stopVideo(); - firetable.scSeek = timeSince; - if (!firetable.disableMediaPlayback) - firetable.scwidget.load("http://api.soundcloud.com/tracks/" + data.cid, { - auto_play: true, - single_active: false, - callback: function () { - var thevolactual = localStorage["firetableVol"]; - player.setVolume(thevolactual); - firetable.scwidget.setVolume(thevolactual); - }, - }); - } - } - if (data.cid != 0) { - var nicename = data.djid; - if (ftapi.users[data.djid]) { - if (ftapi.users[data.djid].username) nicename = ftapi.users[data.djid].username; - } - if (firetable.nonpmsg) { - firetable.nonpmsg = false; - } else { - var doTheScrollThing = firetable.utilities.isChatPrettyMuchAtBottom(); - if (showPlaycount) { - $("#chats").append( - '
    DJ ' + - nicename + - " started playing " + - data.title + - " by " + - data.artist + - "
    This song has been played " + - firetable.tagUpdate.adamData.playcount + - " times.
    " - ); - } else { - $("#chats").append( - '
    DJ ' + - nicename + - " started playing " + - data.title + - " by " + - data.artist + - "
    " - ); - } - if (doTheScrollThing) firetable.utilities.scrollToBottom(); - firetable.lastChatPerson = false; - firetable.lastChatId = false; - } - } - $("#timr").countdown({ - until: timeLeft, - compact: true, - description: "", - format: "MS", - }); - - $(".prgbar-style").remove(); - var now = Date.now(); - var sofar = parseInt((now - firetable.song.started) / 1000); - sofar = sofar < 0 ? 0 : sofar; - var pcnt = parseInt((sofar / firetable.song.duration) * 100); - var timeleft = firetable.song.duration - sofar; - $("head") - .delay(500) - .queue(function (next) { - $(this).append( - `` - ); - next(); - }); - }); - ftapi.events.on("screenStateChanged", function (data) { - firetable.debug && console.log("thescreen:", data); - firetable.screenSyncPos = data; - if (firetable.screenControl == "sync") { - if (data) { - firetable.utilities.screenDown(); - } else { - firetable.utilities.screenUp(); - } - } - }); - ftapi.events.on("danceStateChanged", function (data) { - firetable.debug && console.log("dance check:", data); - if (data) { - $("#deck").addClass("dance"); - } else { - $("#deck").removeClass("dance"); - } - }); - ftapi.events.on("lightsChanged", function (data) { - firetable.debug && console.log("lights check:", data); - if (data) { - firetable.lights = true; - $(".festiveLights").remove(); - var colorThing = firetable.utilities.hexToRGB(firetable.color); - var style = - ""; - $("head").append(style); - } else { - $(".festiveLights").remove(); - firetable.lights = false; - } - }); - ftapi.events.on("waitlistChanged", function (data) { - if (data) { - $("#allUsers .djOrder.waitlist").each(function () { - $(this).html("").removeClass("waitlist"); - var $prson = $(this).closest(".prson"); - switch (true) { - case $prson.hasClass("robot"): - $prson.remove().appendTo("#usersBot"); - break; - case $prson.hasClass("supermod"): - $prson.remove().appendTo("#usersSuper"); - break; - case $prson.hasClass("mod"): - $prson.remove().appendTo("#usersMod"); - break; - default: - $prson.remove().appendTo("#usersRegular"); - break; - } - }); - for (var key in data) { - var position = parseInt(key + 1); - firetable.debug && console.log("waitlist", position, data); - if (data.hasOwnProperty(key)) { - $("#user" + data[key].id) - .remove() - .appendTo("#usersWaitlist") - .find(".djOrder") - .html( - 'pending_actions ' + - position - ) - .removeClass("ondeck") - .addClass("waitlist"); - } - } - } - }); - ftapi.events.on("tableChanged", function (data) { - var ok1 = ""; - if (data) { - console.log("table changed", data); - var countr = 0; - $("#allUsers .djOrder.ondeck").each(function () { - $(this).html("").removeClass("ondeck"); - var $prson = $(this).closest(".prson"); - switch (true) { - case $prson.hasClass("robot"): - $prson.remove().appendTo("#usersBot"); - break; - case $prson.hasClass("supermod"): - $prson.remove().appendTo("#usersSuper"); - break; - case $prson.hasClass("mod"): - $prson.remove().appendTo("#usersMod"); - break; - default: - $prson.remove().appendTo("#usersRegular"); - break; - } - }); - for (var key in data) { - if (data.hasOwnProperty(key)) { - var removeMe = ""; - if (data[key].removeAfter) removeMe = "departure_board"; - - ok1 += - '
    ' + - removeMe + - " " + - data[key].name + - '
    ' + - data[key].plays + - '/' + - firetable.playlimit + - "
    "; - countr++; - $("#user" + data[key].id) - .remove() - .appendTo("#usersDJs") - .find(".djOrder") - .html('album ' + countr) - .removeClass("waitlist") - .addClass("ondeck"); - } - } - if (countr < 4) { - ok1 += - '
    !addme
    '; - countr++; - for (var i = countr; i < 4; i++) { - ok1 += '
     
    '; - } - } - } else { - ok1 += - '
    !addme
    '; - for (var i = 0; i < 3; i++) { - ok1 += '
     
    '; - } - } - $("#deck").html(ok1); - for (var i = 0; i < 4; i++) { - if (i != firetable.playdex) { - $("#avtr" + i).removeClass("animate"); - $("#djthing" + i).removeClass("djActive"); - } else { - $("#avtr" + i).addClass("animate"); - $("#djthing" + i).addClass("djActive"); - $(".djactive-style").remove(); - $("head") - .delay(500) - .queue(function (next) { - $(this).append( - `` - ); - next(); - }); - } - } - }); - ftapi.events.on("spotlightStateChanged", function (data) { - firetable.playdex = data; - for (var i = 0; i < 4; i++) { - if (i != data) { - $("#avtr" + i).removeClass("animate"); - $("#djthing" + i).removeClass("djActive"); - } else { - $("#avtr" + i).addClass("animate"); - $("#djthing" + i).addClass("djActive"); - $(".djactive-style").remove(); - $("head") - .delay(500) - .queue(function (next) { - $(this).append( - `` - ); - next(); - }); - } - } - }); - ftapi.events.on("playLimitChanged", function (data) { - firetable.playlimit = data; - for (var i = 0; i < 4; i++) { - $("#plimit" + i).text(data); - } - }); - ftapi.events.on("banListChanged", function (data) { - $("#activeSuspentions").html(""); - for (key in data) { - if (data[key]) { - ftapi.lookup.userByName(key, function (person) { - $("#activeSuspentions").append( - '
    ' + - person.username + - '
    ' - ); - }); - } - } - }); - var $userTemplate = $("#userKEY").remove(); - function userBits(data) { - if (!data.username) data.username = data.userid; - if (data.idle) { - if (data.idle.isIdle && !data.hostbot) - data.html.addClass("idle").find(".prsnStatus").text("nightlight"); - if (data.idle.audio == 2) data.html.addClass("idlething"); - } - if (data.blocked) { - data.html.addClass("blockd"); - } - - data.destination = "#usersRegular"; - data.rolename = ""; - data.rolecon = ""; - data.smallcon = true; - if (data.mod) { - data.rolename = "mod"; - data.rolecon = "star"; - data.smallcon = false; - data.destination = "#usersMod"; - } - if (data.supermod) { - data.rolename = "supermod"; - data.rolecon = "local_police"; - data.smallcon = false; - data.destination = "#usersSuper"; - } - if (data.hostbot) { - data.rolename = "robot"; - data.rolecon = "smart_toy"; - data.smallcon = false; - data.destination = "#usersBot"; - } - return data; - } - ftapi.events.on("userJoined", function (data) { - firetable.debug && console.log("user joined", data); - data.html = $userTemplate.clone(); - data = userBits(data); - data.html.attr("id", "user" + data.userid).addClass(data.rolename); - data.html - .find(".botson") - .css( - "background-image", - "url(https://indiediscotheque.com/robots/" + data.userid + data.username + ".png?size=110x110)" - ); - data.html.find(".prsnName").text(data.username); - data.html.find(".prsnRole").text(data.rolecon); - if (data.rolename) data.html.find(".prsnRole").prop("title", data.rolename); - if (data.smallcon) data.html.addClass("smallIcon"); - else data.html.removeClass("smallIcon"); - data.html.prop("title", "joined " + firetable.utilities.format_date(data.joined)); - firetable.utilities.chatAt(data.html); // adds the click event to @ the user - $(data.destination).append(data.html); - }); - ftapi.events.on("userLeft", function (data) { - $("#user" + data.userid).remove(); - }); - ftapi.events.on("userChanged", function (data) { - console.log("user changed", data); - data.html = $("#user" + data.userid); - data.html.removeClass("idle").find(".prsnStatus").text("lens"); - data.html.removeClass("blockd"); - data = userBits(data); - data.html - .find(".botson") - .css( - "background-image", - "url(https://indiediscotheque.com/robots/" + data.userid + data.username + ".png?size=110x110)" - ); - data.html.find(".prsnName").text(data.username); - data.html.find(".prsnRole").text(data.rolecon); - if (data.rolename) data.html.find(".prsnRole").prop("title", data.rolename); - if (data.smallcon) data.html.addClass("smallIcon"); - else data.html.removeClass("smallIcon"); - }); - ftapi.events.on("usersChanged", function (okdata) { - if ($("#loggedInUser .botson").data("uid") == ftapi.uid) { - if (ftapi.users[ftapi.uid]) { - if (ftapi.users[ftapi.uid].username) { - $("#loggedInUser .botson") - .css( - "background-image", - "url(https://indiediscotheque.com/robots/" + - ftapi.uid + - ftapi.users[ftapi.uid].username + - ".png?size=175x175)" - ) - .prop("title", "You're logged in as " + ftapi.users[ftapi.uid].username); - $("#loggedInUsername").text(ftapi.users[ftapi.uid].username); - } - } - } - if (ftapi.uid) { - if (ftapi.users[ftapi.uid]) { - if (ftapi.users[ftapi.uid].supermod) { - if ($("#ftSuperCopButton").is(":hidden")) { - $("#ftSuperCopButton").show(); - } - } - } - } - var count = Object.keys(okdata).length; - $("#numListeners, #numRoomListeners").text(count); - firetable.debug && console.log("users changed:", okdata); - }); - var $chatTemplate = $("#chatKEY").remove(); - ftapi.events.on("newChat", function (chatData) { - var namebo = chatData.id; - var utitle = ""; - - var atBottom = false; - if (firetable.utilities.isChatPrettyMuchAtBottom()) atBottom = true; - - var you = ftapi.uid; - if (ftapi.users[ftapi.uid]) { - if (ftapi.users[ftapi.uid].username) you = ftapi.users[ftapi.uid].username; - } - - if (ftapi.users[chatData.id]) { - if (ftapi.users[chatData.id].username) namebo = ftapi.users[chatData.id].username; - if (ftapi.users[chatData.id].mod) utitle = "stars"; - if (ftapi.users[chatData.id].supermod) utitle = "local_police"; - if (ftapi.users[chatData.id].hostbot) utitle = "smart_toy"; - } else if (chatData.name) { - namebo = chatData.name; - } - - var badoop = false; - if (chatData.txt.match("@" + you, "i") || chatData.txt.match(/\@everyone/)) { - var oknow = Date.now(); - if (oknow - chatData.time < 10 * 1000) { - firetable.utilities.playSound("sound"); - if (firetable.desktopNotifyMentions) firetable.utilities.desktopNotify(chatData, namebo); - badoop = true; - } - } - if (chatData.id == firetable.lastChatPerson && !badoop) { - $("#chat" + firetable.lastChatId + " .chatContent").append( - '
    ' - ); - $("#chatTime" + firetable.lastChatId).text(firetable.utilities.format_time(chatData.time)); - var txtOut = firetable.ui.strip(chatData.txt); - txtOut = firetable.ui.showImages(txtOut); - txtOut = firetable.ui.textToLinks(txtOut); - txtOut = firetable.utilities.emojiShortnamestoUnicode(txtOut); - txtOut = txtOut.replace(/\`(.*?)\`/g, function (x) { - return "" + x.replace(/\`/g, "") + ""; - }); - if (chatData.hidden) txtOut = "[message removed]"; - $("#chattxt" + chatData.chatID).html(txtOut); - var canBeDeleted = false; - if (ftapi.users[ftapi.uid].mod || ftapi.users[ftapi.uid].supermod) { - if (ftapi.users[chatData.id]) { - if (!ftapi.users[chatData.id].mod && !ftapi.users[chatData.id].supermod) { - canBeDeleted = true; - } - } else { - canBeDeleted = true; - } - if (canBeDeleted && !chatData.hidden) { - // add delete button - $("#chattxt" + chatData.chatID).addClass("deleteMe"); - $("#chattxt" + chatData.chatID).append('
    x
    '); - $("#chattxt" + chatData.chatID) - .find(".modDelete") - .on("click", function () { - console.log("DELETE CHAT", chatData); - ftapi.actions.deleteChat(chatData.feedID); - }); - } - } - twemoji.parse(document.getElementById("chattxt" + chatData.chatID)); - } else { - var $chatthing = $chatTemplate.clone(); - $chatthing.attr("id", "chat" + chatData.chatID); - $chatthing - .find(".botson") - .css( - "background-image", - "url(https://indiediscotheque.com/robots/" + chatData.id + namebo + ".png?size=110x110" - ); - $chatthing.find(".utitle").html(utitle); - $chatthing - .find(".chatTime") - .attr("id", "chatTime" + chatData.chatID) - .html(firetable.utilities.format_time(chatData.time)); - if (badoop) $chatthing.addClass("badoop"); - var txtOut = firetable.ui.strip(chatData.txt); - txtOut = firetable.ui.showImages(txtOut); - txtOut = firetable.ui.textToLinks(txtOut); - txtOut = firetable.utilities.emojiShortnamestoUnicode(txtOut); - txtOut = txtOut.replace(/\`(.*?)\`/g, function (x) { - return "" + x.replace(/\`/g, "") + ""; - }); - if (chatData.hidden) txtOut = "[message removed]"; - $chatthing - .find(".chatText") - .html(txtOut) - .attr("id", "chattxt" + chatData.chatID); - console.log(chatData); - - $chatthing.find(".chatName").text(namebo); - firetable.utilities.chatAt($chatthing.find(".botson")); // adds the click event to @ the user - firetable.utilities.chatAt($chatthing.find(".chatName")); // adds the click event to @ the user - twemoji.parse($chatthing.find(".chatText")[0]); - $chatthing.appendTo("#chats"); - try { - if (ftapi.users[ftapi.uid].mod || ftapi.users[ftapi.uid].supermod) { - var canBeDeleted = false; - if (ftapi.users[chatData.id]) { - if ( - !ftapi.users[chatData.id].mod && - !ftapi.users[chatData.id].supermod && - !chatData.hidden - ) { - canBeDeleted = true; - } - } else { - canBeDeleted = true; - } - if (canBeDeleted && !chatData.hidden) { - // add delete button - $chatthing.find(".chatText").addClass("deleteMe"); - $chatthing.find(".chatText").append('
    x
    '); - $chatthing.find(".modDelete").on("click", function () { - ftapi.actions.deleteChat(chatData.feedID); - }); - } - } - } catch (e) { - console.log(e); - } - firetable.lastChatPerson = chatData.id; - firetable.lastChatId = chatData.chatID; - } - - if (chatData.card) { - $("#chattxt" + chatData.chatID).append( - '' - ); - - firetable.actions.showCard(chatData.card, chatData.chatID); - firetable.debug && console.log("showin card"); - } - - if (atBottom || ftapi.uid == chatData.id) firetable.utilities.scrollToBottom(); - else $("#morechats").addClass("show"); - }); - - ftapi.events.on("chatRemoved", function (data) { - console.log("CHAT DELETED", data); - $("#chattxt" + data.chatID).text("[message removed]"); - if (ftapi.users[ftapi.uid].mod || ftapi.users[ftapi.uid].supermod) - $("#chattxt" + data.chatID).removeClass("deleteMe"); - }); - - ftapi.events.on("playlistChanged", function (okdata, listID) { - firetable.debug && console.log(okdata.length); - firetable.queue = okdata; - $("#mainqueue").html("").removeClass("emptyList overFiltered").addClass("loading"); - $("#queueFilterForm")[0].reset(); - if ($.isEmptyObject(okdata)) $("#mainqueue").addClass("emptyList"); - for (var key in okdata) { - if (okdata.hasOwnProperty(key)) { - var $newli = $playlistItemTemplate.clone(); - var thisone = okdata[key]; - var psign = ""; - if (key == firetable.preview) { - psign = ""; - } - $newli.attr("id", "pvbar" + key); - $newli.attr("data-key", key); - $newli.attr("data-type", thisone.type); - $newli - .find(".previewicon") - .attr("id", "pv" + key) - .on("click", function () { - firetable.actions.pview( - $(this).closest(".pvbar").attr("data-key"), - false, - $(this).closest(".pvbar").attr("data-type") - ); - }) - .html(psign); - $newli.find(".listwords").html(thisone.name); - $newli.find(".bumpsongs").on("click", function () { - firetable.actions.bumpSongInQueue($(this).closest(".pvbar").attr("data-key")); - }); - $newli.find(".bottomsongs").on("click", function () { - var oldID = $(this).closest(".pvbar").attr("data-key"); - ftapi.actions.moveTrackToBottom($(this).closest(".pvbar").attr("data-key"), function (newID) { - if (firetable.preview) { - // visually update preview in the new location if applicable - if (firetable.preview == oldID) { - firetable.preview = newID; - $("#pv" + newID).html(""); - } - } - }); - }); - if (thisone.flagged) { - var flagLabel = "broken"; - var flagIcon = "warning"; - if (thisone.flagged.code == 7) { - flagLabel = "age restricted"; - } else if (thisone.flagged.code >= 8) { - if (thisone.flagged.code == 8) { - // manual broken flagged by mod - flagLabel = "broken (manual)"; - } else if (thisone.flagged.code == 9) { - // low quality - flagLabel = "low audio quality"; - flagIcon = "disc_full"; - } else if (thisone.flagged.code == 10) { - // offtheme - flagLabel = "offtheme"; - flagIcon = "flag"; - } - } - $newli.find(".track-warning").html(' ' + flagIcon + " "); - $newli - .find(".track-warning") - .prop( - "title", - "Flagged as " + - flagLabel + - " on " + - firetable.utilities.format_date(thisone.flagged.date) + - ". Click to remove flag." - ); - $newli.find(".track-warning").on("click", function () { - ftapi.actions.unflagTrack($(this).closest(".pvbar").attr("data-key")); - $(this).html(""); - }); - } - $newli.find(".edittags").on("click", function () { - firetable.actions.editTagsPrompt($(this).closest(".pvbar").attr("data-key")); - }); - $newli.find(".deletesong").on("click", function () { - firetable.actions.deleteSong($(this).closest(".pvbar").attr("data-key")); - }); - $("#mainqueue").append($newli); - } - } - $("#mainqueue").removeClass("loading"); - }); - - firetable.ui.LinkGrabber.start(); - - $("#addToQueueBttn").bind("click", function () { - $("#mainqueuestuff").css("display", "none"); - $("#filterMachine").css("display", "none"); - $("#addbox").css("display", "flex"); - $("#cancelqsearch").show(); - $("#qControlButtons").hide(); - - $("#plmanager").css("display", "none"); - }); - - $("#minimodeoptions .tab").bind("click", function (event) { - $("#mainGrid").removeClass().addClass($(this).attr("id")); - $("#minimodeoptions .tab").removeClass("on"); - $(this).addClass("on"); - }); - - $("#plmaker").bind("keyup", function (e) { - if (e.which == 13) { - var val = $("#plmaker").val(); - if (val != "") { - var listid = ftapi.actions.createList(val); - $("#listpicker").append( - '" - ); - $("#listpicker").val(listid).change(); - ftapi.actions.switchList(listid); - } - } - }); - $("#cancelqsearch").bind("click", function () { - $("#mainqueuestuff").css("display", "block"); - $("#filterMachine").css("display", "block"); - $("#cancelqsearch").hide(); - $("#qControlButtons").show(); - - $("#addbox").css("display", "none"); - if (firetable.preview) { - if (firetable.preview.slice(0, 5) == "ytcid" || firetable.preview.slice(0, 5) == "sccid") { - $("#pv" + firetable.preview).html(""); - clearTimeout(firetable.ptimeout); - firetable.ptimeout = null; - $("#pvbar" + firetable.preview).css("background-image", "none"); - clearInterval(firetable.movePvBar); - firetable.movePvBar = null; - firetable.preview = false; - //start regular song - var nownow = Date.now(); - var timeSince = nownow - firetable.song.started; - if (timeSince <= 0) timeSince = 0; - - var secSince = Math.floor(timeSince / 1000); - var timeLeft = firetable.song.duration - secSince; - if (firetable.song.type == 1) { - if (!firetable.preview) { - if (firetable.scLoaded) firetable.scwidget.pause(); - if (!firetable.disableMediaPlayback) - player.loadVideoById(firetable.song.cid, secSince, "large"); - } - } else if (firetable.song.type == 2) { - if (!firetable.preview) { - if (firetable.ytLoaded) player.stopVideo(); - firetable.scSeek = timeSince; - if (!firetable.disableMediaPlayback) - firetable.scwidget.load("http://api.soundcloud.com/tracks/" + firetable.song.cid, { - auto_play: true, - }); - } - } - } - } - }); - - $("#grab").bind("click", function () { - var isHidden = $("#stealContain").is(":hidden"); - if (isHidden) { - ftapi.lookup.allLists(function (allPlaylists) { - $("#stealpicker").html( - '' - ); - for (var key in allPlaylists) { - if (allPlaylists.hasOwnProperty(key)) { - $("#stealpicker").append( - '" - ); - } - } - $("#grab").addClass("on"); - $("#stealContain") - .css({ - top: $("#grab").offset().top + $("#grab").height(), - left: $("#grab").offset().left - 16, - }) - .show(); - }); - } else { - $("#grab").removeClass("on"); - $("#stealContain").hide(); - } - }); - $("#shuffleQueue").bind("click", firetable.actions.shuffleQueue); - $("#history").bind("click", function () { - $("#thehistoryWrap") - .slideToggle() - .css("top", $("#djStage").outerHeight() + $("#topbar").outerHeight()); - $(this).toggleClass("on"); - }); - $("#startMerge").bind("click", function () { - var source = $("#mergepicker").val(); - var sourceName = $("#mergepicker option:selected").text(); - var dest = $("#mergepicker2").val(); - var destName = $("#mergepicker2 option:selected").text(); - $("#mergeSetup").hide(); - $("#mergeHappening").show(); - firetable.debug && console.log(sourceName + " -> " + destName); - firetable.actions.mergeLists(source, dest, sourceName); - }); - $("#mergeOK").bind("click", function () { - $("#mergeSetup").show(); - $("#mergeCompleted").hide(); - $("#mergeHappening").hide(); - $("#mergeContain").hide(); - }); - $("#mergeLists").bind("click", function () { - var $this = $(this); - var isHidden = $("#mergeContain").is(":hidden"); - if (isHidden) { - ftapi.lookup.allLists(function (allPlaylists) { - $("#mergepicker").html(''); - $("#mergepicker2").html( - '' - ); - for (var key in allPlaylists) { - if (allPlaylists.hasOwnProperty(key)) { - $("#mergepicker").append( - '" - ); - $("#mergepicker2").append( - '" - ); - } - } - if (ftapi.users[ftapi.uid]) { - if (ftapi.users[ftapi.uid].selectedList) { - $("#mergepicker").val(ftapi.users[ftapi.uid].selectedList).change(); - $("#mergepicker2").val(-1).change(); - } - } - $("#mergeContain").show(); - $this.addClass("on"); - }); - } else { - $("#mergeContain").hide(); - $this.removeClass("on"); - } - }); - $("#reloadtrack").bind("click", firetable.actions.reloadtrack); - - $("#importDubGo").bind("click", firetable.actions.dubtrackImport); - - $("#volstatus").bind("click", function () { - firetable.actions.muteToggle(); - }); - $(".openModal").bind("click", function () { - var modalContentID = $(this).attr("data-modal"); - $(".modalThing").removeClass("show"); - $("#overlay").addClass("show"); - $("#" + modalContentID).addClass("show"); - }); - $(".closeModal").bind("click", function () { - $("#overlay").removeClass("show"); - $("#deletepicker").html(""); - firetable.actions.cardCase(); - $("#plMachine").val(""); - }); - $(document).on("click", ".closeeditor", function () { - $(this).closest(".pvbar").removeClass("editing").find(".tagPromptBox").remove(); - firetable.songToEdit = null; - }); - $("#cardCaseButton").bind("click", function () { - firetable.actions.cardCase(); - $("#cardsOverlay").show(); - }); - $("#pickerNav").on("click", "span", function () { - try { - var sec = $(this)[0].id; - firetable.emojis.sec(sec); - } catch (s) {} - }); - $("#pickEmoji").bind("click", function () { - //toggle emoji picker - if ($("#emojiPicker").is(":hidden")) { - $(this).addClass("on"); - $("#emojiPicker").slideDown(function () { - $("#pickerSearch").focus(); - }); - - if (!firetable.pickerInit) { - const makeRequest = async () => { - twemoji.parse(document.getElementById("pickerResults")); - return true; - }; - - makeRequest(); - } - } else { - $(this).removeClass("on"); - $("#emojiPicker").slideUp(function () { - $("#pickerSearch").val("").trigger("change"); - $("#newchat").focus(); - }); - } - }); - - $("#morechats .butt").bind("click", function () { - firetable.utilities.scrollToBottom(); - }); - - $("#fire").bind("click", function () { - ftapi.actions.sendChat(":fire:"); - $("#cloud_with_rain").removeClass("on"); - $("#fire").addClass("on"); - }); - - $("#cloud_with_rain").bind("click", function () { - ftapi.actions.sendChat(":cloud_with_rain:"); - $("#cloud_with_rain").addClass("on"); - $("#fire").removeClass("on"); - }); - - //SETTINGS TOGGLES - $("#badoopToggle").change(function () { - if (this.checked) { - firetable.debug && console.log("badoop on"); - localStorage["firetableBadoop"] = true; - firetable.playBadoop = true; - } else { - firetable.debug && console.log("badoop off"); - localStorage["firetableBadoop"] = false; - firetable.playBadoop = false; - } - }); - $("#showImagesToggle").change(function () { - if (this.checked) { - firetable.debug && console.log("show images on"); - localStorage["firetableShowImages"] = true; - firetable.showImages = true; - } else { - firetable.debug && console.log("show images off"); - localStorage["firetableShowImages"] = false; - firetable.showImages = false; - } - }); - $("#mediaDisableToggle").change(function () { - if (this.checked) { - firetable.debug && console.log("media disable on"); - localStorage["firetableDisableMedia"] = true; - firetable.disableMediaPlayback = true; - if (firetable.scLoaded) firetable.scwidget.pause(); - if (firetable.ytLoaded) player.stopVideo(); - firetable.ui.hidePlayerControls(); - } else { - firetable.debug && console.log("media disable off"); - localStorage["firetableDisableMedia"] = false; - firetable.disableMediaPlayback = false; - firetable.ui.showPlayerControls(); - firetable.actions.reloadtrack(); - } - }); - $("#showAvatarsToggle").change(function () { - if (this.checked) { - firetable.debug && console.log("show avatars on"); - localStorage["firetableShowAvatars"] = true; - firetable.showAvatars = true; - document.getElementById("actualChat").classList.remove("avatarsOff"); - } else { - firetable.debug && console.log("show avatars off"); - localStorage["firetableShowAvatars"] = false; - firetable.showAvatars = false; - document.getElementById("actualChat").classList.add("avatarsOff"); - } - }); - $(document).on("click", ".hideImage", function (e) { - e.stopPropagation(); - e.preventDefault(); - $(this).closest(".chatText").toggleClass("hideImg"); - }); - $("#desktopNotifyMentionsToggle").change(function () { - if (this.checked) { - firetable.debug && console.log("dtnm on"); - localStorage["firetableDTNM"] = true; - firetable.desktopNotifyMentions = true; - if (Notification) { - if (Notification.permission !== "granted") { - Notification.requestPermission(); - } - } - } else { - firetable.debug && console.log("dtnm off"); - localStorage["firetableDTNM"] = false; - firetable.desktopNotifyMentions = false; - } - }); - - $("input[type=radio][name=screenControl]").change(function () { - firetable.debug && console.log("screen control:", this.value); - localStorage["firetableScreenControl"] = this.value; - firetable.screenControl = this.value; - if (this.value == "off") { - firetable.utilities.screenUp(); - } else if (this.value == "on") { - firetable.utilities.screenDown(); - } else if (this.value == "sync") { - if (firetable.screenSyncPos) { - firetable.utilities.screenDown(); - } else { - firetable.utilities.screenUp(); - } - } - }); - - $("#stealpicker").change(function () { - var dest = $("#stealpicker").val(); - if (dest == "-1") return; - if (firetable.song.cid != 0) { - var title = firetable.song.artist + " - " + firetable.song.title; - $("#grab").removeClass("on"); - ftapi.actions.addToList(firetable.song.type, title, firetable.song.cid, dest); - $("#stealContain").hide(); - } - }); - - $("#pldeleteButton").bind("click", function () { - var val = $("#deletepicker").val(); - firetable.debug && console.log("playlist delete:", val); - if (ftapi.users[ftapi.uid]) { - if (ftapi.users[ftapi.uid].selectedList) { - if (ftapi.users[ftapi.uid].selectedList == val) { - $("#listpicker").val("0").change(); - } - } - } - ftapi.actions.deleteList(val); - $("#pdopt" + val).remove(); - $("#overlay").removeClass("show"); - }); - $("#plimportLauncher").bind("click", function () { - $("#overlay").addClass("show"); - $(".modalThing").removeClass("show"); - $("#importPromptBox").addClass("show"); - }); - $("#pldeleteLauncher").bind("click", function () { - ftapi.lookup.allLists(function (allPlaylists) { - $("#deletepicker").html(""); - for (var key in allPlaylists) { - if (allPlaylists.hasOwnProperty(key)) { - $("#deletepicker").append( - '" - ); - } - } - $("#overlay").addClass("show"); - $(".modalThing").removeClass("show"); - $("#deletePromptBox").addClass("show"); - }); - }); - $("#pickerSearch").on("change paste keyup", function () { - firetable.emojis.niceSearch($("#pickerSearch").val()); - }); - $("#queueFilter").on("change paste keyup", function () { - firetable.actions.filterQueue($("#queueFilter").val()); - }); - $("#pickerResults").on("click", "span", function () { - try { - var oldval = $("#newchat").val(); - var newval = oldval + ":" + $(this).attr("title").trim() + ":"; - $("#newchat").focus().val(newval); - } catch (s) {} - }); - - firetable.ui.loginEventsInit(); - - $("#ytsearchSelect").bind("click", function () { - $("#scsearchSelect").removeClass("on"); - $("#ytsearchSelect").addClass("on"); - firetable.searchSelectsChoice = 1; - }); - $("#scsearchSelect").bind("click", function () { - $("#ytsearchSelect").removeClass("on"); - $("#scsearchSelect").addClass("on"); - firetable.searchSelectsChoice = 2; - }); - $("#ytimportchoice").bind("click", function () { - firetable.debug && console.log("yt import"); - firetable.importSelectsChoice = 1; - }); - $("#scimportchoice").bind("click", function () { - firetable.debug && console.log("sc import"); - firetable.importSelectsChoice = 2; - }); - $("#dtimportchoice").bind("click", function () { - firetable.debug && console.log("dt import"); - firetable.importSelectsChoice = 3; - }); - $(document).on("keyup", ".tagMachine", function (e) { - if (e.which == 13) { - var songKey = $(this).closest(".tagPromptBox").prev(".pvbar").attr("data-key"); - if (firetable.songToEdit) { - var val = $(this).val(); - if (val != "") { - var obj = firetable.songToEdit; - ftapi.actions.editTrackTag(obj.key, obj.song.cid, val); - firetable.songToEdit = null; - $(this).closest(".editing").removeClass("editing").next(".tagPromptBox").remove(); - } - } - } - }); - $("#changeUsername").bind("keyup", function (e) { - if (e.which == 13) { - var oldDjName = ftapi.users[ftapi.uid].username; - var newDjName = $("#changeUsername").val(); - $("#usernameResponse").html(""); - if (newDjName != "") { - // try to change name - ftapi.actions.changeName(newDjName, function (error) { - if (error) { - alert(error); - $("#usernameResponse").text(error); - } else { - $("#usernameResponse").text("Great job! Your name is now " + newDjName); - $("#loggedInUser .botson") - .css( - "background-image", - "url(https://indiediscotheque.com/robots/" + - ftapi.uid + - newDjName + - ".png?size=175x175)" - ) - .prop("title", "You're logged in as " + ftapi.users[ftapi.uid].username); - $("#loggedInUsername").text(ftapi.users[ftapi.uid].username); - } - }); - } - } - }); - $("#dubtrackimportfile").bind("change", firetable.ui.dubtrackImportFileSelect); - $("#supercopSearch").bind("keyup", function (e) { - if (e.which == 13) { - var val = $("#supercopSearch").val(); - $("#supercopResponse").html(""); - if (val != "") { - //begin user search... - ftapi.lookup.userByName(val, function (person) { - //check search results - if (person) { - //found something! - if (!person.supermod) { - ftapi.actions.banUser(person.userid); - $("#supercopResponse").html(person.username + " suspended."); - } else { - $("#supercopResponse").text("Can not suspend that (or any) supermod."); - } - } else { - $("#supercopResponse").text(val + " not found..."); - } - }); - } - } - }); - $("#importSources .tab").bind("click", function (e) { - var searchFrom = firetable.importSelectsChoice; - if (searchFrom == 3) { - $("#importDubContent").show(); - $("#importContent").hide(); - } else { - $("#importDubContent").hide(); - $("#importContent").show(); - } - $(this).siblings().removeClass("on"); - $(this).addClass("on"); - }); - $("#plMachine").bind("keyup", function (e) { - if (e.which == 13) { - var val = $("#plMachine").val(); - if (val != "") { - $("#importResults").html(""); - $("#plMachine").val(""); - var searchFrom = firetable.importSelectsChoice; - if (searchFrom == 1) { - var listID; - var directLink = false; - //see if this is a particular list's url... - if (val.match(/youtube.com\/watch/) || val.match(/youtube.com\/playlist/)) { - function getQueryStringValue(str, key) { - return unescape( - str.replace( - new RegExp( - "^(?:.*[&\\?]" + - escape(key).replace(/[\.\+\*]/g, "\\$&") + - "(?:\\=([^&]*))?)?.*$", - "i" - ), - "$1" - ) - ); - } - listID = getQueryStringValue(val, "list"); - if (listID) directLink = true; - } - - if (directLink) { - function keyWordsearch() { - gapi.client.setApiKey(ftconfigs.youtubeKey); - gapi.client.load("youtube", "v3", function () { - makeRequest(); - }); - } - - function makeRequest() { - var request = gapi.client.youtube.playlists.list({ - id: listID, - part: "snippet", - }); - request.execute(function (response) { - if (response.result) { - if (response.result.items) { - if (response.result.items.length === 1) { - var item = response.result.items[0]; - vidTitle = item.snippet.title; - $("#importResults").append( - '
    ' + - item.snippet.title + - " by " + - item.snippet.channelTitle + - '
    ' - ); - } else { - // no result - } - } - } - }); - } - keyWordsearch(); - } else { - //youtube - function keyWordsearch() { - gapi.client.setApiKey(ftconfigs.youtubeKey); - gapi.client.load("youtube", "v3", function () { - makeRequest(); - }); - } - - function makeRequest() { - var request = gapi.client.youtube.search.list({ - q: val, - type: "playlist", - part: "snippet", - maxResults: 15, - }); - request.execute(function (response) { - var srchItems = response.result.items; - firetable.debug && console.log("import search results:", response); - $.each(srchItems, function (index, item) { - vidTitle = item.snippet.title; - $("#importResults").append( - '
    ' + - item.snippet.title + - " by " + - item.snippet.channelTitle + - '
    ' - ); - }); - }); - } - keyWordsearch(); - } - } else if (searchFrom == 2) { - var listData; - var directLink = false; - //see if this is a particular list's url... - console.log(val); - if (val.match(/.*\/\/soundcloud\.com\/.*\/sets\/.*/)) { - firetable.actions.resolveSCLink(val, function (item) { - if (item) { - if (item.sharing == "public" && item.kind == "playlist") { - $("#importResults").append( - '
    ' + - item.title + - " by " + - item.user.username + - " (" + - item.track_count + - ' songs)
    ' - ); - } - } - }); - //var getList = SC.resolve(val).then(finishUp); - } else { - //cloud sound world dot com - SC.get("/playlists", { - q: val, - }).then(function (lists) { - for (var i = 0; i < lists.length; i++) { - var item = lists[i]; - if (item.sharing == "public") { - $("#importResults").append( - '
    ' + - item.title + - " by " + - item.user.username + - " (" + - item.track_count + - ' songs)
    ' - ); - } - } - }); - } - } - } - } - }); - var $searchItemTemplate = $("#searchResults .pvbar").remove(); - $("#qsearch").bind("keyup", function (e) { - if (e.which == 13) { - var txt = $("#qsearch").val(); - if (firetable.searchSelectsChoice == 1) { - var showResults = function (response) { - firetable.debug && console.log("queue search:", response); - // $("#qsearch").val(""); - $("#searchResults").html(""); - - if (firetable.preview) { - if (firetable.preview.slice(0, 5) == "ytcid" || firetable.preview.slice(0, 5) == "sccid") { - $("#pv" + firetable.preview).html(""); - clearTimeout(firetable.ptimeout); - firetable.ptimeout = null; - $("#pvbar" + firetable.preview).css("background-image", "none"); - clearInterval(firetable.movePvBar); - firetable.movePvBar = null; - firetable.preview = false; - //start regular song - var nownow = Date.now(); - var timeSince = nownow - firetable.song.started; - if (timeSince <= 0) timeSince = 0; - - var secSince = Math.floor(timeSince / 1000); - var timeLeft = firetable.song.duration - secSince; - if (firetable.song.type == 1) { - if (!firetable.preview) { - if (firetable.scLoaded) firetable.scwidget.pause(); - if (!firetable.disableMediaPlayback) - player.loadVideoById(firetable.song.cid, secSince, "large"); - } - } else if (firetable.song.type == 2) { - if (!firetable.preview) { - if (firetable.ytLoaded) player.stopVideo(); - firetable.scSeek = timeSince; - if (!firetable.disableMediaPlayback) - firetable.scwidget.load( - "http://api.soundcloud.com/tracks/" + firetable.song.cid, - { - auto_play: true, - } - ); - } - } - } - } - var srchItems = response.result.items; - $.each(srchItems, function (index, item) { - console.log(item); - var thecid; - if (item.kind == "youtube#searchResult") { - thecid = item.id.videoId; - } else if (item.kind == "youtube#video") { - thecid = item.id; - } - vidtitle = item.snippet.title; - var yargo = item.snippet.title.split(" - "); - var sartist = yargo[0]; - var stitle = yargo[1]; - if (!stitle) { - // yt title not formatted artist - title. use uploader name instead as artist - stitle = sartist; - sartist = item.snippet.channelTitle.replace(" - Topic", ""); - } - vidTitle = sartist + " - " + stitle; - var pkey = "ytcid" + thecid; - var $srli = $searchItemTemplate.clone(); - $srli.attr("id", "pvbar" + pkey); - $srli.attr("data-key", pkey); - $srli.attr("data-cid", thecid); - $srli - .find(".previewicon") - .attr("id", "pv" + pkey) - .on("click", function () { - firetable.actions.pview($(this).closest(".pvbar").attr("data-key"), true, 1); - }); - $srli.find(".listwords").html(vidTitle); - $srli.find(".queuetrack").on("click", function () { - firetable.actions.queueTrack( - $(this).closest(".pvbar").attr("data-cid"), - firetable.utilities.htmlEscape($(this).closest(".pvbar").find(".listwords").text()), - 1 - ); - }); - $("#searchResults").append($srli); - }); - }; - var directLink = false; - var thecid = false; - //see if this is a particular track's url... - if (txt.match(/youtube.com\/watch/)) { - function getQueryStringValue(str, key) { - return unescape( - str.replace( - new RegExp( - "^(?:.*[&\\?]" + - escape(key).replace(/[\.\+\*]/g, "\\$&") + - "(?:\\=([^&]*))?)?.*$", - "i" - ), - "$1" - ) - ); - } - thecid = getQueryStringValue(txt, "v"); - if (thecid) directLink = true; - } - if (directLink) { - firetable.debug && console.log("direct yt link found"); - - function keyWordsearch() { - gapi.client.setApiKey(ftconfigs.youtubeKey); - gapi.client.load("youtube", "v3", function () { - makeRequest(); - }); - } - - function makeRequest() { - var request = gapi.client.youtube.videos.list({ - id: thecid, - part: "snippet", - maxResults: 1, - }); - request.execute(function (response) { - console.log(response); - showResults(response); - }); - } - keyWordsearch(); - } else { - function keyWordsearch() { - gapi.client.setApiKey(ftconfigs.youtubeKey); - gapi.client.load("youtube", "v3", function () { - makeRequest(); - }); - } - - function makeRequest() { - var q = $("#qsearch").val(); - $("#searchResults").html("Searching..."); - - var request = gapi.client.youtube.search.list({ - q: q, - type: "video", - part: "snippet", - maxResults: 15, - }); - request.execute(function (response) { - showResults(response); - }); - } - keyWordsearch(); - } - } else if (firetable.searchSelectsChoice == 2) { - var q = $("#qsearch").val(); - var showResults = function (tracks) { - firetable.debug && console.log("sc tracks:", tracks); - // $("#qsearch").val(""); - $("#searchResults").html(""); - - if (firetable.preview) { - if (firetable.preview.slice(0, 5) == "ytcid" || firetable.preview.slice(0, 5) == "sccid") { - $("#pv" + firetable.preview).html(""); - clearTimeout(firetable.ptimeout); - firetable.ptimeout = null; - $("#pvbar" + firetable.preview).css("background-image", "none"); - clearInterval(firetable.movePvBar); - firetable.movePvBar = null; - firetable.preview = false; - //start regular song - var nownow = Date.now(); - var timeSince = nownow - firetable.song.started; - if (timeSince <= 0) timeSince = 0; - - var secSince = Math.floor(timeSince / 1000); - var timeLeft = firetable.song.duration - secSince; - if (firetable.song.type == 1) { - if (!firetable.preview) { - firetable.scwidget.pause(); - if (!firetable.disableMediaPlayback) - player.loadVideoById(firetable.song.cid, secSince, "large"); - } - } else if (firetable.song.type == 2) { - if (!firetable.preview) { - if (firetable.ytLoaded) player.stopVideo(); - firetable.scSeek = timeSince; - if (!firetable.disableMediaPlayback) - firetable.scwidget.load( - "http://api.soundcloud.com/tracks/" + firetable.song.cid, - { - auto_play: true, - } - ); - } - } - } - } - var srchItems = tracks; - $.each(srchItems, function (index, item) { - vidTitle = item.title; - var yargo = item.title.split(" - "); - var sartist = yargo[0]; - var stitle = yargo[1]; - if (!stitle) { - stitle = sartist; - sartist = item.user.username; - } - vidTitle = sartist + " - " + stitle; - var pkey = "sccid" + item.id; - var $srli = $searchItemTemplate.clone(); - $srli.attr("id", "pvbar" + pkey); - $srli.attr("data-key", pkey); - $srli.attr("data-cid", item.id); - $srli - .find(".previewicon") - .attr("id", "pv" + pkey) - .on("click", function () { - firetable.actions.pview($(this).closest(".pvbar").attr("data-key"), true, 2); - }); - $srli.find(".listwords").html(vidTitle); - $srli.find(".queuetrack").on("click", function () { - firetable.actions.queueTrack( - $(this).closest(".pvbar").attr("data-cid"), - firetable.utilities.htmlEscape($(this).closest(".pvbar").find(".listwords").text()), - 2 - ); - }); - $("#searchResults").append($srli); - }); - }; - var directLink = false; - if (q.match(/:\/\/soundcloud\.com\//)) { - directLink = true; - } - $("#searchResults").html("Searching..."); - if (directLink) { - firetable.debug && console.log("sc direct link found"); - firetable.actions.resolveSCLink(q, function (item) { - var items = []; - if (item.kind == "track") items.push(item); - showResults(items); - }); - //var getList = SC.resolve(q).then(finishUp); - } else { - SC.get("/tracks", { - q: q, - }).then(function (tracks) { - showResults(tracks); - }); - } - } - } - }); - $("#newchat").bind("keypress", function (e) { - firetable.debug && console.log("chat key", e.key); - if (e.key == "Enter") { - var txt = $("#newchat").val(); - if (txt == "") return; - var matches = txt.match(/^(?:[\/])(\w+)\s*(.*)/i); - if (txt == ":fire:" || txt == "🔥") { - $("#cloud_with_rain").removeClass("on"); - $("#fire").addClass("on"); - } else if (txt == ":cloud_with_rain:" || txt == "🌧") { - $("#cloud_with_rain").addClass("on"); - $("#fire").removeClass("on"); - } - if (matches) { - var command = matches[1].toLowerCase(); - var args = matches[2]; - if (command == "mod") { - var personToMod = firetable.actions.uidLookup(args); - if (personToMod) { - ftapi.actions.modUser(personToMod); - } - } else if (command == "unmod") { - var personToMod = firetable.actions.uidLookup(args); - if (personToMod) { - ftapi.actions.unmodUser(personToMod); - } - } else if (command == "block") { - if (args) { - ftapi.actions.blockUser(args, function (response) { - console.log(response); - firetable.actions.localChatResponse(response); - }); - } - } else if (command == "unblock") { - if (args) { - ftapi.actions.unblockUser(args, function (response) { - console.log(response); - - firetable.actions.localChatResponse(response); - }); - } - } else if (command == "hot") { - ftapi.actions.sendChat(":fire:"); - $("#cloud_with_rain").removeClass("on"); - $("#fire").addClass("on"); - } else if (command == "storm") { - ftapi.actions.sendChat(":cloud_with_rain:"); - $("#cloud_with_rain").addClass("on"); - $("#fire").removeClass("on"); - } else if (command == "shrug") { - var thingtosay = "¯\\_(ツ)_/¯"; - if (args) thingtosay = args + " ¯\\_(ツ)_/¯"; - ftapi.actions.sendChat(thingtosay); - } else if (command == "tableflip") { - var thingtosay = "(╯°□°)╯︵ ┻━┻"; - if (args) thingtosay = args + " (╯°□°)╯︵ ┻━┻"; - ftapi.actions.sendChat(thingtosay); - } else if (command == "unflip") { - var thingtosay = "┬─┬ ノ( ゜-゜ノ)"; - if (args) thingtosay = args + " ┬─┬ ノ( ゜-゜ノ)"; - ftapi.actions.sendChat(thingtosay); - } - } else { - ftapi.actions.sendChat(txt); - } - $("#newchat").val(""); - $("#emojiPicker").slideUp(); - $("#pickEmoji").removeClass("on"); - firetable.utilities.exitAtLand(); - } else if (e.key == "@") { - // open the door to @ land - if (firetable.atLand) { - // double @@ - firetable.utilities.exitAtLand(); - } else { - // first step into @ land - firetable.utilities.initAtLand(); - for (var user of firetable.atUsersFiltered) { - $("#atPicker").addClass("show"); - $( - '
    " - ).appendTo("#atPicker"); - } - } - } else if (firetable.atLand) { - // we're in @ land - if (e.key == " " || e.key == "Spacebar") { - // we've got what we want - firetable.utilities.exitAtLand(); - } else if (!e.key.match(/[0-9a-zA-Z_]/)) { - // not possibly a characer from a name - firetable.atString += e.key; - $("#atPicker").html(""); - $('
    Usernames cannot contain "' + e.key + '"
    ').appendTo( - "#atPicker" - ); - } else { - // we're still in @ land - firetable.atString += e.key; - firetable.utilities.updateAtLand(); - } - } - }); - $("#newchat").bind("keyup", function (e) { - if (firetable.atLand) { - // we're in @ land - if (e.key == "Backspace") { - if (!firetable.atString) { - // deleting the @, exit @ land - firetable.utilities.exitAtLand(); - } else { - // still got someone we're lookin for - firetable.atString = firetable.atString.slice(0, -1); - firetable.utilities.updateAtLand(); - } - } else if (e.key == "ArrowUp") { - // i see my @, go up! - $("#atPicker .butt:last").focus(); - } else if (e.key == "ArrowDown") { - // i see my @, go down! - $("#atPicker .butt:first").focus(); - } - } - }); - $("#newchat").bind("keydown", function (e) { - if (e.key == "Tab") { - if (firetable.atUsersFiltered.length === 1) { - $("#newchat").one("blur", function (e) { - $("#newchat").focus().val($("#newchat").val()); - }); - firetable.utilities.chooseAt(firetable.atUsersFiltered[0]); - } else { - firetable.utilities.exitAtLand(); - } - } - }); - $(document).on("click", "#atPicker .butt", function (e) { - e.preventDefault(); - firetable.utilities.chooseAt($(this).text().replace("@", "")); - setTimeout(() => { - var tempText = $("#newchat").val(); - $("#newchat").focus().val(""); - $("#newchat").val(tempText); - }, 250); - }); - $(document).on("keyup", "#atPicker .butt:focus", function (e) { - if (e.key == "ArrowUp") { - if ($("#atPicker .butt:focus").parent().prev().length) { - $("#atPicker .butt:focus").parent().prev().find(".butt").focus(); - } else { - $("#atPicker .butt:last").focus(); - } - } else if (e.key == "ArrowDown") { - if ($("#atPicker .butt:focus").parent().next().length) { - $("#atPicker .butt:focus").parent().next().find(".butt").focus(); - } else { - $("#atPicker .butt:first").focus(); - } - } - }); - - ftapi.events.on("colorsChanged", function (data) { - firetable.debug && console.log("COLOR CHANGE!", data); - - firetable.color = data.color; - //firetable.countcolor = data.txt; - if (data.color == "#fff" || data.color == "#7f7f7f") { - firetable.color = firetable.orange; - firetable.countcolor = "#fff"; - $("#djStage").css("background-color", "#ccc"); - } else { - $("#djStage").css("background-color", data.color); - } - /* - if (firetable.countcolor == "#fff"){ - firetable.countcolor = "#ffffffc9"; - } else if (firetable.countcolor == "#000"){ - firetable.debug && console.log("a") - firetable.countcolor = "#000000c9"; - } - $("#djStage").css("color", firetable.countcolor); - */ - $(".customColorStyles").remove(); - - $(".festiveLights").remove(); - - var colorThing = firetable.utilities.hexToRGB(firetable.color); - $("head").append( - "" - ); - - if (firetable.lights) { - var style = - ""; - $("head").append(style); - } - }); - }, - LinkGrabber: { - textarea: null, - - /* Textarea Management */ - - attach_ta: function (event) { - if (!$.contains(document.getElementById("queuelist"), event.target)) return; - if (firetable.ui.LinkGrabber.textarea != null) return; - - var textarea = (firetable.ui.LinkGrabber.textarea = document.createElement("textarea")); - textarea.setAttribute( - "style", - "position: fixed; width: 100%; margin: 0; top: 0; bottom: 0; right: 0; left: 0; z-index: 99999999" - ); - textarea.style.opacity = "0.000000000000000001"; - - var body = document.getElementsByTagName("body")[0]; - body.appendChild(textarea); - - textarea.oninput = firetable.ui.LinkGrabber.evt_got_link; - }, - - detach_ta: function () { - if (firetable.ui.LinkGrabber.textarea == null) return; - var textarea = firetable.ui.LinkGrabber.textarea; - - textarea.parentNode.removeChild(textarea); - firetable.ui.LinkGrabber.textarea = null; - }, - - /* Event Handlers */ - - evt_drag_over: function (event) { - firetable.ui.LinkGrabber.attach_ta(event); //Create TA overlay - }, - - evt_got_link: function () { - /* THIS IS WHERE WE HANDLE THE RECEIVED LINK */ - - var link = firetable.ui.LinkGrabber.textarea.value; - firetable.debug && console.log("NEW LINK RECEIVED VIA THE DRAGON'S DROP. " + link); - firetable.actions.queueFromLink(link); - - firetable.ui.LinkGrabber.detach_ta(); - }, - - evt_drag_out: function (e) { - if (e.target == firetable.ui.LinkGrabber.textarea) firetable.ui.LinkGrabber.detach_ta(); - }, - - /* Start/Stop */ - - start: function () { - document.addEventListener("dragover", firetable.ui.LinkGrabber.evt_drag_over, false); - document.addEventListener("dragenter", firetable.ui.LinkGrabber.evt_drag_over, false); - - document.addEventListener("mouseup", firetable.ui.LinkGrabber.evt_drag_out, false); - document.addEventListener("dragleave", firetable.ui.LinkGrabber.evt_drag_out, false); - }, - - stop: function () { - document.removeEventListener("dragover", firetable.ui.LinkGrabber.evt_drag_over); - document.removeEventListener("dragenter", firetable.ui.LinkGrabber.evt_drag_over); - - document.removeEventListener("mouseup", firetable.ui.LinkGrabber.evt_drag_out); - document.removeEventListener("dragleave", firetable.ui.LinkGrabber.evt_drag_out); - - firetable.ui.LinkGrabber.detach_ta(); - }, - }, -}; - -let isLoaded = false; -let glitch; -let imgSrc = ""; - -function setup(useThis) { - if (!useThis) useThis = firetable.scImg; - background(0); - let cnv = createCanvas($("#djStage").outerWidth(), $("#djStage").outerHeight()); - cnv.parent("scScreen"); - loadImage(useThis, function (img) { - glitch = new Glitch(img); - isLoaded = true; - var $can = $("#scScreen canvas"); - var canrat = $can.width() / $can.height(); - $can.data("ratio", canrat); - }); -} - -function draw() { - clear(); - background(0); - - if (isLoaded) { - glitch.show(); - } - - // fill(255, 255, 255); - // textSize(14); - // text('FPS: ' + floor(frameRate()), 20, 30); -} - -class Glitch { - constructor(img) { - this.channelLen = 4; - this.imgOrigin = img; - this.imgOrigin.loadPixels(); - this.copyData = []; - this.flowLineImgs = []; - this.shiftLineImgs = []; - this.shiftRGBs = []; - this.scatImgs = []; - this.throughFlag = true; - this.copyData = new Uint8ClampedArray(this.imgOrigin.pixels); - - // flow line - for (let i = 0; i < 1; i++) { - let o = { - pixels: null, - t1: floor(random(0, 1000)), - speed: floor(random(4, 24)), - randX: floor(random(24, 80)), - }; - this.flowLineImgs.push(o); - } - - // shift line - for (let i = 0; i < 6; i++) { - let o = null; - this.shiftLineImgs.push(o); - } - - // shift RGB - for (let i = 0; i < 1; i++) { - let o = null; - this.shiftRGBs.push(o); - } - - // scat imgs - for (let i = 0; i < 3; i++) { - let scatImg = { - img: null, - x: 0, - y: 0, - }; - this.scatImgs.push(scatImg); - } - } - - replaceData(destImg, srcPixels) { - for (let y = 0; y < destImg.height; y++) { - for (let x = 0; x < destImg.width; x++) { - let r, g, b, a; - let index; - index = (y * destImg.width + x) * this.channelLen; - r = index; - g = index + 1; - b = index + 2; - a = index + 3; - destImg.pixels[r] = srcPixels[r]; - destImg.pixels[g] = srcPixels[g]; - destImg.pixels[b] = srcPixels[b]; - destImg.pixels[a] = srcPixels[a]; - } - } - destImg.updatePixels(); - } - - flowLine(srcImg, obj) { - let destPixels, tempY; - destPixels = new Uint8ClampedArray(srcImg.pixels); - obj.t1 %= srcImg.height; - obj.t1 += obj.speed; - //tempY = floor(noise(obj.t1) * srcImg.height); - tempY = floor(obj.t1); - for (let y = 0; y < srcImg.height; y++) { - if (tempY === y) { - for (let x = 0; x < srcImg.width; x++) { - let r, g, b, a; - let index; - index = (y * srcImg.width + x) * this.channelLen; - r = index; - g = index + 1; - b = index + 2; - a = index + 3; - destPixels[r] = srcImg.pixels[r] + obj.randX; - destPixels[g] = srcImg.pixels[g] + obj.randX; - destPixels[b] = srcImg.pixels[b] + obj.randX; - destPixels[a] = srcImg.pixels[a]; - } - } - } - return destPixels; - } - - shiftLine(srcImg) { - let offsetX; - let rangeMin, rangeMax; - let destPixels; - let rangeH; - - destPixels = new Uint8ClampedArray(srcImg.pixels); - rangeH = srcImg.height; - rangeMin = floor(random(0, rangeH)); - rangeMax = rangeMin + floor(random(1, rangeH - rangeMin)); - offsetX = this.channelLen * floor(random(-40, 40)); - - for (let y = 0; y < srcImg.height; y++) { - if (y > rangeMin && y < rangeMax) { - for (let x = 0; x < srcImg.width; x++) { - let r, g, b, a; - let r2, g2, b2, a2; - let index; - - index = (y * srcImg.width + x) * this.channelLen; - r = index; - g = index + 1; - b = index + 2; - a = index + 3; - r2 = r + offsetX; - g2 = g + offsetX; - b2 = b + offsetX; - destPixels[r] = srcImg.pixels[r2]; - destPixels[g] = srcImg.pixels[g2]; - destPixels[b] = srcImg.pixels[b2]; - destPixels[a] = srcImg.pixels[a]; - } - } - } - return destPixels; - } - - shiftRGB(srcImg) { - let randR, randG, randB; - let destPixels; - let range; - - range = 16; - destPixels = new Uint8ClampedArray(srcImg.pixels); - randR = (floor(random(-range, range)) * srcImg.width + floor(random(-range, range))) * this.channelLen; - randG = (floor(random(-range, range)) * srcImg.width + floor(random(-range, range))) * this.channelLen; - randB = (floor(random(-range, range)) * srcImg.width + floor(random(-range, range))) * this.channelLen; - - for (let y = 0; y < srcImg.height; y++) { - for (let x = 0; x < srcImg.width; x++) { - let r, g, b, a; - let r2, g2, b2, a2; - let index; - - index = (y * srcImg.width + x) * this.channelLen; - r = index; - g = index + 1; - b = index + 2; - a = index + 3; - r2 = (r + randR) % srcImg.pixels.length; - g2 = (g + randG) % srcImg.pixels.length; - b2 = (b + randB) % srcImg.pixels.length; - destPixels[r] = srcImg.pixels[r2]; - destPixels[g] = srcImg.pixels[g2]; - destPixels[b] = srcImg.pixels[b2]; - destPixels[a] = srcImg.pixels[a]; - } - } - - return destPixels; - } - - getRandomRectImg(srcImg) { - let startX; - let startY; - let rectW; - let rectH; - let destImg; - startX = floor(random(0, srcImg.width - 30)); - startY = floor(random(0, srcImg.height - 50)); - rectW = floor(random(30, srcImg.width - startX)); - rectH = floor(random(1, 50)); - destImg = srcImg.get(startX, startY, rectW, rectH); - destImg.loadPixels(); - return destImg; - } - - show() { - // restore the original state - this.replaceData(this.imgOrigin, this.copyData); - - // sometimes pass without effect processing - let n = floor(random(100)); - if (n > 75 && this.throughFlag) { - this.throughFlag = false; - setTimeout(() => { - this.throughFlag = true; - }, floor(random(200, 1500))); - } - if (!this.throughFlag) { - push(); - translate((width - this.imgOrigin.width) / 2, (height - this.imgOrigin.height) / 2); - image(this.imgOrigin, 0, 0); - pop(); - return; - } - - // flow line - this.flowLineImgs.forEach((v, i, arr) => { - arr[i].pixels = this.flowLine(this.imgOrigin, v); - if (arr[i].pixels) { - this.replaceData(this.imgOrigin, arr[i].pixels); - } - }); - - // shift line - this.shiftLineImgs.forEach((v, i, arr) => { - if (floor(random(100)) > 50) { - arr[i] = this.shiftLine(this.imgOrigin); - this.replaceData(this.imgOrigin, arr[i]); - } else { - if (arr[i]) { - this.replaceData(this.imgOrigin, arr[i]); - } - } - }); - - // shift rgb - this.shiftRGBs.forEach((v, i, arr) => { - if (floor(random(100)) > 65) { - arr[i] = this.shiftRGB(this.imgOrigin); - this.replaceData(this.imgOrigin, arr[i]); - } - }); - - push(); - translate((width - this.imgOrigin.width) / 2, (height - this.imgOrigin.height) / 2); - image(this.imgOrigin, 0, 0); - pop(); - - // scat image - this.scatImgs.forEach((obj) => { - push(); - translate((width - this.imgOrigin.width) / 2, (height - this.imgOrigin.height) / 2); - if (floor(random(100)) > 80) { - obj.x = floor(random(-this.imgOrigin.width * 0.3, this.imgOrigin.width * 0.7)); - obj.y = floor(random(-this.imgOrigin.height * 0.1, this.imgOrigin.height)); - obj.img = this.getRandomRectImg(this.imgOrigin); - } - if (obj.img) { - image(obj.img, obj.x, obj.y); - } - pop(); - }); - } -} - -if (!firetable.started) firetable.init(); diff --git a/js/main.original.js b/js/main.original.js new file mode 100644 index 0000000..d2d342a --- /dev/null +++ b/js/main.original.js @@ -0,0 +1,4171 @@ +var firetable = { + started: false, + loggedIn: false, + uid: null, + uname: null, + avatarset: "set1", + pvCount: 0, + playdex: 0, + users: {}, + queue: false, + preview: false, + movePvBar: null, + moveBar: null, + song: null, + playBadoop: true, + idle: false, + idleChanged: null, + sbhowImages: false, + screenControl: "sync", + lights: false, + screenSyncPos: false, + scSeek: false, + desktopNotifyMentions: false, + orange: "#F4810B", + color: "#F4810B", + countcolor: "#fff", + ytLoaded: null, + scLoaded: null, + listShowing: null, + parser: null, + songToEdit: null, + scwidget: null, + searchSelectsChoice: 1, + importSelectsChoice: 1, + dtImportName: null, + dtImportList: [], + lastChatPerson: false, + lastChatId: false, + tagUpdate: null, + nonpmsg: true, + playlimit: 2, + scImg: "", + superCopBanUpdates: null, + loginForm: null, + emojiMap: null, + pickerInit: false, + atLand: false, + atUsers: [], + atUsersFiltered: [], + atString: "", + debug: false +}; + +if (typeof ftconfigs == "undefined") throw "config.js is missing! Copy config.js.example and rename to config.js. Edit this file and add your own app's information."; + +var chatScroll = new SimpleBar(document.getElementById('chatsWrap')); +chatScroll.getScrollElement().addEventListener('scroll', function() { + if (firetable.utilities.isChatPrettyMuchAtBottom()) $('#morechats').removeClass('show'); +}); + +firetable.version = "01.10.8"; + +var player, $playlistItemTemplate; + +var idlejs = new IdleJs({ + idle: 5 * 60000, + events: ['mousemove', 'keydown', 'mousedown', 'touchstart'], + onIdle: function() { + ftapi.actions.changeIdleStatus(true, 1); + }, + onActive: function() { + ftapi.actions.changeIdleStatus(false, 1); + }, + onHide: function() { + ftapi.actions.changeIdleStatus(true, 1); + firetable.debug && console.log("hide"); + }, + onShow: function() { + ftapi.actions.changeIdleStatus(false, 1); + }, + keepTracking: true, + startAtIdle: false +}); +idlejs.start(); + +function onYouTubeIframeAPIReady() { + player = new YT.Player('playerArea', { + width: $('#djStage').outerHeight() * 1.7777, + height: $('#djStage').outerHeight(), + playerVars: { + 'autoplay': 1, + 'controls': 0 + }, + videoId: '5mGuCdlCcNM', + events: { + onReady: initialize, + onStateChange: function() { + $('#reloadtrack').removeClass('on working'); + } + } + }); +} + +function initialize(event) { + firetable.ytLoaded = true; + var vol = localStorage["firetableVol"]; + if (typeof vol == "undefined") { + vol = 80; + localStorage["firetableVol"] = 80; + } + player.setVolume(vol); + + var muted = localStorage["firetableMute"]; + if (typeof muted == "undefined") { + localStorage["firetableMute"] = false; + muted = "false"; + $("#volstatus").removeClass('on'); + } + + if (muted != "false") { + var icon = ""; + $("#volstatus i").html(icon); + $("#volstatus").addClass('on'); + } + + $("#slider").slider({ + orientation: "horizontal", + range: "min", + min: 0, + max: 100, + value: vol, + step: 5, + slide: function(event, ui) { + player.setVolume(ui.value); + firetable.scwidget.setVolume(ui.value); + localStorage["firetableVol"] = ui.value; + var muted = localStorage["firetableMute"]; + if (muted != "false") { + localStorage["firetableMute"] = false; + var icon = ""; + $("#volstatus i").html(icon); + $("#volstatus").removeClass('on'); + } else if (ui.value == 0) { + firetable.actions.muteToggle(true); + $("#volstatus").addClass('on'); + } + } + }); + if (firetable.song) { + var data = firetable.song; + var nownow = Date.now(); + var timeSince = nownow - data.started; + if (timeSince <= 0) timeSince = 0; + + var secSince = Math.floor(timeSince / 1000); + var timeLeft = data.duration - secSince; + if (data.type == 1) { + if (!firetable.preview) { + if (!firetable.disableMediaPlayback) player.loadVideoById(data.cid, secSince, "large") + } + } + } + + +} + +function onPlayerStateChange(event) { + //state changed thanks +} + +firetable.init = function() { + console.log(` + ( ) ) ( + )\\ ) ( ( ( ( /( ) ( /( )\\ ( +(()/( )\\ )( ))\\ )\\()) ( /( )\\()) (_)) ))\\ + /(_)) ((_) (()\\ /((_) (_))/ )(_)) ((_)\\ | | /((_) +(_) _| (_) ((_) (_)) | |_ ((_)_ | |(_) | | (_)) + | _| | | | '_| / -_) | _| / _' | | '_ \\ | | / -_) + |_| |_| |_| \\___| \\__| \\__,_| |_.__/ |_| \\___| +`); + firetable.started = true; + + $("#idtitle").text(ftconfigs.roomName); + $("#welcomeName").text(ftconfigs.roomName); + + if (ftconfigs.avatarset) firetable.avatarset = ftconfigs.avatarset; + + if (ftconfigs.facebookURL) { + $(".sociallogo.facebook").attr("href", ftconfigs.facebookURL); + $(".sociallogo.facebook").css("display", "inline-block"); + } + + if (ftconfigs.redditURL) { + $(".sociallogo.reddit").attr("href", ftconfigs.redditURL); + $(".sociallogo.reddit").css("display", "inline-block"); + } + + if (ftconfigs.lastfmURL) { + $(".sociallogo.lastfm").attr("href", ftconfigs.lastfmURL); + $(".sociallogo.lastfm").css("display", "inline-block"); + } + + if (ftconfigs.discordURL) { + $(".sociallogo.discord").attr("href", ftconfigs.discordURL); + $(".sociallogo.discord").css("display", "inline-block"); + } + + if (ftconfigs.soundcloudURL) { + $(".sociallogo.soundcloud").attr("href", ftconfigs.soundcloudURL); + $(".sociallogo.soundcloud").css("display", "inline-block"); + } + + firetable.lastfm = { + sk: false, + key: "e86f3b80e48769c03f2b4e0609e12924", + songStart: null, + duration: null, + timer: null, + killSession: function() { + firetable.lastfm.sk = false; + localStorage["ftLastfmSession"] = firetable.lastfm.sk; + $("#scrobtoggle").html("Set up last.fm scrobbling"); + }, + newSession: function(xhr) { + + return function() { + console.log(xhr.responseText); + jsonResponse = JSON.parse(xhr.responseText); + firetable.lastfm.sk = jsonResponse.session.key; + localStorage["ftLastfmSession"] = firetable.lastfm.sk; + $("#scrobtoggle").html("Disconnect Lastfm Scrobbling"); + + }; + }, + scrobble: function() { + var artist = firetable.song.artist; + var track = firetable.lastfm._cleanForScrobble(firetable.song.title); + + var params = { + artist: artist, + track: track, + timestamp: firetable.lastfm.songStart, + api_key: firetable.lastfm.key, + sk: firetable.lastfm.sk, + method: "track.scrobble" + }; + + var sig = firetable.lastfm.getApiSignature(params); + params.api_sig = sig; + + var request_url = 'https://ws.audioscrobbler.com/2.0/?' + serialize(params); + var xhr = new XMLHttpRequest(); + xhr.open('POST', request_url, true); + xhr.onload = console.log("scrobbled"); + xhr.onerror = firetable.lastfm._onAjaxError; + xhr.send(); + + }, + love: function() { + var artist = firetable.song.artist; + var track = firetable.lastfm._cleanForScrobble(firetable.song.title); + + var params = { + artist: artist, + track: track, + api_key: firetable.lastfm.key, + sk: firetable.lastfm.sk, + method: "track.love" + }; + + var sig = firetable.lastfm.getApiSignature(params); + params.api_sig = sig; + + var request_url = 'https://ws.audioscrobbler.com/2.0/?' + serialize(params); + var xhr = new XMLHttpRequest(); + xhr.open('POST', request_url, true); + xhr.onload = console.log("loved"); + xhr.onerror = firetable.lastfm._onAjaxError; + xhr.send(); + + }, + _onAjaxError: function(xhr, status, error) { + console.log(xhr); + console.log(status); + console.log(error); + }, + _cleanForScrobble: function(title) { + return title.replace(/ \([oO]fficial (?:[aA]udio|[vV]ideo)\)/, "") + }, + nowPlaying: function() { + var artist = firetable.song.artist; + var track = firetable.lastfm._cleanForScrobble(firetable.song.title); + + var params = { + artist: artist, + track: track, + duration: firetable.lastfm.duration, + api_key: firetable.lastfm.key, + sk: firetable.lastfm.sk, + method: "track.updateNowPlaying" + }; + + var sig = firetable.lastfm.getApiSignature(params); + params.api_sig = sig; + + var request_url = 'https://ws.audioscrobbler.com/2.0/?' + serialize(params); + var xhr = new XMLHttpRequest(); + xhr.open('POST', request_url, true); + xhr.onload = console.log("nowplayd"); + xhr.onerror = firetable.lastfm._onAjaxError; + + xhr.send(); + + }, + getApiSignature: function(params) { + var i, key, keys, max, paramString; + + keys = []; + paramString = ""; + + for (key in params) { + if (params.hasOwnProperty(key)) { + keys.push(key); + } + } + keys.sort(); + + for (i = 0, max = keys.length; i < max; i += 1) { + key = keys[i]; + paramString += key + params[key]; + } + + return calcMD5(paramString + "838d63e62b556f74176656640b75e33e"); + } + }; + + if (ftconfigs.logoImage) $("#roomlogo").css("background-image", "url(" + ftconfigs.logoImage + ")") + document.title = ftconfigs.roomName + " | firetable"; + if (ftconfigs.roomInfoUrl.length) $("#roomInfo").attr("href", ftconfigs.roomInfoUrl); + $("#version").text("You're running firetable v" + firetable.version + "."); + firetable.utilities.getEmojiMap(); + firetable.parser = new DOMParser(); + $(window).resize(firetable.utilities.debounce(function() { + // This will execute whenever the window is resized + $("#thehistory").css('top', $('#stage').outerHeight() + $('#topbar').outerHeight()); + $('#playerArea,#scScreen').width($('#djStage').outerWidth()).height($('#djStage').outerHeight()); + $("#stealContain").css({ + 'top': $('#grab').offset().top + $('#grab').height(), + 'left': $('#grab').offset().left - 16 + }); + setup(); + }, 500)); + firetable.utilities.scrollToBottom(); + var widgetIframe = document.getElementById('sc-widget'); + firetable.scwidget = SC.Widget(widgetIframe); + firetable.scwidget.bind(SC.Widget.Events.READY, function() { + firetable.scwidget.bind(SC.Widget.Events.PLAY, function() { + var vol = localStorage["firetableVol"]; + if (!vol) { + vol = 80; + localStorage["firetableVol"] = 80; + } + firetable.scwidget.setVolume(vol); + if (firetable.scSeek) firetable.scwidget.seekTo(firetable.scSeek); + }); + if (firetable.song) { + var data = firetable.song; + var nownow = Date.now(); + var timeSince = nownow - data.started; + if (timeSince <= 0) timeSince = 0; + + var secSince = Math.floor(timeSince / 1000); + var timeLeft = data.duration - secSince; + if (data.type == 2) { + if (!firetable.preview) { + firetable.scSeek = timeSince; + if (!firetable.disableMediaPlayback) firetable.scwidget.load("http://api.soundcloud.com/tracks/" + data.cid, { + auto_play: true + }); + } + } + } + firetable.scLoaded = true; + }); + + $playlistItemTemplate = $('#mainqueue .pvbar').remove(); + $tagEditorTemplate = $('.tagPromptBox').remove(); + + ftapi.init(ftconfigs.firebase); + + SC.initialize({ + client_id: ftconfigs.soundcloudKey + }); + + ftapi.events.on("loggedIn", function(data) { + firetable.actions.loggedIn(data); + }); + + ftapi.events.on("loggedOut", firetable.actions.showLoginScreen); + + ftapi.events.on("authReconnected", function() { + firetable.debug && console.log('reconnected'); + $('body').removeClass('disconnected'); + $('#newchat').prop('disabled', false).focus(); + }); + + ftapi.events.on("authDisconnected", function() { + firetable.debug && console.log('disconnected'); + $('body').addClass('disconnected'); + $('#newchat').prop('disabled', true).blur(); + }); + + ftapi.events.on("userBanned", function() { + firetable.debug && console.log("ban detected."); + if (document.getElementById("notice") == null) { + var usrname2use = ftapi.uid; + if (ftapi.users[ftapi.uid]) { + if (ftapi.users[ftapi.uid].username) usrname2use = ftapi.users[ftapi.uid].username; + } + $('.notice').attr('id', 'notice'); + $("#troublemaker").text(usrname2use); + } + }); + + ftapi.events.on("userUnbanned", function() { + window.location.reload(); + }); + + firetable.ui.init(); +}; + +firetable.actions = { + dubtrackImport: function() { + $("#importDubResults").html("importing (0/" + firetable.dtImportList.length + ")..."); + $("#dubimportButton").hide(); + var listid = ftapi.actions.createList(firetable.dtImportName); + var name = firetable.dtImportName; + + $("#listpicker").append(""); + var trackarray = firetable.dtImportList; + for (var e = 0; e < trackarray.length; e++) { + var thetype = 1; + if (trackarray[e].type == "soundcloud") thetype = 2; + var numbo = e + 1; + $("#importDubResults").html("importing (" + numbo + "/" + firetable.dtImportList.length + ")..."); + if (numbo == firetable.dtImportList.length) $("#importDubResults").html("Import complete! You can now select another file if you'd like to do another!"); + ftapi.actions.addToList(thetype, trackarray[e].name, trackarray[e].cid, listid); + + } + }, + localChatResponse: function(txt) { + if (txt.length) { + $("#chats").append("
    " + txt + "
    "); + firetable.utilities.scrollToBottom(); + } + }, + logOut: function() { + ftapi.actions.logOut(); + firetable.debug && console.log("logout"); + }, + showLoginScreen: function() { + $("#cardCaseButton").hide(); + $("#loggedInName").hide(); + $("#logOutButton").hide().off(); + $('#mainGrid').removeClass().addClass('login'); + $("#grab").css("display", "none"); + if (firetable.loginForm && !$("#login").html()) { + $("#mainGrid").append("
    " + firetable.loginForm + "
    "); + + firetable.ui.loginEventsInit(); + } + }, + logIn: function(email, password) { + firetable.debug && console.log("login"); + ftapi.actions.logIn(email, password, function(error) { + var errorCode = error.code; + var errorMessage = error.message; + if (errorCode === 'auth/wrong-password') { + alert('Wrong password.'); + } else { + alert(errorMessage); + } + firetable.debug && console.log("log in error:", error); + }); + }, + loggedIn: function(user) { + firetable.debug && console.log("user signed in!"); + if ($("#login").html()) { + firetable.loginForm = $("#login").html(); + firetable.ui.loginEventsDestroy(); + $("#login").remove(); + } + + if (ftapi.users[ftapi.uid]) { + if (ftapi.users[ftapi.uid].username) { + $("#loggedInName").text(ftapi.users[ftapi.uid].username); + } else { + $("#loggedInName").text(user.uid); + } + } else { + $("#loggedInName").text(user.uid); + } + + ftapi.lookup.allLists(function(allPlaylists) { + $("#listpicker").off("change"); + $("#listpicker").html(""); + for (var key in allPlaylists) { + if (allPlaylists.hasOwnProperty(key)) { + $("#listpicker").append(""); + } + } + ftapi.lookup.selectedList(function(selectedList) { + $("#listpicker").val(selectedList).change(); + $("#listpicker").change(function() { + var val = $("#listpicker").val(); + if (val == "1") { + //ADD PLAYLIST SCREEN + $("#mainqueuestuff").css("display", "none"); + $("#filterMachine").css("display", "none"); + $("#addbox").css("display", "none"); + $("#cancelqsearch").hide(); + $("#qControlButtons").hide(); + + $("#plmanager").css("display", "flex"); + + } else if (val != ftapi.selectedListThing) { + //LOAD SELECTED LIST + //change selected list in user obj + $("#mainqueuestuff").css("display", "block"); + $("#filterMachine").css("display", "block"); + $("#addbox").css("display", "none"); + $("#cancelqsearch").hide(); + $("#qControlButtons").show(); + + $("#plmanager").css("display", "none"); + ftapi.actions.switchList(val); + } else { + //you selected the thing you already had selected. + $("#mainqueuestuff").css("display", "block"); + $("#filterMachine").css("display", "block"); + $("#addbox").css("display", "none"); + $("#cancelqsearch").hide(); + $("#qControlButtons").show(); + $("#plmanager").css("display", "none"); + } + }); + + }); + }); + $("#cardCaseButton").show(); + $("#loggedInName").show(); + $("#logOutButton").show().on('click', firetable.actions.logOut); + firetable.debug && console.log('remove login class from mainGrid'); + $('#mainGrid').removeClass().addClass('mmusrs'); + $("#grab").css("display", "inline-block"); + }, + cardCase: function() { + $("#cardsMain").html(""); + ftapi.lookup.cardCollection(function(data) { + for (var key in data) { + var childData = data[key]; + firetable.debug && console.log('card:', childData); + $("#cardsMain").append("Gift to DJShare In Chat"); + firetable.actions.displayCard(childData, key); + } + }); + }, + chatCard: function(cardid) { + ftapi.actions.sendChat("Check out my card...", cardid); + }, + giftCard: function(cardid) { + ftapi.actions.sendChat("!giftcard :gift:", cardid); + $("#caseCardSpot" + cardid).remove(); + }, + displayCard: function(data, chatid) { + firetable.debug && console.log("display card"); + var defaultScheme = false; + if (data.colors) { + if (data.colors.color == "#fff" || data.colors.color == "#7f7f7f") { + data.colors.color = firetable.orange; + data.colors.txt = "#000"; + defaultScheme = true; + } + } + + if (data.image == "img/idlogo.png" && ftconfigs.defaultAlbumArtUrl.length) data.image = ftconfigs.defaultAlbumArtUrl; + var set = "set1"; + if (data.set) set = data.set; + + var canvas = document.getElementById('cardMaker' + chatid); + + if (canvas.getContext) { + var ctx = canvas.getContext('2d'); + ctx.clearRect(0, 0, canvas.width, canvas.height); + ctx.fillStyle = "#000"; + ctx.fillRect(0, 0, 225, 300); + + ctx.fillStyle = data.colors.color; + if (defaultScheme) ctx.fillStyle = "#fff"; + ctx.fillRect(1, 30, 223, 175); + + var grd = ctx.createLinearGradient(0, 0, 0, 175); + grd.addColorStop(0, "rgba(0, 0, 0, 0.75)"); + grd.addColorStop(1, "rgba(0, 0, 0, 0.55)"); + + // Fill with gradient + ctx.fillStyle = grd; + ctx.fillRect(1, 30, 223, 175); + + ctx.fillStyle = data.colors.color; + ctx.fillRect(1, 205, 223, 10); + + ctx.fillStyle = "#333333"; + //ctx.fillRect(1, 205, 223, 1); + // ctx.fillRect(1, 215, 223, 1); + + ctx.fillStyle = "#151515"; + ctx.fillRect(1, 216, 223, 75); + + //text go + ctx.fillStyle = "#eee"; + ctx.font = "700 11px Helvetica, Arial, sans-serif"; + ctx.fillText(data.djname, 10, 20); + + + ctx.font = "400 8px Helvetica, Arial, sans-serif"; + ctx.textAlign = "center"; + ctx.fillText("Printed " + firetable.utilities.format_date(data.date) + " | " + ftconfigs.roomNameShort, 112.5, 299); + + ctx.font = "700 10px Helvetica, Arial, sans-serif"; + ctx.textAlign = "left"; + var linez = firetable.utilities.wrapText(ctx, data.title, 66, 240, 160, 15); + firetable.debug && console.log('linez:', linez); + ctx.font = "400 8px Helvetica, Arial, sans-serif"; + ctx.textAlign = "left"; + firetable.utilities.wrapText(ctx, data.artist, 66, 253 + (15 * linez), 160, 15); + + ctx.fillStyle = data.colors.txt; + ctx.font = "400 9px Helvetica, Arial, sans-serif"; + ctx.textAlign = "center"; + ctx.fillText("Card No. " + data.cardnum + " | DJ Card | Max Operating Temp " + data.temp + "°", 112.5, 214); + ctx.beginPath(); + ctx.arc(205, 15, 12, 0, 2 * Math.PI, false); + ctx.fillStyle = data.colors.color; + ctx.fill(); + + ctx.fillStyle = data.colors.txt; + ctx.font = "700 15px Helvetica, Arial, sans-serif"; + ctx.textAlign = "left"; + ctx.fillText(data.num, 200.5, 20); + + var doImages = function() { + var picboy = new Image; + picboy.xvalue = 0; + picboy.onload = function() { + ctx.drawImage(this, 20, 30, 175, 175); + var picboy2 = new Image; + picboy2.xvalue = 0; + picboy2.onload = function() { + var heighta = 50; + if (data.image.match(/ytimg.com/g)) heighta = 28; + ctx.drawImage(this, 10, 230, 50, heighta); + ctx = null; + }; + picboy2.src = data.image; + }; + picboy.src = 'https://indiediscotheque.com/robots/' + data.djid + data.djname + '.png?size=175x175&set=' + set; + + + }; + + // special styles + + if (data.special) { + if (data.special == "id8") { + ctx.fillStyle = data.colors.color; + ctx.fillRect(1, 30, 223, 10); + + ctx.fillStyle = "#333333"; + // ctx.fillRect(1, 29, 223, 1); + // ctx.fillRect(1, 40, 223, 1); + + ctx.fillStyle = data.colors.txt; + ctx.font = "400 10px Helvetica, Arial, sans-serif"; + ctx.textAlign = "center"; + ctx.fillText("Celebrating 8 Years of Indie Discotheque", 112.5, 38); + + var cake = new Image; + cake.xvalue = 0; + cake.onload = function() { + ctx.drawImage(this, 10, 50, 35, 35); + var eight = new Image; + eight.xvalue = 0; + eight.onload = function() { + ctx.drawImage(this, 180, 50, 35, 35); + doImages(); + }; + eight.src = 'img/8.png'; + }; + cake.src = 'img/cake.png'; + + } else if (data.special == "id9") { + ctx.fillStyle = data.colors.color; + ctx.fillRect(1, 30, 223, 10); + + ctx.fillStyle = "#333333"; + // ctx.fillRect(1, 29, 223, 1); + // ctx.fillRect(1, 40, 223, 1); + + ctx.fillStyle = data.colors.txt; + ctx.font = "400 10px Helvetica, Arial, sans-serif"; + ctx.textAlign = "center"; + ctx.fillText("Celebrating 9 Years of Indie Discotheque", 112.5, 38); + + var cake = new Image; + cake.xvalue = 0; + cake.onload = function() { + ctx.drawImage(this, 5, 50, 45, 45); + var eight = new Image; + eight.xvalue = 0; + eight.onload = function() { + ctx.save(); + ctx.translate(75 * 0.5, 75 * 0.5); + ctx.rotate(0.959931); + ctx.translate(-75 * 0.5, -75 * 0.5); + + ctx.drawImage(this, 125, -81, 75, 75); + ctx.restore(); + var picboy = new Image; + picboy.xvalue = 0; + picboy.onload = function() { + ctx.drawImage(this, 25, 40, 170, 170); + var picboy2 = new Image; + picboy2.xvalue = 0; + picboy2.onload = function() { + var heighta = 50; + if (data.image.match(/ytimg.com/g)) heighta = 28; + ctx.drawImage(this, 10, 230, 50, heighta); + ctx = null; + }; + picboy2.src = data.image; + }; + picboy.src = 'img/id9.png'; + }; + eight.src = 'https://indiediscotheque.com/robots/' + data.djid + data.djname + '.png?size=110x110&set=' + set; + }; + cake.src = 'img/arnold.png'; + } + } else { + doImages(); + } + } + }, + showCard: function(cardid, chatid) { + // let's SHOW A CARD + ftapi.lookup.card(cardid, function(data) { + firetable.actions.displayCard(data, chatid); + }); + }, + filterQueue: function(val) { + if (val.length == 0) { + $("#mainqueue .pvbar").show(); + return + } else { + + } + val = val.toLowerCase(); + $("#mainqueue .pvbar").each(function(p, q) { + var txt = $(q).find(".listwords").text(); + var regex = new RegExp(val, 'ig'); + if (txt.match(regex)) { + $(q).show() + } else { + $(q).hide() + } + }); + }, + muteToggle: function(zeroMute) { + + var muted = localStorage["firetableMute"]; + var icon = ""; + firetable.debug && console.log('muted:', muted); + if (zeroMute) { + icon = ""; + muted = 0; + + } else if (typeof muted !== 'undefined') { + if (muted != "false") { + + if (muted == 0) { + $("#slider").slider("value", 80); + player.setVolume(80); + firetable.scwidget.setVolume(80); + localStorage["firetableVol"] = 80; + } else { + muted = parseInt(muted); + $("#slider").slider("value", muted); + player.setVolume(muted); + firetable.scwidget.setVolume(muted); + localStorage["firetableVol"] = muted; + } + muted = false; + } else { + icon = ""; + + muted = $("#slider").slider("value"); + $("#slider").slider('value', 0); + player.setVolume(0); + firetable.scwidget.setVolume(0); + localStorage["firetableVol"] = 0; + + } + } else { + icon = ""; + + muted = $("#slider").slider("value"); + $("#slider").slider('value', 0); + player.setVolume(0); + firetable.scwidget.setVolume(0); + localStorage["firetableVol"] = 0; + } + + if (muted) $("#volstatus").addClass('on'); + else $("#volstatus").removeClass('on'); + $("#volstatus i").html(icon); + localStorage["firetableMute"] = muted; + }, + pview: function(id, fromSearch, type, fromHist) { + if (firetable.preview == id) { + //already previewing this. stop and resume regular song + clearTimeout(firetable.ptimeout); + firetable.ptimeout = null; + $("#pv" + firetable.preview).html(""); + $("#pvbar" + firetable.preview).css("background-image", "none"); + clearInterval(firetable.movePvBar); + firetable.movePvBar = null; + firetable.preview = false; + //start regular song + var nownow = Date.now(); + var timeSince = nownow - firetable.song.started; + if (timeSince <= 0) timeSince = 0; + + var secSince = Math.floor(timeSince / 1000); + var timeLeft = firetable.song.duration - secSince; + if (firetable.song.type == 1) { + if (!firetable.preview) { + if (firetable.scLoaded) firetable.scwidget.pause(); + if (!firetable.disableMediaPlayback) player.loadVideoById(firetable.song.cid, secSince, "large"); + } + } else if (firetable.song.type == 2) { + if (!firetable.preview) { + if (firetable.ytLoaded) player.stopVideo(); + firetable.scSeek = timeSince; + if (!firetable.disableMediaPlayback) firetable.scwidget.load("http://api.soundcloud.com/tracks/" + firetable.song.cid, { + auto_play: true + }); + } + } + } else { + if (firetable.preview) { + $("#pv" + firetable.preview).html(""); + $("#pvbar" + firetable.preview).css("background-image", "none"); + } + + firetable.preview = id; + if (fromSearch) { + var cid = id.slice(5); + } else { + var cid = firetable.queue[id].cid; + } + + if (firetable.ptimeout != null) { + clearTimeout(firetable.ptimeout); + firetable.ptimeout = null; + } + if (firetable.movePvBar != null) { + clearInterval(firetable.movePvBar); + firetable.movePvBar = null; + } + firetable.pvCount = 0; + firetable.ptimeout = setTimeout(function() { + firetable.ptimeout = null; + $("#pv" + firetable.preview).html(""); + $("#pvbar" + firetable.preview).css("background-image", "none"); + clearInterval(firetable.movePvBar); + firetable.movePvBar = null; + firetable.pvCount = 0; + firetable.preview = false; + + //start regular song + var nownow = Date.now(); + var timeSince = nownow - firetable.song.started; + if (timeSince <= 0) timeSince = 0; + + var secSince = Math.floor(timeSince / 1000); + var timeLeft = firetable.song.duration - secSince; + if (firetable.song.type == 1) { + if (!firetable.preview) { + if (firetable.scLoaded) firetable.scwidget.pause(); + if (!firetable.disableMediaPlayback) player.loadVideoById(firetable.song.cid, secSince, "large"); + } + } else if (firetable.song.type == 2) { + if (!firetable.preview) { + if (firetable.ytLoaded) player.stopVideo(); + firetable.scSeek = timeSince; + if (!firetable.disableMediaPlayback) firetable.scwidget.load("http://api.soundcloud.com/tracks/" + firetable.song.cid, { + auto_play: true + }); + } + } + }, 30 * 1000); + $("#pv" + id).html(""); + firetable.movePvBar = setInterval(function() { + var pcnt = (firetable.pvCount / 29) * 100; + firetable.pvCount += 0.2; + var pvcolr = "#222"; + if (fromHist) pvcolr = "#222"; + $("#pvbar" + firetable.preview).css("background-image", "linear-gradient(90deg, rgba(244, 129, 11, 0.267) " + pcnt + "%, " + pvcolr + " " + pcnt + "%)"); + }, 200); + if (type == 1) { + if (firetable.scLoaded) firetable.scwidget.pause(); + if (!firetable.disableMediaPlayback) player.loadVideoById(cid, 0, "large") + } else if (type == 2) { + if (firetable.ytLoaded) player.stopVideo(); + firetable.scSeek = 0; + if (!firetable.disableMediaPlayback) firetable.scwidget.load("http://api.soundcloud.com/tracks/" + cid, { + auto_play: true + }); + } + + + } + + }, + mergeLists: function(source, dest, sourceName) { + if (source == dest) { + //source and dest are the same, let's remove the duplicates + firetable.actions.removeDupesFromQueue(); + return; + } + if (dest == -1) { + // create new list if needed + var newname = firetable.utilities.format_date(Date.now()) + " Copy of " + sourceName; + var dest = ftapi.actions.createList(newname); + $("#listpicker").append(""); + } + ftapi.actions.mergeLists(source, dest, function() { + $("#mergeCompleted").show(); + $("#mergeHappening").hide(); + }); + }, + queueFromLink: function(link) { + if (link.match(/youtube.com\/watch/)) { + //youtube + firetable.debug && console.log("yt"); + + function getQueryStringValue(str, key) { + return unescape(str.replace(new RegExp("^(?:.*[&\\?]" + escape(key).replace(/[\.\+\*]/g, "\\$&") + "(?:\\=([^&]*))?)?.*$", "i"), "$1")); + } + var therealid = getQueryStringValue(link, "v"); + if (therealid) { + function keyWordsearch() { + gapi.client.setApiKey(ftconfigs.youtubeKey); + gapi.client.load('youtube', 'v3', function() { + makeRequest(); + }); + } + + function makeRequest() { + var request = gapi.client.youtube.videos.list({ + id: therealid, + part: 'snippet', + maxResults: 1 + }); + request.execute(function(response) { + firetable.debug && console.log('queue from link:', response); + if (response.result) { + if (response.result.items) { + if (response.result.items.length) { + var goodtitle = response.result.items[0].snippet.title; + var yargo = response.result.items[0].snippet.title.split(" - "); + var sartist = yargo[0]; + var stitle = yargo[1]; + if (!stitle) { + // yt title not formatted artist - title. use uploader name instead as artist + stitle = sartist; + sartist = response.result.items[0].snippet.channelTitle.replace(" - Topic", ""); + } + goodtitle = sartist + " - " + stitle; + firetable.actions.queueTrack(response.result.items[0].id, goodtitle, 1); + } + } + } + }) + } + keyWordsearch(); + } + } else if (link.match(/soundcloud.com/)) { + //soundcloud + firetable.debug && console.log("sc"); + var getComments = function(track) { + return SC.get("tracks/" + track.id); + }; + + firetable.actions.resolveSCLink(link, function(tracks) { + if (tracks) { + var yargo = tracks.title.split(" - "); + var sartist = yargo[0]; + var stitle = yargo[1]; + if (!stitle) { + stitle = sartist; + sartist = tracks.user.username; + } + var goodTitle = sartist + " - " + stitle; + firetable.actions.queueTrack(tracks.id, goodTitle, 2); + } + }); + //SC.resolve(link).then(getComments).then(listComments); + + } + }, + resolveSCLink: function(link, callback) { + var importantStuff = link.replace("https://soundcloud.com/", ""); + importantStuff = importantStuff.replace("http://soundcloud.com/", ""); + $.ajax({ + url: "https://thompsn.com/resolvesc/?q=" + importantStuff, + type: 'GET', + dataType: 'json', + success: function(res) { + console.log(res); + callback(res.response); + } + }); + }, + scGet: function(type, q, callback) { + $.ajax({ + url: "https://thompsn.com/soundcloud/?type=" + type + "&q=" + q, + type: 'GET', + dataType: 'json', + success: function(res) { + console.log(res); + callback(res.response); + } + }); + }, + updateQueue: function() { + //this fires when someone drags a song to a new spot in the queue + var arr = $('#mainqueue > div').map(function() { + var theid = this.id; + var idraw = theid.slice(5); + return idraw; + }).get(); + ftapi.actions.reorderList(arr, firetable.preview, function(changePV) { + if (changePV) firetable.preview = changePV; + }); + }, + shuffleQueue: function() { + ftapi.actions.shuffleList(firetable.preview, function(changePV) { + if (changePV) firetable.preview = changePV; + }); + }, + removeDupesFromQueue: function() { + ftapi.actions.removeDuplicatesFromList(); + $("#mergeCompleted").show(); + $("#mergeHappening").hide(); + }, + editTagsPrompt: function(songid, tag, type, cid) { + var $pvbar = $('#thehistory .pvbar[data-key="' + songid + '"]'); + $('#thehistory .pvbar.editing').removeClass('editing'); + $('.tagPromptBox').remove(); + $pvbar.addClass('editing'); + var $tags = $tagEditorTemplate.clone().appendTo($pvbar); + $tags.find(".tagMachine").val(tag); + + firetable.debug && console.log('edit tags song id:', songid); + + }, + importList(id, name, type) { + //time to IMPORT SOME LISTS! + $("#overlay").removeClass('show'); + $("#importResults").html(""); + $("#plMachine").val(""); + if (type == 1) { + //youtube + var finalList = []; + + function keyWordsearch(pg) { + gapi.client.setApiKey(ftconfigs.youtubeKey); + gapi.client.load('youtube', 'v3', function() { + makeRequest(pg); + }); + } + + function makeRequest(pg) { + if (pg) { + var request = gapi.client.youtube.playlistItems.list({ + playlistId: id, + maxResults: 50, + part: "snippet", + pageToken: pg + }); + } else { + var request = gapi.client.youtube.playlistItems.list({ + playlistId: id, + maxResults: 50, + part: "snippet" + }); + } + request.execute(function(response) { + if (response.items.length) { + for (var idx = 0; idx < response.items.length; idx++) { + finalList.push(response.items[idx]); + } + } + if (response.nextPageToken) { + if (response.nextPageToken != "") keyWordsearch(response.nextPageToken); + } else { + firetable.debug && console.log(finalList); + var listid = ftapi.actions.createList(name); + $("#listpicker").append(""); + for (var i = 0; i < finalList.length; i++) { + var goodTitle = finalList[i].snippet.title; + // can't use youtube uploader name to fix tags here because YOUTUBE DECIDED NOT TO INCLUDE THAT INFORMATION >:o + if (goodTitle !== "Private video" && goodTitle !== "Deleted video") { + ftapi.actions.addToList(1, goodTitle, finalList[i].snippet.resourceId.videoId, listid); + } + } + } + }) + } + keyWordsearch(); + + } else if (type == 2) { + firetable.actions.scGet('playlists', id, function(listinfo) { + firetable.debug && console.log('sc tracks:', listinfo.tracks); + var listid = ftapi.actions.createList(name); + $("#listpicker").append(""); + for (var i = 0; i < listinfo.tracks.length; i++) { + if (listinfo.tracks[i].title) { + var yargo = listinfo.tracks[i].title.split(" - "); + var sartist = yargo[0]; + var stitle = yargo[1]; + if (!stitle) { + stitle = sartist; + sartist = listinfo.tracks[i].user.username; + } + var goodTitle = sartist + " - " + stitle; + } else { + var goodTitle = "Unknown"; + } + + ftapi.actions.addToList(2, goodTitle, listinfo.tracks[i].id, listid); + } + }); + } + }, + bumpSongInQueue: function(songid) { + ftapi.actions.moveTrackToTop(songid, firetable.preview, function(changePV) { + if (changePV) firetable.preview = changePV; + }); + }, + signUp: function(email, password, username) { + firetable.debug && console.log("signup"); + ftapi.actions.signUp(email, password, username, function(error) { + alert(error); + }); + }, + deleteSong: function(id) { + ftapi.actions.deleteTrack(id); + }, + uidLookup: function(name) { + var match = false; + var usrs = ftapi.users; + for (var key in usrs) { + if (usrs.hasOwnProperty(key)) { + if (ftapi.users[key].username) { + if (ftapi.users[key].username == name) { + match = key; + } + } + } + } + if (!match && ftapi.users[name]) match = name; + return match; + }, + grab: function() { + if (firetable.song.cid != 0) { + var title = firetable.song.artist + " - " + firetable.song.title; + firetable.actions.queueTrack(firetable.song.cid, title, firetable.song.type, true); + } + }, + unban: function(userid) { + ftapi.actions.unbanUser(userid); + }, + reloadtrack: function() { + $('#reloadtrack').addClass('on working'); + //start regular song + var nownow = Date.now(); + var timeSince = nownow - firetable.song.started; + if (timeSince <= 0) timeSince = 0; + var secSince = Math.floor(timeSince / 1000); + var timeLeft = firetable.song.duration - secSince; + if (firetable.song.type == 1) { + if (!firetable.preview) { + if (firetable.scLoaded) firetable.scwidget.pause(); + if (!firetable.disableMediaPlayback) player.loadVideoById(firetable.song.cid, secSince, "large"); + } + } else if (firetable.song.type == 2) { + if (!firetable.preview) { + if (firetable.ytLoaded) player.stopVideo(); + firetable.scSeek = timeSince; + if (!firetable.disableMediaPlayback) firetable.scwidget.load("http://api.soundcloud.com/tracks/" + firetable.song.cid, { + auto_play: true + }, function() { + $('#reloadtrack').removeClass('on working'); + }); + } + } + }, + queueTrack: function(cid, name, type, tobottom) { + var info = { + type: type, + name: name, + cid: cid + }; + $("#apv" + type + cid).find(".material-icons").text("check"); + $("#apv" + type + cid).css("color", firetable.orange); + $("#apv" + type + cid).css("pointer-events", "none"); + setTimeout(function() { + $("#apv" + type + cid).find(".material-icons").text("playlist_add"); + $("#apv" + type + cid).removeAttr("style"); + }, 3000); + + var cuteid = ftapi.actions.addToList(type, name, cid, false, function() { + firetable.debug && console.log('queue track id:', cuteid); + if (!tobottom) firetable.actions.bumpSongInQueue(cuteid); + }); + + if (firetable.preview) { + if (firetable.preview.slice(0, 5) == "ytcid" || firetable.preview.slice(0, 5) == "sccid") { + $("#pv" + firetable.preview).html(""); + clearTimeout(firetable.ptimeout); + firetable.ptimeout = null; + $("#pvbar" + firetable.preview).css("background-image", "none"); + clearInterval(firetable.movePvBar); + firetable.movePvBar = null; + firetable.preview = false; + //start regular song + var nownow = Date.now(); + var timeSince = nownow - firetable.song.started; + if (timeSince <= 0) timeSince = 0; + var secSince = Math.floor(timeSince / 1000); + var timeLeft = firetable.song.duration - secSince; + if (firetable.song.type == 1) { + if (!firetable.preview) { + if (firetable.scLoaded) firetable.scwidget.pause(); + if (!firetable.disableMediaPlayback) player.loadVideoById(firetable.song.cid, secSince, "large"); + } + } else if (firetable.song.type == 2) { + if (!firetable.preview) { + if (firetable.ytLoaded) player.stopVideo(); + firetable.scSeek = timeSince; + if (!firetable.disableMediaPlayback) firetable.scwidget.load("http://api.soundcloud.com/tracks/" + firetable.song.cid, { + auto_play: true + }); + } + } + } + } + $("#mainqueuestuff").css("display", "block"); + $("#filterMachine").css("display", "block"); + $("#addbox").css("display", "none"); + $("#cancelqsearch").hide(); + $("#qControlButtons").show(); + } +}; + +firetable.emojis = { + h: function() { + $(".pickerResult").show(); + $("#pickerResults h3").show(); + }, + n: function(p, q) { + var e = p.attr("data-alternative-name"); + return ($(p).text().toLowerCase().indexOf(q) >= 0) || (e != null && e.toLowerCase().indexOf(q) >= 0) + }, + sec: function(sec) { + firetable.debug && console.log('emoji sec:', sec); + var selectedSec = $("#pickerNav > .on"); + var thething = sec.substr(1); + console.log(thething); + if (selectedSec.length) { + firetable.debug && console.log("already selected sec"); + if (selectedSec[0].id == sec) { + firetable.debug && console.log("toggle selected... back to FULL LIST"); + $("#" + selectedSec[0].id).removeClass("on"); + $("#pickerContents div").show(); + } else { + //new sec selected + $("#" + selectedSec[0].id).removeClass("on"); + $("#" + selectedSec[0].id.substr(1)).hide(); + $("#" + sec).addClass("on"); + $("#" + thething).show(); + } + } else { + firetable.debug && console.log("first select"); + $("#" + sec).addClass("on"); + $("#pickerContents div").hide(); + $("#" + thething).show(); + } + }, + niceSearch: function(val) { + if (val.length == 0) { + firetable.emojis.h(); + return + } else { + var isvisible = $("#pickerResults h3").is(":visible"); + if (isvisible) $("#pickerResults h3").hide(); + } + val = val.toLowerCase(); + $(".pickerResult").each(function(p, q) { + if (firetable.emojis.n($(q), val)) { + $(q).show() + } else { + $(q).hide() + } + }); + // simplebar scroll update? + } +}; + +firetable.utilities = { + getEmojiMap: function() { + firetable.emojiMap = {}; + (async function() { + urls = [ + "https://unpkg.com/unicode-emoji-json@0.3.0/data-by-group.json", + "https://unpkg.com/emojilib@2.4.0/emojis.json", + "https://unpkg.com/emojilib@3.0.4/dist/emoji-en-US.json" + ]; + try { + const requests = urls.map((url) => fetch(url)); + const responses = await Promise.all(requests); + const promises = responses.map((response) => response.json()); + const data = await Promise.all(promises); + let oldmojis = {}; + for (const [oldSlug, emojiObj] of Object.entries(data[1])) { + oldmojis[emojiObj.char] = oldSlug; + } + for (const [category, emojisArr] of Object.entries(data[0])) { + let catid = category.replace(/[\s&]+/g, '_').toLowerCase(); + $('#pickerNav').append('' + emojisArr[0].emoji + ''); + $('#pickerContents').append('

    ' + category + '

    '); + for (let i in emojisArr) { + firetable.emojiMap[emojisArr[i].slug] = emojisArr[i].emoji; + var words = ""; + words += (data[2][emojisArr[i].emoji] !== undefined) ? data[2][emojisArr[i].emoji].join(',') : ""; + words += (oldmojis[emojisArr[i].emoji] !== undefined) ? ',' + oldmojis[emojisArr[i].emoji] : ""; + $("#picker" + catid).append('' + emojisArr[i].emoji + ''); + } + for (let i in oldmojis) { + firetable.emojiMap[oldmojis[i]] = i; + } + } + twemoji.parse(document.getElementById("pickerNav")); + } catch (err) {}; + })() + }, + hexToRGB: function(hex) { + var shorthandRegex = /^#?([a-f\d])([a-f\d])([a-f\d])$/i; + hex = hex.replace(shorthandRegex, function(m, r, g, b) { + return r + r + g + g + b + b; + }); + + var result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex); + return result ? { + r: parseInt(result[1], 16), + g: parseInt(result[2], 16), + b: parseInt(result[3], 16) + } : null; + }, + wrapText: function(context, text, x, y, maxWidth, lineHeight) { + var words = text.split(' '); + var line = ''; + var lines = 0; + + for (var n = 0; n < words.length; n++) { + var testLine = line + words[n] + ' '; + var metrics = context.measureText(testLine); + var testWidth = metrics.width; + if (testWidth > maxWidth && n > 0) { + context.fillText(line, x, y); + line = words[n] + ' '; + y += lineHeight; + lines++; + } else { + line = testLine; + } + } + context.fillText(line, x, y); + return lines; + }, + emojiShortnamestoUnicode: function(str) { + var res = str.replace(/\:(.*?)\:/g, function(x) { + var response = x; + var shortname = x.replace(/\:/g, ""); + if (firetable.emojiMap[shortname]) { + response = "" + firetable.emojiMap[shortname] + ""; + } else if (shortname == "rohn") { + response = ""; + } + return response; + }); + return res; + }, + playSound: function(filename) { + if (firetable.playBadoop) { + document.getElementById("audilert").setAttribute('src', filename + ".mp3"); + } + }, + desktopNotify: function(chatData, namebo) { + if (Notification) { + if (Notification.permission !== "granted") { + Notification.requestPermission(); + } else { + var notification = new Notification(namebo, { + icon: "https://indiediscotheque.com/robots/" + chatData.id + namebo + ".png?size=110x110&set=" + firetable.avatarset, + body: chatData.txt, + }); + } + } + }, + screenUp: function() { + $('body').removeClass('screen'); + }, + screenDown: function() { + $('body').addClass('screen'); + }, + isChatPrettyMuchAtBottom: function() { + var scrollable = chatScroll.contentEl.scrollHeight - chatScroll.el.clientHeight; + var scrolled = chatScroll.contentWrapperEl.scrollTop; + console.log('near bottom?', scrollable, scrolled); + return (Math.abs(scrollable - scrolled) <= 25); + }, + scrollToBottom: function() { + chatScroll.contentWrapperEl.scrollTop = chatScroll.contentEl.scrollHeight; + }, + htmlEscape: function(s, preserveCR) { + preserveCR = preserveCR ? ' ' : '\n'; + return ('' + s) /* Forces the conversion to string. */ + .replace(/&/g, '&') /* This MUST be the 1st replacement. */ + .replace(/'/g, '\\'') /* The 4 other predefined entities, required. */ + .replace(/"/g, '"') + .replace(//g, '>') + /* + You may add other replacements here for HTML only + (but it's not necessary). + Or for XML, only if the named entities are defined in its DTD. + */ + .replace(/\r\n/g, preserveCR) /* Must be before the next replacement. */ + .replace(/[\r\n]/g, preserveCR);; + }, + format_date: function(d) { + + var date = new Date(d); + + var month = date.getMonth() + 1; + var day = date.getDate(); + var year = date.getFullYear(); + + var formatted_date = month + "/" + day + "/" + year; + return formatted_date; + }, + format_time: function(d) { + var date = new Date(d); + + var hours1 = date.getHours(); + var ampm = "am"; + var hours = hours1; + if (hours1 >= 12) { + ampm = "pm"; + if (hours !== 12) hours = hours1 - 12; + } + if (hours == 0) hours = 12; + var minutes = date.getMinutes(); + var min = ""; + if (minutes > 9) { + min += minutes; + } else { + min += "0" + minutes; + } + return hours + ":" + min + "" + ampm; + }, + debounce(func, wait, immediate) { + var timeout; + return function() { + var context = this, + args = arguments; + var later = function() { + timeout = null; + if (!immediate) func.apply(context, args); + }; + var callNow = immediate && !timeout; + clearTimeout(timeout); + timeout = setTimeout(later, wait); + if (callNow) func.apply(context, args); + }; + }, + chatAt: function(element) { + element.bind("click", function() { + console.log(element); + var nameToAt; + if (element[0].className == "prson") { + nameToAt = $(this).find(".prsnName").text(); + } else if (element[0].className == "botson") { + nameToAt = $(this).next(".chatContent").find(".chatName").text(); + } else if (element[0].className == "chatName") { + nameToAt = $(this).text(); + } + $("#newchat").val(function(i, val) { + return val + "@" + nameToAt + " "; + }).focus(); + }) + }, + initAtLand: function() { + firetable.atLand = true; + firetable.atString = ""; + firetable.atUsers = ["everyone"]; + for (var user in ftapi.users) { + firetable.atUsers.push(ftapi.users[user].username); + } + firetable.atUsersFiltered = firetable.atUsers.sort(); + }, + updateAtLand: function() { + firetable.atUsersFiltered = firetable.atUsers.filter(user => user.toLowerCase().startsWith(firetable.atString.toLowerCase())).sort(); + $('#atPicker').html(''); + if (firetable.atUsersFiltered.length) { + for (var user of firetable.atUsersFiltered) { + $('
    ').appendTo('#atPicker'); + } + } else { + $('
    No users match
    ').appendTo('#atPicker'); + } + }, + chooseAt: function(atPeep) { + var $chatText = $('#newchat'); + if (firetable.atString.length > 0) $chatText.val($chatText.val().slice(0, firetable.atString.length * -1)); + $chatText.val($chatText.val() + atPeep + " "); + firetable.utilities.exitAtLand(); + }, + exitAtLand: function() { + firetable.atLand = false; + firetable.atUsersFiltered = []; + firetable.atString = ""; + $('#atPicker').removeClass('show').html(''); + } +}; + +firetable.ui = { + textToLinks: function(text, themeBox) { + var re = /(\b(https?|ftp|file):\/\/[-A-Z0-9+&@#\/%?=~_|!:,.;]*[-A-Z0-9+&@#\/%=~_|])/ig; + if (firetable.showImages && !themeBox) re = /(https?:\/\/(?![/|.|\w|\s|-]*(?:jpe?g|png|gif))[^" ]+)/g; + return text.replace(re, "$1"); + + return text; + }, + dubtrackImportFileSelect: function(evt) { + var files = evt.target.files; // FileList object + var file = files[0]; + // read the file contents + var reader = new FileReader(); + reader.readAsText(file); + reader.onload = function(event) { + try { + var allthestuff = event.currentTarget.result; + console.log(allthestuff); + firetable.dtImportName = firetable.ui.strip(allthestuff.split('

    ')[1].split('

    ')[0]); + var hams = allthestuff.split('
  • (.*)<\/li>/gm; + var matches = thingsRegex.exec(hams[i]) + var type = matches[2]; + var cid = matches[4]; + var name = firetable.ui.strip(matches[5]); + firetable.dtImportList.push({ + type: type, + cid: cid, + name: name + }); + } + console.log(firetable.dtImportList); + console.log(firetable.dtImportName); + if (firetable.dtImportList.length) { + $("#importDubResults").text("Ok... import " + firetable.dtImportName + " (" + firetable.dtImportList.length + " tracks)?") + $("#dubimportButton").show(); + } else { + $("#importDubResults").text("ERROR... NO TRAX?") + $("#dubimportButton").hide(); + } + } catch (e) { + console.log(e); + $("#importDubResults").text("ERROR") + $("#dubimportButton").hide(); + } + + + }; + + }, + strip: function(html) { + var doc = firetable.parser.parseFromString(html, 'text/html'); + return doc.body.textContent || ""; + }, + hidePlayerControls: function() { + $("head").append(""); + }, + showPlayerControls: function() { + $(".playerControlsHider").remove(); + }, + showImages: function(chatTxt) { + if (firetable.showImages) { + var imageUrlRegex = /((http(s?):)([/|.|\w|\s|-])*\.(?:jpe?g|gif|png))/g; + var hasImage = chatTxt.search(imageUrlRegex) >= 0; + if (hasImage) { + chatTxt = chatTxt.replace(imageUrlRegex, function(imageUrl) { + var chatImage = new Image(); + chatImage.onload = function() { + if (firetable.utilities.isChatPrettyMuchAtBottom()) firetable.utilities.scrollToBottom(); + } + chatImage.src = imageUrl; + return '×' + }); + + } + } + return chatTxt; + }, + loginLinkToggle: function(id) { + $("#formlinks").find(".selected").removeClass("selected"); + $("#" + id).addClass("selected"); + }, + loginEventsInit: function() { + $("#resetpass").bind("click", function() { + $("#logscreen").css("display", "none"); + $("#createscreen").css("display", "none"); + $("#resetscreen").css("display", "block"); + firetable.ui.loginLinkToggle($(this).attr('id')); + }); + $("#loginlink").bind("click", function() { + $("#logscreen").css("display", "block"); + $("#createscreen").css("display", "none"); + $("#resetscreen").css("display", "none"); + firetable.ui.loginLinkToggle($(this).attr('id')); + }); + $("#signuplink").bind("click", function() { + $("#logscreen").css("display", "none"); + $("#createscreen").css("display", "block"); + $("#resetscreen").css("display", "none"); + firetable.ui.loginLinkToggle($(this).attr('id')); + }); + $("#loginpass").bind("keyup", function(e) { + if (e.which == 13) { + var email = $("#loginemail").val(); + var pass = $("#loginpass").val(); + $("#loginemail").val(""); + $("#loginpass").val(""); + firetable.actions.logIn(email, pass); + } + }); + $("#newpass2").bind("keyup", function(e) { + if (e.which == 13) { + var email = $("#newemail").val(); + var pass = $("#newpass").val(); + var pass2 = $("#newpass2").val(); + var username = $("#newusername").val(); + if (pass == pass2) { + firetable.actions.signUp(email, pass, username); + } else { + alert("Those passwords do not match!"); + } + } + }); + $("#theAddress").bind("keyup", function(e) { + if (e.which == 13) { + var email = $("#theAddress").val(); + firetable.debug && console.log("reset email return"); + ftapi.actions.resetPassword(email, function(error) { + var errorCode = error.code; + var errorMessage = error.message; + if (errorCode === 'auth/wrong-password') { + alert('Wrong password.'); + } else { + alert(errorMessage); + } + firetable.debug && console.log('send pass reset error:', error); + }); + alert("Reset email sent. Click the reset link when it arrives thanks."); + } + }); + $("#createAccountBttn").bind("click", function() { + var email = $("#newemail").val(); + var pass = $("#newpass").val(); + var pass2 = $("#newpass2").val(); + var termsAgreedTo = $("#agreetoterms").is(":checked"); + var username = $("#newusername").val(); + if (!termsAgreedTo) { + alert("You must read and agree to the Terms of Service and Privacy Policy before you can create an account."); + } else if (pass != pass2) { + alert("Those passwords do not match!"); + } else { + firetable.actions.signUp(email, pass, username); + } + }); + $("#resetPassBttn").bind("click", function() { + var email = $("#theAddress").val(); + firetable.debug && console.log("reset email click button"); + ftapi.actions.resetPassword(email, function(error) { + var errorCode = error.code; + var errorMessage = error.message; + if (errorCode === 'auth/wrong-password') { + alert('Wrong password.'); + } else { + alert(errorMessage); + } + firetable.debug && console.log('send pass reset error:', error); + }); + alert("Reset email sent. Click the reset link when it arrives thanks."); + }); + $("#loginBttn").bind("click", function() { + var email = $("#loginemail").val(); + var pass = $("#loginpass").val(); + $("#loginemail").val(""); + $("#loginpass").val(""); + firetable.actions.logIn(email, pass); + }); + }, + loginEventsDestroy: function() { + $("#resetpass").off("click"); + $("#loginlink").off("click"); + $("#signuplink").off("click"); + $("#loginpass").off("keyup"); + $("#newpass2").off("keyup"); + $("#theAddress").off("keyup"); + $("#createAccountBttn").off("click"); + $("#resetPassBttn").off("click"); + $("#loginBttn").off("click"); + }, + init: function() { + + $("#minidiscover").bind("click", function() { + $("#discover").removeClass("miniLoginInvisible"); + $("#login").addClass("miniLoginInvisible"); + }); + + $("#minijoin").bind("click", function() { + $("#discover").addClass("miniLoginInvisible"); + $("#login").removeClass("miniLoginInvisible"); + }); + + + $('#mainqueue').sortable({ + start: function(event, ui) { + var start_pos = ui.item.index(); + ui.item.data('start_pos', start_pos); + }, + change: function(event, ui) { + + }, + update: function(event, ui) { + firetable.debug && console.log("UPDATE"); + firetable.actions.updateQueue(); + } + }); + + //CHECK FOR LASTFM TOKEN + + var thingo = localStorage["ftLastfmSession"]; + if (thingo == "false") thingo = false; + if (thingo) { + firetable.lastfm.sk = thingo; + $("#scrobtoggle").html("Disconnect Lastfm Scrobbling"); + } else { + $("#scrobtoggle").html("Set up last.fm scrobbling"); + } + + var pattern = /[?&]token=/; + var URL = location.search; + + if (pattern.test(URL) && !firetable.lastfm.sk) { + var queries = {}; + $.each(document.location.search.substr(1).split('&'), function(c, q) { + var i = q.split('='); + queries[i[0].toString()] = i[1].toString(); + }); + //token time + var params = { + api_key: firetable.lastfm.key, + token: queries.token, + method: "auth.getSession" + }; + + var sig = firetable.lastfm.getApiSignature(params); + params.api_sig = sig; + + var request_url = 'https://ws.audioscrobbler.com/2.0/?' + serialize(params) + "&format=json"; + var xhr = new XMLHttpRequest(); + xhr.open('POST', request_url, true); + xhr.onload = firetable.lastfm.newSession(xhr); + xhr.onerror = firetable.lastfm._onAjaxError; + + xhr.send(); + } + + + //GET SETTINGS FROM LOCALSTORAGE + var disableMediaPlayback = localStorage["firetableDisableMedia"]; + if (typeof disableMediaPlayback == "undefined") { + localStorage["disableMediaPlayback"] = false; + firetable.disableMediaPlayback = false; + $("#mediaDisableToggle").prop("checked", false); + } else { + disableMediaPlayback = JSON.parse(disableMediaPlayback); + firetable.disableMediaPlayback = disableMediaPlayback; + $("#mediaDisableToggle").prop("checked", disableMediaPlayback); + if (disableMediaPlayback) { + firetable.ui.hidePlayerControls(); + } + } + + var showImages = localStorage["firetableShowImages"]; + if (typeof showImages == "undefined") { + localStorage["firetableShowImages"] = false; + firetable.showImages = false; + $("#showImagesToggle").prop("checked", false); + } else { + showImages = JSON.parse(showImages); + firetable.showImages = showImages; + $("#showImagesToggle").prop("checked", showImages); + } + var showAvatars = localStorage["firetableShowAvatars"]; + if (typeof showAvatars == "undefined") { + localStorage["firetableShowAvatars"] = true; + firetable.showAvatars = true; + $("#showAvatarsToggle").prop("checked", true); + } else { + showAvatars = JSON.parse(showAvatars); + firetable.showAvatars = showAvatars; + $("#showAvatarsToggle").prop("checked", showAvatars); + if (showAvatars == false) { + document.getElementById("actualChat").classList.add("avatarsOff"); + } + } + var playBadoop = localStorage["firetableBadoop"]; + if (typeof playBadoop == "undefined") { + localStorage["firetableBadoop"] = true; + firetable.playBadoop = true; + $("#badoopToggle").prop("checked", true); + } else { + playBadoop = JSON.parse(playBadoop); + firetable.playBadoop = playBadoop; + $("#badoopToggle").prop("checked", playBadoop); + } + var dtnmt = localStorage["firetableDTNM"]; + if (typeof dtnmt == "undefined") { + localStorage["firetableDTNM"] = false; + firetable.desktopNotifyMentions = false; + $("#desktopNotifyMentionsToggle").prop("checked", false); + } else { + dtnmt = JSON.parse(dtnmt); + firetable.desktopNotifyMentions = dtnmt; + $("#desktopNotifyMentionsToggle").prop("checked", dtnmt); + } + var screenControl = localStorage["firetableScreenControl"]; + if (typeof screenControl == "undefined") { + localStorage["firetableScreenControl"] = "sync"; + firetable.screenControl = "sync"; + $("#screenControlTog" + firetable.screenControl).prop("checked", true); + } else { + firetable.screenControl = screenControl; + $("#screenControlTog" + firetable.screenControl).prop("checked", true); + if (screenControl == "on") { + firetable.utilities.screenDown(); + } else if (screenControl == "off") { + firetable.utilities.screenUp(); + } else if (screenControl == "sync") { + if (firetable.screenSyncPos) { + firetable.utilities.screenDown(); + } else { + firetable.utilities.screenUp(); + } + } + } + var $discoverItem = $('#thediscovers .pvbar').remove(); + ftapi.events.on('newProduce', function(data) { + if (data.img == "img/idlogo.png" && ftconfigs.defaultAlbumArtUrl.length) data.img = ftconfigs.defaultAlbumArtUrl; + var firstpart = "yt"; + if (data.type == 2) firstpart == "sc"; + var pkey = firstpart + "cid" + data.cid; + var $histItem = $discoverItem.clone(); + $histItem.attr('id', "pvbar" + pkey); + $histItem.attr("data-key", pkey); + $histItem.attr("data-histid", data.histID); + $histItem.attr("data-cid", data.cid); + $histItem.attr("data-type", data.type); + + $histItem.find('.previewicon').attr('id', "pv" + pkey).on('click', function() { + firetable.actions.pview( + $(this).closest('.pvbar').attr('data-key'), + true, + $(this).closest('.pvbar').attr('data-type'), + true + ); + }); + $histItem.find('.histlink').attr({ + 'href': data.url, + 'tabindex': "-1", + 'id': data.histID + }).text(data.artist + " - " + data.title); + $histItem.find('.edittags').on('click', function() { + if ($(this).hasClass("editing")) { + $(this).removeClass("editing"); + $(this).closest('.pvbar').find('.tagPromptBox').remove(); + } else { + $(this).addClass("editing"); + firetable.actions.editTagsPrompt($(this).closest('.pvbar').attr('data-key'), data.artist + " - " + data.title) + } + }); + try { + + if (!ftapi.isMod) $histItem.find('.edittags').hide(); + } catch (e) { + console.log(e); + } + + $histItem.find('.histdj').text(data.dj); + $histItem.find('.histdate').text(firetable.utilities.format_date(data.when)); + $histItem.find('.histtime').text(firetable.utilities.format_time(data.when)); + $histItem.find('.histeal').attr('id', "apv" + data.type + data.cid).on('click', function() { + firetable.actions.queueTrack( + $(this).closest('.pvbar').attr('data-cid'), + firetable.utilities.htmlEscape($(this).closest('.pvbar').find('.histlink').text()), + $(this).closest('.pvbar').attr('data-type'), + true + ); + }); + $histItem.find('.discart').css('background-image', 'url(' + data.img + ')'); + $histItem.prependTo("#thediscovers"); + // simplebar scroll update? + }); + + var $historyItem = $('#thehistory .pvbar').remove(); + ftapi.events.on('editedHistory', function(data) { + console.log("HIST EDIT", data); + $("#" + data.histID).text(data.artist + " - " + data.title); + }); + ftapi.events.on('modCheck', function(data) { + if (data) $(".edittags").show(); + }); + ftapi.events.on('newHistory', function(data) { + if (data.img == "img/idlogo.png" && ftconfigs.defaultAlbumArtUrl.length) data.img = ftconfigs.defaultAlbumArtUrl; + var firstpart = "yt"; + if (data.type == 2) firstpart == "sc"; + var pkey = firstpart + "cid" + data.cid; + var $histItem = $historyItem.clone(); + $histItem.attr('id', "pvbar" + pkey); + $histItem.attr("data-key", pkey); + $histItem.attr("data-histid", data.histID); + $histItem.attr("data-cid", data.cid); + $histItem.attr("data-type", data.type); + + $histItem.find('.previewicon').attr('id', "pv" + pkey).on('click', function() { + firetable.actions.pview( + $(this).closest('.pvbar').attr('data-key'), + true, + $(this).closest('.pvbar').attr('data-type'), + true + ); + }); + $histItem.find('.histlink').attr({ + 'href': data.url, + 'tabindex': "-1", + 'id': data.histID + }).text(data.artist + " - " + data.title); + $histItem.find('.edittags').on('click', function() { + if ($(this).hasClass("editing")) { + $(this).removeClass("editing"); + $(this).closest('.pvbar').find('.tagPromptBox').remove(); + } else { + $(this).addClass("editing"); + firetable.actions.editTagsPrompt($(this).closest('.pvbar').attr('data-key'), data.artist + " - " + data.title) + } + }); + try { + + if (!ftapi.isMod) $histItem.find('.edittags').hide(); + } catch (e) { + console.log(e); + } + + $histItem.find('.histdj').text(data.dj); + $histItem.find('.histdate').text(firetable.utilities.format_date(data.when)); + $histItem.find('.histtime').text(firetable.utilities.format_time(data.when)); + $histItem.find('.histeal').attr('id', "apv" + data.type + data.cid).on('click', function() { + firetable.actions.queueTrack( + $(this).closest('.pvbar').attr('data-cid'), + firetable.utilities.htmlEscape($(this).closest('.pvbar').find('.histlink').text()), + $(this).closest('.pvbar').attr('data-type'), + true + ); + }); + $histItem.find('.histart').css('background-image', 'url(' + data.img + ')'); + $histItem.prependTo("#thehistory"); + // simplebar scroll update? + }); + ftapi.events.on("newTheme", function(data) { + if (!data) { + //no theme + $("#currentTheme").text("!suggest a theme"); + } else { + var txtOut = firetable.ui.strip(data); + txtOut = firetable.ui.textToLinks(txtOut, true); + txtOut = firetable.utilities.emojiShortnamestoUnicode(txtOut); + txtOut = txtOut.replace(/\`(.*?)\`/g, function(x) { + return "" + x.replace(/\`/g, "") + ""; + }); + $("#currentTheme").html(txtOut); + twemoji.parse(document.getElementById("currentTheme")); + } + }); + ftapi.events.on("tagUpdate", function(data) { + firetable.debug && console.log("TAG UPDATE", data); + firetable.tagUpdate = data; + if (firetable.song) { + if (firetable.song.cid == data.cid && data.adamData.track_name) { + $("#track").text(firetable.ui.strip(data.adamData.track_name)); + $("#artist").text(firetable.ui.strip(data.adamData.artist)); + firetable.song.title = firetable.ui.strip(data.adamData.track_name); + firetable.song.artist = firetable.ui.strip(data.adamData.artist); + var nicename = firetable.song.djname; + var showPlaycount = false; + if (data.adamData.playcount) { + if (data.adamData.playcount > 0) { + showPlaycount = true; + } + } + if (data.adamData.last_play) { + $("#lastPlay").text("last " + firetable.utilities.format_date(data.adamData.last_play) + " by " + data.adamData.last_play_dj); + } else { + $("#lastPlay").text(""); + } + if (data.adamData.first_play) { + $("#firstPlay").text("first " + firetable.utilities.format_date(data.adamData.first_play) + " by " + data.adamData.first_play_dj); + } else { + $("#firstPlay").text(""); + } + var doTheScrollThing = firetable.utilities.isChatPrettyMuchAtBottom(); + if (showPlaycount) { + $("#playCount").text(data.adamData.playcount + " plays"); + $(".npmsg" + data.cid).last().html("
    DJ " + nicename + " started playing " + data.adamData.track_name + " by " + data.adamData.artist + "
    This song has been played " + data.adamData.playcount + " times.
    "); + } else { + $("#playCount").text(""); + $(".npmsg" + data.cid).last().html("
    DJ " + nicename + " started playing " + data.adamData.track_name + " by " + data.adamData.artist + "
    "); + } + if (doTheScrollThing) firetable.utilities.scrollToBottom(); + } + } + }); + + + ftapi.events.on('newSong', function(data) { + $("#playCount").text(""); + $("#lastPlay").text(""); + $("#firstPlay").text(""); + window.dispatchEvent(new Event('resize')); + $("#cloud_with_rain").removeClass("on"); + $("#fire").removeClass("on"); + $("#timr").countdown("destroy"); + if (firetable.moveBar != null) { + clearInterval(firetable.moveBar); + firetable.moveBar = null; + } + if (data.image == "img/idlogo.png" && ftconfigs.defaultAlbumArtUrl.length) data.image = ftconfigs.defaultAlbumArtUrl; + $("#prgbar").css("background", "#151515"); + var showPlaycount = false; + if (firetable.tagUpdate) { + if (data.cid == firetable.tagUpdate.cid && firetable.tagUpdate.adamData.track_name) { + data.title = firetable.tagUpdate.adamData.track_name; + data.artist = firetable.tagUpdate.adamData.artist; + if (firetable.tagUpdate.adamData.last_play) { + $("#lastPlay").text("last " + firetable.utilities.format_date(firetable.tagUpdate.adamData.last_play) + " by " + firetable.tagUpdate.adamData.last_play_dj); + } + if (firetable.tagUpdate.adamData.first_play) { + $("#firstPlay").text("first " + firetable.utilities.format_date(firetable.tagUpdate.adamData.first_play) + " by " + firetable.tagUpdate.adamData.first_play_dj); + } + if (firetable.tagUpdate.adamData.playcount) { + if (firetable.tagUpdate.adamData.playcount > 0) { + showPlaycount = true; + $("#playCount").text(firetable.tagUpdate.adamData.playcount + " plays"); + } + } + } + } + $("#track").text(firetable.ui.strip(data.title)); + $("#artist").text(firetable.ui.strip(data.artist)); + $("#songlink").attr("href", data.url); + $("#albumArt").css("background-image", "url(" + data.image + ")"); + var nownow = Date.now(); + var timeSince = nownow - data.started; + if (timeSince <= 0) timeSince = 0; + var secSince = Math.floor(timeSince / 1000); + var timeLeft = data.duration - secSince; + firetable.song = data; + firetable.debug && console.log("NEW TRACK", data); + firetable.debug && console.log('time since:', timeSince); + + if (firetable.lastfm.timer != null) { + clearTimeout(firetable.lastfm.timer); + firetable.lastfm.timer = null; + } + if (firetable.lastfm.sk) { + firetable.lastfm.duration = Math.floor(timeLeft); + firetable.lastfm.songStart = Math.floor((new Date()).getTime() / 1000); + firetable.lastfm.timer = setTimeout(function() { + firetable.lastfm.timer = null; + firetable.lastfm.scrobble(); + }, (timeLeft * 1000) - 3000); + firetable.lastfm.nowPlaying(); + } + + if (data.type == 1) { + $("#scScreen").hide(); + $("#songlink").html(''); + } else if (data.type == 2) { + $("#scScreen").show(); + $("#songlink").html(''); + var biggerImg = data.image.replace('-large', '-t500x500'); + firetable.scImg = biggerImg; + $("#albumArt").css("background-image", "url(" + biggerImg + ")") + try { + setup(biggerImg); + } catch (e) { + firetable.debug && console.log('big image error:', e) + } + } + if (data.type == 1 && firetable.ytLoaded) { + if (!firetable.preview) { + if (firetable.scLoaded) firetable.scwidget.pause(); + if (!firetable.disableMediaPlayback) player.loadVideoById(data.cid, secSince, "large"); + var thevolactual = $("#slider").slider("value"); + player.setVolume(thevolactual); + firetable.scwidget.setVolume(thevolactual); + } + } else if (data.type == 2 && firetable.scLoaded) { + if (!firetable.preview) { + if (firetable.ytLoaded) player.stopVideo(); + firetable.scSeek = timeSince; + if (!firetable.disableMediaPlayback) firetable.scwidget.load("http://api.soundcloud.com/tracks/" + data.cid, { + auto_play: true, + single_active: false, + callback: function() { + var thevolactual = localStorage["firetableVol"]; + player.setVolume(thevolactual); + firetable.scwidget.setVolume(thevolactual); + } + }); + } + } + if (data.cid != 0) { + var nicename = data.djid; + if (ftapi.users[data.djid]) { + if (ftapi.users[data.djid].username) nicename = ftapi.users[data.djid].username; + } + if (firetable.nonpmsg) { + firetable.nonpmsg = false; + } else { + var doTheScrollThing = firetable.utilities.isChatPrettyMuchAtBottom(); + if (showPlaycount) { + $("#chats").append("
    DJ " + nicename + " started playing " + data.title + " by " + data.artist + "
    This song has been played " + firetable.tagUpdate.adamData.playcount + " times.
    ") + } else { + $("#chats").append("
    DJ " + nicename + " started playing " + data.title + " by " + data.artist + "
    ") + } + if (doTheScrollThing) firetable.utilities.scrollToBottom(); + firetable.lastChatPerson = false; + firetable.lastChatId = false; + } + + } + $("#timr").countdown({ + until: timeLeft, + compact: true, + description: "", + format: "MS" + }); + firetable.moveBar = setInterval(function() { + var now = Date.now(); + var sofar = now - firetable.song.started; + var pcnt = (sofar / (firetable.song.duration * 1000)) * 100; + $("#prgbar").css("background", "linear-gradient(90deg, " + firetable.color + " " + pcnt + "%, #151515 " + pcnt + "%)"); + }, 500); + }); + ftapi.events.on("screenStateChanged", function(data) { + firetable.debug && console.log('thescreen:', data); + firetable.screenSyncPos = data; + if (firetable.screenControl == "sync") { + if (data) { + firetable.utilities.screenDown(); + } else { + firetable.utilities.screenUp(); + } + } + }); + ftapi.events.on("danceStateChanged", function(data) { + firetable.debug && console.log('dance check:', data); + if (data) { + $("#deck").addClass("dance"); + } else { + $("#deck").removeClass("dance"); + } + }); + ftapi.events.on("lightsChanged", function(data) { + firetable.debug && console.log('lights check:', data); + if (data) { + firetable.lights = true; + $('.festiveLights').remove(); + var colorThing = firetable.utilities.hexToRGB(firetable.color); + var style = ""; + $("head").append(style); + } else { + $('.festiveLights').remove(); + firetable.lights = false; + } + }); + ftapi.events.on("waitlistChanged", function(data) { + var ok1 = ""; + var cnt = "0"; + if (data) { + var countr = 1; + for (var key in data) { + firetable.debug && console.log('waitlist', data); + if (data.hasOwnProperty(key)) { + cnt = countr; + var removeMe = ""; + if (data[key].removeAfter) removeMe = "departure_board" + ok1 += "
    " + countr + ". " + data[key].name + " " + removeMe + "
    "; + countr++; + } + } + } + $("#label2 .count").text(" (" + cnt + ")"); + $("#justwaitlist").html(ok1); + }); + ftapi.events.on("tableChanged", function(data) { + var ok1 = ""; + if (data) { + var countr = 0; + for (var key in data) { + if (data.hasOwnProperty(key)) { + var removeMe = ""; + if (data[key].removeAfter) removeMe = "departure_board" + + ok1 += "
    " + removeMe + " " + data[key].name + "
    " + data[key].plays + "/" + firetable.playlimit + "
    "; + countr++; + } + } + if (countr < 4) { + ok1 += "
    !addme
    "; + countr++; + for (var i = countr; i < 4; i++) { + ok1 += "
     
    "; + } + } + + } else { + ok1 += "
    !addme
    "; + for (var i = 0; i < 3; i++) { + ok1 += "
     
    "; + } + } + $("#deck").html(ok1); + for (var i = 0; i < 4; i++) { + if (i != firetable.playdex) { + $("#avtr" + i).removeClass("animate"); + $("#djthing" + i).removeClass("djActive"); + + } else { + $("#avtr" + i).addClass("animate"); + $("#djthing" + i).addClass("djActive"); + } + } + }); + ftapi.events.on("spotlightStateChanged", function(data) { + firetable.playdex = data; + for (var i = 0; i < 4; i++) { + if (i != data) { + $("#avtr" + i).removeClass("animate"); + $("#djthing" + i).removeClass("djActive"); + + } else { + $("#avtr" + i).addClass("animate"); + $("#djthing" + i).addClass("djActive"); + } + } + }); + ftapi.events.on("playLimitChanged", function(data) { + firetable.playlimit = data; + for (var i = 0; i < 4; i++) { + $("#plimit" + i).text(data); + } + }); + ftapi.events.on("banListChanged", function(data) { + $("#activeSuspentions").html(""); + for (key in data) { + if (data[key]) { + ftapi.lookup.userByName(key, function(person) { + $("#activeSuspentions").append("
    " + person.username + "
    "); + }); + } + } + }); + ftapi.events.on("userJoined", function(data) { + console.log(data); + var user = data; + var block = ""; + var blockcon = ""; + var herecon = "lens"; + var isIdle = ""; + if (data.idle) { + if (data.idle.isIdle && !data.hostbot) isIdle = "idle"; + if (data.idle.audio == 2) { + herecon = "label_important"; + } + } + if (data.blocked) { + block = "blockd"; + blockcon = "block"; + } + + if (!data.username) data.username = data.userid; + + var destination = "#usersRegular"; + var rolename = ""; + if (data.mod) { + rolename = "mod"; + destination = "#usersMod"; + } + if (data.supermod) { + rolename = "supermod"; + destination = "#usersSuper"; + } + if (data.hostbot) { + rolename = "robot"; + destination = "#usersBot"; + } + + var newUserToAddX = $("
    "); + newUserToAddX.addClass("prson " + block); + newUserToAddX.attr("id", "user" + data.userid); + newUserToAddX.html("
    " + blockcon + "" + herecon + "
    " + data.username + "" + rolename + "joined " + firetable.utilities.format_date(data.joined) + ""); + firetable.utilities.chatAt(newUserToAddX); // adds the click event to @ the user + $(destination).append(newUserToAddX); + }); + ftapi.events.on("userLeft", function(data) { + $("#user" + data.userid).remove(); + }); + ftapi.events.on("userChanged", function(data) { + var user = data; + var block = ""; + var blockcon = ""; + var herecon = "lens"; + var isIdle = ""; + console.log("CHANGE", data) + if (data.idle) { + if (data.idle.isIdle && !data.hostbot) isIdle = "idle"; + if (data.idle.audio == 2) { + herecon = "label_important"; + } + } + if (data.blocked) { + block = "blockd"; + blockcon = "block"; + } + + if (!data.username) data.username = data.userid; + + var destination = "#usersRegular"; + var rolename = ""; + if (data.mod) { + rolename = "mod"; + destination = "#usersMod"; + } + if (data.supermod) { + rolename = "supermod"; + destination = "#usersSuper"; + } + if (data.hostbot) { + rolename = "robot"; + destination = "#usersBot"; + } + + $("#user" + data.userid).html("
    " + blockcon + "" + herecon + "
    " + data.username + "" + rolename + "joined " + firetable.utilities.format_date(data.joined) + ""); + }); + ftapi.events.on("usersChanged", function(okdata) { + if ($("#loggedInName").text() == ftapi.uid) { + if (ftapi.users[ftapi.uid]) { + if (ftapi.users[ftapi.uid].username) { + $("#loggedInName").text(ftapi.users[ftapi.uid].username); + } + } + } + if (ftapi.uid) { + if (ftapi.users[ftapi.uid]) { + if (ftapi.users[ftapi.uid].supermod) { + if ($("#ftSuperCopButton").is(":hidden")) { + $("#ftSuperCopButton").show(); + } + } + } + } + var count = Object.keys(okdata).length; + $("#label1 .count").text(" (" + count + ")"); + firetable.debug && console.log('users:', okdata); + }); + var $chatTemplate = $('#chatKEY').remove(); + ftapi.events.on("newChat", function(chatData) { + var namebo = chatData.id; + var utitle = ""; + + var atBottom = false; + if (firetable.utilities.isChatPrettyMuchAtBottom()) atBottom = true; + + var you = ftapi.uid; + if (ftapi.users[ftapi.uid]) { + if (ftapi.users[ftapi.uid].username) you = ftapi.users[ftapi.uid].username; + } + + if (ftapi.users[chatData.id]) { + if (ftapi.users[chatData.id].username) namebo = ftapi.users[chatData.id].username; + if (ftapi.users[chatData.id].mod) utitle = "mod"; + if (ftapi.users[chatData.id].supermod) utitle = "supermod"; + if (ftapi.users[chatData.id].hostbot) utitle = "robot"; + } else if (chatData.name) { + namebo = chatData.name; + } + + var badoop = false; + if (chatData.txt.match("@" + you, 'i') || chatData.txt.match(/\@everyone/)) { + var oknow = Date.now(); + if (oknow - chatData.time < (10 * 1000)) { + firetable.utilities.playSound("sound"); + if (firetable.desktopNotifyMentions) firetable.utilities.desktopNotify(chatData, namebo); + badoop = true; + } + } + if (chatData.id == firetable.lastChatPerson && !badoop) { + $("#chat" + firetable.lastChatId + " .chatContent").append("
    "); + $("#chatTime" + firetable.lastChatId).text(firetable.utilities.format_time(chatData.time)); + var txtOut = firetable.ui.strip(chatData.txt); + txtOut = firetable.ui.showImages(txtOut); + txtOut = firetable.ui.textToLinks(txtOut); + txtOut = firetable.utilities.emojiShortnamestoUnicode(txtOut); + txtOut = txtOut.replace(/\`(.*?)\`/g, function(x) { + return "" + x.replace(/\`/g, "") + ""; + }); + if (chatData.hidden) txtOut = "[message removed]"; + $("#chattxt" + chatData.chatID).html(txtOut); + var canBeDeleted = false; + if (ftapi.users[ftapi.uid].mod || ftapi.users[ftapi.uid].supermod) { + if (ftapi.users[chatData.id]) { + if (!ftapi.users[chatData.id].mod && !ftapi.users[chatData.id].supermod) { + canBeDeleted = true; + } + } else { + canBeDeleted = true; + } + if (canBeDeleted && !chatData.hidden) { + // add delete button + $("#chattxt" + chatData.chatID).addClass("deleteMe"); + $("#chattxt" + chatData.chatID).append("
    x
    "); + $("#chattxt" + chatData.chatID).find(".modDelete").on('click', function() { + console.log("DELETE CHAT", chatData); + ftapi.actions.deleteChat(chatData.feedID); + }); + } + } + twemoji.parse(document.getElementById("chattxt" + chatData.chatID)); + + } else { + var $chatthing = $chatTemplate.clone(); + $chatthing.attr('id', "chat" + chatData.chatID); + $chatthing.find('.botson').css('background-image', "url(https://indiediscotheque.com/robots/" + chatData.id + namebo + ".png?size=110x110&set=" + firetable.avatarset); + $chatthing.find('.utitle').html(utitle); + $chatthing.find('.chatTime').attr('id', "chatTime" + chatData.chatID).html(firetable.utilities.format_time(chatData.time)); + if (badoop) $chatthing.addClass('badoop'); + var txtOut = firetable.ui.strip(chatData.txt); + txtOut = firetable.ui.showImages(txtOut); + txtOut = firetable.ui.textToLinks(txtOut); + txtOut = firetable.utilities.emojiShortnamestoUnicode(txtOut); + txtOut = txtOut.replace(/\`(.*?)\`/g, function(x) { + return "" + x.replace(/\`/g, "") + ""; + }); + if (chatData.hidden) txtOut = "[message removed]"; + $chatthing.find(".chatText").html(txtOut).attr('id', "chattxt" + chatData.chatID); + console.log(chatData); + + $chatthing.find(".chatName").text(namebo); + firetable.utilities.chatAt($chatthing.find('.botson')); // adds the click event to @ the user + firetable.utilities.chatAt($chatthing.find('.chatName')); // adds the click event to @ the user + twemoji.parse($chatthing.find(".chatText")[0]); + $chatthing.appendTo("#chats"); + try { + if (ftapi.users[ftapi.uid].mod || ftapi.users[ftapi.uid].supermod) { + var canBeDeleted = false; + if (ftapi.users[chatData.id]) { + if (!ftapi.users[chatData.id].mod && !ftapi.users[chatData.id].supermod && !chatData.hidden) { + canBeDeleted = true; + } + } else { + canBeDeleted = true; + } + if (canBeDeleted && !chatData.hidden) { + // add delete button + $chatthing.find(".chatText").addClass("deleteMe"); + $chatthing.find(".chatText").append("
    x
    "); + $chatthing.find(".modDelete").on('click', function() { + ftapi.actions.deleteChat(chatData.feedID); + }); + } + } + } catch (e) { + console.log(e) + } + firetable.lastChatPerson = chatData.id; + firetable.lastChatId = chatData.chatID; + } + + if (chatData.card) { + $("#chattxt" + chatData.chatID).append(""); + + firetable.actions.showCard(chatData.card, chatData.chatID); + firetable.debug && console.log("showin card"); + } + + if (atBottom || ftapi.uid == chatData.id) firetable.utilities.scrollToBottom(); + //else $('#morechats').addClass('show'); + }); + + ftapi.events.on("chatRemoved", function(data) { + console.log("CHAT DELETED", data); + $("#chattxt" + data.chatID).text("[message removed]"); + if (ftapi.users[ftapi.uid].mod || ftapi.users[ftapi.uid].supermod) $("#chattxt" + data.chatID).removeClass("deleteMe"); + }); + + ftapi.events.on("playlistChanged", function(okdata, listID) { + firetable.queue = okdata; + $('#mainqueue').html(""); + for (var key in okdata) { + if (okdata.hasOwnProperty(key)) { + var $newli = $playlistItemTemplate.clone(); + var thisone = okdata[key]; + var psign = ""; + if (key == firetable.preview) { + psign = ""; + } + $newli.attr('id', "pvbar" + key); + $newli.attr("data-key", key); + $newli.attr("data-type", thisone.type); + $newli.find('.previewicon').attr('id', "pv" + key).on('click', function() { + firetable.actions.pview($(this).closest('.pvbar').attr('data-key'), false, $(this).closest('.pvbar').attr('data-type')); + }).html(psign); + $newli.find('.listwords').html(thisone.name); + $newli.find('.bumpsongs').on('click', function() { + firetable.actions.bumpSongInQueue($(this).closest('.pvbar').attr('data-key')) + }); + $newli.find('.bottomsongs').on('click', function() { + var oldID = $(this).closest('.pvbar').attr('data-key'); + ftapi.actions.moveTrackToBottom($(this).closest('.pvbar').attr('data-key'), function(newID) { + if (firetable.preview) { + // visually update preview in the new location if applicable + if (firetable.preview == oldID) { + firetable.preview = newID; + $("#pv" + newID).html(""); + } + } + }); + }); + if (thisone.flagged) { + var flagLabel = "broken"; + var flagIcon = "warning"; + if (thisone.flagged.code == 7) { + flagLabel = "age restricted"; + } else if (thisone.flagged.code >= 8) { + if (thisone.flagged.code == 8) { + // manual broken flagged by mod + flagLabel = "broken (manual)"; + } else if (thisone.flagged.code == 9) { + // low quality + flagLabel = "low audio quality"; + flagIcon = "disc_full"; + } else if (thisone.flagged.code == 10) { + // offtheme + flagLabel = "offtheme"; + flagIcon = "flag"; + } + } + $newli.find('.track-warning').html(" " + flagIcon + " "); + $newli.find('.track-warning').prop('title', 'Flagged as ' + flagLabel + ' on ' + firetable.utilities.format_date(thisone.flagged.date) + '. Click to remove flag.'); + $newli.find('.track-warning').on('click', function() { + ftapi.actions.unflagTrack($(this).closest('.pvbar').attr('data-key')); + $(this).html(""); + }); + + } + $newli.find('.deletesong').on('click', function() { + firetable.actions.deleteSong($(this).closest('.pvbar').attr('data-key')) + }); + $('#mainqueue').append($newli); + } + } + }); + + firetable.ui.LinkGrabber.start(); + + $("#label1").bind("click.lb1tab", firetable.ui.usertab1); + $("#label2").bind("click.lb2tab", firetable.ui.usertab2); + $("#addToQueueBttn").bind("click", function() { + $("#mainqueuestuff").css("display", "none"); + $("#filterMachine").css("display", "none"); + $("#addbox").css("display", "flex"); + $("#cancelqsearch").show(); + $("#qControlButtons").hide(); + + $("#plmanager").css("display", "none"); + }); + + $("#minimodeoptions .tab").bind("click", function(event) { + $("#mainGrid").removeClass().addClass($(this).attr('id')); + $("#minimodeoptions .tab").removeClass('on'); + $(this).addClass('on'); + }); + + $("#plmaker").bind("keyup", function(e) { + if (e.which == 13) { + var val = $("#plmaker").val(); + if (val != "") { + var listid = ftapi.actions.createList(val); + $("#listpicker").append(""); + $("#listpicker").val(listid).change(); + ftapi.actions.switchList(listid); + } + } + }); + $("#cancelqsearch").bind("click", function() { + $("#mainqueuestuff").css("display", "block"); + $("#filterMachine").css("display", "block"); + $("#cancelqsearch").hide(); + $("#qControlButtons").show(); + + $("#addbox").css("display", "none"); + if (firetable.preview) { + if (firetable.preview.slice(0, 5) == "ytcid" || firetable.preview.slice(0, 5) == "sccid") { + $("#pv" + firetable.preview).html(""); + clearTimeout(firetable.ptimeout); + firetable.ptimeout = null; + $("#pvbar" + firetable.preview).css("background-image", "none"); + clearInterval(firetable.movePvBar); + firetable.movePvBar = null; + firetable.preview = false; + //start regular song + var nownow = Date.now(); + var timeSince = nownow - firetable.song.started; + if (timeSince <= 0) timeSince = 0; + + var secSince = Math.floor(timeSince / 1000); + var timeLeft = firetable.song.duration - secSince; + if (firetable.song.type == 1) { + if (!firetable.preview) { + if (firetable.scLoaded) firetable.scwidget.pause(); + if (!firetable.disableMediaPlayback) player.loadVideoById(firetable.song.cid, secSince, "large"); + } + } else if (firetable.song.type == 2) { + if (!firetable.preview) { + if (firetable.ytLoaded) player.stopVideo(); + firetable.scSeek = timeSince; + if (!firetable.disableMediaPlayback) firetable.scwidget.load("http://api.soundcloud.com/tracks/" + firetable.song.cid, { + auto_play: true + }); + } + } + } + } + }); + + $("#grab").bind("click", function() { + var isHidden = $("#stealContain").is(":hidden"); + if (isHidden) { + ftapi.lookup.allLists(function(allPlaylists) { + $("#stealpicker").html(""); + for (var key in allPlaylists) { + if (allPlaylists.hasOwnProperty(key)) { + $("#stealpicker").append(""); + } + } + $('#grab').addClass('on'); + $("#stealContain").css({ + 'top': $('#grab').offset().top + $('#grab').height(), + 'left': $('#grab').offset().left - 16 + }).show(); + }); + + } else { + $('#grab').removeClass('on'); + $("#stealContain").hide(); + } + }); + $("#shuffleQueue").bind("click", firetable.actions.shuffleQueue); + $("#history").bind("click", function() { + $("#thehistoryWrap").slideToggle().css('top', $('#stage').outerHeight() + $('#topbar').outerHeight()); + $(this).toggleClass('on'); + }); + $("#startMerge").bind("click", function() { + var source = $("#mergepicker").val(); + var sourceName = $("#mergepicker option:selected").text(); + var dest = $("#mergepicker2").val(); + var destName = $("#mergepicker2 option:selected").text(); + $("#mergeSetup").hide(); + $("#mergeHappening").show(); + firetable.debug && console.log(sourceName + " -> " + destName); + firetable.actions.mergeLists(source, dest, sourceName); + }); + $("#mergeOK").bind("click", function() { + $("#mergeSetup").show(); + $("#mergeCompleted").hide(); + $("#mergeHappening").hide(); + $("#mergeContain").hide(); + }); + $("#mergeLists").bind("click", function() { + var $this = $(this); + var isHidden = $("#mergeContain").is(":hidden"); + if (isHidden) { + ftapi.lookup.allLists(function(allPlaylists) { + $("#mergepicker").html(""); + $("#mergepicker2").html(""); + for (var key in allPlaylists) { + if (allPlaylists.hasOwnProperty(key)) { + $("#mergepicker").append(""); + $("#mergepicker2").append(""); + } + } + if (ftapi.users[ftapi.uid]) { + if (ftapi.users[ftapi.uid].selectedList) { + $("#mergepicker").val(ftapi.users[ftapi.uid].selectedList).change(); + $("#mergepicker2").val(-1).change(); + } + } + $("#mergeContain").show(); + $this.addClass('on'); + }); + + } else { + $("#mergeContain").hide(); + $this.removeClass('on'); + } + }); + $("#reloadtrack").bind("click", firetable.actions.reloadtrack); + + $("#importDubGo").bind("click", firetable.actions.dubtrackImport); + + $("#volstatus").bind("click", function() { + firetable.actions.muteToggle(); + }); + $(".openModal").bind("click", function() { + var modalContentID = $(this).attr('data-modal'); + $(".modalThing").removeClass('show'); + $("#overlay").addClass('show'); + $("#" + modalContentID).addClass('show'); + }); + $(".closeModal").bind("click", function() { + $("#overlay").removeClass('show'); + $("#deletepicker").html(""); + firetable.actions.cardCase(); + $("#plMachine").val(""); + }); + + $("#cardCaseButton").bind("click", function() { + firetable.actions.cardCase(); + $("#cardsOverlay").show(); + }); + $("#pickerNav").on("click", "span", function() { + try { + var sec = $(this)[0].id; + firetable.emojis.sec(sec); + } catch (s) {} + }); + $("#pickEmoji").bind("click", function() { + //toggle emoji picker + if ($("#emojiPicker").is(":hidden")) { + $(this).addClass('on'); + $("#emojiPicker").slideDown(function() { + $('#pickerSearch').focus(); + }); + + if (!firetable.pickerInit) { + const makeRequest = async () => { + twemoji.parse(document.getElementById("pickerResults")); + return true; + } + + makeRequest() + } + } else { + $(this).removeClass('on'); + $("#emojiPicker").slideUp(function() { + $('#pickerSearch').val('').trigger('change'); + $('#newchat').focus(); + }); + } + }); + + $("#morechats .butt").bind("click", function() { + firetable.utilities.scrollToBottom(); + }); + + $("#fire").bind("click", function() { + ftapi.actions.sendChat(":fire:"); + $("#cloud_with_rain").removeClass("on"); + $("#fire").addClass("on"); + }); + + $("#cloud_with_rain").bind("click", function() { + ftapi.actions.sendChat(":cloud_with_rain:"); + $("#cloud_with_rain").addClass("on"); + $("#fire").removeClass("on"); + }); + + + //SETTINGS TOGGLES + + + $('#badoopToggle').change(function() { + if (this.checked) { + firetable.debug && console.log("badoop on"); + localStorage["firetableBadoop"] = true; + firetable.playBadoop = true; + } else { + firetable.debug && console.log("badoop off"); + localStorage["firetableBadoop"] = false; + firetable.playBadoop = false; + + } + }); + $('#showImagesToggle').change(function() { + if (this.checked) { + firetable.debug && console.log("show images on"); + localStorage["firetableShowImages"] = true; + firetable.showImages = true; + } else { + firetable.debug && console.log("show images off"); + localStorage["firetableShowImages"] = false; + firetable.showImages = false; + + } + }); + $('#mediaDisableToggle').change(function() { + if (this.checked) { + firetable.debug && console.log("media disable on"); + localStorage["firetableDisableMedia"] = true; + firetable.disableMediaPlayback = true; + if (firetable.scLoaded) firetable.scwidget.pause(); + if (firetable.ytLoaded) player.stopVideo(); + firetable.ui.hidePlayerControls(); + } else { + firetable.debug && console.log("media disable off"); + localStorage["firetableDisableMedia"] = false; + firetable.disableMediaPlayback = false; + firetable.ui.showPlayerControls(); + firetable.actions.reloadtrack(); + } + }); + $('#showAvatarsToggle').change(function() { + if (this.checked) { + firetable.debug && console.log("show avatars on"); + localStorage["firetableShowAvatars"] = true; + firetable.showAvatars = true; + document.getElementById("actualChat").classList.remove("avatarsOff"); + } else { + firetable.debug && console.log("show avatars off"); + localStorage["firetableShowAvatars"] = false; + firetable.showAvatars = false; + document.getElementById("actualChat").classList.add("avatarsOff"); + } + }); + $(document).on('click', '.hideImage', function(e) { + e.stopPropagation(); + e.preventDefault(); + $(this).closest('.chatText').toggleClass('hideImg'); + }); + $('#desktopNotifyMentionsToggle').change(function() { + if (this.checked) { + firetable.debug && console.log("dtnm on"); + localStorage["firetableDTNM"] = true; + firetable.desktopNotifyMentions = true; + if (Notification) { + if (Notification.permission !== "granted") { + Notification.requestPermission(); + } + } + } else { + firetable.debug && console.log("dtnm off"); + localStorage["firetableDTNM"] = false; + firetable.desktopNotifyMentions = false; + + } + }); + + $('input[type=radio][name=screenControl]').change(function() { + firetable.debug && console.log('screen control:', this.value); + localStorage["firetableScreenControl"] = this.value; + firetable.screenControl = this.value; + if (this.value == "off") { + firetable.utilities.screenUp(); + } else if (this.value == "on") { + firetable.utilities.screenDown(); + } else if (this.value == "sync") { + if (firetable.screenSyncPos) { + firetable.utilities.screenDown(); + } else { + firetable.utilities.screenUp(); + } + } + }); + + + $("#stealpicker").change(function() { + var dest = $("#stealpicker").val(); + if (dest == "-1") return; + if (firetable.song.cid != 0) { + var title = firetable.song.artist + " - " + firetable.song.title; + $("#grab").removeClass('on'); + ftapi.actions.addToList(firetable.song.type, title, firetable.song.cid, dest); + $("#stealContain").hide(); + } + }); + + $("#pldeleteButton").bind("click", function() { + var val = $("#deletepicker").val(); + firetable.debug && console.log('playlist delete:', val); + if (ftapi.users[ftapi.uid]) { + if (ftapi.users[ftapi.uid].selectedList) { + if (ftapi.users[ftapi.uid].selectedList == val) { + $("#listpicker").val("0").change(); + } + } + } + ftapi.actions.deleteList(val); + $("#pdopt" + val).remove(); + $("#overlay").removeClass('show'); + }); + $("#plimportLauncher").bind("click", function() { + $("#overlay").addClass('show'); + $(".modalThing").removeClass('show'); + $('#importPromptBox').addClass('show'); + }); + $("#pldeleteLauncher").bind("click", function() { + ftapi.lookup.allLists(function(allPlaylists) { + $("#deletepicker").html(""); + for (var key in allPlaylists) { + if (allPlaylists.hasOwnProperty(key)) { + $("#deletepicker").append(""); + } + } + $("#overlay").addClass('show'); + $(".modalThing").removeClass('show'); + $('#deletePromptBox').addClass('show'); + }); + }); + $("#pickerSearch").on("change paste keyup", function() { + firetable.emojis.niceSearch($("#pickerSearch").val()); + }); + $("#queueFilter").on("change paste keyup", function() { + firetable.actions.filterQueue($("#queueFilter").val()); + }); + $("#pickerResults").on("click", "span", function() { + try { + var oldval = $("#newchat").val(); + var newval = oldval + ":" + $(this).attr("title").trim() + ":"; + $("#newchat").focus().val(newval); + + } catch (s) {} + }); + + firetable.ui.loginEventsInit(); + + $("#ytsearchSelect").bind("click", function() { + $("#scsearchSelect").removeClass("on"); + $("#ytsearchSelect").addClass("on"); + firetable.searchSelectsChoice = 1; + }); + $("#scsearchSelect").bind("click", function() { + $("#ytsearchSelect").removeClass("on"); + $("#scsearchSelect").addClass("on"); + firetable.searchSelectsChoice = 2; + }); + $("#ytimportchoice").bind("click", function() { + firetable.debug && console.log("yt import"); + firetable.importSelectsChoice = 1; + }); + $("#scimportchoice").bind("click", function() { + firetable.debug && console.log("sc import"); + firetable.importSelectsChoice = 2; + }); + $("#dtimportchoice").bind("click", function() { + firetable.debug && console.log("dt import"); + firetable.importSelectsChoice = 3; + }); + $(document).on("keyup", ".tagMachine", function(e) { + if (e.which == 13) { + var thetype = $(this).closest('.pvbar').attr('data-type'); + var songCid = $(this).closest('.pvbar').attr('data-cid'); + var histID = $(this).closest('.pvbar').attr('data-histid'); + var val = $(this).val(); + if (val != "") { + var yargo = val.split(" - "); + var theartist = yargo[0]; + var thetitle = yargo[1]; + if (!theartist || !thetitle) { + alert("check yr tags"); + } else { + ftapi.actions.editTag(thetype, songCid, val, histID); + $(this).closest('.editing').removeClass('editing').next('.tagPromptBox').remove(); + } + } + } + }); + $("#changeUsername").bind("keyup", function(e) { + if (e.which == 13) { + var oldDjName = ftapi.users[ftapi.uid].username; + var newDjName = $("#changeUsername").val(); + $("#usernameResponse").html(""); + if (newDjName != "") { + // try to change name + ftapi.actions.changeName(newDjName, function(error) { + if (error) { + alert(error); + $("#usernameResponse").text(error); + } else { + $("#usernameResponse").text("Great job! Your name is now " + newDjName); + $("#loggedInName").text(newDjName); + } + }); + } + } + }); + $('#dubtrackimportfile').bind('change', firetable.ui.dubtrackImportFileSelect); + $("#supercopSearch").bind("keyup", function(e) { + if (e.which == 13) { + var val = $("#supercopSearch").val(); + $("#supercopResponse").html(""); + if (val != "") { + //begin user search... + ftapi.lookup.userByName(val, function(person) { + //check search results + if (person) { + //found something! + if (!person.supermod) { + ftapi.actions.banUser(person.userid); + $("#supercopResponse").html(person.username + " suspended."); + + } else { + $("#supercopResponse").text("Can not suspend that (or any) supermod."); + } + } else { + $("#supercopResponse").text(val + " not found..."); + } + }); + } + } + }); + $("#importSources .tab").bind("click", function(e) { + var searchFrom = firetable.importSelectsChoice; + if (searchFrom == 3) { + $("#importDubContent").show(); + $("#importContent").hide(); + } else { + $("#importDubContent").hide(); + $("#importContent").show(); + } + $(this).siblings().removeClass('on'); + $(this).addClass('on'); + }); + $("#plMachine").bind("keyup", function(e) { + if (e.which == 13) { + var val = $("#plMachine").val(); + if (val != "") { + $("#importResults").html(""); + $("#plMachine").val(""); + var searchFrom = firetable.importSelectsChoice; + if (searchFrom == 1) { + var listID; + var directLink = false; + //see if this is a particular list's url... + if (val.match(/youtube.com\/watch/) || val.match(/youtube.com\/playlist/)) { + function getQueryStringValue(str, key) { + return unescape(str.replace(new RegExp("^(?:.*[&\\?]" + escape(key).replace(/[\.\+\*]/g, "\\$&") + "(?:\\=([^&]*))?)?.*$", "i"), "$1")); + } + listID = getQueryStringValue(val, "list"); + if (listID) directLink = true; + } + + if (directLink) { + function keyWordsearch() { + gapi.client.setApiKey(ftconfigs.youtubeKey); + gapi.client.load('youtube', 'v3', function() { + makeRequest(); + }); + } + + function makeRequest() { + var request = gapi.client.youtube.playlists.list({ + id: listID, + part: 'snippet' + }); + request.execute(function(response) { + if (response.result) { + if (response.result.items) { + if (response.result.items.length === 1) { + var item = response.result.items[0]; + vidTitle = item.snippet.title; + $("#importResults").append("
    " + item.snippet.title + " by " + item.snippet.channelTitle + "
    "); + } else { + // no result + } + } + } + }) + } + keyWordsearch(); + } else { + //youtube + function keyWordsearch() { + gapi.client.setApiKey(ftconfigs.youtubeKey); + gapi.client.load('youtube', 'v3', function() { + makeRequest(); + }); + } + + function makeRequest() { + var request = gapi.client.youtube.search.list({ + q: val, + type: 'playlist', + part: 'snippet', + maxResults: 15 + }); + request.execute(function(response) { + var srchItems = response.result.items; + firetable.debug && console.log('import search results:', response); + $.each(srchItems, function(index, item) { + vidTitle = item.snippet.title; + $("#importResults").append("
    " + item.snippet.title + " by " + item.snippet.channelTitle + "
    "); + }) + }) + } + keyWordsearch(); + } + + } else if (searchFrom == 2) { + var listData; + var directLink = false; + //see if this is a particular list's url... + console.log(val); + if (val.match(/.*\/\/soundcloud\.com\/.*\/sets\/.*/)) { + firetable.actions.resolveSCLink(val, function(item) { + if (item) { + if (item.sharing == "public" && item.kind == "playlist") { + $("#importResults").append("
    " + item.title + " by " + item.user.username + " (" + item.track_count + " songs)
    "); + } + } + }); + //var getList = SC.resolve(val).then(finishUp); + + } else { + //cloud sound world dot com + SC.get('/playlists', { + q: val + }).then(function(lists) { + for (var i = 0; i < lists.length; i++) { + var item = lists[i]; + if (item.sharing == "public") { + $("#importResults").append("
    " + item.title + " by " + item.user.username + " (" + item.track_count + " songs)
    "); + } + } + }); + } + } + } + } + }); + var $searchItemTemplate = $('#searchResults .pvbar').remove(); + $("#qsearch").bind("keyup", function(e) { + if (e.which == 13) { + var txt = $("#qsearch").val(); + if (firetable.searchSelectsChoice == 1) { + var showResults = function(response) { + firetable.debug && console.log('queue search:', response); + // $("#qsearch").val(""); + $('#searchResults').html(""); + + if (firetable.preview) { + if (firetable.preview.slice(0, 5) == "ytcid" || firetable.preview.slice(0, 5) == "sccid") { + $("#pv" + firetable.preview).html(""); + clearTimeout(firetable.ptimeout); + firetable.ptimeout = null; + $("#pvbar" + firetable.preview).css("background-image", "none"); + clearInterval(firetable.movePvBar); + firetable.movePvBar = null; + firetable.preview = false; + //start regular song + var nownow = Date.now(); + var timeSince = nownow - firetable.song.started; + if (timeSince <= 0) timeSince = 0; + + var secSince = Math.floor(timeSince / 1000); + var timeLeft = firetable.song.duration - secSince; + if (firetable.song.type == 1) { + if (!firetable.preview) { + if (firetable.scLoaded) firetable.scwidget.pause(); + if (!firetable.disableMediaPlayback) player.loadVideoById(firetable.song.cid, secSince, "large"); + } + } else if (firetable.song.type == 2) { + if (!firetable.preview) { + if (firetable.ytLoaded) player.stopVideo(); + firetable.scSeek = timeSince; + if (!firetable.disableMediaPlayback) firetable.scwidget.load("http://api.soundcloud.com/tracks/" + firetable.song.cid, { + auto_play: true + }); + } + } + } + } + var srchItems = response.result.items; + $.each(srchItems, function(index, item) { + console.log(item); + var thecid; + if (item.kind == "youtube#searchResult") { + thecid = item.id.videoId; + } else if (item.kind == "youtube#video") { + thecid = item.id; + } + vidtitle = item.snippet.title; + var yargo = item.snippet.title.split(" - "); + var sartist = yargo[0]; + var stitle = yargo[1]; + if (!stitle) { + // yt title not formatted artist - title. use uploader name instead as artist + stitle = sartist; + sartist = item.snippet.channelTitle.replace(" - Topic", ""); + } + vidTitle = sartist + " - " + stitle; + var pkey = "ytcid" + thecid; + var $srli = $searchItemTemplate.clone(); + $srli.attr('id', "pvbar" + pkey); + $srli.attr("data-key", pkey); + $srli.attr("data-cid", thecid); + $srli.find('.previewicon').attr('id', "pv" + pkey).on('click', function() { + firetable.actions.pview($(this).closest('.pvbar').attr('data-key'), true, 1); + }); + $srli.find('.listwords').html(vidTitle); + $srli.find('.queuetrack').on('click', function() { + firetable.actions.queueTrack( + $(this).closest('.pvbar').attr('data-cid'), + firetable.utilities.htmlEscape($(this).closest('.pvbar').find('.listwords').text()), + 1 + ); + }); + $("#searchResults").append($srli); + }) + }; + var directLink = false; + var thecid = false; + //see if this is a particular track's url... + if (txt.match(/youtube.com\/watch/)) { + function getQueryStringValue(str, key) { + return unescape(str.replace(new RegExp("^(?:.*[&\\?]" + escape(key).replace(/[\.\+\*]/g, "\\$&") + "(?:\\=([^&]*))?)?.*$", "i"), "$1")); + } + thecid = getQueryStringValue(txt, "v"); + if (thecid) directLink = true; + } + if (directLink) { + firetable.debug && console.log("direct yt link found"); + + function keyWordsearch() { + gapi.client.setApiKey(ftconfigs.youtubeKey); + gapi.client.load('youtube', 'v3', function() { + makeRequest(); + }); + } + + function makeRequest() { + var request = gapi.client.youtube.videos.list({ + id: thecid, + part: 'snippet', + maxResults: 1 + }); + request.execute(function(response) { + console.log(response); + showResults(response); + }) + } + keyWordsearch(); + } else { + function keyWordsearch() { + gapi.client.setApiKey(ftconfigs.youtubeKey); + gapi.client.load('youtube', 'v3', function() { + makeRequest(); + }); + } + + function makeRequest() { + var q = $('#qsearch').val(); + $('#searchResults').html("Searching..."); + + var request = gapi.client.youtube.search.list({ + q: q, + type: 'video', + part: 'snippet', + maxResults: 15 + }); + request.execute(function(response) { + showResults(response); + }) + } + keyWordsearch(); + } + } else if (firetable.searchSelectsChoice == 2) { + var q = $('#qsearch').val(); + var showResults = function(tracks) { + firetable.debug && console.log('sc tracks:', tracks); + // $("#qsearch").val(""); + $('#searchResults').html(""); + + if (firetable.preview) { + if (firetable.preview.slice(0, 5) == "ytcid" || firetable.preview.slice(0, 5) == "sccid") { + $("#pv" + firetable.preview).html(""); + clearTimeout(firetable.ptimeout); + firetable.ptimeout = null + $("#pvbar" + firetable.preview).css("background-image", "none"); + clearInterval(firetable.movePvBar); + firetable.movePvBar = null; + firetable.preview = false; + //start regular song + var nownow = Date.now(); + var timeSince = nownow - firetable.song.started; + if (timeSince <= 0) timeSince = 0; + + var secSince = Math.floor(timeSince / 1000); + var timeLeft = firetable.song.duration - secSince; + if (firetable.song.type == 1) { + if (!firetable.preview) { + firetable.scwidget.pause(); + if (!firetable.disableMediaPlayback) player.loadVideoById(firetable.song.cid, secSince, "large"); + } + } else if (firetable.song.type == 2) { + if (!firetable.preview) { + if (firetable.ytLoaded) player.stopVideo(); + firetable.scSeek = timeSince; + if (!firetable.disableMediaPlayback) firetable.scwidget.load("http://api.soundcloud.com/tracks/" + firetable.song.cid, { + auto_play: true + }); + } + } + } + } + var srchItems = tracks; + $.each(srchItems, function(index, item) { + vidTitle = item.title; + var yargo = item.title.split(" - "); + var sartist = yargo[0]; + var stitle = yargo[1]; + if (!stitle) { + stitle = sartist; + sartist = item.user.username; + } + vidTitle = sartist + " - " + stitle; + var pkey = "sccid" + item.id; + var $srli = $searchItemTemplate.clone(); + $srli.attr('id', "pvbar" + pkey); + $srli.attr("data-key", pkey); + $srli.attr("data-cid", item.id); + $srli.find('.previewicon').attr('id', "pv" + pkey).on('click', function() { + firetable.actions.pview( + $(this).closest('.pvbar').attr('data-key'), + true, + 2 + ); + }); + $srli.find('.listwords').html(vidTitle); + $srli.find('.queuetrack').on('click', function() { + firetable.actions.queueTrack( + $(this).closest('.pvbar').attr('data-cid'), + firetable.utilities.htmlEscape($(this).closest('.pvbar').find('.listwords').text()), + 2 + ); + }); + $("#searchResults").append($srli); + }) + } + var directLink = false; + if (q.match(/:\/\/soundcloud\.com\//)) { + directLink = true; + } + $('#searchResults').html("Searching..."); + if (directLink) { + firetable.debug && console.log("sc direct link found"); + firetable.actions.resolveSCLink(q, function(item) { + var items = []; + if (item.kind == "track") items.push(item); + showResults(items); + }); + //var getList = SC.resolve(q).then(finishUp); + } else { + SC.get('/tracks', { + q: q + }).then(function(tracks) { + showResults(tracks); + }); + } + } + } + }); + $("#newchat").bind("keypress", function(e) { + firetable.debug && console.log('chat key', e.key); + if (e.key == "Enter") { + var txt = $("#newchat").val(); + if (txt == "") return; + var matches = txt.match(/^(?:[\/])(\w+)\s*(.*)/i); + if (txt == ":fire:" || txt == "🔥") { + $("#cloud_with_rain").removeClass("on"); + $("#fire").addClass("on"); + } else if (txt == ":cloud_with_rain:" || txt == "🌧") { + $("#cloud_with_rain").addClass("on"); + $("#fire").removeClass("on"); + } + if (matches) { + var command = matches[1].toLowerCase(); + var args = matches[2]; + if (command == "mod") { + var personToMod = firetable.actions.uidLookup(args); + if (personToMod) { + ftapi.actions.modUser(personToMod); + } + } else if (command == "unmod") { + var personToMod = firetable.actions.uidLookup(args); + if (personToMod) { + ftapi.actions.unmodUser(personToMod); + } + } else if (command == "block") { + if (args) { + ftapi.actions.blockUser(args, function(response) { + console.log(response); + firetable.actions.localChatResponse(response); + }); + } + } else if (command == "unblock") { + if (args) { + ftapi.actions.unblockUser(args, function(response) { + console.log(response); + + firetable.actions.localChatResponse(response); + }); + } + } else if (command == "hot") { + ftapi.actions.sendChat(":fire:"); + $("#cloud_with_rain").removeClass("on"); + $("#fire").addClass("on"); + } else if (command == "storm") { + ftapi.actions.sendChat(":cloud_with_rain:"); + $("#cloud_with_rain").addClass("on"); + $("#fire").removeClass("on"); + } else if (command == "shrug") { + var thingtosay = "¯\\_(ツ)_/¯"; + if (args) thingtosay = args + " ¯\\_(ツ)_/¯"; + ftapi.actions.sendChat(thingtosay); + } else if (command == "tableflip") { + var thingtosay = "(╯°□°)╯︵ ┻━┻"; + if (args) thingtosay = args + " (╯°□°)╯︵ ┻━┻"; + ftapi.actions.sendChat(thingtosay); + } else if (command == "unflip") { + var thingtosay = "┬─┬ ノ( ゜-゜ノ)"; + if (args) thingtosay = args + " ┬─┬ ノ( ゜-゜ノ)"; + ftapi.actions.sendChat(thingtosay); + } + } else { + ftapi.actions.sendChat(txt); + } + $("#newchat").val(""); + $("#emojiPicker").slideUp(); + $("#pickEmoji").removeClass("on"); + firetable.utilities.exitAtLand(); + } else if (e.key == "@") { // open the door to @ land + if (firetable.atLand) { // double @@ + firetable.utilities.exitAtLand(); + } else { // first step into @ land + firetable.utilities.initAtLand(); + for (var user of firetable.atUsersFiltered) { + $('#atPicker').addClass('show'); + $('
    ').appendTo('#atPicker'); + } + } + } else if (firetable.atLand) { // we're in @ land + if (e.key == " " || e.key == "Spacebar") { // we've got what we want + firetable.utilities.exitAtLand(); + } else if (!e.key.match(/[0-9a-zA-Z_]/)) { // not possibly a characer from a name + firetable.atString += e.key; + $('#atPicker').html(''); + $('
    Usernames cannot contain "' + e.key + '"
    ').appendTo('#atPicker'); + } else { // we're still in @ land + firetable.atString += e.key; + firetable.utilities.updateAtLand(); + } + } + }); + $("#newchat").bind("keyup", function(e) { + if (firetable.atLand) { // we're in @ land + if (e.key == "Backspace") { + if (!firetable.atString) { // deleting the @, exit @ land + firetable.utilities.exitAtLand(); + } else { // still got someone we're lookin for + firetable.atString = firetable.atString.slice(0, -1); + firetable.utilities.updateAtLand(); + } + } else if (e.key == "ArrowUp") { // i see my @, go up! + $('#atPicker .butt:last').focus(); + } else if (e.key == "ArrowDown") { // i see my @, go down! + $('#atPicker .butt:first').focus(); + } + } + }); + $("#newchat").bind("keydown", function(e) { + if (e.key == "Tab") { + if (firetable.atUsersFiltered.length === 1) { + $("#newchat").one("blur", function(e) { + $("#newchat").focus().val($("#newchat").val()); + }); + firetable.utilities.chooseAt(firetable.atUsersFiltered[0]); + } else { + firetable.utilities.exitAtLand(); + } + } + }); + $(document).on('click', '#atPicker .butt', function(e) { + e.preventDefault(); + firetable.utilities.chooseAt($(this).text().replace("@", "")); + setTimeout(() => { + var tempText = $("#newchat").val(); + $('#newchat').focus().val(''); + $('#newchat').val(tempText); + }, 250); + }); + $(document).on('keyup', '#atPicker .butt:focus', function(e) { + if (e.key == "ArrowUp") { + if ($('#atPicker .butt:focus').parent().prev().length) { + $('#atPicker .butt:focus').parent().prev().find('.butt').focus(); + } else { + $('#atPicker .butt:last').focus(); + } + } else if (e.key == "ArrowDown") { + if ($('#atPicker .butt:focus').parent().next().length) { + $('#atPicker .butt:focus').parent().next().find('.butt').focus(); + } else { + $('#atPicker .butt:first').focus(); + } + } + }); + + ftapi.events.on("colorsChanged", function(data) { + firetable.debug && console.log("COLOR CHANGE!", data); + + firetable.color = data.color; + firetable.countcolor = data.txt; + if (data.color == "#fff" || data.color == "#7f7f7f") { + firetable.color = firetable.orange; + firetable.countcolor = "#fff"; + $("#stage").css("background-color", "#fff"); + } else { + $("#stage").css("background-color", data.color); + } + /* + if (firetable.countcolor == "#fff"){ + firetable.countcolor = "#ffffffc9"; + } else if (firetable.countcolor == "#000"){ + firetable.debug && console.log("a") + firetable.countcolor = "#000000c9"; + } + $("#stage").css("color", firetable.countcolor); + */ + $('.customColorStyles').remove(); + + $('.festiveLights').remove(); + + var colorThing = firetable.utilities.hexToRGB(firetable.color); + $("head").append(""); + + if (firetable.lights) { + var style = ""; + $("head").append(style); + } + + }); + }, + usertab1: function() { + $("#allusersWrap").css("display", "block"); + $("#justwaitWrap").css("display", "none"); + $("#usertabs").find(".on").removeClass("on"); + $("#label1").addClass("on"); + }, + usertab2: function() { + $("#usertabs").find(".on").removeClass("on"); + $("#label2").addClass("on"); + $("#allusersWrap").css("display", "none"); + $("#justwaitWrap").css("display", "block"); + + }, + LinkGrabber: { + textarea: null, + + /* Textarea Management */ + + attach_ta: function(event) { + if (!$.contains(document.getElementById("queuelist"), event.target)) return; + if (firetable.ui.LinkGrabber.textarea != null) return; + + var textarea = firetable.ui.LinkGrabber.textarea = document.createElement("textarea"); + textarea.setAttribute("style", "position: fixed; width: 100%; margin: 0; top: 0; bottom: 0; right: 0; left: 0; z-index: 99999999"); + textarea.style.opacity = "0.000000000000000001"; + + var body = document.getElementsByTagName("body")[0]; + body.appendChild(textarea); + + textarea.oninput = firetable.ui.LinkGrabber.evt_got_link; + }, + + detach_ta: function() { + if (firetable.ui.LinkGrabber.textarea == null) return; + var textarea = firetable.ui.LinkGrabber.textarea; + + textarea.parentNode.removeChild(textarea); + firetable.ui.LinkGrabber.textarea = null; + }, + + /* Event Handlers */ + + evt_drag_over: function(event) { + firetable.ui.LinkGrabber.attach_ta(event); //Create TA overlay + }, + + evt_got_link: function() { + /* THIS IS WHERE WE HANDLE THE RECEIVED LINK */ + + var link = firetable.ui.LinkGrabber.textarea.value; + firetable.debug && console.log("NEW LINK RECEIVED VIA THE DRAGON'S DROP. " + link); + firetable.actions.queueFromLink(link); + + firetable.ui.LinkGrabber.detach_ta(); + }, + + evt_drag_out: function(e) { + if (e.target == firetable.ui.LinkGrabber.textarea) firetable.ui.LinkGrabber.detach_ta(); + }, + + /* Start/Stop */ + + start: function() { + document.addEventListener("dragover", firetable.ui.LinkGrabber.evt_drag_over, false); + document.addEventListener("dragenter", firetable.ui.LinkGrabber.evt_drag_over, false); + + document.addEventListener("mouseup", firetable.ui.LinkGrabber.evt_drag_out, false); + document.addEventListener("dragleave", firetable.ui.LinkGrabber.evt_drag_out, false); + }, + + stop: function() { + document.removeEventListener("dragover", firetable.ui.LinkGrabber.evt_drag_over); + document.removeEventListener("dragenter", firetable.ui.LinkGrabber.evt_drag_over); + + document.removeEventListener("mouseup", firetable.ui.LinkGrabber.evt_drag_out); + document.removeEventListener("dragleave", firetable.ui.LinkGrabber.evt_drag_out); + + firetable.ui.LinkGrabber.detach_ta(); + } + } + + +} + + + +let isLoaded = false; +let glitch; +let imgSrc = ''; + +function setup(useThis) { + if (!useThis) useThis = firetable.scImg; + background(0); + let cnv = createCanvas($('#djStage').outerWidth(), $('#djStage').outerHeight()); + cnv.parent('scScreen'); + loadImage(useThis, function(img) { + glitch = new Glitch(img); + isLoaded = true; + var $can = $('#scScreen canvas'); + var canrat = $can.width() / $can.height(); + $can.data('ratio', canrat); + }); +} + +function draw() { + clear(); + background(0); + + if (isLoaded) { + glitch.show(); + } + + // fill(255, 255, 255); + // textSize(14); + // text('FPS: ' + floor(frameRate()), 20, 30); + +} + +class Glitch { + constructor(img) { + this.channelLen = 4; + this.imgOrigin = img; + this.imgOrigin.loadPixels(); + this.copyData = []; + this.flowLineImgs = []; + this.shiftLineImgs = []; + this.shiftRGBs = []; + this.scatImgs = []; + this.throughFlag = true; + this.copyData = new Uint8ClampedArray(this.imgOrigin.pixels); + + // flow line + for (let i = 0; i < 1; i++) { + let o = { + pixels: null, + t1: floor(random(0, 1000)), + speed: floor(random(4, 24)), + randX: floor(random(24, 80)) + }; + this.flowLineImgs.push(o); + } + + // shift line + for (let i = 0; i < 6; i++) { + let o = null; + this.shiftLineImgs.push(o); + } + + // shift RGB + for (let i = 0; i < 1; i++) { + let o = null; + this.shiftRGBs.push(o); + } + + // scat imgs + for (let i = 0; i < 3; i++) { + let scatImg = { + img: null, + x: 0, + y: 0 + }; + this.scatImgs.push(scatImg); + } + } + + replaceData(destImg, srcPixels) { + for (let y = 0; y < destImg.height; y++) { + for (let x = 0; x < destImg.width; x++) { + let r, g, b, a; + let index; + index = (y * destImg.width + x) * this.channelLen; + r = index; + g = index + 1; + b = index + 2; + a = index + 3; + destImg.pixels[r] = srcPixels[r]; + destImg.pixels[g] = srcPixels[g]; + destImg.pixels[b] = srcPixels[b]; + destImg.pixels[a] = srcPixels[a]; + } + } + destImg.updatePixels(); + } + + flowLine(srcImg, obj) { + + let destPixels, + tempY; + destPixels = new Uint8ClampedArray(srcImg.pixels); + obj.t1 %= srcImg.height; + obj.t1 += obj.speed; + //tempY = floor(noise(obj.t1) * srcImg.height); + tempY = floor(obj.t1); + for (let y = 0; y < srcImg.height; y++) { + if (tempY === y) { + for (let x = 0; x < srcImg.width; x++) { + let r, g, b, a; + let index; + index = (y * srcImg.width + x) * this.channelLen; + r = index; + g = index + 1; + b = index + 2; + a = index + 3; + destPixels[r] = srcImg.pixels[r] + obj.randX; + destPixels[g] = srcImg.pixels[g] + obj.randX; + destPixels[b] = srcImg.pixels[b] + obj.randX; + destPixels[a] = srcImg.pixels[a]; + } + } + } + return destPixels; + } + + shiftLine(srcImg) { + + let offsetX; + let rangeMin, rangeMax; + let destPixels; + let rangeH; + + destPixels = new Uint8ClampedArray(srcImg.pixels); + rangeH = srcImg.height; + rangeMin = floor(random(0, rangeH)); + rangeMax = rangeMin + floor(random(1, rangeH - rangeMin)); + offsetX = this.channelLen * floor(random(-40, 40)); + + for (let y = 0; y < srcImg.height; y++) { + if (y > rangeMin && y < rangeMax) { + for (let x = 0; x < srcImg.width; x++) { + let r, g, b, a; + let r2, g2, b2, a2; + let index; + + index = (y * srcImg.width + x) * this.channelLen; + r = index; + g = index + 1; + b = index + 2; + a = index + 3; + r2 = r + offsetX; + g2 = g + offsetX; + b2 = b + offsetX; + destPixels[r] = srcImg.pixels[r2]; + destPixels[g] = srcImg.pixels[g2]; + destPixels[b] = srcImg.pixels[b2]; + destPixels[a] = srcImg.pixels[a]; + } + } + } + return destPixels; + } + + shiftRGB(srcImg) { + + let randR, randG, randB; + let destPixels; + let range; + + range = 16; + destPixels = new Uint8ClampedArray(srcImg.pixels); + randR = (floor(random(-range, range)) * srcImg.width + floor(random(-range, range))) * this.channelLen; + randG = (floor(random(-range, range)) * srcImg.width + floor(random(-range, range))) * this.channelLen; + randB = (floor(random(-range, range)) * srcImg.width + floor(random(-range, range))) * this.channelLen; + + for (let y = 0; y < srcImg.height; y++) { + for (let x = 0; x < srcImg.width; x++) { + let r, g, b, a; + let r2, g2, b2, a2; + let index; + + index = (y * srcImg.width + x) * this.channelLen; + r = index; + g = index + 1; + b = index + 2; + a = index + 3; + r2 = (r + randR) % srcImg.pixels.length; + g2 = (g + randG) % srcImg.pixels.length; + b2 = (b + randB) % srcImg.pixels.length; + destPixels[r] = srcImg.pixels[r2]; + destPixels[g] = srcImg.pixels[g2]; + destPixels[b] = srcImg.pixels[b2]; + destPixels[a] = srcImg.pixels[a]; + } + } + + return destPixels; + } + + getRandomRectImg(srcImg) { + let startX; + let startY; + let rectW; + let rectH; + let destImg; + startX = floor(random(0, srcImg.width - 30)); + startY = floor(random(0, srcImg.height - 50)); + rectW = floor(random(30, srcImg.width - startX)); + rectH = floor(random(1, 50)); + destImg = srcImg.get(startX, startY, rectW, rectH); + destImg.loadPixels(); + return destImg; + } + + show() { + + // restore the original state + this.replaceData(this.imgOrigin, this.copyData); + + // sometimes pass without effect processing + let n = floor(random(100)); + if (n > 75 && this.throughFlag) { + this.throughFlag = false; + setTimeout(() => { + this.throughFlag = true; + }, floor(random(200, 1500))); + } + if (!this.throughFlag) { + push(); + translate((width - this.imgOrigin.width) / 2, (height - this.imgOrigin.height) / 2); + image(this.imgOrigin, 0, 0); + pop(); + return; + } + + // flow line + this.flowLineImgs.forEach((v, i, arr) => { + arr[i].pixels = this.flowLine(this.imgOrigin, v); + if (arr[i].pixels) { + this.replaceData(this.imgOrigin, arr[i].pixels); + } + }) + + // shift line + this.shiftLineImgs.forEach((v, i, arr) => { + if (floor(random(100)) > 50) { + arr[i] = this.shiftLine(this.imgOrigin); + this.replaceData(this.imgOrigin, arr[i]); + } else { + if (arr[i]) { + this.replaceData(this.imgOrigin, arr[i]); + } + } + }) + + // shift rgb + this.shiftRGBs.forEach((v, i, arr) => { + if (floor(random(100)) > 65) { + arr[i] = this.shiftRGB(this.imgOrigin); + this.replaceData(this.imgOrigin, arr[i]); + } + }) + + push(); + translate((width - this.imgOrigin.width) / 2, (height - this.imgOrigin.height) / 2); + image(this.imgOrigin, 0, 0); + pop(); + + // scat image + this.scatImgs.forEach((obj) => { + push(); + translate((width - this.imgOrigin.width) / 2, (height - this.imgOrigin.height) / 2); + if (floor(random(100)) > 80) { + obj.x = floor(random(-this.imgOrigin.width * 0.3, this.imgOrigin.width * 0.7)); + obj.y = floor(random(-this.imgOrigin.height * 0.1, this.imgOrigin.height)); + obj.img = this.getRandomRectImg(this.imgOrigin); + } + if (obj.img) { + image(obj.img, obj.x, obj.y); + } + pop(); + }) + + } + +} + +/* + * A JavaScript implementation of the RSA Data Security, Inc. MD5 Message + * Digest Algorithm, as defined in RFC 1321. + * Copyright (C) Paul Johnston 1999 - 2000. + * Updated by Greg Holt 2000 - 2001. + * See http://pajhome.org.uk/site/legal.html for details. + */ + +/* + * Convert a 32-bit number to a hex string with ls-byte first + */ +var hex_chr = "0123456789abcdef"; + +function rhex(num) { + str = ""; + for (j = 0; j <= 3; j++) + str += hex_chr.charAt((num >> (j * 8 + 4)) & 0x0F) + + hex_chr.charAt((num >> (j * 8)) & 0x0F); + return str; +} + +/* + * Convert a string to a sequence of 16-word blocks, stored as an array. + * Append padding bits and the length, as described in the MD5 standard. + */ +function str2blks_MD5(str) { + nblk = ((str.length + 8) >> 6) + 1; + blks = new Array(nblk * 16); + for (i = 0; i < nblk * 16; i++) blks[i] = 0; + for (i = 0; i < str.length; i++) + blks[i >> 2] |= str.charCodeAt(i) << ((i % 4) * 8); + blks[i >> 2] |= 0x80 << ((i % 4) * 8); + blks[nblk * 16 - 2] = str.length * 8; + return blks; +} + +/* + * Add integers, wrapping at 2^32. This uses 16-bit operations internally + * to work around bugs in some JS interpreters. + */ +function add(x, y) { + var lsw = (x & 0xFFFF) + (y & 0xFFFF); + var msw = (x >> 16) + (y >> 16) + (lsw >> 16); + return (msw << 16) | (lsw & 0xFFFF); +} + +/* + * Bitwise rotate a 32-bit number to the left + */ +function rol(num, cnt) { + return (num << cnt) | (num >>> (32 - cnt)); +} + +/* + * These functions implement the basic operation for each round of the + * algorithm. + */ +function cmn(q, a, b, x, s, t) { + return add(rol(add(add(a, q), add(x, t)), s), b); +} + +function ff(a, b, c, d, x, s, t) { + return cmn((b & c) | ((~b) & d), a, b, x, s, t); +} + +function gg(a, b, c, d, x, s, t) { + return cmn((b & d) | (c & (~d)), a, b, x, s, t); +} + +function hh(a, b, c, d, x, s, t) { + return cmn(b ^ c ^ d, a, b, x, s, t); +} + +function ii(a, b, c, d, x, s, t) { + return cmn(c ^ (b | (~d)), a, b, x, s, t); +} + +/* + * Take a string and return the hex representation of its MD5. + */ +function calcMD5(str) { + x = str2blks_MD5(str); + a = 1732584193; + b = -271733879; + c = -1732584194; + d = 271733878; + + for (i = 0; i < x.length; i += 16) { + olda = a; + oldb = b; + oldc = c; + oldd = d; + + a = ff(a, b, c, d, x[i + 0], 7, -680876936); + d = ff(d, a, b, c, x[i + 1], 12, -389564586); + c = ff(c, d, a, b, x[i + 2], 17, 606105819); + b = ff(b, c, d, a, x[i + 3], 22, -1044525330); + a = ff(a, b, c, d, x[i + 4], 7, -176418897); + d = ff(d, a, b, c, x[i + 5], 12, 1200080426); + c = ff(c, d, a, b, x[i + 6], 17, -1473231341); + b = ff(b, c, d, a, x[i + 7], 22, -45705983); + a = ff(a, b, c, d, x[i + 8], 7, 1770035416); + d = ff(d, a, b, c, x[i + 9], 12, -1958414417); + c = ff(c, d, a, b, x[i + 10], 17, -42063); + b = ff(b, c, d, a, x[i + 11], 22, -1990404162); + a = ff(a, b, c, d, x[i + 12], 7, 1804603682); + d = ff(d, a, b, c, x[i + 13], 12, -40341101); + c = ff(c, d, a, b, x[i + 14], 17, -1502002290); + b = ff(b, c, d, a, x[i + 15], 22, 1236535329); + + a = gg(a, b, c, d, x[i + 1], 5, -165796510); + d = gg(d, a, b, c, x[i + 6], 9, -1069501632); + c = gg(c, d, a, b, x[i + 11], 14, 643717713); + b = gg(b, c, d, a, x[i + 0], 20, -373897302); + a = gg(a, b, c, d, x[i + 5], 5, -701558691); + d = gg(d, a, b, c, x[i + 10], 9, 38016083); + c = gg(c, d, a, b, x[i + 15], 14, -660478335); + b = gg(b, c, d, a, x[i + 4], 20, -405537848); + a = gg(a, b, c, d, x[i + 9], 5, 568446438); + d = gg(d, a, b, c, x[i + 14], 9, -1019803690); + c = gg(c, d, a, b, x[i + 3], 14, -187363961); + b = gg(b, c, d, a, x[i + 8], 20, 1163531501); + a = gg(a, b, c, d, x[i + 13], 5, -1444681467); + d = gg(d, a, b, c, x[i + 2], 9, -51403784); + c = gg(c, d, a, b, x[i + 7], 14, 1735328473); + b = gg(b, c, d, a, x[i + 12], 20, -1926607734); + + a = hh(a, b, c, d, x[i + 5], 4, -378558); + d = hh(d, a, b, c, x[i + 8], 11, -2022574463); + c = hh(c, d, a, b, x[i + 11], 16, 1839030562); + b = hh(b, c, d, a, x[i + 14], 23, -35309556); + a = hh(a, b, c, d, x[i + 1], 4, -1530992060); + d = hh(d, a, b, c, x[i + 4], 11, 1272893353); + c = hh(c, d, a, b, x[i + 7], 16, -155497632); + b = hh(b, c, d, a, x[i + 10], 23, -1094730640); + a = hh(a, b, c, d, x[i + 13], 4, 681279174); + d = hh(d, a, b, c, x[i + 0], 11, -358537222); + c = hh(c, d, a, b, x[i + 3], 16, -722521979); + b = hh(b, c, d, a, x[i + 6], 23, 76029189); + a = hh(a, b, c, d, x[i + 9], 4, -640364487); + d = hh(d, a, b, c, x[i + 12], 11, -421815835); + c = hh(c, d, a, b, x[i + 15], 16, 530742520); + b = hh(b, c, d, a, x[i + 2], 23, -995338651); + + a = ii(a, b, c, d, x[i + 0], 6, -198630844); + d = ii(d, a, b, c, x[i + 7], 10, 1126891415); + c = ii(c, d, a, b, x[i + 14], 15, -1416354905); + b = ii(b, c, d, a, x[i + 5], 21, -57434055); + a = ii(a, b, c, d, x[i + 12], 6, 1700485571); + d = ii(d, a, b, c, x[i + 3], 10, -1894986606); + c = ii(c, d, a, b, x[i + 10], 15, -1051523); + b = ii(b, c, d, a, x[i + 1], 21, -2054922799); + a = ii(a, b, c, d, x[i + 8], 6, 1873313359); + d = ii(d, a, b, c, x[i + 15], 10, -30611744); + c = ii(c, d, a, b, x[i + 6], 15, -1560198380); + b = ii(b, c, d, a, x[i + 13], 21, 1309151649); + a = ii(a, b, c, d, x[i + 4], 6, -145523070); + d = ii(d, a, b, c, x[i + 11], 10, -1120210379); + c = ii(c, d, a, b, x[i + 2], 15, 718787259); + b = ii(b, c, d, a, x[i + 9], 21, -343485551); + + a = add(a, olda); + b = add(b, oldb); + c = add(c, oldc); + d = add(d, oldd); + } + return rhex(a) + rhex(b) + rhex(c) + rhex(d); +} + + +var serialize = function(obj, prefix) { + var str = []; + for (var p in obj) { + if (obj.hasOwnProperty(p)) { + var k = prefix ? prefix + "[" + p + "]" : p, + v = obj[p]; + str.push(typeof v == "object" ? + serialize(v, k) : + encodeURIComponent(k) + "=" + encodeURIComponent(v)); + } + } + return str.join("&"); +} + +if (!firetable.started) firetable.init(); \ No newline at end of file diff --git a/js/md5.js b/js/md5.js new file mode 100644 index 0000000..09366fb --- /dev/null +++ b/js/md5.js @@ -0,0 +1,213 @@ +/** + * md5.js — MD5 message-digest algorithm. + * + * A JavaScript implementation of the RSA Data Security, Inc. MD5 Message + * Digest Algorithm, as defined in RFC 1321. + * + * Copyright (C) Paul Johnston 1999 - 2000. + * Updated by Greg Holt 2000 - 2001. + * See http://pajhome.org.uk/site/legal.html for details. + * + * Used in this app solely for signing Last.fm API requests. + * All variables properly scoped with `var` to prevent global leaks. + */ + +/** Hex character lookup table */ +var hex_chr = "0123456789abcdef"; + +/** + * Convert a 32-bit number to a hex string with least-significant byte first. + * @param {number} num - 32-bit integer + * @returns {string} 8-character hex string + */ +function rhex(num) { + var str = ""; + for (var j = 0; j <= 3; j++) { + str += hex_chr.charAt((num >> (j * 8 + 4)) & 0x0F) + + hex_chr.charAt((num >> (j * 8)) & 0x0F); + } + return str; +} + +/** + * Convert a string to a sequence of 16-word blocks stored as an array. + * Appends padding bits and the length as described in the MD5 standard. + * @param {string} str - Input string + * @returns {number[]} Array of 32-bit integers + */ +function str2blks_MD5(str) { + var nblk = ((str.length + 8) >> 6) + 1; + var blks = new Array(nblk * 16); + var i; + for (i = 0; i < nblk * 16; i++) blks[i] = 0; + for (i = 0; i < str.length; i++) { + blks[i >> 2] |= str.charCodeAt(i) << ((i % 4) * 8); + } + blks[i >> 2] |= 0x80 << ((i % 4) * 8); + blks[nblk * 16 - 2] = str.length * 8; + return blks; +} + +/** + * Add two 32-bit integers, wrapping at 2^32. + * Uses 16-bit operations internally to work around bugs in some JS interpreters. + * @param {number} x + * @param {number} y + * @returns {number} + */ +function add(x, y) { + var lsw = (x & 0xFFFF) + (y & 0xFFFF); + var msw = (x >> 16) + (y >> 16) + (lsw >> 16); + return (msw << 16) | (lsw & 0xFFFF); +} + +/** + * Bitwise rotate a 32-bit number to the left. + * @param {number} num - Value to rotate + * @param {number} cnt - Number of bits to rotate + * @returns {number} + */ +function rol(num, cnt) { + return (num << cnt) | (num >>> (32 - cnt)); +} + +/* + * These functions implement the basic operation for each round of the + * MD5 algorithm (F, G, H, I respectively). + */ +function cmn(q, a, b, x, s, t) { + return add(rol(add(add(a, q), add(x, t)), s), b); +} + +function ff(a, b, c, d, x, s, t) { + return cmn((b & c) | ((~b) & d), a, b, x, s, t); +} + +function gg(a, b, c, d, x, s, t) { + return cmn((b & d) | (c & (~d)), a, b, x, s, t); +} + +function hh(a, b, c, d, x, s, t) { + return cmn(b ^ c ^ d, a, b, x, s, t); +} + +function ii(a, b, c, d, x, s, t) { + return cmn(c ^ (b | (~d)), a, b, x, s, t); +} + +/** + * Compute the MD5 hash of a string. + * @param {string} str - Input string + * @returns {string} 32-character lowercase hex digest + */ +function calcMD5(str) { + var x = str2blks_MD5(str); + var a = 1732584193; + var b = -271733879; + var c = -1732584194; + var d = 271733878; + + for (var i = 0; i < x.length; i += 16) { + var olda = a; + var oldb = b; + var oldc = c; + var oldd = d; + + a = ff(a, b, c, d, x[i + 0], 7, -680876936); + d = ff(d, a, b, c, x[i + 1], 12, -389564586); + c = ff(c, d, a, b, x[i + 2], 17, 606105819); + b = ff(b, c, d, a, x[i + 3], 22, -1044525330); + a = ff(a, b, c, d, x[i + 4], 7, -176418897); + d = ff(d, a, b, c, x[i + 5], 12, 1200080426); + c = ff(c, d, a, b, x[i + 6], 17, -1473231341); + b = ff(b, c, d, a, x[i + 7], 22, -45705983); + a = ff(a, b, c, d, x[i + 8], 7, 1770035416); + d = ff(d, a, b, c, x[i + 9], 12, -1958414417); + c = ff(c, d, a, b, x[i + 10], 17, -42063); + b = ff(b, c, d, a, x[i + 11], 22, -1990404162); + a = ff(a, b, c, d, x[i + 12], 7, 1804603682); + d = ff(d, a, b, c, x[i + 13], 12, -40341101); + c = ff(c, d, a, b, x[i + 14], 17, -1502002290); + b = ff(b, c, d, a, x[i + 15], 22, 1236535329); + + a = gg(a, b, c, d, x[i + 1], 5, -165796510); + d = gg(d, a, b, c, x[i + 6], 9, -1069501632); + c = gg(c, d, a, b, x[i + 11], 14, 643717713); + b = gg(b, c, d, a, x[i + 0], 20, -373897302); + a = gg(a, b, c, d, x[i + 5], 5, -701558691); + d = gg(d, a, b, c, x[i + 10], 9, 38016083); + c = gg(c, d, a, b, x[i + 15], 14, -660478335); + b = gg(b, c, d, a, x[i + 4], 20, -405537848); + a = gg(a, b, c, d, x[i + 9], 5, 568446438); + d = gg(d, a, b, c, x[i + 14], 9, -1019803690); + c = gg(c, d, a, b, x[i + 3], 14, -187363961); + b = gg(b, c, d, a, x[i + 8], 20, 1163531501); + a = gg(a, b, c, d, x[i + 13], 5, -1444681467); + d = gg(d, a, b, c, x[i + 2], 9, -51403784); + c = gg(c, d, a, b, x[i + 7], 14, 1735328473); + b = gg(b, c, d, a, x[i + 12], 20, -1926607734); + + a = hh(a, b, c, d, x[i + 5], 4, -378558); + d = hh(d, a, b, c, x[i + 8], 11, -2022574463); + c = hh(c, d, a, b, x[i + 11], 16, 1839030562); + b = hh(b, c, d, a, x[i + 14], 23, -35309556); + a = hh(a, b, c, d, x[i + 1], 4, -1530992060); + d = hh(d, a, b, c, x[i + 4], 11, 1272893353); + c = hh(c, d, a, b, x[i + 7], 16, -155497632); + b = hh(b, c, d, a, x[i + 10], 23, -1094730640); + a = hh(a, b, c, d, x[i + 13], 4, 681279174); + d = hh(d, a, b, c, x[i + 0], 11, -358537222); + c = hh(c, d, a, b, x[i + 3], 16, -722521979); + b = hh(b, c, d, a, x[i + 6], 23, 76029189); + a = hh(a, b, c, d, x[i + 9], 4, -640364487); + d = hh(d, a, b, c, x[i + 12], 11, -421815835); + c = hh(c, d, a, b, x[i + 15], 16, 530742520); + b = hh(b, c, d, a, x[i + 2], 23, -995338651); + + a = ii(a, b, c, d, x[i + 0], 6, -198630844); + d = ii(d, a, b, c, x[i + 7], 10, 1126891415); + c = ii(c, d, a, b, x[i + 14], 15, -1416354905); + b = ii(b, c, d, a, x[i + 5], 21, -57434055); + a = ii(a, b, c, d, x[i + 12], 6, 1700485571); + d = ii(d, a, b, c, x[i + 3], 10, -1894986606); + c = ii(c, d, a, b, x[i + 10], 15, -1051523); + b = ii(b, c, d, a, x[i + 1], 21, -2054922799); + a = ii(a, b, c, d, x[i + 8], 6, 1873313359); + d = ii(d, a, b, c, x[i + 15], 10, -30611744); + c = ii(c, d, a, b, x[i + 6], 15, -1560198380); + b = ii(b, c, d, a, x[i + 13], 21, 1309151649); + a = ii(a, b, c, d, x[i + 4], 6, -145523070); + d = ii(d, a, b, c, x[i + 11], 10, -1120210379); + c = ii(c, d, a, b, x[i + 2], 15, 718787259); + b = ii(b, c, d, a, x[i + 9], 21, -343485551); + + a = add(a, olda); + b = add(b, oldb); + c = add(c, oldc); + d = add(d, oldd); + } + return rhex(a) + rhex(b) + rhex(c) + rhex(d); +} + +/** + * Serialize an object into a URL query string. + * Handles nested objects via recursive bracket notation. + * @param {Object} obj - Key/value pairs to serialize + * @param {string} [prefix] - Key prefix for nested objects + * @returns {string} URL-encoded query string + */ +var serialize = function (obj, prefix) { + var str = []; + for (var p in obj) { + if (obj.hasOwnProperty(p)) { + var k = prefix ? prefix + "[" + p + "]" : p; + var v = obj[p]; + str.push( + typeof v === "object" + ? serialize(v, k) + : encodeURIComponent(k) + "=" + encodeURIComponent(v) + ); + } + } + return str.join("&"); +}; diff --git a/js/player.js b/js/player.js new file mode 100644 index 0000000..ef42a7a --- /dev/null +++ b/js/player.js @@ -0,0 +1,269 @@ +/** + * player.js — YouTube + SoundCloud media playback. + * + * Handles: + * - YouTube IFrame API ready callback + player initialization + * - SoundCloud Widget initialization (in init.js, widget events bound here) + * - Volume slider + mute toggle + * - Track preview (play a 30-second snippet of a song in the queue or search) + * - Reload current track at correct elapsed position + * + * Uses the `firetable.utilities.resumeCurrentSong()` helper from helpers.js + * to avoid duplicating the "calculate elapsed → load YT or SC" logic. + */ + +// ─── YouTube IFrame API Callback ───────────────────────────────────────────── +/** + * Called automatically by the YouTube IFrame API when it finishes loading. + * Creates the YT.Player instance inside #playerArea. + */ +function onYouTubeIframeAPIReady() { + player = new YT.Player('playerArea', { + width: $('#djStage').outerHeight() * ASPECT_RATIO, + height: $('#djStage').outerHeight(), + playerVars: { + autoplay: 1, + controls: 0 + }, + videoId: '5mGuCdlCcNM', // placeholder video + events: { + onReady: onPlayerReady, + onStateChange: function () { + $('#reloadtrack').removeClass('on working'); + } + } + }); +} + +/** + * Called when the YouTube player is ready. Sets up: + * - Volume from localStorage (or DEFAULT_VOLUME on first visit) + * - Mute state from localStorage + * - jQuery UI volume slider + * - Resumes the current song if one is already playing + * + * @param {Object} event - YouTube player ready event + */ +function onPlayerReady(event) { + firetable.ytLoaded = true; + + // ── Restore volume ── + var vol = localStorage[STORAGE.volume]; + if (typeof vol === "undefined") { + vol = DEFAULT_VOLUME; + localStorage[STORAGE.volume] = DEFAULT_VOLUME; + } + player.setVolume(vol); + + // ── Restore mute state ── + var muted = localStorage[STORAGE.mute]; + if (typeof muted === "undefined") { + localStorage[STORAGE.mute] = false; + muted = "false"; + $("#volstatus").removeClass('on'); + } + if (muted !== "false") { + $("#volstatus i").html(""); + $("#volstatus").addClass('on'); + } + + // ── Volume slider ── + $("#slider").slider({ + orientation: "vertical", + range: "min", + min: 0, + max: 100, + value: vol, + step: 5, + slide: function (event, ui) { + player.setVolume(ui.value); + firetable.scwidget.setVolume(ui.value); + localStorage[STORAGE.volume] = ui.value; + + var isMuted = localStorage[STORAGE.mute]; + if (isMuted !== "false") { + // Un-mute since user is dragging the slider + localStorage[STORAGE.mute] = false; + $("#volstatus i").html(""); + $("#volstatus").removeClass('on'); + } else if (ui.value === 0) { + firetable.actions.muteToggle(true); + $("#volstatus").addClass('on'); + } + } + }); + + // ── Resume song if one was already playing when YT loaded ── + if (firetable.song && firetable.song.type === MEDIA_YOUTUBE) { + firetable.utilities.resumeCurrentSong(); + } +} + +/** + * Stub — YouTube fires this when playback state changes. + * Currently unused but required by the API. + */ +function onPlayerStateChange(event) { + // no-op +} + +// ─── Mute / Volume Toggle ──────────────────────────────────────────────────── + +firetable.actions = firetable.actions || {}; + +/** + * Toggle mute on/off. Stores previous volume to restore when un-muting. + * + * @param {boolean} [zeroMute=false] - If true, force-mute at volume 0 + * (used when the slider is dragged to 0) + */ +firetable.actions.muteToggle = function (zeroMute) { + var muted = localStorage[STORAGE.mute]; + var icon = ""; // volume_up icon + + if (zeroMute) { + // Slider was dragged to 0 — mark as zero-muted + icon = ""; // volume_off icon + muted = 0; + + } else if (typeof muted !== 'undefined') { + if (muted !== "false") { + // Currently muted → un-mute, restore saved volume + if (parseInt(muted) === 0) { + // Was zero-muted — restore to default volume + $("#slider").slider("value", DEFAULT_VOLUME); + player.setVolume(DEFAULT_VOLUME); + firetable.scwidget.setVolume(DEFAULT_VOLUME); + localStorage[STORAGE.volume] = DEFAULT_VOLUME; + } else { + // Restore the volume that was saved before muting + var savedVol = parseInt(muted); + $("#slider").slider("value", savedVol); + player.setVolume(savedVol); + firetable.scwidget.setVolume(savedVol); + localStorage[STORAGE.volume] = savedVol; + } + muted = false; + } else { + // Not muted → mute: save current volume, set to 0 + icon = ""; + muted = $("#slider").slider("value"); + $("#slider").slider('value', 0); + player.setVolume(0); + firetable.scwidget.setVolume(0); + localStorage[STORAGE.volume] = 0; + } + } else { + // First time — mute + icon = ""; + muted = $("#slider").slider("value"); + $("#slider").slider('value', 0); + player.setVolume(0); + firetable.scwidget.setVolume(0); + localStorage[STORAGE.volume] = 0; + } + + if (muted) { + $("#volstatus").addClass('on'); + } else { + $("#volstatus").removeClass('on'); + } + $("#volstatus i").html(icon); + localStorage[STORAGE.mute] = muted; +}; + +// ─── Track Preview ─────────────────────────────────────────────────────────── + +/** + * Preview a song for 30 seconds. If the same song is already being previewed, + * stop the preview and resume the current room song. + * + * @param {string} id - Song key (queue key or "ytcid..."/"sccid..." for search results) + * @param {boolean} fromSearch - True if previewing from search results (id has prefix) + * @param {number} type - MEDIA_YOUTUBE or MEDIA_SOUNDCLOUD + * @param {boolean} [fromHist=false] - True if previewing from history (darker progress bar) + */ +firetable.actions.pview = function (id, fromSearch, type, fromHist) { + if (firetable.preview === id) { + // ── Already previewing this track → stop and resume room song ── + clearTimeout(firetable.ptimeout); + firetable.ptimeout = null; + $("#pv" + firetable.preview).html(""); // play_arrow + $("#pvbar" + firetable.preview).css("background-image", "none"); + clearInterval(firetable.movePvBar); + firetable.movePvBar = null; + firetable.preview = false; + + firetable.utilities.resumeCurrentSong(); + + } else { + // ── Stop any existing preview ── + if (firetable.preview) { + $("#pv" + firetable.preview).html(""); + $("#pvbar" + firetable.preview).css("background-image", "none"); + } + + firetable.preview = id; + var cid = fromSearch ? id.slice(5) : firetable.queue[id].cid; + + // Clear any existing preview timer + if (firetable.ptimeout != null) { + clearTimeout(firetable.ptimeout); + firetable.ptimeout = null; + } + if (firetable.movePvBar != null) { + clearInterval(firetable.movePvBar); + firetable.movePvBar = null; + } + + // ── Auto-stop after PREVIEW_DURATION ── + firetable.pvCount = 0; + firetable.ptimeout = setTimeout(function () { + firetable.ptimeout = null; + $("#pv" + firetable.preview).html(""); + $("#pvbar" + firetable.preview).css("background-image", "none"); + clearInterval(firetable.movePvBar); + firetable.movePvBar = null; + firetable.pvCount = 0; + firetable.preview = false; + + // Resume the room's current song + firetable.utilities.resumeCurrentSong(); + }, PREVIEW_DURATION); + + // ── Show pause icon + animate progress bar ── + $("#pv" + id).html(""); // pause icon + firetable.movePvBar = setInterval(function () { + var pcnt = (firetable.pvCount / 29) * 100; // 29 = PREVIEW_DURATION/1000 - 1 + firetable.pvCount += 0.2; + var bgColor = fromHist ? "#222" : "#222"; + $("#pvbar" + firetable.preview).css( + "background-image", + "linear-gradient(90deg, rgba(244, 129, 11, 0.267) " + pcnt + "%, " + bgColor + " " + pcnt + "%)" + ); + }, PREVIEW_BAR_INTERVAL); + + // ── Start playing the preview from the beginning ── + if (type == MEDIA_YOUTUBE) { + if (firetable.scLoaded) firetable.scwidget.pause(); + if (!firetable.disableMediaPlayback) player.loadVideoById(cid, 0, "large"); + } else if (type == MEDIA_SOUNDCLOUD) { + if (firetable.ytLoaded) player.stopVideo(); + firetable.scSeek = 0; + if (!firetable.disableMediaPlayback) { + firetable.scwidget.load(SC_API_TRACK_URL + cid, { auto_play: true }); + } + } + } +}; + +// ─── Reload Track ──────────────────────────────────────────────────────────── + +/** + * Reload the current song at the correct elapsed position. + * Shows a loading spinner on the reload button. + */ +firetable.actions.reloadtrack = function () { + $('#reloadtrack').addClass('on working'); + firetable.utilities.resumeCurrentSong(); +}; diff --git a/js/playlist.js b/js/playlist.js new file mode 100644 index 0000000..dccc375 --- /dev/null +++ b/js/playlist.js @@ -0,0 +1,659 @@ +/** + * playlist.js — Queue and playlist management. + * + * Handles: + * - Adding tracks to queue (queueTrack) + * - Reordering via drag-and-drop (updateQueue, sortable) + * - Shuffle, dedup, bump-to-top, move-to-bottom, delete + * - Queue filtering (#queueFilter) + * - Merge lists between playlists + * - Queue from link (YouTube/SoundCloud URL drag-and-drop) + * - SoundCloud URL resolution (resolveSCLink, scGet) + * - Import playlist from YouTube/SoundCloud (importList) + * - Dubtrack import (dubtrackImport, dubtrackImportFileSelect) + * - List CRUD (create, delete, switch) + * - playlistChanged event handler + * - Tag editing (editTagsPrompt) + */ + +firetable.actions = firetable.actions || {}; + +// ─── Queue Track ───────────────────────────────────────────────────────────── + +/** + * Add a track to the current playlist queue. + * If a search preview is active, cancels it and resumes the room song. + * + * @param {string} cid - Content ID (YouTube video ID or SoundCloud track ID) + * @param {string} name - Track display name "Artist - Title" + * @param {number} type - MEDIA_YOUTUBE (1) or MEDIA_SOUNDCLOUD (2) + * @param {boolean} [tobottom] - If true, don't bump to top of queue + */ +firetable.actions.queueTrack = function (cid, name, type, tobottom) { + var info = { type: type, name: name, cid: cid }; + + // Visual feedback: checkmark on the queue button + $("#apv" + type + cid).find(".material-icons").text("check"); + $("#apv" + type + cid).css("color", firetable.orange); + $("#apv" + type + cid).css("pointer-events", "none"); + setTimeout(function () { + $("#apv" + type + cid).find(".material-icons").text("playlist_add"); + $("#apv" + type + cid).removeAttr("style"); + }, 3000); + + var cuteid = ftapi.actions.addToList(type, name, cid, false, function () { + firetable.debug && console.log('queue track id:', cuteid); + if (!tobottom) firetable.actions.bumpSongInQueue(cuteid); + }); + + // Cancel any active search preview + firetable.utilities.cancelSearchPreview(); + + // Switch view back to queue + $("#mainqueuestuff").css("display", "block"); + $("#filterMachine").css("display", "block"); + $("#addbox").css("display", "none"); + $("#cancelqsearch").hide(); + $("#qControlButtons").show(); +}; + +// ─── Queue Reorder (Sortable) ──────────────────────────────────────────────── + +/** + * Called when the user drags a song to a new position in the queue. + * Reads the new DOM order and pushes it to the server. + */ +firetable.actions.updateQueue = function () { + var arr = $('#mainqueue > div').map(function () { + return this.id.slice(5); + }).get(); + ftapi.actions.reorderList(arr, firetable.preview, function (changePV) { + if (changePV) firetable.preview = changePV; + }); +}; + +/** Shuffle the current playlist on the server. */ +firetable.actions.shuffleQueue = function () { + ftapi.actions.shuffleList(firetable.preview, function (changePV) { + if (changePV) firetable.preview = changePV; + }); +}; + +/** Remove duplicate tracks from the current playlist. */ +firetable.actions.removeDupesFromQueue = function () { + ftapi.actions.removeDuplicatesFromList(); + $("#mergeCompleted").show(); + $("#mergeHappening").hide(); +}; + +/** + * Move a track to the top of the queue. + * @param {string} songid - Track key in the playlist + */ +firetable.actions.bumpSongInQueue = function (songid) { + ftapi.actions.moveTrackToTop(songid, firetable.preview, function (changePV) { + if (changePV) firetable.preview = changePV; + }); +}; + +/** + * Delete a track from the current playlist. + * @param {string} id - Track key + */ +firetable.actions.deleteSong = function (id) { + ftapi.actions.deleteTrack(id); +}; + +/** + * Filter visible queue items by a search string. + * @param {string} val - Filter text (empty string shows all) + */ +firetable.actions.filterQueue = function (val) { + if (val.length === 0) { + $("#mainqueue .pvbar").show(); + return; + } + val = val.toLowerCase(); + $("#mainqueue .pvbar").each(function (p, q) { + var txt = $(q).find(".listwords").text(); + if (txt.match(new RegExp(val, 'ig'))) { + $(q).show(); + } else { + $(q).hide(); + } + }); +}; + +// ─── Merge / Copy Lists ────────────────────────────────────────────────────── + +/** + * Merge (copy) tracks from one playlist into another. + * If source === dest, deduplicates instead. + * If dest === -1, creates a new playlist copy. + * @param {string} source - Source list ID + * @param {string} dest - Destination list ID (or -1 for new) + * @param {string} sourceName - Display name of the source list + */ +firetable.actions.mergeLists = function (source, dest, sourceName) { + if (source === dest) { + firetable.actions.removeDupesFromQueue(); + return; + } + if (dest == -1) { + var newname = firetable.utilities.format_date(Date.now()) + " Copy of " + sourceName; + dest = ftapi.actions.createList(newname); + $("#listpicker").append(''); + } + ftapi.actions.mergeLists(source, dest, function () { + $("#mergeCompleted").show(); + $("#mergeHappening").hide(); + }); +}; + +// ─── Queue From Link (Drag & Drop URLs) ───────────────────────────────────── + +/** + * Parse a YouTube or SoundCloud URL and add the track to the queue. + * Called by the LinkGrabber when a URL is dragged onto the queue area. + * @param {string} link - Full URL + */ +firetable.actions.queueFromLink = function (link) { + if (link.match(/youtube.com\/watch/)) { + firetable.debug && console.log("yt"); + var therealid = getQueryStringValue(link, "v"); + if (therealid) { + youtubeAPIReady(function () { + gapi.client.youtube.videos.list({ + id: therealid, + part: 'snippet', + maxResults: 1 + }).execute(function (response) { + firetable.debug && console.log('queue from link:', response); + if (response.result && response.result.items && response.result.items.length) { + var item = response.result.items[0]; + var parsed = firetable.utilities.parseArtistTitle( + item.snippet.title, + item.snippet.channelTitle.replace(" - Topic", "") + ); + firetable.actions.queueTrack(item.id, parsed.artist + " - " + parsed.title, MEDIA_YOUTUBE); + } + }); + }); + } + } else if (link.match(/soundcloud.com/)) { + firetable.debug && console.log("sc"); + firetable.actions.resolveSCLink(link, function (tracks) { + if (tracks) { + var parsed = firetable.utilities.parseArtistTitle(tracks.title, tracks.user.username); + firetable.actions.queueTrack(tracks.id, parsed.artist + " - " + parsed.title, MEDIA_SOUNDCLOUD); + } + }); + } +}; + +// ─── SoundCloud Resolution ─────────────────────────────────────────────────── + +/** + * Resolve a SoundCloud URL to track/playlist metadata via proxy. + * @param {string} link - Full SoundCloud URL + * @param {Function} callback - Called with the resolved response object + */ +firetable.actions.resolveSCLink = function (link, callback) { + var importantStuff = link.replace("https://soundcloud.com/", "").replace("http://soundcloud.com/", ""); + $.ajax({ + url: SC_RESOLVE_URL + importantStuff, + type: 'GET', + dataType: 'json', + success: function (res) { + console.log(res); + callback(res.response); + } + }); +}; + +/** + * Generic SoundCloud API GET request via proxy. + * @param {string} type - Resource type (e.g. 'playlists') + * @param {string} q - Query/ID + * @param {Function} callback - Called with the response + */ +firetable.actions.scGet = function (type, q, callback) { + $.ajax({ + url: SC_PROXY_URL + "?type=" + type + "&q=" + q, + type: 'GET', + dataType: 'json', + success: function (res) { + console.log(res); + callback(res.response); + } + }); +}; + +// ─── Playlist Import ───────────────────────────────────────────────────────── + +/** + * Import a full playlist from YouTube or SoundCloud into a new local list. + * YouTube playlists are paginated (50 items per page). + * @param {string} id - Playlist/set ID + * @param {string} name - Display name for the new local list + * @param {number} type - MEDIA_YOUTUBE (1) or MEDIA_SOUNDCLOUD (2) + */ +firetable.actions.importList = function (id, name, type) { + $("#overlay").removeClass('show'); + $("#importResults").html(""); + $("#plMachine").val(""); + + if (type === MEDIA_YOUTUBE) { + var finalList = []; + + var fetchPage = function (pageToken) { + youtubeAPIReady(function () { + var params = { + playlistId: id, + maxResults: IMPORT_PAGE_SIZE, + part: "snippet" + }; + if (pageToken) params.pageToken = pageToken; + + gapi.client.youtube.playlistItems.list(params).execute(function (response) { + if (response.items && response.items.length) { + for (var idx = 0; idx < response.items.length; idx++) { + finalList.push(response.items[idx]); + } + } + if (response.nextPageToken) { + fetchPage(response.nextPageToken); + } else { + // All pages fetched — create the list + firetable.debug && console.log(finalList); + var listid = ftapi.actions.createList(name); + $("#listpicker").append(''); + for (var i = 0; i < finalList.length; i++) { + var goodTitle = finalList[i].snippet.title; + if (goodTitle !== "Private video" && goodTitle !== "Deleted video") { + ftapi.actions.addToList(MEDIA_YOUTUBE, goodTitle, finalList[i].snippet.resourceId.videoId, listid); + } + } + } + }); + }); + }; + fetchPage(); // start with first page + + } else if (type === MEDIA_SOUNDCLOUD) { + firetable.actions.scGet('playlists', id, function (listinfo) { + firetable.debug && console.log('sc tracks:', listinfo.tracks); + var listid = ftapi.actions.createList(name); + $("#listpicker").append(''); + for (var i = 0; i < listinfo.tracks.length; i++) { + var goodTitle; + if (listinfo.tracks[i].title) { + var parsed = firetable.utilities.parseArtistTitle(listinfo.tracks[i].title, listinfo.tracks[i].user.username); + goodTitle = parsed.artist + " - " + parsed.title; + } else { + goodTitle = "Unknown"; + } + ftapi.actions.addToList(MEDIA_SOUNDCLOUD, goodTitle, listinfo.tracks[i].id, listid); + } + }); + } +}; + +// ─── Dubtrack Import ───────────────────────────────────────────────────────── + +/** + * Import tracks from the parsed Dubtrack export file. + * Expects firetable.dtImportList and firetable.dtImportName to be populated + * by dubtrackImportFileSelect(). + */ +firetable.actions.dubtrackImport = function () { + $("#importDubResults").html("importing (0/" + firetable.dtImportList.length + ")..."); + $("#dubimportButton").hide(); + var listid = ftapi.actions.createList(firetable.dtImportName); + var name = firetable.dtImportName; + $("#listpicker").append(''); + + var trackarray = firetable.dtImportList; + for (var e = 0; e < trackarray.length; e++) { + var thetype = trackarray[e].type === "soundcloud" ? MEDIA_SOUNDCLOUD : MEDIA_YOUTUBE; + var numbo = e + 1; + $("#importDubResults").html("importing (" + numbo + "/" + firetable.dtImportList.length + ")..."); + if (numbo === firetable.dtImportList.length) { + $("#importDubResults").html("Import complete! You can now select another file if you'd like to do another!"); + } + ftapi.actions.addToList(thetype, trackarray[e].name, trackarray[e].cid, listid); + } +}; + +/** + * Parse a Dubtrack HTML export file and prepare it for import. + * Populates firetable.dtImportName and firetable.dtImportList. + * @param {Event} evt - File input change event + */ +firetable.ui.dubtrackImportFileSelect = function (evt) { + var file = evt.target.files[0]; + var reader = new FileReader(); + reader.readAsText(file); + reader.onload = function (event) { + try { + var allthestuff = event.currentTarget.result; + firetable.dtImportName = firetable.ui.strip(allthestuff.split('

    ')[1].split('

    ')[0]); + var hams = allthestuff.split('
  • (.*)<\/li>/gm; + var matches = thingsRegex.exec(hams[i]); + firetable.dtImportList.push({ + type: matches[2], + cid: matches[4], + name: firetable.ui.strip(matches[5]) + }); + } + if (firetable.dtImportList.length) { + $("#importDubResults").text("Ok... import " + firetable.dtImportName + " (" + firetable.dtImportList.length + " tracks)?"); + $("#dubimportButton").show(); + } else { + $("#importDubResults").text("ERROR... NO TRAX?"); + $("#dubimportButton").hide(); + } + } catch (e) { + console.log(e); + $("#importDubResults").text("ERROR"); + $("#dubimportButton").hide(); + } + }; +}; + +// ─── Tag Editing ───────────────────────────────────────────────────────────── + +/** + * Show the tag editor prompt on a history item. + * @param {string} songid - data-key of the history item + * @param {string} tag - Current "Artist - Title" string + */ +firetable.actions.editTagsPrompt = function (songid, tag) { + var $pvbar = $('#thehistory .pvbar[data-key="' + songid + '"]'); + $('#thehistory .pvbar.editing').removeClass('editing'); + $('.tagPromptBox').remove(); + $pvbar.addClass('editing'); + var $tags = $tagEditorTemplate.clone().appendTo($pvbar); + $tags.find(".tagMachine").val(tag); + firetable.debug && console.log('edit tags song id:', songid); +}; + +// ─── Playlist Event Binding ────────────────────────────────────────────────── + +/** + * Set up the playlistChanged event handler and queue-related UI bindings. + * Called once from firetable.ui.init(). + */ +firetable.ui.setupPlaylistEvents = function () { + + // ── Sortable drag-and-drop queue ── + $('#mainqueue').sortable({ + start: function (event, ui) { + ui.item.data('start_pos', ui.item.index()); + }, + update: function () { + firetable.debug && console.log("UPDATE"); + firetable.actions.updateQueue(); + } + }); + + // ── Playlist changed: re-render the queue ── + ftapi.events.on("playlistChanged", function (okdata, listID) { + firetable.queue = okdata; + $('#mainqueue').html(""); + + for (var key in okdata) { + if (!okdata.hasOwnProperty(key)) continue; + var thisone = okdata[key]; + var $newli = $playlistItemTemplate.clone(); + var psign = (key === firetable.preview) ? "" : ""; + + $newli.attr('id', "pvbar" + key) + .attr("data-key", key) + .attr("data-type", thisone.type) + .attr("data-cid", thisone.cid); + + // Preview button + $newli.find('.previewicon').attr('id', "pv" + key).on('click', function () { + firetable.actions.pview( + $(this).closest('.pvbar').attr('data-key'), + false, + $(this).closest('.pvbar').attr('data-type') + ); + }).html(psign); + + // Track title + $newli.find('.listwords').html(thisone.name); + + // Bump to top + $newli.find('.bumpsongs').on('click', function () { + firetable.actions.bumpSongInQueue($(this).closest('.pvbar').attr('data-key')); + }); + + // Move to bottom + $newli.find('.bottomsongs').on('click', function () { + var oldID = $(this).closest('.pvbar').attr('data-key'); + ftapi.actions.moveTrackToBottom(oldID, function (newID) { + if (firetable.preview && firetable.preview === oldID) { + firetable.preview = newID; + $("#pv" + newID).html(""); + } + }); + }); + + // Flagged track warning icon + if (thisone.flagged) { + var flagLabel = "broken"; + var flagIcon = "warning"; + if (thisone.flagged.code === 7) { + flagLabel = "age restricted"; + } else if (thisone.flagged.code === 8) { + flagLabel = "broken (manual)"; + } else if (thisone.flagged.code === 9) { + flagLabel = "low audio quality"; + flagIcon = "disc_full"; + } else if (thisone.flagged.code === 10) { + flagLabel = "offtheme"; + flagIcon = "flag"; + } + $newli.find('.track-warning') + .html(' ' + flagIcon + ' ') + .prop('title', 'Flagged as ' + flagLabel + ' on ' + firetable.utilities.format_date(thisone.flagged.date) + '. Click to remove flag.') + .on('click', function () { + ftapi.actions.unflagTrack($(this).closest('.pvbar').attr('data-key')); + $(this).html(""); + }); + } + + // Delete button + $newli.find('.deletesong').on('click', function () { + firetable.actions.deleteSong($(this).closest('.pvbar').attr('data-key')); + }); + + // Add-to-playlist button + $newli.find('.histeal').on('click', function () { + var $btn = $(this); + var $pvbar = $btn.closest('.pvbar'); + var btnCid = $pvbar.attr('data-cid'); + var btnType = $pvbar.attr('data-type'); + var btnTitle = firetable.utilities.htmlEscape($pvbar.find('.listwords').text()); + + if (firetable.stealSourceBtn && firetable.stealSourceBtn.is($btn) && !$("#stealContain").is(':hidden')) { + $btn.removeClass('on'); + firetable.stealSourceBtn = null; + firetable.stealTarget = null; + $("#stealContain").hide(); + return; + } + + ftapi.lookup.allLists(function (allPlaylists) { + $("#stealpicker").html( + '' + + '' + ); + for (var key in allPlaylists) { + if (allPlaylists.hasOwnProperty(key)) { + $("#stealpicker").append( + '' + ); + } + } + if (firetable.stealSourceBtn) firetable.stealSourceBtn.removeClass('on'); + $("#grab").removeClass('on'); + firetable.stealSourceBtn = $btn; + firetable.stealTarget = { cid: btnCid, type: btnType, title: btnTitle }; + $btn.addClass('on'); + $("#stealContain").css({ + top: $btn.offset().top + $btn.outerHeight(), + left: $btn.offset().left - 16 + }).show(); + }); + }); + + $('#mainqueue').append($newli); + } + }); + + // ── Queue filter input ── + $("#queueFilter").on("change paste keyup", function () { + firetable.actions.filterQueue($(this).val()); + }); + + // ── Shuffle button ── + $("#shuffleQueue").bind("click", firetable.actions.shuffleQueue); + + // ── Add-to-queue toggle ── + $("#addToQueueBttn").bind("click", function () { + $("#mainqueuestuff").css("display", "none"); + $("#filterMachine").css("display", "none"); + $("#addbox").css("display", "flex"); + $("#cancelqsearch").show(); + $("#qControlButtons").hide(); + $("#plmanager").css("display", "none"); + }); + + // ── Cancel search / back to queue ── + $("#cancelqsearch").bind("click", function () { + $("#mainqueuestuff").css("display", "block"); + $("#filterMachine").css("display", "block"); + $("#cancelqsearch").hide(); + $("#qControlButtons").show(); + $("#addbox").css("display", "none"); + firetable.utilities.cancelSearchPreview(); + }); + + // ── Create new playlist ── + $("#plmaker").bind("keyup", function (e) { + if (e.which !== 13) return; + var val = $(this).val(); + if (val) { + var listid = ftapi.actions.createList(val); + $("#listpicker").append(''); + $("#listpicker").val(listid).change(); + ftapi.actions.switchList(listid); + } + }); + + // ── Delete playlist ── + $("#pldeleteButton").bind("click", function () { + var val = $("#deletepicker").val(); + firetable.debug && console.log('playlist delete:', val); + if (ftapi.users[ftapi.uid] && ftapi.users[ftapi.uid].selectedList === val) { + $("#listpicker").val("0").change(); + } + ftapi.actions.deleteList(val); + $("#pdopt" + val).remove(); + $("#overlay").removeClass('show'); + }); + + // ── Import launcher ── + $("#plimportLauncher").bind("click", function () { + $("#overlay").addClass('show'); + $(".modalThing").removeClass('show'); + $('#importPromptBox').addClass('show'); + }); + + // ── Delete launcher ── + $("#pldeleteLauncher").bind("click", function () { + ftapi.lookup.allLists(function (allPlaylists) { + $("#deletepicker").html(""); + for (var key in allPlaylists) { + if (allPlaylists.hasOwnProperty(key)) { + $("#deletepicker").append(''); + } + } + $("#overlay").addClass('show'); + $(".modalThing").removeClass('show'); + $('#deletePromptBox').addClass('show'); + }); + }); + + // ── Dubtrack import file select ── + $('#dubtrackimportfile').bind('change', firetable.ui.dubtrackImportFileSelect); + $("#importDubGo").bind("click", firetable.actions.dubtrackImport); + + // ── Merge lists UI ── + $("#mergeLists").bind("click", function () { + var $this = $(this); + var isHidden = $("#mergeContain").is(":hidden"); + if (isHidden) { + ftapi.lookup.allLists(function (allPlaylists) { + $("#mergepicker").html(''); + $("#mergepicker2").html(''); + for (var key in allPlaylists) { + if (allPlaylists.hasOwnProperty(key)) { + $("#mergepicker").append(''); + $("#mergepicker2").append(''); + } + } + if (ftapi.users[ftapi.uid] && ftapi.users[ftapi.uid].selectedList) { + $("#mergepicker").val(ftapi.users[ftapi.uid].selectedList).change(); + $("#mergepicker2").val(-1).change(); + } + $("#mergeContain").show(); + $this.addClass('on'); + }); + } else { + $("#mergeContain").hide(); + $this.removeClass('on'); + } + }); + $("#startMerge").bind("click", function () { + var source = $("#mergepicker").val(); + var sourceName = $("#mergepicker option:selected").text(); + var dest = $("#mergepicker2").val(); + $("#mergeSetup").hide(); + $("#mergeHappening").show(); + firetable.debug && console.log(sourceName + " -> " + $("#mergepicker2 option:selected").text()); + firetable.actions.mergeLists(source, dest, sourceName); + }); + $("#mergeOK").bind("click", function () { + $("#mergeSetup").show(); + $("#mergeCompleted").hide(); + $("#mergeHappening").hide(); + $("#mergeContain").hide(); + }); + + // ── Tag editing (Enter in .tagMachine) ── + $(document).on("keyup", ".tagMachine", function (e) { + if (e.which !== 13) return; + var songCid = $(this).closest('.pvbar').attr('data-cid'); + var histID = $(this).closest('.pvbar').attr('data-histid'); + var val = $(this).val(); + if (!val) return; + var yargo = val.split(" - "); + if (!yargo[0] || !yargo[1]) { + alert("check yr tags"); + } else { + ftapi.actions.editTag( + $(this).closest('.pvbar').attr('data-type'), + songCid, val, histID + ); + $(this).closest('.editing').removeClass('editing').next('.tagPromptBox').remove(); + } + }); +}; diff --git a/js/room.js b/js/room.js new file mode 100644 index 0000000..e069095 --- /dev/null +++ b/js/room.js @@ -0,0 +1,631 @@ +/** + * room.js — Room state event handlers. + * + * Handles all ftapi events related to room state: + * - newSong: Load new track, start countdown/progress, Last.fm scrobble timer + * - newProduce / newHistory: Render discover/history items + * - editedHistory: Update edited tag in history list + * - modCheck: Show/hide mod-only edit buttons + * - newTheme: Display the current room theme + * - tagUpdate: Update now-playing metadata when tags are edited + * - screenStateChanged / danceStateChanged: Toggle video screen / dance mode + * - lightsChanged / colorsChanged: Festive lights CSS + accent color + * - tableChanged: Render the DJ table (up to 4 spots) + * - waitlistChanged: Render the waitlist + * - spotlightStateChanged / playLimitChanged: Highlight active DJ, update limit + * - banListChanged: Render active suspensions in mod panel + * + * BUG FIXES applied in this file: + * - `firstpart == "sc"` → `firstpart = "sc"` (was comparison, should be assignment) + * This caused SoundCloud tracks in history/discover to always get "yt" prefix. + */ + +// ─── Festive Lights CSS Generator ──────────────────────────────────────────── + +/** + * Build the "; +} + +// ─── History Item Renderer ──────────────────────────────────────────────────── + +/** + * Build and attach a history/discover item to the DOM. + * Extracted from the nearly-identical newProduce / newHistory handlers. + * + * @param {Object} data - Track data from ftapi + * @param {jQuery} $template - Cloneable template element + * @param {string} containerSel - jQuery selector for the target container + * @param {string} [artClass] - CSS class for the album art element ('discart' or 'histart') + */ +function renderHistoryItem(data, $template, containerSel, artClass) { + if (data.img === "img/idlogo.png" && ftconfigs.defaultAlbumArtUrl.length) { + data.img = ftconfigs.defaultAlbumArtUrl; + } + + // BUG FIX: was `firstpart == "sc"` (comparison), now `firstpart = "sc"` (assignment) + var firstpart = "yt"; + if (data.type == 2) firstpart = "sc"; + + var pkey = firstpart + "cid" + data.cid; + var $histItem = $template.clone(); + + $histItem.attr('id', "pvbar" + pkey) + .attr("data-key", pkey) + .attr("data-histid", data.histID) + .attr("data-cid", data.cid) + .attr("data-type", data.type); + + // Preview button + $histItem.find('.previewicon').attr('id', "pv" + pkey).on('click', function () { + firetable.actions.pview( + $(this).closest('.pvbar').attr('data-key'), + true, + $(this).closest('.pvbar').attr('data-type'), + true + ); + }); + + // Track link + $histItem.find('.histlink').attr({ + 'href': data.url, + 'tabindex': "-1", + 'id': data.histID + }).text(data.artist + " - " + data.title); + + // Edit tags button (mod only) + $histItem.find('.edittags').on('click', function () { + if ($(this).hasClass("editing")) { + $(this).removeClass("editing"); + $(this).closest('.pvbar').find('.tagPromptBox').remove(); + } else { + $(this).addClass("editing"); + firetable.actions.editTagsPrompt( + $(this).closest('.pvbar').attr('data-key'), + data.artist + " - " + data.title + ); + } + }); + try { + if (!ftapi.isMod) $histItem.find('.edittags').hide(); + } catch (e) { + console.log(e); + } + + // Metadata + $histItem.find('.histdj').text(data.dj); + $histItem.find('.histdate').text(firetable.utilities.format_date(data.when)); + $histItem.find('.histtime').text(firetable.utilities.format_time(data.when)); + + // Add-to-playlist button (shows playlist picker dropdown) + $histItem.find('.histeal').attr('id', "apv" + data.type + data.cid).on('click', function () { + var $btn = $(this); + var btnCid = $(this).closest('.pvbar').attr('data-cid'); + var btnType = $(this).closest('.pvbar').attr('data-type'); + var btnTitle = firetable.utilities.htmlEscape($(this).closest('.pvbar').find('.histlink').text()); + + // If this button's picker is already open, close it + if (firetable.stealSourceBtn && firetable.stealSourceBtn.is($btn) && !$("#stealContain").is(':hidden')) { + $btn.removeClass('on'); + firetable.stealSourceBtn = null; + firetable.stealTarget = null; + $("#stealContain").hide(); + return; + } + + ftapi.lookup.allLists(function (allPlaylists) { + $("#stealpicker").html( + '' + + '' + ); + for (var key in allPlaylists) { + if (allPlaylists.hasOwnProperty(key)) { + $("#stealpicker").append( + '' + ); + } + } + // Mark any previously open source button as off + if (firetable.stealSourceBtn) firetable.stealSourceBtn.removeClass('on'); + $("#grab").removeClass('on'); + + firetable.stealSourceBtn = $btn; + firetable.stealTarget = { cid: btnCid, type: btnType, title: btnTitle }; + $btn.addClass('on'); + + $("#stealContain").css({ + top: $btn.offset().top + $btn.outerHeight(), + left: $btn.offset().left - 16 + }).show(); + }); + }); + + // Album art + if (artClass) { + $histItem.find('.' + artClass).css('background-image', 'url(' + data.img + ')'); + } + + $histItem.prependTo(containerSel); +} + +// ─── Room Event Binding ────────────────────────────────────────────────────── + +/** + * Set up all room-state ftapi event handlers. + * Called once from firetable.ui.init(). + */ +firetable.ui.setupRoomEvents = function () { + + // ── Discover (Recently Played by Others) ── + var $discoverItem = $('#thediscovers .pvbar').remove(); + ftapi.events.on('newProduce', function (data) { + renderHistoryItem(data, $discoverItem, "#thediscovers", "discart"); + }); + + // ── History (Your Play History) ── + var $historyItem = $('#thehistory .pvbar').remove(); + + function applyHistoryFilter() { + var q = ($("#histFilter").val() || "").toLowerCase().trim(); + $("#thehistory .pvbar").each(function () { + var text = ($(this).find('.histlink').text() + " " + $(this).find('.histdj').text()).toLowerCase(); + $(this).toggle(q.length === 0 || text.indexOf(q) !== -1); + }); + } + + $(document).on('input.histfilter', '#histFilter', applyHistoryFilter); + + ftapi.events.on('newHistory', function (data) { + renderHistoryItem(data, $historyItem, "#thehistory", "histart"); + applyHistoryFilter(); + }); + + // ── Edited History (tag correction) ── + ftapi.events.on('editedHistory', function (data) { + console.log("HIST EDIT", data); + $("#" + data.histID).text(data.artist + " - " + data.title); + }); + + // ── Mod Check (show edit buttons for mods) ── + ftapi.events.on('modCheck', function (data) { + if (data) $(".edittags").show(); + }); + + // ── Theme ── + function checkThemeTicker() { + var el = document.getElementById("currentTheme"); + var container = el.parentElement; + el.classList.remove('is-ticker'); + if (el.scrollWidth > container.offsetWidth) { + var originalHTML = el.innerHTML; + el.innerHTML = '' + originalHTML + ' · ' + originalHTML + ' · '; + el.classList.add('is-ticker'); + } + } + + ftapi.events.on("newTheme", function (data) { + if (!data) { + $("#currentTheme").text("!suggest a theme"); + } else { + var txtOut = firetable.ui.strip(data); + txtOut = firetable.ui.textToLinks(txtOut, true); + txtOut = firetable.utilities.emojiShortnamestoUnicode(txtOut); + txtOut = txtOut.replace(/\`(.*?)\`/g, function (x) { + return "" + x.replace(/\`/g, "") + ""; + }); + $("#currentTheme").html(txtOut); + twemoji.parse(document.getElementById("currentTheme")); + } + setTimeout(checkThemeTicker, 50); + }); + + // ── Tag Update (metadata correction while song is playing) ── + ftapi.events.on("tagUpdate", function (data) { + firetable.debug && console.log("TAG UPDATE", data); + firetable.tagUpdate = data; + if (!firetable.song) return; + if (firetable.song.cid !== data.cid || !data.adamData.track_name) return; + + $("#track").text(firetable.ui.strip(data.adamData.track_name)); + $("#artist").text(firetable.ui.strip(data.adamData.artist)); + firetable.song.title = firetable.ui.strip(data.adamData.track_name); + firetable.song.artist = firetable.ui.strip(data.adamData.artist); + + var nicename = firetable.song.djname; + var showPlaycount = data.adamData.playcount && data.adamData.playcount > 0; + + if (data.adamData.last_play) { + $("#lastPlay").text("last " + firetable.utilities.format_date(data.adamData.last_play) + " by " + data.adamData.last_play_dj); + } else { + $("#lastPlay").text(""); + } + if (data.adamData.first_play) { + $("#firstPlay").text("first " + firetable.utilities.format_date(data.adamData.first_play) + " by " + data.adamData.first_play_dj); + } else { + $("#firstPlay").text(""); + } + + var doTheScrollThing = firetable.utilities.isChatPrettyMuchAtBottom(); + if (showPlaycount) { + $("#playCount").text(data.adamData.playcount + " plays"); + $(".npmsg" + data.cid).last().find(".npmsg").html( + 'DJ ' + nicename + ' started playing ' + data.adamData.track_name + ' by ' + data.adamData.artist + '
    This song has been played ' + data.adamData.playcount + ' times.' + ); + } else { + $("#playCount").text(""); + $(".npmsg" + data.cid).last().find(".npmsg").html( + 'DJ ' + nicename + ' started playing ' + data.adamData.track_name + ' by ' + data.adamData.artist + '' + ); + } + if (doTheScrollThing) firetable.utilities.scrollToBottom(); + }); + + // ── New Song ── + ftapi.events.on('newSong', function (data) { + $("#playCount, #lastPlay, #firstPlay").text(""); + window.dispatchEvent(new Event('resize')); + $("#cloud_with_rain, #fire").removeClass("on"); + $("#timr").countdown("destroy"); + + if (firetable.moveBar != null) { + clearInterval(firetable.moveBar); + firetable.moveBar = null; + } + + if (data.image === "img/idlogo.png" && ftconfigs.defaultAlbumArtUrl.length) { + data.image = ftconfigs.defaultAlbumArtUrl; + } + $("#prgbar").css("background", "#151515"); + + // Check if tagUpdate has pre-corrected metadata for this track + var showPlaycount = false; + if (firetable.tagUpdate && data.cid === firetable.tagUpdate.cid && firetable.tagUpdate.adamData.track_name) { + data.title = firetable.tagUpdate.adamData.track_name; + data.artist = firetable.tagUpdate.adamData.artist; + if (firetable.tagUpdate.adamData.last_play) { + $("#lastPlay").text("last " + firetable.utilities.format_date(firetable.tagUpdate.adamData.last_play) + " by " + firetable.tagUpdate.adamData.last_play_dj); + } + if (firetable.tagUpdate.adamData.first_play) { + $("#firstPlay").text("first " + firetable.utilities.format_date(firetable.tagUpdate.adamData.first_play) + " by " + firetable.tagUpdate.adamData.first_play_dj); + } + if (firetable.tagUpdate.adamData.playcount > 0) { + showPlaycount = true; + $("#playCount").text(firetable.tagUpdate.adamData.playcount + " plays"); + } + } + + // Update now-playing UI + $("#track").text(firetable.ui.strip(data.title)); + $("#artist").text(firetable.ui.strip(data.artist)); + $("#songlink").attr("href", data.url); + $("#albumArt").css("background-image", "url(" + data.image + ")"); + + // Calculate elapsed time + var nownow = Date.now(); + var timeSince = nownow - data.started; + if (timeSince <= 0) timeSince = 0; + var secSince = Math.floor(timeSince / 1000); + var timeLeft = data.duration - secSince; + firetable.song = data; + firetable.debug && console.log("NEW TRACK", data); + + // ── Last.fm scrobble timer ── + if (firetable.lastfm.timer != null) { + clearTimeout(firetable.lastfm.timer); + firetable.lastfm.timer = null; + } + if (firetable.lastfm.sk) { + firetable.lastfm.duration = Math.floor(timeLeft); + firetable.lastfm.songStart = Math.floor(Date.now() / 1000); + firetable.lastfm.timer = setTimeout(function () { + firetable.lastfm.timer = null; + firetable.lastfm.scrobble(); + }, (timeLeft * 1000) - 3000); + firetable.lastfm.nowPlaying(); + } + + // ── Platform-specific UI + playback ── + if (data.type === MEDIA_YOUTUBE) { + $("#scScreen").hide(); + $("#songlink").html(''); + + if (firetable.ytLoaded && !firetable.preview) { + if (firetable.scLoaded) firetable.scwidget.pause(); + if (!firetable.disableMediaPlayback) player.loadVideoById(data.cid, secSince, "large"); + var thevol = $("#slider").slider("value"); + player.setVolume(thevol); + firetable.scwidget.setVolume(thevol); + } + + } else if (data.type === MEDIA_SOUNDCLOUD) { + $("#scScreen").show(); + $("#songlink").html(''); + + var biggerImg = data.image.replace('-large', '-t500x500'); + firetable.scImg = biggerImg; + $("#albumArt").css("background-image", "url(" + biggerImg + ")"); + try { setup(biggerImg); } catch (e) { + firetable.debug && console.log('big image error:', e); + } + + if (firetable.scLoaded && !firetable.preview) { + if (firetable.ytLoaded) player.stopVideo(); + firetable.scSeek = timeSince; + if (!firetable.disableMediaPlayback) { + firetable.scwidget.load(SC_API_TRACK_URL + data.cid, { + auto_play: true, + single_active: false, + callback: function () { + var vol = localStorage[STORAGE.volume]; + player.setVolume(vol); + firetable.scwidget.setVolume(vol); + } + }); + } + } + } + + // ── Now-playing chat message ── + if (data.cid !== 0) { + var nicename = data.djid; + if (ftapi.users[data.djid] && ftapi.users[data.djid].username) { + nicename = ftapi.users[data.djid].username; + } + + if (firetable.nonpmsg) { + firetable.nonpmsg = false; + } else { + var doTheScrollThing = firetable.utilities.isChatPrettyMuchAtBottom(); + var npmsgHTML; + if (showPlaycount) { + npmsgHTML = '
    DJ ' + nicename + ' started playing ' + data.title + ' by ' + data.artist + '
    This song has been played ' + firetable.tagUpdate.adamData.playcount + ' times.
    '; + } else { + npmsgHTML = '
    DJ ' + nicename + ' started playing ' + data.title + ' by ' + data.artist + '
    '; + } + $("#chats").append(npmsgHTML); + if (doTheScrollThing) firetable.utilities.scrollToBottom(); + firetable.lastChatPerson = false; + firetable.lastChatId = false; + } + } + + // ── Countdown timer ── + $("#timr").countdown({ + until: timeLeft, + compact: true, + description: "", + format: "MS" + }); + + // ── Progress bar ── + firetable.moveBar = setInterval(function () { + var now = Date.now(); + var sofar = now - firetable.song.started; + var pcnt = (sofar / (firetable.song.duration * 1000)) * 100; + $("#prgbar").css("background", "linear-gradient(90deg, " + firetable.color + " " + pcnt + "%, #151515 " + pcnt + "%)"); + }, PROGRESS_BAR_INTERVAL); + }); + + // ── Screen State ── + ftapi.events.on("screenStateChanged", function (data) { + firetable.debug && console.log('thescreen:', data); + firetable.screenSyncPos = data; + if (firetable.screenControl === "sync") { + if (data) firetable.utilities.screenDown(); + else firetable.utilities.screenUp(); + } + firetable.ui.updateScreenBtn(firetable.screenControl); + }); + + // ── Dance Mode ── + ftapi.events.on("danceStateChanged", function (data) { + firetable.debug && console.log('dance check:', data); + if (data) $("#deck").addClass("dance"); + else $("#deck").removeClass("dance"); + }); + + // ── Festive Lights ── + ftapi.events.on("lightsChanged", function (data) { + firetable.debug && console.log('lights check:', data); + $('.festiveLights').remove(); + if (data) { + firetable.lights = true; + var rgb = firetable.utilities.hexToRGB(firetable.color); + $("head").append(buildFestiveLightsCSS(rgb)); + } else { + firetable.lights = false; + } + }); + + // ── Waitlist ── + ftapi.events.on("waitlistChanged", function (data) { + firetable.waitlistData = data; + var html = ""; + var cnt = "0"; + if (data) { + var countr = 1; + for (var key in data) { + if (data.hasOwnProperty(key)) { + cnt = countr; + var removeMe = data[key].removeAfter ? "departure_board" : ""; + html += '
    ' + countr + '. ' + data[key].name + + ' ' + removeMe + '
    '; + countr++; + } + } + } + $("#label2 .count").text(" (" + cnt + ")"); + $("#justwaitlist").html(html); + }); + + // ── DJ Table ── + ftapi.events.on("tableChanged", function (data) { + firetable.tableData = data; + var html = ""; + if (data) { + var countr = 0; + for (var key in data) { + if (data.hasOwnProperty(key)) { + var isSelf = data[key].id === ftapi.uid; + var ownUser = ftapi.uid && ftapi.users && ftapi.users[ftapi.uid]; + var isMod = ownUser && (ownUser.mod || ownUser.supermod); + var showBtn = isSelf || isMod; + var btnIcon = isSelf ? 'close' : 'person_remove'; + var btnTitle = isSelf ? 'Step down' : 'Remove from deck'; + var actionBtn = showBtn + ? '' + : ''; + var departureIndicator = data[key].removeAfter + ? 'departure_board' + : ''; + html += '
    ' + + '
    ' + + '
    ' + + '
    ' + data[key].name + '
    ' + + departureIndicator + actionBtn + + '
    ' + data[key].plays + '/' + + firetable.playlimit + '
    '; + countr++; + } + } + // Fill empty spots + if (countr < 4) { + html += '
    '; + countr++; + for (var i = countr; i < 4; i++) { + html += '
     
    '; + } + } + } else { + html += '
    '; + for (var i = 0; i < 3; i++) { + html += '
     
    '; + } + } + $("#deck").html(html); + $("#deck").off('click.addme').on('click.addme', '.addmeButt', function () { + ftapi.actions.sendBotCommand("!addme"); + }); + $("#deck").off('click.remove').on('click.remove', '.deckRemoveBtn', function () { + var userId = $(this).data('userid'); + if (userId === ftapi.uid) { + ftapi.actions.sendBotCommand("!removeme"); + } else { + var tableKey = $(this).data('tablekey'); + firebase.app("firetable").database().ref("table/" + tableKey).remove(); + if (firetable.waitlistData) { + for (var wkey in firetable.waitlistData) { + if (firetable.waitlistData.hasOwnProperty(wkey) && firetable.waitlistData[wkey].id === userId) { + firebase.app("firetable").database().ref("waitlist/" + wkey).remove(); + break; + } + } + } + } + }); + + // Highlight current DJ + for (var i = 0; i < 4; i++) { + if (i === firetable.playdex) { + $("#avtr" + i).addClass("animate"); + $("#djthing" + i).addClass("djActive"); + } else { + $("#avtr" + i).removeClass("animate"); + $("#djthing" + i).removeClass("djActive"); + } + } + }); + + // ── Spotlight (Active DJ Index) ── + ftapi.events.on("spotlightStateChanged", function (data) { + firetable.playdex = data; + for (var i = 0; i < 4; i++) { + if (i === data) { + $("#avtr" + i).addClass("animate"); + $("#djthing" + i).addClass("djActive"); + } else { + $("#avtr" + i).removeClass("animate"); + $("#djthing" + i).removeClass("djActive"); + } + } + }); + + // ── Play Limit ── + ftapi.events.on("playLimitChanged", function (data) { + firetable.playlimit = data; + for (var i = 0; i < 4; i++) { + $("#plimit" + i).text(data); + } + }); + + // ── Ban List ── + ftapi.events.on("banListChanged", function (data) { + $("#activeSuspentions").html(""); + for (var key in data) { + if (data[key]) { + ftapi.lookup.userByName(key, function (person) { + $("#activeSuspentions").append( + '
    ' + person.username + '
    ' + + '
    ' + ); + }); + } + } + }); + + // ── Colors Changed (Accent Color) ── + ftapi.events.on("colorsChanged", function (data) { + firetable.debug && console.log("COLOR CHANGE!", data); + + firetable.color = data.color; + firetable.countcolor = data.txt; + if (data.color === "#fff" || data.color === "#7f7f7f") { + firetable.color = firetable.orange; + firetable.countcolor = "#fff"; + } + + // Update custom color styles + $('.customColorStyles').remove(); + $("head").append( + "" + ); + + // Rebuild festive lights with new color + $('.festiveLights').remove(); + if (firetable.lights) { + var rgb = firetable.utilities.hexToRGB(firetable.color); + $("head").append(buildFestiveLightsCSS(rgb)); + } + }); +}; diff --git a/js/search.js b/js/search.js new file mode 100644 index 0000000..17d1077 --- /dev/null +++ b/js/search.js @@ -0,0 +1,285 @@ +/** + * search.js — YouTube and SoundCloud track & playlist search. + * + * Handles: + * - Track search via YouTube Data API v3 + SoundCloud API + * - Direct link detection (youtube.com/watch, soundcloud.com/…) + * - Playlist/import search via YouTube + SoundCloud + * - Dubtrack import file parsing + import + * - Search source toggle buttons (#ytsearchSelect, #scsearchSelect) + * - Import source toggle tabs + */ + +// ─── YouTube API Helper ────────────────────────────────────────────────────── + +/** + * Call the YouTube Data API v3 directly via AJAX. + * Avoids gapi client entirely — no discovery loading, no Promise conflicts. + * @param {string} resource - API resource path (e.g. 'search', 'videos', 'playlistItems') + * @param {Object} params - Query parameters (key is added automatically) + * @param {Function} callback - Called with the raw API response object + */ +function ytAPI(resource, params, callback) { + params.key = ftconfigs.youtubeKey; + $.ajax({ + url: 'https://www.googleapis.com/youtube/v3/' + resource, + data: params, + type: 'GET', + dataType: 'json', + success: callback, + error: function (xhr) { + var msg = (xhr.responseJSON && xhr.responseJSON.error) + ? xhr.responseJSON.error.code + ' ' + xhr.responseJSON.error.message + : xhr.status + ' ' + xhr.statusText; + console.error('YouTube API error:', msg, xhr.responseJSON || xhr.responseText); + callback({ items: [] }); + } + }); +} + +/** + * Extract a query-string parameter value from a URL string. + * @param {string} str - Full URL + * @param {string} key - Parameter name + * @returns {string} Decoded parameter value (or empty string) + */ +function getQueryStringValue(str, key) { + return unescape( + str.replace( + new RegExp("^(?:.*[&\\?]" + escape(key).replace(/[\.\+\*]/g, "\\$&") + "(?:\\=([^&]*))?)?.*$", "i"), + "$1" + ) + ); +} + +// ─── Track Search Setup ────────────────────────────────────────────────────── + +/** + * Bind the track search (#qsearch) and import search (#plMachine) keyup handlers. + * Called once from firetable.ui.init(). + */ +firetable.ui.setupSearchEvents = function () { + var $searchItemTemplate = $('#searchResults .pvbar').remove(); + + // ── Track Search (Enter in #qsearch) ── + $("#qsearch").bind("keyup", function (e) { + if (e.which !== 13) return; + var txt = $("#qsearch").val(); + if (!txt) return; + + if (firetable.searchSelectsChoice === MEDIA_YOUTUBE) { + // ── YouTube Track Search ── + var showYTResults = function (response) { + firetable.debug && console.log('queue search:', response); + $('#searchResults').html(""); + firetable.utilities.cancelSearchPreview(); + + var srchItems = response.items; + $.each(srchItems, function (index, item) { + var thecid = item.kind === "youtube#searchResult" ? item.id.videoId : item.id; + var parsed = firetable.utilities.parseArtistTitle( + item.snippet.title, + item.snippet.channelTitle.replace(" - Topic", "") + ); + var vidTitle = parsed.artist + " - " + parsed.title; + var pkey = "ytcid" + thecid; + + var $srli = $searchItemTemplate.clone(); + $srli.attr('id', "pvbar" + pkey) + .attr("data-key", pkey) + .attr("data-cid", thecid); + $srli.find('.previewicon').attr('id', "pv" + pkey).on('click', function () { + firetable.actions.pview($(this).closest('.pvbar').attr('data-key'), true, MEDIA_YOUTUBE); + }); + $srli.find('.listwords').html(vidTitle); + $srli.find('.queuetrack').on('click', function () { + firetable.actions.queueTrack( + $(this).closest('.pvbar').attr('data-cid'), + firetable.utilities.htmlEscape($(this).closest('.pvbar').find('.listwords').text()), + MEDIA_YOUTUBE + ); + }); + $("#searchResults").append($srli); + }); + }; + + // Check if it's a direct YouTube URL + var directLink = false; + var thecid = false; + if (txt.match(/youtube.com\/watch/)) { + thecid = getQueryStringValue(txt, "v"); + if (thecid) directLink = true; + } + + if (directLink) { + firetable.debug && console.log("direct yt link found"); + ytAPI('videos', { id: thecid, part: 'snippet', maxResults: 1 }, showYTResults); + } else { + $('#searchResults').html("Searching..."); + ytAPI('search', { q: txt, type: 'video', part: 'snippet', maxResults: SEARCH_MAX_RESULTS }, showYTResults); + } + + } else if (firetable.searchSelectsChoice === MEDIA_SOUNDCLOUD) { + // ── SoundCloud Track Search ── + var q = txt; + + var showSCResults = function (tracks) { + firetable.debug && console.log('sc tracks:', tracks); + $('#searchResults').html(""); + firetable.utilities.cancelSearchPreview(); + + $.each(tracks, function (index, item) { + var parsed = firetable.utilities.parseArtistTitle(item.title, item.user.username); + var vidTitle = parsed.artist + " - " + parsed.title; + var pkey = "sccid" + item.id; + + var $srli = $searchItemTemplate.clone(); + $srli.attr('id', "pvbar" + pkey) + .attr("data-key", pkey) + .attr("data-cid", item.id); + $srli.find('.previewicon').attr('id', "pv" + pkey).on('click', function () { + firetable.actions.pview($(this).closest('.pvbar').attr('data-key'), true, MEDIA_SOUNDCLOUD); + }); + $srli.find('.listwords').html(vidTitle); + $srli.find('.queuetrack').on('click', function () { + firetable.actions.queueTrack( + $(this).closest('.pvbar').attr('data-cid'), + firetable.utilities.htmlEscape($(this).closest('.pvbar').find('.listwords').text()), + MEDIA_SOUNDCLOUD + ); + }); + $("#searchResults").append($srli); + }); + }; + + var directLink = false; + if (q.match(/:\/\/soundcloud\.com\//)) directLink = true; + + $('#searchResults').html("Searching..."); + if (directLink) { + firetable.debug && console.log("sc direct link found"); + firetable.actions.resolveSCLink(q, function (item) { + var items = []; + if (item.kind === "track") items.push(item); + showSCResults(items); + }); + } else { + SC.get('/tracks', { q: q }).then(function (tracks) { + showSCResults(tracks); + }); + } + } + }); + + // ── Search Source Toggle Buttons ── + $("#ytsearchSelect").bind("click", function () { + $("#scsearchSelect").removeClass("on"); + $(this).addClass("on"); + firetable.searchSelectsChoice = MEDIA_YOUTUBE; + }); + $("#scsearchSelect").bind("click", function () { + $("#ytsearchSelect").removeClass("on"); + $(this).addClass("on"); + firetable.searchSelectsChoice = MEDIA_SOUNDCLOUD; + }); + + // ── Import Source Toggle Tabs ── + $("#ytimportchoice").bind("click", function () { + firetable.debug && console.log("yt import"); + firetable.importSelectsChoice = MEDIA_YOUTUBE; + }); + $("#scimportchoice").bind("click", function () { + firetable.debug && console.log("sc import"); + firetable.importSelectsChoice = MEDIA_SOUNDCLOUD; + }); + $("#dtimportchoice").bind("click", function () { + firetable.debug && console.log("dt import"); + firetable.importSelectsChoice = 3; // Dubtrack + }); + $("#importSources .tab").bind("click", function () { + if (firetable.importSelectsChoice === 3) { + $("#importDubContent").show(); + $("#importContent").hide(); + } else { + $("#importDubContent").hide(); + $("#importContent").show(); + } + $(this).siblings().removeClass('on'); + $(this).addClass('on'); + }); + + // ── Playlist/Import Search (#plMachine) ── + $("#plMachine").bind("keyup", function (e) { + if (e.which !== 13) return; + var val = $("#plMachine").val(); + if (!val) return; + $("#importResults").html(""); + $("#plMachine").val(""); + var searchFrom = firetable.importSelectsChoice; + + if (searchFrom === MEDIA_YOUTUBE) { + // ── YouTube Playlist Search ── + var listID; + var directLink = false; + + // Check for direct playlist URL + if (val.match(/youtube.com\/watch/) || val.match(/youtube.com\/playlist/)) { + listID = getQueryStringValue(val, "list"); + if (listID) directLink = true; + } + + if (directLink) { + ytAPI('playlists', { id: listID, part: 'snippet' }, function (response) { + if (response.items && response.items.length === 1) { + var item = response.items[0]; + $("#importResults").append( + '
    ' + item.snippet.title + ' by ' + item.snippet.channelTitle + '
    ' + + ' ' + + '
    ' + ); + } + }); + } else { + ytAPI('search', { q: val, type: 'playlist', part: 'snippet', maxResults: SEARCH_MAX_RESULTS }, function (response) { + firetable.debug && console.log('import search results:', response); + $.each(response.items || [], function (index, item) { + $("#importResults").append( + '
    ' + item.snippet.title + ' by ' + item.snippet.channelTitle + '
    ' + + ' ' + + '
    ' + ); + }); + }); + } + + } else if (searchFrom === MEDIA_SOUNDCLOUD) { + // ── SoundCloud Playlist Search ── + if (val.match(/.*\/\/soundcloud\.com\/.*\/sets\/.*/)) { + // Direct set URL + firetable.actions.resolveSCLink(val, function (item) { + if (item && item.sharing === "public" && item.kind === "playlist") { + $("#importResults").append( + '
    ' + item.title + ' by ' + item.user.username + ' (' + item.track_count + ' songs)
    ' + + ' ' + + '
    ' + ); + } + }); + } else { + // Keyword search for playlists + SC.get('/playlists', { q: val }).then(function (lists) { + for (var i = 0; i < lists.length; i++) { + var item = lists[i]; + if (item.sharing === "public") { + $("#importResults").append( + '
    ' + item.title + ' by ' + item.user.username + ' (' + item.track_count + ' songs)
    ' + + ' ' + + '
    ' + ); + } + } + }); + } + } + }); +}; diff --git a/js/state.js b/js/state.js new file mode 100644 index 0000000..a10443e --- /dev/null +++ b/js/state.js @@ -0,0 +1,224 @@ +/** + * state.js — Central application state object for firetable. + * + * This is the single source of truth for all runtime state. Every property + * is documented with its type and purpose. Other modules read/write these + * properties directly via the global `firetable` object. + * + * Also sets up the chat scroll container, idle-detection, and throws early + * if config.js is missing. + */ + +// ─── Config Guard ──────────────────────────────────────────────────────────── +if (typeof ftconfigs === "undefined") { + throw "config.js is missing! Copy config.js.example and rename to config.js. Edit this file and add your own app's information."; +} + +// ─── Application State ────────────────────────────────────────────────────── +var firetable = { + + // ── Session ── + /** @type {boolean} Has firetable.init() been called? */ + started: false, + /** @type {boolean} Is the current user authenticated? */ + loggedIn: false, + /** @type {string|null} Current user's UID (set after login) */ + uid: null, + /** @type {string|null} Current user's display name */ + uname: null, + + // ── Avatars ── + /** @type {string} Robohash set name (e.g. "set1", "set2") */ + avatarset: "set1", + /** @type {string} Active avatar style key e.g. "robohash:set1" or "dicebear:pixel-art" */ + avatarStyle: "robohash:set1", + + // ── Preview State ── + /** @type {number} Elapsed preview bar counter (seconds, in 0.2 increments) */ + pvCount: 0, + /** @type {string|false} Song key currently being previewed, or false */ + preview: false, + /** @type {number|null} setInterval ID for the preview progress bar */ + movePvBar: null, + /** @type {number|null} setTimeout ID for auto-stopping preview after 30s */ + ptimeout: null, + + // ── Playback ── + /** @type {number} Index of the currently-active DJ seat (0-3) */ + playdex: 0, + /** @type {number|null} setInterval ID for the song progress bar */ + moveBar: null, + /** @type {Object|null} Current song data {cid, type, artist, title, started, duration, …} */ + song: null, + /** @type {boolean} Play notification sound on @-mentions? */ + playBadoop: true, + + // ── Idle ── + /** @type {boolean} Is the current user idle / tab hidden? */ + idle: false, + /** @type {number|null} Timestamp of last idle-state change */ + idleChanged: null, + + // ── Display Preferences ── + /** @type {boolean} Show inline images in chat? (typo preserved: "sbhowImages" in original) */ + showImages: false, + /** @type {string} Screen control mode: "sync" | "on" | "off" */ + screenControl: "sync", + /** @type {boolean} Are festive lights enabled? */ + lights: false, + /** @type {boolean} Server-side screen-up/down position (used when screenControl == "sync") */ + screenSyncPos: false, + /** @type {boolean} Should media playback be completely disabled? */ + disableMediaPlayback: false, + /** @type {boolean} Show avatar images in chat? */ + showAvatars: true, + + // ── SoundCloud ── + /** @type {number|false} Seek position (ms) to set when SC widget starts playing */ + scSeek: false, + /** @type {Object|null} SoundCloud Widget API instance */ + scwidget: null, + /** @type {string} URL of the current SoundCloud track's large artwork */ + scImg: "", + /** @type {boolean|null} Has the SoundCloud widget finished loading? */ + scLoaded: null, + + // ── YouTube ── + /** @type {boolean|null} Has the YouTube IFrame API finished loading? */ + ytLoaded: null, + + // ── Desktop Notifications ── + /** @type {boolean} Send desktop notifications on @-mentions? */ + desktopNotifyMentions: false, + + // ── Theme / Colours ── + /** @type {string} The default brand orange (never changes) */ + orange: COLOR_ORANGE, + /** @type {string} Current room accent colour (hex) — set by colorsChanged event */ + color: COLOR_ORANGE, + /** @type {string} Current text colour for accent backgrounds */ + countcolor: "#fff", + + // ── Playlist / Queue ── + /** @type {Object|false} Current playlist data keyed by song ID */ + queue: false, + /** @type {string|null} Which playlist panel is currently visible */ + listShowing: null, + /** @type {number} Play limit per DJ turn */ + playlimit: 2, + + // ── DOM / Parser ── + /** @type {DOMParser|null} Shared DOMParser for stripping HTML */ + parser: null, + + // ── Tag Editing ── + /** @type {string|null} Song ID currently being tag-edited */ + songToEdit: null, + /** @type {Object|null} Latest tag update data from server */ + tagUpdate: null, + + // ── Search Source Toggle ── + /** @type {number} 1 = YouTube, 2 = SoundCloud — tracks the "Add" search toggle */ + searchSelectsChoice: MEDIA_YOUTUBE, + /** @type {number} 1 = YouTube, 2 = SoundCloud, 3 = Dubtrack — tracks the "Import" toggle */ + importSelectsChoice: MEDIA_YOUTUBE, + + // ── Dubtrack Import ── + /** @type {string|null} Name parsed from Dubtrack export file */ + dtImportName: null, + /** @type {Array} Track list parsed from Dubtrack export file */ + dtImportList: [], + + // ── Chat State ── + /** @type {string|false} UID of the last person who sent a chat (for message grouping) */ + lastChatPerson: false, + /** @type {string|false} Chat ID of the last grouped message block */ + lastChatId: false, + /** @type {boolean} Suppress the first "now playing" chat message on page load */ + nonpmsg: true, + + // ── Mod Tools ── + /** @type {Object|null} Ban update subscription data */ + superCopBanUpdates: null, + + // ── Login ── + /** @type {string|null} Cached login form HTML (preserved across auth state changes) */ + loginForm: null, + + // ── Emoji ── + /** @type {Object|null} Map of shortname → unicode emoji character */ + emojiMap: null, + /** @type {boolean} Has the emoji picker been twemoji-parsed? */ + pickerInit: false, + + // ── @-Mention Autocomplete ── + /** @type {boolean} Is the user currently in @-mention mode? */ + atLand: false, + /** @type {Array} All usernames available for @-mention */ + atUsers: [], + /** @type {Array} Filtered usernames matching current @-string */ + atUsersFiltered: [], + /** @type {string} Characters typed after the @ symbol so far */ + atString: "", + + // ── Debug ── + /** @type {boolean} Enable verbose console logging */ + debug: false +}; + +// ─── Version ───────────────────────────────────────────────────────────────── +firetable.version = "01.10.8"; + +// ─── Chat Scroll Container ────────────────────────────────────────────────── +/** Native scroll container wrapper preserving the legacy chatScroll API shape. */ +var chatScrollElement = document.getElementById('chatsWrap'); +var chatScroll = chatScrollElement ? { + el: chatScrollElement, + contentEl: chatScrollElement, + contentWrapperEl: chatScrollElement, + getScrollElement: function () { + return chatScrollElement; + } +} : null; + +if (chatScroll) { + chatScroll.getScrollElement().addEventListener('scroll', function () { + if (firetable.utilities.isChatPrettyMuchAtBottom()) { + $('#morechats').removeClass('show'); + } + }, { passive: true }); +} + +// ─── Global Player Reference ──────────────────────────────────────────────── +/** @type {YT.Player} YouTube IFrame Player instance (set in onYouTubeIframeAPIReady) */ +var player; +/** @type {jQuery} Cloned template element for playlist queue items */ +var $playlistItemTemplate; +/** @type {jQuery} Cloned template element for the tag editor */ +var $tagEditorTemplate; + +// ─── Idle Detection ───────────────────────────────────────────────────────── +/** + * Tracks mouse/keyboard activity and fires idle/active events. + * Sends idle status to the server via ftapi so other users see you as away. + */ +var idlejs = new IdleJs({ + idle: IDLE_TIMEOUT, + events: ['mousemove', 'keydown', 'mousedown', 'touchstart'], + onIdle: function () { + ftapi.actions.changeIdleStatus(true, 1); + }, + onActive: function () { + ftapi.actions.changeIdleStatus(false, 1); + }, + onHide: function () { + ftapi.actions.changeIdleStatus(true, 1); + firetable.debug && console.log("Tab hidden — marking idle"); + }, + onShow: function () { + ftapi.actions.changeIdleStatus(false, 1); + }, + keepTracking: true, + startAtIdle: false +}); +idlejs.start(); diff --git a/js/twemoji.js b/js/twemoji.js new file mode 100644 index 0000000..a95ab20 --- /dev/null +++ b/js/twemoji.js @@ -0,0 +1,2 @@ +/*! Copyright Twitter Inc. and other contributors. Licensed under MIT */ +var twemoji=function(){"use strict";var twemoji={base:"https://cdn.jsdelivr.net/gh/twitter/twemoji/assets/",ext:".png",size:"72x72",className:"emoji",convert:{fromCodePoint:fromCodePoint,toCodePoint:toCodePoint},onerror:function onerror(){if(this.parentNode){this.parentNode.replaceChild(createText(this.alt,false),this)}},parse:parse,replace:replace,test:test},escaper={"&":"&","<":"<",">":">","'":"'",'"':"""},re=/(?:\ud83d\udc68\ud83c\udffb\u200d\u2764\ufe0f\u200d\ud83d\udc8b\u200d\ud83d\udc68\ud83c[\udffb-\udfff]|\ud83d\udc68\ud83c\udffc\u200d\u2764\ufe0f\u200d\ud83d\udc8b\u200d\ud83d\udc68\ud83c[\udffb-\udfff]|\ud83d\udc68\ud83c\udffd\u200d\u2764\ufe0f\u200d\ud83d\udc8b\u200d\ud83d\udc68\ud83c[\udffb-\udfff]|\ud83d\udc68\ud83c\udffe\u200d\u2764\ufe0f\u200d\ud83d\udc8b\u200d\ud83d\udc68\ud83c[\udffb-\udfff]|\ud83d\udc68\ud83c\udfff\u200d\u2764\ufe0f\u200d\ud83d\udc8b\u200d\ud83d\udc68\ud83c[\udffb-\udfff]|\ud83d\udc69\ud83c\udffb\u200d\u2764\ufe0f\u200d\ud83d\udc8b\u200d\ud83d\udc68\ud83c[\udffb-\udfff]|\ud83d\udc69\ud83c\udffb\u200d\u2764\ufe0f\u200d\ud83d\udc8b\u200d\ud83d\udc69\ud83c[\udffb-\udfff]|\ud83d\udc69\ud83c\udffc\u200d\u2764\ufe0f\u200d\ud83d\udc8b\u200d\ud83d\udc68\ud83c[\udffb-\udfff]|\ud83d\udc69\ud83c\udffc\u200d\u2764\ufe0f\u200d\ud83d\udc8b\u200d\ud83d\udc69\ud83c[\udffb-\udfff]|\ud83d\udc69\ud83c\udffd\u200d\u2764\ufe0f\u200d\ud83d\udc8b\u200d\ud83d\udc68\ud83c[\udffb-\udfff]|\ud83d\udc69\ud83c\udffd\u200d\u2764\ufe0f\u200d\ud83d\udc8b\u200d\ud83d\udc69\ud83c[\udffb-\udfff]|\ud83d\udc69\ud83c\udffe\u200d\u2764\ufe0f\u200d\ud83d\udc8b\u200d\ud83d\udc68\ud83c[\udffb-\udfff]|\ud83d\udc69\ud83c\udffe\u200d\u2764\ufe0f\u200d\ud83d\udc8b\u200d\ud83d\udc69\ud83c[\udffb-\udfff]|\ud83d\udc69\ud83c\udfff\u200d\u2764\ufe0f\u200d\ud83d\udc8b\u200d\ud83d\udc68\ud83c[\udffb-\udfff]|\ud83d\udc69\ud83c\udfff\u200d\u2764\ufe0f\u200d\ud83d\udc8b\u200d\ud83d\udc69\ud83c[\udffb-\udfff]|\ud83e\uddd1\ud83c\udffb\u200d\u2764\ufe0f\u200d\ud83d\udc8b\u200d\ud83e\uddd1\ud83c[\udffc-\udfff]|\ud83e\uddd1\ud83c\udffc\u200d\u2764\ufe0f\u200d\ud83d\udc8b\u200d\ud83e\uddd1\ud83c[\udffb\udffd-\udfff]|\ud83e\uddd1\ud83c\udffd\u200d\u2764\ufe0f\u200d\ud83d\udc8b\u200d\ud83e\uddd1\ud83c[\udffb\udffc\udffe\udfff]|\ud83e\uddd1\ud83c\udffe\u200d\u2764\ufe0f\u200d\ud83d\udc8b\u200d\ud83e\uddd1\ud83c[\udffb-\udffd\udfff]|\ud83e\uddd1\ud83c\udfff\u200d\u2764\ufe0f\u200d\ud83d\udc8b\u200d\ud83e\uddd1\ud83c[\udffb-\udffe]|\ud83d\udc68\ud83c\udffb\u200d\u2764\ufe0f\u200d\ud83d\udc68\ud83c[\udffb-\udfff]|\ud83d\udc68\ud83c\udffb\u200d\ud83e\udd1d\u200d\ud83d\udc68\ud83c[\udffc-\udfff]|\ud83d\udc68\ud83c\udffc\u200d\u2764\ufe0f\u200d\ud83d\udc68\ud83c[\udffb-\udfff]|\ud83d\udc68\ud83c\udffc\u200d\ud83e\udd1d\u200d\ud83d\udc68\ud83c[\udffb\udffd-\udfff]|\ud83d\udc68\ud83c\udffd\u200d\u2764\ufe0f\u200d\ud83d\udc68\ud83c[\udffb-\udfff]|\ud83d\udc68\ud83c\udffd\u200d\ud83e\udd1d\u200d\ud83d\udc68\ud83c[\udffb\udffc\udffe\udfff]|\ud83d\udc68\ud83c\udffe\u200d\u2764\ufe0f\u200d\ud83d\udc68\ud83c[\udffb-\udfff]|\ud83d\udc68\ud83c\udffe\u200d\ud83e\udd1d\u200d\ud83d\udc68\ud83c[\udffb-\udffd\udfff]|\ud83d\udc68\ud83c\udfff\u200d\u2764\ufe0f\u200d\ud83d\udc68\ud83c[\udffb-\udfff]|\ud83d\udc68\ud83c\udfff\u200d\ud83e\udd1d\u200d\ud83d\udc68\ud83c[\udffb-\udffe]|\ud83d\udc69\ud83c\udffb\u200d\u2764\ufe0f\u200d\ud83d\udc68\ud83c[\udffb-\udfff]|\ud83d\udc69\ud83c\udffb\u200d\u2764\ufe0f\u200d\ud83d\udc69\ud83c[\udffb-\udfff]|\ud83d\udc69\ud83c\udffb\u200d\ud83e\udd1d\u200d\ud83d\udc68\ud83c[\udffc-\udfff]|\ud83d\udc69\ud83c\udffb\u200d\ud83e\udd1d\u200d\ud83d\udc69\ud83c[\udffc-\udfff]|\ud83d\udc69\ud83c\udffc\u200d\u2764\ufe0f\u200d\ud83d\udc68\ud83c[\udffb-\udfff]|\ud83d\udc69\ud83c\udffc\u200d\u2764\ufe0f\u200d\ud83d\udc69\ud83c[\udffb-\udfff]|\ud83d\udc69\ud83c\udffc\u200d\ud83e\udd1d\u200d\ud83d\udc68\ud83c[\udffb\udffd-\udfff]|\ud83d\udc69\ud83c\udffc\u200d\ud83e\udd1d\u200d\ud83d\udc69\ud83c[\udffb\udffd-\udfff]|\ud83d\udc69\ud83c\udffd\u200d\u2764\ufe0f\u200d\ud83d\udc68\ud83c[\udffb-\udfff]|\ud83d\udc69\ud83c\udffd\u200d\u2764\ufe0f\u200d\ud83d\udc69\ud83c[\udffb-\udfff]|\ud83d\udc69\ud83c\udffd\u200d\ud83e\udd1d\u200d\ud83d\udc68\ud83c[\udffb\udffc\udffe\udfff]|\ud83d\udc69\ud83c\udffd\u200d\ud83e\udd1d\u200d\ud83d\udc69\ud83c[\udffb\udffc\udffe\udfff]|\ud83d\udc69\ud83c\udffe\u200d\u2764\ufe0f\u200d\ud83d\udc68\ud83c[\udffb-\udfff]|\ud83d\udc69\ud83c\udffe\u200d\u2764\ufe0f\u200d\ud83d\udc69\ud83c[\udffb-\udfff]|\ud83d\udc69\ud83c\udffe\u200d\ud83e\udd1d\u200d\ud83d\udc68\ud83c[\udffb-\udffd\udfff]|\ud83d\udc69\ud83c\udffe\u200d\ud83e\udd1d\u200d\ud83d\udc69\ud83c[\udffb-\udffd\udfff]|\ud83d\udc69\ud83c\udfff\u200d\u2764\ufe0f\u200d\ud83d\udc68\ud83c[\udffb-\udfff]|\ud83d\udc69\ud83c\udfff\u200d\u2764\ufe0f\u200d\ud83d\udc69\ud83c[\udffb-\udfff]|\ud83d\udc69\ud83c\udfff\u200d\ud83e\udd1d\u200d\ud83d\udc68\ud83c[\udffb-\udffe]|\ud83d\udc69\ud83c\udfff\u200d\ud83e\udd1d\u200d\ud83d\udc69\ud83c[\udffb-\udffe]|\ud83e\uddd1\ud83c\udffb\u200d\u2764\ufe0f\u200d\ud83e\uddd1\ud83c[\udffc-\udfff]|\ud83e\uddd1\ud83c\udffb\u200d\ud83e\udd1d\u200d\ud83e\uddd1\ud83c[\udffb-\udfff]|\ud83e\uddd1\ud83c\udffc\u200d\u2764\ufe0f\u200d\ud83e\uddd1\ud83c[\udffb\udffd-\udfff]|\ud83e\uddd1\ud83c\udffc\u200d\ud83e\udd1d\u200d\ud83e\uddd1\ud83c[\udffb-\udfff]|\ud83e\uddd1\ud83c\udffd\u200d\u2764\ufe0f\u200d\ud83e\uddd1\ud83c[\udffb\udffc\udffe\udfff]|\ud83e\uddd1\ud83c\udffd\u200d\ud83e\udd1d\u200d\ud83e\uddd1\ud83c[\udffb-\udfff]|\ud83e\uddd1\ud83c\udffe\u200d\u2764\ufe0f\u200d\ud83e\uddd1\ud83c[\udffb-\udffd\udfff]|\ud83e\uddd1\ud83c\udffe\u200d\ud83e\udd1d\u200d\ud83e\uddd1\ud83c[\udffb-\udfff]|\ud83e\uddd1\ud83c\udfff\u200d\u2764\ufe0f\u200d\ud83e\uddd1\ud83c[\udffb-\udffe]|\ud83e\uddd1\ud83c\udfff\u200d\ud83e\udd1d\u200d\ud83e\uddd1\ud83c[\udffb-\udfff]|\ud83d\udc68\u200d\u2764\ufe0f\u200d\ud83d\udc8b\u200d\ud83d\udc68|\ud83d\udc69\u200d\u2764\ufe0f\u200d\ud83d\udc8b\u200d\ud83d[\udc68\udc69]|\ud83e\udef1\ud83c\udffb\u200d\ud83e\udef2\ud83c[\udffc-\udfff]|\ud83e\udef1\ud83c\udffc\u200d\ud83e\udef2\ud83c[\udffb\udffd-\udfff]|\ud83e\udef1\ud83c\udffd\u200d\ud83e\udef2\ud83c[\udffb\udffc\udffe\udfff]|\ud83e\udef1\ud83c\udffe\u200d\ud83e\udef2\ud83c[\udffb-\udffd\udfff]|\ud83e\udef1\ud83c\udfff\u200d\ud83e\udef2\ud83c[\udffb-\udffe]|\ud83d\udc68\u200d\u2764\ufe0f\u200d\ud83d\udc68|\ud83d\udc69\u200d\u2764\ufe0f\u200d\ud83d[\udc68\udc69]|\ud83e\uddd1\u200d\ud83e\udd1d\u200d\ud83e\uddd1|\ud83d\udc6b\ud83c[\udffb-\udfff]|\ud83d\udc6c\ud83c[\udffb-\udfff]|\ud83d\udc6d\ud83c[\udffb-\udfff]|\ud83d\udc8f\ud83c[\udffb-\udfff]|\ud83d\udc91\ud83c[\udffb-\udfff]|\ud83e\udd1d\ud83c[\udffb-\udfff]|\ud83d[\udc6b-\udc6d\udc8f\udc91]|\ud83e\udd1d)|(?:\ud83d[\udc68\udc69]|\ud83e\uddd1)(?:\ud83c[\udffb-\udfff])?\u200d(?:\u2695\ufe0f|\u2696\ufe0f|\u2708\ufe0f|\ud83c[\udf3e\udf73\udf7c\udf84\udf93\udfa4\udfa8\udfeb\udfed]|\ud83d[\udcbb\udcbc\udd27\udd2c\ude80\ude92]|\ud83e[\uddaf-\uddb3\uddbc\uddbd])|(?:\ud83c[\udfcb\udfcc]|\ud83d[\udd74\udd75]|\u26f9)((?:\ud83c[\udffb-\udfff]|\ufe0f)\u200d[\u2640\u2642]\ufe0f)|(?:\ud83c[\udfc3\udfc4\udfca]|\ud83d[\udc6e\udc70\udc71\udc73\udc77\udc81\udc82\udc86\udc87\ude45-\ude47\ude4b\ude4d\ude4e\udea3\udeb4-\udeb6]|\ud83e[\udd26\udd35\udd37-\udd39\udd3d\udd3e\uddb8\uddb9\uddcd-\uddcf\uddd4\uddd6-\udddd])(?:\ud83c[\udffb-\udfff])?\u200d[\u2640\u2642]\ufe0f|(?:\ud83d\udc68\u200d\ud83d\udc68\u200d\ud83d\udc66\u200d\ud83d\udc66|\ud83d\udc68\u200d\ud83d\udc68\u200d\ud83d\udc67\u200d\ud83d[\udc66\udc67]|\ud83d\udc68\u200d\ud83d\udc69\u200d\ud83d\udc66\u200d\ud83d\udc66|\ud83d\udc68\u200d\ud83d\udc69\u200d\ud83d\udc67\u200d\ud83d[\udc66\udc67]|\ud83d\udc69\u200d\ud83d\udc69\u200d\ud83d\udc66\u200d\ud83d\udc66|\ud83d\udc69\u200d\ud83d\udc69\u200d\ud83d\udc67\u200d\ud83d[\udc66\udc67]|\ud83d\udc68\u200d\ud83d\udc66\u200d\ud83d\udc66|\ud83d\udc68\u200d\ud83d\udc67\u200d\ud83d[\udc66\udc67]|\ud83d\udc68\u200d\ud83d\udc68\u200d\ud83d[\udc66\udc67]|\ud83d\udc68\u200d\ud83d\udc69\u200d\ud83d[\udc66\udc67]|\ud83d\udc69\u200d\ud83d\udc66\u200d\ud83d\udc66|\ud83d\udc69\u200d\ud83d\udc67\u200d\ud83d[\udc66\udc67]|\ud83d\udc69\u200d\ud83d\udc69\u200d\ud83d[\udc66\udc67]|\ud83c\udff3\ufe0f\u200d\u26a7\ufe0f|\ud83c\udff3\ufe0f\u200d\ud83c\udf08|\ud83d\ude36\u200d\ud83c\udf2b\ufe0f|\u2764\ufe0f\u200d\ud83d\udd25|\u2764\ufe0f\u200d\ud83e\ude79|\ud83c\udff4\u200d\u2620\ufe0f|\ud83d\udc15\u200d\ud83e\uddba|\ud83d\udc3b\u200d\u2744\ufe0f|\ud83d\udc41\u200d\ud83d\udde8|\ud83d\udc68\u200d\ud83d[\udc66\udc67]|\ud83d\udc69\u200d\ud83d[\udc66\udc67]|\ud83d\udc6f\u200d\u2640\ufe0f|\ud83d\udc6f\u200d\u2642\ufe0f|\ud83d\ude2e\u200d\ud83d\udca8|\ud83d\ude35\u200d\ud83d\udcab|\ud83e\udd3c\u200d\u2640\ufe0f|\ud83e\udd3c\u200d\u2642\ufe0f|\ud83e\uddde\u200d\u2640\ufe0f|\ud83e\uddde\u200d\u2642\ufe0f|\ud83e\udddf\u200d\u2640\ufe0f|\ud83e\udddf\u200d\u2642\ufe0f|\ud83d\udc08\u200d\u2b1b)|[#*0-9]\ufe0f?\u20e3|(?:[©®\u2122\u265f]\ufe0f)|(?:\ud83c[\udc04\udd70\udd71\udd7e\udd7f\ude02\ude1a\ude2f\ude37\udf21\udf24-\udf2c\udf36\udf7d\udf96\udf97\udf99-\udf9b\udf9e\udf9f\udfcd\udfce\udfd4-\udfdf\udff3\udff5\udff7]|\ud83d[\udc3f\udc41\udcfd\udd49\udd4a\udd6f\udd70\udd73\udd76-\udd79\udd87\udd8a-\udd8d\udda5\udda8\uddb1\uddb2\uddbc\uddc2-\uddc4\uddd1-\uddd3\udddc-\uddde\udde1\udde3\udde8\uddef\uddf3\uddfa\udecb\udecd-\udecf\udee0-\udee5\udee9\udef0\udef3]|[\u203c\u2049\u2139\u2194-\u2199\u21a9\u21aa\u231a\u231b\u2328\u23cf\u23ed-\u23ef\u23f1\u23f2\u23f8-\u23fa\u24c2\u25aa\u25ab\u25b6\u25c0\u25fb-\u25fe\u2600-\u2604\u260e\u2611\u2614\u2615\u2618\u2620\u2622\u2623\u2626\u262a\u262e\u262f\u2638-\u263a\u2640\u2642\u2648-\u2653\u2660\u2663\u2665\u2666\u2668\u267b\u267f\u2692-\u2697\u2699\u269b\u269c\u26a0\u26a1\u26a7\u26aa\u26ab\u26b0\u26b1\u26bd\u26be\u26c4\u26c5\u26c8\u26cf\u26d1\u26d3\u26d4\u26e9\u26ea\u26f0-\u26f5\u26f8\u26fa\u26fd\u2702\u2708\u2709\u270f\u2712\u2714\u2716\u271d\u2721\u2733\u2734\u2744\u2747\u2757\u2763\u2764\u27a1\u2934\u2935\u2b05-\u2b07\u2b1b\u2b1c\u2b50\u2b55\u3030\u303d\u3297\u3299])(?:\ufe0f|(?!\ufe0e))|(?:(?:\ud83c[\udfcb\udfcc]|\ud83d[\udd74\udd75\udd90]|[\u261d\u26f7\u26f9\u270c\u270d])(?:\ufe0f|(?!\ufe0e))|(?:\ud83c[\udf85\udfc2-\udfc4\udfc7\udfca]|\ud83d[\udc42\udc43\udc46-\udc50\udc66-\udc69\udc6e\udc70-\udc78\udc7c\udc81-\udc83\udc85-\udc87\udcaa\udd7a\udd95\udd96\ude45-\ude47\ude4b-\ude4f\udea3\udeb4-\udeb6\udec0\udecc]|\ud83e[\udd0c\udd0f\udd18-\udd1c\udd1e\udd1f\udd26\udd30-\udd39\udd3d\udd3e\udd77\uddb5\uddb6\uddb8\uddb9\uddbb\uddcd-\uddcf\uddd1-\udddd\udec3-\udec5\udef0-\udef6]|[\u270a\u270b]))(?:\ud83c[\udffb-\udfff])?|(?:\ud83c\udff4\udb40\udc67\udb40\udc62\udb40\udc65\udb40\udc6e\udb40\udc67\udb40\udc7f|\ud83c\udff4\udb40\udc67\udb40\udc62\udb40\udc73\udb40\udc63\udb40\udc74\udb40\udc7f|\ud83c\udff4\udb40\udc67\udb40\udc62\udb40\udc77\udb40\udc6c\udb40\udc73\udb40\udc7f|\ud83c\udde6\ud83c[\udde8-\uddec\uddee\uddf1\uddf2\uddf4\uddf6-\uddfa\uddfc\uddfd\uddff]|\ud83c\udde7\ud83c[\udde6\udde7\udde9-\uddef\uddf1-\uddf4\uddf6-\uddf9\uddfb\uddfc\uddfe\uddff]|\ud83c\udde8\ud83c[\udde6\udde8\udde9\uddeb-\uddee\uddf0-\uddf5\uddf7\uddfa-\uddff]|\ud83c\udde9\ud83c[\uddea\uddec\uddef\uddf0\uddf2\uddf4\uddff]|\ud83c\uddea\ud83c[\udde6\udde8\uddea\uddec\udded\uddf7-\uddfa]|\ud83c\uddeb\ud83c[\uddee-\uddf0\uddf2\uddf4\uddf7]|\ud83c\uddec\ud83c[\udde6\udde7\udde9-\uddee\uddf1-\uddf3\uddf5-\uddfa\uddfc\uddfe]|\ud83c\udded\ud83c[\uddf0\uddf2\uddf3\uddf7\uddf9\uddfa]|\ud83c\uddee\ud83c[\udde8-\uddea\uddf1-\uddf4\uddf6-\uddf9]|\ud83c\uddef\ud83c[\uddea\uddf2\uddf4\uddf5]|\ud83c\uddf0\ud83c[\uddea\uddec-\uddee\uddf2\uddf3\uddf5\uddf7\uddfc\uddfe\uddff]|\ud83c\uddf1\ud83c[\udde6-\udde8\uddee\uddf0\uddf7-\uddfb\uddfe]|\ud83c\uddf2\ud83c[\udde6\udde8-\udded\uddf0-\uddff]|\ud83c\uddf3\ud83c[\udde6\udde8\uddea-\uddec\uddee\uddf1\uddf4\uddf5\uddf7\uddfa\uddff]|\ud83c\uddf4\ud83c\uddf2|\ud83c\uddf5\ud83c[\udde6\uddea-\udded\uddf0-\uddf3\uddf7-\uddf9\uddfc\uddfe]|\ud83c\uddf6\ud83c\udde6|\ud83c\uddf7\ud83c[\uddea\uddf4\uddf8\uddfa\uddfc]|\ud83c\uddf8\ud83c[\udde6-\uddea\uddec-\uddf4\uddf7-\uddf9\uddfb\uddfd-\uddff]|\ud83c\uddf9\ud83c[\udde6\udde8\udde9\uddeb-\udded\uddef-\uddf4\uddf7\uddf9\uddfb\uddfc\uddff]|\ud83c\uddfa\ud83c[\udde6\uddec\uddf2\uddf3\uddf8\uddfe\uddff]|\ud83c\uddfb\ud83c[\udde6\udde8\uddea\uddec\uddee\uddf3\uddfa]|\ud83c\uddfc\ud83c[\uddeb\uddf8]|\ud83c\uddfd\ud83c\uddf0|\ud83c\uddfe\ud83c[\uddea\uddf9]|\ud83c\uddff\ud83c[\udde6\uddf2\uddfc]|\ud83c[\udccf\udd8e\udd91-\udd9a\udde6-\uddff\ude01\ude32-\ude36\ude38-\ude3a\ude50\ude51\udf00-\udf20\udf2d-\udf35\udf37-\udf7c\udf7e-\udf84\udf86-\udf93\udfa0-\udfc1\udfc5\udfc6\udfc8\udfc9\udfcf-\udfd3\udfe0-\udff0\udff4\udff8-\udfff]|\ud83d[\udc00-\udc3e\udc40\udc44\udc45\udc51-\udc65\udc6a\udc6f\udc79-\udc7b\udc7d-\udc80\udc84\udc88-\udc8e\udc90\udc92-\udca9\udcab-\udcfc\udcff-\udd3d\udd4b-\udd4e\udd50-\udd67\udda4\uddfb-\ude44\ude48-\ude4a\ude80-\udea2\udea4-\udeb3\udeb7-\udebf\udec1-\udec5\uded0-\uded2\uded5-\uded7\udedd-\udedf\udeeb\udeec\udef4-\udefc\udfe0-\udfeb\udff0]|\ud83e[\udd0d\udd0e\udd10-\udd17\udd20-\udd25\udd27-\udd2f\udd3a\udd3c\udd3f-\udd45\udd47-\udd76\udd78-\uddb4\uddb7\uddba\uddbc-\uddcc\uddd0\uddde-\uddff\ude70-\ude74\ude78-\ude7c\ude80-\ude86\ude90-\udeac\udeb0-\udeba\udec0-\udec2\uded0-\uded9\udee0-\udee7]|[\u23e9-\u23ec\u23f0\u23f3\u267e\u26ce\u2705\u2728\u274c\u274e\u2753-\u2755\u2795-\u2797\u27b0\u27bf\ue50a])|\ufe0f/g,UFE0Fg=/\uFE0F/g,U200D=String.fromCharCode(8205),rescaper=/[&<>'"]/g,shouldntBeParsed=/^(?:iframe|noframes|noscript|script|select|style|textarea)$/,fromCharCode=String.fromCharCode;return twemoji;function createText(text,clean){return document.createTextNode(clean?text.replace(UFE0Fg,""):text)}function escapeHTML(s){return s.replace(rescaper,replacer)}function defaultImageSrcGenerator(icon,options){return"".concat(options.base,options.size,"/",icon,options.ext)}function grabAllTextNodes(node,allText){var childNodes=node.childNodes,length=childNodes.length,subnode,nodeType;while(length--){subnode=childNodes[length];nodeType=subnode.nodeType;if(nodeType===3){allText.push(subnode)}else if(nodeType===1&&!("ownerSVGElement"in subnode)&&!shouldntBeParsed.test(subnode.nodeName.toLowerCase())){grabAllTextNodes(subnode,allText)}}return allText}function grabTheRightIcon(rawText){return toCodePoint(rawText.indexOf(U200D)<0?rawText.replace(UFE0Fg,""):rawText)}function parseNode(node,options){var allText=grabAllTextNodes(node,[]),length=allText.length,attrib,attrname,modified,fragment,subnode,text,match,i,index,img,rawText,iconId,src;while(length--){modified=false;fragment=document.createDocumentFragment();subnode=allText[length];text=subnode.nodeValue;i=0;while(match=re.exec(text)){index=match.index;if(index!==i){fragment.appendChild(createText(text.slice(i,index),true))}rawText=match[0];iconId=grabTheRightIcon(rawText);i=index+rawText.length;src=options.callback(iconId,options);if(iconId&&src){img=new Image;img.onerror=options.onerror;img.setAttribute("draggable","false");attrib=options.attributes(rawText,iconId);for(attrname in attrib){if(attrib.hasOwnProperty(attrname)&&attrname.indexOf("on")!==0&&!img.hasAttribute(attrname)){img.setAttribute(attrname,attrib[attrname])}}img.className=options.className;img.alt=rawText;img.src=src;modified=true;fragment.appendChild(img)}if(!img)fragment.appendChild(createText(rawText,false));img=null}if(modified){if(i")}return ret})}function replacer(m){return escaper[m]}function returnNull(){return null}function toSizeSquaredAsset(value){return typeof value==="number"?value+"x"+value:value}function fromCodePoint(codepoint){var code=typeof codepoint==="string"?parseInt(codepoint,16):codepoint;if(code<65536){return fromCharCode(code)}code-=65536;return fromCharCode(55296+(code>>10),56320+(code&1023))}function parse(what,how){if(!how||typeof how==="function"){how={callback:how}}return(typeof what==="string"?parseString:parseNode)(what,{callback:how.callback||defaultImageSrcGenerator,attributes:typeof how.attributes==="function"?how.attributes:returnNull,base:typeof how.base==="string"?how.base:twemoji.base,ext:how.ext||twemoji.ext,size:how.folder||toSizeSquaredAsset(how.size||twemoji.size),className:how.className||twemoji.className,onerror:how.onerror||twemoji.onerror})}function replace(text,callback){return String(text).replace(re,callback)}function test(text){re.lastIndex=0;var result=re.test(text);re.lastIndex=0;return result}function toCodePoint(unicodeSurrogates,sep){var r=[],c=0,p=0,i=0;while(i" + + ".previewicon { display: none !important; } " + + "div#playerControls { display: none !important; } " + + "" + ); +}; + +/** + * Show player/preview controls (re-enables media playback UI). + */ +firetable.ui.showPlayerControls = function () { + $(".playerControlsHider").remove(); +}; + +// ─── User Tabs (All Users / Waitlist) ──────────────────────────────────────── + +/** Show the "All Users" panel, hide the "Waitlist" panel */ +firetable.ui.usertab1 = function () { + $("#allusersWrap").css("display", "block"); + $("#justwaitWrap").css("display", "none"); + $("#usertabs").find(".on").removeClass("on"); + $("#label1").addClass("on"); +}; + +/** Show the "Waitlist" panel, hide the "All Users" panel */ +firetable.ui.usertab2 = function () { + $("#usertabs").find(".on").removeClass("on"); + $("#label2").addClass("on"); + $("#allusersWrap").css("display", "none"); + $("#justwaitWrap").css("display", "block"); +}; + +// ─── LinkGrabber (Drag-and-Drop Track Detection) ───────────────────────────── + +/** + * Intercepts drag-and-drop events over the queue panel. + * Creates a transparent