var StairsViewModel = function() { var self = this; self.tab = ko.observable('mode'); self.mode = ko.observable('Static'); self.static = { brightness: ko.observable(0) }; self.custom = { brightness: ko.observableArray([]) }; self.alternate = { interval: ko.observable(500), brightness: ko.observable(0) } self.slide = { interval: ko.observable(500), brightness: ko.observable(4096), direction: ko.observable(0), fadeOutTime: ko.observable(0) } self.firmware = { host: ko.observable(window.location.hostname), port: ko.observable(window.location.port), path: ko.observable('/checkUpdate') } self.loading = ko.observable(true); self.updatingFromServer = false; self.autoSetTimeout = null; self.autoSetMode = ko.computed(function() { if (self.loading()) return; var url = '/setMode/' + encodeURIComponent(self.mode()); switch (self.mode()) { case 'Static': url += '?brightness=' + encodeURIComponent(self.static.brightness()); break; case 'Custom': var values = self.custom.brightness().map(function(item) { return item.value(); }); values.reverse(); url += '?brightness=' + encodeURIComponent(values.join()); break; case 'Alternate': url += '?interval=' + encodeURIComponent(self.alternate.interval()) + '&brightness=' + encodeURIComponent(self.alternate.brightness()); break; case 'Slide': url += '?interval=' + encodeURIComponent(self.slide.interval()) + '&brightness=' + encodeURIComponent(self.slide.brightness()) + '&direction=' + encodeURIComponent(self.slide.direction()) + '&fadeOutTime=' + encodeURIComponent(self.slide.fadeOutTime()); break; } // Exit after checking all the parameters, so the observers // are properly subscribed if (self.updatingFromServer) return; if (self.autoSetTimeout !== null) { clearTimeout(self.autoSetTimeout); self.autoSetTimeout = null; } self.autoSetTimeout = setTimeout(function() { // TODO retry on failure $.ajax( { url: url, dataType: 'json', cache: false }); clearTimeout(self.autoSetTimeout); self.autoSetTimeout = null; }, 200); return true; }); self.ping = function() { self.loading(true); $.ajax( { url: '/ping', dataType: 'json', cache: false }) .done(function(data) { self.updatingFromServer = true; // Initialize the 'Custom' values based on the step count. // Each item is an object containing an observable; we can't // add the observable directly otherwise the foreach template // will unwrap it before we can attach it to the range input. var values = []; for (var index = 0; index < data.stepCount; index++) values.push({ value: ko.observable(0) }); self.custom.brightness(values); // TODO get current mode // TODO get range settings // TODO allow tweaking of range settings self.loading(false); self.updatingFromServer = false; }) .fail(function() { setTimeout(self.ping, 1000); }); }; self.setTab = function(tab) { self.tab(tab); return false; }; self.updateFirmware = function() { $.ajax( { url: '/updateFirmware', data: { host: self.firmware.host(), port: self.firmware.port(), path: self.firmware.path() }, dataType: 'json', cache: false }) .done(function(data) { alert('Update check running'); }) .fail(function() { alert('Could not check for firmware update'); }); }; }; $(function() { var viewModel = new StairsViewModel(); ko.applyBindings(viewModel); viewModel.ping(); });