Fix HuesSettings persistance issues

No one references localStorage any more, as it's annoying to be consistent when dealing with query vars and temporary overrides
Ephemeral settings + query vars are properly ephemeral
fullAuto can be toggled via settings
master
Will Toohey 8 years ago
parent 77cf50e693
commit 29eb38dafc
  1. 14
      src/js/HuesCanvas.js
  2. 116
      src/js/HuesCore.js
  3. 2
      src/js/HuesEditor.js
  4. 6
      src/js/HuesInfo.js
  5. 145
      src/js/HuesSettings.js
  6. 6
      src/js/HuesWindow.js
  7. 6
      src/js/ResourceManager.js
  8. 2
      src/js/SoundManager.js

@ -120,11 +120,11 @@ class HuesCanvas {
}
settingsUpdated() {
this.setSmartAlign(localStorage["smartAlign"]);
this.setBlurAmount(localStorage["blurAmount"]);
this.setBlurDecay(localStorage["blurDecay"]);
this.setBlurQuality(localStorage["blurQuality"]);
this.trippyOn = localStorage["trippyMode"] == "on";
this.setSmartAlign(this.core.settings.smartAlign);
this.setBlurAmount(this.core.settings.blurAmount);
this.setBlurDecay(this.core.settings.blurDecay);
this.setBlurQuality(this.core.settings.blurQuality);
this.trippyOn = this.core.settings.trippyMode == "on";
}
resetEffects() {
@ -508,7 +508,7 @@ class HuesCanvas {
}
this.blackout = true;
this.needsRedraw = true;
if(localStorage["blackoutUI"] == "on") {
if(this.core.settings.blackoutUI == "on") {
this.core.userInterface.hide();
}
}
@ -517,7 +517,7 @@ class HuesCanvas {
this.blackout = false;
this.blackoutTimeout = 0;
this.needsRedraw = true;
if(localStorage["blackoutUI"] == "on") {
if(this.core.settings.blackoutUI == "on") {
this.core.userInterface.show();
}
}

@ -19,9 +19,6 @@
* THE SOFTWARE.
*/
/* We don't want localstorage variables 'optimised' to different identifiers*/
/*jshint -W069 */
(function(window, document) {
"use strict";
@ -106,7 +103,7 @@ class HuesCore {
/* callback settingsupdated()
*
* Called when settings are updated and should be re-read from localStorage
* Called when settings are updated and should be re-read from the settings object
*/
settingsupdated : []
};
@ -131,25 +128,27 @@ class HuesCore {
this.colourIndex = 0x3f;
this.colours = HuesCore.oldColours;
this.isFullAuto = true;
this.invert = false;
this.loopCount = 0;
this.doBuildup = true;
this.userInterface = null;
this.uiArray = [];
this.settings = new HuesSettings(defaults);
zip.workerScriptsPath = this.settings.workersPath;
// What's our root element?
this.root = null;
if(!defaults.root) {
if(!this.settings.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 if(typeof this.settings.root === "string") {
if(this.settings.root && document.getElementById(this.settings.root)) {
this.root = document.getElementById(this.settings.root);
} else {
this.root = document.body;
}
} else { // been given an element
this.root = defaults.root;
this.root = this.settings.root;
}
this.root.classList.add("hues-root");
// Special case for full page Hues
@ -159,7 +158,7 @@ class HuesCore {
// Yes, we do indeed have Javascript
this.root.innerHTML = "";
this.makePreloader(this.root, defaults);
this.makePreloader(this.root);
window.onerror = (msg, url, line, col, error) => {
this.error(msg);
@ -167,21 +166,16 @@ class HuesCore {
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);
this.window = new HuesWindow(this.root, this.settings);
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);
populateHuesInfo(this.versionStr, this.window, this.settings);
this.window.selectTab(defaults.firstWindow, true);
this.window.selectTab(this.settings.firstWindow, true);
let ui = document.createElement("div");
ui.className = "hues-ui";
@ -189,7 +183,7 @@ class HuesCore {
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.autoSong = this.settings.autoSong;
this.visualiser = document.createElement("canvas");
this.visualiser.className = "hues-visualiser";
@ -199,10 +193,10 @@ class HuesCore {
this.soundManager = new SoundManager(this);
this.soundManager.init().then(() => {
if(!this.soundManager.locked && localStorage["skipPreloader"] == "on") {
if(!this.soundManager.locked && this.settings.skipPreloader == "on") {
return null;
} else {
return this.resourceManager.getSizes(defaults.respacks);
return this.resourceManager.getSizes(this.settings.respacks);
}
}).then( sizes => {
if(sizes === null) {
@ -238,12 +232,14 @@ class HuesCore {
this.setColour(this.colourIndex);
this.animationLoop();
if(defaults.load) {
return this.resourceManager.addAll(defaults.respacks, progress => {
if(this.settings.load) {
return this.resourceManager.addAll(this.settings.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);
let scale = Math.floor(progress * this.settings.preloadMax);
let padding = this.settings.preloadMax.toString(this.settings.preloadBase).length;
this.preloadMsg.textContent = this.settings.preloadPrefix +
(Array(padding).join("0") +
scale.toString(this.settings.preloadBase)).slice(-padding);
});
} else {
this.preloader.style.display = "none";
@ -252,14 +248,14 @@ class HuesCore {
}).then(() => {
this.preloader.classList.add("hues-preloader--loaded");
this.callEventListeners("loaded");
if(defaults.firstImage) {
this.setImageByName(defaults.firstImage);
if(this.settings.firstImage) {
this.setImageByName(this.settings.firstImage);
} else {
this.setImage(0);
}
if(defaults.autoplay) {
if(defaults.firstSong) {
this.setSongByName(defaults.firstSong);
if(this.settings.autoplay) {
if(this.settings.firstSong) {
this.setSongByName(this.settings.firstSong);
} else {
this.setSong(0);
}
@ -268,7 +264,7 @@ class HuesCore {
this.error(error);
});
if(!defaults.disableKeyboard) {
if(!this.settings.disableKeyboard) {
document.addEventListener("keydown", e => {
e = e || window.event;
if(e.defaultPrevented) {
@ -316,15 +312,15 @@ class HuesCore {
}
}
makePreloader(root, defaults) {
makePreloader(root) {
this.preloader = document.createElement("div");
this.preloader.className = "hues-preloader";
root.appendChild(this.preloader);
if(defaults.preloadTitle) {
if(this.settings.preloadTitle) {
this.preloadTitle = document.createElement("div");
this.preloadTitle.className = "hues-preloader__title";
this.preloadTitle.textContent = defaults.preloadTitle;
this.preloadTitle.textContent = this.settings.preloadTitle;
this.preloader.appendChild(this.preloadTitle);
}
@ -343,7 +339,7 @@ class HuesCore {
}
updateVisualiser() {
if(localStorage["visualiser"] != "on") {
if(this.settings.visualiser != "on") {
return;
}
@ -509,7 +505,7 @@ class HuesCore {
this.callEventListeners("newsong", this.currentSong);
this.loopCount = 0;
if (this.currentSong.buildup) {
switch (localStorage["playBuildups"]) {
switch (this.settings.playBuildups) {
case "off":
this.currentSong.buildupPlayed = true;
this.doBuildup = false;
@ -606,16 +602,16 @@ class HuesCore {
onLoop() {
this.loopCount++;
switch (localStorage["autoSong"]) {
switch (this.settings.autoSong) {
case "loop":
console.log("Checking loops");
if (this.loopCount >= localStorage["autoSongDelay"]) {
if (this.loopCount >= this.settings.autoSongDelay) {
this.doAutoSong();
}
break;
case "time":
console.log("Checking times");
if (this.soundManager.loopLength * this.loopCount >= localStorage["autoSongDelay"] * 60) {
if (this.soundManager.loopLength * this.loopCount >= this.settings.autoSongDelay * 60) {
this.doAutoSong();
}
break;
@ -627,12 +623,12 @@ class HuesCore {
if(this.resourceManager.enabledSongs.length < 2) {
return; // don't move if there's nothing to move to
}
if(localStorage["autoSongShuffle"] == "on") {
if(this.settings.autoSongShuffle == "on") {
func = this.randomSong;
} else {
func = this.nextSong;
}
if(localStorage["autoSongFadeout"] == "on") {
if(this.settings.autoSongFadeout == "on") {
this.soundManager.fadeOut(() => {
func.call(this);
});
@ -651,13 +647,13 @@ class HuesCore {
resetAudio() {
this.beatIndex = 0;
this.songDataUpdated();
if(localStorage["visualiser"] == "on") {
if(this.settings.visualiser == "on") {
this.soundManager.initVisualiser(this.visualiser.width/2);
}
}
randomImage() {
if(localStorage["shuffleImages"] == "on") {
if(this.settings.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) {
@ -780,12 +776,12 @@ class HuesCore {
this.randomColour();
break;
case '*':
if(this.isFullAuto) {
if(this.settings.fullAuto) {
this.randomImage();
}
break;
case '=':
if(this.isFullAuto) {
if(this.settings.fullAuto) {
this.randomImage();
}
/* falls through */
@ -806,7 +802,7 @@ class HuesCore {
this.renderer.doSlice(this.getBeatLength(), this.charsToNextBeat(), true, true);
break;
case 'I':
if (this.isFullAuto) {
if (this.settings.fullAuto) {
this.randomImage();
}
/* falls through */
@ -819,7 +815,7 @@ class HuesCore {
}
if([".", "+", "¤", ":", "*", "X", "O", "~", "=", "i", "I", "s", "v", "#"].indexOf(beat) == -1) {
this.randomColour();
if (this.isFullAuto) {
if (this.settings.fullAuto) {
this.randomImage();
}
}
@ -866,14 +862,14 @@ class HuesCore {
}
setIsFullAuto(auto) {
this.isFullAuto = auto;
this.settings.fullAuto = auto;
if (this.userInterface) {
this.callEventListeners("newmode", this.isFullAuto);
this.callEventListeners("newmode", this.settings.fullAuto);
}
}
toggleFullAuto() {
this.setIsFullAuto(!this.isFullAuto);
this.setIsFullAuto(!this.settings.fullAuto);
}
setInvert(invert) {
@ -897,7 +893,7 @@ class HuesCore {
}
this.userInterface = this.uiArray[index];
this.userInterface.connectCore(this);
this.callEventListeners("newmode", this.isFullAuto);
this.callEventListeners("newmode", this.settings.fullAuto);
this.callEventListeners("newsong", this.currentSong);
this.callEventListeners("newimage", this.currentImage);
this.callEventListeners("newcolour", this.colours[this.colourIndex], false);
@ -908,7 +904,7 @@ class HuesCore {
settingsUpdated() {
this.callEventListeners("settingsupdated");
switch (localStorage["currentUI"]) {
switch (this.settings.currentUI) {
case "retro":
this.changeUI(0);
break;
@ -928,7 +924,7 @@ class HuesCore {
this.changeUI(5);
break;
}
switch (localStorage["colourSet"]) {
switch (this.settings.colourSet) {
case "normal":
this.colours = HuesCore.oldColours;
break;
@ -939,7 +935,7 @@ class HuesCore {
this.colours = HuesCore.weedColours;
break;
}
switch (localStorage["blackoutUI"]) {
switch (this.settings.blackoutUI) {
case "off":
this.userInterface.show();
break;
@ -949,7 +945,7 @@ class HuesCore {
}
break;
}
switch (localStorage["visualiser"]) {
switch (this.settings.visualiser) {
case "off":
this.visualiser.classList.add("hidden");
break;
@ -960,11 +956,11 @@ class HuesCore {
}
break;
}
if (this.autoSong == "off" && localStorage["autoSong"] != "off") {
if (this.autoSong == "off" && this.settings.autoSong != "off") {
console.log("Resetting loopCount since AutoSong was enabled");
this.loopCount = 0;
}
this.autoSong = localStorage["autoSong"];
this.autoSong = this.settings.autoSong;
}
enabledChanged() {
@ -1012,7 +1008,7 @@ class HuesCore {
this.previousSong();
break;
case 70: // F
this.setIsFullAuto(!this.isFullAuto);
this.toggleFullAuto();
break;
case 109: // NUMPAD_SUBTRACT
case 189: // MINUS

@ -55,7 +55,7 @@ class HuesEditor {
this.linked = false;
this.core = core;
if(core.settings.defaults.enableWindow) {
if(core.settings.enableWindow) {
this.initUI();
core.addEventListener("beat", this.onBeat.bind(this));
core.addEventListener("newsong", this.onNewSong.bind(this));

@ -69,8 +69,8 @@ const shortcuts = [
"[1-5] Change UI"
];
function populateHuesInfo(version, huesWin, defaults) {
if(!defaults.enableWindow) {
function populateHuesInfo(version, huesWin, settings) {
if(!settings.enableWindow) {
return;
}
let verString = (parseInt(version)/10).toFixed(1);
@ -78,7 +78,7 @@ function populateHuesInfo(version, huesWin, defaults) {
let info = document.createElement("div");
info.className = "hues-ref";
let huesName = defaults.huesName.replace("%VERSION%", version);
let huesName = settings.huesName.replace("%VERSION%", version);
let about = document.createElement("div");
about.className = "hues-about";
about.innerHTML = "<h1>" + huesName + "</h1>" +

@ -30,6 +30,8 @@ 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/",
// List of respacks to load
respacks : [],
// ONLY USED FOR QUERY STRINGS this will be prepended to any respacks
// passed in as a ?packs=query
respackPath : "respacks/",
@ -43,6 +45,8 @@ const defaultSettings = {
firstSong: null,
// If set, will attempt to set the named image first
firstImage: null,
// set to false to never change images
fullAuto: true,
// If set, will disable the remote resources menu. For custom pages.
disableRemoteResources: false,
// You will rarely want to change this. Enables/disables the Hues Window.
@ -83,27 +87,6 @@ const defaultSettings = {
skipPreloader: "off"
};
// Don't get saved to localStorage
const ephemeralSettings = [
"load",
"autoplay",
"overwriteLocal",
"respacks",
"respackPath",
"firstSong",
"firstImage",
"disableRemoteResources",
"preloadPrefix",
"preloadBase",
"preloadMax",
"enableWindow",
"firstWindow",
"workersPath",
"huesName",
"root",
"disableKeyboard"
];
// To dynamically build the UI like the cool guy I am
const settingsCategories = {
"Functionality" : [
@ -173,16 +156,16 @@ const settingsOptions = {
options : ["off", "loop", "time",
{type:"varText", text:function() {
// only display if autosong is on
return localStorage["autoSong"] == "off" ? "" : "after";
return this.autoSong == "off" ? "" : "after";
}},
{type:"input", variable:"autoSongDelay", inputType:"int",
visiblity:function() {
return localStorage["autoSong"] != "off";
return this.autoSong != "off";
}
},
{type:"varText", text:function() {
let ret = "";
switch(localStorage["autoSong"]) {
switch(this.autoSong) {
case "loop":
ret = "loop";
break;
@ -194,7 +177,7 @@ const settingsOptions = {
default:
return "";
}
if(localStorage["autoSongDelay"] > 1) {
if(this.autoSongDelay > 1) {
ret += "s";
}
return ret;
@ -233,6 +216,12 @@ class HuesSettings {
updated : []
};
let settingsVersion = "1";
if(localStorage.settingsVersion != settingsVersion) {
localStorage.clear();
localStorage.settingsVersion = settingsVersion;
}
this.hasUI = false;
this.settingCheckboxes = {};
@ -240,41 +229,35 @@ class HuesSettings {
this.textCallbacks = [];
this.visCallbacks = [];
this.ephemerals = {};
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) {
if(!defaultSettings.hasOwnProperty(attr)) {
continue;
}
Object.defineProperty(this, attr, {
set: this.makeSetter(attr), get: this.makeGetter(attr)
});
if(defaults[attr] !== undefined) {
if(defaults.overwriteLocal) {
localStorage[attr] = defaults[attr];
}
// populate defaults, ignoring current
if(localStorage[attr] === undefined) {
localStorage[attr] = defaults[attr];
this[attr] = defaults[attr];
} else {
this.ephemerals[attr] = defaults[attr];
}
}
}
this.defaults = defaults;
// Override with our query string
let querySettings = this.getQuerySettings();
this.defaults.respacks = this.defaults.respacks.concat(querySettings.respacks);
for(let attr in querySettings) {
if(querySettings.hasOwnProperty(attr) && attr != "respacks") {
this.defaults[attr] = querySettings[attr];
if(ephemeralSettings.indexOf(attr) == -1) {
// TODO: Everything that checks localStorage for settings should
// change to get() to preserve local changes
localStorage[attr] = querySettings[attr];
}
for(let attr in defaultSettings) {
// query string overrides, finally
if(querySettings[attr] !== undefined && attr != 'respacks') {
this.ephemerals[attr] = querySettings[attr];
}
}
this.respacks = this.respacks.concat(querySettings.respacks);
}
getQuerySettings() {
@ -287,12 +270,16 @@ class HuesSettings {
if(pair[0] == "packs" || pair[0] == "respacks"){
let packs = pair[1].split(",");
for(let j = 0; j < packs.length; j++) {
results.respacks.push(this.defaults.respackPath + packs[j]);
results.respacks.push(this.respackPath + packs[j]);
}
} else if(pair[0] == "song") { // alias for firstSong
results.firstSong = pair[1];
} else {
results[pair[0]] = pair[1];
let val = pair[1];
// since we can set ephemeral variables this way
if(val === "true" || val === "false")
val = val == "true";
results[pair[0]] = val;
}
}
return results;
@ -309,7 +296,7 @@ class HuesSettings {
this.value = "";
return;
}
localStorage[variable] = this.value;
self[variable] = this.value;
self.updateConditionals();
self.callEventListeners("updated");
};
@ -347,11 +334,11 @@ class HuesSettings {
}
checkbox.name = setName + "-" + unique;
checkbox.id = id + unique;
if(localStorage[setName] == option) {
if(this[setName] == option) {
checkbox.checked = true;
}
checkbox.onclick = function(self) {
self.set(setName, this.value);
self[setName] = this.value;
}.bind(checkbox, this);
buttonContainer.appendChild(checkbox);
// So we can style this nicely
@ -363,14 +350,14 @@ class HuesSettings {
} else { // special option
if(option.type == "varText") {
let text = document.createElement("span");
text.textContent = option.text();
text.textContent = option.text.bind(this)();
buttonContainer.appendChild(text);
this.textCallbacks.push({func:option.text, element:text});
this.textCallbacks.push({func:option.text.bind(this), 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];
input.value = this[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);
@ -378,8 +365,8 @@ class HuesSettings {
input.autofocus = false;
buttonContainer.appendChild(input);
if(option.visiblity) {
this.visCallbacks.push({func:option.visiblity, element:input});
input.style.visibility = option.visiblity() ? "visible" : "hidden";
this.visCallbacks.push({func:option.visiblity.bind(this), element:input});
input.style.visibility = option.visiblity.bind(this)() ? "visible" : "hidden";
}
}
}
@ -395,22 +382,28 @@ class HuesSettings {
this.hasUI = true;
}
get(setting) {
if(this.defaults.hasOwnProperty(setting)) {
if(ephemeralSettings.indexOf(setting) != -1) {
return this.defaults[setting];
} else {
makeGetter(setting) {
return () => {
if(defaultSettings.hasOwnProperty(setting)) {
if(this.ephemerals[setting] !== undefined)
return this.ephemerals[setting];
else if(localStorage[setting] !== undefined)
return localStorage[setting];
}
else
return defaultSettings[setting];
} else {
console.log("WARNING: Attempted to fetch invalid setting:", setting);
return null;
}
};
}
// Set a named index to its named value, returns false if name doesn't exist
set(setting, value) {
value = value.toLowerCase();
makeSetter(setting) {
return value => {
if(this.isEphemeral(setting)) {
this.ephemerals[setting] = value;
} else {
let opt = settingsOptions[setting];
if(!opt || opt.options.indexOf(value) == -1) {
console.log(value, "is not a valid value for", setting);
@ -421,9 +414,16 @@ class HuesSettings {
this.settingCheckboxes[setting + "-" + value].checked = true;
} catch(e) {}
localStorage[setting] = value;
this.ephemerals[setting] = undefined;
}
this.updateConditionals();
this.callEventListeners("updated");
return true;
};
}
isEphemeral(setting) {
return settingsOptions[setting] === undefined;
}
updateConditionals() {
@ -438,19 +438,6 @@ class HuesSettings {
}
}
// 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];
}
}
}
callEventListeners(ev) {
let args = Array.prototype.slice.call(arguments, 1);
this.eventListeners[ev].forEach(function(callback) {

@ -23,7 +23,7 @@
"use strict";
class HuesWindow {
constructor(root, defaults) {
constructor(root, settings) {
this.eventListeners = {
/* callback windowshown(shown)
*
@ -38,7 +38,7 @@ class HuesWindow {
tabselected : []
};
this.hasUI = defaults.enableWindow;
this.hasUI = settings.enableWindow;
if(!this.hasUI)
return;
@ -69,7 +69,7 @@ class HuesWindow {
this.tabNames = [];
if(defaults.showWindow) {
if(settings.showWindow) {
this.show();
} else {
this.hide();

@ -82,7 +82,7 @@ class Resources {
this.remotes = null;
this.fileInput = null;
this.fileParseQueue = [];
if(core.settings.defaults.enableWindow) {
if(core.settings.enableWindow) {
this.initUI();
huesWin.addTab("RESOURCES", this.root);
}
@ -323,7 +323,7 @@ class Resources {
// 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) {
if(!this.core.settings.disableRemoteResources) {
remoteHeader = document.createElement("div");
remoteHeader.textContent = "Remote respacks";
remoteHeader.className = "respacks__header";
@ -388,7 +388,7 @@ class Resources {
packsContainer.appendChild(packHeader);
packsContainer.appendChild(packList);
if(!this.core.settings.defaults.disableRemoteResources) {
if(!this.core.settings.disableRemoteResources) {
packsContainer.appendChild(remoteHeader);
packsContainer.appendChild(remoteList);
}

@ -427,7 +427,7 @@ class SoundManager {
}
createWorker() {
return new Worker(this.core.settings.defaults.workersPath + 'audio-worker.js');
return new Worker(this.core.settings.workersPath + 'audio-worker.js');
}
initVisualiser(bars) {

Loading…
Cancel
Save