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
master
William Toohey 9 years ago
parent 94a12f941d
commit 2df280e071
  1. 3
      .jshintrc
  2. 50
      index.html
  3. 92
      src/css/hues-editor.css
  4. 188
      src/css/hues-main.css
  5. 233
      src/css/hues-res.css
  6. 201
      src/css/hues-respacks.css
  7. 182
      src/css/hues-settings.css
  8. 34
      src/css/huesUI-hlwn.css
  9. 16
      src/css/huesUI-modern.css
  10. 12
      src/css/huesUI-retro.css
  11. 14
      src/css/huesUI-weed.css
  12. 31
      src/css/huesUI-xmas.css
  13. 377
      src/css/style.css
  14. 140
      src/js/HuesCanvas.js
  15. 177
      src/js/HuesCore.js
  16. 163
      src/js/HuesEditor.js
  17. 54
      src/js/HuesInfo.js
  18. 198
      src/js/HuesSettings.js
  19. 203
      src/js/HuesUI.js
  20. 171
      src/js/HuesWindow.js
  21. 175
      src/js/ResourceManager.js

@ -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
}

@ -23,54 +23,6 @@
</script>
</head>
<body>
<div id="preloadHelper">
<div id="preloader">
<div id="preMain">Initialising...</div>
<div id="preSub">This page requires Javascript</div>
</div>
</div>
<div id="settingsHelper">
<div id="settingsWindow">
<div id="closeButton">x</div>
<div id="tabs">
<label class="tab-label" for="tab-resources">RESOURCES</label>
<label class="tab-label" for="tab-editor">EDITOR</label>
<label class="tab-label" for="tab-options">OPTIONS</label>
<label class="tab-label checked" for="tab-info">INFO</label>
</div>
<div>
<input class="tab-input" type="radio" name="tabs" id="tab-resources">
<input class="tab-input" type="radio" name="tabs" id="tab-editor">
<input class="tab-input" type="radio" name="tabs" id="tab-options">
<input class="tab-input" type="radio" name="tabs" id="tab-info" checked>
<div id="tab-resources-content" class="tab-content">
<!-- Populated by ResourceManager.js -->
<div id="huesResources"></div>
</div>
<div id="tab-editor-content" class="tab-content">
<!-- Populated by HuesEditor.js -->
<div id="huesEditor"></div>
</div>
<div id="tab-options-content" class="tab-content">
<!-- Populated by HuesSettings.js -->
<div id="huesSettings"></div>
</div>
<div id="tab-info-content" class="tab-content">
<div id="about">
<h1>0x40 Hues of JS, <span id="versionText"><!-- Populated by HuesInfo.js --></span></h1>
<h2>Adapted from the <a target="_blank" href="http://0x40hues.blogspot.com">0x40 Flash</a></h2>
<h2>Web-ified by <a target="_blank" href="https://github.com/mon">mon</a></h2>
<h3>With help from <a target="_blank" href="https://github.com/kepstin/0x40hues-html5">Kepstin</a></h3>
</div>
<!-- Populated by HuesInfo.js -->
<div id="reference"></div>
</div>
</div>
</div>
</div>
<!-- Populated by HuesUI.js -->
<div id="huesUI"></div>
<canvas id="snow" class="hidden" width="1280" height="720"></canvas>
<canvas id="waifu" width="1280" height="720"></canvas>
This page requires Javascript.
</body>
</html>

@ -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;
}

@ -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);
}
}

@ -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;
}

@ -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;
}

@ -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;
}

@ -7,7 +7,7 @@
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;
}

@ -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);
}
@ -25,8 +25,8 @@
height: 64px;
bottom: 108px;
left: -8px;
right:0px;
margin:0px auto;
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 {

@ -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;
}

@ -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;}
}

@ -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 {

@ -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);
}
}

@ -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);

@ -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,6 +251,7 @@ function HuesCore(defaults) {
this.error(error);
});
if(!defaults.disableKeyboard) {
document.addEventListener("keydown", e => {
e = e || window.event;
if(e.defaultPrevented) {
@ -249,6 +270,7 @@ function HuesCore(defaults) {
return this.keyHandler(key);
});
}
}
HuesCore.prototype.callEventListeners = function(ev) {
let args = Array.prototype.slice.call(arguments, 1);
@ -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();

@ -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 = "&block;";
this.loopEdit._hilight.innerHTML = "&block;";
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,16 +242,19 @@ 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");
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;
}
if(this.song) {
this.saveBtn.classList.remove("disabled");
this.copyBtn.classList.remove("disabled");
this.saveBtn.classList.remove("hues-button--disabled");
this.copyBtn.classList.remove("hues-button--disabled");
if(this.song.independentBuild) {
this.timeLock._locker.innerHTML = "&#xe904;";
@ -263,13 +264,12 @@ HuesEditor.prototype.updateInfo = function() {
this.timeLock.classList.remove("unlocked");
}
if(this.song.sound) {
this.seekLoop.classList.remove("disabled");
this.loopEdit._removeBtn.classList.remove("disabled");
this.seekLoop.classList.remove("hues-button--disabled");
this.loopEdit._removeBtn.classList.remove("hues-button--disabled");
}
if(this.song.buildup) {
this.seekStart.classList.remove("disabled");
this.buildEdit._removeBtn.classList.remove("disabled");
}
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:&nbsp;", "edit-source", "Source link", info);
this.source = this.createTextInput("Link:&nbsp;", "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 &#xe905; for CHAIN
let locker = this.createButton("&#xe904;", 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 = "&#xe90b;";
@ -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 = "&#xe908;"; // 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&nbsp;", "sound", "rhythm", "edit-loop", editArea);
this.loopEdit = this.uiCreateSingleEditor("Rhythm&nbsp;", "sound", "rhythm", editArea);
this.seekLoop = this.loopEdit._seek;
// FIRST |<<
this.seekLoop.innerHTML = "&#xe90b;";
@ -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("&#xe907;", 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));
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;

@ -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);
function populateHuesInfo(version, huesWin, defaults) {
if(!defaults.enableWindow) {
return;
}
let verString = (parseInt(version)/10).toFixed(1);
addInfo("Beat glossary", beatGlossary);
addInfo("Keyboard shortcuts", shortcuts);
}
let info = document.createElement("div");
info.className = "hues-ref";
let addInfo = function(titleText, list) {
let refElem = document.getElementById(huesInfo.referenceID);
if(!refElem) {
return;
let huesName = defaults.huesName.replace("%VERSION%", version);
let about = document.createElement("div");
about.className = "hues-about";
about.innerHTML = "<h1>" + huesName + "</h1>" +
'<h2>Adapted from the <a target="_blank" href="http://0x40hues.blogspot.com">0x40 Flash</a></h2>' +
'<h2>Web-ified by <a target="_blank" href="https://github.com/mon">mon</a></h2>' +
'<h3>With help from <a target="_blank" href="https://github.com/kepstin/0x40hues-html5">Kepstin</a></h3>';
info.appendChild(about);
addReference(info, "Beat glossary", beatGlossary);
addReference(info, "Keyboard shortcuts", shortcuts);
huesWin.addTab("INFO", info);
}
let info = document.createElement("div");
info.className = huesInfo.referenceClass;
refElem.appendChild(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;

@ -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 <body>, 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);

@ -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 = '&#xe900;'; // 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);

@ -0,0 +1,171 @@
/* Copyright (c) 2015 William Toohey <will@mon.im>
*
* 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);

@ -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 = "<select a respack>";
packName.className = "respacks__header";
let packInfo = document.createElement("div");
packInfo.id = "res-packinfo";
packInfo.className = "stat-text";
let packCreator = document.createElement("div");
packCreator.id = "res-packcreator";
let packCreatorText = document.createElement("a");
packCreatorText.className = "unstyled-link";
packCreatorText.textContent = "<author>";
packCreator.appendChild(packCreatorText);
packInfo.appendChild(packCreator);
@ -404,47 +399,43 @@ Resources.prototype.initUI = function() {
packSize.textContent = "0b";
packInfo.appendChild(packSize);
let packDesc = document.createElement("div");
packDesc.id = "res-packdesc";
packDesc.className = "respack-description";
packDesc.textContent = "<no description>";
let packTabs = document.createElement("div");
packTabs.id = "res-packtabs";
let tabContainer = document.createElement("div");
tabContainer.className = "respack-tab-container";
let songCheck = document.createElement("input");
songCheck.type = "radio";
songCheck.name = "packtab";
songCheck.value = "songs";
songCheck.checked = true;
songCheck.id = "res-songtab";
let songCount = document.createElement("label");
let songCount = document.createElement("div");
songCount.textContent = "Songs:";
songCount.htmlFor = "res-songtab";
packTabs.appendChild(songCheck);
packTabs.appendChild(songCount);
let imageCheck = document.createElement("input");
imageCheck.type = "radio";
imageCheck.name = "packtab";
imageCheck.value = "images";
imageCheck.id = "res-imagetab";
let imageCount = document.createElement("label");
songCount.className = "respack-tab respack-tab--checked";
let imageCount = document.createElement("div");
imageCount.textContent = "Images:";
imageCount.htmlFor = "res-imagetab";
packTabs.appendChild(imageCheck);
packTabs.appendChild(imageCount);
imageCount.className = "respack-tab";
let songList = document.createElement("div");
songList.id = "res-songlist";
songList.className = "res-list";
songList.className = "resource-list respack-tab__content respack-tab__content--checked";
let imageList = document.createElement("div");
imageList.id = "res-imagelist";
imageList.className = "res-list";
packTabs.appendChild(songList);
packTabs.appendChild(imageList);
imageList.className = "resource-list respack-tab__content";
songCount.onclick = () => {
songCount.classList.add("respack-tab--checked");
imageCount.classList.remove("respack-tab--checked");
songList.classList.add("respack-tab__content--checked");
imageList.classList.remove("respack-tab__content--checked");
};
imageCount.onclick = () => {
imageCount.classList.add("respack-tab--checked");
songCount.classList.remove("respack-tab--checked");
imageList.classList.add("respack-tab__content--checked");
songList.classList.remove("respack-tab__content--checked");
};
let packButtons = document.createElement("div");
packButtons.className = "res-buttons hidden";
packButtons.id = "res-packbuttons";
packButtons.className = "respacks-buttons respacks-buttons--fill invisible";
let enableAll = document.createElement("div");
enableAll.textContent = "ENABLE ALL";
enableAll.className = "hues-button";
@ -462,21 +453,23 @@ Resources.prototype.initUI = function() {
packButtons.appendChild(disableAll);
let totalCounts = document.createElement("div");
totalCounts.id = "res-countscontainer";
totalCounts.className = "respacks-bottom-container";
let totalSongsCont = document.createElement("div");
totalSongsCont.className = "respacks-count-container";
let totalSongsLabel = document.createElement("span");
totalSongsLabel.textContent = "Total Songs:";
let totalSongs = document.createElement("span");
totalSongs.className = "res-counts";
totalSongs.className = "respacks-counts";
totalSongsCont.appendChild(totalSongsLabel);
totalSongsCont.appendChild(totalSongs);
let totalImagesCont = document.createElement("div");
totalImagesCont.className = "respacks-count-container";
let totalImagesLabel = document.createElement("span");
totalImagesLabel.textContent = "Total images:";
let totalImages = document.createElement("span");
totalImages.className = "res-counts";
totalImages.className = "respacks-counts";
totalImagesCont.appendChild(totalImagesLabel);
totalImagesCont.appendChild(totalImages);
@ -498,7 +491,13 @@ Resources.prototype.initUI = function() {
indivView.appendChild(packName);
indivView.appendChild(packInfo);
indivView.appendChild(packDesc);
indivView.appendChild(packTabs);
tabContainer.appendChild(songCount);
tabContainer.appendChild(imageCount);
indivView.appendChild(tabContainer);
indivView.appendChild(songList);
indivView.appendChild(imageList);
indivView.appendChild(packButtons);
indivView.appendChild(totalCounts);
@ -507,11 +506,9 @@ Resources.prototype.initUI = function() {
this.listView = document.createElement("div");
this.enabledSongList = document.createElement("div");
this.enabledSongList.id = "res-enabledsonglist";
this.enabledSongList.className = "hidden";
this.enabledSongList.className = "resource-list respacks-enabledsongs hidden";
this.enabledImageList = document.createElement("div");
this.enabledImageList.id = "res-enabledimagelist";
this.enabledImageList.className = "hidden";
this.enabledImageList.className = "resource-list respacks-enabledimages hidden";
this.listView.appendChild(this.enabledSongList);
this.listView.appendChild(this.enabledImageList);
@ -521,28 +518,26 @@ Resources.prototype.initUI = function() {
Resources.prototype.hideLists = function() {
if(!this.hasUI) {return;}
this.enabledSongList.className = "hidden";
this.enabledImageList.className = "hidden";
this.enabledSongList.classList.add("hidden");
this.enabledImageList.classList.add("hidden");
};
Resources.prototype.toggleSongList = function() {
Resources.prototype.toggleVisible = function(me, other) {
if(!this.hasUI) {return;}
if(this.enabledSongList.className == "hidden") {
this.enabledSongList.className = "res-list";
if(me.classList.contains("hidden")) {
me.classList.remove("hidden");
} else {
this.enabledSongList.className = "hidden";
me.classList.add("hidden");
}
this.enabledImageList.className = "hidden";
other.classList.add("hidden");
};
Resources.prototype.toggleSongList = function() {
this.toggleVisible(this.enabledSongList, this.enabledImageList);
};
Resources.prototype.toggleImageList = function() {
if(!this.hasUI) {return;}
if(this.enabledImageList.className == "hidden") {
this.enabledImageList.className = "res-list";
} else {
this.enabledImageList.className = "hidden";
}
this.enabledSongList.className = "hidden";
this.toggleVisible(this.enabledImageList, this.enabledSongList);
};
Resources.prototype.updateTotals = function() {
@ -561,8 +556,8 @@ Resources.prototype.selectPack = function(id) {
let pack = this.resourcePacks[id];
this.packView.pack = pack;
this.packView.packButtons.className = "res-buttons";
this.packsView.loadRemote.className = "hues-button hidden";
this.packView.packButtons.classList.remove("invisible");
this.packsView.loadRemote.classList.add("hidden");
this.packView.name.textContent = pack.name;
this.packView.creator.textContent = pack.author;
@ -678,13 +673,18 @@ Resources.prototype.appendListItem = function(name, value, id, root, oncheck, on
if(checked === undefined) {
checked = true;
}
// For multiple hues on one page
let unique = 0;
while(document.getElementById(id + "-" + unique)) {
unique++;
}
let div = document.createElement("div");
div.className = "res-listitem";
div.className = "respacks-listitem";
let checkbox = document.createElement("input");
checkbox.type = "checkbox";
checkbox.name = name;
checkbox.value = value;
checkbox.id = id;
checkbox.id = id + "-" + unique;
checkbox.checked = checked;
checkbox.onclick = oncheck;
let checkStyler = document.createElement("label");
@ -740,12 +740,13 @@ Resources.prototype.selectRemotePack = function(id) {
let pack = this.remotes[id];
this.packView.pack = pack;
this.packView.packButtons.className = "res-buttons hidden";
this.packsView.loadRemote.className = "hues-button";
this.packView.packButtons.classList.add("invisible");
this.packsView.loadRemote.classList.remove("hidden");
if(pack.loaded) {
this.packsView.loadRemote.className += " loaded";
this.packsView.loadRemote.classList.add("hues-button--loaded");
this.packsView.loadRemote.textContent = "LOADED";
} else {
this.packsView.loadRemote.classList.remove("hues-button--loaded");
this.packsView.loadRemote.textContent = "LOAD REMOTE";
}
@ -810,7 +811,7 @@ Resources.prototype.loadCurrentRemote = function() {
// TODO Error checking on failure
pack.loaded = true;
this.packsView.loadRemote.className = "hues-button loaded";
this.packsView.loadRemote.className = "hues-button hues-button--loaded";
this.packsView.loadRemote.textContent = "LOADING";
this.addAll([pack.url], (progress, respack) => {
this.remoteProgress(progress, respack);
@ -848,7 +849,7 @@ Resources.prototype.remoteComplete = function() {
Resources.prototype.appendSimpleListItem = function(value, root, onclick) {
let div = document.createElement("div");
div.className = "res-listitem";
div.className = "respacks-listitem";
let label = document.createElement("span");
// Because we're using textContent, we replace with literal &
label.textContent = value.replace(/&amp;/g, '&');

Loading…
Cancel
Save