From 2df280e07184366db236c4f3a6d3e9099540c2b1 Mon Sep 17 00:00:00 2001 From: William Toohey Date: Wed, 20 Apr 2016 21:36:54 +1000 Subject: [PATCH] The Great CSS Update of 2016 - Mostly use BEM now - No longer use IDs for anything (except labels for inputs, but these are generated to not collide) - Better organise CSS - Remove some redundant CSS - No longer have any HTML, all generated - Canvas now renders at any size - Changed some settings - Add new super minimal "mini" UI - Probably some other stuff since I haven't committed in forever --- .jshintrc | 3 +- index.html | 50 +-- src/css/hues-editor.css | 92 +++--- src/css/hues-main.css | 188 +++++++++++ src/css/hues-res.css | 233 ------------- src/css/hues-respacks.css | 201 ++++++++++++ src/css/hues-settings.css | 182 +++++++++++ src/css/{hues-h.css => huesUI-hlwn.css} | 36 +-- src/css/{hues-m.css => huesUI-modern.css} | 20 +- src/css/{hues-r.css => huesUI-retro.css} | 12 +- src/css/{hues-w.css => huesUI-weed.css} | 14 +- src/css/{hues-x.css => huesUI-xmas.css} | 31 +- src/css/style.css | 377 ---------------------- src/js/HuesCanvas.js | 140 ++------ src/js/HuesCore.js | 211 +++++++----- src/js/HuesEditor.js | 189 ++++++----- src/js/HuesInfo.js | 56 ++-- src/js/HuesSettings.js | 198 ++++++------ src/js/HuesUI.js | 203 +++++++++--- src/js/HuesWindow.js | 171 ++++++++++ src/js/ResourceManager.js | 181 +++++------ 21 files changed, 1476 insertions(+), 1312 deletions(-) create mode 100644 src/css/hues-main.css delete mode 100644 src/css/hues-res.css create mode 100644 src/css/hues-respacks.css create mode 100644 src/css/hues-settings.css rename src/css/{hues-h.css => huesUI-hlwn.css} (89%) rename src/css/{hues-m.css => huesUI-modern.css} (97%) rename src/css/{hues-r.css => huesUI-retro.css} (89%) rename src/css/{hues-w.css => huesUI-weed.css} (84%) rename src/css/{hues-x.css => huesUI-xmas.css} (93%) delete mode 100644 src/css/style.css create mode 100644 src/js/HuesWindow.js diff --git a/.jshintrc b/.jshintrc index d22fe13..f2d58b4 100644 --- a/.jshintrc +++ b/.jshintrc @@ -2,5 +2,6 @@ "esversion": 6, // Allow array["access"]. We use this with localStorage to avoid any // aggressive minification doing variable name optimisation - "sub": true + "sub": true, + "loopfunc": true } \ No newline at end of file diff --git a/index.html b/index.html index 9230440..fb42f9a 100644 --- a/index.html +++ b/index.html @@ -23,54 +23,6 @@ -
-
-
Initialising...
-
This page requires Javascript
-
-
-
-
-
x
-
- - - - -
-
- - - - -
- -
-
-
- -
-
-
- -
-
-
-
-

0x40 Hues of JS,

-

Adapted from the 0x40 Flash

-

Web-ified by mon

-

With help from Kepstin

-
- -
-
-
-
-
- -
- - + This page requires Javascript. \ No newline at end of file diff --git a/src/css/hues-editor.css b/src/css/hues-editor.css index de8451d..b15532e 100644 --- a/src/css/hues-editor.css +++ b/src/css/hues-editor.css @@ -1,6 +1,6 @@ -#huesEditor { +.editor { position: relative; - display: flex !important; + display: flex; flex-direction: column; max-width: calc(100% - 10px); width: 1000px; @@ -11,22 +11,21 @@ font-size: 13px; } -#huesEditor > hr { +.editor > hr { width: 100%; } -#edit-titlebuttons { - margin: -2px 0 2px -2px; +.editor__title-buttons { height: 15px; } -#edit-status-msg { +.editor__status-msg { font-size: 10px; color: red; opacity: 1; } -#edit-status-msg.fade { +.editor__status-msg--fade { animation-duration: 10s; animation-name: hues-pulsefade; animation-fill-mode: forwards; @@ -70,42 +69,41 @@ } } -#edit-topbar { +.editor__top-bar { display: flex; height: 60px; } - -#edit-info { +.editor__info { flex-grow: 1; } -#edit-imports { +.editor__imports { padding-top: 6px; } -#edit-songstats { +.editor__song-stats { margin-top: 5px; margin-left: -8px; } -.edit-songstat-value { +.editor__song-stats__value { position: absolute; right: 2px; } -.edit-label { +.editor__label { display: flex; margin: 12px auto; } -.edit-textbox-container { +.editor__textinput-container { display: flex; margin-top: -2px; flex-grow: 1; } -.edit-textbox { +.editor__textinput { flex-grow: 1; font-family: 'PetMe64Web'; font-size: 7pt; @@ -115,7 +113,18 @@ border-style: solid; } -#edit-timelock { +.edit-area { + flex-grow: 1; + /*height: initial !important;*/ + margin: 0 auto; + width: 100%; +} + +.edit_area__song-section { + height: auto; +} + +.edit-area__timelock { display: flex; align-items: center; position: absolute; @@ -123,7 +132,7 @@ margin-left: -4px; } -#edit-timelock:before { +.edit-area__timelock:before { position: absolute; z-index: -1; content : ""; @@ -136,56 +145,48 @@ border-bottom: 3px #666 solid; } -#edit-timelock.unlocked:before { +.edit-area__timelock--unlocked:before { border-left: 3px #666 dashed; } -#edit-timelock > .hues-button { +.edit-area__timelock > .hues-button { /* because of the pseudo element this one can't be transparent */ background-color: rgb(171,171,171) !important; } -#edit-timelock > .hues-button:hover { +.edit-area__timelock > .hues-button:hover { background-color: rgb(236,236,236) !important; } - -#edit-area { - flex-grow: 1; - /*height: initial !important;*/ - margin: 0 auto; - width: 100%; -} - -.edit-area-header { +.edit-area__header { padding-bottom: 8px; margin-left: 15px; } -.beat-count { +.edit-area__beat-count { font-size: 10px; margin-left: 4px; } -.edit-area-right-header { +.edit-area__header__right { position: absolute; right: 25px; } -.edit-box { +.edit-area__box { position: relative; overflow-y : scroll; background-color: white; margin: auto 20px; } -.beatmap { +.edit-area__beatmap { height: inherit; width: inherit; position: relative; } -.beatmap:focus { +.edit-area__beatmap:focus { outline-width: 0; outline: none; } @@ -198,46 +199,38 @@ padding: 0; } -.beat-hilight.hidden { - visibility: hidden; -} - -#edit-build, #edit-loop { - height: auto; -} - -#edit-resize-handle-container { +.resize-handle { width: 100%; height: 20px; cursor: row-resize; overflow: hidden; } -#edit-resize-handle { +.resize-handle__handle { transform: scale(10.0, 1.0); font-size: 20px; text-align:center; color: #999; } -#edit-resize-handle:hover { +.resize-handle__handle:hover { color: #000; } -#edit-controls { +.edit__controls { display: flex; justify-content: space-between; height: 25px !important; margin: 0 20px; } -#edit-waveform { +.waveform { width: 100%; height: 50px; } /* Make it as invisible as we can */ -#edit-copybox { +.copybox { position : fixed; top : 0; left : 0; @@ -245,6 +238,5 @@ height : 2em; padding : 0; border : none; - outline : none; background : transparent; } \ No newline at end of file diff --git a/src/css/hues-main.css b/src/css/hues-main.css new file mode 100644 index 0000000..e973ef9 --- /dev/null +++ b/src/css/hues-main.css @@ -0,0 +1,188 @@ +@font-face { + font-family: 'PetMe64Web'; + font-style: normal; + font-weight: normal; + -webkit-font-smoothing: none; + font-smooth: never; + src: url("../fonts/PetMe64.woff") format('woff'); +} + +@font-face { + font-family: 'icomoon'; + src: url('../fonts/HuesExtra.eot?gmxg3s'); + src: url('../fonts/HuesExtra.eot?gmxg3s#iefix') format('embedded-opentype'), + url('../fonts/HuesExtra.ttf?gmxg3s') format('truetype'), + url('../fonts/HuesExtra.woff?gmxg3s') format('woff'), + url('../fonts/HuesExtra.svg?gmxg3s#icomoon') format('svg'); + font-weight: normal; + font-style: normal; +} + +.hues-root { + height: 100%; + margin: 0; padding: 0; + overflow: hidden; + font-family: 'PetMe64Web'; + position: relative; +} + +.hues-root h1, .hues-root h2, .hues-root h3 { + text-align: center; +} + +.hues-root h1 { + font-size: 15pt; +} + +.hues-root h2 { + font-size: 10pt; + +} + +.hues-root h3 { + font-size: 7pt; +} + +.hidden { + display: none !important; +} + +.invisible { + visibility: hidden !important; +} + +.hues-icon { + /* use !important to prevent issues with browser extensions that change fonts */ + font-family: 'icomoon' !important; + speak: none; + font-style: normal; + font-weight: normal; + font-variant: normal; + text-transform: none; + line-height: 1; + + /* Better Font Rendering =========== */ + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} + +.hues-canvas { + position: absolute; + top: 0; + left: 0; + display: block; + height: 100%; + padding: 0; + z-index: -10; +} + +.hues-visualiser { + position:absolute; + z-index: -1; +} + +.hues-preloader { + /* first 2 colours are the loaded colour, next 2 are unloaded */ + background: linear-gradient(to right, #fff 0%,#fff 50%,#ddd 50%,#ddd 100%); + background-size: 200% 100%; + background-position: 100% 0; + + width: 100%; + height: 100%; + display:flex; + justify-content:center; + align-items:center; + flex-direction: column; + font-size: 25pt; + + position: absolute; + top: 0; + left: 0; + z-index: 10; + visibility: visible; + opacity: 1; + transition: visibility 1s linear, opacity 1s linear, background-position 0.5s ease; +} + +.hues-preloader--loaded { + visibility: hidden; + opacity: 0; +} + +.hues-preloader__text { + display: block; + text-align: center; +} + +.hues-preloader__subtext { + font-size: 12pt; +} + +.hues-preloader__subtext span{ + font-size: 8pt; + opacity: 0.7; +} + +.unstyled-link { + color: inherit; + text-decoration: none; +} + +.hues-button { + font-size: 10px; + margin: 3px 2px; + padding: 3px; + background-color: rgba(127,127,127, 0.5); + border-color: rgb(0,0,0); + border-width: 1px; + border-style: solid; + cursor: pointer; + /* Don't want double click to select */ + -webkit-touch-callout: none; + -webkit-user-select: none; + -khtml-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; +} + +.hues-button--loaded { + background-color: rgba(0,127,0,0.5); + cursor: default; +} + +.hues-button--disabled { + color: #777; + cursor: default; +} + +.hues-button:hover { + background: rgba(255,255,255, 0.5); +} + +.hues-button--loaded:hover { + background-color: rgba(0,127,0,0.5); + cursor: default; +} + +.hues-button--disabled:hover { + background-color: rgba(127,127,127, 0.5); +} + +.hues-button--glow { + animation-name: glow; + animation-duration: 2s; + animation-iteration-count: infinite; +} + +@keyframes glow { + from { + background-color: rgba(127,127,127, 0.5); + } + 50% { + background-color: rgba(200,220,200, 0.5); + } + to { + background-color: rgba(127,127,127, 0.5); + } +} diff --git a/src/css/hues-res.css b/src/css/hues-res.css deleted file mode 100644 index 9235258..0000000 --- a/src/css/hues-res.css +++ /dev/null @@ -1,233 +0,0 @@ -#huesResources { - position: relative; - margin: 5px; - height: 385px; - font-size: 14px; -} - -.res-packscontainer, .res-packcontainer { - position: absolute; -} - -.res-packscontainer { - width: 43%; - left: 0; - margin-right: 10px; -} - -.res-packcontainer { - width: 55%; - height: 100%; - right: 0; - margin-left: 10px; -} - -.res-header { - padding: 5px 0px; -} - -#res-curheader { - padding-top: 0px; -} - -#res-packlist { - height: 120px; -} - -#res-packlist.noremotes { - height: 305px; -} - -#res-remotelist { - height: 155px; -} - -#res-progress-container { - font-size: 11px; -} - -#res-progress-bar { - height:5px; /* Can be anything */ - position: relative; - background: #000; - border-radius: 25px; - padding: 2px; - margin: 2px; -} - -#res-progress-filled { - display: block; - height: 100%; - border-radius: 8px; - background-color: rgb(43,194,83); - position: relative; - overflow: hidden; -} - -#res-progress-texts { - font-size: 9px; -} - -#res-progress-current { - position: absolute; - left: 5px; -} - -#res-progress-top { - position: absolute; - right: 5px; -} - -#res-progress-percent { - text-align: center; -} - -.res-list { - border: 2px solid black; - background: rgba(255,255,255,0.3); - overflow: auto; - overflow-x: hidden; -} - -.res-listitem { - font-size: 10px; - border-bottom: 1px solid black; - display: flex; - align-items: center; -} - -.res-listitem > span { - display: block; - width: 100%; - height: 100%; - padding: 2px; - - cursor: pointer; -} - -.res-listitem :hover { - background: rgba(255,255,255,0.5); -} - -.res-listitem input[type=checkbox] { - display: none; -} - -.res-listitem > label { - content: ""; - - width: 12px; - height: 10px; - margin: auto 2px; - - background-color: #ccc; - border: 1px solid black; - cursor: pointer; -} - -.res-listitem input[type=checkbox]:before { - border-radius: 3px; -} - -.res-listitem input[type=checkbox]:checked + label { - background-color: #222; - text-align: center; - line-height: 15px; -} - -.res-buttons { - display: flex; - justify-content: space-between; - padding: 0px 0px; -} - -.res-buttons.hidden { - display: none; -} - -#res-countscontainer { - position: absolute; - bottom: 0; - width: 100%; -} - -.res-counts { - position: absolute; - right: 3px; -} - -#res-packinfo { - margin-top: 5px; - font-size: 9px; - display: flex; - justify-content: space-between; -} - -#res-packcreator > a:link, #res-packcreator > a:visited { - color: inherit; - text-decoration: none; -} - -#res-packdesc { - border: 3px solid gray; - background: rgba(255,255,255,0.5); - font-size: 9px; - height: 85px; - margin: 2px; - padding: 2px; -} - -#res-packtabs > label { - display: table-cell; - border: 2px solid black; - padding: 5px; - cursor: pointer; - /* Actually wider than the container, but has a centering effect */ - width: 200px; -} - -#res-packtabs > input:checked + label { - background: rgba(255,255,255,0.3); - border-bottom: none; -} - -#res-packtabs > label:hover { - background: rgba(255,255,255,0.3); -} - -#res-songlist, #res-imagelist { - display: none; - height: 170px; - border-top: none; -} - -#res-songtab:checked ~ #res-songlist, -#res-imagetab:checked ~ #res-imagelist { - display: block; -} - -#res-packbuttons > .res-button { - flex-grow: 1; - text-align: center; -} - -#res-enabledsonglist, #res-enabledimagelist { - display: block; - position: absolute; - bottom: 0; - right: 0; - max-height: 150px; - overflow: auto; -} - -#res-enabledsonglist.hidden, #res-enabledimagelist.hidden { - display: none; -} - -#res-enabledsonglist { - width: 515px; -} - -#res-enabledimagelist { - width: 315px; -} \ No newline at end of file diff --git a/src/css/hues-respacks.css b/src/css/hues-respacks.css new file mode 100644 index 0000000..be48cc7 --- /dev/null +++ b/src/css/hues-respacks.css @@ -0,0 +1,201 @@ +.respacks { + display: flex; + box-sizing: border-box; + width: 640px; + + margin: 5px; + height: 400px; + font-size: 14px; +} + +.respacks__manager, .respacks__display { + box-sizing: border-box; + display: flex; + flex-direction: column; +} + +.respacks__manager { + width: 40%; + margin-right: 5px; +} + +.respacks__display { + width: 60%; + margin-left: 5px; +} + +.respacks__header { + padding: 5px 0; + flex-shrink: 0; +} + +.resource-list { + flex-grow: 1; + + border: 2px solid black; + background: rgba(255,255,255,0.3); + overflow: auto; + overflow-x: hidden; +} + +.resource-list--fill { + height: 100%; +} + +.respacks-listitem { + font-size: 10px; + border-bottom: 1px solid black; + display: flex; + align-items: center; +} + +.respacks-listitem > span { + display: block; + width: 100%; + height: 100%; + padding: 2px; + + cursor: pointer; +} + +.respacks-listitem :hover { + background: rgba(255,255,255,0.5); +} + +.respacks-listitem input[type=checkbox] { + display: none; +} + +.respacks-listitem > label { + content: ""; + + width: 12px; + height: 10px; + margin: auto 2px; + + background-color: #ccc; + border: 1px solid black; + cursor: pointer; +} + +.respacks-listitem input[type=checkbox]:before { + border-radius: 3px; +} + +.respacks-listitem input[type=checkbox]:checked + label { + background-color: #222; + text-align: center; + line-height: 15px; +} + +.respacks-buttons { + flex-shrink: 0; + display: flex; + justify-content: space-between; + padding: 0; +} + +.respacks-buttons--fill > .hues-button { + flex-grow: 1; + text-align: center; +} + +.respacks-bottom-container { + height: 35px; +} + +.progress-container { + height: 35px; + font-size: 11px; +} + +.progress-bar { + height:5px; /* Can be anything */ + position: relative; + background: #000; + border-radius: 25px; + padding: 2px; + margin: 2px; +} + +.progress-bar--filled { + display: block; + height: 100%; + border-radius: 8px; + background-color: rgb(43,194,83); + position: relative; + overflow: hidden; +} + +.stat-text { + flex-shrink: 0; + display: flex; + justify-content: space-between; + margin: 0 5px; + font-size: 9px; +} + +.respack-description { + flex-shrink: 0; + border: 3px solid gray; + background: rgba(255,255,255,0.5); + font-size: 9px; + margin: 2px; + padding: 2px; +} + +.respack-tab-container { + flex-shrink: 0; + display: flex; + width: 100%; +} + +.respack-tab { + box-sizing: border-box; + border: 2px solid black; + padding: 5px; + cursor: pointer; + /* Actually wider than the container, but has a centering effect */ + width: 50%; +} + +.respack-tab--checked { + background: rgba(255,255,255,0.3); + border-bottom: none; +} + +.respack-tab:hover { + background: rgba(255,255,255,0.3); +} + +.respack-tab__content { + display: none; + border-top: none; +} + +.respack-tab__content--checked { + display: block; +} + +.respacks-count-container { + flex-shrink: 0; + display: flex; + justify-content: space-between; +} + +.respacks-enabledsongs, .respacks-enabledimages { + display: block; + position: absolute; + bottom: 0; + right: 0; + max-height: 150px; + overflow: auto; +} + +.respacks-enabledsongs { + width: 515px; +} + +.respacks-enabledimages { + width: 315px; +} \ No newline at end of file diff --git a/src/css/hues-settings.css b/src/css/hues-settings.css new file mode 100644 index 0000000..ddfb4ed --- /dev/null +++ b/src/css/hues-settings.css @@ -0,0 +1,182 @@ +.hues-win-helper { + display: flex; + justify-content: center; + align-items: center; + position: absolute; + margin-top: -15px; + width: 100%; + height: 100%; +} + +.hues-win { + position: relative; + z-index: 9; + min-width: 640px; + min-height: 470px; + max-height: calc(100% - 50px); + margin: 10px; + + background: rgba(200,200,200, 0.7); + border-color: black; + border-width: 2px; + border-style: solid; +} + +.hues-win__closebtn { + height: 20px; + width: 20px; + font-size: 20px; + color: white; + position: absolute; + right: 0; + background-color: rgb(128,128,128); + border: 1px solid black; + cursor: pointer; +} + +.hues-win__tabs { + margin: -1px; + padding-top: 22px; + display: flex; +} + + +.tab-label{ + flex-grow: 1; + cursor: pointer; + padding: 10px; + border: 2px solid black; + text-align: center; +} + +.tab-label--active { + border-bottom: 0; +} + +l.tab-label:hover { + background: rgba(255,255,255,0.3); +} + +.tab-content { + display: none; +} + +.tab-content--active { + display: block; +} + +.hues-about { + border-bottom: 2px solid black; +} + +.hues-options { + display:flex; + flex-wrap: wrap; + width: 640px; + padding: 5px; +} + +.hues-ref { + display:flex; + flex-wrap: wrap; + width: 640px; + align-items:center; + justify-content: center; + padding: 5px; +} + +.hues-ref__info { + float: left; + font-size: 8pt; + text-align: left; + background: rgba(100,100,100,0.3); + border-radius: 15px; + margin: 10px; + padding: 0 5px; +} + +.hues-ref__info h3 { + font-size: 10pt; + margin: 10px 0 0 10px; + text-align: left; +} + +.hues-ref__info ul { + list-style-type: none; + padding: 0; + margin: 10px; +} + +.hues-ref__info li { + /* Chrome rendering fix */ + line-height: 9pt; +} + +.hues-win__closebtn:hover { + background-color: rgb(200,200,200); +} + +.hues-win__closebtn:after { + content : "x"; +} + +.settings-category { + font-size: 16px; + width: 50%; + float: left; + margin-bottom: 10px; +} + +.settings-individual{ + font-size: 8pt; + /* Chrome rendering fix */ + line-height: 9pt; + padding-left: 10px; +} + +.settings-buttons{ + display: flex; + flex-wrap: wrap; +} + +.settings-checkbox{ + display: none; +} + +.settings-label { + font-size: 7pt; + margin: 4px 2px; + padding: 3px; + background: rgba(127,127,127, 0.5); + border-color: rgb(0,0,0); + border-width: 1px; + border-style: solid; + cursor: pointer; + /* Don't want double click to select */ + -webkit-touch-callout: none; + -webkit-user-select: none; + -khtml-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; +} + +.settings-label:hover { + background: rgba(200,200,200,0.5); +} + +input[type="radio"]:checked + .settings-label { + background: rgba(255,255,255, 0.5); +} + +.settings-input { + font-family: 'PetMe64Web'; + font-size: 7pt; + padding: 3px; + margin: -6px 0; + background: rgba(127,127,127, 0.5); + border-color: rgb(0,0,0); + border-width: 1px; + border-style: solid; + width: 2em; +} \ No newline at end of file diff --git a/src/css/hues-h.css b/src/css/huesUI-hlwn.css similarity index 89% rename from src/css/hues-h.css rename to src/css/huesUI-hlwn.css index dacb7c7..af4636f 100644 --- a/src/css/hues-h.css +++ b/src/css/huesUI-hlwn.css @@ -1,13 +1,13 @@ /* HalloweenUI */ -.hues-h-text { +.hues-h-text { color: #ff3300; } .inverted .hues-h-text { color: #00ccff; } -#preloadHelper.hues-h-text { +.hues-preloader.hues-h-text { background: linear-gradient(to right, #000 0%,#000 50%,#222 50%,#222 100%); background-size: 200% 100%; background-position: 100% 0; @@ -29,7 +29,7 @@ .hues-m-beatcenter.hues-h-text { background: none; background-image: url('../img/skull.png'); - top: 0px; + top: 0; width: 42px; height: 43px; box-shadow: none; @@ -43,14 +43,14 @@ font-size: 13px; } -.hues-m-beatcenter.hues-h-text.hidden{ +.hues-m-beatcenter.hues-h-text.hues-ui--hidden{ transform: translateY(-80px); } .hues-h-eyes { background: none; background-image: url('../img/skull-eyes.png'); - top: 0px; + top: 0; width: 42px; height: 64px; box-shadow: none; @@ -60,11 +60,11 @@ animation-fill-mode: forwards; } .inverted .hues-h-eyes { - /* Set as important to override the other .inverted selector from modern */ - background: none !important; - background-image: url('../img/skull-eyes.png') !important; - box-shadow: none !important; - background-position: -42px 0 !important; + /* Set again to override the other .inverted selector from modern */ + background: none; + background-image: url('../img/skull-eyes.png'); + box-shadow: none; + background-position: -42px 0; } .hues-h-left-hand { @@ -94,17 +94,17 @@ } @media (min-width: 768px) { - .hues-m-controls.hues-h-controls.hidden { + .hues-m-controls.hues-h-controls.hues-ui--hidden { transform: translateY(64px); } } .hues-m-songtitle.hues-h-text, .hues-m-imagename.hues-h-text { - padding: 4px 0px; - margin: 0px 5px; + padding: 4px 0; + margin: 0 5px; background: none; border-style: solid; - border-width: 0px 19px 0px 18px; + border-width: 0 19px 0 18px; border-image: url(../img/bones.png) 29 19 0 18 fill repeat stretch; } .inverted .hues-m-songtitle.hues-h-text, .inverted .hues-m-imagename.hues-h-text { @@ -164,7 +164,7 @@ z-index: -10; border-style: solid; - border-width: 22px 40px 0px 42px; + border-width: 22px 40px 0 42px; border-image: url(../img/tombstone.png) 22 42 0 fill stretch; } .inverted .hues-h-tombstone { @@ -219,11 +219,11 @@ } .hues-h-topleft, .hues-h-topright { - top: 0px; + top: 0; } .hues-h-bottomright, .hues-h-topright { - right: 0px; + right: 0; } .hues-h-topleft { @@ -246,7 +246,7 @@ .hues-h-bottomright { background-image: url("../img/web-bottomright.png"); - bottom: 0px; + bottom: 0; width:358px; height: 284px; } diff --git a/src/css/hues-m.css b/src/css/huesUI-modern.css similarity index 97% rename from src/css/hues-m.css rename to src/css/huesUI-modern.css index 011d7fe..789433f 100644 --- a/src/css/hues-m.css +++ b/src/css/huesUI-modern.css @@ -11,11 +11,11 @@ transition: transform 1s ease-out; } -.hues-m-beatbar.hidden, .hues-m-beatcenter.hidden{ +.hues-m-beatbar.hues-ui--hidden, .hues-m-beatcenter.hues-ui--hidden{ transform: translateY(-40px); } -.hues-m-controls.hidden { +.hues-m-controls.hues-ui--hidden { transform: translateY(108px); } @@ -23,10 +23,10 @@ position: absolute; width: 100%; height: 64px; - bottom:108px; - left:-8px; - right:0px; - margin:0px auto; + bottom: 108px; + left: -8px; + right: 0; + margin: 0 auto; } .hues-m-beatbar { @@ -323,7 +323,7 @@ position: absolute; left: 8px; right: 8px; - bottom: 0px; + bottom: 0; margin: 0 auto; height: 30px; max-width: 992px; @@ -343,7 +343,7 @@ background: linear-gradient(rgba(255,255,255,0), rgba(255,255,255,0.4)); } -.hues-m-hiderestore.hidden { +.hues-m-hiderestore.hues-ui--hidden { display: block; } @@ -403,6 +403,7 @@ input[type=range] { padding: 0; height: 12px; background: transparent; + -moz-appearance: none; -webkit-appearance: none; } @@ -418,6 +419,7 @@ input[type=range]::-webkit-slider-runnable-track { } input[type=range]::-webkit-slider-thumb { + -moz-appearance: none; -webkit-appearance: none; box-shadow: none; border: none; @@ -492,7 +494,7 @@ input[type=range]::-ms-thumb { .hues-m-controls { height: 54px; } - .hues-m-controls.hidden { + .hues-m-controls.hues-ui--hidden { transform: translateY(54px); } .hues-m-imagename { diff --git a/src/css/hues-r.css b/src/css/huesUI-retro.css similarity index 89% rename from src/css/hues-r.css rename to src/css/huesUI-retro.css index 202007d..ea168d3 100644 --- a/src/css/hues-r.css +++ b/src/css/huesUI-retro.css @@ -6,7 +6,7 @@ .hues-r-container { position: absolute; - bottom: 0px; + bottom: 0; white-space: nowrap; overflow: hidden; width: 100%; @@ -24,7 +24,7 @@ display:flex; align-items:center; position: absolute; - right: 0px; + right: 0; bottom: 10px; font-size: 30px; } @@ -42,7 +42,7 @@ .hues-r-songs { font-size: 13px; - margin: 0px -8px; + margin: 0 -8px; } .hues-r-manualmode, .hues-r-automode { @@ -60,7 +60,7 @@ .hues-r-subcontrols { position: absolute; - right: 0px; + right: 0; bottom: 40px; font-size: 25px; text-align: center; @@ -86,7 +86,7 @@ opacity: 0.5; } -.hues-r-hiderestore.hidden { +.hues-r-hiderestore.hues-ui--hidden { display: block; } @@ -100,7 +100,7 @@ transition: visibility 0.5s linear, opacity 0.5s linear; } -.hues-r-container.hidden, .hues-r-controls.hidden, .hues-r-subcontrols.hidden { +.hues-r-container.hues-ui--hidden, .hues-r-controls.hues-ui--hidden, .hues-r-subcontrols.hues-ui--hidden { visibility: hidden; opacity: 0; } diff --git a/src/css/hues-w.css b/src/css/huesUI-weed.css similarity index 84% rename from src/css/hues-w.css rename to src/css/huesUI-weed.css index 1879dca..cd98c69 100644 --- a/src/css/hues-w.css +++ b/src/css/huesUI-weed.css @@ -8,14 +8,14 @@ display:flex; align-items:center; position: absolute; - right: 0px; - bottom: 0px; + right: 0; + bottom: 0; font-size: 30px; } .hues-w-subcontrols { position: absolute; - right: 0px; + right: 0; bottom: 30px; font-size: 25px; text-align: center; @@ -37,7 +37,7 @@ transition: visibility 0.5s linear, opacity 0.5s linear; } -.hues-w-controls.hidden, .hues-w-subcontrols.hidden, .hues-w-beatbar.hidden { +.hues-w-controls.hues-ui--hidden, .hues-w-subcontrols.hues-ui--hidden, .hues-w-beatbar.hues-ui--hidden { visibility: hidden; opacity: 0; } @@ -70,18 +70,18 @@ text-align:center; font-size: 35px; opacity: 0; - text-shadow: -2px 2px 0px #666; + text-shadow: -2px 2px 0 #666; animation-name: fallspin; animation-timing-function: cubic-bezier(0.215, 0.61, 0.355, 1); animation-duration: 0.5s; } .inverted .hues-w-beataccent{ - text-shadow: -2px 2px 0px #999; + text-shadow: -2px 2px 0 #999; } @keyframes fallspin { - from {transform: rotate(0deg) translate(0px, 0px); + from {transform: rotate(0deg) translate(0, 0); opacity: 1;} } diff --git a/src/css/hues-x.css b/src/css/huesUI-xmas.css similarity index 93% rename from src/css/hues-x.css rename to src/css/huesUI-xmas.css index 4874920..db97854 100644 --- a/src/css/hues-x.css +++ b/src/css/huesUI-xmas.css @@ -7,6 +7,10 @@ border-style: none; } +.hues-x-snow { + z-index: -9; +} + .hues-x-controls > .hues-m-songtitle { bottom: 5px; } @@ -73,23 +77,17 @@ .hues-x-wiresleft, .hues-x-wiresbottom, .hues-x-wiresright { position: absolute; - background-repeat: no-repeat; -} -.hues-x-wiresleft::before, .hues-x-wiresbottom::before, .hues-x-wiresright::before { - content: ''; - position: absolute; - z-index: -1; } .hues-x-wiresleft, .hues-x-wiresright { height: 100%; width: 200px; - top: 0px; + top: 0; overflow: hidden; } .hues-x-wiresleft { - left: 0px; + left: 0; } .hues-x-wiresleft::before { width: 60px; @@ -101,7 +99,7 @@ } .hues-x-wiresright { - right: 0px; + right: 0; } .hues-x-wiresright::before { right: 0; @@ -110,12 +108,12 @@ background-image: url("../img/wiresright.png"); } .inverted .hues-x-wiresright::before { - background-position: 58px 0px; + background-position: -58px 0; } .hues-x-wiresbottomhelper { position: absolute; - bottom: 0px; + bottom: 0; width: 100%; height: 200px; overflow: hidden; @@ -133,10 +131,17 @@ width: 2621px; height: 49px; background-image: url("../img/wiresbottom.png"); - background-position: 127px 49px; + background-position: 127px -49px; } .inverted .hues-x-wiresbottom::before { - background-position: 127px 0px; + background-position: 127px 0; +} + +.hues-x-wiresleft::before, .hues-x-wiresbottom::before, .hues-x-wiresright::before { + content: ''; + position: absolute; + z-index: -1; + background-repeat: no-repeat; } .hues-x-visualisercontainer { diff --git a/src/css/style.css b/src/css/style.css deleted file mode 100644 index e582146..0000000 --- a/src/css/style.css +++ /dev/null @@ -1,377 +0,0 @@ -@font-face { - font-family: 'PetMe64Web'; - font-style: normal; - font-weight: normal; - -webkit-font-smoothing: none; - font-smooth: never; - src: local("PetMe64Web"); - src: local('Pet Me 64'), local('Pet Me 64'), url("../fonts/PetMe64.woff") format('woff'); -} - -@font-face { - font-family: 'icomoon'; - src: url('../fonts/HuesExtra.eot?gmxg3s'); - src: url('../fonts/HuesExtra.eot?gmxg3s#iefix') format('embedded-opentype'), - url('../fonts/HuesExtra.ttf?gmxg3s') format('truetype'), - url('../fonts/HuesExtra.woff?gmxg3s') format('woff'), - url('../fonts/HuesExtra.svg?gmxg3s#icomoon') format('svg'); - font-weight: normal; - font-style: normal; -} - -.hues-icon { - /* use !important to prevent issues with browser extensions that change fonts */ - font-family: 'icomoon' !important; - speak: none; - font-style: normal; - font-weight: normal; - font-variant: normal; - text-transform: none; - line-height: 1; - - /* Better Font Rendering =========== */ - -webkit-font-smoothing: antialiased; - -moz-osx-font-smoothing: grayscale; -} - -html, body { - height: 100%; - margin: 0; padding: 0; - overflow: hidden; - font-family: 'PetMe64Web'; -} - -body { - position: relative; -} - -h1 { - font-size: 15pt; -} - -h2 { - font-size: 10pt; - -} - -h3 { - font-size: 7pt; -} - -h1, h2, h3 { - text-align: center; -} - -#about { - border-bottom: 2px solid black; -} - -#reference { - display:flex; - align-items:center; - justify-content: center; -} - -.info-ref { - float: left; - font-size: 8pt; - text-align: left; - background: rgba(100,100,100,0.3); - border-radius: 15px; - margin: 10px; - padding: 0px 5px; -} - -.info-ref h3 { - font-size: 10pt; - margin: 10px 0px 0px 10px; - text-align: left; -} - -.info-ref ul { - list-style-type: none; - padding: 0px; - margin: 10px; -} - -.info-ref li { - /* Chrome rendering fix */ - line-height: 9pt; -} - -#waifu, #snow { - position: absolute; - display: block; - height: 100%; - padding: 0; -} - -#waifu { - z-index: -10; -} - -#snow { - z-index: -9; -} - -#snow.hidden { - display: none; -} - -#visualiser { - position:absolute; - z-index: -1; -} - -#visualiser.hidden { - display: none; -} - -#preloadHelper { - /* first 2 colours are the loaded colour, next 2 are unloaded */ - background: linear-gradient(to right, #fff 0%,#fff 50%,#ddd 50%,#ddd 100%); - background-size: 200% 100%; - background-position: 100% 0; - - width: 100%; - height: 100%; - display:flex; - justify-content:center; - align-items:center; - flex-direction: column; - font-size: 25pt; - - position: absolute; - top: 0; - left: 0; - z-index: 10; - visibility: visible; - opacity: 1; - transition: visibility 1s linear, opacity 1s linear, background-position 0.5s ease; -} - -#preloadHelper.loaded { - visibility: hidden; - opacity: 0; -} - -#preloader { - display: block; - text-align: center; -} - -#preSub { - font-size: 12pt; -} - -#preSub span { - font-size: 8pt; - opacity: 0.7; -} - -#tabs { - margin: -1px; - padding-top: 22px; - display: flex; -} - -input[type=radio] { - display:none; -} - -.tab-label{ - flex-grow: 1; - cursor: pointer; - padding: 10px; - border: 2px solid black; - text-align: center; -} - -.tab-label.checked { - border-bottom: 0px; -} - -l.tab-label:hover { - background: rgba(255,255,255,0.3); -} - -.tab-content { - display: none; -} - -#tab-resources:checked ~ #tab-resources-content, -#tab-editor:checked ~ #tab-editor-content, -#tab-options:checked ~ #tab-options-content, -#tab-info:checked ~ #tab-info-content { - display: block; -} - -#settingsHelper { - display: flex; - justify-content: center; - align-items: center; - position: absolute; - margin-top: -15px; - width: 100%; - height: 100%; -} - -#settingsWindow { - position: relative; - z-index: 9; - min-width: 640px; - min-height: 470px; - max-height: calc(100% - 50px); - margin: 10px; - - background: rgba(200,200,200, 0.7); - border-color: black; - border-width: 2px; - border-style: solid; -} - -#huesSettings { - padding: 5px; - width: 630px; - display: flex; - flex-wrap: wrap; -} - -#closeButton { - height: 20px; - width: 20px; - font-size: 20px; - color: white; - position: absolute; - right: 0px; - background-color: rgb(128,128,128); - border: 1px solid black; - cursor: pointer; -} - -#closeButton:hover { - background-color: rgb(200,200,200); -} - -.settings-category { - font-size: 16px; - width: 50%; - float: left; - margin-bottom: 10px; -} - -.settings-individual{ - font-size: 8pt; - /* Chrome rendering fix */ - line-height: 9pt; - padding-left: 10px; -} - -.settings-buttons{ - margin: 5px 2px 8px -5px; -} - -.settings-checkbox{ - display: none; -} - -.settings-label { - font-size: 7pt; - margin: 10px 2px; - padding: 3px; - background: rgba(127,127,127, 0.5); - border-color: rgb(0,0,0); - border-width: 1px; - border-style: solid; - cursor: pointer; - /* Don't want double click to select */ - -webkit-touch-callout: none; - -webkit-user-select: none; - -khtml-user-select: none; - -moz-user-select: none; - -ms-user-select: none; - user-select: none; -} - -label.settings-label:hover { - background: rgba(200,200,200,0.5); -} - -.settings-input { - font-family: 'PetMe64Web'; - font-size: 7pt; - padding: 3px; - margin: -6px 0; - background: rgba(127,127,127, 0.5); - border-color: rgb(0,0,0); - border-width: 1px; - border-style: solid; - width: 2em; -} - -#huesSettings input[type="radio"]:checked + label { - background: rgba(255,255,255, 0.5); -} - -.hues-button { - font-size: 10px; - margin: 3px 2px; - padding: 3px; - background-color: rgba(127,127,127, 0.5); - border-color: rgb(0,0,0); - border-width: 1px; - border-style: solid; - cursor: pointer; - /* Don't want double click to select */ - -webkit-touch-callout: none; - -webkit-user-select: none; - -khtml-user-select: none; - -moz-user-select: none; - -ms-user-select: none; - user-select: none; -} - -.hues-button.hidden { - display: none; -} - -.hues-button.loaded { - background-color: rgba(0,127,0,0.5); - cursor: default; -} - -.hues-button.disabled { - color: #777; - cursor: default; -} - -.hues-button:hover { - background: rgba(255,255,255, 0.5); -} - -.hues-button.loaded:hover { - background-color: rgba(0,127,0,0.5); - cursor: default; -} - -.hues-button.disabled:hover { - background-color: rgba(127,127,127, 0.5); -} - -.hues-button.glow { - animation-name: glow; - animation-duration: 2s; - animation-iteration-count: infinite; -} - -@keyframes glow { - from { - background-color: rgba(127,127,127, 0.5); - } - 50% { - background-color: rgba(200,220,200, 0.5); - } - to { - background-color: rgba(127,127,127, 0.5); - } -} \ No newline at end of file diff --git a/src/js/HuesCanvas.js b/src/js/HuesCanvas.js index 81513f8..1b433b6 100644 --- a/src/js/HuesCanvas.js +++ b/src/js/HuesCanvas.js @@ -23,9 +23,9 @@ (function(window, document) { "use strict"; -/* Takes an element name to attach to, and an audio context element for +/* Takes root element to attach to, and an audio context element for getting the current time with reasonable accuracy */ -function HuesCanvas(element, audioContext, core) { +function HuesCanvas(root, audioContext, core) { this.audio = audioContext; core.addEventListener("newimage", this.setImage.bind(this)); core.addEventListener("newcolour", this.setColour.bind(this)); @@ -79,24 +79,19 @@ function HuesCanvas(element, audioContext, core) { this.setBlurQuality("high"); this.setBlurDecay("fast"); - this.canvas = document.getElementById(element); + this.canvas = document.createElement('canvas'); this.context = this.canvas.getContext("2d"); + this.canvas.width = 1280; + this.canvas.height = 720; + this.canvas.className = "hues-canvas"; + root.appendChild(this.canvas); this.offCanvas = document.createElement('canvas'); this.offContext = this.offCanvas.getContext('2d'); - this.snowCanvas = document.getElementById("snow"); - this.snowContext = this.snowCanvas.getContext("2d"); - window.addEventListener('resize', this.resize.bind(this)); this.resize(); - this.snowing = false; - this.maxSnow = 30; - this.snowAngle = 0; - this.lastSnow = 0; - this.snowflakes = []; - this.animating = true; requestAnimationFrame(this.animationLoop.bind(this)); } @@ -115,12 +110,13 @@ HuesCanvas.prototype.settingsUpdated = function() { }; HuesCanvas.prototype.resize = function() { - // height is constant 720px, we expand width to suit - let ratio = window.innerWidth / window.innerHeight; - this.canvas.width = Math.ceil(720 * ratio); + // height is max 720px, we expand width to suit + let height = this.core.root.clientHeight; + let ratio = this.core.root.clientWidth / height; + this.canvas.height = Math.min(height, 720); + this.canvas.width = Math.ceil(this.canvas.height * ratio); this.offCanvas.height = this.canvas.height; this.offCanvas.width = this.canvas.width; - this.snowCanvas.width = Math.ceil(720 * ratio); this.trippyRadius = Math.max(this.canvas.width, this.canvas.height) / 2; this.needsRedraw = true; }; @@ -129,6 +125,7 @@ HuesCanvas.prototype.redraw = function() { let offset; // for centering/right/left align let bOpacity; let width = this.canvas.width; + let height = this.canvas.height; let cTime = this.audio.currentTime; // white BG for the hard light filter @@ -139,33 +136,35 @@ HuesCanvas.prototype.redraw = function() { bOpacity = (cTime - this.blackoutStart)*10; if(bOpacity > 1) { // optimise the draw this.context.fillStyle = this.blackoutColour; - this.context.fillRect(0,0,width,720); + this.context.fillRect(0,0,width,height); this.needsRedraw = false; this.drawInvert(); return; } } else { this.context.fillStyle = "#FFF"; - this.context.fillRect(0,0,width,720); + this.context.fillRect(0,0,width,height); } if(this.image && (this.image.bitmap || this.image.bitmaps)) { let bitmap = this.image.animated ? this.image.bitmaps[this.animFrame] : this.image.bitmap; + let drawHeight = bitmap.height * (height / bitmap.height); + let drawWidth = (bitmap.width / bitmap.height) * drawHeight; if(this.smartAlign) { switch(this.image.align) { case "left": offset = 0; break; case "right": - offset = width - bitmap.width; + offset = width - drawWidth; break; default: - offset = width/2 - bitmap.width/2; + offset = width/2 - drawWidth/2; break; } } else { - offset = width/2 - bitmap.width/2; + offset = width/2 - drawWidth/2; } if(this.xBlur || this.yBlur) { this.context.globalAlpha = this.blurAlpha; @@ -174,25 +173,25 @@ HuesCanvas.prototype.redraw = function() { if(this.blurIterations < 0) { this.context.globalAlpha = 1; this.context.drawImage(bitmap, Math.floor(offset - this.blurDistance/2), 0, - bitmap.width + this.blurDistance, bitmap.height); + drawWidth + this.blurDistance, drawHeight); } else { for(let i=-1; i<=1; i+= this.blurDelta) { - this.context.drawImage(bitmap, Math.floor(this.blurDistance * i) + offset, 0); + this.context.drawImage(bitmap, Math.floor(this.blurDistance * i) + offset, 0, drawWidth, drawHeight); } } } else if(this.yBlur) { if(this.blurIterations < 0) { this.context.globalAlpha = 1; this.context.drawImage(bitmap, offset, Math.floor(-this.blurDistance/2), - bitmap.width, bitmap.height + this.blurDistance); + drawWidth, drawHeight + this.blurDistance); } else { for(let i=-1; i<=1; i+= this.blurDelta) { - this.context.drawImage(bitmap, offset, Math.floor(this.blurDistance * i)); + this.context.drawImage(bitmap, offset, Math.floor(this.blurDistance * i), drawWidth, drawHeight); } } } else { this.context.globalAlpha = 1; - this.context.drawImage(bitmap, offset, 0); + this.context.drawImage(bitmap, offset, 0, drawWidth, drawHeight); } } @@ -204,7 +203,7 @@ HuesCanvas.prototype.redraw = function() { let invertC = this.intToHex(0xFFFFFF ^ this.colour); let normalC = this.intToHex(this.colour); this.offContext.fillStyle = baseInvert ? invertC : normalC; - this.offContext.fillRect(0,0,width,720); + this.offContext.fillRect(0,0,width,height); // sort high to low this.trippyRadii.sort(function(a,b) { @@ -219,14 +218,14 @@ HuesCanvas.prototype.redraw = function() { // Invert for each subsequent draw this.offContext.beginPath(); this.offContext.fillStyle = this.intToHex(invert ? invertC : normalC); - this.offContext.arc(width/2, this.canvas.height/2, this.trippyRadii[i], 0, 2 * Math.PI, false); + this.offContext.arc(width/2, height/2, this.trippyRadii[i], 0, 2 * Math.PI, false); this.offContext.fill(); this.offContext.closePath(); invert = !invert; } } else { this.offContext.fillStyle = this.intToHex(this.colour); - this.offContext.fillRect(0,0,width,720); + this.offContext.fillRect(0,0,width,height); } this.context.globalAlpha = 0.7; this.context.globalCompositeOperation = this.blendMode; @@ -234,7 +233,7 @@ HuesCanvas.prototype.redraw = function() { if(this.blackout) { this.context.globalAlpha = bOpacity; this.context.fillStyle = this.blackoutColour; - this.context.fillRect(0,0,width,720); + this.context.fillRect(0,0,width,height); this.needsRedraw = true; } else { this.needsRedraw = false; @@ -247,7 +246,7 @@ HuesCanvas.prototype.drawInvert = function() { this.context.globalAlpha = 1; this.context.globalCompositeOperation = "difference"; this.context.fillStyle = "#FFF"; - this.context.fillRect(0,0,this.canvas.width,720); + this.context.fillRect(0,0,this.canvas.width,this.canvas.height); } }; @@ -329,9 +328,6 @@ HuesCanvas.prototype.animationLoop = function() { } else if(this.needsRedraw){ this.redraw(); } - if(this.snowing) { - this.drawSnow(); - } if(this.animating) { requestAnimationFrame(this.animationLoop.bind(this)); } @@ -503,82 +499,6 @@ HuesCanvas.prototype.setAnimating = function(anim) { this.animating = anim; }; -// From http://thecodeplayer.com/walkthrough/html5-canvas-snow-effect - -HuesCanvas.prototype.startSnow = function() { - this.snowing = true; - this.snowCanvas.style.display = "block"; - let height = this.canvas.height; - let width = this.canvas.width; - this.snowAngle = 0; - this.snowflakes = []; - for(let i = 0; i < this.maxSnow; i++) { - this.snowflakes.push({ - x: Math.random()*width, //x-coordinate - y: Math.random()*height, //y-coordinate - r: Math.random()*4+1, //radius - d: Math.random()*25 //density - }); - } - this.lastSnow = this.audio.currentTime; -}; - -HuesCanvas.prototype.stopSnow = function() { - this.snowing = false; - this.snowCanvas.style.display = "none"; -}; - -HuesCanvas.prototype.drawSnow = function() { - let width = this.snowCanvas.width; - let height = this.snowCanvas.height; - let delta = this.lastSnow - this.audio.currentTime; - this.snowContext.clearRect(0, 0, width, height); - - if(this.invert) { - this.snowContext.fillStyle = "rgba(0, 0, 0, 0.8)"; - } else { - this.snowContext.fillStyle = "rgba(255, 255, 255, 0.8)"; - } - this.snowContext.beginPath(); - for(let i = 0; i < this.maxSnow; i++) { - let flake = this.snowflakes[i]; - this.snowContext.moveTo(flake.x, flake.y); - this.snowContext.arc(flake.x, flake.y, flake.r, 0, Math.PI * 2, true); - } - this.snowContext.fill(); - - this.snowAngle += delta / 6; - for(let i = 0; i < this.maxSnow; i++) { - let flake = this.snowflakes[i]; - //Updating X and Y coordinates - //We will add 1 to the cos function to prevent negative values which will lead flakes to move upwards - //Every particle has its own density which can be used to make the downward movement different for each flake - //Lets make it more random by adding in the radius - flake.y += Math.cos(this.snowAngle + flake.d) + 1 + flake.r / 2; - flake.x += Math.sin(this.snowAngle) * 2; - - //Sending flakes back from the top when it exits - //Lets make it a bit more organic and let flakes enter from the left and right also. - if(flake.x > width + 5 || flake.x < -5 || flake.y > height) { - if(i % 3 > 0) {//66.67% of the flakes - this.snowflakes[i] = {x: Math.random() * width, y: -10, r: flake.r, d: flake.d}; - } - else { - //If the flake is exitting from the right - if(Math.sin(this.snowAngle) > 0) { - //Enter from the left - this.snowflakes[i] = {x: -5, y: Math.random() * height, r: flake.r, d: flake.d}; - } - else { - //Enter from the right - this.snowflakes[i] = {x: width+5, y: Math.random() * height, r: flake.r, d: flake.d}; - } - } - } - } - this.lastSnow = this.audio.currentTime; -}; - window.HuesCanvas = HuesCanvas; })(window, document); \ No newline at end of file diff --git a/src/js/HuesCore.js b/src/js/HuesCore.js index 1def27f..e3192d1 100644 --- a/src/js/HuesCore.js +++ b/src/js/HuesCore.js @@ -25,43 +25,7 @@ (function(window, document) { "use strict"; -// Gets an element by ID. If it doesn't exist, return an empty div so you -// don't litter your code with null checks -window.getElemWithFallback = function(elemName) { - let elem = document.getElementById(elemName); - if(!elem) { - console.log("Couldn't find element '" + elemName + "'. Ignoring."); - elem = document.createElement("div"); - } - return elem; -}; - function HuesCore(defaults) { - // Yes, we do indeed have Javascript - this.preloadMsg = getElemWithFallback("preMain"); - this.preloadSubMsg = getElemWithFallback("preSub"); - this.clearMessage(); - // Bunch-o-initialisers - this.version = "0x16"; - this.beatIndex = 0; - - // How long a beat lasts for in each section - this.buildLength = -1; - this.loopLength = -1; - - this.currentSong = null; - this.currentImage = null; - - this.songIndex = -1; - this.colourIndex = 0x3f; - this.imageIndex = -1; - - this.isFullAuto = true; - this.invert = false; - this.loopCount = 0; - this.doBuildup = true; - this.userInterface = null; - this.eventListeners = { /* callback time(hundredths) * @@ -131,37 +95,96 @@ function HuesCore(defaults) { settingsupdated : [] }; + // Bunch-o-initialisers + this.version = 30; + this.versionStr = (this.version/10).toFixed(1); + this.versionHex = this.version.toString(16); + this.beatIndex = 0; + + // How long a beat lasts for in each section + this.buildLength = -1; + this.loopLength = -1; + + this.currentSong = null; + this.currentImage = null; + this.songIndex = -1; + this.imageIndex = -1; + this.lastSongArray = []; + this.lastImageArray = []; + + this.colourIndex = 0x3f; + this.colours = this.oldColours; + + this.isFullAuto = true; + this.invert = false; + this.loopCount = 0; + this.doBuildup = true; + this.userInterface = null; + this.uiArray = []; + + // What's our root element? + this.root = null;; + if(!defaults.root) { + this.root = document.body; + } else if(typeof defaults.root === "string") { + if(defaults.root && document.getElementById(defaults.root)) { + this.root = document.getElementById(defaults.root); + } else { + this.root = document.body; + } + } else { // been given an element + this.root = defaults.root; + } + this.root.classList.add("hues-root"); + // Special case for full page Hues + if(this.root === document.body) { + document.documentElement.className = "hues-root"; + } + // Yes, we do indeed have Javascript + this.root.innerHTML = ""; + + this.makePreloader(this.root); + window.onerror = (msg, url, line, col, error) => { this.error(msg); // Get more info in console return false; }; - let versionString = "v" + (parseInt(this.version)/10).toFixed(1); - console.log("0x40 Hues " + versionString + " - start your engines!"); - populateHuesInfo(this.version); - this.colours = this.oldColours; - this.uiArray = []; - this.lastSongArray = []; - this.lastImageArray = []; - this.uiArray.push(new RetroUI(), new WeedUI(), new ModernUI(), new XmasUI(), new HalloweenUI()); - this.settings = new HuesSettings(defaults); - zip.workerScriptsPath = this.settings.defaults.workersPath; - this.resourceManager = new Resources(this); - this.editor = new HuesEditor(this); + // Update with merged defaults + defaults = this.settings.defaults; + zip.workerScriptsPath = defaults.workersPath; + + this.window = new HuesWindow(this.root, defaults); + + console.log("0x40 Hues v" + this.versionStr + " - start your engines!"); + + this.resourceManager = new Resources(this, this.window); + this.editor = new HuesEditor(this, this.window); + this.settings.initUI(this.window); + populateHuesInfo(this.versionStr, this.window, defaults); + + this.window.selectTab(defaults.firstWindow, true); + + let ui = document.createElement("div"); + ui.className = "hues-ui"; + this.root.appendChild(ui); + this.uiArray.push(new RetroUI(ui), new WeedUI(ui), new ModernUI(ui), + new XmasUI(ui), new HalloweenUI(ui), new MinimalUI(ui)); this.autoSong = localStorage["autoSong"]; this.visualiser = document.createElement("canvas"); - this.visualiser.id = "visualiser"; + this.visualiser.className = "hues-visualiser"; this.visualiser.height = "64"; this.vCtx = this.visualiser.getContext("2d"); - - let preloadHelper = getElemWithFallback("preloadHelper"); this.soundManager = new SoundManager(this); + this.settings.addEventListener("updated", this.settingsUpdated.bind(this)); + this.settingsUpdated(); + this.soundManager.init().then(() => { if(!this.soundManager.locked && localStorage["skipPreloader"] == "on") { return null; @@ -195,26 +218,23 @@ function HuesCore(defaults) { }).then(() => { this.clearMessage(); setInterval(this.loopCheck.bind(this), 1000); - this.renderer = new HuesCanvas("waifu", this.soundManager.context, this); - this.settings.connectCore(this); - // Update with merged - defaults = this.settings.defaults; + this.renderer = new HuesCanvas(this.root, this.soundManager.context, this); this.setColour(this.colourIndex); this.animationLoop(); if(defaults.load) { return this.resourceManager.addAll(defaults.respacks, progress => { - preloadHelper.style.backgroundPosition = (100 - progress*100) + "% 0%"; + this.preloader.style.backgroundPosition = (100 - progress*100) + "% 0%"; let scale = Math.floor(progress * defaults.preloadMax); let padding = defaults.preloadMax.toString(defaults.preloadBase).length; this.preloadMsg.textContent = defaults.preloadPrefix + (Array(padding).join("0")+scale.toString(defaults.preloadBase)).slice(-padding); }); } else { - preloadHelper.style.display = "none"; + this.preloader.style.display = "none"; return; } }).then(() => { - preloadHelper.classList.add("loaded"); + this.preloader.classList.add("hues-preloader--loaded"); if(defaults.firstImage) { this.setImageByName(defaults.firstImage); } else { @@ -231,23 +251,25 @@ function HuesCore(defaults) { this.error(error); }); - document.addEventListener("keydown", e => { - e = e || window.event; - if(e.defaultPrevented) { - return true; - } - // Ignore modifiers so we don't steal other events - if (e.altKey || e.ctrlKey || e.metaKey || e.shiftKey) { - return true; - } - // If we've focused a text input, let the input go through! - if((e.target.tagName.toLowerCase() == "input" && e.target.type == "text") || - e.target.contentEditable === "true") { - return true; - } - let key = e.keyCode || e.which; - return this.keyHandler(key); - }); + if(!defaults.disableKeyboard) { + document.addEventListener("keydown", e => { + e = e || window.event; + if(e.defaultPrevented) { + return true; + } + // Ignore modifiers so we don't steal other events + if (e.altKey || e.ctrlKey || e.metaKey || e.shiftKey) { + return true; + } + // If we've focused a text input, let the input go through! + if((e.target.tagName.toLowerCase() == "input" && e.target.type == "text") || + e.target.contentEditable === "true") { + return true; + } + let key = e.keyCode || e.which; + return this.keyHandler(key); + }); + } } HuesCore.prototype.callEventListeners = function(ev) { @@ -277,6 +299,21 @@ HuesCore.prototype.removeEventListener = function(ev, callback) { } }; +HuesCore.prototype.makePreloader = function(root) { + this.preloader = document.createElement("div"); + this.preloader.className = "hues-preloader"; + root.appendChild(this.preloader); + + this.preloadMsg = document.createElement("div"); + this.preloadMsg.className = "hues-preloader__text"; + this.preloadMsg.textContent = "Initialising..."; + this.preloader.appendChild(this.preloadMsg); + + this.preloadSubMsg = document.createElement("div"); + this.preloadSubMsg.className = "hues-preloader__subtext"; + this.preloader.appendChild(this.preloadSubMsg); +}; + HuesCore.prototype.resizeVisualiser = function() { this.soundManager.initVisualiser(this.visualiser.width/2); }; @@ -829,6 +866,9 @@ HuesCore.prototype.settingsUpdated = function() { case "hlwn": this.changeUI(4); break; + case "mini": + this.changeUI(5); + break; } switch (localStorage["colourSet"]) { case "normal": @@ -853,10 +893,10 @@ HuesCore.prototype.settingsUpdated = function() { } switch (localStorage["visualiser"]) { case "off": - document.getElementById("visualiser").className = "hidden"; + this.visualiser.classList.add("hidden"); break; case "on": - document.getElementById("visualiser").className = ""; + this.visualiser.classList.remove("hidden"); if(!this.soundManager.vReady) { this.soundManager.initVisualiser(this.visualiser.width/2); } @@ -878,12 +918,12 @@ HuesCore.prototype.hideLists = function() { }; HuesCore.prototype.toggleSongList = function() { - this.settings.hide(); + this.window.hide(); this.resourceManager.toggleSongList(); }; HuesCore.prototype.toggleImageList = function() { - this.settings.hide(); + this.window.hide(); this.resourceManager.toggleImageList(); }; @@ -933,16 +973,16 @@ HuesCore.prototype.keyHandler = function(key) { this.userInterface.toggleHide(); break; case 82: // R - this.settings.showRespacks(); + this.window.selectTab("RESOURCES"); break; case 69: // E - this.settings.showEditor(); + this.window.selectTab("EDITOR"); break; case 79: // O - this.settings.showOptions(); + this.window.selectTab("OPTIONS"); break; case 73: // I - this.settings.showInfo(); + this.window.selectTab("INFO"); break; case 49: // NUMBER_1 this.settings.set("currentUI", "retro"); @@ -959,6 +999,9 @@ HuesCore.prototype.keyHandler = function(key) { case 53: // NUMBER_5 this.settings.set("currentUI", "hlwn"); break; + case 54: // NUMBER_6 + this.settings.set("currentUI", "mini"); + break; case 67: // C this.toggleImageList(); break; @@ -966,7 +1009,7 @@ HuesCore.prototype.keyHandler = function(key) { this.toggleSongList(); break; case 87: // W - this.settings.toggle(); + this.window.toggle(); break; case 78: // N this.randomSong(); diff --git a/src/js/HuesEditor.js b/src/js/HuesEditor.js index f2eee06..3e460ac 100644 --- a/src/js/HuesEditor.js +++ b/src/js/HuesEditor.js @@ -25,7 +25,7 @@ let WAVE_PIXELS_PER_SECOND = 100; let WAVE_HEIGHT_PIXELS = 20; -function HuesEditor(core) { +function HuesEditor(core, huesWin) { this.buildEditSize = 80; // pixels, including header this.buildEdit = null; this.loopEdit = null; @@ -54,20 +54,19 @@ function HuesEditor(core) { this.linked = false; this.core = core; - this.root = document.getElementById("huesEditor"); - if(!this.root) { - return; - } - if(!core.settings.defaults.noUI) { + if(core.settings.defaults.enableWindow) { this.initUI(); core.addEventListener("beat", this.onBeat.bind(this)); core.addEventListener("newsong", this.onNewSong.bind(this)); + huesWin.addTab("EDITOR", this.root); } } HuesEditor.prototype.initUI = function() { + this.root = document.createElement("div"); + this.root.className = "editor"; let titleButtons = document.createElement("div"); - titleButtons.id = "edit-titlebuttons"; + titleButtons.className = "editor__title-buttons"; this.root.appendChild(titleButtons); this.saveBtn = this.createButton("Save XML", titleButtons, true); this.saveBtn.addEventListener("click", this.saveXML.bind(this)); @@ -84,11 +83,11 @@ HuesEditor.prototype.initUI = function() { }); this.statusMsg = document.createElement("span"); - this.statusMsg.id = "edit-status-msg"; + this.statusMsg.className = "editor__status-msg"; titleButtons.appendChild(this.statusMsg); this.topBar = document.createElement("div"); - this.topBar.id = "edit-topbar"; + this.topBar.className = "editor__top-bar"; this.root.appendChild(this.topBar); this.uiCreateInfo(); @@ -121,14 +120,12 @@ HuesEditor.prototype.initUI = function() { window.addEventListener('resize', this.resize.bind(this)); // Fix Chrome rendering - redraw on tab load - document.getElementById("tab-editor").addEventListener("change", this.resize.bind(this)); + // tabselected passes the name of the selected tab, we force noHilightCalc to false + this.core.window.addEventListener("tabselected", this.resize.bind(this, false)); this.resize(); }; HuesEditor.prototype.resize = function(noHilightCalc) { - // If we were never instantiated but called externally - if(!this.root) - return; this.root.style.height = (window.innerHeight - 200) + "px"; let boxHeight = this.editArea.offsetHeight; let bHeadHeight = this.buildEdit._header.offsetHeight; @@ -191,12 +188,13 @@ HuesEditor.prototype.onNewSong = function(song) { // Clear beat hilight this.buildEdit._hilight.innerHTML = "█"; this.loopEdit._hilight.innerHTML = "█"; - this.buildEdit._hilight.className = "beat-hilight hidden"; - this.loopEdit._hilight.className = "beat-hilight hidden"; - // Clear waveform - this.buildWave = null; - this.loopWave = null; + this.buildEdit._hilight.className = "beat-hilight invisible"; + this.loopEdit._hilight.className = "beat-hilight invisible"; + // Clear the waveform + this.waveContext.clearRect(0, 0, this.waveCanvas.width, WAVE_HEIGHT_PIXELS); } + } else if(song == this.song) { // went to another song then came back + this.linked = true; } }; @@ -208,11 +206,11 @@ HuesEditor.prototype.onBeat = function(map, index) { if(index < 0) { index += this.core.currentSong.buildupRhythm.length; editor = this.buildEdit; - this.loopEdit._hilight.className = "beat-hilight hidden"; + this.loopEdit._hilight.className = "beat-hilight invisible"; } else { editor = this.loopEdit; if(this.song.buildup) { - this.buildEdit._hilight.className = "beat-hilight hidden"; + this.buildEdit._hilight.className = "beat-hilight invisible"; } } editor._hilight.className = "beat-hilight"; @@ -244,32 +242,34 @@ HuesEditor.prototype.reflow = function(editor, map) { HuesEditor.prototype.updateInfo = function() { // Avoid a bunch of nested elses - this.seekStart.classList.add("disabled"); - this.seekLoop.classList.add("disabled"); - this.saveBtn.classList.add("disabled"); - this.copyBtn.classList.add("disabled"); - this.buildEdit._removeBtn.classList.add("disabled"); - this.loopEdit._removeBtn.classList.add("disabled"); - - if(this.song) { - this.saveBtn.classList.remove("disabled"); - this.copyBtn.classList.remove("disabled"); - - if(this.song.independentBuild) { - this.timeLock._locker.innerHTML = ""; - this.timeLock.classList.add("unlocked"); - } else { - this.timeLock._locker.innerHTML = ""; - this.timeLock.classList.remove("unlocked"); - } - if(this.song.sound) { - this.seekLoop.classList.remove("disabled"); - this.loopEdit._removeBtn.classList.remove("disabled"); - } - if(this.song.buildup) { - this.seekStart.classList.remove("disabled"); - this.buildEdit._removeBtn.classList.remove("disabled"); - } + this.seekStart.classList.add("hues-button--disabled"); + this.seekLoop.classList.add("hues-button--disabled"); + this.saveBtn.classList.add("hues-button--disabled"); + this.copyBtn.classList.add("hues-button--disabled"); + this.buildEdit._removeBtn.classList.add("hues-button--disabled"); + this.loopEdit._removeBtn.classList.add("hues-button--disabled"); + + if(!this.song) { + return; + } + + this.saveBtn.classList.remove("hues-button--disabled"); + this.copyBtn.classList.remove("hues-button--disabled"); + + if(this.song.independentBuild) { + this.timeLock._locker.innerHTML = ""; + this.timeLock.classList.add("unlocked"); + } else { + this.timeLock._locker.innerHTML = ""; + this.timeLock.classList.remove("unlocked"); + } + if(this.song.sound) { + this.seekLoop.classList.remove("hues-button--disabled"); + this.loopEdit._removeBtn.classList.remove("hues-button--disabled"); + } + if(this.song.buildup) { + this.seekStart.classList.remove("hues-button--disabled"); + this.buildEdit._removeBtn.classList.remove("hues-button--disabled"); } if(!this.linked) { @@ -404,12 +404,12 @@ HuesEditor.prototype.newSong = function(song) { this.core.setSongOject(song); } // Clear instructions - this.buildEdit._hilight.className = "beat-hilight hidden"; - this.loopEdit._hilight.className = "beat-hilight hidden"; + this.buildEdit._hilight.className = "beat-hilight invisible"; + this.loopEdit._hilight.className = "beat-hilight invisible"; // Clear helpful glows - this.newSongBtn.classList.remove("glow"); - this.fromSongBtn.classList.remove("glow"); + this.newSongBtn.classList.remove("hues-button--glow"); + this.fromSongBtn.classList.remove("hues-button--glow"); // Enable title edits this.title.disabled = false; @@ -551,14 +551,14 @@ HuesEditor.prototype.clearUndoRedo = function() { }; HuesEditor.prototype.updateUndoUI = function() { - this.undoBtn.className = "hues-button disabled"; - this.redoBtn.className = "hues-button disabled"; + this.undoBtn.className = "hues-button hues-button--disabled"; + this.redoBtn.className = "hues-button hues-button--disabled"; if(this.undoBuffer.length > 0) { - this.undoBtn.classList.remove("disabled"); + this.undoBtn.classList.remove("hues-button--disabled"); } if(this.redoBuffer.length > 0) { - this.redoBtn.classList.remove("disabled"); + this.redoBtn.classList.remove("hues-button--disabled"); } }; @@ -599,8 +599,8 @@ HuesEditor.prototype.doubleBeats = function(editor) { }; HuesEditor.prototype.updateHalveDoubleButtons = function(editor) { - editor._halveBtn.className = "hues-button disabled"; - editor._doubleBtn.className = "hues-button disabled"; + editor._halveBtn.className = "hues-button hues-button--disabled"; + editor._doubleBtn.className = "hues-button hues-button--disabled"; if(!editor._locked) { let txtLen = this.getText(editor).length; @@ -617,19 +617,17 @@ HuesEditor.prototype.updateHalveDoubleButtons = function(editor) { } }; -HuesEditor.prototype.createTextInput = function(label, id, subtitle, parent) { +HuesEditor.prototype.createTextInput = function(label, subtitle, parent) { let div = document.createElement("div"); - div.className = "edit-label"; + div.className = "editor__label"; let caption = document.createElement("label"); caption.innerHTML = label; - caption.htmlFor = id; div.appendChild(caption); let container = document.createElement("span"); - container.className = "edit-textbox-container"; + container.className = "editor__textinput-container"; let input = document.createElement("input"); - input.className = "edit-textbox"; + input.className = "editor__textinput"; input.type = "text"; - input.id = id; input.value = subtitle; container.appendChild(input); div.appendChild(container); @@ -643,14 +641,14 @@ HuesEditor.prototype.createButton = function(label, parent, disabled, extraClass let button = document.createElement("span"); button.className = "hues-button"; if(disabled) { - button.className += " disabled"; + button.classList.add("hues-button--disabled"); } if(extraClass) { button.className += " " + extraClass; } // Automagically make disabled buttons ignore clicks button.addEventListener("click", event => { - if(button.classList.contains("disabled")) { + if(button.classList.contains("hues-button--disabled")) { event.preventDefault(); event.stopPropagation(); event.stopImmediatePropagation(); @@ -667,7 +665,7 @@ HuesEditor.prototype.createButton = function(label, parent, disabled, extraClass HuesEditor.prototype.uiCreateInfo = function() { let info = document.createElement("div"); this.topBar.appendChild(info); - info.id = "edit-info"; + info.className = "editor__info"; let songUpdate = function(name) { if(!this.song ) { @@ -680,10 +678,10 @@ HuesEditor.prototype.uiCreateInfo = function() { this.core.callEventListeners("newsong", this.song); }; - this.title = this.createTextInput("Title:", "edit-title", "Song name", info); + this.title = this.createTextInput("Title:", "Song name", info); this.title.oninput = songUpdate.bind(this, "title"); this.title.disabled = true; - this.source = this.createTextInput("Link: ", "edit-source", "Source link", info); + this.source = this.createTextInput("Link: ", "Source link", info); this.source.oninput = songUpdate.bind(this, "source"); this.source.disabled = true; }; @@ -691,16 +689,16 @@ HuesEditor.prototype.uiCreateInfo = function() { HuesEditor.prototype.uiCreateImport = function() { let imports = document.createElement("div"); this.topBar.appendChild(imports); - imports.id = "edit-imports"; + imports.className = "editor__imports"; let songEdits = document.createElement("div"); imports.appendChild(songEdits); - let newSongBtn = this.createButton("New song", songEdits, false, "glow"); + let newSongBtn = this.createButton("New song", songEdits, false, "hues-button--glow"); newSongBtn.addEventListener("click", () => { this.newSong(); }); this.newSongBtn = newSongBtn; - let fromSong = this.createButton("Edit current song", songEdits, false, "glow"); + let fromSong = this.createButton("Edit current song", songEdits, false, "hues-button--glow"); fromSong.addEventListener("click", () => { if(this.core.currentSong) { this.newSong(this.core.currentSong); @@ -709,8 +707,7 @@ HuesEditor.prototype.uiCreateImport = function() { this.fromSongBtn = fromSong; let songInfos = document.createElement("div"); - songInfos.className = "settings-individual"; - songInfos.id = "edit-songstats"; + songInfos.className = "settings-individual editor__song-stats"; imports.appendChild(songInfos); this.loopLen = this.uiCreateSongStat("Loop length (s):", "0.00", songInfos); @@ -726,7 +723,7 @@ HuesEditor.prototype.uiCreateSongStat = function(name, value, parent) { container.appendChild(label); let valueSpan = document.createElement("span"); valueSpan.textContent = value; - valueSpan.className = "edit-songstat-value"; + valueSpan.className = "editor__song-stats__value"; container.appendChild(valueSpan); return valueSpan; }; @@ -734,14 +731,13 @@ HuesEditor.prototype.uiCreateSongStat = function(name, value, parent) { HuesEditor.prototype.uiCreateEditArea = function() { let editArea = document.createElement("div"); this.editArea = editArea; - editArea.id = "edit-area"; + editArea.className = "edit-area"; this.root.appendChild(editArea); // Lock build/loop lengths this.timeLock = document.createElement("div"); editArea.appendChild(this.timeLock); - this.timeLock.id = "edit-timelock"; - this.timeLock.className = "hues-icon unlocked"; + this.timeLock.className = "hues-icon edit-area__timelock edit-area__timelock--unlocked"; // CHAIN-BROKEN, use  for CHAIN let locker = this.createButton("", this.timeLock); locker.addEventListener("click", () => { @@ -753,7 +749,7 @@ HuesEditor.prototype.uiCreateEditArea = function() { }); this.timeLock._locker = locker; - this.buildEdit = this.uiCreateSingleEditor("Buildup", "buildup", "buildupRhythm", "edit-build", editArea); + this.buildEdit = this.uiCreateSingleEditor("Buildup", "buildup", "buildupRhythm", editArea); this.seekStart = this.buildEdit._seek; // FIRST |<< this.seekStart.innerHTML = ""; @@ -763,11 +759,10 @@ HuesEditor.prototype.uiCreateEditArea = function() { // drag handle let handleContainer = document.createElement("div"); - handleContainer.id = "edit-resize-handle-container"; + handleContainer.className = "resize-handle"; editArea.appendChild(handleContainer); let handle = document.createElement("div"); - handle.id = 'edit-resize-handle'; - handle.className = 'hues-icon'; + handle.className = 'hues-icon resize-handle__handle'; handle.innerHTML = ""; // DRAG HANDLE handleContainer.appendChild(handle); this.resizeHandle = handleContainer; @@ -791,7 +786,7 @@ HuesEditor.prototype.uiCreateEditArea = function() { document.addEventListener("mouseup", mouseup); }); - this.loopEdit = this.uiCreateSingleEditor("Rhythm ", "sound", "rhythm", "edit-loop", editArea); + this.loopEdit = this.uiCreateSingleEditor("Rhythm ", "sound", "rhythm", editArea); this.seekLoop = this.loopEdit._seek; // FIRST |<< this.seekLoop.innerHTML = ""; @@ -818,13 +813,12 @@ HuesEditor.prototype.uiCreateEditArea = function() { 'inclusion into a Resource Pack!'; }; -HuesEditor.prototype.uiCreateSingleEditor = function(title, soundName, rhythmName, id, parent) { +HuesEditor.prototype.uiCreateSingleEditor = function(title, soundName, rhythmName, parent) { let container = document.createElement("div"); - container.id = id; parent.appendChild(container); let header = document.createElement("div"); - header.className = "edit-area-header"; + header.className = "edit-area__header"; container.appendChild(header); let nameLabel = document.createElement("span"); @@ -837,7 +831,7 @@ HuesEditor.prototype.uiCreateSingleEditor = function(title, soundName, rhythmNam let beatCount = document.createElement("span"); header.appendChild(beatCount); - beatCount.className = "beat-count"; + beatCount.className = "edit-area__beat-count"; beatCount.textContent = "0 beats"; container._lockedBtn = this.createButton("", header, false, "hues-icon"); container._lockedBtn.addEventListener("click", () => { @@ -850,7 +844,7 @@ HuesEditor.prototype.uiCreateSingleEditor = function(title, soundName, rhythmNam }); let rightHeader = document.createElement("span"); - rightHeader.className = "edit-area-right-header"; + rightHeader.className = "edit-area__header__right"; header.appendChild(rightHeader); container._halveBtn = this.createButton("Halve", rightHeader, true); @@ -870,9 +864,9 @@ HuesEditor.prototype.uiCreateSingleEditor = function(title, soundName, rhythmNam container._removeBtn.addEventListener("click", this.removeAudio.bind(this, container)); let editBox = document.createElement("div"); - editBox.className = "edit-box"; + editBox.className = "edit-area__box"; let beatmap = document.createElement("div"); - beatmap.className = "beatmap"; + beatmap.className = "edit-area__beatmap"; beatmap.contentEditable = true; beatmap.spellcheck = false; beatmap.oninput = this.textUpdated.bind(this, container); @@ -903,7 +897,7 @@ HuesEditor.prototype.uiCreateSingleEditor = function(title, soundName, rhythmNam HuesEditor.prototype.uiCreateControls = function() { let controls = document.createElement("div"); - controls.id = "edit-controls"; + controls.className = "edit__controls"; this.root.appendChild(controls); let changeRate = function(change) { @@ -957,9 +951,8 @@ HuesEditor.prototype.uiCreateControls = function() { }; HuesEditor.prototype.uiCreateVisualiser = function() { - // TODO placeholder let wave = document.createElement("canvas"); - wave.id = "edit-waveform"; + wave.className = "waveform"; wave.height = WAVE_HEIGHT_PIXELS; this.root.appendChild(wave); this.waveCanvas = wave; @@ -1237,7 +1230,7 @@ HuesEditor.prototype.renderWave = function(buffer, length) { }; HuesEditor.prototype.drawWave = function() { - if(!this.buildWave && !this.loopWave) + if((!this.buildWave && !this.loopWave) || !this.linked) return; let width = this.waveCanvas.width; @@ -1263,7 +1256,11 @@ HuesEditor.prototype.drawWave = function() { if(this.buildWave && bLen && minTime < 0) { // Bit of legwork to convert negative to positive let waveOffset = Math.floor((1 - drawTime / -bLen) * (this.buildWave.width-1)); - drawOffset = this.drawOneWave(this.buildWave, waveOffset, drawOffset, width); + try { + drawOffset = this.drawOneWave(this.buildWave, waveOffset, drawOffset, width); + } catch (err) { + console.log(this.waveCanvas); + } // If there's more to draw after the build, it'll be from the start of the wave drawTime = 0; } @@ -1314,11 +1311,11 @@ HuesEditor.prototype.confirmLeave = function() { }; HuesEditor.prototype.alert = function(msg) { - this.statusMsg.classList.remove("fade"); + this.statusMsg.classList.remove("editor__status-msg--fade"); this.statusMsg.textContent = msg; // Trigger a reflow and thus restart the animation var useless = this.statusMsg.offsetWidth; - this.statusMsg.classList.add("fade"); + this.statusMsg.classList.add("editor__status-msg--fade"); }; HuesEditor.prototype.generateXML = function() { @@ -1378,7 +1375,7 @@ HuesEditor.prototype.copyXML = function() { } let textArea = document.createElement("textarea"); - textArea.id = "edit-copybox"; + textArea.className = "copybox"; textArea.value = text; diff --git a/src/js/HuesInfo.js b/src/js/HuesInfo.js index 8155050..de3f5a5 100644 --- a/src/js/HuesInfo.js +++ b/src/js/HuesInfo.js @@ -23,18 +23,9 @@ (function(window, document) { "use strict"; - /* HuesInfo.js populates the beat glossary, shortcut list, and version string. - * This means the HTML should rarely need to be updated. - * If the element IDs are not present, the DOM is not modified. If you would - * like a custom info page, simply leave them out. + /* HuesInfo.js populates the INFO tab in the Hues Window. */ -let huesInfo = { - versionID: "versionText", - referenceID: "reference", - referenceClass: "info-ref" -}; - let beatGlossary = [ "x Vertical blur (snare)", "o Horizontal blur (bass)", @@ -68,31 +59,38 @@ let shortcuts = [ "[1-5] Change UI" ]; -function populateHuesInfo(version) { - let versionInt = parseInt(version); - - let versionElem = document.getElementById(huesInfo.versionID); - if(versionElem) { - versionElem.textContent = "v" + (versionInt/10).toFixed(1); - } - - addInfo("Beat glossary", beatGlossary); - addInfo("Keyboard shortcuts", shortcuts); -} - -let addInfo = function(titleText, list) { - let refElem = document.getElementById(huesInfo.referenceID); - if(!refElem) { +function populateHuesInfo(version, huesWin, defaults) { + if(!defaults.enableWindow) { return; } + let verString = (parseInt(version)/10).toFixed(1); let info = document.createElement("div"); - info.className = huesInfo.referenceClass; - refElem.appendChild(info); + info.className = "hues-ref"; + + let huesName = defaults.huesName.replace("%VERSION%", version); + let about = document.createElement("div"); + about.className = "hues-about"; + about.innerHTML = "

" + huesName + "

" + + '

Adapted from the 0x40 Flash

' + + '

Web-ified by mon

' + + '

With help from Kepstin

'; + info.appendChild(about); + + addReference(info, "Beat glossary", beatGlossary); + addReference(info, "Keyboard shortcuts", shortcuts); + + huesWin.addTab("INFO", info); +} + +let addReference = function(root, titleText, list) { + let ref = document.createElement("div"); + ref.className = "hues-ref__info"; + root.appendChild(ref); let title = document.createElement("h3"); title.textContent = titleText; - info.appendChild(title); + ref.appendChild(title); let listElem = document.createElement("ul"); list.forEach(function(elem) { @@ -100,7 +98,7 @@ let addInfo = function(titleText, list) { item.textContent = elem; listElem.appendChild(item); }); - info.appendChild(listElem); + ref.appendChild(listElem); }; window.populateHuesInfo = populateHuesInfo; diff --git a/src/js/HuesSettings.js b/src/js/HuesSettings.js index 715f8d9..f2c74d5 100644 --- a/src/js/HuesSettings.js +++ b/src/js/HuesSettings.js @@ -42,14 +42,22 @@ HuesSettings.prototype.defaultSettings = { firstImage: null, // If set, will disable the remote resources menu. For custom pages. disableRemoteResources: false, - // You will rarely want this. Disables the generated UI elements in the tab box - noUI: false, - // Whether to show the info window on page load - showInfo: false, + // You will rarely want to change this. Enables/disables the Hues Window. + enableWindow: true, + // Whether to show the Hues Window on page load + showWindow: false, + // What tab will be displayed first in the Hues Window + firstWindow: "INFO", // Preloader customisation preloadPrefix: "0x", preloadBase: 16, preloadMax: 0x40, + // Info customisation + huesName: "0x40 Hues of JS, v%VERSION%", + // If unset, uses , otherwise sets which element to turn hues-y + root: null, + // If set, keyboard shortcuts are ignored + disableKeyboard: false, // UI accessible config smartAlign: "on", @@ -83,9 +91,12 @@ HuesSettings.prototype.ephemeralSettings = [ "preloadPrefix", "preloadBase", "preloadMax", - "noUI", - "showInfo", - "workersPath" + "enableWindow", + "firstWindow", + "workersPath", + "huesName", + "root", + "disableKeyboard" ]; // To dynamically build the UI like the cool guy I am @@ -138,7 +149,7 @@ HuesSettings.prototype.settingsOptions = { }, currentUI : { name : "UI style", - options : ["retro", "v4.20", "modern", "xmas", "hlwn"] + options : ["retro", "v4.20", "modern", "xmas", "hlwn", "mini"] }, colourSet : { name : "Colour set", @@ -208,10 +219,17 @@ HuesSettings.prototype.settingsOptions = { }; function HuesSettings(defaults) { - this.core = null; + this.eventListeners = { + /* callback updated() + * + * Called when settings are updated + */ + updated : [] + }; + this.hasUI = false; - this.root = getElemWithFallback("huesSettings"); - this.window = getElemWithFallback("settingsHelper"); + + this.settingCheckboxes = {}; this.textCallbacks = []; this.visCallbacks = []; @@ -236,86 +254,11 @@ function HuesSettings(defaults) { } this.defaults = defaults; - - if(this.defaults.showInfo) { - this.show(); - } else { - this.hide(); - } - - // because we still care about the main window - getElemWithFallback("closeButton").onclick = this.hide.bind(this); - - // we also care about tabs looking nice. - let checkListener = function(check) { - for(let i = 0; i < tabs.length; i++) { - tabs[i].className = "tab-label"; - } - check._label.className = "tab-label checked"; - // Fix the editor from having 0 size - if(check.id == "tab-editor" && this.core && this.core.editor) - this.core.editor.resize(); - }; - let tabs = document.getElementsByClassName("tab-label"); - for(let i = 0; i < tabs.length; i++) { - let check = document.getElementById(tabs[i].htmlFor); - check._label = tabs[i]; - check.addEventListener("change", checkListener.bind(this, check)); - } - if(!this.defaults.noUI) { - this.initUI(); - } } -HuesSettings.prototype.connectCore = function(core) { - this.core = core; - core.settingsUpdated(); -}; - -HuesSettings.prototype.show = function() { - this.window.style.display = "-webkit-flex"; - this.window.style.display = "flex"; - if(this.core) { - this.core.hideLists(); - if(this.core.editor) - this.core.editor.resize(); - } -}; - -HuesSettings.prototype.hide = function() { - this.window.style.display = "none"; -}; - -HuesSettings.prototype.toggle = function() { - if(this.window.style.display == "none") { - this.show(); - } else { - this.hide(); - } -}; - -HuesSettings.prototype.showRespacks = function() { - this.show(); - getElemWithFallback("tab-resources").click(); -}; - -HuesSettings.prototype.showEditor = function() { - this.show(); - getElemWithFallback("tab-editor").click(); -}; - -HuesSettings.prototype.showOptions = function() { - this.show(); - getElemWithFallback("tab-options").click(); -}; - -HuesSettings.prototype.showInfo = function() { - this.show(); - getElemWithFallback("tab-info").click(); -}; - -HuesSettings.prototype.initUI = function() { - let doc = this.root.ownerDocument; +HuesSettings.prototype.initUI = function(huesWin) { + let root = document.createElement("div"); + root.className = "hues-options"; // Don't make in every loop let intValidator = function(self, variable) { @@ -326,55 +269,63 @@ HuesSettings.prototype.initUI = function() { } localStorage[variable] = this.value; self.updateConditionals(); - self.core.settingsUpdated(); + self.callEventListeners("updated"); }; // To order things nicely for(let cat in this.settingsCategories) { if(this.settingsCategories.hasOwnProperty(cat)) { - let catContainer = doc.createElement("div"); + let catContainer = document.createElement("div"); catContainer.textContent = cat; catContainer.className = "settings-category"; let cats = this.settingsCategories[cat]; for(let i = 0; i < cats.length; i++) { let setName = cats[i]; - let setContainer = doc.createElement("div"); + let setContainer = document.createElement("div"); let setting = this.settingsOptions[setName]; setContainer.textContent = setting.name; setContainer.className = "settings-individual"; - let buttonContainer = doc.createElement("div"); + let buttonContainer = document.createElement("div"); buttonContainer.className = "settings-buttons"; for(let j = 0; j < setting.options.length; j++) { let option = setting.options[j]; if(typeof option === "string") { - let checkbox = doc.createElement("input"); + let checkbox = document.createElement("input"); + // Save checkbox so we can update UI stuff + this.settingCheckboxes[setName + "-" + option] = checkbox; checkbox.className = "settings-checkbox"; checkbox.type = "radio"; - checkbox.name = setName; checkbox.value = option; - checkbox.id = setName + "-" + option; + let unique = 0; + // Lets us have multiple hues on 1 page + let id = setName + "-" + option + "-"; + while(document.getElementById(id + unique)) { + unique++; + } + checkbox.name = setName + "-" + unique; + checkbox.id = id + unique; if(localStorage[setName] == option) { checkbox.checked = true; } checkbox.onclick = function(self) { - self.set(this.name, this.value); + self.set(setName, this.value); }.bind(checkbox, this); buttonContainer.appendChild(checkbox); // So we can style this nicely - let label = doc.createElement("label"); + let label = document.createElement("label"); label.className = "settings-label"; label.htmlFor = checkbox.id; label.textContent = option.toUpperCase(); buttonContainer.appendChild(label); } else { // special option if(option.type == "varText") { - let text = doc.createElement("span"); + let text = document.createElement("span"); text.textContent = option.text(); buttonContainer.appendChild(text); this.textCallbacks.push({func:option.text, element:text}); } else if(option.type == "input") { - let input = doc.createElement("input"); + let input = document.createElement("input"); input.setAttribute("type", "text"); input.className = "settings-input"; input.value = localStorage[option.variable]; @@ -395,12 +346,26 @@ HuesSettings.prototype.initUI = function() { setContainer.appendChild(buttonContainer); catContainer.appendChild(setContainer); } - this.root.appendChild(catContainer); + root.appendChild(catContainer); } } + huesWin.addTab("OPTIONS", root); this.hasUI = true; }; +HuesSettings.prototype.get = function(setting) { + if(this.defaults.hasOwnProperty(setting)) { + if(this.ephemeralSettings.indexOf(setting) != -1) { + return this.defaults[setting]; + } else { + return localStorage[setting]; + } + } else { + console.log("WARNING: Attempted to fetch invalid setting:", setting); + return null; + } +}; + // Set a named index to its named value, returns false if name doesn't exist HuesSettings.prototype.set = function(setting, value) { value = value.toLowerCase(); @@ -411,11 +376,11 @@ HuesSettings.prototype.set = function(setting, value) { } // for updating the UI selection try { - document.getElementById(setting + "-" + value).checked = true; + this.settingCheckboxes[setting + "-" + value].checked = true; } catch(e) {} localStorage[setting] = value; this.updateConditionals(); - this.core.settingsUpdated(); + this.callEventListeners("updated"); return true; }; @@ -444,6 +409,33 @@ HuesSettings.prototype.setDefaults = function() { } }; +HuesSettings.prototype.callEventListeners = function(ev) { + let args = Array.prototype.slice.call(arguments, 1); + this.eventListeners[ev].forEach(function(callback) { + callback.apply(null, args); + }); +}; + +HuesSettings.prototype.addEventListener = function(ev, callback) { + ev = ev.toLowerCase(); + if (typeof(this.eventListeners[ev]) !== "undefined") { + this.eventListeners[ev].push(callback); + } else { + throw Error("Unknown event: " + ev); + } +}; + +HuesSettings.prototype.removeEventListener = function(ev, callback) { + ev = ev.toLowerCase(); + if (typeof(this.eventListeners[ev]) !== "undefined") { + this.eventListeners[ev] = this.eventListeners[ev].filter(function(a) { + return (a !== callback); + }); + } else { + throw Error("Unknown event: " + ev); + } +}; + window.HuesSettings = HuesSettings; })(window, document); \ No newline at end of file diff --git a/src/js/HuesUI.js b/src/js/HuesUI.js index b49aecf..25ce809 100644 --- a/src/js/HuesUI.js +++ b/src/js/HuesUI.js @@ -29,7 +29,7 @@ */ function HuesUI(parent, name) { if(!parent) { - parent = document.getElementById("huesUI"); + return; } this.root = document.createElement("div"); this.root.className = name ? name : this.constructor.name; @@ -142,7 +142,7 @@ HuesUI.prototype.initUI = function() { this.settingsToggle.innerHTML = ''; // COG this.settingsToggle.className = 'hues-icon'; this.settingsToggle.onclick = () => { - this.core.settings.toggle(); + this.core.window.toggle(); }; this.hideToggle = document.createElement("div"); @@ -179,6 +179,9 @@ HuesUI.prototype.connectCore = function(core) { }; HuesUI.prototype.disconnect = function() { + this.callbacks.forEach(callback => { + this.core.removeEventListener(callback.name, callback.func); + }); this.core = null; this.root.style.display = "none"; while (this.listContainer.firstElementChild) { @@ -187,10 +190,6 @@ HuesUI.prototype.disconnect = function() { while (this.visualiserContainer.firstElementChild) { this.visualiserContainer.removeChild(this.visualiserContainer.firstElementChild); } - - this.callbacks.forEach(function(callback) { - core.removeEventListener(callback.name, callback.func); - }); window.removeEventListener('resize', this.resizeHandler); }; @@ -207,9 +206,9 @@ HuesUI.prototype.hide = function() { HuesUI.prototype.toggleHide = function() { this.hidden = !this.hidden; if(this.hidden) { - this.root.className = this.constructor.name + " hidden"; + this.root.classList.add("hues-ui--hidden"); } else { - this.root.className = this.constructor.name; + this.root.classList.remove("hues-ui--hidden"); } }; @@ -379,26 +378,25 @@ RetroUI.prototype.initUI = function() { this.addCoreCallback("newmode", this.newMode.bind(this)); }; -RetroUI.prototype.toggleHide = function(stylename) { - stylename = stylename ? stylename : 'r'; +RetroUI.prototype.toggleHide = function() { + this.hidden = !this.hidden; if(this.hidden) { - this.subControls.className = "hues-" + stylename + "-subcontrols"; - this.controls.className = "hues-" + stylename + "-controls"; - this.container.className = "hues-r-container"; - this.hideRestore.className = "hues-r-hiderestore"; + this.subControls.classList.add("hues-ui--hidden"); + this.controls.classList.add("hues-ui--hidden"); + this.container.classList.add("hues-ui--hidden"); + this.hideRestore.classList.add("hues-ui--hidden"); } else { - this.subControls.className = "hues-" + stylename + "-subcontrols hidden"; - this.controls.className = "hues-" + stylename + "-controls hidden"; - this.container.className = "hues-r-container hidden"; - this.hideRestore.className = "hues-r-hiderestore hidden"; + this.subControls.classList.remove("hues-ui--hidden"); + this.controls.classList.remove("hues-ui--hidden"); + this.container.classList.remove("hues-ui--hidden"); + this.hideRestore.classList.remove("hues-ui--hidden"); } - this.hidden = !this.hidden; }; RetroUI.prototype.connectCore = function(core) { HuesUI.prototype.connectCore.call(this, core); - this.version.textContent = "V=$" + core.version; + this.version.textContent = "V=$" + core.versionHex; }; RetroUI.prototype.newMode = function(isAuto) { @@ -431,6 +429,23 @@ RetroUI.prototype.resize = function() { this.core.resizeVisualiser(); }; +function MinimalUI(parent, name) { + RetroUI.call(this, parent, name ? name : "MinimalUI"); +} + +MinimalUI.prototype = Object.create(RetroUI.prototype); +MinimalUI.prototype.constructor = MinimalUI; + +MinimalUI.prototype.initUI = function() { + RetroUI.prototype.initUI.call(this); + + this.root.removeChild(this.controls); + this.root.removeChild(this.subControls); + this.container.removeChild(this.beatBar); + this.container.innerHTML = ""; + this.container.appendChild(this.beatBar); +} + function WeedUI(parent, name) { RetroUI.call(this, parent, name ? name : "WeedUI"); @@ -471,12 +486,12 @@ WeedUI.prototype.initUI = function() { }; WeedUI.prototype.toggleHide = function() { + RetroUI.prototype.toggleHide.call(this); if(this.hidden) { - this.beatBar.className = "hues-w-beatbar"; + this.beatBar.classList.add("hues-ui--hidden"); } else { - this.beatBar.className = "hues-w-beatbar hidden"; + this.beatBar.classList.remove("hues-ui--hidden"); } - RetroUI.prototype.toggleHide.call(this, 'w'); }; WeedUI.prototype.beat = function(beats, index) { @@ -585,7 +600,7 @@ ModernUI.prototype.initUI = function() { this.infoToggle.innerHTML = '?'; this.infoToggle.className = "hues-m-question"; this.infoToggle.onclick = () => { - this.core.settings.showInfo(); + this.core.window.selectTab("INFO"); }; volCluster.appendChild(this.infoToggle); @@ -699,18 +714,18 @@ ModernUI.prototype.initUI = function() { ModernUI.prototype.toggleHide = function() { // classList is new-ish, but if you have web audio you'll have this - this.beatBar.classList.remove("hidden"); - this.beatCenter.classList.remove("hidden"); - this.controls.classList.remove("hidden"); - this.hideRestore.classList.remove("hidden"); + this.beatBar.classList.remove("hues-ui--hidden"); + this.beatCenter.classList.remove("hues-ui--hidden"); + this.controls.classList.remove("hues-ui--hidden"); + this.hideRestore.classList.remove("hues-ui--hidden"); switch(this.hidden) { case 1: - this.beatBar.classList.add("hidden"); - this.beatCenter.classList.add("hidden"); + this.beatBar.classList.add("hues-ui--hidden"); + this.beatCenter.classList.add("hues-ui--hidden"); /* falls through */ case 0: - this.controls.classList.add("hidden"); - this.hideRestore.classList.add("hidden"); + this.controls.classList.add("hues-ui--hidden"); + this.hideRestore.classList.add("hues-ui--hidden"); } this.hidden = (this.hidden+1) % 3; }; @@ -798,6 +813,8 @@ ModernUI.prototype.newImage = function(image) { function XmasUI(parent, name) { ModernUI.call(this, parent, name ? name : "XmasUI"); + this.initSnow(); + // This will cache our inverted lights images this.invert(true); @@ -867,13 +884,23 @@ function XmasUI(parent, name) { XmasUI.prototype = Object.create(ModernUI.prototype); XmasUI.prototype.constructor = XmasUI; +XmasUI.prototype.invert = function(invert) { + HuesUI.prototype.invert.call(this, invert); + + if(invert) { + this.snowContext.fillStyle = "rgba(0, 0, 0, 0.8)"; + } else { + this.snowContext.fillStyle = "rgba(255, 255, 255, 0.8)"; + } +}; + XmasUI.prototype.connectCore = function(core) { HuesUI.prototype.connectCore.call(this, core); - this.core.renderer.startSnow(); + this.startSnow(); }; XmasUI.prototype.disconnect = function() { - this.core.renderer.stopSnow(); + this.stopSnow(); HuesUI.prototype.disconnect.call(this); }; @@ -943,6 +970,107 @@ XmasUI.prototype.beat = function(beats, index) { } }; +XmasUI.prototype.initSnow = function() { + this.snowCanvas = document.createElement("canvas"); + this.snowContext = this.snowCanvas.getContext("2d"); + this.snowCanvas.width = 1280; + this.snowCanvas.height = 720; + this.snowCanvas.style.display = "none"; + this.snowCanvas.className = "hues-canvas hues-x-snow"; + + this.root.appendChild(this.snowCanvas); + + this.snowing = false; + this.maxSnow = 30; + this.snowAngle = 0; + this.lastSnow = 0; + this.snowflakes = []; + + this.addCoreCallback("frame", this.drawSnow.bind(this)); +}; + +// From http://thecodeplayer.com/walkthrough/html5-canvas-snow-effect + +XmasUI.prototype.startSnow = function() { + this.snowing = true; + this.snowCanvas.style.display = "block"; + let height = this.snowCanvas.height; + let width = this.snowCanvas.width; + this.snowAngle = 0; + this.snowflakes = []; + for(let i = 0; i < this.maxSnow; i++) { + this.snowflakes.push({ + x: Math.random()*width, //x-coordinate + y: Math.random()*height, //y-coordinate + r: Math.random()*4+1, //radius + d: Math.random()*25 //density + }); + } + this.lastSnow = Date.now() / 1000; +}; + +XmasUI.prototype.stopSnow = function() { + this.snowing = false; + this.snowCanvas.style.display = "none"; +}; + +XmasUI.prototype.drawSnow = function() { + let width = this.snowCanvas.width; + let height = this.snowCanvas.height; + let now = Date.now() / 1000; + let delta = this.lastSnow - now; + this.lastSnow = now; + this.snowContext.clearRect(0, 0, width, height); + + this.snowContext.beginPath(); + for(let i = 0; i < this.maxSnow; i++) { + let flake = this.snowflakes[i]; + this.snowContext.moveTo(flake.x, flake.y); + this.snowContext.arc(flake.x, flake.y, flake.r, 0, Math.PI * 2, true); + } + this.snowContext.fill(); + + this.snowAngle += delta / 6; + for(let i = 0; i < this.maxSnow; i++) { + let flake = this.snowflakes[i]; + //Updating X and Y coordinates + //We will add 1 to the cos function to prevent negative values which will lead flakes to move upwards + //Every particle has its own density which can be used to make the downward movement different for each flake + //Lets make it more random by adding in the radius + flake.y += Math.cos(this.snowAngle + flake.d) + 1 + flake.r / 2; + flake.x += Math.sin(this.snowAngle) * 2; + + //Sending flakes back from the top when it exits + //Lets make it a bit more organic and let flakes enter from the left and right also. + if(flake.x > width + 5 || flake.x < -5 || flake.y > height) { + if(i % 3 > 0) {//66.67% of the flakes + this.snowflakes[i] = {x: Math.random() * width, y: -10, r: flake.r, d: flake.d}; + } + else { + //If the flake is exitting from the right + if(Math.sin(this.snowAngle) > 0) { + //Enter from the left + this.snowflakes[i] = {x: -5, y: Math.random() * height, r: flake.r, d: flake.d}; + } + else { + //Enter from the right + this.snowflakes[i] = {x: width+5, y: Math.random() * height, r: flake.r, d: flake.d}; + } + } + } + } +}; + +XmasUI.prototype.resize = function() { + ModernUI.prototype.resize.call(this); + + let ratio = window.innerWidth / window.innerHeight; + // cleared on resize + let savedFill = this.snowContext.fillStyle; + this.snowCanvas.width = Math.ceil(720 * ratio); + this.snowContext.fillStyle = savedFill; +}; + XmasUI.prototype.newColour = function(colour) {}; XmasUI.prototype.blurUpdated = function(x, y) {}; XmasUI.prototype.updateTime = function(time) {}; @@ -1038,13 +1166,13 @@ HalloweenUI.prototype.beat = function(beats, index) { HalloweenUI.prototype.connectCore = function(core) { ModernUI.prototype.connectCore.call(this, core); - getElemWithFallback("preloadHelper").classList.add("hues-h-text"); + this.core.preloader.classList.add("hues-h-text"); }; HalloweenUI.prototype.disconnect = function() { - ModernUI.prototype.disconnect.call(this, core); + this.core.preloader.classList.remove("hues-h-text"); - getElemWithFallback("preloadHelper").classList.remove("hues-h-text"); + ModernUI.prototype.disconnect.call(this); }; // Positions and angles for the Xmas lights @@ -1129,5 +1257,6 @@ window.WeedUI = WeedUI; window.ModernUI = ModernUI; window.XmasUI = XmasUI; window.HalloweenUI = HalloweenUI; +window.MinimalUI = MinimalUI; })(window, document); \ No newline at end of file diff --git a/src/js/HuesWindow.js b/src/js/HuesWindow.js new file mode 100644 index 0000000..ad035b4 --- /dev/null +++ b/src/js/HuesWindow.js @@ -0,0 +1,171 @@ +/* Copyright (c) 2015 William Toohey + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +(function(window, document) { +"use strict"; + +function HuesWindow(root, defaults) { + this.eventListeners = { + /* callback windowshown(shown) + * + * When the window is shown, hidden or toggled this fires. + * 'shown' is true if the window was made visible, false otherwise + */ + windowshown : [], + /* callback tabselected(tabName) + * + * The name of the tab that was selected + */ + tabselected : [] + }; + + this.hasUI = defaults.enableWindow; + + if(!this.hasUI) + return; + + this.window = document.createElement("div"); + this.window.className = "hues-win-helper"; + root.appendChild(this.window); + + let actualWindow = document.createElement("div"); + actualWindow.className = "hues-win"; + this.window.appendChild(actualWindow); + + let closeButton = document.createElement("div"); + closeButton.className = "hues-win__closebtn"; + closeButton.onclick = this.hide.bind(this); + actualWindow.appendChild(closeButton); + + this.tabContainer = document.createElement("div"); + this.tabContainer.className = "hues-win__tabs"; + actualWindow.appendChild(this.tabContainer); + + this.contentContainer = document.createElement("div"); + this.contentContainer.className = "hues-win__content"; + actualWindow.appendChild(this.contentContainer); + + this.contents = []; + this.tabs = []; + this.tabNames = []; + + + if(defaults.showWindow) { + this.show(); + } else { + this.hide(); + } +} + +HuesWindow.prototype.addTab = function(tabName, tabContent) { + if(!this.hasUI) + return; + + let label = document.createElement("div"); + label.textContent = tabName; + label.className = "tab-label"; + label.onclick = this.selectTab.bind(this, tabName); + this.tabContainer.appendChild(label); + this.tabs.push(label); + this.tabNames.push(tabName); + + let content = document.createElement("div"); + content.className = "tab-content"; + content.appendChild(tabContent); + this.contentContainer.appendChild(content); + this.contents.push(content); +}; + +HuesWindow.prototype.selectTab = function(tabName, dontShowWin) { + if(!this.hasUI) + return; + if(!dontShowWin) { + this.show(); + } + for(let i = 0; i < this.tabNames.length; i++) { + let name = this.tabNames[i]; + if(tabName.toLowerCase() == name.toLowerCase()) { + this.contents[i].classList.add("tab-content--active"); + this.tabs[i].classList.add("tab-label--active"); + this.callEventListeners("tabselected", name); + } else { + this.contents[i].classList.remove("tab-content--active"); + this.tabs[i].classList.remove("tab-label--active"); + } + } +}; + +HuesWindow.prototype.hide = function() { + if(!this.hasUI) + return; + + this.window.classList.add("hidden"); + this.callEventListeners("windowshown", false); +}; + +HuesWindow.prototype.show = function() { + if(!this.hasUI) + return; + + this.window.classList.remove("hidden"); + this.callEventListeners("windowshown", true); +}; + +HuesWindow.prototype.toggle = function() { + if(!this.hasUI) + return; + if(this.window.classList.contains("hidden")) { + this.show(); + } else { + this.hide(); + } +}; + +HuesWindow.prototype.callEventListeners = function(ev) { + let args = Array.prototype.slice.call(arguments, 1); + this.eventListeners[ev].forEach(function(callback) { + callback.apply(null, args); + }); +}; + +HuesWindow.prototype.addEventListener = function(ev, callback) { + ev = ev.toLowerCase(); + if (typeof(this.eventListeners[ev]) !== "undefined") { + this.eventListeners[ev].push(callback); + } else { + throw Error("Unknown event: " + ev); + } +}; + +HuesWindow.prototype.removeEventListener = function(ev, callback) { + ev = ev.toLowerCase(); + if (typeof(this.eventListeners[ev]) !== "undefined") { + this.eventListeners[ev] = this.eventListeners[ev].filter(function(a) { + return (a !== callback); + }); + } else { + throw Error("Unknown event: " + ev); + } +}; + +window.HuesWindow = HuesWindow; + +})(window, document); \ No newline at end of file diff --git a/src/js/ResourceManager.js b/src/js/ResourceManager.js index 9aaad53..ebc87f2 100644 --- a/src/js/ResourceManager.js +++ b/src/js/ResourceManager.js @@ -25,7 +25,7 @@ // NOTE: Any packs referenced need CORS enabled or loads fail let packsURL = "https://cdn.0x40hu.es/getRespacks.php"; -function Resources(core) { +function Resources(core, huesWin) { this.core = core; this.hasUI = false; @@ -39,7 +39,6 @@ function Resources(core) { this.progressState = []; this.progressCallback = null; - this.root = null; // For songs/images this.listView = null; this.enabledSongList = null; @@ -71,8 +70,9 @@ function Resources(core) { this.remotes = null; this.fileInput = null; this.fileParseQueue = []; - if(!core.settings.defaults.noUI) { + if(core.settings.defaults.enableWindow) { this.initUI(); + huesWin.addTab("RESOURCES", this.root); } } @@ -294,18 +294,17 @@ Resources.prototype.localComplete = function(progress) { }; Resources.prototype.initUI = function() { - this.root = document.getElementById("huesResources"); - + this.root = document.createElement("div"); + this.root.className = "respacks"; + let packsContainer = document.createElement("div"); - packsContainer.className = "res-packscontainer"; + packsContainer.className = "respacks__manager"; let packHeader = document.createElement("div"); packHeader.textContent = "Current respacks"; - packHeader.className = "res-header"; - packHeader.id = "res-curheader"; + packHeader.className = "respacks__header"; let packList = document.createElement("div"); - packList.className = "res-list"; - packList.id = "res-packlist"; + packList.className = "resource-list"; this.packsView.respackList = packList; // so we don't use it out of scope in the next if let remoteHeader = null; @@ -313,19 +312,17 @@ Resources.prototype.initUI = function() { if(!this.core.settings.defaults.disableRemoteResources) { remoteHeader = document.createElement("div"); remoteHeader.textContent = "Remote respacks"; - remoteHeader.className = "res-header"; + remoteHeader.className = "respacks__header"; remoteList = document.createElement("div"); - remoteList.className = "res-list"; - remoteList.id = "res-remotelist"; + remoteList.className = "resource-list resource-list--fill"; + packList.classList.add("resource-list--fill"); this.appendSimpleListItem("Click to load the list", remoteList, this.loadRemotes.bind(this)); this.packsView.remoteList = remoteList; - } else { - packList.className += " noremotes"; } let buttons = document.createElement("div"); - buttons.className = "res-buttons"; + buttons.className = "respacks-buttons"; let loadRemote = document.createElement("div"); loadRemote.className = "hues-button hidden"; loadRemote.textContent = "LOAD REMOTE"; @@ -345,25 +342,22 @@ Resources.prototype.initUI = function() { this.fileInput.onchange = this.loadLocal.bind(this); let progressContainer = document.createElement("div"); - progressContainer.id = "res-progress-container"; + progressContainer.className = "progress-container respacks-bottom-container"; let progressBar = document.createElement("div"); - progressBar.id = "res-progress-bar"; + progressBar.className = "progress-bar"; let progressFilled = document.createElement("span"); - progressFilled.id = "res-progress-filled"; + progressFilled.className = "progress-bar--filled"; progressBar.appendChild(progressFilled); let progressStatus = document.createElement("div"); progressStatus.textContent = "Idle"; let progressTexts = document.createElement("div"); - progressTexts.id = "res-progress-texts"; + progressTexts.className = "stat-text"; let progressCurrent = document.createElement("div"); - progressCurrent.id = "res-progress-current"; progressCurrent.textContent = "0b"; let progressTop = document.createElement("div"); - progressTop.id = "res-progress-top"; progressTop.textContent = "0b"; let progressPercent = document.createElement("div"); - progressPercent.id = "res-progress-percent"; progressPercent.textContent = "0%"; progressTexts.appendChild(progressCurrent); progressTexts.appendChild(progressTop); @@ -388,15 +382,16 @@ Resources.prototype.initUI = function() { packsContainer.appendChild(progressContainer); let indivView = document.createElement("div"); - indivView.className = "res-packcontainer"; + indivView.className = "respacks__display"; let packName = document.createElement("div"); packName.textContent = "