diff --git a/package.json b/package.json
index 6b76c46..91fe784 100644
--- a/package.json
+++ b/package.json
@@ -20,7 +20,7 @@
},
"homepage": "https://github.com/mon/0x40-web#readme",
"devDependencies": {
- "babel-preset-es2015": "^6.6.0",
+ "babel-preset-es2015": "^6.9.0",
"del": "^2.2.0",
"gulp": "^3.9.1",
"gulp-autoprefixer": "^3.1.0",
@@ -33,6 +33,6 @@
"gulp-plumber": "^1.1.0",
"gulp-sourcemaps": "^1.6.0",
"gulp-uglify": "^1.5.3",
- "jshint": "^2.9.1"
+ "jshint": "^2.9.2"
}
}
diff --git a/src/js/HuesCanvas.js b/src/js/HuesCanvas.js
index 4cbde4f..7d549ef 100644
--- a/src/js/HuesCanvas.js
+++ b/src/js/HuesCanvas.js
@@ -25,149 +25,186 @@
/* Takes root element to attach to, and an audio context element for
getting the current time with reasonable accuracy */
-function HuesCanvas(root, audioContext, core) {
- this.audio = audioContext;
- core.addEventListener("newimage", this.setImage.bind(this));
- core.addEventListener("newcolour", this.setColour.bind(this));
- core.addEventListener("beat", this.beat.bind(this));
- core.addEventListener("invert", this.setInvert.bind(this));
- core.addEventListener("settingsupdated", this.settingsUpdated.bind(this));
- core.addEventListener("frame", this.animationLoop.bind(this));
- this.core = core;
-
- this.needsRedraw = false;
- this.colour = 0xFFFFFF;
- this.image = null;
- this.smartAlign = true; // avoid string comparisons every frame
-
- this.animTimeout = null;
- this.animFrame = null;
- this.lastBeat = 0;
-
- // set later
- this.blurDecay = null;
- this.blurAmount = null;
- this.blurIterations = null;
- this.blurDelta = null;
- this.blurAlpha = null;
- // dynamic
- this.blurStart = 0;
- this.blurDistance = 0;
- this.xBlur = false;
- this.yBlur = false;
-
- // trippy mode
- this.trippyStart = [0, 0]; // x, y
- this.trippyRadii = [0, 0]; // x, y
- // force trippy mode
- this.trippyOn = false;
- this.trippyRadius = 0;
-
- this.blackout = false;
- this.blackoutColour = "#000"; // for the whiteout case we must store this
- this.blackoutTimeout = null;
-
- this.invert = false;
-
- this.colourFade = false;
- this.colourFadeStart=0;
- this.colourFadeLength=0;
- this.oldColour=0xFFFFFF;
- this.newColour=0xFFFFFF;
-
- this.blendMode = "hard-light";
- // Chosen because they look decent
- this.setBlurAmount("medium");
- this.setBlurQuality("high");
- this.setBlurDecay("fast");
-
- 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');
-
- window.addEventListener('resize', this.resize.bind(this));
- this.resize();
-}
+class HuesCanvas {
+ constructor(root, audioContext, core) {
+ this.audio = audioContext;
+ core.addEventListener("newimage", this.setImage.bind(this));
+ core.addEventListener("newcolour", this.setColour.bind(this));
+ core.addEventListener("beat", this.beat.bind(this));
+ core.addEventListener("invert", this.setInvert.bind(this));
+ core.addEventListener("settingsupdated", this.settingsUpdated.bind(this));
+ core.addEventListener("frame", this.animationLoop.bind(this));
+ this.core = core;
-HuesCanvas.prototype.setInvert = function(invert) {
- this.invert = invert;
- this.needsRedraw = true;
-};
-
-HuesCanvas.prototype.settingsUpdated = function() {
- this.setSmartAlign(localStorage["smartAlign"]);
- this.setBlurAmount(localStorage["blurAmount"]);
- this.setBlurDecay(localStorage["blurDecay"]);
- this.setBlurQuality(localStorage["blurQuality"]);
- this.trippyOn = localStorage["trippyMode"] == "on";
-};
-
-HuesCanvas.prototype.resize = function() {
- // 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.trippyRadius = Math.max(this.canvas.width, this.canvas.height) / 2;
- this.needsRedraw = true;
-};
-
-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
- this.context.globalAlpha = 1;
- this.context.globalCompositeOperation = "source-over";
- if(this.blackout) {
- // original is 3 frames at 30fps, this is close
- bOpacity = (cTime - this.blackoutStart)*10;
- if(bOpacity > 1) { // optimise the draw
- this.context.fillStyle = this.blackoutColour;
+ this.needsRedraw = false;
+ this.colour = 0xFFFFFF;
+ this.image = null;
+ this.smartAlign = true; // avoid string comparisons every frame
+
+ this.animTimeout = null;
+ this.animFrame = null;
+ this.lastBeat = 0;
+
+ // set later
+ this.blurDecay = null;
+ this.blurAmount = null;
+ this.blurIterations = null;
+ this.blurDelta = null;
+ this.blurAlpha = null;
+ // dynamic
+ this.blurStart = 0;
+ this.blurDistance = 0;
+ this.xBlur = false;
+ this.yBlur = false;
+
+ // trippy mode
+ this.trippyStart = [0, 0]; // x, y
+ this.trippyRadii = [0, 0]; // x, y
+ // force trippy mode
+ this.trippyOn = false;
+ this.trippyRadius = 0;
+
+ this.blackout = false;
+ this.blackoutColour = "#000"; // for the whiteout case we must store this
+ this.blackoutTimeout = null;
+
+ this.invert = false;
+
+ this.colourFade = false;
+ this.colourFadeStart=0;
+ this.colourFadeLength=0;
+ this.oldColour=0xFFFFFF;
+ this.newColour=0xFFFFFF;
+
+ this.blendMode = "hard-light";
+ // Chosen because they look decent
+ this.setBlurAmount("medium");
+ this.setBlurQuality("high");
+ this.setBlurDecay("fast");
+
+ 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');
+
+ window.addEventListener('resize', this.resize.bind(this));
+ this.resize();
+ }
+
+ setInvert(invert) {
+ this.invert = invert;
+ this.needsRedraw = true;
+ }
+
+ settingsUpdated() {
+ this.setSmartAlign(localStorage["smartAlign"]);
+ this.setBlurAmount(localStorage["blurAmount"]);
+ this.setBlurDecay(localStorage["blurDecay"]);
+ this.setBlurQuality(localStorage["blurQuality"]);
+ this.trippyOn = localStorage["trippyMode"] == "on";
+ }
+
+ resize() {
+ // 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.trippyRadius = Math.max(this.canvas.width, this.canvas.height) / 2;
+ this.needsRedraw = true;
+ }
+
+ redraw() {
+ 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
+ this.context.globalAlpha = 1;
+ this.context.globalCompositeOperation = "source-over";
+ if(this.blackout) {
+ // original is 3 frames at 30fps, this is close
+ bOpacity = (cTime - this.blackoutStart)*10;
+ if(bOpacity > 1) { // optimise the draw
+ this.context.fillStyle = this.blackoutColour;
+ this.context.fillRect(0,0,width,height);
+ this.needsRedraw = false;
+ this.drawInvert();
+ return;
+ }
+ } else {
+ this.context.fillStyle = "#FFF";
this.context.fillRect(0,0,width,height);
- this.needsRedraw = false;
- this.drawInvert();
- return;
}
- } else {
- this.context.fillStyle = "#FFF";
- 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 - drawWidth;
- break;
- default:
- offset = width/2 - drawWidth/2;
- break;
+ 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 - drawWidth;
+ break;
+ default:
+ offset = width/2 - drawWidth/2;
+ break;
+ }
+ } else {
+ offset = width/2 - drawWidth/2;
}
+ if(this.xBlur || this.yBlur) {
+ this.drawBlur(bitmap, offset, drawWidth, drawHeight);
+ }else {
+ this.context.globalAlpha = 1;
+ this.context.drawImage(bitmap, offset, 0, drawWidth, drawHeight);
+ }
+ }
+
+ if(this.trippyStart[0] || this.trippyStart[1]) {
+ this.drawTrippy(width, height);
} else {
- offset = width/2 - drawWidth/2;
+ this.offContext.fillStyle = this.intToHex(this.colour);
+ this.offContext.fillRect(0,0,width,height);
}
- if(this.xBlur || this.yBlur) {
- this.context.globalAlpha = this.blurAlpha;
+ this.context.globalAlpha = 0.7;
+ this.context.globalCompositeOperation = this.blendMode;
+ this.context.drawImage(this.offCanvas, 0, 0);
+ if(this.blackout) {
+ this.context.globalAlpha = bOpacity;
+ this.context.fillStyle = this.blackoutColour;
+ this.context.fillRect(0,0,width,height);
+ this.needsRedraw = true;
+ } else {
+ this.needsRedraw = false;
}
+ this.drawInvert();
+ }
+
+ drawInvert() {
+ if(this.invert) {
+ this.context.globalAlpha = 1;
+ this.context.globalCompositeOperation = "difference";
+ this.context.fillStyle = "#FFF";
+ this.context.fillRect(0,0,this.canvas.width,this.canvas.height);
+ }
+ }
+
+ drawBlur(bitmap, offset, drawWidth, drawHeight) {
+ this.context.globalAlpha = this.blurAlpha;
if(this.xBlur) {
if(this.blurIterations < 0) {
this.context.globalAlpha = 1;
@@ -188,13 +225,11 @@ HuesCanvas.prototype.redraw = function() {
this.context.drawImage(bitmap, offset, Math.floor(this.blurDistance * i), drawWidth, drawHeight);
}
}
- } else {
- this.context.globalAlpha = 1;
- this.context.drawImage(bitmap, offset, 0, drawWidth, drawHeight);
}
}
-
- if(this.trippyStart[0] || this.trippyStart[1]) {
+
+ // draws the correct trippy colour circles onto the offscreen canvas
+ drawTrippy(width, height) {
// x blur moves inwards from the corners, y comes out
// So the base colour is inverted for y, normal for x
// Thus if the y start is more recent, we invert
@@ -222,289 +257,265 @@ HuesCanvas.prototype.redraw = function() {
this.offContext.closePath();
invert = !invert;
}
- } else {
- this.offContext.fillStyle = this.intToHex(this.colour);
- this.offContext.fillRect(0,0,width,height);
}
- this.context.globalAlpha = 0.7;
- this.context.globalCompositeOperation = this.blendMode;
- this.context.drawImage(this.offCanvas, 0, 0);
- if(this.blackout) {
- this.context.globalAlpha = bOpacity;
- this.context.fillStyle = this.blackoutColour;
- this.context.fillRect(0,0,width,height);
- this.needsRedraw = true;
- } else {
- this.needsRedraw = false;
- }
- this.drawInvert();
-};
-HuesCanvas.prototype.drawInvert = function() {
- if(this.invert) {
- this.context.globalAlpha = 1;
- this.context.globalCompositeOperation = "difference";
- this.context.fillStyle = "#FFF";
- this.context.fillRect(0,0,this.canvas.width,this.canvas.height);
+ /* Second fastest method from
+ http://stackoverflow.com/questions/10073699/pad-a-number-with-leading-zeros-in-javascript
+ It stil does millions of ops per second, and isn't ugly like the integer if/else */
+ intToHex(num) {
+ return '#' + ("00000"+num.toString(16)).slice(-6);
}
-};
-
-/* Second fastest method from
- http://stackoverflow.com/questions/10073699/pad-a-number-with-leading-zeros-in-javascript
- It stil does millions of ops per second, and isn't ugly like the integer if/else */
-HuesCanvas.prototype.intToHex = function(num) {
- return '#' + ("00000"+num.toString(16)).slice(-6);
-};
-
-HuesCanvas.prototype.animationLoop = function() {
- if (this.colourFade) {
- let delta = this.audio.currentTime - this.colourFadeStart;
- let fadeVal = delta / this.colourFadeLength;
- if (fadeVal >= 1) {
- this.stopFade();
- this.colour = this.newColour;
- } else {
- this.mixColours(fadeVal);
+
+ animationLoop() {
+ if (this.colourFade) {
+ let delta = this.audio.currentTime - this.colourFadeStart;
+ let fadeVal = delta / this.colourFadeLength;
+ if (fadeVal >= 1) {
+ this.stopFade();
+ this.colour = this.newColour;
+ } else {
+ this.mixColours(fadeVal);
+ }
+ this.needsRedraw = true;
}
- this.needsRedraw = true;
- }
- if(this.blackoutTimeout && this.audio.currentTime > this.blackoutTimeout) {
- this.clearBlackout();
- }
- if(this.image && this.image.animated){
- if(this.image.beatsPerAnim && this.core.currentSong && this.core.currentSong.charsPerBeat) {
- let a = this.animFrame;
- this.syncAnim();
- if(this.animFrame != a) {
+ if(this.blackoutTimeout && this.audio.currentTime > this.blackoutTimeout) {
+ this.clearBlackout();
+ }
+ if(this.image && this.image.animated){
+ if(this.image.beatsPerAnim && this.core.currentSong && this.core.currentSong.charsPerBeat) {
+ let a = this.animFrame;
+ this.syncAnim();
+ if(this.animFrame != a) {
+ this.needsRedraw = true;
+ // If you change to a non-synced song, this needs to be reset
+ this.animTimeout = this.audio.currentTime;
+ }
+ } else if(this.animTimeout < this.audio.currentTime) {
+ this.animFrame++;
+ this.animFrame %= this.image.frameDurations.length;
+ // Don't rebase to current time otherwise we may lag
+ this.animTimeout += this.image.frameDurations[this.animFrame]/1000;
this.needsRedraw = true;
- // If you change to a non-synced song, this needs to be reset
- this.animTimeout = this.audio.currentTime;
}
- } else if(this.animTimeout < this.audio.currentTime) {
- this.animFrame++;
- this.animFrame %= this.image.frameDurations.length;
- // Don't rebase to current time otherwise we may lag
- this.animTimeout += this.image.frameDurations[this.animFrame]/1000;
+ }
+ if(this.blurStart) {
+ // flash offsets blur gen by a frame
+ let delta = this.audio.currentTime - this.blurStart + (1/30);
+ this.blurDistance = this.blurAmount * Math.exp(-this.blurDecay * delta);
+
+ // Update UI
+ let dist = this.blurDistance / this.blurAmount;
+ if(this.xBlur)
+ this.core.blurUpdated(dist, 0);
+ else
+ this.core.blurUpdated(0, dist);
+ }
+ if(this.trippyStart[0] || this.trippyStart[1]) {
+ for(let i = 0; i < 2; i++) {
+ this.trippyRadii[i] = Math.floor((this.audio.currentTime - this.trippyStart[i]) * this.trippyRadius) * 2;
+ if(this.trippyRadii[i] > this.trippyRadius) {
+ this.trippyStart[i] = 0;
+ this.trippyRadii[i] = 0;
+ continue;
+ }
+ // x comes from outside the window
+ if(i % 2 === 0) {
+ this.trippyRadii[i] = this.trippyRadius - this.trippyRadii[i];
+ }
+ }
this.needsRedraw = true;
}
- }
- if(this.blurStart) {
- // flash offsets blur gen by a frame
- let delta = this.audio.currentTime - this.blurStart + (1/30);
- this.blurDistance = this.blurAmount * Math.exp(-this.blurDecay * delta);
- // Update UI
- let dist = this.blurDistance / this.blurAmount;
- if(this.xBlur)
- this.core.blurUpdated(dist, 0);
- else
- this.core.blurUpdated(0, dist);
+ if(this.blurStart && this.blurDistance < 1) {
+ this.core.blurUpdated(0, 0);
+ this.blurDistance = 0;
+ this.blurStart = 0;
+ this.xBlur = this.yBlur = false;
+ this.redraw();
+ } else if(this.blurStart) {
+ this.redraw();
+ } else if(this.needsRedraw){
+ this.redraw();
+ }
}
- if(this.trippyStart[0] || this.trippyStart[1]) {
- for(let i = 0; i < 2; i++) {
- this.trippyRadii[i] = Math.floor((this.audio.currentTime - this.trippyStart[i]) * this.trippyRadius) * 2;
- if(this.trippyRadii[i] > this.trippyRadius) {
- this.trippyStart[i] = 0;
- this.trippyRadii[i] = 0;
- continue;
- }
- // x comes from outside the window
- if(i % 2 === 0) {
- this.trippyRadii[i] = this.trippyRadius - this.trippyRadii[i];
- }
+
+ setImage(image) {
+ if(this.image == image) {
+ return;
}
this.needsRedraw = true;
+ this.image = image;
+ // Null images don't need anything interesting done to them
+ if(!image || (!image.bitmap && !image.bitmaps)) {
+ return;
+ }
+ if(image.animated) {
+ this.animBeat = null;
+ this.animFrame = 0;
+ this.animTimeout = this.audio.currentTime + image.frameDurations[0]/1000;
+ if(image.beatsPerAnim && this.core.currentSong && this.core.currentSong.charsPerBeat) {
+ this.syncAnim();
+ }
+ }
}
-
- if(this.blurStart && this.blurDistance < 1) {
- this.core.blurUpdated(0, 0);
- this.blurDistance = 0;
- this.blurStart = 0;
- this.xBlur = this.yBlur = false;
- this.redraw();
- } else if(this.blurStart) {
- this.redraw();
- } else if(this.needsRedraw){
- this.redraw();
+
+ beat() {
+ this.lastBeat = this.audio.currentTime;
+ }
+
+ syncAnim() {
+ let song = this.core.currentSong;
+ if(!song) { // fallback to default
+ return;
+ }
+ let index = this.core.beatIndex;
+ // When animation has more frames than song has beats, or part thereof
+ if(this.lastBeat && this.core.getBeatLength()) {
+ let interp = (this.audio.currentTime - this.lastBeat) / this.core.getBeatLength();
+ index += Math.min(interp, 1);
+ }
+ // This loops A-OK because the core's beatIndex never rolls over for a new loop
+ let beatLoc = (index / song.charsPerBeat) % this.image.beatsPerAnim;
+
+ let aLen = this.image.bitmaps.length;
+ this.animFrame = Math.floor(aLen * (beatLoc / this.image.beatsPerAnim));
+ if(this.image.syncOffset) {
+ this.animFrame += this.image.syncOffset;
+ }
+ // Because negative mods are different in JS
+ this.animFrame = ((this.animFrame % aLen) + aLen) % aLen;
}
-};
-HuesCanvas.prototype.setImage = function(image) {
- if(this.image == image) {
- return;
+ setColour(colour, isFade) {
+ if(colour.c == this.colour) {
+ return;
+ }
+ if(isFade) {
+ this.newColour = colour.c;
+ } else {
+ this.stopFade();
+ this.colour = colour.c;
+ }
+ this.needsRedraw = true;
}
- this.needsRedraw = true;
- this.image = image;
- // Null images don't need anything interesting done to them
- if(!image || (!image.bitmap && !image.bitmaps)) {
- return;
+
+ doBlackout(whiteout) {
+ if (typeof(whiteout)==='undefined') whiteout = false;
+ if(whiteout) {
+ this.blackoutColour = "#FFF";
+ } else {
+ this.blackoutColour = "#000";
+ }
+ this.blackoutTimeout = 0; // indefinite
+ // Don't restart the blackout animation if we're already blacked out
+ if(!this.blackout) {
+ this.blackoutStart = this.audio.currentTime;
+ }
+ this.blackout = true;
+ this.needsRedraw = true;
+ if(localStorage["blackoutUI"] == "on") {
+ this.core.userInterface.hide();
+ }
}
- if(image.animated) {
- this.animBeat = null;
- this.animFrame = 0;
- this.animTimeout = this.audio.currentTime + image.frameDurations[0]/1000;
- if(image.beatsPerAnim && this.core.currentSong && this.core.currentSong.charsPerBeat) {
- this.syncAnim();
+
+ // for song changes
+ clearBlackout() {
+ this.blackout = false;
+ this.blackoutTimeout = 0;
+ this.needsRedraw = true;
+ if(localStorage["blackoutUI"] == "on") {
+ this.core.userInterface.show();
}
}
-};
-HuesCanvas.prototype.beat = function() {
- this.lastBeat = this.audio.currentTime;
-};
+ doShortBlackout(beatTime) {
+ this.doBlackout();
+ this.blackoutTimeout = this.audio.currentTime + beatTime / 1.7;
+ // looks better if we go right to black
+ this.blackoutStart = 0;
+ }
+
+ doColourFade(length) {
+ this.colourFade = true;
+ this.colourFadeLength = length;
+ this.colourFadeStart = this.audio.currentTime;
+ this.oldColour = this.colour;
+ }
-HuesCanvas.prototype.syncAnim = function() {
- let song = this.core.currentSong;
- if(!song) { // fallback to default
- return;
+ stopFade() {
+ this.colourFade = false;
+ this.colourFadeStart = 0;
+ this.colourFadeLength = 0;
}
- let index = this.core.beatIndex;
- // When animation has more frames than song has beats, or part thereof
- if(this.lastBeat && this.core.getBeatLength()) {
- let interp = (this.audio.currentTime - this.lastBeat) / this.core.getBeatLength();
- index += Math.min(interp, 1);
+
+ mixColours(percent) {
+ percent = Math.min(1, percent);
+ let oldR = this.oldColour >> 16 & 0xFF;
+ let oldG = this.oldColour >> 8 & 0xFF;
+ let oldB = this.oldColour & 0xFF;
+ let newR = this.newColour >> 16 & 0xFF;
+ let newG = this.newColour >> 8 & 0xFF;
+ let newB = this.newColour & 0xFF;
+ let mixR = oldR * (1 - percent) + newR * percent;
+ let mixG = oldG * (1 - percent) + newG * percent;
+ let mixB = oldB * (1 - percent) + newB * percent;
+ this.colour = mixR << 16 | mixG << 8 | mixB;
+ }
+
+ doXBlur() {
+ this.blurStart = this.audio.currentTime;
+ if(this.trippyOn)
+ this.trippyStart[0] = this.blurStart;
+ this.blurDistance = this.blurAmount;
+ this.xBlur = true;
+ this.yBlur = false;
+ this.needsRedraw = true;
}
- // This loops A-OK because the core's beatIndex never rolls over for a new loop
- let beatLoc = (index / song.charsPerBeat) % this.image.beatsPerAnim;
- let aLen = this.image.bitmaps.length;
- this.animFrame = Math.floor(aLen * (beatLoc / this.image.beatsPerAnim));
- if(this.image.syncOffset) {
- this.animFrame += this.image.syncOffset;
+ doYBlur() {
+ this.blurStart = this.audio.currentTime;
+ if(this.trippyOn)
+ this.trippyStart[1] = this.blurStart;
+ this.blurDistance = this.blurAmount;
+ this.xBlur = false;
+ this.yBlur = true;
+ this.needsRedraw = true;
}
- // Because negative mods are different in JS
- this.animFrame = ((this.animFrame % aLen) + aLen) % aLen;
-};
-HuesCanvas.prototype.setColour = function(colour, isFade) {
- if(colour.c == this.colour) {
- return;
+ doTrippyX() {
+ let saveTrippy = this.trippyOn;
+ // force trippy
+ this.trippyOn = true;
+ this.doXBlur();
+ this.trippyOn = saveTrippy;
}
- if(isFade) {
- this.newColour = colour.c;
- } else {
- this.stopFade();
- this.colour = colour.c;
+
+ doTrippyY() {
+ let saveTrippy = this.trippyOn;
+ // force trippy
+ this.trippyOn = true;
+ this.doYBlur();
+ this.trippyOn = saveTrippy;
}
- this.needsRedraw = true;
-};
-
-HuesCanvas.prototype.doBlackout = function(whiteout) {
- if (typeof(whiteout)==='undefined') whiteout = false;
- if(whiteout) {
- this.blackoutColour = "#FFF";
- } else {
- this.blackoutColour = "#000";
+
+ setBlurDecay(decay) {
+ this.blurDecay = {"slow" : 7.8, "medium" : 14.1, "fast" : 20.8, "faster!" : 28.7}[decay];
}
- this.blackoutTimeout = 0; // indefinite
- // Don't restart the blackout animation if we're already blacked out
- if(!this.blackout) {
- this.blackoutStart = this.audio.currentTime;
+
+ setBlurQuality(quality) {
+ this.blurIterations = {"low" : -1, "medium" : 11, "high" : 19, "extreme" : 35}[quality];
+ this.blurDelta = 1 / (this.blurIterations/2);
+ this.blurAlpha = 1 / (this.blurIterations/2);
}
- this.blackout = true;
- this.needsRedraw = true;
- if(localStorage["blackoutUI"] == "on") {
- this.core.userInterface.hide();
+
+ setBlurAmount(amount) {
+ this.blurAmount = {"low" : 48, "medium" : 96, "high" : 384}[amount];
}
-};
-
-// for song changes
-HuesCanvas.prototype.clearBlackout = function() {
- this.blackout = false;
- this.blackoutTimeout = 0;
- this.needsRedraw = true;
- if(localStorage["blackoutUI"] == "on") {
- this.core.userInterface.show();
+
+ setSmartAlign(align) {
+ this.smartAlign = align == "on";
}
-};
-
-HuesCanvas.prototype.doShortBlackout = function(beatTime) {
- this.doBlackout();
- this.blackoutTimeout = this.audio.currentTime + beatTime / 1.7;
- // looks better if we go right to black
- this.blackoutStart = 0;
-};
-
-HuesCanvas.prototype.doColourFade = function(length) {
- this.colourFade = true;
- this.colourFadeLength = length;
- this.colourFadeStart = this.audio.currentTime;
- this.oldColour = this.colour;
-};
-
-HuesCanvas.prototype.stopFade = function() {
- this.colourFade = false;
- this.colourFadeStart = 0;
- this.colourFadeLength = 0;
-};
-
-HuesCanvas.prototype.mixColours = function(percent) {
- percent = Math.min(1, percent);
- let oldR = this.oldColour >> 16 & 0xFF;
- let oldG = this.oldColour >> 8 & 0xFF;
- let oldB = this.oldColour & 0xFF;
- let newR = this.newColour >> 16 & 0xFF;
- let newG = this.newColour >> 8 & 0xFF;
- let newB = this.newColour & 0xFF;
- let mixR = oldR * (1 - percent) + newR * percent;
- let mixG = oldG * (1 - percent) + newG * percent;
- let mixB = oldB * (1 - percent) + newB * percent;
- this.colour = mixR << 16 | mixG << 8 | mixB;
-};
-
-HuesCanvas.prototype.doXBlur = function() {
- this.blurStart = this.audio.currentTime;
- if(this.trippyOn)
- this.trippyStart[0] = this.blurStart;
- this.blurDistance = this.blurAmount;
- this.xBlur = true;
- this.yBlur = false;
- this.needsRedraw = true;
-};
-
-HuesCanvas.prototype.doYBlur = function() {
- this.blurStart = this.audio.currentTime;
- if(this.trippyOn)
- this.trippyStart[1] = this.blurStart;
- this.blurDistance = this.blurAmount;
- this.xBlur = false;
- this.yBlur = true;
- this.needsRedraw = true;
-};
-
-HuesCanvas.prototype.doTrippyX = function() {
- let saveTrippy = this.trippyOn;
- // force trippy
- this.trippyOn = true;
- this.doXBlur();
- this.trippyOn = saveTrippy;
-};
-
-HuesCanvas.prototype.doTrippyY = function() {
- let saveTrippy = this.trippyOn;
- // force trippy
- this.trippyOn = true;
- this.doYBlur();
- this.trippyOn = saveTrippy;
-};
-
-HuesCanvas.prototype.setBlurDecay = function(decay) {
- this.blurDecay = {"slow" : 7.8, "medium" : 14.1, "fast" : 20.8, "faster!" : 28.7}[decay];
-};
-
-HuesCanvas.prototype.setBlurQuality = function(quality) {
- this.blurIterations = {"low" : -1, "medium" : 11, "high" : 19, "extreme" : 35}[quality];
- this.blurDelta = 1 / (this.blurIterations/2);
- this.blurAlpha = 1 / (this.blurIterations/2);
-};
-
-HuesCanvas.prototype.setBlurAmount = function(amount) {
- this.blurAmount = {"low" : 48, "medium" : 96, "high" : 384}[amount];
-};
-
-HuesCanvas.prototype.setSmartAlign = function(align) {
- this.smartAlign = align == "on";
-};
+}
window.HuesCanvas = HuesCanvas;
diff --git a/src/js/HuesCore.js b/src/js/HuesCore.js
index daf8a61..e7192f0 100644
--- a/src/js/HuesCore.js
+++ b/src/js/HuesCore.js
@@ -25,1028 +25,1030 @@
(function(window, document) {
"use strict";
-function HuesCore(defaults) {
- this.eventListeners = {
- /* callback time(hundredths)
- *
- * When the song time is updated - negative for buildup
- * Returns a floating point number denoting seconds
- */
- time : [],
- /* callback blurUpdate(xPercent, yPercent)
- *
- * The current blur amounts, in percent of full blur
- */
- blurupdate : [],
+class HuesCore {
+ constructor(defaults) {
+ this.eventListeners = {
+ /* callback time(hundredths)
+ *
+ * When the song time is updated - negative for buildup
+ * Returns a floating point number denoting seconds
+ */
+ time : [],
+ /* callback blurUpdate(xPercent, yPercent)
+ *
+ * The current blur amounts, in percent of full blur
+ */
+ blurupdate : [],
+
+ /* callback newsong(song)
+ *
+ * Called on song change, whether user triggered or autosong.
+ * Song object is passed.
+ */
+ newsong : [],
+ /* callback newimage(image)
+ *
+ * Called on image change, whether user triggered or FULL AUTO mode.
+ * Image object is passed.
+ */
+ newimage : [],
+ /* callback newcolour(colour, isFade)
+ *
+ * Called on colour change.
+ * colour: colour object.
+ * isFade: if the colour is fading from the previous value
+ */
+ newcolour : [],
+ /* callback newmode(mode)
+ *
+ * Called on mode change.
+ * Mode is passed as a boolean.
+ */
+ newmode : [],
+ /* callback beat(beatString, beatIndex)
+ *
+ * Called on every new beat.
+ * beatString is a 256 char long array of current and upcoming beat chars
+ * beatIndex is the beat index. Negative during buildups
+ */
+ beat : [],
+ /* callback invert(isInverted)
+ *
+ * Called whenever the invert state changes.
+ * Invert state is passed as a boolean.
+ */
+ invert : [],
+ /* callback frame()
+ *
+ * Called on each new frame, at the end of all other frame processing
+ */
+ frame : [],
+ /* callback songstarted()
+ *
+ * Called when the song actually begins to play, not just when the
+ * new song processing begins
+ */
+ songstarted : [],
+ /* callback settingsupdated()
+ *
+ * Called when settings are updated and should be re-read from localStorage
+ */
+ settingsupdated : []
+ };
+
+ // Bunch-o-initialisers
+ this.version = 30;
+ this.versionStr = (this.version/10).toFixed(1);
+ this.versionHex = this.version.toString(16);
+ this.beatIndex = 0;
- /* callback newsong(song)
- *
- * Called on song change, whether user triggered or autosong.
- * Song object is passed.
- */
- newsong : [],
- /* callback newimage(image)
- *
- * Called on image change, whether user triggered or FULL AUTO mode.
- * Image object is passed.
- */
- newimage : [],
- /* callback newcolour(colour, isFade)
- *
- * Called on colour change.
- * colour: colour object.
- * isFade: if the colour is fading from the previous value
- */
- newcolour : [],
- /* callback newmode(mode)
- *
- * Called on mode change.
- * Mode is passed as a boolean.
- */
- newmode : [],
- /* callback beat(beatString, beatIndex)
- *
- * Called on every new beat.
- * beatString is a 256 char long array of current and upcoming beat chars
- * beatIndex is the beat index. Negative during buildups
- */
- beat : [],
- /* callback invert(isInverted)
- *
- * Called whenever the invert state changes.
- * Invert state is passed as a boolean.
- */
- invert : [],
- /* callback frame()
- *
- * Called on each new frame, at the end of all other frame processing
- */
- frame : [],
- /* callback songstarted()
- *
- * Called when the song actually begins to play, not just when the
- * new song processing begins
- */
- songstarted : [],
- /* callback settingsupdated()
- *
- * Called when settings are updated and should be re-read from localStorage
- */
- 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 {
+ // 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 = HuesCore.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;
+ };
+
+ this.settings = new HuesSettings(defaults);
+ // 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.className = "hues-visualiser";
+ this.visualiser.height = "64";
+ this.vCtx = this.visualiser.getContext("2d");
+
+ this.soundManager = new SoundManager(this);
+
+ this.soundManager.init().then(() => {
+ if(!this.soundManager.locked && localStorage["skipPreloader"] == "on") {
+ return null;
+ } else {
+ return this.resourceManager.getSizes(defaults.respacks);
+ }
+ }).then( sizes => {
+ if(sizes === null) {
+ return;
+ }
+
+ let size = sizes.reduce( (prev, curr) => {
+ return typeof curr === 'number' ? prev + curr : null;
+ }, 0);
+ if(typeof size === 'number') {
+ size = size.toFixed(1);
+ } else {
+ size = '???';
+ }
+
+ let warning = size + "MB of music/images.
" +
+ "Flashing lights.
" +
+ "Tap or click to start";
+
+ if(!this.soundManager.locked) {
+ warning += "
Skip this screen from Options";
+ }
+ this.warning(warning);
+ // Even if not locked, this steals clicks which is useful here
+ return this.soundManager.unlock();
+ }).then(() => {
+ this.clearMessage();
+ setInterval(this.loopCheck.bind(this), 1000);
+ this.renderer = new HuesCanvas(this.root, this.soundManager.context, this);
+ // Now all our objects are instantiated, we fire the updated settings
+ this.settings.addEventListener("updated", this.settingsUpdated.bind(this));
+ this.settingsUpdated();
+ this.setColour(this.colourIndex);
+ this.animationLoop();
+
+ if(defaults.load) {
+ return this.resourceManager.addAll(defaults.respacks, progress => {
+ 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 {
+ this.preloader.style.display = "none";
+ return;
+ }
+ }).then(() => {
+ this.preloader.classList.add("hues-preloader--loaded");
+ if(defaults.firstImage) {
+ this.setImageByName(defaults.firstImage);
+ } else {
+ this.setImage(0);
+ }
+ if(defaults.autoplay) {
+ if(defaults.firstSong) {
+ this.setSongByName(defaults.firstSong);
+ } else {
+ this.setSong(0);
+ }
+ }
+ }).catch(error => { // Comment this out to get proper stack traces
+ this.error(error);
+ });
+
+ if(!defaults.disableKeyboard) {
+ document.addEventListener("keydown", e => {
+ e = e || window.event;
+ if(e.defaultPrevented) {
+ return true;
+ }
+ // Ignore modifiers so we don't steal other events
+ if (e.altKey || e.ctrlKey || e.metaKey || e.shiftKey) {
+ return true;
+ }
+ // If we've focused a text input, let the input go through!
+ if((e.target.tagName.toLowerCase() == "input" && e.target.type == "text") ||
+ e.target.contentEditable === "true") {
+ return true;
+ }
+ let key = e.keyCode || e.which;
+ return this.keyHandler(key);
+ });
}
- } 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";
+
+ callEventListeners(ev) {
+ let args = Array.prototype.slice.call(arguments, 1);
+ this.eventListeners[ev].forEach(function(callback) {
+ callback.apply(null, args);
+ });
}
- // 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;
- };
-
- this.settings = new HuesSettings(defaults);
- // 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);
+ addEventListener(ev, callback) {
+ ev = ev.toLowerCase();
+ if (typeof(this.eventListeners[ev]) !== "undefined") {
+ this.eventListeners[ev].push(callback);
+ } else {
+ throw Error("Unknown event: " + ev);
+ }
+ }
- 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.className = "hues-visualiser";
- this.visualiser.height = "64";
- this.vCtx = this.visualiser.getContext("2d");
-
- this.soundManager = new SoundManager(this);
-
- this.soundManager.init().then(() => {
- if(!this.soundManager.locked && localStorage["skipPreloader"] == "on") {
- return null;
+ removeEventListener(ev, callback) {
+ ev = ev.toLowerCase();
+ if (typeof(this.eventListeners[ev]) !== "undefined") {
+ this.eventListeners[ev] = this.eventListeners[ev].filter(function(a) {
+ return (a !== callback);
+ });
} else {
- return this.resourceManager.getSizes(defaults.respacks);
+ throw Error("Unknown event: " + ev);
+ }
+ }
+
+ makePreloader(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);
+ }
+
+ resizeVisualiser() {
+ this.soundManager.initVisualiser(this.visualiser.width/2);
+ }
+
+ updateVisualiser() {
+ if(localStorage["visualiser"] != "on") {
+ return;
}
- }).then( sizes => {
- if(sizes === null) {
+
+ let logArrays = this.soundManager.getVisualiserData();
+ if(!logArrays) {
return;
}
+
+ this.vCtx.clearRect(0, 0, this.vCtx.canvas.width, this.vCtx.canvas.height);
- let size = sizes.reduce( (prev, curr) => {
- return typeof curr === 'number' ? prev + curr : null;
- }, 0);
- if(typeof size === 'number') {
- size = size.toFixed(1);
+ let gradient=this.vCtx.createLinearGradient(0,64,0,0);
+ if(this.invert) {
+ gradient.addColorStop(1,"rgba(20,20,20,0.6)");
+ gradient.addColorStop(0,"rgba(255,255,255,0.6)");
} else {
- size = '???';
+ gradient.addColorStop(1,"rgba(255,255,255,0.6)");
+ gradient.addColorStop(0,"rgba(20,20,20,0.6)");
}
+ this.vCtx.fillStyle = gradient;
- let warning = size + "MB of music/images.
" +
- "Flashing lights.
" +
- "Tap or click to start";
-
- if(!this.soundManager.locked) {
- warning += "
Skip this screen from Options";
+ let barWidth = 2;
+ let barHeight;
+ let x = 0;
+ for(let a = 0; a < logArrays.length; a++) {
+ let vals = logArrays[a];
+ for(let i = 0; i < vals.length; i++) {
+ let index = 0;
+ if(logArrays.length == 2 && a === 0) {
+ index = vals.length - i - 1;
+ } else {
+ index = i;
+ }
+ barHeight = vals[index]/4;
+
+ this.vCtx.fillRect(x,this.vCtx.canvas.height-barHeight,barWidth,barHeight);
+
+ x += barWidth;
+ }
}
- this.warning(warning);
- // Even if not locked, this steals clicks which is useful here
- return this.soundManager.unlock();
- }).then(() => {
- this.clearMessage();
- setInterval(this.loopCheck.bind(this), 1000);
- this.renderer = new HuesCanvas(this.root, this.soundManager.context, this);
- // Now all our objects are instantiated, we fire the updated settings
- this.settings.addEventListener("updated", this.settingsUpdated.bind(this));
- this.settingsUpdated();
- this.setColour(this.colourIndex);
- this.animationLoop();
-
- if(defaults.load) {
- return this.resourceManager.addAll(defaults.respacks, progress => {
- 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 {
- this.preloader.style.display = "none";
+ }
+
+ animationLoop() {
+ requestAnimationFrame(this.animationLoop.bind(this));
+ if(!this.soundManager.playing) {
+ this.callEventListeners("frame");
return;
}
- }).then(() => {
- this.preloader.classList.add("hues-preloader--loaded");
- if(defaults.firstImage) {
- this.setImageByName(defaults.firstImage);
- } else {
- this.setImage(0);
+ this.updateVisualiser();
+ let now = this.soundManager.currentTime();
+ this.callEventListeners("time", this.soundManager.clampedTime());
+ if(now >= 0 && this.doBuildup) {
+ this.currentSong.buildupPlayed = true;
}
- if(defaults.autoplay) {
- if(defaults.firstSong) {
- this.setSongByName(defaults.firstSong);
- } else {
- this.setSong(0);
- }
+ for(let beatTime = this.beatIndex * this.getBeatLength(); beatTime < now;
+ beatTime = ++this.beatIndex * this.getBeatLength()) {
+ let beat = this.getBeat(this.beatIndex);
+ this.beater(beat);
}
- }).catch(error => { // Comment this out to get proper stack traces
- this.error(error);
- });
-
- if(!defaults.disableKeyboard) {
- document.addEventListener("keydown", e => {
- e = e || window.event;
- if(e.defaultPrevented) {
- return true;
- }
- // Ignore modifiers so we don't steal other events
- if (e.altKey || e.ctrlKey || e.metaKey || e.shiftKey) {
- return true;
- }
- // If we've focused a text input, let the input go through!
- if((e.target.tagName.toLowerCase() == "input" && e.target.type == "text") ||
- e.target.contentEditable === "true") {
- return true;
+ this.callEventListeners("frame");
+ }
+
+ recalcBeatIndex(forcedNow) {
+ let now = typeof forcedNow === "number" ? forcedNow : this.soundManager.currentTime();
+ // getBeatLength isn't updated with the right beatIndex yet
+ this.beatIndex = Math.floor(now / (now < 0 ? this.buildLength : this.loopLength));
+ // beatIndex is NaN, abort
+ if(this.beatIndex != this.beatIndex) {
+ this.setInvert(false);
+ return;
+ }
+
+ // We should sync up to how many inverts there are
+ let build = this.currentSong.buildupRhythm;
+ let rhythm = this.currentSong.rhythm;
+ let mapSoFar;
+ if(this.beatIndex < 0) {
+ // Clamp to 0 in case we've juuust started
+ mapSoFar = build.slice(0, Math.max(this.beatIndex + build.length, 0));
+ } else {
+ // If the rhythm has an odd number of inverts, don't reset because it
+ // alternates on each loop anyway
+ if((rhythm.match(/i|I/g)||[]).length % 2) {
+ return;
}
- let key = e.keyCode || e.which;
- return this.keyHandler(key);
- });
+ mapSoFar = (build ? build : "") + rhythm.slice(0, this.beatIndex);
+ }
+ // If there's an odd amount of inverts thus far, invert our display
+ let invertCount = (mapSoFar.match(/i|I/g)||[]).length;
+ this.setInvert(invertCount % 2);
}
-}
-HuesCore.prototype.callEventListeners = function(ev) {
- let args = Array.prototype.slice.call(arguments, 1);
- this.eventListeners[ev].forEach(function(callback) {
- callback.apply(null, args);
- });
-};
-
-HuesCore.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);
+ getBeatIndex() {
+ if(!this.soundManager.playing) {
+ return 0;
+ } else if(this.beatIndex < 0) {
+ return this.beatIndex;
+ } else {
+ return this.beatIndex % this.currentSong.rhythm.length;
+ }
}
-};
-HuesCore.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);
+ getSafeBeatIndex() {
+ let index = this.getBeatIndex();
+ if(index < 0) {
+ return 0;
+ } else {
+ return index;
+ }
}
-};
-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);
-};
-
-HuesCore.prototype.updateVisualiser = function() {
- if(localStorage["visualiser"] != "on") {
- return;
+ blurUpdated(x, y) {
+ this.callEventListeners("blurupdate", x, y);
}
-
- let logArrays = this.soundManager.getVisualiserData();
- if(!logArrays) {
- return;
+
+ nextSong() {
+ let index = (this.songIndex + 1) % this.resourceManager.enabledSongs.length;
+ this.setSong(index);
}
- this.vCtx.clearRect(0, 0, this.vCtx.canvas.width, this.vCtx.canvas.height);
-
- let gradient=this.vCtx.createLinearGradient(0,64,0,0);
- if(this.invert) {
- gradient.addColorStop(1,"rgba(20,20,20,0.6)");
- gradient.addColorStop(0,"rgba(255,255,255,0.6)");
- } else {
- gradient.addColorStop(1,"rgba(255,255,255,0.6)");
- gradient.addColorStop(0,"rgba(20,20,20,0.6)");
+ previousSong() {
+ let index = ((this.songIndex - 1) + this.resourceManager.enabledSongs.length) % this.resourceManager.enabledSongs.length;
+ this.setSong(index);
}
- this.vCtx.fillStyle = gradient;
-
- let barWidth = 2;
- let barHeight;
- let x = 0;
- for(let a = 0; a < logArrays.length; a++) {
- let vals = logArrays[a];
- for(let i = 0; i < vals.length; i++) {
- let index = 0;
- if(logArrays.length == 2 && a === 0) {
- index = vals.length - i - 1;
- } else {
- index = i;
- }
- barHeight = vals[index]/4;
-
- this.vCtx.fillRect(x,this.vCtx.canvas.height-barHeight,barWidth,barHeight);
- x += barWidth;
+ setSongByName(name) {
+ let songs = this.resourceManager.enabledSongs;
+ for(let i = 0; i < songs.length; i++) {
+ if(songs[i].title == name) {
+ return this.setSong(i);
+ }
}
+ return this.setSong(0); // fallback
}
-};
-HuesCore.prototype.animationLoop = function() {
- requestAnimationFrame(this.animationLoop.bind(this));
- if(!this.soundManager.playing) {
- this.callEventListeners("frame");
- return;
- }
- this.updateVisualiser();
- let now = this.soundManager.currentTime();
- this.callEventListeners("time", this.soundManager.clampedTime());
- if(now >= 0 && this.doBuildup) {
- this.currentSong.buildupPlayed = true;
- }
- for(let beatTime = this.beatIndex * this.getBeatLength(); beatTime < now;
- beatTime = ++this.beatIndex * this.getBeatLength()) {
- let beat = this.getBeat(this.beatIndex);
- this.beater(beat);
- }
- this.callEventListeners("frame");
-};
-
-HuesCore.prototype.recalcBeatIndex = function(forcedNow) {
- let now = typeof forcedNow === "number" ? forcedNow : this.soundManager.currentTime();
- // getBeatLength isn't updated with the right beatIndex yet
- this.beatIndex = Math.floor(now / (now < 0 ? this.buildLength : this.loopLength));
- // beatIndex is NaN, abort
- if(this.beatIndex != this.beatIndex) {
- this.setInvert(false);
- return;
+ /* To set songs via reference instead of index - used in HuesEditor */
+ setSongOject(song) {
+ for(let i = 0; i < this.resourceManager.enabledSongs.length; i++) {
+ if(this.resourceManager.enabledSongs[i] === song) {
+ return this.setSong(i);
+ }
+ }
}
-
- // We should sync up to how many inverts there are
- let build = this.currentSong.buildupRhythm;
- let rhythm = this.currentSong.rhythm;
- let mapSoFar;
- if(this.beatIndex < 0) {
- // Clamp to 0 in case we've juuust started
- mapSoFar = build.slice(0, Math.max(this.beatIndex + build.length, 0));
- } else {
- // If the rhythm has an odd number of inverts, don't reset because it
- // alternates on each loop anyway
- if((rhythm.match(/i|I/g)||[]).length % 2) {
+
+ setSong(index, leaveArray) {
+ if(this.currentSong == this.resourceManager.enabledSongs[index]) {
return;
}
- mapSoFar = (build ? build : "") + rhythm.slice(0, this.beatIndex);
- }
- // If there's an odd amount of inverts thus far, invert our display
- let invertCount = (mapSoFar.match(/i|I/g)||[]).length;
- this.setInvert(invertCount % 2);
-};
-
-HuesCore.prototype.getBeatIndex = function() {
- if(!this.soundManager.playing) {
- return 0;
- } else if(this.beatIndex < 0) {
- return this.beatIndex;
- } else {
- return this.beatIndex % this.currentSong.rhythm.length;
+ // When not randoming, clear this
+ if(!leaveArray) {
+ this.lastSongArray = [];
+ }
+ this.lastSongArray.push(index);
+ this.songIndex = index;
+ this.currentSong = this.resourceManager.enabledSongs[this.songIndex];
+ if (this.currentSong === undefined) {
+ this.currentSong = {"name":"None", "title":"None", "rhythm":".", "source":null, "crc":"none", "sound":null, "enabled":true, "filename":"none"};
+ }
+ console.log("Next song:", this.songIndex, this.currentSong);
+ this.callEventListeners("newsong", this.currentSong);
+ this.loopCount = 0;
+ if (this.currentSong.buildup) {
+ switch (localStorage["playBuildups"]) {
+ case "off":
+ this.currentSong.buildupPlayed = true;
+ this.doBuildup = false;
+ break;
+ case "on":
+ this.currentSong.buildupPlayed = false;
+ this.doBuildup = true;
+ break;
+ case "once":
+ this.doBuildup = !this.currentSong.buildupPlayed;
+ break;
+ }
+ }
+ this.setInvert(false);
+ this.renderer.doBlackout();
+ return this.soundManager.playSong(this.currentSong, this.doBuildup)
+ .then(() => {
+ this.resetAudio();
+ this.fillBuildup();
+ this.callEventListeners("songstarted");
+ });
}
-};
-HuesCore.prototype.getSafeBeatIndex = function() {
- let index = this.getBeatIndex();
- if(index < 0) {
- return 0;
- } else {
- return index;
- }
-};
-
-HuesCore.prototype.blurUpdated = function(x, y) {
- this.callEventListeners("blurupdate", x, y);
-};
-
-HuesCore.prototype.nextSong = function() {
- let index = (this.songIndex + 1) % this.resourceManager.enabledSongs.length;
- this.setSong(index);
-};
-
-HuesCore.prototype.previousSong = function() {
- let index = ((this.songIndex - 1) + this.resourceManager.enabledSongs.length) % this.resourceManager.enabledSongs.length;
- this.setSong(index);
-};
-
-HuesCore.prototype.setSongByName = function(name) {
- let songs = this.resourceManager.enabledSongs;
- for(let i = 0; i < songs.length; i++) {
- if(songs[i].title == name) {
- return this.setSong(i);
+ updateBeatLength() {
+ this.loopLength = this.soundManager.loopLength / this.currentSong.rhythm.length;
+ if(this.currentSong.buildup) {
+ if (!this.currentSong.buildupRhythm) {
+ this.currentSong.buildupRhythm = ".";
+ }
+ this.buildLength = this.soundManager.buildLength / this.currentSong.buildupRhythm.length;
+ } else {
+ this.buildLength = -1;
}
}
- return this.setSong(0); // fallback
-};
-
-/* To set songs via reference instead of index - used in HuesEditor */
-HuesCore.prototype.setSongOject = function(song) {
- for(let i = 0; i < this.resourceManager.enabledSongs.length; i++) {
- if(this.resourceManager.enabledSongs[i] === song) {
- return this.setSong(i);
+
+ getBeatLength() {
+ if(this.beatIndex < 0) {
+ return this.buildLength;
+ } else {
+ return this.loopLength;
}
}
-};
-HuesCore.prototype.setSong = function(index, leaveArray) {
- if(this.currentSong == this.resourceManager.enabledSongs[index]) {
- return;
+ fillBuildup() {
+ // update loop length for flash style filling
+ this.updateBeatLength();
+ if(this.currentSong.buildup) {
+ if(this.currentSong.independentBuild) {
+ console.log("New behaviour - separate build/loop lengths");
+ // Do nothing
+ } else {
+ console.log("Flash behaviour - filling buildup");
+ let buildBeats = Math.floor(this.soundManager.buildLength / this.loopLength);
+ if(buildBeats < 1) {
+ buildBeats = 1;
+ }
+ while (this.currentSong.buildupRhythm.length < buildBeats) {
+ this.currentSong.buildupRhythm = this.currentSong.buildupRhythm + ".";
+ }
+ console.log("Buildup length:", buildBeats);
+ }
+ }
+ // update with a buildup of possibly different length
+ this.updateBeatLength();
+ // If we're in the build or loop this will adjust
+ // If we've lagged a bit, we'll miss the first beat. Rewind!
+ this.recalcBeatIndex(this.doBuildup ? -this.soundManager.buildLength : 0);
}
- // When not randoming, clear this
- if(!leaveArray) {
- this.lastSongArray = [];
+
+ randomSong() {
+ let songCount = this.resourceManager.enabledSongs.length;
+ let index=Math.floor((Math.random() * songCount));
+ if (songCount > 1 && (index == this.songIndex || this.lastSongArray.indexOf(index) != -1)) {
+ this.randomSong();
+ } else {
+ console.log("Randoming a song!");
+ this.setSong(index, true);
+ let noRepeat = Math.min(5, Math.floor(songCount / 2));
+ while (this.lastSongArray.length > noRepeat && noRepeat >= 0) {
+ this.lastSongArray.shift();
+ }
+ }
}
- this.lastSongArray.push(index);
- this.songIndex = index;
- this.currentSong = this.resourceManager.enabledSongs[this.songIndex];
- if (this.currentSong === undefined) {
- this.currentSong = {"name":"None", "title":"None", "rhythm":".", "source":null, "crc":"none", "sound":null, "enabled":true, "filename":"none"};
+
+ /* This is its own function because requestAnimationFrame is called very very
+ rarely when the tab is backgrounded. As autoSong is often used to chill with
+ music, it's important to keep checking the loop so songs don't go for too
+ long. */
+ loopCheck() {
+ if(Math.floor(this.soundManager.currentTime() / this.soundManager.loopLength) > this.loopCount) {
+ this.onLoop();
+ }
}
- console.log("Next song:", this.songIndex, this.currentSong);
- this.callEventListeners("newsong", this.currentSong);
- this.loopCount = 0;
- if (this.currentSong.buildup) {
- switch (localStorage["playBuildups"]) {
- case "off":
- this.currentSong.buildupPlayed = true;
- this.doBuildup = false;
- break;
- case "on":
- this.currentSong.buildupPlayed = false;
- this.doBuildup = true;
+
+ onLoop() {
+ this.loopCount++;
+ switch (localStorage["autoSong"]) {
+ case "loop":
+ console.log("Checking loops");
+ if (this.loopCount >= localStorage["autoSongDelay"]) {
+ this.doAutoSong();
+ }
break;
- case "once":
- this.doBuildup = !this.currentSong.buildupPlayed;
+ case "time":
+ console.log("Checking times");
+ if (this.soundManager.loopLength * this.loopCount >= localStorage["autoSongDelay"] * 60) {
+ this.doAutoSong();
+ }
break;
}
}
- this.setInvert(false);
- this.renderer.doBlackout();
- return this.soundManager.playSong(this.currentSong, this.doBuildup)
- .then(() => {
- this.resetAudio();
- this.fillBuildup();
- this.callEventListeners("songstarted");
- });
-};
-
-HuesCore.prototype.updateBeatLength = function() {
- this.loopLength = this.soundManager.loopLength / this.currentSong.rhythm.length;
- if(this.currentSong.buildup) {
- if (!this.currentSong.buildupRhythm) {
- this.currentSong.buildupRhythm = ".";
+
+ doAutoSong() {
+ let func = null;
+ if(localStorage["autoSongShuffle"] == "on") {
+ func = this.randomSong;
+ } else {
+ func = this.nextSong;
+ }
+ if(localStorage["autoSongFadeout"] == "on") {
+ this.soundManager.fadeOut(() => {
+ func.call(this);
+ });
+ } else {
+ func.call(this);
}
- this.buildLength = this.soundManager.buildLength / this.currentSong.buildupRhythm.length;
- } else {
- this.buildLength = -1;
}
-};
-HuesCore.prototype.getBeatLength = function() {
- if(this.beatIndex < 0) {
- return this.buildLength;
- } else {
- return this.loopLength;
- }
-};
-
-HuesCore.prototype.fillBuildup = function() {
- // update loop length for flash style filling
- this.updateBeatLength();
- if(this.currentSong.buildup) {
- if(this.currentSong.independentBuild) {
- console.log("New behaviour - separate build/loop lengths");
- // Do nothing
- } else {
- console.log("Flash behaviour - filling buildup");
- let buildBeats = Math.floor(this.soundManager.buildLength / this.loopLength);
- if(buildBeats < 1) {
- buildBeats = 1;
- }
- while (this.currentSong.buildupRhythm.length < buildBeats) {
- this.currentSong.buildupRhythm = this.currentSong.buildupRhythm + ".";
- }
- console.log("Buildup length:", buildBeats);
+ songDataUpdated() {
+ if (this.currentSong) {
+ this.callEventListeners("newsong", this.currentSong);
+ this.callEventListeners("newimage", this.currentImage);
}
}
- // update with a buildup of possibly different length
- this.updateBeatLength();
- // If we're in the build or loop this will adjust
- // If we've lagged a bit, we'll miss the first beat. Rewind!
- this.recalcBeatIndex(this.doBuildup ? -this.soundManager.buildLength : 0);
-};
-
-HuesCore.prototype.randomSong = function() {
- let songCount = this.resourceManager.enabledSongs.length;
- let index=Math.floor((Math.random() * songCount));
- if (songCount > 1 && (index == this.songIndex || this.lastSongArray.indexOf(index) != -1)) {
- this.randomSong();
- } else {
- console.log("Randoming a song!");
- this.setSong(index, true);
- let noRepeat = Math.min(5, Math.floor(songCount / 2));
- while (this.lastSongArray.length > noRepeat && noRepeat >= 0) {
- this.lastSongArray.shift();
+
+ resetAudio() {
+ this.beatIndex = 0;
+ this.songDataUpdated();
+ if(localStorage["visualiser"] == "on") {
+ this.soundManager.initVisualiser(this.visualiser.width/2);
}
}
-};
-
-/* This is its own function because requestAnimationFrame is called very very
- rarely when the tab is backgrounded. As autoSong is often used to chill with
- music, it's important to keep checking the loop so songs don't go for too
- long. */
-HuesCore.prototype.loopCheck = function() {
- if(Math.floor(this.soundManager.currentTime() / this.soundManager.loopLength) > this.loopCount) {
- this.onLoop();
+
+ randomImage() {
+ if(localStorage["shuffleImages"] == "on") {
+ let len = this.resourceManager.enabledImages.length;
+ let index = Math.floor(Math.random() * len);
+ if ((index == this.imageIndex || this.lastImageArray.indexOf(index) != -1) && len > 1) {
+ this.randomImage();
+ } else {
+ this.setImage(index, true);
+ this.lastImageArray.push(index);
+ let cull = Math.min(20, Math.floor((len / 2)));
+ while (this.lastImageArray.length > cull && cull >= 0) {
+ this.lastImageArray.shift();
+ }
+ }
+ } else { // jk, not actually random
+ let img=(this.imageIndex + 1) % this.resourceManager.enabledImages.length;
+ this.setImage(img);
+ }
}
-};
-
-HuesCore.prototype.onLoop = function() {
- this.loopCount++;
- switch (localStorage["autoSong"]) {
- case "loop":
- console.log("Checking loops");
- if (this.loopCount >= localStorage["autoSongDelay"]) {
- this.doAutoSong();
+
+ setImage(index, leaveArray) {
+ // If there are no images, this corrects NaN to 0
+ this.imageIndex = index ? index : 0;
+ let img=this.resourceManager.enabledImages[this.imageIndex];
+ if (img == this.currentImage && img !== null) {
+ return;
+ }
+ // When not randoming, clear this
+ if(!leaveArray) {
+ this.lastImageArray = [];
}
- break;
- case "time":
- console.log("Checking times");
- if (this.soundManager.loopLength * this.loopCount >= localStorage["autoSongDelay"] * 60) {
- this.doAutoSong();
+ if (img) {
+ this.currentImage = img;
+ } else {
+ this.currentImage = {"name":"None", "fullname":"None", "align":"center", "bitmap":null, "source":null, "enabled":true};
+ this.imageIndex = -1;
+ this.lastImageArray = [];
}
- break;
+ this.callEventListeners("newimage", this.currentImage);
}
-};
-
-HuesCore.prototype.doAutoSong = function() {
- let func = null;
- if(localStorage["autoSongShuffle"] == "on") {
- func = this.randomSong;
- } else {
- func = this.nextSong;
+
+ setImageByName(name) {
+ let images = this.resourceManager.enabledImages;
+ for(let i = 0; i < images.length; i++) {
+ if(images[i].name == name || images[i].fullname == name) {
+ this.setImage(i);
+ return;
+ }
+ }
+ this.setImage(0); // fallback
}
- if(localStorage["autoSongFadeout"] == "on") {
- this.soundManager.fadeOut(() => {
- func.call(this);
- });
- } else {
- func.call(this);
+
+ nextImage() {
+ this.setIsFullAuto(false);
+ let img=(this.imageIndex + 1) % this.resourceManager.enabledImages.length;
+ this.setImage(img);
}
-};
-HuesCore.prototype.songDataUpdated = function() {
- if (this.currentSong) {
- this.callEventListeners("newsong", this.currentSong);
- this.callEventListeners("newimage", this.currentImage);
+ previousImage() {
+ this.setIsFullAuto(false);
+ let img=((this.imageIndex - 1) + this.resourceManager.enabledImages.length) % this.resourceManager.enabledImages.length;
+ this.setImage(img);
}
-};
-HuesCore.prototype.resetAudio = function() {
- this.beatIndex = 0;
- this.songDataUpdated();
- if(localStorage["visualiser"] == "on") {
- this.soundManager.initVisualiser(this.visualiser.width/2);
+ randomColourIndex() {
+ let index=Math.floor((Math.random() * this.colours.length));
+ if (index == this.colourIndex) {
+ return this.randomColourIndex();
+ }
+ return index;
+ }
+
+ randomColour(isFade) {
+ let index=this.randomColourIndex();
+ this.setColour(index, isFade);
}
-};
-
-HuesCore.prototype.randomImage = function() {
- if(localStorage["shuffleImages"] == "on") {
- let len = this.resourceManager.enabledImages.length;
- let index = Math.floor(Math.random() * len);
- if ((index == this.imageIndex || this.lastImageArray.indexOf(index) != -1) && len > 1) {
- this.randomImage();
+
+ setColour(index, isFade) {
+ this.colourIndex = index;
+ let colour = this.colours[this.colourIndex];
+ this.callEventListeners("newcolour", colour, isFade);
+ }
+
+ getBeat(index) {
+ if(index < 0) {
+ return this.currentSong.buildupRhythm[this.currentSong.buildupRhythm.length+index];
} else {
- this.setImage(index, true);
- this.lastImageArray.push(index);
- let cull = Math.min(20, Math.floor((len / 2)));
- while (this.lastImageArray.length > cull && cull >= 0) {
- this.lastImageArray.shift();
+ return this.currentSong.rhythm[index % this.currentSong.rhythm.length];
+ }
+ }
+
+ beater(beat) {
+ this.callEventListeners("beat", this.getBeatString(), this.getBeatIndex());
+ switch(beat) {
+ case 'X':
+ case 'x':
+ this.renderer.doYBlur();
+ break;
+ case 'O':
+ case 'o':
+ this.renderer.doXBlur();
+ break;
+ case ')':
+ this.renderer.doTrippyX();
+ break;
+ case '(':
+ this.renderer.doTrippyY();
+ break;
+ case '+':
+ this.renderer.doXBlur();
+ this.renderer.doBlackout();
+ break;
+ case '¤':
+ this.renderer.doXBlur();
+ this.renderer.doBlackout(true);
+ break;
+ case '|':
+ this.renderer.doShortBlackout(this.getBeatLength());
+ this.randomColour();
+ break;
+ case ':':
+ this.randomColour();
+ break;
+ case '*':
+ if(this.isFullAuto) {
+ this.randomImage();
+ }
+ break;
+ case '=':
+ if(this.isFullAuto) {
+ this.randomImage();
+ }
+ /* falls through */
+ case '~':
+ // case: fade in build, not in rhythm. Must max out fade timer.
+ let maxSearch = this.currentSong.rhythm.length;
+ if(this.beatIndex < 0) {
+ maxSearch -= this.beatIndex;
+ }
+ let fadeLen;
+ for (fadeLen = 1; fadeLen <= maxSearch; fadeLen++) {
+ if (this.getBeat(fadeLen + this.beatIndex) != ".") {
+ break;
+ }
+ }
+ this.renderer.doColourFade((fadeLen * this.getBeatLength()) / this.soundManager.playbackRate);
+ this.randomColour(true);
+ break;
+ case 'I':
+ if (this.isFullAuto) {
+ this.randomImage();
+ }
+ /* falls through */
+ case 'i':
+ this.toggleInvert();
+ break;
+ }
+ if ([".", "+", "|", "¤"].indexOf(beat) == -1) {
+ this.renderer.clearBlackout();
}
+ if([".", "+", "¤", ":", "*", "X", "O", "~", "=", "i", "I"].indexOf(beat) == -1) {
+ this.randomColour();
+ if (this.isFullAuto) {
+ this.randomImage();
+ }
}
- } else { // jk, not actually random
- let img=(this.imageIndex + 1) % this.resourceManager.enabledImages.length;
- this.setImage(img);
}
-};
-
-HuesCore.prototype.setImage = function(index, leaveArray) {
- // If there are no images, this corrects NaN to 0
- this.imageIndex = index ? index : 0;
- let img=this.resourceManager.enabledImages[this.imageIndex];
- if (img == this.currentImage && img !== null) {
- return;
+
+ getBeatString(length) {
+ length = length ? length : 256;
+
+ let beatString = "";
+ let song = this.currentSong;
+ if (song) {
+ if(this.beatIndex < 0) {
+ beatString = song.buildupRhythm.slice(
+ song.buildupRhythm.length + this.beatIndex);
+ } else {
+ beatString = song.rhythm.slice(this.beatIndex % song.rhythm.length);
+ }
+ while (beatString.length < length) {
+ beatString += song.rhythm;
+ }
+ }
+
+ return beatString;
}
- // When not randoming, clear this
- if(!leaveArray) {
- this.lastImageArray = [];
+
+ setIsFullAuto(auto) {
+ this.isFullAuto = auto;
+ if (this.userInterface) {
+ this.callEventListeners("newmode", this.isFullAuto);
+ }
}
- if (img) {
- this.currentImage = img;
- } else {
- this.currentImage = {"name":"None", "fullname":"None", "align":"center", "bitmap":null, "source":null, "enabled":true};
- this.imageIndex = -1;
- this.lastImageArray = [];
+
+ toggleFullAuto() {
+ this.setIsFullAuto(!this.isFullAuto);
}
- this.callEventListeners("newimage", this.currentImage);
-};
-
-HuesCore.prototype.setImageByName = function(name) {
- let images = this.resourceManager.enabledImages;
- for(let i = 0; i < images.length; i++) {
- if(images[i].name == name || images[i].fullname == name) {
- this.setImage(i);
- return;
- }
+
+ setInvert(invert) {
+ this.invert = !!invert;
+ this.callEventListeners("invert", invert);
}
- this.setImage(0); // fallback
-};
-
-HuesCore.prototype.nextImage = function() {
- this.setIsFullAuto(false);
- let img=(this.imageIndex + 1) % this.resourceManager.enabledImages.length;
- this.setImage(img);
-};
-
-HuesCore.prototype.previousImage = function() {
- this.setIsFullAuto(false);
- let img=((this.imageIndex - 1) + this.resourceManager.enabledImages.length) % this.resourceManager.enabledImages.length;
- this.setImage(img);
-};
-
-HuesCore.prototype.randomColourIndex = function() {
- let index=Math.floor((Math.random() * this.colours.length));
- if (index == this.colourIndex) {
- return this.randomColourIndex();
+
+ toggleInvert() {
+ this.setInvert(!this.invert);
+ }
+
+ respackLoaded() {
+ this.init();
}
- return index;
-};
-
-HuesCore.prototype.randomColour = function(isFade) {
- let index=this.randomColourIndex();
- this.setColour(index, isFade);
-};
-
-HuesCore.prototype.setColour = function(index, isFade) {
- this.colourIndex = index;
- let colour = this.colours[this.colourIndex];
- this.callEventListeners("newcolour", colour, isFade);
-};
-
-HuesCore.prototype.getBeat = function(index) {
- if(index < 0) {
- return this.currentSong.buildupRhythm[this.currentSong.buildupRhythm.length+index];
- } else {
- return this.currentSong.rhythm[index % this.currentSong.rhythm.length];
+
+ changeUI(index) {
+ if (index >= 0 && this.uiArray.length > index && this.userInterface != this.uiArray[index]) {
+ this.hideLists();
+ if(this.userInterface) {
+ this.userInterface.disconnect();
+ }
+ this.userInterface = this.uiArray[index];
+ this.userInterface.connectCore(this);
+ this.callEventListeners("newmode", this.isFullAuto);
+ this.callEventListeners("newsong", this.currentSong);
+ this.callEventListeners("newimage", this.currentImage);
+ this.callEventListeners("newcolour", this.colours[this.colourIndex], false);
+ this.callEventListeners("beat", this.getBeatString(), this.getBeatIndex());
+ this.callEventListeners("invert", this.invert);
+ }
}
-};
-
-HuesCore.prototype.beater = function(beat) {
- this.callEventListeners("beat", this.getBeatString(), this.getBeatIndex());
- switch(beat) {
- case 'X':
- case 'x':
- this.renderer.doYBlur();
+
+ settingsUpdated() {
+ this.callEventListeners("settingsupdated");
+ switch (localStorage["currentUI"]) {
+ case "retro":
+ this.changeUI(0);
break;
- case 'O':
- case 'o':
- this.renderer.doXBlur();
+ case "v4.20":
+ this.changeUI(1);
break;
- case ')':
- this.renderer.doTrippyX();
+ case "modern":
+ this.changeUI(2);
break;
- case '(':
- this.renderer.doTrippyY();
+ case "xmas":
+ this.changeUI(3);
break;
- case '+':
- this.renderer.doXBlur();
- this.renderer.doBlackout();
+ case "hlwn":
+ this.changeUI(4);
break;
- case '¤':
- this.renderer.doXBlur();
- this.renderer.doBlackout(true);
+ case "mini":
+ this.changeUI(5);
break;
- case '|':
- this.renderer.doShortBlackout(this.getBeatLength());
- this.randomColour();
+ }
+ switch (localStorage["colourSet"]) {
+ case "normal":
+ this.colours = HuesCore.oldColours;
break;
- case ':':
- this.randomColour();
+ case "pastel":
+ this.colours = HuesCore.pastelColours;
break;
- case '*':
- if(this.isFullAuto) {
- this.randomImage();
- }
+ case "v4.20":
+ this.colours = HuesCore.weedColours;
break;
- case '=':
- if(this.isFullAuto) {
- this.randomImage();
- }
- /* falls through */
- case '~':
- // case: fade in build, not in rhythm. Must max out fade timer.
- let maxSearch = this.currentSong.rhythm.length;
- if(this.beatIndex < 0) {
- maxSearch -= this.beatIndex;
- }
- let fadeLen;
- for (fadeLen = 1; fadeLen <= maxSearch; fadeLen++) {
- if (this.getBeat(fadeLen + this.beatIndex) != ".") {
- break;
- }
+ }
+ switch (localStorage["blackoutUI"]) {
+ case "off":
+ this.userInterface.show();
+ break;
+ case "on":
+ if(this.renderer.blackout) {
+ this.userInterface.hide();
}
- this.renderer.doColourFade((fadeLen * this.getBeatLength()) / this.soundManager.playbackRate);
- this.randomColour(true);
break;
- case 'I':
- if (this.isFullAuto) {
- this.randomImage();
+ }
+ switch (localStorage["visualiser"]) {
+ case "off":
+ this.visualiser.classList.add("hidden");
+ break;
+ case "on":
+ this.visualiser.classList.remove("hidden");
+ if(!this.soundManager.vReady) {
+ this.soundManager.initVisualiser(this.visualiser.width/2);
}
- /* falls through */
- case 'i':
- this.toggleInvert();
break;
}
- if ([".", "+", "|", "¤"].indexOf(beat) == -1) {
- this.renderer.clearBlackout();
+ if (this.autoSong == "off" && localStorage["autoSong"] != "off") {
+ console.log("Resetting loopCount since AutoSong was enabled");
+ this.loopCount = 0;
}
- if([".", "+", "¤", ":", "*", "X", "O", "~", "=", "i", "I"].indexOf(beat) == -1) {
- this.randomColour();
- if (this.isFullAuto) {
- this.randomImage();
- }
+ this.autoSong = localStorage["autoSong"];
}
-};
-HuesCore.prototype.getBeatString = function(length) {
- length = length ? length : 256;
+ enabledChanged() {
+ this.resourceManager.rebuildEnabled();
+ }
- let beatString = "";
- let song = this.currentSong;
- if (song) {
- if(this.beatIndex < 0) {
- beatString = song.buildupRhythm.slice(
- song.buildupRhythm.length + this.beatIndex);
- } else {
- beatString = song.rhythm.slice(this.beatIndex % song.rhythm.length);
- }
- while (beatString.length < length) {
- beatString += song.rhythm;
- }
+ hideLists() {
+ this.resourceManager.hideLists();
}
- return beatString;
-};
+ toggleSongList() {
+ this.window.hide();
+ this.resourceManager.toggleSongList();
+ }
-HuesCore.prototype.setIsFullAuto = function(auto) {
- this.isFullAuto = auto;
- if (this.userInterface) {
- this.callEventListeners("newmode", this.isFullAuto);
+ toggleImageList() {
+ this.window.hide();
+ this.resourceManager.toggleImageList();
}
-};
-
-HuesCore.prototype.toggleFullAuto = function() {
- this.setIsFullAuto(!this.isFullAuto);
-};
-
-HuesCore.prototype.setInvert = function(invert) {
- this.invert = !!invert;
- this.callEventListeners("invert", invert);
-};
-
-HuesCore.prototype.toggleInvert = function() {
- this.setInvert(!this.invert);
-};
-
-HuesCore.prototype.respackLoaded = function() {
- this.init();
-};
-
-HuesCore.prototype.changeUI = function(index) {
- if (index >= 0 && this.uiArray.length > index && this.userInterface != this.uiArray[index]) {
- this.hideLists();
- if(this.userInterface) {
- this.userInterface.disconnect();
+
+ openSongSource() {
+ if (this.currentSong && this.currentSong.source) {
+ window.open(this.currentSong.source,'_blank');
}
- this.userInterface = this.uiArray[index];
- this.userInterface.connectCore(this);
- this.callEventListeners("newmode", this.isFullAuto);
- this.callEventListeners("newsong", this.currentSong);
- this.callEventListeners("newimage", this.currentImage);
- this.callEventListeners("newcolour", this.colours[this.colourIndex], false);
- this.callEventListeners("beat", this.getBeatString(), this.getBeatIndex());
- this.callEventListeners("invert", this.invert);
- }
-};
-
-HuesCore.prototype.settingsUpdated = function() {
- this.callEventListeners("settingsupdated");
- switch (localStorage["currentUI"]) {
- case "retro":
- this.changeUI(0);
- break;
- case "v4.20":
- this.changeUI(1);
- break;
- case "modern":
- this.changeUI(2);
- break;
- case "xmas":
- this.changeUI(3);
- break;
- case "hlwn":
- this.changeUI(4);
- break;
- case "mini":
- this.changeUI(5);
- break;
}
- switch (localStorage["colourSet"]) {
- case "normal":
- this.colours = this.oldColours;
- break;
- case "pastel":
- this.colours = this.pastelColours;
- break;
- case "v4.20":
- this.colours = this.weedColours;
- break;
- }
- switch (localStorage["blackoutUI"]) {
- case "off":
- this.userInterface.show();
- break;
- case "on":
- if(this.renderer.blackout) {
- this.userInterface.hide();
+
+ openImageSource() {
+ if (this.currentImage && this.currentImage.source) {
+ window.open(this.currentImage.source,'_blank');
}
- break;
}
- switch (localStorage["visualiser"]) {
- case "off":
- this.visualiser.classList.add("hidden");
- break;
- case "on":
- this.visualiser.classList.remove("hidden");
- if(!this.soundManager.vReady) {
- this.soundManager.initVisualiser(this.visualiser.width/2);
+
+ keyHandler(key) {
+ switch (key) {
+ case 37: // LEFT
+ this.previousImage();
+ break;
+ case 39: // RIGHT
+ this.nextImage();
+ break;
+ case 38: // UP
+ this.nextSong();
+ break;
+ case 40: // DOWN
+ this.previousSong();
+ break;
+ case 70: // F
+ this.setIsFullAuto(!this.isFullAuto);
+ break;
+ case 109: // NUMPAD_SUBTRACT
+ case 189: // MINUS
+ case 173: // MINUS, legacy
+ this.soundManager.decreaseVolume();
+ break;
+ case 107: // NUMPAD_ADD
+ case 187: // EQUAL
+ case 61: // EQUAL, legacy
+ this.soundManager.increaseVolume();
+ break;
+ case 66: // B
+ this.soundManager.seek(-this.soundManager.buildLength);
+ break;
+ case 77: // M
+ this.soundManager.toggleMute();
+ break;
+ case 72: // H
+ this.userInterface.toggleHide();
+ break;
+ case 82: // R
+ this.window.selectTab("RESOURCES");
+ break;
+ case 69: // E
+ this.window.selectTab("EDITOR");
+ break;
+ case 79: // O
+ this.window.selectTab("OPTIONS");
+ break;
+ case 73: // I
+ this.window.selectTab("INFO");
+ break;
+ case 49: // NUMBER_1
+ this.settings.set("currentUI", "retro");
+ break;
+ case 50: // NUMBER_2
+ this.settings.set("currentUI", "v4.20");
+ break;
+ case 51: // NUMBER_3
+ this.settings.set("currentUI", "modern");
+ break;
+ case 52: // NUMBER_4
+ this.settings.set("currentUI", "xmas");
+ break;
+ 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;
+ case 83: // S
+ this.toggleSongList();
+ break;
+ case 87: // W
+ this.window.toggle();
+ break;
+ case 78: // N
+ this.randomSong();
+ break;
+ default:
+ return true;
}
- break;
- }
- if (this.autoSong == "off" && localStorage["autoSong"] != "off") {
- console.log("Resetting loopCount since AutoSong was enabled");
- this.loopCount = 0;
+ return false;
}
- this.autoSong = localStorage["autoSong"];
-};
-
-HuesCore.prototype.enabledChanged = function() {
- this.resourceManager.rebuildEnabled();
-};
-
-HuesCore.prototype.hideLists = function() {
- this.resourceManager.hideLists();
-};
-
-HuesCore.prototype.toggleSongList = function() {
- this.window.hide();
- this.resourceManager.toggleSongList();
-};
-
-HuesCore.prototype.toggleImageList = function() {
- this.window.hide();
- this.resourceManager.toggleImageList();
-};
-
-HuesCore.prototype.openSongSource = function() {
- if (this.currentSong && this.currentSong.source) {
- window.open(this.currentSong.source,'_blank');
+
+ error(message) {
+ console.log(message);
+ this.preloadSubMsg.textContent = message;
+ this.preloadMsg.style.color = "#F00";
}
-};
-HuesCore.prototype.openImageSource = function() {
- if (this.currentImage && this.currentImage.source) {
- window.open(this.currentImage.source,'_blank');
+ warning(message) {
+ console.log(message);
+ this.preloadSubMsg.innerHTML = message;
+ this.preloadMsg.style.color = "#F93";
}
-};
-
-HuesCore.prototype.keyHandler = function(key) {
- switch (key) {
- case 37: // LEFT
- this.previousImage();
- break;
- case 39: // RIGHT
- this.nextImage();
- break;
- case 38: // UP
- this.nextSong();
- break;
- case 40: // DOWN
- this.previousSong();
- break;
- case 70: // F
- this.setIsFullAuto(!this.isFullAuto);
- break;
- case 109: // NUMPAD_SUBTRACT
- case 189: // MINUS
- case 173: // MINUS, legacy
- this.soundManager.decreaseVolume();
- break;
- case 107: // NUMPAD_ADD
- case 187: // EQUAL
- case 61: // EQUAL, legacy
- this.soundManager.increaseVolume();
- break;
- case 66: // B
- this.soundManager.seek(-this.soundManager.buildLength);
- break;
- case 77: // M
- this.soundManager.toggleMute();
- break;
- case 72: // H
- this.userInterface.toggleHide();
- break;
- case 82: // R
- this.window.selectTab("RESOURCES");
- break;
- case 69: // E
- this.window.selectTab("EDITOR");
- break;
- case 79: // O
- this.window.selectTab("OPTIONS");
- break;
- case 73: // I
- this.window.selectTab("INFO");
- break;
- case 49: // NUMBER_1
- this.settings.set("currentUI", "retro");
- break;
- case 50: // NUMBER_2
- this.settings.set("currentUI", "v4.20");
- break;
- case 51: // NUMBER_3
- this.settings.set("currentUI", "modern");
- break;
- case 52: // NUMBER_4
- this.settings.set("currentUI", "xmas");
- break;
- 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;
- case 83: // S
- this.toggleSongList();
- break;
- case 87: // W
- this.window.toggle();
- break;
- case 78: // N
- this.randomSong();
- break;
- default:
- return true;
+
+ clearMessage() {
+ this.preloadSubMsg.textContent = "";
+ this.preloadMsg.style.color = "";
}
- return false;
-};
-
-HuesCore.prototype.error = function(message) {
- console.log(message);
- this.preloadSubMsg.textContent = message;
- this.preloadMsg.style.color = "#F00";
-};
-
-HuesCore.prototype.warning = function(message) {
- console.log(message);
- this.preloadSubMsg.innerHTML = message;
- this.preloadMsg.style.color = "#F93";
-};
-
-HuesCore.prototype.clearMessage = function() {
- this.preloadSubMsg.textContent = "";
- this.preloadMsg.style.color = "";
-};
-
-HuesCore.prototype.oldColours =
+}
+
+HuesCore.oldColours =
[{'c': 0x000000, 'n': 'black'},
{'c': 0x550000, 'n': 'brick'},
{'c': 0xAA0000, 'n': 'crimson'},
@@ -1111,7 +1113,7 @@ HuesCore.prototype.oldColours =
{'c': 0x55FFFF, 'n': 'turquoise'},
{'c': 0xAAFFFF, 'n': 'powder'},
{'c': 0xFFFFFF, 'n': 'white'}];
-HuesCore.prototype.pastelColours =
+HuesCore.pastelColours =
[{'c': 0xCD4A4A, 'n': 'Mahogany'},
{'c': 0xFAE7B5, 'n': 'Banana Mania'},
{'c': 0x9F8170, 'n': 'Beaver'},
@@ -1176,7 +1178,7 @@ HuesCore.prototype.pastelColours =
{'c': 0xFCE883, 'n': 'Yellow'},
{'c': 0xC5E384, 'n': 'Yellow Green'},
{'c': 0xFFB653, 'n': 'Yellow Orange'}];
-HuesCore.prototype.weedColours =
+HuesCore.weedColours =
[{'c': 0x00FF00, 'n': 'Green'},
{'c': 0x5A6351, 'n': 'Lizard'},
{'c': 0x636F57, 'n': 'Cactus'},
diff --git a/src/js/HuesEditor.js b/src/js/HuesEditor.js
index 3e460ac..171a252 100644
--- a/src/js/HuesEditor.js
+++ b/src/js/HuesEditor.js
@@ -25,1379 +25,1381 @@
let WAVE_PIXELS_PER_SECOND = 100;
let WAVE_HEIGHT_PIXELS = 20;
-function HuesEditor(core, huesWin) {
- this.buildEditSize = 80; // pixels, including header
- this.buildEdit = null;
- this.loopEdit = null;
- this.editArea = null;
- this.wrapAt = 32;
-
- this.hilightWidth = 0;
- this.hilightHeight = 0;
-
- this.undoBuffer = [];
- this.redoBuffer = [];
- // Will be an array if many actions are performed in one undo
- this.batchUndoArray = null;
-
- // For rendering the waveform
- this.buildWave = null;
- this.loopWave = null;
- this.buildWaveBuff = null;
- this.loopWaveBuff = null;
- this.waveContext = null;
- this.waveCanvas = null;
-
- // for storing respacks created with "new"
- this.respack = null;
- // when we're actually following the playing song
- this.linked = false;
-
- this.core = core;
- 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);
+class HuesEditor {
+ constructor(core, huesWin) {
+ this.buildEditSize = 80; // pixels, including header
+ this.buildEdit = null;
+ this.loopEdit = null;
+ this.editArea = null;
+ this.wrapAt = 32;
+
+ this.hilightWidth = 0;
+ this.hilightHeight = 0;
+
+ this.undoBuffer = [];
+ this.redoBuffer = [];
+ // Will be an array if many actions are performed in one undo
+ this.batchUndoArray = null;
+
+ // For rendering the waveform
+ this.buildWave = null;
+ this.loopWave = null;
+ this.buildWaveBuff = null;
+ this.loopWaveBuff = null;
+ this.waveContext = null;
+ this.waveCanvas = null;
+
+ // for storing respacks created with "new"
+ this.respack = null;
+ // when we're actually following the playing song
+ this.linked = false;
+
+ this.core = core;
+ 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.className = "editor__title-buttons";
- this.root.appendChild(titleButtons);
- this.saveBtn = this.createButton("Save XML", titleButtons, true);
- this.saveBtn.addEventListener("click", this.saveXML.bind(this));
- this.copyBtn = this.createButton("Copy XML", titleButtons, true);
- this.copyBtn.addEventListener("click", this.copyXML.bind(this));
- this.undoBtn = this.createButton("Undo", titleButtons, true);
- this.undoBtn.addEventListener("click", this.undo.bind(this));
- this.redoBtn = this.createButton("Redo", titleButtons, true);
- this.redoBtn.addEventListener("click", this.redo.bind(this));
- let help = this.createButton("Help?", titleButtons);
- help.style.backgroundColor = "rgba(0,160,0,0.3)";
- help.addEventListener("click", () => {
- window.open("https://github.com/mon/0x40-web/tree/master/docs/Editor.md", '_blank');
- });
-
- this.statusMsg = document.createElement("span");
- this.statusMsg.className = "editor__status-msg";
- titleButtons.appendChild(this.statusMsg);
-
- this.topBar = document.createElement("div");
- this.topBar.className = "editor__top-bar";
- this.root.appendChild(this.topBar);
-
- this.uiCreateInfo();
- this.uiCreateImport();
- this.root.appendChild(document.createElement("hr"));
- this.uiCreateEditArea();
- this.uiCreateControls();
- this.uiCreateVisualiser();
-
- document.addEventListener("keydown", e => {
- e = e || window.event;
- if(e.defaultPrevented) {
- return true;
- }
- let key = e.keyCode || e.which;
+ initUI() {
+ this.root = document.createElement("div");
+ this.root.className = "editor";
+ let titleButtons = document.createElement("div");
+ 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));
+ this.copyBtn = this.createButton("Copy XML", titleButtons, true);
+ this.copyBtn.addEventListener("click", this.copyXML.bind(this));
+ this.undoBtn = this.createButton("Undo", titleButtons, true);
+ this.undoBtn.addEventListener("click", this.undo.bind(this));
+ this.redoBtn = this.createButton("Redo", titleButtons, true);
+ this.redoBtn.addEventListener("click", this.redo.bind(this));
+ let help = this.createButton("Help?", titleButtons);
+ help.style.backgroundColor = "rgba(0,160,0,0.3)";
+ help.addEventListener("click", () => {
+ window.open("https://github.com/mon/0x40-web/tree/master/docs/Editor.md", '_blank');
+ });
+
+ this.statusMsg = document.createElement("span");
+ this.statusMsg.className = "editor__status-msg";
+ titleButtons.appendChild(this.statusMsg);
- if (e.ctrlKey) {
- if(key == 90) { // Z
- this.undo();
- } else if(key == 89) { // Y
- this.redo();
+ this.topBar = document.createElement("div");
+ this.topBar.className = "editor__top-bar";
+ this.root.appendChild(this.topBar);
+
+ this.uiCreateInfo();
+ this.uiCreateImport();
+ this.root.appendChild(document.createElement("hr"));
+ this.uiCreateEditArea();
+ this.uiCreateControls();
+ this.uiCreateVisualiser();
+
+ document.addEventListener("keydown", e => {
+ e = e || window.event;
+ if(e.defaultPrevented) {
+ return true;
}
- if(key == 90 || key == 89) {
- e.preventDefault();
- return false;
+ let key = e.keyCode || e.which;
+
+ if (e.ctrlKey) {
+ if(key == 90) { // Z
+ this.undo();
+ } else if(key == 89) { // Y
+ this.redo();
+ }
+ if(key == 90 || key == 89) {
+ e.preventDefault();
+ return false;
+ }
}
- }
- return true;
- });
-
- window.addEventListener('resize', this.resize.bind(this));
- // Fix Chrome rendering - redraw on tab load
- // 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) {
- this.root.style.height = (window.innerHeight - 200) + "px";
- let boxHeight = this.editArea.offsetHeight;
- let bHeadHeight = this.buildEdit._header.offsetHeight;
- let lHeadHeight = this.loopEdit._header.offsetHeight;
- let handleHeight = this.resizeHandle.offsetHeight;
- let minHeight = bHeadHeight;
- let maxHeight = boxHeight - handleHeight - lHeadHeight - bHeadHeight;
- let buildHeight = Math.min(maxHeight, Math.max(minHeight, this.buildEditSize - handleHeight));
- this.buildEdit.style.height = buildHeight + "px";
- this.buildEdit._box.style.height = (buildHeight - bHeadHeight) + "px";
- let loopHeight = maxHeight - buildHeight + lHeadHeight;
- this.loopEdit.style.height = loopHeight + "px";
- this.loopEdit._box.style.height = (loopHeight - lHeadHeight) + "px";
-
- // For window resizing down situation
- if(this.editArea.offsetHeight != boxHeight) {
+ return true;
+ });
+
+ window.addEventListener('resize', this.resize.bind(this));
+ // Fix Chrome rendering - redraw on tab load
+ // 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();
}
-
- // Resize the time lock
- this.timeLock.style.height = (buildHeight + handleHeight) + "px";
-
- // Save to fix Chrome rendering and to enable right click to seek
- // We only resize on a window resize event, not when dragging the handle
- if(!noHilightCalc) {
- let hilight = document.createElement("div");
- hilight.className = "beat-hilight";
- // Because clientWidth is rounded, we need to take the average. 100 is plenty.
- let grid = "";
- // height goes to 99 because we always have 1 line
- for(let i = 0; i < 99; i++) {
- grid += "
";
- }
- // width
- for(let i = 0; i < 100; i++) {
- grid += " ";
+
+ resize(noHilightCalc) {
+ this.root.style.height = (window.innerHeight - 200) + "px";
+ let boxHeight = this.editArea.offsetHeight;
+ let bHeadHeight = this.buildEdit._header.offsetHeight;
+ let lHeadHeight = this.loopEdit._header.offsetHeight;
+ let handleHeight = this.resizeHandle.offsetHeight;
+ let minHeight = bHeadHeight;
+ let maxHeight = boxHeight - handleHeight - lHeadHeight - bHeadHeight;
+ let buildHeight = Math.min(maxHeight, Math.max(minHeight, this.buildEditSize - handleHeight));
+ this.buildEdit.style.height = buildHeight + "px";
+ this.buildEdit._box.style.height = (buildHeight - bHeadHeight) + "px";
+ let loopHeight = maxHeight - buildHeight + lHeadHeight;
+ this.loopEdit.style.height = loopHeight + "px";
+ this.loopEdit._box.style.height = (loopHeight - lHeadHeight) + "px";
+
+ // For window resizing down situation
+ if(this.editArea.offsetHeight != boxHeight) {
+ this.resize();
}
- hilight.innerHTML = grid;
- this.loopEdit.appendChild(hilight);
- this.hilightWidth = hilight.clientWidth / 100;
- this.hilightHeight = hilight.clientHeight / 100;
- this.loopEdit.removeChild(hilight);
- this.waveCanvas.width = this.waveCanvas.clientWidth;
+ // Resize the time lock
+ this.timeLock.style.height = (buildHeight + handleHeight) + "px";
+
+ // Save to fix Chrome rendering and to enable right click to seek
+ // We only resize on a window resize event, not when dragging the handle
+ if(!noHilightCalc) {
+ let hilight = document.createElement("div");
+ hilight.className = "beat-hilight";
+ // Because clientWidth is rounded, we need to take the average. 100 is plenty.
+ let grid = "";
+ // height goes to 99 because we always have 1 line
+ for(let i = 0; i < 99; i++) {
+ grid += "
";
+ }
+ // width
+ for(let i = 0; i < 100; i++) {
+ grid += " ";
+ }
+ hilight.innerHTML = grid;
+ this.loopEdit.appendChild(hilight);
+ this.hilightWidth = hilight.clientWidth / 100;
+ this.hilightHeight = hilight.clientHeight / 100;
+ this.loopEdit.removeChild(hilight);
+
+ this.waveCanvas.width = this.waveCanvas.clientWidth;
+ }
}
-};
-HuesEditor.prototype.getOther = function(editor) {
- return editor == this.loopEdit ? this.buildEdit : this.loopEdit;
-};
+ getOther(editor) {
+ return editor == this.loopEdit ? this.buildEdit : this.loopEdit;
+ }
-HuesEditor.prototype.onNewSong = function(song) {
- if(this.linked) {
- if(song == this.song) {
- // Because you can "edit current" before it loads
- this.updateInfo();
- this.updateWaveform();
- } else {
- this.linked = false;
- // Clear beat hilight
- this.buildEdit._hilight.innerHTML = "█";
- this.loopEdit._hilight.innerHTML = "█";
- 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);
+ onNewSong(song) {
+ if(this.linked) {
+ if(song == this.song) {
+ // Because you can "edit current" before it loads
+ this.updateInfo();
+ this.updateWaveform();
+ } else {
+ this.linked = false;
+ // Clear beat hilight
+ this.buildEdit._hilight.innerHTML = "█";
+ this.loopEdit._hilight.innerHTML = "█";
+ 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;
}
- } else if(song == this.song) { // went to another song then came back
- this.linked = true;
}
-};
-HuesEditor.prototype.onBeat = function(map, index) {
- if(!this.song || this.core.currentSong != this.song) {
- return;
- }
- let editor;
- if(index < 0) {
- index += this.core.currentSong.buildupRhythm.length;
- editor = this.buildEdit;
- this.loopEdit._hilight.className = "beat-hilight invisible";
- } else {
- editor = this.loopEdit;
- if(this.song.buildup) {
- this.buildEdit._hilight.className = "beat-hilight invisible";
+ onBeat(map, index) {
+ if(!this.song || this.core.currentSong != this.song) {
+ return;
+ }
+ let editor;
+ if(index < 0) {
+ index += this.core.currentSong.buildupRhythm.length;
+ editor = this.buildEdit;
+ this.loopEdit._hilight.className = "beat-hilight invisible";
+ } else {
+ editor = this.loopEdit;
+ if(this.song.buildup) {
+ this.buildEdit._hilight.className = "beat-hilight invisible";
+ }
}
- }
- editor._hilight.className = "beat-hilight";
- let offsetX = index % this.wrapAt;
- let offsetY = Math.floor(index / this.wrapAt);
- // Not computing width/height here due to Chrome bug
- editor._hilight.style.left = Math.floor(offsetX * this.hilightWidth) + "px";
- editor._hilight.style.top = Math.floor(offsetY * this.hilightHeight) + "px";
-};
-
-HuesEditor.prototype.reflow = function(editor, map) {
- if(!map) { // NOTHING TO SEE HERE
- editor._beatmap.textContent = "";
- editor._hilight.textContent = "[none]";
editor._hilight.className = "beat-hilight";
- editor._hilight.style.top = "0";
- editor._hilight.style.left = "0";
- editor._beatCount.textContent = "0 beats";
- return;
- } else {
- editor._hilight.innerHTML = "█";
- }
- editor._beatCount.textContent = map.length + " beats";
- // http://stackoverflow.com/a/27012001
- // if it's too long to wrap, scroll in the x direction
- let regex = new RegExp("(.{" + this.wrapAt + "})", "g");
- editor._beatmap.innerHTML = map.replace(regex, "$1
");
-};
-
-HuesEditor.prototype.updateInfo = function() {
- // Avoid a bunch of nested elses
- this.seekStart.classList.add("hues-button--disabled");
- this.seekLoop.classList.add("hues-button--disabled");
- this.saveBtn.classList.add("hues-button--disabled");
- this.copyBtn.classList.add("hues-button--disabled");
- this.buildEdit._removeBtn.classList.add("hues-button--disabled");
- this.loopEdit._removeBtn.classList.add("hues-button--disabled");
-
- if(!this.song) {
- return;
- }
-
- this.saveBtn.classList.remove("hues-button--disabled");
- this.copyBtn.classList.remove("hues-button--disabled");
-
- if(this.song.independentBuild) {
- this.timeLock._locker.innerHTML = "";
- this.timeLock.classList.add("unlocked");
- } else {
- this.timeLock._locker.innerHTML = "";
- this.timeLock.classList.remove("unlocked");
- }
- if(this.song.sound) {
- this.seekLoop.classList.remove("hues-button--disabled");
- this.loopEdit._removeBtn.classList.remove("hues-button--disabled");
- }
- if(this.song.buildup) {
- this.seekStart.classList.remove("hues-button--disabled");
- this.buildEdit._removeBtn.classList.remove("hues-button--disabled");
- }
-
- if(!this.linked) {
- return;
- }
-
- let loopLen = this.core.soundManager.loopLength;
- let buildLen = this.core.soundManager.buildLength;
- let beatLen = (loopLen / this.song.rhythm.length) * 1000;
-
- this.loopLen.textContent = loopLen.toFixed(2);
- this.buildLen.textContent = buildLen.toFixed(2);
- this.beatLen.textContent = beatLen.toFixed(2);
-};
-
-HuesEditor.prototype.loadAudio = function(editor) {
- if(editor._fileInput.files.length < 1) {
- return;
+ let offsetX = index % this.wrapAt;
+ let offsetY = Math.floor(index / this.wrapAt);
+ // Not computing width/height here due to Chrome bug
+ editor._hilight.style.left = Math.floor(offsetX * this.hilightWidth) + "px";
+ editor._hilight.style.top = Math.floor(offsetY * this.hilightHeight) + "px";
}
- // If first load, this makes fresh, gets the core synced up
- this.newSong(this.song);
-
- // Have we just added a build to a song with a rhythm, or vice versa?
- // If so, link their lengths
- let newlyLinked = !this.song[editor._sound] && !!this.song[this.getOther(editor)._sound];
-
- // Disable load button TODO
- let file = editor._fileInput.files[0];
-
- // load audio
- this.blobToArrayBuffer(file)
- .then(buffer => {
- // Is this buffer even decodable?
- let testSong = {test: buffer};
- return this.core.soundManager.loadBuffer(testSong, "test")
- // keep the buffer moving through the chain
- // remember it's been passed through to a worker, so we update the reference
- .then(() => {
- return testSong.test;
- });
- }).then(buffer => {
- this.song[editor._sound] = buffer;
- // Save filename for XML export
- let noExt = file.name.replace(/\.[^/.]+$/, "");
- if(editor._sound == "sound") {
- this.song.name = noExt;
+
+ reflow(editor, map) {
+ if(!map) { // NOTHING TO SEE HERE
+ editor._beatmap.textContent = "";
+ editor._hilight.textContent = "[none]";
+ editor._hilight.className = "beat-hilight";
+ editor._hilight.style.top = "0";
+ editor._hilight.style.left = "0";
+ editor._beatCount.textContent = "0 beats";
+ return;
} else {
- this.song.buildupName = noExt;
+ editor._hilight.innerHTML = "█";
}
- // make empty map if needed
- if(!this.getText(editor)) {
- this.setText(editor, "x...o...x...o...");
+ editor._beatCount.textContent = map.length + " beats";
+ // http://stackoverflow.com/a/27012001
+ // if it's too long to wrap, scroll in the x direction
+ let regex = new RegExp("(.{" + this.wrapAt + "})", "g");
+ editor._beatmap.innerHTML = map.replace(regex, "$1
");
+ }
+
+ updateInfo() {
+ // Avoid a bunch of nested elses
+ this.seekStart.classList.add("hues-button--disabled");
+ this.seekLoop.classList.add("hues-button--disabled");
+ this.saveBtn.classList.add("hues-button--disabled");
+ this.copyBtn.classList.add("hues-button--disabled");
+ this.buildEdit._removeBtn.classList.add("hues-button--disabled");
+ this.loopEdit._removeBtn.classList.add("hues-button--disabled");
+
+ if(!this.song) {
+ return;
+ }
+
+ this.saveBtn.classList.remove("hues-button--disabled");
+ this.copyBtn.classList.remove("hues-button--disabled");
+
+ if(this.song.independentBuild) {
+ this.timeLock._locker.innerHTML = "";
+ this.timeLock.classList.add("unlocked");
+ } else {
+ this.timeLock._locker.innerHTML = "";
+ this.timeLock.classList.remove("unlocked");
}
- // Do we have a loop to play?
if(this.song.sound) {
- // Force refresh
- return this.core.soundManager.playSong(this.song, true, true);
+ this.seekLoop.classList.remove("hues-button--disabled");
+ this.loopEdit._removeBtn.classList.remove("hues-button--disabled");
}
- }).then(() => {
- if(newlyLinked) {
- this.setIndependentBuild(false);
+ if(this.song.buildup) {
+ this.seekStart.classList.remove("hues-button--disabled");
+ this.buildEdit._removeBtn.classList.remove("hues-button--disabled");
}
- this.updateInfo();
- this.core.updateBeatLength();
- // We may have to go backwards in time
- this.core.recalcBeatIndex();
- this.updateWaveform();
- }).catch(error => {
- console.log(error);
- this.alert("Couldn't load song! Is it a LAME encoded MP3?");
- });
-};
-
-HuesEditor.prototype.removeAudio = function(editor) {
- if(!this.song) {
- return;
+
+ if(!this.linked) {
+ return;
+ }
+
+ let loopLen = this.core.soundManager.loopLength;
+ let buildLen = this.core.soundManager.buildLength;
+ let beatLen = (loopLen / this.song.rhythm.length) * 1000;
+
+ this.loopLen.textContent = loopLen.toFixed(2);
+ this.buildLen.textContent = buildLen.toFixed(2);
+ this.beatLen.textContent = beatLen.toFixed(2);
}
-
- this.song[editor._sound] = null;
- this.song[editor._rhythm] = "";
- this.setIndependentBuild(true);
- this.reflow(editor, "");
- // Is the loop playable?
- if(this.song.sound && this.linked) {
- this.core.soundManager.playSong(this.song, true, true)
- .then(() => {
+
+ loadAudio(editor) {
+ if(editor._fileInput.files.length < 1) {
+ return;
+ }
+ // If first load, this makes fresh, gets the core synced up
+ this.newSong(this.song);
+
+ // Have we just added a build to a song with a rhythm, or vice versa?
+ // If so, link their lengths
+ let newlyLinked = !this.song[editor._sound] && !!this.song[this.getOther(editor)._sound];
+
+ // Disable load button TODO
+ let file = editor._fileInput.files[0];
+
+ // load audio
+ this.blobToArrayBuffer(file)
+ .then(buffer => {
+ // Is this buffer even decodable?
+ let testSong = {test: buffer};
+ return this.core.soundManager.loadBuffer(testSong, "test")
+ // keep the buffer moving through the chain
+ // remember it's been passed through to a worker, so we update the reference
+ .then(() => {
+ return testSong.test;
+ });
+ }).then(buffer => {
+ this.song[editor._sound] = buffer;
+ // Save filename for XML export
+ let noExt = file.name.replace(/\.[^/.]+$/, "");
+ if(editor._sound == "sound") {
+ this.song.name = noExt;
+ } else {
+ this.song.buildupName = noExt;
+ }
+ // make empty map if needed
+ if(!this.getText(editor)) {
+ this.setText(editor, "x...o...x...o...");
+ }
+ // Do we have a loop to play?
+ if(this.song.sound) {
+ // Force refresh
+ return this.core.soundManager.playSong(this.song, true, true);
+ }
+ }).then(() => {
+ if(newlyLinked) {
+ this.setIndependentBuild(false);
+ }
+ this.updateInfo();
+ this.core.updateBeatLength();
+ // We may have to go backwards in time
+ this.core.recalcBeatIndex();
this.updateWaveform();
+ }).catch(error => {
+ console.log(error);
+ this.alert("Couldn't load song! Is it a LAME encoded MP3?");
});
- } else {
- this.core.soundManager.stop();
- this.updateWaveform();
}
- this.updateInfo();
- this.updateHalveDoubleButtons(editor);
-};
-
-HuesEditor.prototype.blobToArrayBuffer = function(blob) {
- return new Promise((resolve, reject) => {
- let fr = new FileReader();
- fr.onload = () => {
- resolve(fr.result);
- };
- fr.onerror = () => {
- reject(new Error("File read failed!"));
- };
- fr.readAsArrayBuffer(blob);
- });
-};
-
-HuesEditor.prototype.newSong = function(song) {
- if(!song) {
- song = {"name":"Name",
- "title":"Title",
- "rhythm":"",
- "source":"",
- "sound":null,
- "enabled":true,
- "filename":null,
- "charsPerBeat": null,
- // Because new songs are empty
- "independentBuild": true};
- if(!this.respack) {
- this.respack = new Respack();
- this.respack.name = "Editor Respack";
- this.respack.author = "You!";
- this.respack.description = "An internal resourcepack for editing new songs";
- this.core.resourceManager.addPack(this.respack);
- }
- this.respack.songs.push(song);
- this.core.resourceManager.rebuildArrays();
- this.core.resourceManager.rebuildEnabled();
- this.core.setSongOject(song);
- }
- // Clear instructions
- this.buildEdit._hilight.className = "beat-hilight invisible";
- this.loopEdit._hilight.className = "beat-hilight invisible";
-
- // Clear helpful glows
- this.newSongBtn.classList.remove("hues-button--glow");
- this.fromSongBtn.classList.remove("hues-button--glow");
-
- // Enable title edits
- this.title.disabled = false;
- this.source.disabled = false;
-
- this.clearUndoRedo();
-
- this.song = song;
- this.reflow(this.buildEdit, song.buildupRhythm);
- this.reflow(this.loopEdit, song.rhythm);
- this.title.value = song.title;
- this.source.value = song.source;
-
- // Force independent build if only 1 source is present
- this.updateIndependentBuild();
-
- // Unlock beatmap lengths
- this.setLocked(this.buildEdit, 0);
- this.setLocked(this.loopEdit, 0);
-
- this.linked = true;
- this.updateInfo();
- this.updateWaveform();
-};
-HuesEditor.prototype.updateIndependentBuild = function() {
- // Force independent build if only 1 source is present
-
- // Effectively buildup XOR loop - does only 1 exist?
- let hasBuild = !!this.song.buildup;
- let hasLoop = !!this.song.sound;
- if(hasBuild != hasLoop) {
+ removeAudio(editor) {
+ if(!this.song) {
+ return;
+ }
+
+ this.song[editor._sound] = null;
+ this.song[editor._rhythm] = "";
this.setIndependentBuild(true);
- }
-};
-
-HuesEditor.prototype.setIndependentBuild = function(indep) {
- this.song.independentBuild = indep;
- if(!indep) {
- // If both are locked, we lock the result, otherwise unlock both
- let lock = this.loopEdit._locked && this.buildEdit._locked;
- // Then unlock both so text adjustment can occur
- this.loopEdit._locked = 0;
- this.buildEdit._locked = 0;
- // Correct the lengths
- this.setText(this.loopEdit, this.getText(this.loopEdit));
- // Restore locked state
- if(lock) {
- this.loopEdit._locked = this.song.rhythm.length;
- this.buildEdit._locked = this.song.buildupRhythm.length;
+ this.reflow(editor, "");
+ // Is the loop playable?
+ if(this.song.sound && this.linked) {
+ this.core.soundManager.playSong(this.song, true, true)
+ .then(() => {
+ this.updateWaveform();
+ });
+ } else {
+ this.core.soundManager.stop();
+ this.updateWaveform();
}
+ this.updateInfo();
+ this.updateHalveDoubleButtons(editor);
}
- this.updateInfo();
-};
-
-HuesEditor.prototype.batchUndo = function() {
- if(!this.batchUndoArray)
- this.batchUndoArray = [];
-};
-
-HuesEditor.prototype.commitUndo = function() {
- if(this.batchUndoArray) {
- this.undoBuffer.push(this.batchUndoArray);
- this.trimUndo();
- this.batchUndoArray = null;
- this.updateUndoUI();
- }
-};
-HuesEditor.prototype.pushUndo = function(name, editor, oldText, newText) {
- if(oldText == newText) {
- return;
- }
- this.redoBuffer = [];
-
- let undoObj = {songVar: name,
- editor: editor,
- text: oldText,
- indep: this.song.independentBuild};
- if(this.batchUndoArray) {
- this.batchUndoArray.push(undoObj);
- } else {
- // 1 element array so undoRedo is neater
- this.undoBuffer.push([undoObj]);
- this.trimUndo();
+ blobToArrayBuffer(blob) {
+ return new Promise((resolve, reject) => {
+ let fr = new FileReader();
+ fr.onload = () => {
+ resolve(fr.result);
+ };
+ fr.onerror = () => {
+ reject(new Error("File read failed!"));
+ };
+ fr.readAsArrayBuffer(blob);
+ });
}
- this.updateUndoUI();
-};
-HuesEditor.prototype.trimUndo = function() {
- while(this.undoBuffer.length > 50) {
- this.undoBuffer.shift();
+ newSong(song) {
+ if(!song) {
+ song = {"name":"Name",
+ "title":"Title",
+ "rhythm":"",
+ "source":"",
+ "sound":null,
+ "enabled":true,
+ "filename":null,
+ "charsPerBeat": null,
+ // Because new songs are empty
+ "independentBuild": true};
+ if(!this.respack) {
+ this.respack = new Respack();
+ this.respack.name = "Editor Respack";
+ this.respack.author = "You!";
+ this.respack.description = "An internal resourcepack for editing new songs";
+ this.core.resourceManager.addPack(this.respack);
+ }
+ this.respack.songs.push(song);
+ this.core.resourceManager.rebuildArrays();
+ this.core.resourceManager.rebuildEnabled();
+ this.core.setSongOject(song);
+ }
+ // Clear instructions
+ this.buildEdit._hilight.className = "beat-hilight invisible";
+ this.loopEdit._hilight.className = "beat-hilight invisible";
+
+ // Clear helpful glows
+ this.newSongBtn.classList.remove("hues-button--glow");
+ this.fromSongBtn.classList.remove("hues-button--glow");
+
+ // Enable title edits
+ this.title.disabled = false;
+ this.source.disabled = false;
+
+ this.clearUndoRedo();
+
+ this.song = song;
+ this.reflow(this.buildEdit, song.buildupRhythm);
+ this.reflow(this.loopEdit, song.rhythm);
+ this.title.value = song.title;
+ this.source.value = song.source;
+
+ // Force independent build if only 1 source is present
+ this.updateIndependentBuild();
+
+ // Unlock beatmap lengths
+ this.setLocked(this.buildEdit, 0);
+ this.setLocked(this.loopEdit, 0);
+
+ this.linked = true;
+ this.updateInfo();
+ this.updateWaveform();
}
-};
-HuesEditor.prototype.undo = function() {
- this.undoRedo(this.undoBuffer, this.redoBuffer);
-};
+ updateIndependentBuild() {
+ // Force independent build if only 1 source is present
+
+ // Effectively buildup XOR loop - does only 1 exist?
+ let hasBuild = !!this.song.buildup;
+ let hasLoop = !!this.song.sound;
+ if(hasBuild != hasLoop) {
+ this.setIndependentBuild(true);
+ }
+ }
-HuesEditor.prototype.redo = function() {
- this.undoRedo(this.redoBuffer, this.undoBuffer);
-};
+ setIndependentBuild(indep) {
+ this.song.independentBuild = indep;
+ if(!indep) {
+ // If both are locked, we lock the result, otherwise unlock both
+ let lock = this.loopEdit._locked && this.buildEdit._locked;
+ // Then unlock both so text adjustment can occur
+ this.loopEdit._locked = 0;
+ this.buildEdit._locked = 0;
+ // Correct the lengths
+ this.setText(this.loopEdit, this.getText(this.loopEdit));
+ // Restore locked state
+ if(lock) {
+ this.loopEdit._locked = this.song.rhythm.length;
+ this.buildEdit._locked = this.song.buildupRhythm.length;
+ }
+ }
+ this.updateInfo();
+ }
-HuesEditor.prototype.undoRedo = function(from, to) {
- if(from.length === 0 || !this.song) {
- return;
+ batchUndo() {
+ if(!this.batchUndoArray)
+ this.batchUndoArray = [];
}
- // Remove old data
- let fromArray = from.pop();
- let toArray = [];
- for(let i = 0; i < fromArray.length; i++) {
- let fromData = fromArray[i];
- // Make restore from current
- toArray.push({songVar: fromData.songVar,
- editor: fromData.editor,
- text: this.song[fromData.songVar],
- indep: this.song.independentBuild});
- // Restore to editor
- this.song[fromData.songVar] = fromData.text;
- this.song.independentBuild = fromData.indep;
- // Don't have weird behaviour there
- if(fromData.editor._locked) {
- fromData.editor._locked = fromData.text.length;
+
+ commitUndo() {
+ if(this.batchUndoArray) {
+ this.undoBuffer.push(this.batchUndoArray);
+ this.trimUndo();
+ this.batchUndoArray = null;
+ this.updateUndoUI();
}
- this.reflow(fromData.editor, this.song[fromData.songVar]);
- this.updateHalveDoubleButtons(fromData.editor);
}
- to.push(toArray);
- this.updateUndoUI();
- this.updateInfo();
- this.core.updateBeatLength();
- this.core.recalcBeatIndex();
-};
-
-HuesEditor.prototype.clearUndoRedo = function() {
- this.undoBuffer = [];
- this.redoBuffer = [];
- this.updateUndoUI();
-};
-
-HuesEditor.prototype.updateUndoUI = function() {
- 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("hues-button--disabled");
+
+ pushUndo(name, editor, oldText, newText) {
+ if(oldText == newText) {
+ return;
+ }
+ this.redoBuffer = [];
+
+ let undoObj = {songVar: name,
+ editor: editor,
+ text: oldText,
+ indep: this.song.independentBuild};
+ if(this.batchUndoArray) {
+ this.batchUndoArray.push(undoObj);
+ } else {
+ // 1 element array so undoRedo is neater
+ this.undoBuffer.push([undoObj]);
+ this.trimUndo();
+ }
+ this.updateUndoUI();
}
- if(this.redoBuffer.length > 0) {
- this.redoBtn.classList.remove("hues-button--disabled");
+
+ trimUndo() {
+ while(this.undoBuffer.length > 50) {
+ this.undoBuffer.shift();
+ }
}
-};
-
-HuesEditor.prototype.halveBeats = function(editor) {
- let commit = false;
- if(!this.song.independentBuild) {
- commit = true;
- this.batchUndo();
- // halve them both
- let other = this.getOther(editor);
- this.song.independentBuild = true;
- this.halveBeats(other);
+
+ undo() {
+ this.undoRedo(this.undoBuffer, this.redoBuffer);
}
- this.setText(editor, this.song[editor._rhythm].replace(/(.)./g, "$1"));
- if(commit) {
- this.commitUndo();
- // We set it so any rounding is padded
- this.setIndependentBuild(false);
+
+ redo() {
+ this.undoRedo(this.redoBuffer, this.undoBuffer);
}
-};
-
-HuesEditor.prototype.doubleBeats = function(editor) {
- let commit = false;
- if(!this.song.independentBuild) {
- commit = true;
- this.batchUndo();
- // Double them both
- let other = this.getOther(editor);
- this.song.independentBuild = true;
- this.doubleBeats(other);
+
+ undoRedo(from, to) {
+ if(from.length === 0 || !this.song) {
+ return;
+ }
+ // Remove old data
+ let fromArray = from.pop();
+ let toArray = [];
+ for(let i = 0; i < fromArray.length; i++) {
+ let fromData = fromArray[i];
+ // Make restore from current
+ toArray.push({songVar: fromData.songVar,
+ editor: fromData.editor,
+ text: this.song[fromData.songVar],
+ indep: this.song.independentBuild});
+ // Restore to editor
+ this.song[fromData.songVar] = fromData.text;
+ this.song.independentBuild = fromData.indep;
+ // Don't have weird behaviour there
+ if(fromData.editor._locked) {
+ fromData.editor._locked = fromData.text.length;
+ }
+ this.reflow(fromData.editor, this.song[fromData.songVar]);
+ this.updateHalveDoubleButtons(fromData.editor);
+ }
+ to.push(toArray);
+ this.updateUndoUI();
+ this.updateInfo();
+ this.core.updateBeatLength();
+ this.core.recalcBeatIndex();
}
- this.setText(editor, this.song[editor._rhythm].replace(/(.)/g, "$1."));
- if(commit) {
- this.commitUndo();
- // We set it so any rounding is padded
- this.setIndependentBuild(false);
+
+ clearUndoRedo() {
+ this.undoBuffer = [];
+ this.redoBuffer = [];
+ this.updateUndoUI();
}
-};
-HuesEditor.prototype.updateHalveDoubleButtons = function(editor) {
- editor._halveBtn.className = "hues-button hues-button--disabled";
- editor._doubleBtn.className = "hues-button hues-button--disabled";
+ updateUndoUI() {
+ 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("hues-button--disabled");
+ }
+ if(this.redoBuffer.length > 0) {
+ this.redoBtn.classList.remove("hues-button--disabled");
+ }
+ }
- if(!editor._locked) {
- let txtLen = this.getText(editor).length;
+ halveBeats(editor) {
+ let commit = false;
if(!this.song.independentBuild) {
+ commit = true;
+ this.batchUndo();
+ // halve them both
let other = this.getOther(editor);
- txtLen = Math.min(txtLen, this.getText(other).length);
- }
- if(txtLen > 0) {
- editor._doubleBtn.className = "hues-button";
+ this.song.independentBuild = true;
+ this.halveBeats(other);
}
- if(txtLen > 1) {
- editor._halveBtn.className = "hues-button";
+ this.setText(editor, this.song[editor._rhythm].replace(/(.)./g, "$1"));
+ if(commit) {
+ this.commitUndo();
+ // We set it so any rounding is padded
+ this.setIndependentBuild(false);
}
}
-};
-
-HuesEditor.prototype.createTextInput = function(label, subtitle, parent) {
- let div = document.createElement("div");
- div.className = "editor__label";
- let caption = document.createElement("label");
- caption.innerHTML = label;
- div.appendChild(caption);
- let container = document.createElement("span");
- container.className = "editor__textinput-container";
- let input = document.createElement("input");
- input.className = "editor__textinput";
- input.type = "text";
- input.value = subtitle;
- container.appendChild(input);
- div.appendChild(container);
-
- parent.appendChild(div);
-
- return input;
-};
-
-HuesEditor.prototype.createButton = function(label, parent, disabled, extraClass) {
- let button = document.createElement("span");
- button.className = "hues-button";
- if(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("hues-button--disabled")) {
- event.preventDefault();
- event.stopPropagation();
- event.stopImmediatePropagation();
- return false;
- } else {
- return true;
+
+ doubleBeats(editor) {
+ let commit = false;
+ if(!this.song.independentBuild) {
+ commit = true;
+ this.batchUndo();
+ // Double them both
+ let other = this.getOther(editor);
+ this.song.independentBuild = true;
+ this.doubleBeats(other);
}
- });
- button.innerHTML = label.toUpperCase();
- parent.appendChild(button);
- return button;
-};
-
-HuesEditor.prototype.uiCreateInfo = function() {
- let info = document.createElement("div");
- this.topBar.appendChild(info);
- info.className = "editor__info";
-
- let songUpdate = function(name) {
- if(!this.song ) {
- return;
+ this.setText(editor, this.song[editor._rhythm].replace(/(.)/g, "$1."));
+ if(commit) {
+ this.commitUndo();
+ // We set it so any rounding is padded
+ this.setIndependentBuild(false);
}
- this.song[name] = this[name].value;
- if(this.song != this.core.currentSong) {
- return;
+ }
+
+ updateHalveDoubleButtons(editor) {
+ 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;
+ if(!this.song.independentBuild) {
+ let other = this.getOther(editor);
+ txtLen = Math.min(txtLen, this.getText(other).length);
+ }
+ if(txtLen > 0) {
+ editor._doubleBtn.className = "hues-button";
+ }
+ if(txtLen > 1) {
+ editor._halveBtn.className = "hues-button";
+ }
}
- this.core.callEventListeners("newsong", this.song);
- };
-
- this.title = this.createTextInput("Title:", "Song name", info);
- this.title.oninput = songUpdate.bind(this, "title");
- this.title.disabled = true;
- this.source = this.createTextInput("Link: ", "Source link", info);
- this.source.oninput = songUpdate.bind(this, "source");
- this.source.disabled = true;
-};
-
-HuesEditor.prototype.uiCreateImport = function() {
- let imports = document.createElement("div");
- this.topBar.appendChild(imports);
- imports.className = "editor__imports";
-
- let songEdits = document.createElement("div");
- imports.appendChild(songEdits);
- 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, "hues-button--glow");
- fromSong.addEventListener("click", () => {
- if(this.core.currentSong) {
- this.newSong(this.core.currentSong);
+ }
+
+ createTextInput(label, subtitle, parent) {
+ let div = document.createElement("div");
+ div.className = "editor__label";
+ let caption = document.createElement("label");
+ caption.innerHTML = label;
+ div.appendChild(caption);
+ let container = document.createElement("span");
+ container.className = "editor__textinput-container";
+ let input = document.createElement("input");
+ input.className = "editor__textinput";
+ input.type = "text";
+ input.value = subtitle;
+ container.appendChild(input);
+ div.appendChild(container);
+
+ parent.appendChild(div);
+
+ return input;
+ }
+
+ createButton(label, parent, disabled, extraClass) {
+ let button = document.createElement("span");
+ button.className = "hues-button";
+ if(disabled) {
+ button.classList.add("hues-button--disabled");
}
- });
- this.fromSongBtn = fromSong;
-
- let songInfos = document.createElement("div");
- songInfos.className = "settings-individual editor__song-stats";
- imports.appendChild(songInfos);
-
- this.loopLen = this.uiCreateSongStat("Loop length (s):", "0.00", songInfos);
- this.buildLen = this.uiCreateSongStat("Build length (s):", "0.00", songInfos);
- this.beatLen = this.uiCreateSongStat("Beat length (ms):", "0.00", songInfos);
-};
-
-HuesEditor.prototype.uiCreateSongStat = function(name, value, parent) {
- let container = document.createElement("div");
- parent.appendChild(container);
- let label = document.createElement("span");
- label.textContent = name;
- container.appendChild(label);
- let valueSpan = document.createElement("span");
- valueSpan.textContent = value;
- valueSpan.className = "editor__song-stats__value";
- container.appendChild(valueSpan);
- return valueSpan;
-};
-
-HuesEditor.prototype.uiCreateEditArea = function() {
- let editArea = document.createElement("div");
- this.editArea = editArea;
- editArea.className = "edit-area";
- this.root.appendChild(editArea);
-
- // Lock build/loop lengths
- this.timeLock = document.createElement("div");
- editArea.appendChild(this.timeLock);
- this.timeLock.className = "hues-icon edit-area__timelock edit-area__timelock--unlocked";
- // CHAIN-BROKEN, use for CHAIN
- let locker = this.createButton("", this.timeLock);
- locker.addEventListener("click", () => {
- // Only allow if both song bits exist
- if(!this.song || !this.song.buildup || !this.song.sound) {
- return;
+ if(extraClass) {
+ button.className += " " + extraClass;
}
- this.setIndependentBuild(!this.song.independentBuild);
- });
- this.timeLock._locker = locker;
-
- this.buildEdit = this.uiCreateSingleEditor("Buildup", "buildup", "buildupRhythm", editArea);
- this.seekStart = this.buildEdit._seek;
- // FIRST |<<
- this.seekStart.innerHTML = "";
- this.seekStart.addEventListener("click", () => {
- this.core.soundManager.seek(-this.core.soundManager.buildLength);
- });
-
- // drag handle
- let handleContainer = document.createElement("div");
- handleContainer.className = "resize-handle";
- editArea.appendChild(handleContainer);
- let handle = document.createElement("div");
- handle.className = 'hues-icon resize-handle__handle';
- handle.innerHTML = ""; // DRAG HANDLE
- handleContainer.appendChild(handle);
- this.resizeHandle = handleContainer;
-
- handleContainer.addEventListener("mousedown", (e) => {
- e.preventDefault();
- let editTop = this.editArea.getBoundingClientRect().top;
- let handleSize = this.resizeHandle.clientHeight;
-
- let resizer = (e) => {
- this.buildEditSize = Math.floor(e.clientY - editTop + handleSize/2);
- this.resize(true);
- };
+ // Automagically make disabled buttons ignore clicks
+ button.addEventListener("click", event => {
+ if(button.classList.contains("hues-button--disabled")) {
+ event.preventDefault();
+ event.stopPropagation();
+ event.stopImmediatePropagation();
+ return false;
+ } else {
+ return true;
+ }
+ });
+ button.innerHTML = label.toUpperCase();
+ parent.appendChild(button);
+ return button;
+ }
+
+ uiCreateInfo() {
+ let info = document.createElement("div");
+ this.topBar.appendChild(info);
+ info.className = "editor__info";
- let mouseup = function(e) {
- document.removeEventListener("mousemove", resizer);
- document.removeEventListener("mouseup", mouseup);
+ let songUpdate = function(name) {
+ if(!this.song ) {
+ return;
+ }
+ this.song[name] = this[name].value;
+ if(this.song != this.core.currentSong) {
+ return;
+ }
+ this.core.callEventListeners("newsong", this.song);
};
- document.addEventListener("mousemove", resizer);
- document.addEventListener("mouseup", mouseup);
- });
-
- this.loopEdit = this.uiCreateSingleEditor("Rhythm ", "sound", "rhythm", editArea);
- this.seekLoop = this.loopEdit._seek;
- // FIRST |<<
- this.seekLoop.innerHTML = "";
- this.seekLoop.addEventListener("click", () => {
- this.core.soundManager.seek(0);
- });
-
- this.buildEdit._hilight.textContent = "[none]";
- this.loopEdit._hilight.innerHTML =
- '
' +
- 'Click [LOAD RHYTHM] to load a loop! LAME encoded MP3s work best.
' +
- '(LAME is important for seamless MP3 loops)
' +
- '
' +
- '[DOUBLE] doubles the selected map length by padding it with "."s.
' +
- '[HALVE] shortens the map length by removing every other character.
' +
- '
' +
- 'You can also add a buildup with [LOAD BUILDUP], or remove it
' +
- 'with [REMOVE].
' +
- '
' +
- '[NEW SONG] adds a completely empty song for you to edit, and
' +
- '[EDIT CURRENT SONG] takes the current playing song to the editor.
' +
- '
' +
- '[COPY/SAVE XML] allow for storing the rhythms and easy
' +
- 'inclusion into a Resource Pack!';
-};
-
-HuesEditor.prototype.uiCreateSingleEditor = function(title, soundName, rhythmName, parent) {
- let container = document.createElement("div");
- parent.appendChild(container);
-
- let header = document.createElement("div");
- header.className = "edit-area__header";
- container.appendChild(header);
-
- let nameLabel = document.createElement("span");
- header.appendChild(nameLabel);
- nameLabel.innerHTML = title;
-
- let seek = this.createButton("", header, true, "hues-icon");
- header.appendChild(seek);
- container._seek = seek;
-
- let beatCount = document.createElement("span");
- header.appendChild(beatCount);
- beatCount.className = "edit-area__beat-count";
- beatCount.textContent = "0 beats";
- container._lockedBtn = this.createButton("", header, false, "hues-icon");
- container._lockedBtn.addEventListener("click", () => {
- if(container._locked) {
- this.setLocked(container, 0);
- } else {
- let textLen = this.getText(container).length;
- this.setLocked(container, textLen);
- }
- });
-
- let rightHeader = document.createElement("span");
- rightHeader.className = "edit-area__header__right";
- header.appendChild(rightHeader);
-
- container._halveBtn = this.createButton("Halve", rightHeader, true);
- container._halveBtn.addEventListener("click", this.halveBeats.bind(this, container));
- container._doubleBtn = this.createButton("Double", rightHeader, true);
- container._doubleBtn.addEventListener("click", this.doubleBeats.bind(this, container));
-
- let fileInput = document.createElement("input");
- fileInput.type ="file";
- fileInput.accept=".mp3, .wav, .ogg";
- fileInput.multiple = false;
- fileInput.onchange = this.loadAudio.bind(this, container);
- let load = this.createButton("Load " + title.replace(/ /g,""), rightHeader);
- load.addEventListener("click", () => {fileInput.click();});
-
- container._removeBtn = this.createButton("Remove", rightHeader, true);
- container._removeBtn.addEventListener("click", this.removeAudio.bind(this, container));
-
- let editBox = document.createElement("div");
- editBox.className = "edit-area__box";
- let beatmap = document.createElement("div");
- beatmap.className = "edit-area__beatmap";
- beatmap.contentEditable = true;
- beatmap.spellcheck = false;
- beatmap.oninput = this.textUpdated.bind(this, container);
- beatmap.oncontextmenu = this.rightClick.bind(this, container);
-
- let beatHilight = document.createElement("div");
- beatHilight.className = "beat-hilight";
-
- editBox.appendChild(beatHilight);
- editBox.appendChild(beatmap);
- container.appendChild(editBox);
-
- container._header = header;
- container._beatCount = beatCount;
- container._box = editBox;
- container._beatmap = beatmap;
- container._hilight = beatHilight;
- container._fileInput = fileInput;
-
- container._sound = soundName;
- container._rhythm = rhythmName;
-
- // Are we in insert mode? Default = no
- container._locked = 0;
-
- return container;
-};
+ this.title = this.createTextInput("Title:", "Song name", info);
+ this.title.oninput = songUpdate.bind(this, "title");
+ this.title.disabled = true;
+ this.source = this.createTextInput("Link: ", "Source link", info);
+ this.source.oninput = songUpdate.bind(this, "source");
+ this.source.disabled = true;
+ }
-HuesEditor.prototype.uiCreateControls = function() {
- let controls = document.createElement("div");
- controls.className = "edit__controls";
- this.root.appendChild(controls);
-
- let changeRate = function(change) {
- let rate = this.core.soundManager.playbackRate;
- rate += change;
- this.core.soundManager.setRate(rate);
- // In case it gets clamped, check
- let newRate = this.core.soundManager.playbackRate;
- playRateLab.textContent = newRate.toFixed(2) + "x";
- };
-
- let speedControl = document.createElement("div");
- controls.appendChild(speedControl);
-
- // BACKWARD
- let speedDown = this.createButton("", speedControl, false, "hues-icon");
- speedDown.addEventListener("click", changeRate.bind(this, -0.25));
- // FORWARD
- let speedUp = this.createButton("", speedControl, false, "hues-icon");
- speedUp.addEventListener("click", changeRate.bind(this, 0.25));
-
- let playRateLab = document.createElement("span");
- playRateLab.className = "settings-individual";
- playRateLab.textContent = "1.00x";
- speedControl.appendChild(playRateLab);
-
- let wrapControl = document.createElement("div");
- controls.appendChild(wrapControl);
-
- let wrapLab = document.createElement("span");
- wrapLab.className = "settings-individual";
- wrapLab.textContent = "New line at beat ";
- wrapControl.appendChild(wrapLab);
-
- let wrapAt = document.createElement("input");
- wrapAt.className = "settings-input";
- wrapAt.value = this.wrapAt;
- wrapAt.type = "text";
- wrapAt.oninput = () => {
- wrapAt.value = wrapAt.value.replace(/\D/g,'');
- if(wrapAt.value === "" || wrapAt.value < 1) {
- wrapAt.value = "";
- return;
- }
- this.wrapAt = parseInt(wrapAt.value);
- this.reflow(this.buildEdit, this.song.buildupRhythm);
- this.reflow(this.loopEdit, this.song.rhythm);
-
- };
- wrapControl.appendChild(wrapAt);
-};
-
-HuesEditor.prototype.uiCreateVisualiser = function() {
- let wave = document.createElement("canvas");
- wave.className = "waveform";
- wave.height = WAVE_HEIGHT_PIXELS;
- this.root.appendChild(wave);
- this.waveCanvas = wave;
- this.waveContext = wave.getContext("2d");
-
- this.core.addEventListener("frame", this.drawWave.bind(this));
-};
+ uiCreateImport() {
+ let imports = document.createElement("div");
+ this.topBar.appendChild(imports);
+ imports.className = "editor__imports";
+
+ let songEdits = document.createElement("div");
+ imports.appendChild(songEdits);
+ 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, "hues-button--glow");
+ fromSong.addEventListener("click", () => {
+ if(this.core.currentSong) {
+ this.newSong(this.core.currentSong);
+ }
+ });
+ this.fromSongBtn = fromSong;
+
+ let songInfos = document.createElement("div");
+ songInfos.className = "settings-individual editor__song-stats";
+ imports.appendChild(songInfos);
+
+ this.loopLen = this.uiCreateSongStat("Loop length (s):", "0.00", songInfos);
+ this.buildLen = this.uiCreateSongStat("Build length (s):", "0.00", songInfos);
+ this.beatLen = this.uiCreateSongStat("Beat length (ms):", "0.00", songInfos);
+ }
-HuesEditor.prototype.rightClick = function(editor, event) {
- if(!this.linked) {
- return;
+ uiCreateSongStat(name, value, parent) {
+ let container = document.createElement("div");
+ parent.appendChild(container);
+ let label = document.createElement("span");
+ label.textContent = name;
+ container.appendChild(label);
+ let valueSpan = document.createElement("span");
+ valueSpan.textContent = value;
+ valueSpan.className = "editor__song-stats__value";
+ container.appendChild(valueSpan);
+ return valueSpan;
}
- // If the right click is also a focus event, caret doesn't move, so we have to use coords
- let coords = this.getTextCoords(event);
-
- if(coords.x > this.wrapAt)
- return true;
- let caret = coords.y * this.wrapAt + coords.x;
- let totalLen = this.getText(editor).length;
- if(caret > totalLen)
- return true;
-
- // in case of focus event
- this.setCaret(editor._beatmap, caret);
- let percent = caret / totalLen;
- let seekTime = 0;
- if(editor == this.loopEdit) { // loop
- seekTime = this.core.soundManager.loopLength * percent;
- } else { // build
- let bLen = this.core.soundManager.buildLength;
- seekTime = -bLen + bLen * percent;
+ uiCreateEditArea() {
+ let editArea = document.createElement("div");
+ this.editArea = editArea;
+ editArea.className = "edit-area";
+ this.root.appendChild(editArea);
+
+ // Lock build/loop lengths
+ this.timeLock = document.createElement("div");
+ editArea.appendChild(this.timeLock);
+ this.timeLock.className = "hues-icon edit-area__timelock edit-area__timelock--unlocked";
+ // CHAIN-BROKEN, use for CHAIN
+ let locker = this.createButton("", this.timeLock);
+ locker.addEventListener("click", () => {
+ // Only allow if both song bits exist
+ if(!this.song || !this.song.buildup || !this.song.sound) {
+ return;
+ }
+ this.setIndependentBuild(!this.song.independentBuild);
+ });
+ this.timeLock._locker = locker;
+
+ this.buildEdit = this.uiCreateSingleEditor("Buildup", "buildup", "buildupRhythm", editArea);
+ this.seekStart = this.buildEdit._seek;
+ // FIRST |<<
+ this.seekStart.innerHTML = "";
+ this.seekStart.addEventListener("click", () => {
+ this.core.soundManager.seek(-this.core.soundManager.buildLength);
+ });
+
+ // drag handle
+ let handleContainer = document.createElement("div");
+ handleContainer.className = "resize-handle";
+ editArea.appendChild(handleContainer);
+ let handle = document.createElement("div");
+ handle.className = 'hues-icon resize-handle__handle';
+ handle.innerHTML = ""; // DRAG HANDLE
+ handleContainer.appendChild(handle);
+ this.resizeHandle = handleContainer;
+
+ handleContainer.addEventListener("mousedown", (e) => {
+ e.preventDefault();
+ let editTop = this.editArea.getBoundingClientRect().top;
+ let handleSize = this.resizeHandle.clientHeight;
+
+ let resizer = (e) => {
+ this.buildEditSize = Math.floor(e.clientY - editTop + handleSize/2);
+ this.resize(true);
+ };
+
+ let mouseup = function(e) {
+ document.removeEventListener("mousemove", resizer);
+ document.removeEventListener("mouseup", mouseup);
+ };
+
+ document.addEventListener("mousemove", resizer);
+ document.addEventListener("mouseup", mouseup);
+ });
+
+ this.loopEdit = this.uiCreateSingleEditor("Rhythm ", "sound", "rhythm", editArea);
+ this.seekLoop = this.loopEdit._seek;
+ // FIRST |<<
+ this.seekLoop.innerHTML = "";
+ this.seekLoop.addEventListener("click", () => {
+ this.core.soundManager.seek(0);
+ });
+
+ this.buildEdit._hilight.textContent = "[none]";
+ this.loopEdit._hilight.innerHTML =
+ '
' +
+ 'Click [LOAD RHYTHM] to load a loop! LAME encoded MP3s work best.
' +
+ '(LAME is important for seamless MP3 loops)
' +
+ '
' +
+ '[DOUBLE] doubles the selected map length by padding it with "."s.
' +
+ '[HALVE] shortens the map length by removing every other character.
' +
+ '
' +
+ 'You can also add a buildup with [LOAD BUILDUP], or remove it
' +
+ 'with [REMOVE].
' +
+ '
' +
+ '[NEW SONG] adds a completely empty song for you to edit, and
' +
+ '[EDIT CURRENT SONG] takes the current playing song to the editor.
' +
+ '
' +
+ '[COPY/SAVE XML] allow for storing the rhythms and easy
' +
+ 'inclusion into a Resource Pack!';
}
- this.core.soundManager.seek(seekTime);
-
- event.preventDefault();
- return false;
-};
-
-HuesEditor.prototype.getTextCoords = function(event) {
- // http://stackoverflow.com/a/10816667
- let el = event.target,
- x = 0,
- y = 0;
-
- while (el && !isNaN(el.offsetLeft) && !isNaN(el.offsetTop)) {
- x += el.offsetLeft - el.scrollLeft;
- y += el.offsetTop - el.scrollTop;
- el = el.offsetParent;
+
+ uiCreateSingleEditor(title, soundName, rhythmName, parent) {
+ let container = document.createElement("div");
+ parent.appendChild(container);
+
+ let header = document.createElement("div");
+ header.className = "edit-area__header";
+ container.appendChild(header);
+
+ let nameLabel = document.createElement("span");
+ header.appendChild(nameLabel);
+ nameLabel.innerHTML = title;
+
+ let seek = this.createButton("", header, true, "hues-icon");
+ header.appendChild(seek);
+ container._seek = seek;
+
+ let beatCount = document.createElement("span");
+ header.appendChild(beatCount);
+ beatCount.className = "edit-area__beat-count";
+ beatCount.textContent = "0 beats";
+ container._lockedBtn = this.createButton("", header, false, "hues-icon");
+ container._lockedBtn.addEventListener("click", () => {
+ if(container._locked) {
+ this.setLocked(container, 0);
+ } else {
+ let textLen = this.getText(container).length;
+ this.setLocked(container, textLen);
+ }
+ });
+
+ let rightHeader = document.createElement("span");
+ rightHeader.className = "edit-area__header__right";
+ header.appendChild(rightHeader);
+
+ container._halveBtn = this.createButton("Halve", rightHeader, true);
+ container._halveBtn.addEventListener("click", this.halveBeats.bind(this, container));
+ container._doubleBtn = this.createButton("Double", rightHeader, true);
+ container._doubleBtn.addEventListener("click", this.doubleBeats.bind(this, container));
+
+ let fileInput = document.createElement("input");
+ fileInput.type ="file";
+ fileInput.accept=".mp3, .wav, .ogg";
+ fileInput.multiple = false;
+ fileInput.onchange = this.loadAudio.bind(this, container);
+ let load = this.createButton("Load " + title.replace(/ /g,""), rightHeader);
+ load.addEventListener("click", () => {fileInput.click();});
+
+ container._removeBtn = this.createButton("Remove", rightHeader, true);
+ container._removeBtn.addEventListener("click", this.removeAudio.bind(this, container));
+
+ let editBox = document.createElement("div");
+ editBox.className = "edit-area__box";
+ let beatmap = document.createElement("div");
+ beatmap.className = "edit-area__beatmap";
+ beatmap.contentEditable = true;
+ beatmap.spellcheck = false;
+ beatmap.oninput = this.textUpdated.bind(this, container);
+ beatmap.oncontextmenu = this.rightClick.bind(this, container);
+
+ let beatHilight = document.createElement("div");
+ beatHilight.className = "beat-hilight";
+
+ editBox.appendChild(beatHilight);
+ editBox.appendChild(beatmap);
+ container.appendChild(editBox);
+
+ container._header = header;
+ container._beatCount = beatCount;
+ container._box = editBox;
+ container._beatmap = beatmap;
+ container._hilight = beatHilight;
+ container._fileInput = fileInput;
+
+ container._sound = soundName;
+ container._rhythm = rhythmName;
+
+ // Are we in insert mode? Default = no
+ container._locked = 0;
+
+ return container;
}
- x = Math.floor((event.clientX - x) / this.hilightWidth);
- y = Math.floor((event.clientY - y) / this.hilightHeight);
-
- return {x: x, y: y};
-};
+ uiCreateControls() {
+ let controls = document.createElement("div");
+ controls.className = "edit__controls";
+ this.root.appendChild(controls);
+
+ let changeRate = function(change) {
+ let rate = this.core.soundManager.playbackRate;
+ rate += change;
+ this.core.soundManager.setRate(rate);
+ // In case it gets clamped, check
+ let newRate = this.core.soundManager.playbackRate;
+ playRateLab.textContent = newRate.toFixed(2) + "x";
+ };
+
+ let speedControl = document.createElement("div");
+ controls.appendChild(speedControl);
+
+ // BACKWARD
+ let speedDown = this.createButton("", speedControl, false, "hues-icon");
+ speedDown.addEventListener("click", changeRate.bind(this, -0.25));
+ // FORWARD
+ let speedUp = this.createButton("", speedControl, false, "hues-icon");
+ speedUp.addEventListener("click", changeRate.bind(this, 0.25));
+
+ let playRateLab = document.createElement("span");
+ playRateLab.className = "settings-individual";
+ playRateLab.textContent = "1.00x";
+ speedControl.appendChild(playRateLab);
+
+ let wrapControl = document.createElement("div");
+ controls.appendChild(wrapControl);
+
+ let wrapLab = document.createElement("span");
+ wrapLab.className = "settings-individual";
+ wrapLab.textContent = "New line at beat ";
+ wrapControl.appendChild(wrapLab);
+
+ let wrapAt = document.createElement("input");
+ wrapAt.className = "settings-input";
+ wrapAt.value = this.wrapAt;
+ wrapAt.type = "text";
+ wrapAt.oninput = () => {
+ wrapAt.value = wrapAt.value.replace(/\D/g,'');
+ if(wrapAt.value === "" || wrapAt.value < 1) {
+ wrapAt.value = "";
+ return;
+ }
+ this.wrapAt = parseInt(wrapAt.value);
+ this.reflow(this.buildEdit, this.song.buildupRhythm);
+ this.reflow(this.loopEdit, this.song.rhythm);
+
+ };
+ wrapControl.appendChild(wrapAt);
+ }
-HuesEditor.prototype.textUpdated = function(editor) {
- if(!this.song || !this.song[editor._sound]) {
- this.reflow(editor, "");
- return;
+ uiCreateVisualiser() {
+ let wave = document.createElement("canvas");
+ wave.className = "waveform";
+ wave.height = WAVE_HEIGHT_PIXELS;
+ this.root.appendChild(wave);
+ this.waveCanvas = wave;
+ this.waveContext = wave.getContext("2d");
+
+ this.core.addEventListener("frame", this.drawWave.bind(this));
}
- // Space at start of line is nonbreaking, get it with \u00a0
- let input = editor._beatmap.textContent.replace(/ |\u00a0/g, "");
- if(input.length === 0) {
- input = ".";
+
+ rightClick(editor, event) {
+ if(!this.linked) {
+ return;
+ }
+ // If the right click is also a focus event, caret doesn't move, so we have to use coords
+ let coords = this.getTextCoords(event);
+
+ if(coords.x > this.wrapAt)
+ return true;
+
+ let caret = coords.y * this.wrapAt + coords.x;
+ let totalLen = this.getText(editor).length;
+ if(caret > totalLen)
+ return true;
+
+ // in case of focus event
+ this.setCaret(editor._beatmap, caret);
+ let percent = caret / totalLen;
+ let seekTime = 0;
+ if(editor == this.loopEdit) { // loop
+ seekTime = this.core.soundManager.loopLength * percent;
+ } else { // build
+ let bLen = this.core.soundManager.buildLength;
+ seekTime = -bLen + bLen * percent;
+ }
+ this.core.soundManager.seek(seekTime);
+
+ event.preventDefault();
+ return false;
}
- this.setText(editor, input);
-};
-
-HuesEditor.prototype.getText = function(editor) {
- if(!this.song || !this.song[editor._rhythm]) {
- return "";
- } else {
- return this.song[editor._rhythm];
+
+ getTextCoords(event) {
+ // http://stackoverflow.com/a/10816667
+ let el = event.target,
+ x = 0,
+ y = 0;
+
+ while (el && !isNaN(el.offsetLeft) && !isNaN(el.offsetTop)) {
+ x += el.offsetLeft - el.scrollLeft;
+ y += el.offsetTop - el.scrollTop;
+ el = el.offsetParent;
+ }
+
+ x = Math.floor((event.clientX - x) / this.hilightWidth);
+ y = Math.floor((event.clientY - y) / this.hilightHeight);
+
+ return {x: x, y: y};
}
-};
-HuesEditor.prototype.setText = function(editor, text, caretFromEnd) {
- if(!this.song || !this.song[editor._sound]) {
- this.reflow(editor, "");
- return;
+ textUpdated(editor) {
+ if(!this.song || !this.song[editor._sound]) {
+ this.reflow(editor, "");
+ return;
+ }
+ // Space at start of line is nonbreaking, get it with \u00a0
+ let input = editor._beatmap.textContent.replace(/ |\u00a0/g, "");
+ if(input.length === 0) {
+ input = ".";
+ }
+ this.setText(editor, input);
}
- let commitUndo = false;
- let caret = caretFromEnd ? text.length : this.getCaret(editor._beatmap);
- if(editor._locked) {
- caret = Math.min(editor._locked, caret);
- if(text.length > editor._locked) {
- // Works for pastes too! Removes the different between sizes from the caret position
- text = text.slice(0, caret) + text.slice(caret + (text.length - editor._locked), text.length);
+
+ getText(editor) {
+ if(!this.song || !this.song[editor._rhythm]) {
+ return "";
} else {
- while(text.length < editor._locked) {
- text += ".";
- }
+ return this.song[editor._rhythm];
}
- // time to scale things to fit
- } else if(!this.song.independentBuild && this.song.buildupRhythm && this.song.rhythm) {
- let ratio;
- if(editor == this.loopEdit) {
- ratio = this.core.soundManager.loopLength / this.core.soundManager.buildLength;
- } else {
- ratio = this.core.soundManager.buildLength / this.core.soundManager.loopLength;
+ }
+
+ setText(editor, text, caretFromEnd) {
+ if(!this.song || !this.song[editor._sound]) {
+ this.reflow(editor, "");
+ return;
}
- let newLen = Math.round(text.length / ratio);
- // We've tried to make the other map impossibly short, force us to be longer
- while(newLen === 0) {
- text += ".";
- newLen = Math.round(text.length / ratio);
+ let commitUndo = false;
+ let caret = caretFromEnd ? text.length : this.getCaret(editor._beatmap);
+ if(editor._locked) {
+ caret = Math.min(editor._locked, caret);
+ if(text.length > editor._locked) {
+ // Works for pastes too! Removes the different between sizes from the caret position
+ text = text.slice(0, caret) + text.slice(caret + (text.length - editor._locked), text.length);
+ } else {
+ while(text.length < editor._locked) {
+ text += ".";
+ }
+ }
+ // time to scale things to fit
+ } else if(!this.song.independentBuild && this.song.buildupRhythm && this.song.rhythm) {
+ let ratio;
+ if(editor == this.loopEdit) {
+ ratio = this.core.soundManager.loopLength / this.core.soundManager.buildLength;
+ } else {
+ ratio = this.core.soundManager.buildLength / this.core.soundManager.loopLength;
+ }
+ let newLen = Math.round(text.length / ratio);
+ // We've tried to make the other map impossibly short, force us to be longer
+ while(newLen === 0) {
+ text += ".";
+ newLen = Math.round(text.length / ratio);
+ }
+ let otherMap = this.getOther(editor);
+ let wasLocked = otherMap._locked;
+ // clamp the length
+ otherMap._locked = newLen;
+ // Make undos also sync
+ this.batchUndo();
+ commitUndo = true;
+ // avoid infinite loop
+ this.song.independentBuild = true;
+ // Use setText to update undo state and fill/clamp beats
+ this.setText(otherMap, this.song[otherMap._rhythm], true);
+ // Restore
+ this.song.independentBuild = false;
+ // Otherwise we'll lose the new length on the next edit
+ if(!wasLocked) {
+ otherMap._locked = 0;
+ }
+ // Fix the buttons
+ this.updateHalveDoubleButtons(otherMap);
}
- let otherMap = this.getOther(editor);
- let wasLocked = otherMap._locked;
- // clamp the length
- otherMap._locked = newLen;
- // Make undos also sync
- this.batchUndo();
- commitUndo = true;
- // avoid infinite loop
- this.song.independentBuild = true;
- // Use setText to update undo state and fill/clamp beats
- this.setText(otherMap, this.song[otherMap._rhythm], true);
- // Restore
- this.song.independentBuild = false;
- // Otherwise we'll lose the new length on the next edit
- if(!wasLocked) {
- otherMap._locked = 0;
+ this.pushUndo(editor._rhythm, editor, this.song[editor._rhythm], text);
+ // If we were linked, commit our 2 edits as 1 undo state
+ if(commitUndo) {
+ this.commitUndo();
}
- // Fix the buttons
- this.updateHalveDoubleButtons(otherMap);
- }
- this.pushUndo(editor._rhythm, editor, this.song[editor._rhythm], text);
- // If we were linked, commit our 2 edits as 1 undo state
- if(commitUndo) {
- this.commitUndo();
+ // Make sure you can't accidentally close the tab
+ window.onbeforeunload = this.confirmLeave;
+ this.song[editor._rhythm] = text;
+ this.reflow(editor, this.song[editor._rhythm]);
+ this.setCaret(editor._beatmap, caret);
+ this.updateHalveDoubleButtons(editor);
+
+ this.core.updateBeatLength();
+ // We may have to go backwards in time
+ this.core.recalcBeatIndex();
+ this.updateInfo();
}
- // Make sure you can't accidentally close the tab
- window.onbeforeunload = this.confirmLeave;
- this.song[editor._rhythm] = text;
- this.reflow(editor, this.song[editor._rhythm]);
- this.setCaret(editor._beatmap, caret);
- this.updateHalveDoubleButtons(editor);
-
- this.core.updateBeatLength();
- // We may have to go backwards in time
- this.core.recalcBeatIndex();
- this.updateInfo();
-};
-
-HuesEditor.prototype.getCaret = function(editable) {
- let caret = 0;
- let sel = window.getSelection();
- if (sel.rangeCount) {
- let range = sel.getRangeAt(0);
- //
elements are empty, and pastes do weird things.
- // So don't go up in multiples of 2 for getCaret
- for(let i = 0; i < editable.childNodes.length; i++) {
- if (range.commonAncestorContainer == editable.childNodes[i]) {
- caret += range.endOffset;
- return caret;
- } else {
- caret += editable.childNodes[i].textContent.length;
+
+ getCaret(editable) {
+ let caret = 0;
+ let sel = window.getSelection();
+ if (sel.rangeCount) {
+ let range = sel.getRangeAt(0);
+ //
elements are empty, and pastes do weird things.
+ // So don't go up in multiples of 2 for getCaret
+ for(let i = 0; i < editable.childNodes.length; i++) {
+ if (range.commonAncestorContainer == editable.childNodes[i]) {
+ caret += range.endOffset;
+ return caret;
+ } else {
+ caret += editable.childNodes[i].textContent.length;
+ }
}
- }
- }
- return 0;
-};
-
-HuesEditor.prototype.setCaret = function(editable, caret) {
- let range = document.createRange();
- let sel = window.getSelection();
- //
elements mean children go up in multiples of 2
- for(let i = 0; i < editable.childNodes.length; i+= 2) {
- let textLen = editable.childNodes[i].textContent.length;
- if(caret > textLen) {
- caret -= textLen;
- } else {
- range.setStart(editable.childNodes[i], caret);
- range.collapse(true);
- sel.removeAllRanges();
- sel.addRange(range);
- break;
}
+ return 0;
}
-};
-
-HuesEditor.prototype.setLocked = function(editor, locked) {
- editor._locked = locked;
- if(locked) {
- editor._lockedBtn.innerHTML = ""; // LOCKED
- } else {
- editor._lockedBtn.innerHTML = ""; // UNLOCKED
- }
- // Synchronise locks when lengths are linked
- if(!this.song.independentBuild) {
- let other = this.getOther(editor);
- let otherLock = locked ? this.getText(other).length : 0;
- this.song.independentBuild = true;
- this.setLocked(other, otherLock);
- this.song.independentBuild = false;
- }
- this.updateHalveDoubleButtons(editor);
-};
-HuesEditor.prototype.updateWaveform = function() {
- if(this.buildWaveBuff != this.core.soundManager.buildup) {
- this.buildWaveBuff = this.core.soundManager.buildup;
- this.buildWave = this.renderWave(this.buildWaveBuff, this.core.soundManager.buildLength);
- }
- if(this.loopWaveBuff != this.core.soundManager.loop) {
- this.loopWaveBuff = this.core.soundManager.loop;
- this.loopWave = this.renderWave(this.loopWaveBuff, this.core.soundManager.loopLength);
+ setCaret(editable, caret) {
+ let range = document.createRange();
+ let sel = window.getSelection();
+ //
elements mean children go up in multiples of 2
+ for(let i = 0; i < editable.childNodes.length; i+= 2) {
+ let textLen = editable.childNodes[i].textContent.length;
+ if(caret > textLen) {
+ caret -= textLen;
+ } else {
+ range.setStart(editable.childNodes[i], caret);
+ range.collapse(true);
+ sel.removeAllRanges();
+ sel.addRange(range);
+ break;
+ }
+ }
}
-};
-HuesEditor.prototype.renderWave = function(buffer, length) {
- if(!buffer) {
- return null;
+ setLocked(editor, locked) {
+ editor._locked = locked;
+ if(locked) {
+ editor._lockedBtn.innerHTML = ""; // LOCKED
+ } else {
+ editor._lockedBtn.innerHTML = ""; // UNLOCKED
+ }
+ // Synchronise locks when lengths are linked
+ if(!this.song.independentBuild) {
+ let other = this.getOther(editor);
+ let otherLock = locked ? this.getText(other).length : 0;
+ this.song.independentBuild = true;
+ this.setLocked(other, otherLock);
+ this.song.independentBuild = false;
+ }
+ this.updateHalveDoubleButtons(editor);
}
- // The individual wave section
- let wave = document.createElement("canvas");
- let waveContext = wave.getContext("2d");
-
- wave.height = WAVE_HEIGHT_PIXELS;
- wave.width = Math.floor(WAVE_PIXELS_PER_SECOND * length);
-
- let samplesPerPixel = Math.floor(buffer.sampleRate / WAVE_PIXELS_PER_SECOND);
- let waveData = [];
- for(let i = 0; i < buffer.numberOfChannels; i++) {
- waveData.push(buffer.getChannelData(i));
+
+ updateWaveform() {
+ if(this.buildWaveBuff != this.core.soundManager.buildup) {
+ this.buildWaveBuff = this.core.soundManager.buildup;
+ this.buildWave = this.renderWave(this.buildWaveBuff, this.core.soundManager.buildLength);
+ }
+ if(this.loopWaveBuff != this.core.soundManager.loop) {
+ this.loopWaveBuff = this.core.soundManager.loop;
+ this.loopWave = this.renderWave(this.loopWaveBuff, this.core.soundManager.loopLength);
+ }
}
- let channels = buffer.numberOfChannels;
- // Half pixel offset makes things look crisp
- let pixel = 0.5;
- let halfHeight = WAVE_HEIGHT_PIXELS/2;
- for(let i = 0; i < buffer.length; i += samplesPerPixel) {
- let min = 0, max = 0, avgHi = 0, avgLo = 0;
- let j;
- for(j = 0; j < samplesPerPixel && i + j < buffer.length; j++) {
- for(let chan = 0; chan < channels; chan++) {
- let sample = waveData[chan][i+j];
- if(sample > 0) {
- avgHi += sample;
- } else {
- avgLo += sample;
+
+ renderWave(buffer, length) {
+ if(!buffer) {
+ return null;
+ }
+ // The individual wave section
+ let wave = document.createElement("canvas");
+ let waveContext = wave.getContext("2d");
+
+ wave.height = WAVE_HEIGHT_PIXELS;
+ wave.width = Math.floor(WAVE_PIXELS_PER_SECOND * length);
+
+ let samplesPerPixel = Math.floor(buffer.sampleRate / WAVE_PIXELS_PER_SECOND);
+ let waveData = [];
+ for(let i = 0; i < buffer.numberOfChannels; i++) {
+ waveData.push(buffer.getChannelData(i));
+ }
+ let channels = buffer.numberOfChannels;
+ // Half pixel offset makes things look crisp
+ let pixel = 0.5;
+ let halfHeight = WAVE_HEIGHT_PIXELS/2;
+ for(let i = 0; i < buffer.length; i += samplesPerPixel) {
+ let min = 0, max = 0, avgHi = 0, avgLo = 0;
+ let j;
+ for(j = 0; j < samplesPerPixel && i + j < buffer.length; j++) {
+ for(let chan = 0; chan < channels; chan++) {
+ let sample = waveData[chan][i+j];
+ if(sample > 0) {
+ avgHi += sample;
+ } else {
+ avgLo += sample;
+ }
+ if(sample > max) max = sample;
+ if(sample < min) min = sample;
}
- if(sample > max) max = sample;
- if(sample < min) min = sample;
}
+ let maxPix = Math.floor(halfHeight + max * halfHeight);
+ // Min is negative, addition is correct
+ let minPix = Math.floor(halfHeight + min * halfHeight);
+ waveContext.strokeStyle = "black";
+ waveContext.globalAlpha = "1";
+ waveContext.beginPath();
+ waveContext.moveTo(pixel, maxPix);
+ waveContext.lineTo(pixel, minPix);
+ waveContext.stroke();
+
+ // Draw the average too, gives a better feel for the wave
+ avgHi /= j * channels;
+ avgLo /= j * channels;
+ let maxAvg = Math.floor(halfHeight + avgHi * halfHeight);
+ let minAvg = Math.floor(halfHeight + avgLo * halfHeight);
+ waveContext.strokeStyle = "white";
+ waveContext.globalAlpha = "0.5";
+ waveContext.beginPath();
+ waveContext.moveTo(pixel, maxAvg);
+ waveContext.lineTo(pixel, minAvg);
+ waveContext.stroke();
+
+ pixel+=1;
}
- let maxPix = Math.floor(halfHeight + max * halfHeight);
- // Min is negative, addition is correct
- let minPix = Math.floor(halfHeight + min * halfHeight);
- waveContext.strokeStyle = "black";
- waveContext.globalAlpha = "1";
- waveContext.beginPath();
- waveContext.moveTo(pixel, maxPix);
- waveContext.lineTo(pixel, minPix);
- waveContext.stroke();
-
- // Draw the average too, gives a better feel for the wave
- avgHi /= j * channels;
- avgLo /= j * channels;
- let maxAvg = Math.floor(halfHeight + avgHi * halfHeight);
- let minAvg = Math.floor(halfHeight + avgLo * halfHeight);
- waveContext.strokeStyle = "white";
- waveContext.globalAlpha = "0.5";
- waveContext.beginPath();
- waveContext.moveTo(pixel, maxAvg);
- waveContext.lineTo(pixel, minAvg);
- waveContext.stroke();
-
- pixel+=1;
+
+ return wave;
}
-
- return wave;
-};
-HuesEditor.prototype.drawWave = function() {
- if((!this.buildWave && !this.loopWave) || !this.linked)
- return;
-
- let width = this.waveCanvas.width;
- let now = this.core.soundManager.currentTime();
- let timespan = width / WAVE_PIXELS_PER_SECOND / 2;
- let minTime = now - timespan;
- let maxTime = now + timespan;
-
- let bLen = this.core.soundManager.buildLength;
- let loopLen = this.core.soundManager.loopLength;
-
- let drawTime, drawOffset;
- if(bLen) {
- drawTime = Math.max(minTime, -bLen);
- } else {
- drawTime = Math.max(minTime, 0);
- }
- // drawOffset is "pixels from the left"
- drawOffset = Math.floor((drawTime - minTime) * WAVE_PIXELS_PER_SECOND);
-
- this.waveContext.clearRect(0, 0, width, WAVE_HEIGHT_PIXELS);
-
- 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);
+ drawWave() {
+ if((!this.buildWave && !this.loopWave) || !this.linked)
+ return;
+
+ let width = this.waveCanvas.width;
+ let now = this.core.soundManager.currentTime();
+ let timespan = width / WAVE_PIXELS_PER_SECOND / 2;
+ let minTime = now - timespan;
+ let maxTime = now + timespan;
+
+ let bLen = this.core.soundManager.buildLength;
+ let loopLen = this.core.soundManager.loopLength;
+
+ let drawTime, drawOffset;
+ if(bLen) {
+ drawTime = Math.max(minTime, -bLen);
+ } else {
+ drawTime = Math.max(minTime, 0);
}
- // If there's more to draw after the build, it'll be from the start of the wave
- drawTime = 0;
- }
-
- let loopPoints = [];
- if(this.loopWave && loopLen && maxTime > 0) {
- while(drawOffset < width) {
- if(drawTime === 0) {
- loopPoints.push(drawOffset);
+ // drawOffset is "pixels from the left"
+ drawOffset = Math.floor((drawTime - minTime) * WAVE_PIXELS_PER_SECOND);
+
+ this.waveContext.clearRect(0, 0, width, WAVE_HEIGHT_PIXELS);
+
+ 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);
}
-
- let waveOffset = Math.floor((drawTime / loopLen) * (this.loopWave.width-1));
- drawOffset = this.drawOneWave(this.loopWave, waveOffset, drawOffset, width);
- // If we're drawing more than 1 loop it's starting at 0
+ // If there's more to draw after the build, it'll be from the start of the wave
drawTime = 0;
}
+
+ let loopPoints = [];
+ if(this.loopWave && loopLen && maxTime > 0) {
+ while(drawOffset < width) {
+ if(drawTime === 0) {
+ loopPoints.push(drawOffset);
+ }
+
+ let waveOffset = Math.floor((drawTime / loopLen) * (this.loopWave.width-1));
+ drawOffset = this.drawOneWave(this.loopWave, waveOffset, drawOffset, width);
+ // If we're drawing more than 1 loop it's starting at 0
+ drawTime = 0;
+ }
+ }
+
+ // trackbar
+ this.drawWaveBar("red", width/2);
+ // Signify loop point with a green bar, drawing over the wave
+ for(let point of loopPoints) {
+ this.drawWaveBar("green", point);
+ }
}
-
- // trackbar
- this.drawWaveBar("red", width/2);
- // Signify loop point with a green bar, drawing over the wave
- for(let point of loopPoints) {
- this.drawWaveBar("green", point);
+
+ drawOneWave(wave, waveOffset, drawOffset, width) {
+ let drawWidth = Math.min(width - drawOffset, wave.width - waveOffset);
+ this.waveContext.drawImage(wave,
+ waveOffset, 0, // source x/y
+ drawWidth, WAVE_HEIGHT_PIXELS, // source width/height
+ drawOffset, 0, // dest x/y
+ drawWidth, WAVE_HEIGHT_PIXELS); // dest width/height
+ return drawOffset + drawWidth;
+ }
+
+ drawWaveBar(colour, offset) {
+ this.waveContext.strokeStyle = colour;
+ this.waveContext.lineWidth = 2;
+ this.waveContext.beginPath();
+ this.waveContext.moveTo(offset, 0);
+ this.waveContext.lineTo(offset, WAVE_HEIGHT_PIXELS);
+ this.waveContext.stroke();
}
-};
-
-HuesEditor.prototype.drawOneWave = function(wave, waveOffset, drawOffset, width) {
- let drawWidth = Math.min(width - drawOffset, wave.width - waveOffset);
- this.waveContext.drawImage(wave,
- waveOffset, 0, // source x/y
- drawWidth, WAVE_HEIGHT_PIXELS, // source width/height
- drawOffset, 0, // dest x/y
- drawWidth, WAVE_HEIGHT_PIXELS); // dest width/height
- return drawOffset + drawWidth;
-};
-
-HuesEditor.prototype.drawWaveBar = function(colour, offset) {
- this.waveContext.strokeStyle = colour;
- this.waveContext.lineWidth = 2;
- this.waveContext.beginPath();
- this.waveContext.moveTo(offset, 0);
- this.waveContext.lineTo(offset, WAVE_HEIGHT_PIXELS);
- this.waveContext.stroke();
-};
-
-HuesEditor.prototype.confirmLeave = function() {
- return "Unsaved beatmap - leave anyway?";
-};
-
-HuesEditor.prototype.alert = function(msg) {
- 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("editor__status-msg--fade");
-};
-
-HuesEditor.prototype.generateXML = function() {
- if(!this.song) {
- return null;
+
+ confirmLeave() {
+ return "Unsaved beatmap - leave anyway?";
}
- // Yes, this is just a bunch of strings. Simple XML, simple method.
- let result = " \n";
- result += " " + this.song.title + "\n";
- if(this.song.source) {
- result += " " + this.song.source + "\n";
+ alert(msg) {
+ 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("editor__status-msg--fade");
}
- result += " " + this.song.rhythm + "\n";
- if(this.song.buildup) {
- result += " " + this.song.buildupName + "\n";
- result += " " + this.song.buildupRhythm + "\n";
- if(this.song.independentBuild) {
- result += " true\n";
+
+ generateXML() {
+ if(!this.song) {
+ return null;
}
+ // Yes, this is just a bunch of strings. Simple XML, simple method.
+
+ let result = " \n";
+ result += " " + this.song.title + "\n";
+ if(this.song.source) {
+ result += " " + this.song.source + "\n";
+ }
+ result += " " + this.song.rhythm + "\n";
+ if(this.song.buildup) {
+ result += " " + this.song.buildupName + "\n";
+ result += " " + this.song.buildupRhythm + "\n";
+ if(this.song.independentBuild) {
+ result += " true\n";
+ }
+ }
+ result += " \n";
+ return result;
}
- result += " \n";
- return result;
-};
-
-HuesEditor.prototype.saveXML = function() {
- let xml = this.generateXML();
- if(!xml) {
- return;
- }
- let result = "\n";
- result += xml;
- result += "\n";
-
- // http://stackoverflow.com/a/18197341
- let element = document.createElement('a');
- element.setAttribute('href', 'data:text/plain;charset=utf-8,' + encodeURIComponent(result));
- element.setAttribute('download', "0x40Hues - " + this.song.name + ".xml");
- element.style.display = 'none';
- document.body.appendChild(element);
+ saveXML() {
+ let xml = this.generateXML();
+ if(!xml) {
+ return;
+ }
+ let result = "\n";
+ result += xml;
+ result += "\n";
+
+ // http://stackoverflow.com/a/18197341
+ let element = document.createElement('a');
+ element.setAttribute('href', 'data:text/plain;charset=utf-8,' + encodeURIComponent(result));
+ element.setAttribute('download', "0x40Hues - " + this.song.name + ".xml");
- element.click();
+ element.style.display = 'none';
+ document.body.appendChild(element);
- document.body.removeChild(element);
-
- window.onbeforeunload = null;
-};
+ element.click();
-// http://stackoverflow.com/a/30810322
-HuesEditor.prototype.copyXML = function() {
- let text = this.generateXML();
-
- // Clicking when disabled
- if(!text) {
- return;
+ document.body.removeChild(element);
+
+ window.onbeforeunload = null;
}
-
- let textArea = document.createElement("textarea");
- textArea.className = "copybox";
- textArea.value = text;
+ // http://stackoverflow.com/a/30810322
+ copyXML() {
+ let text = this.generateXML();
+
+ // Clicking when disabled
+ if(!text) {
+ return;
+ }
+
+ let textArea = document.createElement("textarea");
+ textArea.className = "copybox";
- document.body.appendChild(textArea);
+ textArea.value = text;
- textArea.select();
-
- let success;
+ document.body.appendChild(textArea);
- try {
- success = document.execCommand('copy');
- } catch (err) {
- success = false;
- }
-
- document.body.removeChild(textArea);
- if(success) {
- this.alert("Beatmap XML copied to clipboard!");
- } else {
- this.alert("Copy failed! Try saving instead");
+ textArea.select();
+
+ let success;
+
+ try {
+ success = document.execCommand('copy');
+ } catch (err) {
+ success = false;
+ }
+
+ document.body.removeChild(textArea);
+ if(success) {
+ this.alert("Beatmap XML copied to clipboard!");
+ } else {
+ this.alert("Copy failed! Try saving instead");
+ }
}
-};
+}
window.HuesEditor = HuesEditor;
diff --git a/src/js/HuesInfo.js b/src/js/HuesInfo.js
index bdcb149..722ac69 100644
--- a/src/js/HuesInfo.js
+++ b/src/js/HuesInfo.js
@@ -26,7 +26,7 @@
/* HuesInfo.js populates the INFO tab in the Hues Window.
*/
-let beatGlossary = [
+const beatGlossary = [
"x Vertical blur (snare)",
"o Horizontal blur (bass)",
"- No blur",
@@ -44,7 +44,7 @@ let beatGlossary = [
"I Invert & change image"
];
-let shortcuts = [
+const shortcuts = [
"↑↓ Change song",
"←→ Change image",
"[N] Random song",
diff --git a/src/js/HuesSettings.js b/src/js/HuesSettings.js
index f2c74d5..36abe63 100644
--- a/src/js/HuesSettings.js
+++ b/src/js/HuesSettings.js
@@ -26,7 +26,7 @@
/* If you're modifying settings for your hues, DON'T EDIT THIS
- Go to the HTML and edit the `defaults` object instead!
*/
-HuesSettings.prototype.defaultSettings = {
+const defaultSettings = {
// Location relative to root - where do the audio/zip workers live
// This is required because Web Workers need an absolute path
workersPath : "lib/workers/",
@@ -80,7 +80,7 @@ HuesSettings.prototype.defaultSettings = {
};
// Don't get saved to localStorage
-HuesSettings.prototype.ephemeralSettings = [
+const ephemeralSettings = [
"load",
"autoplay",
"overwriteLocal",
@@ -100,7 +100,7 @@ HuesSettings.prototype.ephemeralSettings = [
];
// To dynamically build the UI like the cool guy I am
-HuesSettings.prototype.settingsCategories = {
+const settingsCategories = {
"Functionality" : [
"autoSong",
"autoSongShuffle",
@@ -126,7 +126,7 @@ HuesSettings.prototype.settingsCategories = {
]
};
-HuesSettings.prototype.settingsOptions = {
+const settingsOptions = {
smartAlign : {
name : "Smart Align images",
options : ["off", "on"]
@@ -218,223 +218,225 @@ HuesSettings.prototype.settingsOptions = {
}
};
-function HuesSettings(defaults) {
- this.eventListeners = {
- /* callback updated()
- *
- * Called when settings are updated
- */
- updated : []
- };
-
- this.hasUI = false;
-
- this.settingCheckboxes = {};
-
- this.textCallbacks = [];
- this.visCallbacks = [];
+class HuesSettings {
+ constructor(defaults) {
+ this.eventListeners = {
+ /* callback updated()
+ *
+ * Called when settings are updated
+ */
+ updated : []
+ };
+
+ this.hasUI = false;
+
+ this.settingCheckboxes = {};
+
+ this.textCallbacks = [];
+ this.visCallbacks = [];
- for(let attr in this.defaultSettings) {
- if(this.defaultSettings.hasOwnProperty(attr)) {
- if(defaults[attr] === undefined) {
- defaults[attr] = this.defaultSettings[attr];
- }
- // don't write to local if it's a temp settings
- if(this.ephemeralSettings.indexOf(attr) != -1) {
- continue;
- }
- if(defaults.overwriteLocal) {
- localStorage[attr] = defaults[attr];
+ for(let attr in defaultSettings) {
+ if(defaultSettings.hasOwnProperty(attr)) {
+ if(defaults[attr] === undefined) {
+ defaults[attr] = defaultSettings[attr];
+ }
+ // don't write to local if it's a temp settings
+ if(ephemeralSettings.indexOf(attr) != -1) {
+ continue;
+ }
+ if(defaults.overwriteLocal) {
+ localStorage[attr] = defaults[attr];
+ }
+ // populate defaults, ignoring current
+ if(localStorage[attr] === undefined) {
+ localStorage[attr] = defaults[attr];
+ }
}
- // populate defaults, ignoring current
- if(localStorage[attr] === undefined) {
- localStorage[attr] = defaults[attr];
- }
- }
- }
+ }
- this.defaults = defaults;
-}
+ this.defaults = defaults;
+ }
-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) {
- this.value = this.value.replace(/\D/g,'');
- if(this.value === "" || this.value < 1) {
- this.value = "";
- return;
- }
- localStorage[variable] = this.value;
- self.updateConditionals();
- self.callEventListeners("updated");
- };
+ initUI(huesWin) {
+ let root = document.createElement("div");
+ root.className = "hues-options";
+
+ // Don't make in every loop
+ let intValidator = function(self, variable) {
+ this.value = this.value.replace(/\D/g,'');
+ if(this.value === "" || this.value < 1) {
+ this.value = "";
+ return;
+ }
+ localStorage[variable] = this.value;
+ self.updateConditionals();
+ self.callEventListeners("updated");
+ };
- // To order things nicely
- for(let cat in this.settingsCategories) {
- if(this.settingsCategories.hasOwnProperty(cat)) {
- 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 = document.createElement("div");
- let setting = this.settingsOptions[setName];
- setContainer.textContent = setting.name;
- setContainer.className = "settings-individual";
- 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 = document.createElement("input");
- // Save checkbox so we can update UI stuff
- this.settingCheckboxes[setName + "-" + option] = checkbox;
- checkbox.className = "settings-checkbox";
- checkbox.type = "radio";
- checkbox.value = 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(setName, this.value);
- }.bind(checkbox, this);
- buttonContainer.appendChild(checkbox);
- // So we can style this nicely
- 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 = 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 = document.createElement("input");
- input.setAttribute("type", "text");
- input.className = "settings-input";
- input.value = localStorage[option.variable];
- // TODO: support more than just positive ints when the need arises
- if(option.inputType == "int") {
- input.oninput = intValidator.bind(input, this, option.variable);
+ // To order things nicely
+ for(let cat in settingsCategories) {
+ if(settingsCategories.hasOwnProperty(cat)) {
+ let catContainer = document.createElement("div");
+ catContainer.textContent = cat;
+ catContainer.className = "settings-category";
+ let cats = settingsCategories[cat];
+ for(let i = 0; i < cats.length; i++) {
+ let setName = cats[i];
+ let setContainer = document.createElement("div");
+ let setting = settingsOptions[setName];
+ setContainer.textContent = setting.name;
+ setContainer.className = "settings-individual";
+ 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 = document.createElement("input");
+ // Save checkbox so we can update UI stuff
+ this.settingCheckboxes[setName + "-" + option] = checkbox;
+ checkbox.className = "settings-checkbox";
+ checkbox.type = "radio";
+ checkbox.value = option;
+ let unique = 0;
+ // Lets us have multiple hues on 1 page
+ let id = setName + "-" + option + "-";
+ while(document.getElementById(id + unique)) {
+ unique++;
}
- input.autofocus = false;
- buttonContainer.appendChild(input);
- if(option.visiblity) {
- this.visCallbacks.push({func:option.visiblity, element:input});
- input.style.visibility = option.visiblity() ? "visible" : "hidden";
+ checkbox.name = setName + "-" + unique;
+ checkbox.id = id + unique;
+ if(localStorage[setName] == option) {
+ checkbox.checked = true;
+ }
+ checkbox.onclick = function(self) {
+ self.set(setName, this.value);
+ }.bind(checkbox, this);
+ buttonContainer.appendChild(checkbox);
+ // So we can style this nicely
+ 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 = 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 = document.createElement("input");
+ input.setAttribute("type", "text");
+ input.className = "settings-input";
+ input.value = localStorage[option.variable];
+ // TODO: support more than just positive ints when the need arises
+ if(option.inputType == "int") {
+ input.oninput = intValidator.bind(input, this, option.variable);
+ }
+ input.autofocus = false;
+ buttonContainer.appendChild(input);
+ if(option.visiblity) {
+ this.visCallbacks.push({func:option.visiblity, element:input});
+ input.style.visibility = option.visiblity() ? "visible" : "hidden";
+ }
}
}
- }
+ }
+ setContainer.appendChild(buttonContainer);
+ catContainer.appendChild(setContainer);
}
- setContainer.appendChild(buttonContainer);
- catContainer.appendChild(setContainer);
+ root.appendChild(catContainer);
}
- root.appendChild(catContainer);
}
+ huesWin.addTab("OPTIONS", root);
+ this.hasUI = true;
}
- 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];
+ get(setting) {
+ if(this.defaults.hasOwnProperty(setting)) {
+ if(ephemeralSettings.indexOf(setting) != -1) {
+ return this.defaults[setting];
+ } else {
+ return localStorage[setting];
+ }
} else {
- return localStorage[setting];
+ console.log("WARNING: Attempted to fetch invalid setting:", setting);
+ return null;
}
- } 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();
- let opt = this.settingsOptions[setting];
- if(!opt || opt.options.indexOf(value) == -1) {
- console.log(value, "is not a valid value for", setting);
- return false;
+ // Set a named index to its named value, returns false if name doesn't exist
+ set(setting, value) {
+ value = value.toLowerCase();
+ let opt = settingsOptions[setting];
+ if(!opt || opt.options.indexOf(value) == -1) {
+ console.log(value, "is not a valid value for", setting);
+ return false;
+ }
+ // for updating the UI selection
+ try {
+ this.settingCheckboxes[setting + "-" + value].checked = true;
+ } catch(e) {}
+ localStorage[setting] = value;
+ this.updateConditionals();
+ this.callEventListeners("updated");
+ return true;
}
- // for updating the UI selection
- try {
- this.settingCheckboxes[setting + "-" + value].checked = true;
- } catch(e) {}
- localStorage[setting] = value;
- this.updateConditionals();
- this.callEventListeners("updated");
- return true;
-};
-HuesSettings.prototype.updateConditionals = function() {
- // update any conditionally formatted settings text
- for(let i = 0; i < this.textCallbacks.length; i++) {
- let text = this.textCallbacks[i];
- text.element.textContent = text.func();
- }
- for(let i = 0; i < this.visCallbacks.length; i++) {
- let callback = this.visCallbacks[i];
- callback.element.style.visibility = callback.func() ? "visible" : "hidden";
+ updateConditionals() {
+ // update any conditionally formatted settings text
+ for(let i = 0; i < this.textCallbacks.length; i++) {
+ let text = this.textCallbacks[i];
+ text.element.textContent = text.func();
+ }
+ for(let i = 0; i < this.visCallbacks.length; i++) {
+ let callback = this.visCallbacks[i];
+ callback.element.style.visibility = callback.func() ? "visible" : "hidden";
+ }
}
-};
-// Note: This is not defaults as per defaultSettings, but those merged with
-// the defaults given in the initialiser
-HuesSettings.prototype.setDefaults = function() {
- for(let attr in this.defaults) {
- if(this.defaults.hasOwnProperty(attr)) {
- if(this.ephemeralSettings.indexOf(attr) != -1) {
- continue;
+ // Note: This is not defaults as per defaultSettings, but those merged with
+ // the defaults given in the initialiser
+ setDefaults() {
+ for(let attr in this.defaults) {
+ if(this.defaults.hasOwnProperty(attr)) {
+ if(ephemeralSettings.indexOf(attr) != -1) {
+ continue;
+ }
+ localStorage[attr] = this.defaults[attr];
}
- localStorage[attr] = this.defaults[attr];
}
}
-};
-HuesSettings.prototype.callEventListeners = function(ev) {
- let args = Array.prototype.slice.call(arguments, 1);
- this.eventListeners[ev].forEach(function(callback) {
- callback.apply(null, args);
- });
-};
+ callEventListeners(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);
+ addEventListener(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);
+ removeEventListener(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;
diff --git a/src/js/HuesUI.js b/src/js/HuesUI.js
index 8197cd3..1fd6bed 100644
--- a/src/js/HuesUI.js
+++ b/src/js/HuesUI.js
@@ -27,1185 +27,1160 @@
to put all your own elements under, but make a div
underneath so it can be entirely hidden.
*/
-function HuesUI(parent, name) {
- if(!parent) {
- return;
+class HuesUI {
+
+ constructor(parent, name) {
+ if(!parent) {
+ return;
+ }
+ this.root = document.createElement("div");
+ this.root.className = name ? name : this.constructor.name;
+ parent.appendChild(this.root);
+ this.root.style.display = "none";
+
+ this.core = null;
+
+ this.imageName = null;
+ this.imageLink = null;
+
+ this.songName = null;
+ this.songLink = null;
+
+ this.hueName = null;
+
+ this.imagePrev = null;
+ this.imageNext = null;
+ this.songPrev = null;
+ this.songNext = null;
+
+ this.beatCount = null;
+ this.timer = null;
+ this.xBlur = null;
+ this.yBlur = null;
+
+ this.settingsToggle = null;
+ this.hideToggle = null;
+
+ // To deregister on UI hide we need to keep track of these
+ // Each callback is { name : "callbackname", func : function }
+ // Add using this.addCoreCallback
+ this.callbacks = [];
+
+ // Put this near the links to song/image lists/ Bottom right alignment
+ this.listContainer = null;
+ // Must be dynamic width, 64 pixels high. Will be filled with visualiser
+ this.visualiserContainer = null;
+
+ this.hidden = false;
+
+ this.initUI();
}
- this.root = document.createElement("div");
- this.root.className = name ? name : this.constructor.name;
- parent.appendChild(this.root);
- this.root.style.display = "none";
- this.core = null;
+ addCoreCallback(name, func) {
+ this.callbacks.push({name : name, func : func});
+ }
- this.imageName = null;
- this.imageLink = null;
+ initUI() {
+ // Major info, image, song names
+ let imageName = document.createElement("div");
+ this.imageName = imageName;
+
+ this.imageLink = document.createElement("a");
+ this.imageLink.target = "_blank";
+ this.imageName.appendChild(this.imageLink);
+
+ let songName = document.createElement("div");
+ this.songName = songName;
+
+ this.songLink = document.createElement("a");
+ this.songLink.target = "_blank";
+ this.songName.appendChild(this.songLink);
+
+ let hueName = document.createElement("div");
+ this.hueName = hueName;
+
+ // Prev/next controls
+ let imagePrev = document.createElement("div");
+ imagePrev.textContent = "<";
+ imagePrev.onclick = () => {this.core.previousImage();};
+ this.imagePrev = imagePrev;
+ let imageNext = document.createElement("div");
+ imageNext.textContent = ">";
+ imageNext.onclick = () =>{this.core.nextImage();};
+ this.imageNext = imageNext;
+ let songPrev = document.createElement("div");
+ songPrev.textContent = "<";
+ this.songPrev = songPrev;
+ songPrev.onclick = () =>{this.core.previousSong();};
+ let songNext = document.createElement("div");
+ songNext.textContent = ">";
+ songNext.onclick = () =>{this.core.nextSong();};
+ this.songNext = songNext;
+
+ let songList = document.createElement("div");
+ songList.textContent = "SONGS";
+ songList.onclick = () =>{this.core.toggleSongList();};
+ this.songList = songList;
+ let imageList = document.createElement("div");
+ imageList.textContent = "IMAGES";
+ imageList.onclick = () =>{this.core.toggleImageList();};
+ this.imageList = imageList;
+
+ // Beat timer, x and y blur, millis timer
+ this.timer = document.createElement("div");
+ this.timer.textContent = "T=$0x00000";
+
+ this.beatCount = document.createElement("div");
+ this.beatCount.textContent = "B=$0x0000";
+
+ this.xBlur = document.createElement("div");
+ this.xBlur.textContent = "X=$0x00";
+
+ this.yBlur = document.createElement("div");
+ this.yBlur.textContent = "Y=$0x00";
+
+ // Config stuff
+ this.settingsToggle = document.createElement("div");
+ this.settingsToggle.innerHTML = ''; // COG
+ this.settingsToggle.className = 'hues-icon';
+ this.settingsToggle.onclick = () => {
+ this.core.window.toggle();
+ };
+
+ this.hideToggle = document.createElement("div");
+ this.hideToggle.innerHTML = "▼";
+ this.hideToggle.onclick = () => {
+ this.toggleHide();
+ };
+
+ this.listContainer = document.createElement("div");
+ this.visualiserContainer = document.createElement("div");
+
+ this.addCoreCallback("newsong", this.newSong.bind(this));
+ this.addCoreCallback("newimage", this.newImage.bind(this));
+ this.addCoreCallback("newcolour", this.newColour.bind(this));
+ this.addCoreCallback("blurupdate", this.blurUpdated.bind(this));
+ this.addCoreCallback("time", this.updateTime.bind(this));
+ this.addCoreCallback("invert", this.invert.bind(this));
+ this.resizeHandler = this.resize.bind(this);
+ }
- this.songName = null;
- this.songLink = null;
+ connectCore(core) {
+ this.core = core;
+ this.root.style.display = "block";
+ if(core.resourceManager.hasUI) {
+ this.listContainer.appendChild(core.resourceManager.listView);
+ }
+ this.visualiserContainer.appendChild(this.core.visualiser);
- this.hueName = null;
+ this.callbacks.forEach(function(callback) {
+ core.addEventListener(callback.name, callback.func);
+ });
+ window.addEventListener('resize', this.resizeHandler);
+ this.resizeHandler();
+ }
- this.imagePrev = null;
- this.imageNext = null;
- this.songPrev = null;
- this.songNext = null;
+ disconnect() {
+ this.callbacks.forEach(callback => {
+ this.core.removeEventListener(callback.name, callback.func);
+ });
+ this.core = null;
+ this.root.style.display = "none";
+ while (this.listContainer.firstElementChild) {
+ this.listContainer.removeChild(this.listContainer.firstElementChild);
+ }
+ while (this.visualiserContainer.firstElementChild) {
+ this.visualiserContainer.removeChild(this.visualiserContainer.firstElementChild);
+ }
+ window.removeEventListener('resize', this.resizeHandler);
+ }
- this.beatCount = null;
- this.timer = null;
- this.xBlur = null;
- this.yBlur = null;
+ // ONLY FOR CHANGING UI, NOT FOR "HIDE" FEATURE
+ show() {
+ this.root.style.display = "block";
+ }
- this.settingsToggle = null;
- this.hideToggle = null;
-
- // To deregister on UI hide we need to keep track of these
- // Each callback is { name : "callbackname", func : function }
- // Add using this.addCoreCallback
- this.callbacks = [];
+ // ONLY FOR CHANGING UI, NOT FOR "HIDE" FEATURE
+ hide() {
+ this.root.style.display = "none";
+ }
- // Put this near the links to song/image lists/ Bottom right alignment
- this.listContainer = null;
- // Must be dynamic width, 64 pixels high. Will be filled with visualiser
- this.visualiserContainer = null;
+ toggleHide() {
+ this.hidden = !this.hidden;
+ if(this.hidden) {
+ this.root.classList.add("hues-ui--hidden");
+ } else {
+ this.root.classList.remove("hues-ui--hidden");
+ }
+ }
- this.hidden = false;
+ resize() {}
+ updateVolume(vol) {}
- this.initUI();
-}
+ newSong(song) {
+ if(!song) {
+ return;
+ }
+
+ this.songLink.textContent = song.title.toUpperCase();
+ this.songLink.href = song.source;
+ }
+
+ newImage(image) {
+ if(!image) {
+ return;
+ }
+
+ let name = image.fullname ? image.fullname : image.name;
+
+ this.imageLink.textContent = name.toUpperCase();
+ this.imageLink.href = image.source ? image.source : "";
+ }
+
+ newColour(colour) {
+ this.hueName.textContent = colour.n.toUpperCase();
+ }
+
+ blurUpdated(x, y) {
+ x = Math.floor(x * 0xFF);
+ y = Math.floor(y * 0xFF);
+ this.xBlur.textContent = "X=" + this.intToHex(x, 2);
+ this.yBlur.textContent = "Y=" + this.intToHex(y, 2);
+ }
-HuesUI.prototype.addCoreCallback = function(name, func) {
- this.callbacks.push({name : name, func : func});
-};
-
-HuesUI.prototype.initUI = function() {
- // Major info, image, song names
- let imageName = document.createElement("div");
- this.imageName = imageName;
-
- this.imageLink = document.createElement("a");
- this.imageLink.target = "_blank";
- this.imageName.appendChild(this.imageLink);
-
- let songName = document.createElement("div");
- this.songName = songName;
-
- this.songLink = document.createElement("a");
- this.songLink.target = "_blank";
- this.songName.appendChild(this.songLink);
-
- let hueName = document.createElement("div");
- this.hueName = hueName;
-
- // Prev/next controls
- let imagePrev = document.createElement("div");
- imagePrev.textContent = "<";
- imagePrev.onclick = () => {this.core.previousImage();};
- this.imagePrev = imagePrev;
- let imageNext = document.createElement("div");
- imageNext.textContent = ">";
- imageNext.onclick = () =>{this.core.nextImage();};
- this.imageNext = imageNext;
- let songPrev = document.createElement("div");
- songPrev.textContent = "<";
- this.songPrev = songPrev;
- songPrev.onclick = () =>{this.core.previousSong();};
- let songNext = document.createElement("div");
- songNext.textContent = ">";
- songNext.onclick = () =>{this.core.nextSong();};
- this.songNext = songNext;
-
- let songList = document.createElement("div");
- songList.textContent = "SONGS";
- songList.onclick = () =>{this.core.toggleSongList();};
- this.songList = songList;
- let imageList = document.createElement("div");
- imageList.textContent = "IMAGES";
- imageList.onclick = () =>{this.core.toggleImageList();};
- this.imageList = imageList;
-
- // Beat timer, x and y blur, millis timer
- this.timer = document.createElement("div");
- this.timer.textContent = "T=$0x00000";
-
- this.beatCount = document.createElement("div");
- this.beatCount.textContent = "B=$0x0000";
-
- this.xBlur = document.createElement("div");
- this.xBlur.textContent = "X=$0x00";
-
- this.yBlur = document.createElement("div");
- this.yBlur.textContent = "Y=$0x00";
-
- // Config stuff
- this.settingsToggle = document.createElement("div");
- this.settingsToggle.innerHTML = ''; // COG
- this.settingsToggle.className = 'hues-icon';
- this.settingsToggle.onclick = () => {
- this.core.window.toggle();
- };
-
- this.hideToggle = document.createElement("div");
- this.hideToggle.innerHTML = "▼";
- this.hideToggle.onclick = () => {
- this.toggleHide();
- };
-
- this.listContainer = document.createElement("div");
- this.visualiserContainer = document.createElement("div");
-
- this.addCoreCallback("newsong", this.newSong.bind(this));
- this.addCoreCallback("newimage", this.newImage.bind(this));
- this.addCoreCallback("newcolour", this.newColour.bind(this));
- this.addCoreCallback("blurupdate", this.blurUpdated.bind(this));
- this.addCoreCallback("time", this.updateTime.bind(this));
- this.addCoreCallback("invert", this.invert.bind(this));
- this.resizeHandler = this.resize.bind(this);
-};
-
-HuesUI.prototype.connectCore = function(core) {
- this.core = core;
- this.root.style.display = "block";
- if(core.resourceManager.hasUI) {
- this.listContainer.appendChild(core.resourceManager.listView);
- }
- this.visualiserContainer.appendChild(this.core.visualiser);
-
- this.callbacks.forEach(function(callback) {
- core.addEventListener(callback.name, callback.func);
- });
- window.addEventListener('resize', this.resizeHandler);
- this.resizeHandler();
-};
-
-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) {
- this.listContainer.removeChild(this.listContainer.firstElementChild);
- }
- while (this.visualiserContainer.firstElementChild) {
- this.visualiserContainer.removeChild(this.visualiserContainer.firstElementChild);
- }
- window.removeEventListener('resize', this.resizeHandler);
-};
-
-// ONLY FOR CHANGING UI, NOT FOR "HIDE" FEATURE
-HuesUI.prototype.show = function() {
- this.root.style.display = "block";
-};
-
-// ONLY FOR CHANGING UI, NOT FOR "HIDE" FEATURE
-HuesUI.prototype.hide = function() {
- this.root.style.display = "none";
-};
-
-HuesUI.prototype.toggleHide = function() {
- this.hidden = !this.hidden;
- if(this.hidden) {
- this.root.classList.add("hues-ui--hidden");
- } else {
- this.root.classList.remove("hues-ui--hidden");
- }
-};
-
-HuesUI.prototype.resize = function() {};
-HuesUI.prototype.updateVolume = function(vol) {};
-
-HuesUI.prototype.newSong = function(song) {
- if(!song) {
- return;
- }
-
- this.songLink.textContent = song.title.toUpperCase();
- this.songLink.href = song.source;
-};
-
-HuesUI.prototype.newImage = function(image) {
- if(!image) {
- return;
- }
-
- let name = image.fullname ? image.fullname : image.name;
-
- this.imageLink.textContent = name.toUpperCase();
- this.imageLink.href = image.source ? image.source : "";
-};
-
-HuesUI.prototype.newColour = function(colour) {
- this.hueName.textContent = colour.n.toUpperCase();
-};
-
-HuesUI.prototype.blurUpdated = function(x, y) {
- x = Math.floor(x * 0xFF);
- y = Math.floor(y * 0xFF);
- this.xBlur.textContent = "X=" + this.intToHex(x, 2);
- this.yBlur.textContent = "Y=" + this.intToHex(y, 2);
-};
-
-HuesUI.prototype.updateTime = function(time) {
- time = Math.floor(time * 1000);
- this.timer.textContent = "T=" + this.intToHex(time, 5);
-};
-
-HuesUI.prototype.intToHex = function(num, pad) {
- let str = Math.abs(num).toString(16);
- while (str.length < pad)
- str = "0" + str;
- let prefix = num < 0 ? "-" : "$";
- return prefix + "0x" + str;
-};
-
-HuesUI.prototype.invert = function(invert) {
- if (invert) {
- this.root.classList.add("inverted");
- } else {
- this.root.classList.remove("inverted");
- }
-};
+ updateTime(time) {
+ time = Math.floor(time * 1000);
+ this.timer.textContent = "T=" + this.intToHex(time, 5);
+ }
+
+ intToHex(num, pad) {
+ let str = Math.abs(num).toString(16);
+ while (str.length < pad)
+ str = "0" + str;
+ let prefix = num < 0 ? "-" : "$";
+ return prefix + "0x" + str;
+ }
+
+ invert(invert) {
+ if (invert) {
+ this.root.classList.add("inverted");
+ } else {
+ this.root.classList.remove("inverted");
+ }
+ }
+}
/*
Individual UIs ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*/
-function RetroUI(parent, name) {
- this.container = null;
- this.mode = null;
- this.beatBar = null;
- this.colourIndex = null;
- this.version = null;
- this.imageModeAuto = null;
- this.imageModeAuto = null;
- this.subControls = null;
-
- HuesUI.call(this, parent, name ? name : "RetroUI");
-}
+class RetroUI extends HuesUI {
+ constructor(parent, name) {
+ super(parent, name ? name : "RetroUI");
+ }
-RetroUI.prototype = Object.create(HuesUI.prototype);
-RetroUI.prototype.constructor = RetroUI;
-
-RetroUI.prototype.initUI = function() {
- HuesUI.prototype.initUI.call(this);
-
- let container = document.createElement("div");
- container.className = "hues-r-container";
- this.root.appendChild(container);
- this.container = container;
-
- this.mode = document.createElement("div");
- container.appendChild(this.mode);
- container.appendChild(this.imageName);
- container.appendChild(this.timer);
- container.appendChild(this.beatCount);
- container.appendChild(this.xBlur);
- container.appendChild(this.yBlur);
-
- this.colourIndex = document.createElement("div");
- this.colourIndex.textContent = "C=$0x00";
- container.appendChild(this.colourIndex);
-
- this.version = document.createElement("div");
- container.appendChild(this.version);
-
- container.appendChild(this.hueName);
- container.appendChild(this.songName);
-
- this.beatBar = document.createElement("div");
- container.appendChild(this.beatBar);
-
- this.controls = document.createElement("div");
- this.controls.className = "hues-r-controls";
-
- let imageMode = document.createElement("div");
- this.imageModeManual = document.createElement("div");
- this.imageModeManual.textContent = "NORMAL";
- this.imageModeManual.onclick = () => {
- this.core.setIsFullAuto(false);
- };
- this.imageModeManual.className = "hues-r-manualmode hues-r-button";
- this.imageModeAuto = document.createElement("div");
- this.imageModeAuto.textContent = "FULL AUTO";
- this.imageModeAuto.onclick = () => {
- this.core.setIsFullAuto(true);
- };
- this.imageModeAuto.className = "hues-r-automode hues-r-button";
- imageMode.appendChild(this.imageModeManual);
- imageMode.appendChild(this.imageModeAuto);
-
- this.imagePrev.className = "hues-r-button";
- this.imageNext.className = "hues-r-button";
- this.songPrev.className = "hues-r-button";
- this.songNext.className = "hues-r-button";
- this.controls.appendChild(this.imagePrev);
- this.controls.appendChild(imageMode);
- this.controls.appendChild(this.imageNext);
-
- this.songList.className = "hues-r-songs hues-r-button";
- this.controls.appendChild(this.songPrev);
- this.controls.appendChild(this.songList);
- this.controls.appendChild(this.songNext);
-
- this.root.appendChild(this.controls);
-
- let subControl = document.createElement("div");
- subControl.className = "hues-r-subcontrols";
- subControl.appendChild(this.settingsToggle);
- this.imageList.textContent = "C";
- subControl.appendChild(this.imageList);
- subControl.appendChild(this.hideToggle);
- this.subControls = subControl;
-
- this.root.appendChild(subControl);
-
- this.hideRestore = document.createElement("div");
- this.hideRestore.className = "hues-r-hiderestore";
- this.hideRestore.innerHTML = "▲";
- this.hideRestore.onclick = () => {
- this.toggleHide();
- };
- this.root.appendChild(this.hideRestore);
-
- this.listContainer.className = "hues-r-listcontainer";
- this.root.appendChild(this.listContainer);
-
- this.visualiserContainer.className = "hues-r-visualisercontainer";
- this.root.appendChild(this.visualiserContainer);
-
- this.addCoreCallback("beat", this.beat.bind(this));
- this.addCoreCallback("newmode", this.newMode.bind(this));
-};
-
-RetroUI.prototype.toggleHide = function() {
- this.hidden = !this.hidden;
- if(this.hidden) {
- 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.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");
+ initUI() {
+ super.initUI();
+
+ let container = document.createElement("div");
+ container.className = "hues-r-container";
+ this.root.appendChild(container);
+ this.container = container;
+
+ this.mode = document.createElement("div");
+ container.appendChild(this.mode);
+ container.appendChild(this.imageName);
+ container.appendChild(this.timer);
+ container.appendChild(this.beatCount);
+ container.appendChild(this.xBlur);
+ container.appendChild(this.yBlur);
+
+ this.colourIndex = document.createElement("div");
+ this.colourIndex.textContent = "C=$0x00";
+ container.appendChild(this.colourIndex);
+
+ this.version = document.createElement("div");
+ container.appendChild(this.version);
+
+ container.appendChild(this.hueName);
+ container.appendChild(this.songName);
+
+ this.beatBar = document.createElement("div");
+ container.appendChild(this.beatBar);
+
+ this.controls = document.createElement("div");
+ this.controls.className = "hues-r-controls";
+
+ let imageMode = document.createElement("div");
+ this.imageModeManual = document.createElement("div");
+ this.imageModeManual.textContent = "NORMAL";
+ this.imageModeManual.onclick = () => {
+ this.core.setIsFullAuto(false);
+ };
+ this.imageModeManual.className = "hues-r-manualmode hues-r-button";
+ this.imageModeAuto = document.createElement("div");
+ this.imageModeAuto.textContent = "FULL AUTO";
+ this.imageModeAuto.onclick = () => {
+ this.core.setIsFullAuto(true);
+ };
+ this.imageModeAuto.className = "hues-r-automode hues-r-button";
+ imageMode.appendChild(this.imageModeManual);
+ imageMode.appendChild(this.imageModeAuto);
+
+ this.imagePrev.className = "hues-r-button";
+ this.imageNext.className = "hues-r-button";
+ this.songPrev.className = "hues-r-button";
+ this.songNext.className = "hues-r-button";
+ this.controls.appendChild(this.imagePrev);
+ this.controls.appendChild(imageMode);
+ this.controls.appendChild(this.imageNext);
+
+ this.songList.className = "hues-r-songs hues-r-button";
+ this.controls.appendChild(this.songPrev);
+ this.controls.appendChild(this.songList);
+ this.controls.appendChild(this.songNext);
+
+ this.root.appendChild(this.controls);
+
+ let subControl = document.createElement("div");
+ subControl.className = "hues-r-subcontrols";
+ subControl.appendChild(this.settingsToggle);
+ this.imageList.textContent = "C";
+ subControl.appendChild(this.imageList);
+ subControl.appendChild(this.hideToggle);
+ this.subControls = subControl;
+
+ this.root.appendChild(subControl);
+
+ this.hideRestore = document.createElement("div");
+ this.hideRestore.className = "hues-r-hiderestore";
+ this.hideRestore.innerHTML = "▲";
+ this.hideRestore.onclick = () => {
+ this.toggleHide();
+ };
+ this.root.appendChild(this.hideRestore);
+
+ this.listContainer.className = "hues-r-listcontainer";
+ this.root.appendChild(this.listContainer);
+
+ this.visualiserContainer.className = "hues-r-visualisercontainer";
+ this.root.appendChild(this.visualiserContainer);
+
+ this.addCoreCallback("beat", this.beat.bind(this));
+ this.addCoreCallback("newmode", this.newMode.bind(this));
}
-};
-RetroUI.prototype.connectCore = function(core) {
- HuesUI.prototype.connectCore.call(this, core);
+ toggleHide() {
+ this.hidden = !this.hidden;
+ if(this.hidden) {
+ 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.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.version.textContent = "V=$" + core.versionHex;
-};
+ connectCore(core) {
+ super.connectCore(core);
-RetroUI.prototype.newMode = function(isAuto) {
- this.mode.textContent = "M=" + (isAuto ? "FULL AUTO" : "NORMAL");
-};
+ this.version.textContent = "V=$" + core.versionHex;
+ }
-RetroUI.prototype.newImage = function(image) {
- if(!image) {
- return;
+ newMode(isAuto) {
+ this.mode.textContent = "M=" + (isAuto ? "FULL AUTO" : "NORMAL");
}
- this.imageLink.textContent = "I=" + image.name.toUpperCase();
- this.imageLink.href = image.source;
-};
+ newImage(image) {
+ if(!image) {
+ return;
+ }
-RetroUI.prototype.newColour = function(colour) {
- HuesUI.prototype.newColour.call(this, colour);
+ this.imageLink.textContent = "I=" + image.name.toUpperCase();
+ this.imageLink.href = image.source;
+ }
- this.colourIndex.textContent = "C=" + this.intToHex(this.core.colourIndex, 2);
-};
+ newColour(colour) {
+ super.newColour(colour);
-RetroUI.prototype.beat = function(beats, index) {
- let rest = beats.slice(1);
- this.beatBar.textContent = ">>" + rest;
- this.beatCount.textContent = "B=" + this.intToHex(index, 4);
-};
+ this.colourIndex.textContent = "C=" + this.intToHex(this.core.colourIndex, 2);
+ }
-RetroUI.prototype.resize = function() {
- this.core.visualiser.width = this.visualiserContainer.offsetWidth;
- this.core.resizeVisualiser();
-};
+ beat(beats, index) {
+ let rest = beats.slice(1);
+ this.beatBar.textContent = ">>" + rest;
+ this.beatCount.textContent = "B=" + this.intToHex(index, 4);
+ }
-function MinimalUI(parent, name) {
- RetroUI.call(this, parent, name ? name : "MinimalUI");
+ resize() {
+ this.core.visualiser.width = this.visualiserContainer.offsetWidth;
+ this.core.resizeVisualiser();
+ }
}
-MinimalUI.prototype = Object.create(RetroUI.prototype);
-MinimalUI.prototype.constructor = MinimalUI;
+class MinimalUI extends RetroUI {
+ constructor(parent, name) {
+ super(parent, name ? name : "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");
-
- this.xVariance = 10;
- this.yVariance = 20;
+ initUI() {
+ super.initUI();
+
+ this.root.removeChild(this.controls);
+ this.root.removeChild(this.subControls);
+ this.container.removeChild(this.beatBar);
+ this.container.innerHTML = "";
+ this.container.appendChild(this.beatBar);
+ }
}
-WeedUI.prototype = Object.create(RetroUI.prototype);
-WeedUI.prototype.constructor = WeedUI;
+class WeedUI extends RetroUI {
+ constructor(parent, name) {
+ super(parent, name ? name : "WeedUI");
-WeedUI.prototype.initUI = function() {
- RetroUI.prototype.initUI.call(this);
+ this.xVariance = 10;
+ this.yVariance = 20;
+ }
- this.container.removeChild(this.beatBar);
+ initUI() {
+ super.initUI();
- this.controls.className = "hues-w-controls";
- this.subControls.className = "hues-w-subcontrols";
+ this.container.removeChild(this.beatBar);
- let beatBar = document.createElement("div");
- beatBar.className = "hues-w-beatbar";
- this.root.appendChild(beatBar);
- this.beatBar = beatBar;
+ this.controls.className = "hues-w-controls";
+ this.subControls.className = "hues-w-subcontrols";
- let beatLeft = document.createElement("div");
- beatLeft.className = "hues-w-beatleft";
- beatBar.appendChild(beatLeft);
- this.beatLeft = beatLeft;
+ let beatBar = document.createElement("div");
+ beatBar.className = "hues-w-beatbar";
+ this.root.appendChild(beatBar);
+ this.beatBar = beatBar;
- let beatRight = document.createElement("div");
- beatRight.className = "hues-w-beatright";
- beatBar.appendChild(beatRight);
- this.beatRight = beatRight;
+ let beatLeft = document.createElement("div");
+ beatLeft.className = "hues-w-beatleft";
+ beatBar.appendChild(beatLeft);
+ this.beatLeft = beatLeft;
- this.imageModeManual.textContent = "ONE";
- this.imageModeAuto.textContent = "MANY";
-
- this.visualiserContainer.className += " hues-w-visualisercontainer";
-};
-
-WeedUI.prototype.toggleHide = function() {
- RetroUI.prototype.toggleHide.call(this);
- if(this.hidden) {
- this.beatBar.classList.add("hues-ui--hidden");
- } else {
- this.beatBar.classList.remove("hues-ui--hidden");
+ let beatRight = document.createElement("div");
+ beatRight.className = "hues-w-beatright";
+ beatBar.appendChild(beatRight);
+ this.beatRight = beatRight;
+
+ this.imageModeManual.textContent = "ONE";
+ this.imageModeAuto.textContent = "MANY";
+
+ this.visualiserContainer.className += " hues-w-visualisercontainer";
}
-};
-WeedUI.prototype.beat = function(beats, index) {
- let rest = beats.slice(1);
+ toggleHide() {
+ super.toggleHide(this);
+ if(this.hidden) {
+ this.beatBar.classList.add("hues-ui--hidden");
+ } else {
+ this.beatBar.classList.remove("hues-ui--hidden");
+ }
+ }
- this.beatLeft.textContent = rest;
- this.beatRight.textContent = rest;
-
- this.beatCount.textContent = "B=" + this.intToHex(index, 4);
+ beat(beats, index) {
+ let rest = beats.slice(1);
+
+ this.beatLeft.textContent = rest;
+ this.beatRight.textContent = rest;
+
+ this.beatCount.textContent = "B=" + this.intToHex(index, 4);
+
+ if(["x", "o", "X", "O"].indexOf(beats[0]) != -1) {
+ let beatCenter = document.createElement("div");
+ beatCenter.className = "hues-w-beataccent";
+ let rot = this.round10(15 - Math.random() * 30);
+ let x = this.round10(- this.xVariance / 2 + Math.random() * this.xVariance);
+ let y = this.round10(30 - this.yVariance / 2 + Math.random() * this.yVariance);
+ let transform = "rotate(" + rot + "deg) translate(" + x + "px, " + y + "px)";
+ beatCenter.style.MozTransform = transform;
+ beatCenter.style.webkitTransform = transform;
+ beatCenter.style.transform = transform;
+ beatCenter.textContent = beats[0].toUpperCase();
+ this.root.appendChild(beatCenter);
+ window.setTimeout(this.removeBeat.bind(this, beatCenter), 1500);
+ }
+ }
+
+ round10(num) {
+ return Math.round(num * 10) / 10;
+ }
+
+ removeBeat(element) {
+ this.root.removeChild(element);
+ }
+}
+
+class ModernUI extends HuesUI {
+ constructor(parent, name) {
+ super(parent, name ? name : "ModernUI");
+
+ this.textSize_normal = 0;
+ this.textSize_small = 0;
+ this.songLink_size = 0;
+ this.imageLink_size = 0;
+
+ this.currentBeat = ".";
+
+ this.hidden = 0; // we have a 3 stage hide
+ }
+
+ initUI() {
+ super.initUI();
+
+ this.imageName.className = "hues-m-imagename";
+ this.songName.className = "hues-m-songtitle";
+
+ let controls = document.createElement("div");
+ controls.className = "hues-m-controls";
+ this.root.appendChild(controls);
+ this.controls = controls;
+
+ controls.appendChild(this.imageName);
+ controls.appendChild(this.songName);
+
+ let leftBox = document.createElement("div");
+ leftBox.className = "hues-m-leftbox";
+ controls.appendChild(leftBox);
+ this.leftBox = leftBox;
+
+ this.hueName.className = "hues-m-huename";
+ leftBox.appendChild(this.hueName);
+
+ let volCluster = document.createElement("div");
+ volCluster.className = "hues-m-vol-cluster";
+ leftBox.appendChild(volCluster);
+
+ this.settingsToggle.className += " hues-m-cog";
+ volCluster.appendChild(this.settingsToggle);
+
+ this.hideToggle.className = "hues-m-hide";
+ volCluster.appendChild(this.hideToggle);
+
+ let volBar = document.createElement("div");
+ volBar.className = "hues-m-vol-bar";
+ this.volBar = volBar;
+ volCluster.appendChild(volBar);
+
+ let label = document.createElement("div");
+ label.textContent = "VOL";
+ label.className = "hues-m-vol-label";
+ label.onclick = () => {
+ this.core.soundManager.toggleMute();
+ };
+ volBar.appendChild(label);
+ this.volLabel = label;
+
+ this.infoToggle = document.createElement("div");
+ this.infoToggle.innerHTML = '?';
+ this.infoToggle.className = "hues-m-question";
+ this.infoToggle.onclick = () => {
+ this.core.window.selectTab("INFO");
+ };
+ volCluster.appendChild(this.infoToggle);
+
+ let input = document.createElement("input");
+ input.type = "range";
+ input.min = 0;
+ input.max = 1;
+ input.step = 0.1;
+ volBar.appendChild(input);
+ this.volInput = input;
+ input.oninput = () => {
+ this.core.soundManager.setVolume(parseFloat(input.value));
+ };
+
+ let rightBox = document.createElement("div");
+ rightBox.className = "hues-m-rightbox";
+ controls.appendChild(rightBox);
+ this.rightBox = rightBox;
+
+ //Song/image controls
+ let songs = document.createElement("div");
+ songs.className = "hues-m-controlblock";
+ this.songBlock = songs;
+ this.songList.className = "hues-m-songbutton";
+
+ let songControls = document.createElement("div");
+ songControls.className = "hues-m-controlbuttons";
+ this.songPrev.className = "hues-m-prevbutton";
+ this.songNext.className = "hues-m-nextbutton";
+ this.songShuffle = document.createElement("div");
+ this.songShuffle.innerHTML = ''; // SHUFFLE
+ this.songShuffle.className = "hues-m-actbutton hues-icon";
+ this.songShuffle.onclick = () => {this.core.randomSong();};
+ songs.appendChild(this.songList);
+ songControls.appendChild(this.songPrev);
+ songControls.appendChild(this.songShuffle);
+ songControls.appendChild(this.songNext);
+ songs.appendChild(songControls);
+ rightBox.appendChild(songs);
+
+ let images = document.createElement("div");
+ images.className = "hues-m-controlblock";
+ this.imageList.className = "hues-m-songbutton";
+ this.imageBlock = images;
+
+ let imageControls = document.createElement("div");
+ imageControls.className = "hues-m-controlbuttons";
+
+ this.imageMode = document.createElement("div");
+ this.imageMode.innerHTML = ""; // PLAY
+ this.imageMode.className = "hues-m-actbutton hues-icon";
+ this.imageMode.onclick = () => {this.core.toggleFullAuto();};
+ this.imagePrev.className = "hues-m-prevbutton";
+ this.imageNext.className = "hues-m-nextbutton";
+ images.appendChild(this.imageList);
+ imageControls.appendChild(this.imagePrev);
+ imageControls.appendChild(this.imageMode);
+ imageControls.appendChild(this.imageNext);
+ images.appendChild(imageControls);
+ rightBox.appendChild(images);
+
+ let leftInfo = document.createElement("div");
+ leftInfo.className = "hues-m-leftinfo";
+ let rightInfo = document.createElement("div");
+ rightInfo.className = "hues-m-rightinfo";
+ leftInfo.appendChild(this.xBlur);
+ leftInfo.appendChild(this.yBlur);
+ rightInfo.appendChild(this.timer);
+ rightInfo.appendChild(this.beatCount);
+ this.rightInfo = rightInfo;
+ this.leftInfo = leftInfo;
+ controls.appendChild(leftInfo);
+ controls.appendChild(rightInfo);
+
+ this.visualiserContainer.className = "hues-m-visualisercontainer";
+ controls.appendChild(this.visualiserContainer);
+
+ let beatBar = document.createElement("div");
+ beatBar.className = "hues-m-beatbar";
+ this.root.appendChild(beatBar);
+ this.beatBar = beatBar;
+
+ let beatLeft = document.createElement("div");
+ beatLeft.className = "hues-m-beatleft";
+ beatBar.appendChild(beatLeft);
+ this.beatLeft = beatLeft;
+
+ let beatRight = document.createElement("div");
+ beatRight.className = "hues-m-beatright";
+ beatBar.appendChild(beatRight);
+ this.beatRight = beatRight;
- if(["x", "o", "X", "O"].indexOf(beats[0]) != -1) {
let beatCenter = document.createElement("div");
- beatCenter.className = "hues-w-beataccent";
- let rot = this.round10(15 - Math.random() * 30);
- let x = this.round10(- this.xVariance / 2 + Math.random() * this.xVariance);
- let y = this.round10(30 - this.yVariance / 2 + Math.random() * this.yVariance);
- let transform = "rotate(" + rot + "deg) translate(" + x + "px, " + y + "px)";
- beatCenter.style.MozTransform = transform;
- beatCenter.style.webkitTransform = transform;
- beatCenter.style.transform = transform;
- beatCenter.textContent = beats[0].toUpperCase();
+ beatCenter.className = "hues-m-beatcenter";
this.root.appendChild(beatCenter);
- window.setTimeout(this.removeBeat.bind(this, beatCenter), 1500);
- }
-};
-
-WeedUI.prototype.round10 = function(num) {
- return Math.round(num * 10) / 10;
-};
-
-WeedUI.prototype.removeBeat = function(element) {
- this.root.removeChild(element);
-};
-
-function ModernUI(parent, name) {
- this.beatBar = null;
- this.beatLeft = null;
- this.beatRight = null;
- this.beatCenter = null;
- this.rightBox = null;
- this.leftBox = null;
- this.rightInfo = null;
- this.leftInfo = null;
- this.controls = null;
- this.volInput = null;
- this.volLabel = null;
- this.hideRestore = null;
-
- this.textSize_normal = 0;
- this.textSize_small = 0;
- this.songLink_size = 0;
- this.imageLink_size = 0;
+ this.beatCenter = beatCenter;
+
+ this.hideRestore = document.createElement("div");
+ this.hideRestore.className = "hues-m-hiderestore";
+ this.hideRestore.onclick = () => {
+ this.toggleHide();
+ };
+ this.root.appendChild(this.hideRestore);
+
+ this.listContainer.className = "hues-m-listcontainer";
+ this.root.appendChild(this.listContainer);
+
+ this.addCoreCallback("beat", this.beat.bind(this));
+ this.addCoreCallback("newmode", this.newMode.bind(this));
+ }
+
+ toggleHide() {
+ // classList is new-ish, but if you have web audio you'll have this
+ 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("hues-ui--hidden");
+ this.beatCenter.classList.add("hues-ui--hidden");
+ /* falls through */
+ case 0:
+ this.controls.classList.add("hues-ui--hidden");
+ this.hideRestore.classList.add("hues-ui--hidden");
+ }
+ this.hidden = (this.hidden+1) % 3;
+ }
- this.currentBeat = ".";
+ updateVolume(vol) {
+ this.volInput.value = vol;
+ if(vol === 0) {
+ this.volLabel.textContent = "(VOL)";
+ } else {
+ this.volLabel.textContent = "VOL";
+ }
+ }
- HuesUI.call(this, parent, name ? name : "ModernUI");
+ newMode (isAuto) {
+ if(isAuto) {
+ this.imageMode.innerHTML = ''; // PAUSE;
+ } else {
+ this.imageMode.innerHTML = ""; // PLAY
+ }
+ }
- this.hidden = 0; // we have a 3 stage hide
-}
+ beat(beats, index) {
+ this.currentBeat = beats[0];
+ let rest = beats.slice(1);
-ModernUI.prototype = Object.create(HuesUI.prototype);
-ModernUI.prototype.constructor = ModernUI;
-
-ModernUI.prototype.initUI = function() {
- HuesUI.prototype.initUI.call(this);
-
- this.imageName.className = "hues-m-imagename";
- this.songName.className = "hues-m-songtitle";
-
- let controls = document.createElement("div");
- controls.className = "hues-m-controls";
- this.root.appendChild(controls);
- this.controls = controls;
-
- controls.appendChild(this.imageName);
- controls.appendChild(this.songName);
-
- let leftBox = document.createElement("div");
- leftBox.className = "hues-m-leftbox";
- controls.appendChild(leftBox);
- this.leftBox = leftBox;
-
- this.hueName.className = "hues-m-huename";
- leftBox.appendChild(this.hueName);
-
- let volCluster = document.createElement("div");
- volCluster.className = "hues-m-vol-cluster";
- leftBox.appendChild(volCluster);
-
- this.settingsToggle.className += " hues-m-cog";
- volCluster.appendChild(this.settingsToggle);
-
- this.hideToggle.className = "hues-m-hide";
- volCluster.appendChild(this.hideToggle);
-
- let volBar = document.createElement("div");
- volBar.className = "hues-m-vol-bar";
- this.volBar = volBar;
- volCluster.appendChild(volBar);
-
- let label = document.createElement("div");
- label.textContent = "VOL";
- label.className = "hues-m-vol-label";
- label.onclick = () => {
- this.core.soundManager.toggleMute();
- };
- volBar.appendChild(label);
- this.volLabel = label;
-
- this.infoToggle = document.createElement("div");
- this.infoToggle.innerHTML = '?';
- this.infoToggle.className = "hues-m-question";
- this.infoToggle.onclick = () => {
- this.core.window.selectTab("INFO");
- };
- volCluster.appendChild(this.infoToggle);
-
- let input = document.createElement("input");
- input.type = "range";
- input.min = 0;
- input.max = 1;
- input.step = 0.1;
- volBar.appendChild(input);
- this.volInput = input;
- input.oninput = () => {
- this.core.soundManager.setVolume(parseFloat(input.value));
- };
-
- let rightBox = document.createElement("div");
- rightBox.className = "hues-m-rightbox";
- controls.appendChild(rightBox);
- this.rightBox = rightBox;
-
- //Song/image controls
- let songs = document.createElement("div");
- songs.className = "hues-m-controlblock";
- this.songBlock = songs;
- this.songList.className = "hues-m-songbutton";
-
- let songControls = document.createElement("div");
- songControls.className = "hues-m-controlbuttons";
- this.songPrev.className = "hues-m-prevbutton";
- this.songNext.className = "hues-m-nextbutton";
- this.songShuffle = document.createElement("div");
- this.songShuffle.innerHTML = ''; // SHUFFLE
- this.songShuffle.className = "hues-m-actbutton hues-icon";
- this.songShuffle.onclick = () => {this.core.randomSong();};
- songs.appendChild(this.songList);
- songControls.appendChild(this.songPrev);
- songControls.appendChild(this.songShuffle);
- songControls.appendChild(this.songNext);
- songs.appendChild(songControls);
- rightBox.appendChild(songs);
-
- let images = document.createElement("div");
- images.className = "hues-m-controlblock";
- this.imageList.className = "hues-m-songbutton";
- this.imageBlock = images;
-
- let imageControls = document.createElement("div");
- imageControls.className = "hues-m-controlbuttons";
-
- this.imageMode = document.createElement("div");
- this.imageMode.innerHTML = ""; // PLAY
- this.imageMode.className = "hues-m-actbutton hues-icon";
- this.imageMode.onclick = () => {this.core.toggleFullAuto();};
- this.imagePrev.className = "hues-m-prevbutton";
- this.imageNext.className = "hues-m-nextbutton";
- images.appendChild(this.imageList);
- imageControls.appendChild(this.imagePrev);
- imageControls.appendChild(this.imageMode);
- imageControls.appendChild(this.imageNext);
- images.appendChild(imageControls);
- rightBox.appendChild(images);
-
- let leftInfo = document.createElement("div");
- leftInfo.className = "hues-m-leftinfo";
- let rightInfo = document.createElement("div");
- rightInfo.className = "hues-m-rightinfo";
- leftInfo.appendChild(this.xBlur);
- leftInfo.appendChild(this.yBlur);
- rightInfo.appendChild(this.timer);
- rightInfo.appendChild(this.beatCount);
- this.rightInfo = rightInfo;
- this.leftInfo = leftInfo;
- controls.appendChild(leftInfo);
- controls.appendChild(rightInfo);
-
- this.visualiserContainer.className = "hues-m-visualisercontainer";
- controls.appendChild(this.visualiserContainer);
-
- let beatBar = document.createElement("div");
- beatBar.className = "hues-m-beatbar";
- this.root.appendChild(beatBar);
- this.beatBar = beatBar;
-
- let beatLeft = document.createElement("div");
- beatLeft.className = "hues-m-beatleft";
- beatBar.appendChild(beatLeft);
- this.beatLeft = beatLeft;
-
- let beatRight = document.createElement("div");
- beatRight.className = "hues-m-beatright";
- beatBar.appendChild(beatRight);
- this.beatRight = beatRight;
-
- let beatCenter = document.createElement("div");
- beatCenter.className = "hues-m-beatcenter";
- this.root.appendChild(beatCenter);
- this.beatCenter = beatCenter;
-
- this.hideRestore = document.createElement("div");
- this.hideRestore.className = "hues-m-hiderestore";
- this.hideRestore.onclick = () => {
- this.toggleHide();
- };
- this.root.appendChild(this.hideRestore);
-
- this.listContainer.className = "hues-m-listcontainer";
- this.root.appendChild(this.listContainer);
-
- this.addCoreCallback("beat", this.beat.bind(this));
- this.addCoreCallback("newmode", this.newMode.bind(this));
-};
-
-ModernUI.prototype.toggleHide = function() {
- // classList is new-ish, but if you have web audio you'll have this
- 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("hues-ui--hidden");
- this.beatCenter.classList.add("hues-ui--hidden");
- /* falls through */
- case 0:
- this.controls.classList.add("hues-ui--hidden");
- this.hideRestore.classList.add("hues-ui--hidden");
+ this.beatLeft.textContent = rest;
+ this.beatRight.textContent = rest;
+
+
+ if (this.currentBeat != ".") {
+ while (this.beatCenter.firstElementChild) {
+ this.beatCenter.removeChild(this.beatCenter.firstElementChild);
+ }
+ let span = this.beatCenter.ownerDocument.createElement("span");
+ span.textContent = this.currentBeat;
+ this.beatCenter.appendChild(span);
+ }
+ this.beatCount.textContent = "B=" + this.intToHex(index, 4);
}
- this.hidden = (this.hidden+1) % 3;
-};
-ModernUI.prototype.updateVolume = function(vol) {
- this.volInput.value = vol;
- if(vol === 0) {
- this.volLabel.textContent = "(VOL)";
- } else {
- this.volLabel.textContent = "VOL";
+ // get the width of a single character in the link box for a given classname
+ textWidth(className) {
+ // Could be song or image link, don't care
+ let el = this.songLink;
+ let oldContent = el.innerHTML;
+
+ // offsetWidth is rounded, divide by 100
+ let text = "";
+ for(let i = 0; i < 100; i++) {
+ text += " ";
+ }
+ el.innerHTML = text;
+ // We override this just after so don't bother to restore it
+ el.className = className;
+ let size = el.offsetWidth / 100;
+
+ el.innerHTML = oldContent;
+
+ return size;
}
-};
-ModernUI.prototype.newMode = function(isAuto) {
- if(isAuto) {
- this.imageMode.innerHTML = ''; // PAUSE;
- } else {
- this.imageMode.innerHTML = ""; // PLAY
+ resize() {
+ this.textSize_normal = this.textWidth("");
+ this.textSize_small = this.textWidth("small");
+ this.songLink_size = this.songName.clientWidth;
+ this.imageLink_size = this.imageName.clientWidth;
+
+ this.resizeSong();
+ this.resizeImage();
+ this.core.visualiser.width = this.controls.offsetWidth;
+ this.core.resizeVisualiser();
}
-};
-ModernUI.prototype.beat = function(beats, index) {
- this.currentBeat = beats[0];
- let rest = beats.slice(1);
+ resizeElement(el, parentSize) {
+ let chars = el.textContent.length;
+ if (chars * this.textSize_normal < parentSize) {
+ el.className = "";
+ } else if(chars * this.textSize_small < parentSize) {
+ el.className = "small";
+ } else {
+ el.className = "x-small";
+ }
+ }
- this.beatLeft.textContent = rest;
- this.beatRight.textContent = rest;
+ resizeSong() {
+ this.resizeElement(this.songLink, this.songLink_size);
+ }
+
+ resizeImage() {
+ this.resizeElement(this.imageLink, this.imageLink_size);
+ }
+ newSong(song) {
+ super.newSong(song);
- if (this.currentBeat != ".") {
- while (this.beatCenter.firstElementChild) {
- this.beatCenter.removeChild(this.beatCenter.firstElementChild);
+ if(!song) {
+ return;
}
- let span = this.beatCenter.ownerDocument.createElement("span");
- span.textContent = this.currentBeat;
- this.beatCenter.appendChild(span);
- }
- this.beatCount.textContent = "B=" + this.intToHex(index, 4);
-};
-
-// get the width of a single character in the link box for a given classname
-ModernUI.prototype.textWidth = function(className) {
- // Could be song or image link, don't care
- let el = this.songLink;
- let oldContent = el.innerHTML;
-
- // offsetWidth is rounded, divide by 100
- let text = "";
- for(let i = 0; i < 100; i++) {
- text += " ";
- }
- el.innerHTML = text;
- // We override this just after so don't bother to restore it
- el.className = className;
- let size = el.offsetWidth / 100;
-
- el.innerHTML = oldContent;
-
- return size;
+
+ this.resizeSong();
+ }
+
+ newImage(image) {
+ super.newImage(image);
+
+ if(!image) {
+ return;
+ }
+
+ this.resizeImage();
+ }
}
-ModernUI.prototype.resize = function() {
- this.textSize_normal = this.textWidth("");
- this.textSize_small = this.textWidth("small");
- this.songLink_size = this.songName.clientWidth;
- this.imageLink_size = this.imageName.clientWidth;
+class XmasUI extends ModernUI {
+ constructor(parent, name) {
+ super(parent, name ? name : "XmasUI");
+ this.initSnow();
+
+ // This will cache our inverted lights images
+ this.invert(true);
+
+ this.controls.removeChild(this.leftBox);
+ this.controls.removeChild(this.rightBox);
+ this.controls.removeChild(this.rightInfo);
+ this.controls.removeChild(this.leftInfo);
+
+ this.leftBox = null;
+ this.rightBox = null;
+ this.hueName = null;
+ this.xBlur = null;
+ this.yBlur = null;
+ this.timer = null;
+
+ this.controls.className += " hues-x-controls";
+ this.beatBar.className += " hues-x-beatbar";
+
+ this.lights = [];
+
+ let wires = document.createElement("div");
+ wires.className = "hues-x-wires";
+
+ let left = document.createElement("div");
+ left.className = "hues-x-wiresleft";
+ xleft.forEach(function(l, i, a) {
+ let light = this.newLight(l, left);
+ light.style.transform = "rotate(" + l.angle + "deg)";
+ light.style.left = l.x + "px";
+ light.style.top = l.y + "px";
+ this.lights.push(light);
+ }, this);
+
+ let right = document.createElement("div");
+ right.className = "hues-x-wiresright";
+ xright.forEach(function(l, i, a) {
+ let light = this.newLight(l, right);
+ light.style.transform = "rotate(" + (-l.angle) + "deg)";
+ light.style.right = l.x + "px";
+ light.style.top = l.y + "px";
+ this.lights.push(light);
+ }, this);
- this.resizeSong();
- this.resizeImage();
- this.core.visualiser.width = this.controls.offsetWidth;
- this.core.resizeVisualiser();
-};
+ let bottomHelper = document.createElement("div");
+ bottomHelper.className = "hues-x-wiresbottomhelper";
+ let bottom = document.createElement("div");
+ bottom.className = "hues-x-wiresbottom";
+ xbottom.forEach(function(l, i, a) {
+ let light = this.newLight(l, bottom);
+ light.style.transform = "rotate(" + l.angle + "deg)";
+ light.style.left = l.x + "px";
+ light.style.bottom = l.y + "px";
+ this.lights.push(light);
+ }, this);
-ModernUI.prototype.resizeElement = function(el, parentSize) {
- let chars = el.textContent.length;
- if (chars * this.textSize_normal < parentSize) {
- el.className = "";
- } else if(chars * this.textSize_small < parentSize) {
- el.className = "small";
- } else {
- el.className = "x-small";
+ wires.appendChild(left);
+ wires.appendChild(right);
+ bottomHelper.appendChild(bottom);
+ wires.appendChild(bottomHelper);
+ this.root.appendChild(wires);
+
+ this.visualiserContainer.className = "hues-x-visualisercontainer";
+ this.controls.removeChild(this.visualiserContainer);
+ this.beatBar.appendChild(this.visualiserContainer);
+ }
+
+ invert(invert) {
+ super.invert(invert);
+
+ if(invert) {
+ this.snowContext.fillStyle = "rgba(0, 0, 0, 0.8)";
+ } else {
+ this.snowContext.fillStyle = "rgba(255, 255, 255, 0.8)";
+ }
}
-};
-ModernUI.prototype.resizeSong = function() {
- this.resizeElement(this.songLink, this.songLink_size);
-};
+ connectCore(core) {
+ super.connectCore(core);
+ this.startSnow();
+ }
-ModernUI.prototype.resizeImage = function() {
- this.resizeElement(this.imageLink, this.imageLink_size);
-};
+ disconnect() {
+ this.stopSnow();
+ super.disconnect();
+ }
-ModernUI.prototype.newSong = function(song) {
- HuesUI.prototype.newSong.call(this, song);
+ lightOn(light) {
+ light.on.className = "hues-x-lighton";
+ light.off.className = "hues-x-lightoff";
+ }
- if(!song) {
- return;
+ lightOff(light) {
+ light.on.className = "hues-x-lighton off";
+ light.off.className = "hues-x-lightoff off";
}
- this.resizeSong();
-};
+ lightFadeOut(light) {
+ light.on.className = "hues-x-lighton hues-x-fade off";
+ light.off.className = "hues-x-lightoff hues-x-fade off";
+ }
-ModernUI.prototype.newImage = function(image) {
- HuesUI.prototype.newImage.call(this, image);
+ lightRecolour(light) {
+ let hue = Math.floor(Math.random() * 7) * -56;
+ light.on.style.backgroundPosition = hue + "px 0";
+ light.off.style.backgroundPosition = hue + "px 0";
+ }
- if(!image) {
- return;
+ randomLight(light) {
+ if(Math.random() >= 0.5) {
+ this.lightOn(light);
+ } else {
+ this.lightOff(light);
+ }
}
- this.resizeImage();
-};
+ newLight(l, parent) {
+ let light = document.createElement("div");
+ light.className = "hues-x-light";
+ let bulb = document.createElement("div");
+ let on = document.createElement("div");
+ let off = document.createElement("div");
+ bulb.appendChild(on);
+ bulb.appendChild(off);
+ light.appendChild(bulb);
+ parent.appendChild(light);
+ light.on = on;
+ light.off = off;
+ light.bulb = bulb;
+ this.randomLight(light);
+ this.lightRecolour(light);
+ return light;
+ }
-function XmasUI(parent, name) {
- ModernUI.call(this, parent, name ? name : "XmasUI");
- this.initSnow();
-
- // This will cache our inverted lights images
- this.invert(true);
-
- this.controls.removeChild(this.leftBox);
- this.controls.removeChild(this.rightBox);
- this.controls.removeChild(this.rightInfo);
- this.controls.removeChild(this.leftInfo);
-
- this.leftBox = null;
- this.rightBox = null;
- this.hueName = null;
- this.xBlur = null;
- this.yBlur = null;
- this.timer = null;
-
- this.controls.className += " hues-x-controls";
- this.beatBar.className += " hues-x-beatbar";
-
- this.lights = [];
-
- let wires = document.createElement("div");
- wires.className = "hues-x-wires";
-
- let left = document.createElement("div");
- left.className = "hues-x-wiresleft";
- xleft.forEach(function(l, i, a) {
- let light = this.newLight(l, left);
- light.style.transform = "rotate(" + l.angle + "deg)";
- light.style.left = l.x + "px";
- light.style.top = l.y + "px";
- this.lights.push(light);
- }, this);
-
- let right = document.createElement("div");
- right.className = "hues-x-wiresright";
- xright.forEach(function(l, i, a) {
- let light = this.newLight(l, right);
- light.style.transform = "rotate(" + (-l.angle) + "deg)";
- light.style.right = l.x + "px";
- light.style.top = l.y + "px";
- this.lights.push(light);
- }, this);
-
- let bottomHelper = document.createElement("div");
- bottomHelper.className = "hues-x-wiresbottomhelper";
- let bottom = document.createElement("div");
- bottom.className = "hues-x-wiresbottom";
- xbottom.forEach(function(l, i, a) {
- let light = this.newLight(l, bottom);
- light.style.transform = "rotate(" + l.angle + "deg)";
- light.style.left = l.x + "px";
- light.style.bottom = l.y + "px";
- this.lights.push(light);
- }, this);
-
- wires.appendChild(left);
- wires.appendChild(right);
- bottomHelper.appendChild(bottom);
- wires.appendChild(bottomHelper);
- this.root.appendChild(wires);
-
- this.visualiserContainer.className = "hues-x-visualisercontainer";
- this.controls.removeChild(this.visualiserContainer);
- this.beatBar.appendChild(this.visualiserContainer);
-}
+ beat(beats, index) {
+ super.beat(beats, index);
+ if(this.currentBeat != ".") {
+ this.lights.forEach(function(light, i, a) {
+ switch(this.currentBeat) {
+ case ":":
+ this.lightOn(light);
+ this.lightRecolour(light);
+ break;
+ case "+":
+ this.lightFadeOut(light);
+ break;
+ default:
+ this.randomLight(light);
+ }
+ }, this);
+ }
+ }
-XmasUI.prototype = Object.create(ModernUI.prototype);
-XmasUI.prototype.constructor = XmasUI;
+ initSnow() {
+ 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));
+ }
-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.startSnow();
-};
-
-XmasUI.prototype.disconnect = function() {
- this.stopSnow();
- HuesUI.prototype.disconnect.call(this);
-};
-
-XmasUI.prototype.lightOn = function(light) {
- light.on.className = "hues-x-lighton";
- light.off.className = "hues-x-lightoff";
-};
-
-XmasUI.prototype.lightOff = function(light) {
- light.on.className = "hues-x-lighton off";
- light.off.className = "hues-x-lightoff off";
-};
-
-XmasUI.prototype.lightFadeOut = function(light) {
- light.on.className = "hues-x-lighton hues-x-fade off";
- light.off.className = "hues-x-lightoff hues-x-fade off";
-};
-
-XmasUI.prototype.lightRecolour = function(light) {
- let hue = Math.floor(Math.random() * 7) * -56;
- light.on.style.backgroundPosition = hue + "px 0";
- light.off.style.backgroundPosition = hue + "px 0";
-};
-
-XmasUI.prototype.randomLight = function(light) {
- if(Math.random() >= 0.5) {
- this.lightOn(light);
- } else {
- this.lightOff(light);
- }
-};
-
-XmasUI.prototype.newLight = function(l, parent) {
- let light = document.createElement("div");
- light.className = "hues-x-light";
- let bulb = document.createElement("div");
- let on = document.createElement("div");
- let off = document.createElement("div");
- bulb.appendChild(on);
- bulb.appendChild(off);
- light.appendChild(bulb);
- parent.appendChild(light);
- light.on = on;
- light.off = off;
- light.bulb = bulb;
- this.randomLight(light);
- this.lightRecolour(light);
- return light;
-};
-
-XmasUI.prototype.beat = function(beats, index) {
- ModernUI.prototype.beat.call(this, beats, index);
- if(this.currentBeat != ".") {
- this.lights.forEach(function(light, i, a) {
- switch(this.currentBeat) {
- case ":":
- this.lightOn(light);
- this.lightRecolour(light);
- break;
- case "+":
- this.lightFadeOut(light);
- break;
- default:
- this.randomLight(light);
- }
- }, this);
+ // From http://thecodeplayer.com/walkthrough/html5-canvas-snow-effect
+
+ startSnow() {
+ 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.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);
+ stopSnow() {
+ this.snowing = false;
+ this.snowCanvas.style.display = "none";
+ }
- 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};
+ drawSnow() {
+ 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 {
- //Enter from the right
- this.snowflakes[i] = {x: width+5, y: Math.random() * height, r: flake.r, d: flake.d};
+ //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) {};
-
-function HalloweenUI(parent, name) {
- ModernUI.call(this, parent, name ? name : "HalloweenUI");
- // This will cache our inverted tombstone image
- this.invert(true);
+ resize() {
+ super.resize();
+
+ 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;
+ }
+
+ newColour(colour) {}
+ blurUpdated(x, y) {}
+ updateTime(time) {}
}
-HalloweenUI.prototype = Object.create(ModernUI.prototype);
-HalloweenUI.prototype.constructor = HalloweenUI;
+class HalloweenUI extends ModernUI {
+ constructor(parent, name) {
+ super(parent, name ? name : "HalloweenUI");
+ // This will cache our inverted tombstone image
+ this.invert(true);
+ }
-HalloweenUI.prototype.initUI = function() {
- ModernUI.prototype.initUI.call(this);
-
- this.controls.className += " hues-h-controls";
- this.beatBar.className += " hues-h-beatbar";
- this.leftBox.className += " hues-h-leftbox";
- this.rightBox.className += " hues-h-rightbox";
- this.volBar.className += " hues-h-vol-bar";
-
- this.beatLeft.className += " hues-h-text";
- this.beatRight.className += " hues-h-text";
- this.beatCenter.className += " hues-h-text";
- this.songShuffle.className += " hues-h-text";
- this.songNext.className += " hues-h-text";
- this.songPrev.className += " hues-h-text";
- this.songList.className += " hues-h-text";
- this.songName.className += " hues-h-text";
- this.imageMode.className += " hues-h-text";
- this.imageNext.className += " hues-h-text";
- this.imagePrev.className += " hues-h-text";
- this.imageList.className += " hues-h-text";
- this.imageName.className += " hues-h-text";
- this.hueName.className += " hues-h-text";
-
- this.settingsToggle.className += " hues-h-text";
- this.hideToggle.className += " hues-h-text";
- this.infoToggle.className += " hues-h-text";
- this.volLabel.className += " hues-h-text";
-
- this.timer.className = "hues-h-textfade";
- this.beatCount.className = "hues-h-textfade";
- this.xBlur.className = "hues-h-textfade";
- this.yBlur.className = "hues-h-textfade";
-
- let leftBoxTomb = document.createElement("div");
- leftBoxTomb.className = "hues-h-tombstone";
- this.leftBox.appendChild(leftBoxTomb);
-
- let songTomb = document.createElement("div");
- songTomb.className = "hues-h-tombstone";
- this.songBlock.insertBefore(songTomb,this.songBlock.firstChild);
-
- let imageTomb = document.createElement("div");
- imageTomb.className = "hues-h-tombstone";
- this.imageBlock.insertBefore(imageTomb,this.imageBlock.firstChild);
-
- let topLeft = document.createElement("div");
- topLeft.className = "hues-h-topleft";
- let topRight = document.createElement("div");
- topRight.className = "hues-h-topright";
- let bottomRight = document.createElement("div");
- bottomRight.className = "hues-h-bottomright";
-
- this.root.appendChild(topLeft);
- this.root.appendChild(topRight);
- this.root.appendChild(bottomRight);
-
- let leftHand = document.createElement("div");
- leftHand.className = "hues-h-left-hand";
- this.beatBar.appendChild(leftHand);
- let rightHand = document.createElement("div");
- rightHand.className = "hues-h-right-hand";
- this.beatBar.appendChild(rightHand);
-
- this.vignette = document.createElement("div");
- this.vignette.className = "hues-h-vignette";
- this.root.appendChild(this.vignette);
-};
+ initUI() {
+ super.initUI();
+
+ this.controls.className += " hues-h-controls";
+ this.beatBar.className += " hues-h-beatbar";
+ this.leftBox.className += " hues-h-leftbox";
+ this.rightBox.className += " hues-h-rightbox";
+ this.volBar.className += " hues-h-vol-bar";
+
+ this.beatLeft.className += " hues-h-text";
+ this.beatRight.className += " hues-h-text";
+ this.beatCenter.className += " hues-h-text";
+ this.songShuffle.className += " hues-h-text";
+ this.songNext.className += " hues-h-text";
+ this.songPrev.className += " hues-h-text";
+ this.songList.className += " hues-h-text";
+ this.songName.className += " hues-h-text";
+ this.imageMode.className += " hues-h-text";
+ this.imageNext.className += " hues-h-text";
+ this.imagePrev.className += " hues-h-text";
+ this.imageList.className += " hues-h-text";
+ this.imageName.className += " hues-h-text";
+ this.hueName.className += " hues-h-text";
+
+ this.settingsToggle.className += " hues-h-text";
+ this.hideToggle.className += " hues-h-text";
+ this.infoToggle.className += " hues-h-text";
+ this.volLabel.className += " hues-h-text";
+
+ this.timer.className = "hues-h-textfade";
+ this.beatCount.className = "hues-h-textfade";
+ this.xBlur.className = "hues-h-textfade";
+ this.yBlur.className = "hues-h-textfade";
+
+ let leftBoxTomb = document.createElement("div");
+ leftBoxTomb.className = "hues-h-tombstone";
+ this.leftBox.appendChild(leftBoxTomb);
+
+ let songTomb = document.createElement("div");
+ songTomb.className = "hues-h-tombstone";
+ this.songBlock.insertBefore(songTomb,this.songBlock.firstChild);
+
+ let imageTomb = document.createElement("div");
+ imageTomb.className = "hues-h-tombstone";
+ this.imageBlock.insertBefore(imageTomb,this.imageBlock.firstChild);
+
+ let topLeft = document.createElement("div");
+ topLeft.className = "hues-h-topleft";
+ let topRight = document.createElement("div");
+ topRight.className = "hues-h-topright";
+ let bottomRight = document.createElement("div");
+ bottomRight.className = "hues-h-bottomright";
+
+ this.root.appendChild(topLeft);
+ this.root.appendChild(topRight);
+ this.root.appendChild(bottomRight);
+
+ let leftHand = document.createElement("div");
+ leftHand.className = "hues-h-left-hand";
+ this.beatBar.appendChild(leftHand);
+ let rightHand = document.createElement("div");
+ rightHand.className = "hues-h-right-hand";
+ this.beatBar.appendChild(rightHand);
+
+ this.vignette = document.createElement("div");
+ this.vignette.className = "hues-h-vignette";
+ this.root.appendChild(this.vignette);
+ }
-HalloweenUI.prototype.beat = function(beats, index) {
- ModernUI.prototype.beat.call(this, beats, index);
-
- if (this.currentBeat != ".") {
- let eyes = this.beatCenter.ownerDocument.createElement("div");
- eyes.className = "hues-m-beatcenter hues-h-eyes";
- this.beatCenter.appendChild(eyes);
+ beat(beats, index) {
+ super.beat(beats, index);
+
+ if (this.currentBeat != ".") {
+ let eyes = this.beatCenter.ownerDocument.createElement("div");
+ eyes.className = "hues-m-beatcenter hues-h-eyes";
+ this.beatCenter.appendChild(eyes);
+ }
}
-};
-HalloweenUI.prototype.connectCore = function(core) {
- ModernUI.prototype.connectCore.call(this, core);
-
- this.core.preloader.classList.add("hues-h-text");
-};
+ connectCore(core) {
+ super.connectCore(core);
+
+ this.core.preloader.classList.add("hues-h-text");
+ }
-HalloweenUI.prototype.disconnect = function() {
- this.core.preloader.classList.remove("hues-h-text");
-
- ModernUI.prototype.disconnect.call(this);
-};
+ disconnect() {
+ this.core.preloader.classList.remove("hues-h-text");
+
+ super.disconnect();
+ }
+}
// Positions and angles for the Xmas lights
let xleft = [
diff --git a/src/js/HuesWindow.js b/src/js/HuesWindow.js
index ad035b4..7805db9 100644
--- a/src/js/HuesWindow.js
+++ b/src/js/HuesWindow.js
@@ -22,149 +22,151 @@
(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 = [];
+class HuesWindow {
+ constructor(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();
+
+ 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();
+ addTab(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);
}
- 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");
+
+ selectTab(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);
-};
+ hide() {
+ 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);
-};
+ show() {
+ 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();
+ toggle() {
+ 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);
- });
-};
+ callEventListeners(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);
+ addEventListener(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);
+ removeEventListener(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;
diff --git a/src/js/ResourceManager.js b/src/js/ResourceManager.js
index dbffc7e..ddb7d58 100644
--- a/src/js/ResourceManager.js
+++ b/src/js/ResourceManager.js
@@ -29,845 +29,847 @@ let TAB_IMAGES = 1;
let unique = 0;
let getAndIncrementUnique = function() {
return unique++;
-}
+};
// NOTE: Any packs referenced need CORS enabled or loads fail
let packsURL = "https://cdn.0x40hu.es/getRespacks.php";
-function Resources(core, huesWin) {
- this.core = core;
- this.hasUI = false;
-
- this.resourcePacks = [];
-
- this.allSongs = [];
- this.allImages = [];
- this.enabledSongs = [];
- this.enabledImages = [];
-
- this.progressState = [];
- this.progressCallback = null;
-
- // For songs/images
- this.listView = null;
- this.enabledSongList = null;
- this.enabledImageList = null;
- this.packView = {
- pack: null,
- name: null,
- creator: null,
- size: null,
- desc: null,
- songCount: null,
- imageCount: null,
- songList: null,
- imageList: null,
- packButtons: null,
- totalSongs: null,
- totalImages: null
- };
- this.packsView = {
- respackList: null,
- remoteList: null,
- loadRemote: null,
- progressBar: null,
- progressStatus: null,
- progressCurrent: null,
- progressTop: null,
- progressPercent: null
- };
- this.currentTab = TAB_SONGS;
- this.unique = getAndIncrementUnique();
- this.remotes = null;
- this.fileInput = null;
- this.fileParseQueue = [];
- if(core.settings.defaults.enableWindow) {
- this.initUI();
- huesWin.addTab("RESOURCES", this.root);
+class Resources {
+ constructor(core, huesWin) {
+ this.core = core;
+ this.hasUI = false;
+
+ this.resourcePacks = [];
+
+ this.allSongs = [];
+ this.allImages = [];
+ this.enabledSongs = [];
+ this.enabledImages = [];
+
+ this.progressState = [];
+ this.progressCallback = null;
+
+ // For songs/images
+ this.listView = null;
+ this.enabledSongList = null;
+ this.enabledImageList = null;
+ this.packView = {
+ pack: null,
+ name: null,
+ creator: null,
+ size: null,
+ desc: null,
+ songCount: null,
+ imageCount: null,
+ songList: null,
+ imageList: null,
+ packButtons: null,
+ totalSongs: null,
+ totalImages: null
+ };
+ this.packsView = {
+ respackList: null,
+ remoteList: null,
+ loadRemote: null,
+ progressBar: null,
+ progressStatus: null,
+ progressCurrent: null,
+ progressTop: null,
+ progressPercent: null
+ };
+ this.currentTab = TAB_SONGS;
+ this.unique = getAndIncrementUnique();
+ this.remotes = null;
+ this.fileInput = null;
+ this.fileParseQueue = [];
+ if(core.settings.defaults.enableWindow) {
+ this.initUI();
+ huesWin.addTab("RESOURCES", this.root);
+ }
}
-}
-/* Uses HTTP HEAD requests to get the size of all the linked URLs
- Returns an Promise.all which will resolve to an array of sizes */
-Resources.prototype.getSizes = function(urls) {
- let promises = [];
-
- urls.forEach(url => {
- let p = new Promise((resolve, reject) => {
- let xhr = new XMLHttpRequest();
- xhr.open("HEAD", url, true);
- xhr.onreadystatechange = function() {
- if (this.readyState == this.DONE) {
- let bytes = parseInt(xhr.getResponseHeader("Content-Length"));
- resolve(bytes / 1024 / 1024);
+ /* Uses HTTP HEAD requests to get the size of all the linked URLs
+ Returns an Promise.all which will resolve to an array of sizes */
+ getSizes(urls) {
+ let promises = [];
+
+ urls.forEach(url => {
+ let p = new Promise((resolve, reject) => {
+ let xhr = new XMLHttpRequest();
+ xhr.open("HEAD", url, true);
+ xhr.onreadystatechange = function() {
+ if (this.readyState == this.DONE) {
+ let bytes = parseInt(xhr.getResponseHeader("Content-Length"));
+ resolve(bytes / 1024 / 1024);
+ }
+ };
+ xhr.onerror = function() {
+ reject(Error(req.status + ": Could not fetch respack at " + url));
+ };
+ xhr.send();
+ }).catch(error => {
+ // Infinitely more user friendly than the error Same Origin gives
+ if(error.code == 1012) {
+ throw Error("Respack at URL " + url + " is restricted. Check CORS.");
+ } else {
+ throw error;
}
- };
- xhr.onerror = function() {
- reject(Error(req.status + ": Could not fetch respack at " + url));
- };
- xhr.send();
- }).catch(error => {
- // Infinitely more user friendly than the error Same Origin gives
- if(error.code == 1012) {
- throw Error("Respack at URL " + url + " is restricted. Check CORS.");
- } else {
- throw error;
- }
- });
- promises.push(p);
- });
-
- return Promise.all(promises);
-};
-
-// Array of URLs to load, and a callback for when we're done
-// Preserves order of URLs being loaded
-Resources.prototype.addAll = function(urls, progressCallback) {
- if(progressCallback) {
- this.progressCallback = progressCallback;
- this.progressState = Array.apply(null, Array(urls.length)).map(Number.prototype.valueOf,0);
- }
-
- let respackPromises = [];
-
- let progressFunc = function(index, progress, pack) {
- this.progressState[index] = progress;
- this.updateProgress(pack);
- };
-
- for(let i = 0; i < urls.length; i++) {
- let r = new Respack();
- respackPromises.push(r.loadFromURL(urls[i], progressFunc.bind(this, i)));
- }
- // Start all the promises at once, but add in sequence
- return respackPromises.reduce((sequence, packPromise) => {
- return sequence.then(() => {
- return packPromise;
- }).then(pack => {
- this.addPack(pack);
+ });
+ promises.push(p);
});
- }, Promise.resolve());
-};
+
+ return Promise.all(promises);
+ }
-Resources.prototype.updateProgress = function(pack) {
- let total = 0;
- for(let i = 0; i < this.progressState.length; i++) {
- total += this.progressState[i];
+ // Array of URLs to load, and a callback for when we're done
+ // Preserves order of URLs being loaded
+ addAll(urls, progressCallback) {
+ if(progressCallback) {
+ this.progressCallback = progressCallback;
+ this.progressState = Array.apply(null, Array(urls.length)).map(Number.prototype.valueOf,0);
+ }
+
+ let respackPromises = [];
+
+ let progressFunc = function(index, progress, pack) {
+ this.progressState[index] = progress;
+ this.updateProgress(pack);
+ };
+
+ for(let i = 0; i < urls.length; i++) {
+ let r = new Respack();
+ respackPromises.push(r.loadFromURL(urls[i], progressFunc.bind(this, i)));
+ }
+ // Start all the promises at once, but add in sequence
+ return respackPromises.reduce((sequence, packPromise) => {
+ return sequence.then(() => {
+ return packPromise;
+ }).then(pack => {
+ this.addPack(pack);
+ });
+ }, Promise.resolve());
}
- total /= this.progressState.length;
- this.progressCallback(total, pack);
-};
-Resources.prototype.addPack = function(pack) {
- console.log("Added", pack.name, "to respacks");
- let id = this.resourcePacks.length;
- this.resourcePacks.push(pack);
- this.addResourcesToArrays(pack);
- this.rebuildEnabled();
- this.updateTotals();
-
- let self = this;
- this.appendListItem("respacks", pack.name, "res" + id, this.packsView.respackList,
- function() {
- pack.enabled = this.checked;
- self.rebuildEnabled();
- }, function(id) {
- this.selectPack(id);
- }.bind(this, id)
- );
-};
+ updateProgress(pack) {
+ let total = 0;
+ for(let i = 0; i < this.progressState.length; i++) {
+ total += this.progressState[i];
+ }
+ total /= this.progressState.length;
+ this.progressCallback(total, pack);
+ }
-Resources.prototype.addResourcesToArrays = function(pack) {
- this.allImages = this.allImages.concat(pack.images);
- this.allSongs = this.allSongs.concat(pack.songs);
-};
+ addPack(pack) {
+ console.log("Added", pack.name, "to respacks");
+ let id = this.resourcePacks.length;
+ this.resourcePacks.push(pack);
+ this.addResourcesToArrays(pack);
+ this.rebuildEnabled();
+ this.updateTotals();
-Resources.prototype.rebuildArrays = function() {
- this.allSongs = [];
- this.allImages = [];
- this.allAnimations = [];
+ let self = this;
+ this.appendListItem("respacks", pack.name, "res" + id, this.packsView.respackList,
+ function() {
+ pack.enabled = this.checked;
+ self.rebuildEnabled();
+ }, function(id) {
+ this.selectPack(id);
+ }.bind(this, id)
+ );
+ }
- for(let i = 0; i < this.resourcePacks.length; i++) {
- this.addResourcesToArrays(this.resourcePacks[i]);
+ addResourcesToArrays(pack) {
+ this.allImages = this.allImages.concat(pack.images);
+ this.allSongs = this.allSongs.concat(pack.songs);
}
-};
-Resources.prototype.rebuildEnabled = function() {
- this.enabledSongs = [];
- this.enabledImages = [];
+ rebuildArrays() {
+ this.allSongs = [];
+ this.allImages = [];
+ this.allAnimations = [];
- for(let i = 0; i < this.resourcePacks.length; i++) {
- let pack = this.resourcePacks[i];
- if (pack.enabled !== true) {
- continue;
+ for(let i = 0; i < this.resourcePacks.length; i++) {
+ this.addResourcesToArrays(this.resourcePacks[i]);
}
- for(let j = 0; j < pack.songs.length; j++) {
- let song = pack.songs[j];
- if (song.enabled && this.enabledSongs.indexOf(song) == -1) {
- this.enabledSongs.push(song);
+ }
+
+ rebuildEnabled() {
+ this.enabledSongs = [];
+ this.enabledImages = [];
+
+ for(let i = 0; i < this.resourcePacks.length; i++) {
+ let pack = this.resourcePacks[i];
+ if (pack.enabled !== true) {
+ continue;
+ }
+ for(let j = 0; j < pack.songs.length; j++) {
+ let song = pack.songs[j];
+ if (song.enabled && this.enabledSongs.indexOf(song) == -1) {
+ this.enabledSongs.push(song);
+ }
+ }
+ for(let j = 0; j < pack.images.length; j++) {
+ let image = pack.images[j];
+ if (image.enabled && this.enabledImages.indexOf(image) == -1) {
+ this.enabledImages.push(image);
+ }
}
}
- for(let j = 0; j < pack.images.length; j++) {
- let image = pack.images[j];
- if (image.enabled && this.enabledImages.indexOf(image) == -1) {
- this.enabledImages.push(image);
+ if(this.hasUI) {
+ let songList = this.enabledSongList;
+ while(songList.firstElementChild) {
+ songList.removeChild(songList.firstElementChild);
+ }
+ let imageList = this.enabledImageList;
+ while(imageList.firstElementChild) {
+ imageList.removeChild(imageList.firstElementChild);
+ }
+ for(let i = 0; i < this.enabledSongs.length; i++) {
+ let song = this.enabledSongs[i];
+ this.appendSimpleListItem(song.title, songList, function(index) {
+ this.core.setSong(index);
+ }.bind(this, i));
+ }
+ for(let i = 0; i < this.enabledImages.length; i++) {
+ let image = this.enabledImages[i];
+ this.appendSimpleListItem(image.name, imageList, function(index) {
+ this.core.setImage(index);
+ this.core.setIsFullAuto(false);
+ }.bind(this, i));
}
}
+ this.updateTotals();
}
- if(this.hasUI) {
- let songList = this.enabledSongList;
- while(songList.firstElementChild) {
- songList.removeChild(songList.firstElementChild);
- }
- let imageList = this.enabledImageList;
- while(imageList.firstElementChild) {
- imageList.removeChild(imageList.firstElementChild);
- }
- for(let i = 0; i < this.enabledSongs.length; i++) {
- let song = this.enabledSongs[i];
- this.appendSimpleListItem(song.title, songList, function(index) {
- this.core.setSong(index);
- }.bind(this, i));
- }
- for(let i = 0; i < this.enabledImages.length; i++) {
- let image = this.enabledImages[i];
- this.appendSimpleListItem(image.name, imageList, function(index) {
- this.core.setImage(index);
- this.core.setIsFullAuto(false);
- }.bind(this, i));
+
+ removePack(pack) {
+ let index = this.resourcePacks.indexOf(pack);
+ if (index != -1) {
+ this.resourcePacks.splice(index, 1);
+ this.rebuildArrays();
}
}
- this.updateTotals();
-};
-Resources.prototype.removePack = function(pack) {
- let index = this.resourcePacks.indexOf(pack);
- if (index != -1) {
- this.resourcePacks.splice(index, 1);
+ removeAllPacks() {
+ this.resourcePacks = [];
this.rebuildArrays();
}
-};
-Resources.prototype.removeAllPacks = function() {
- this.resourcePacks = [];
- this.rebuildArrays();
-};
-
-Resources.prototype.getSongNames = function() {
- let names = [];
- for(let i = 0; i < this.allSongs.length; i++) {
- names.push(this.allSongs[i]);
+ getSongNames() {
+ let names = [];
+ for(let i = 0; i < this.allSongs.length; i++) {
+ names.push(this.allSongs[i]);
+ }
+ return names;
}
- return names;
-};
-Resources.prototype.loadLocal = function() {
- console.log("Loading local zip(s)");
-
- let files = this.fileInput.files;
- let p = Promise.resolve();
- for(let i = 0; i < files.length; i++) {
- let r = new Respack();
- /*jshint -W083 */
- p = p.then(() => {
- return r.loadFromBlob(files[i], (progress, respack) => {
- this.localProgress(progress, respack);
+ loadLocal() {
+ console.log("Loading local zip(s)");
+
+ let files = this.fileInput.files;
+ let p = Promise.resolve();
+ for(let i = 0; i < files.length; i++) {
+ let r = new Respack();
+ /*jshint -W083 */
+ p = p.then(() => {
+ return r.loadFromBlob(files[i], (progress, respack) => {
+ this.localProgress(progress, respack);
+ });
+ }).then(pack => {
+ this.addPack(pack);
+ this.localComplete();
});
- }).then(pack => {
- this.addPack(pack);
- this.localComplete();
+ }
+ return p.then(() => {
+ console.log("Local respack parsing complete");
});
}
- return p.then(() => {
- console.log("Local respack parsing complete");
- });
-};
-Resources.prototype.localProgress = function(progress, respack) {
- if(!this.hasUI) {return;}
- this.packsView.progressStatus.textContent = "Processing...";
+ localProgress(progress, respack) {
+ if(!this.hasUI) {return;}
+ this.packsView.progressStatus.textContent = "Processing...";
- this.packsView.progressBar.style.width = (progress * 100) + "%";
- this.packsView.progressCurrent.textContent = respack.filesLoaded;
- this.packsView.progressTop.textContent = respack.filesToLoad;
- this.packsView.progressPercent.textContent = Math.round(progress * 100) + "%";
-};
+ this.packsView.progressBar.style.width = (progress * 100) + "%";
+ this.packsView.progressCurrent.textContent = respack.filesLoaded;
+ this.packsView.progressTop.textContent = respack.filesToLoad;
+ this.packsView.progressPercent.textContent = Math.round(progress * 100) + "%";
+ }
-Resources.prototype.localComplete = function(progress) {
- let progStat = this.packsView.progressStatus;
- progStat.textContent = "Complete";
- window.setTimeout(function() {progStat.textContent = "Idle";}, 2000);
+ localComplete(progress) {
+ let progStat = this.packsView.progressStatus;
+ progStat.textContent = "Complete";
+ window.setTimeout(function() {progStat.textContent = "Idle";}, 2000);
- this.packsView.progressBar.style.width = "100%";
- this.packsView.progressCurrent.textContent = "0b";
- this.packsView.progressTop.textContent = "0b";
- this.packsView.progressPercent.textContent = "0%";
-};
+ this.packsView.progressBar.style.width = "100%";
+ this.packsView.progressCurrent.textContent = "0b";
+ this.packsView.progressTop.textContent = "0b";
+ this.packsView.progressPercent.textContent = "0%";
+ }
-Resources.prototype.initUI = function() {
- this.root = document.createElement("div");
- this.root.className = "respacks";
-
- let packsContainer = document.createElement("div");
- packsContainer.className = "respacks__manager";
-
- let packHeader = document.createElement("div");
- packHeader.textContent = "Current respacks";
- packHeader.className = "respacks__header";
- let packList = document.createElement("div");
- packList.className = "resource-list";
- this.packsView.respackList = packList;
- // so we don't use it out of scope in the next if
- let remoteHeader = null;
- let remoteList = null;
- if(!this.core.settings.defaults.disableRemoteResources) {
- remoteHeader = document.createElement("div");
- remoteHeader.textContent = "Remote respacks";
- remoteHeader.className = "respacks__header";
- remoteList = document.createElement("div");
- 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;
- }
-
- let buttons = document.createElement("div");
- buttons.className = "respacks-buttons";
- let loadRemote = document.createElement("div");
- loadRemote.className = "hues-button hidden";
- loadRemote.textContent = "LOAD REMOTE";
- loadRemote.onclick = this.loadCurrentRemote.bind(this);
- let loadLocal = document.createElement("div");
- loadLocal.className = "hues-button";
- loadLocal.textContent = "LOAD ZIPS";
- loadLocal.onclick = () => {this.fileInput.click();};
- buttons.appendChild(loadLocal);
- buttons.appendChild(loadRemote);
- this.packsView.loadRemote = loadRemote;
-
- this.fileInput = document.createElement("input");
- this.fileInput.type ="file";
- this.fileInput.accept="application/zip";
- this.fileInput.multiple = true;
- this.fileInput.onchange = this.loadLocal.bind(this);
-
- let progressContainer = document.createElement("div");
- progressContainer.className = "progress-container respacks-bottom-container";
- let progressBar = document.createElement("div");
- progressBar.className = "progress-bar";
- let progressFilled = document.createElement("span");
- progressFilled.className = "progress-bar--filled";
- progressBar.appendChild(progressFilled);
- let progressStatus = document.createElement("div");
- progressStatus.textContent = "Idle";
-
- let progressTexts = document.createElement("div");
- progressTexts.className = "stat-text";
- let progressCurrent = document.createElement("div");
- progressCurrent.textContent = "0b";
- let progressTop = document.createElement("div");
- progressTop.textContent = "0b";
- let progressPercent = document.createElement("div");
- progressPercent.textContent = "0%";
- progressTexts.appendChild(progressCurrent);
- progressTexts.appendChild(progressTop);
- progressTexts.appendChild(progressPercent);
-
- this.packsView.progressBar = progressFilled;
- this.packsView.progressStatus = progressStatus;
- this.packsView.progressCurrent = progressCurrent;
- this.packsView.progressTop = progressTop;
- this.packsView.progressPercent = progressPercent;
- progressContainer.appendChild(progressStatus);
- progressContainer.appendChild(progressBar);
- progressContainer.appendChild(progressTexts);
-
- packsContainer.appendChild(packHeader);
- packsContainer.appendChild(packList);
- if(!this.core.settings.defaults.disableRemoteResources) {
- packsContainer.appendChild(remoteHeader);
- packsContainer.appendChild(remoteList);
- }
- packsContainer.appendChild(buttons);
- packsContainer.appendChild(progressContainer);
-
- let indivView = document.createElement("div");
- indivView.className = "respacks__display";
-
- let packName = document.createElement("div");
- packName.textContent = "