From 228b1cb417b4c8fece3b343673d1b4aaf0068002 Mon Sep 17 00:00:00 2001 From: Egor Date: Fri, 7 Oct 2016 00:14:33 -0700 Subject: [PATCH] modernizing part 2 --- .../components/add-group-modal/component.js | 71 +- .../add-soundcloud-sound-modal/component.js | 65 +- .../components/bridge-finder/component.js | 123 +-- app/pods/components/color-picker/component.js | 25 +- .../delete-group-modal/component.js | 15 +- app/pods/components/groups-list/component.js | 120 ++- app/pods/components/hue-controls/component.js | 272 ++--- app/pods/components/huegasm-app/component.js | 60 +- app/pods/components/light-group/component.js | 138 +-- app/pods/components/lights-tab/component.js | 330 +++--- app/pods/components/music-tab/component.js | 973 +++++++++--------- .../components/music-tab/mixins/helpers.js | 273 ++--- .../components/music-tab/mixins/visualizer.js | 34 +- app/styles/app.scss | 2 +- 14 files changed, 1293 insertions(+), 1208 deletions(-) diff --git a/app/pods/components/add-group-modal/component.js b/app/pods/components/add-group-modal/component.js index 778a912..653fd7b 100644 --- a/app/pods/components/add-group-modal/component.js +++ b/app/pods/components/add-group-modal/component.js @@ -1,14 +1,48 @@ -import Em from 'ember'; +import Ember from 'ember'; + +const { + Component, + observer, + computed, + isEmpty, + isNone, + $ +} = Ember; + +export default Component.extend({ + groupName: null, + selectedLights: [], + + onIsShowingModalChange: observer('isShowingModal', function(){ + if(this.get('isShowingModal')){ + this.setProperties({ + selectedLights: [], + groupName: null + }); + } + }), + + saveDisabled: computed('groupName', 'selectedLights.[]', function(){ + return isNone(this.get('groupName')) || isEmpty(this.get('selectedLights')) || isEmpty(this.get('groupName').trim()); + }), + + didInsertElement: function() { + $(document).keypress((event) => { + if(!this.get('saveDisabled') && event.which === 13) { + this.send('save'); + } + }); + }, -export default Em.Component.extend({ actions: { close: function(){ this.sendAction(); }, save: function(){ - var newGroupData = {"name": this.get('groupName'), "lights": this.get('selectedLights')}, newGroupsData = this.get('groupsData'); + let newGroupData = {"name": this.get('groupName'), "lights": this.get('selectedLights')}, + newGroupsData = this.get('groupsData'); - Em.$.ajax(this.get('apiURL') + '/groups', { + $.ajax(this.get('apiURL') + '/groups', { data: JSON.stringify(newGroupData), contentType: 'application/json', type: 'POST' @@ -24,7 +58,7 @@ export default Em.Component.extend({ this.sendAction(); }, clickLight: function(id) { - var selectedLights = this.get('selectedLights'); + let selectedLights = this.get('selectedLights'); if(selectedLights.contains(id)){ selectedLights.removeObject(id); @@ -32,30 +66,5 @@ export default Em.Component.extend({ selectedLights.pushObject(id); } } - }, - - didInsertElement: function() { - Em.$(document).keypress((event) => { - if(!this.get('saveDisabled') && event.which === 13) { - this.send('save'); - } - }); - }, - - groupName: null, - - selectedLights: [], - - onIsShowingModalChange: function(){ - if(this.get('isShowingModal')){ - this.setProperties({ - selectedLights: [], - groupName: null - }); - } - }.observes('isShowingModal'), - - saveDisabled: function(){ - return Em.isNone(this.get('groupName')) || Em.isEmpty(this.get('selectedLights')) || Em.isEmpty(this.get('groupName').trim()); - }.property('groupName', 'selectedLights.[]') + } }); diff --git a/app/pods/components/add-soundcloud-sound-modal/component.js b/app/pods/components/add-soundcloud-sound-modal/component.js index fb9b114..d95bc08 100644 --- a/app/pods/components/add-soundcloud-sound-modal/component.js +++ b/app/pods/components/add-soundcloud-sound-modal/component.js @@ -1,6 +1,39 @@ -import Em from 'ember'; +import Ember from 'ember'; + +const { + Component, + observer, + computed, + isEmpty, + isNone, + $ +} = Ember; + +export default Component.extend({ + url: null, + + onIsShowingModalChange: observer('isShowingModal', function(){ + if(this.get('isShowingModal')){ + this.set('url', null); + setTimeout(()=>{ + $('md-input-container input').focus(); + }, 500); + } + + }), + + saveDisabled: computed('url', function(){ + return isNone(this.get('url')) || isEmpty(this.get('url').trim()); + }), + + didInsertElement: function() { + $(document).keypress((event)=>{ + if(!this.get('saveDisabled') && event.which === 13) { + this.send('add'); + } + }); + }, -export default Em.Component.extend({ actions: { close () { this.sendAction(); @@ -8,31 +41,5 @@ export default Em.Component.extend({ add (){ this.sendAction('action', this.get('url')); } - }, - - url: null, - - onIsShowingModalChange: function(){ - if(this.get('isShowingModal')){ - this.set('url', null); - setTimeout(()=>{ - Em.$('md-input-container input').focus(); - }, 500); - } - - }.observes('isShowingModal'), - - didInsertElement: function() { - var self = this; - - Em.$(document).keypress(function(event) { - if(!self.get('saveDisabled') && event.which === 13) { - self.send('add'); - } - }); - }, - - saveDisabled: function(){ - return Em.isNone(this.get('url')) || Em.isEmpty(this.get('url').trim()); - }.property('url') + } }); diff --git a/app/pods/components/bridge-finder/component.js b/app/pods/components/bridge-finder/component.js index 6b68fd1..c25cd11 100644 --- a/app/pods/components/bridge-finder/component.js +++ b/app/pods/components/bridge-finder/component.js @@ -1,65 +1,51 @@ -import Em from 'ember'; +import Ember from 'ember'; -export default Em.Component.extend({ +const { + Component, + observer, + computed, + on, + isNone, + $ +} = Ember; + +export default Component.extend({ classNames: ['container', 'bridgeFinder'], - bridgeIp: null, trial: false, bridgeUsername: null, - bridgeFindStatus: null, - bridgeFindSuccess: Em.computed.equal('bridgeFindStatus', 'success'), - bridgeFindMultiple: Em.computed.equal('bridgeFindStatus', 'multiple'), - bridgeFindFail: Em.computed.equal('bridgeFindStatus', 'fail'), - - // 30 seconds - bridgeUsernamePingMaxTime: 30000, + bridgeFindSuccess: computed.equal('bridgeFindStatus', 'success'), + bridgeFindMultiple: computed.equal('bridgeFindStatus', 'multiple'), + bridgeFindFail: computed.equal('bridgeFindStatus', 'fail'), + bridgeUsernamePingMaxTime: 30000, // 30 seconds bridgeUsernamePingIntervalTime: 1000, bridgeUserNamePingIntervalProgress: 0, - bridgePingIntervalHandle: null, bridgeAuthenticateReachedStatus: null, - manualBridgeIp: null, manualBridgeIpNotFound: false, multipleBridgeIps: [], error: false, - actions: { - retry(){ - this.onBridgeIpChange(); - }, + isAuthenticating: computed('bridgePingIntervalHandle', function(){ + return this.get('bridgePingIntervalHandle') !== null; + }), - findBridgeByIp() { - var manualBridgeIp = this.get('manualBridgeIp'); - - if (manualBridgeIp.toLowerCase() === 'trial' || manualBridgeIp.toLowerCase() === 'offline') { - this.setProperties({ - trial: true, - bridgeIp: 'trial', - bridgeUsername: 'trial' - }); - } else { - Em.$.ajax('http://' + manualBridgeIp + '/api', { - data: JSON.stringify({"devicetype": "huegasm"}), - contentType: 'application/json', - type: 'POST' - }).fail(() => { - this.set('manualBridgeIpNotFound', true); - setTimeout(() => { this.set('manualBridgeIpNotFound', false); }, 5000); - }).then(() => { - this.set('bridgeIp', manualBridgeIp); - }); - } + // try to authenticate against the bridge here + onBridgeIpChange: on('init', observer('bridgeIp', function(){ + if(!this.get('trial') && !this.get('isAuthenticating')) { + this.setProperties({ + bridgePingIntervalHandle: setInterval(this.pingBridgeUser.bind(this), this.get('bridgeUsernamePingIntervalTime')), + bridgeUserNamePingIntervalProgress: 0 + }); } - }, + })), didInsertElement() { - var self = this; - - Em.$(document).keypress(function(event) { - if(!Em.isNone(self.get('manualBridgeIp')) && event.which === 13) { - self.send('findBridgeByIp'); + $(document).keypress((event)=>{ + if(!isNone(this.get('manualBridgeIp')) && event.which === 13) { + this.send('findBridgeByIp'); } }); }, @@ -69,18 +55,18 @@ export default Em.Component.extend({ this._super(); if(this.get('bridgeIp') === null) { - Em.$.ajax('https://www.meethue.com/api/nupnp', { + $.ajax('https://www.meethue.com/api/nupnp', { timeout: 30000 }) .done((result, status)=> { - var bridgeFindStatus = 'fail'; + let bridgeFindStatus = 'fail'; if (status === 'success' && result.length === 1) { this.set('bridgeIp', result[0].internalipaddress); this.get('storage').set('huegasm.bridgeIp', result[0].internalipaddress); bridgeFindStatus = 'success'; } else if (result.length > 1) { - var multipleBridgeIps = this.get('multipleBridgeIps'); + let multipleBridgeIps = this.get('multipleBridgeIps'); result.forEach(function (item) { multipleBridgeIps.pushObject(item.internalipaddress); @@ -99,23 +85,13 @@ export default Em.Component.extend({ } }, - // try to authenticate against the bridge here - onBridgeIpChange: function () { - if(!this.get('trial') && !this.get('isAuthenticating')) { - this.setProperties({ - bridgePingIntervalHandle: setInterval(this.pingBridgeUser.bind(this), this.get('bridgeUsernamePingIntervalTime')), - bridgeUserNamePingIntervalProgress: 0 - }); - } - }.observes('bridgeIp').on('init'), - pingBridgeUser() { - var bridgeIp = this.get('bridgeIp'), + let bridgeIp = this.get('bridgeIp'), bridgeUserNamePingIntervalProgress = this.get('bridgeUserNamePingIntervalProgress'), bridgeUsernamePingMaxTime = this.get('bridgeUsernamePingMaxTime'); if (bridgeIp !== null && bridgeUserNamePingIntervalProgress < 100) { - Em.$.ajax('http://' + bridgeIp + '/api', { + $.ajax('http://' + bridgeIp + '/api', { data: JSON.stringify({"devicetype": "huegasm"}), contentType: 'application/json', type: 'POST' @@ -143,7 +119,32 @@ export default Em.Component.extend({ this.set('bridgePingIntervalHandle', null); }, - isAuthenticating: function(){ - return this.get('bridgePingIntervalHandle') !== null; - }.property('bridgePingIntervalHandle') + actions: { + retry(){ + this.onBridgeIpChange(); + }, + + findBridgeByIp() { + let manualBridgeIp = this.get('manualBridgeIp'); + + if (manualBridgeIp.toLowerCase() === 'trial' || manualBridgeIp.toLowerCase() === 'offline') { + this.setProperties({ + trial: true, + bridgeIp: 'trial', + bridgeUsername: 'trial' + }); + } else { + $.ajax('http://' + manualBridgeIp + '/api', { + data: JSON.stringify({"devicetype": "huegasm"}), + contentType: 'application/json', + type: 'POST' + }).fail(() => { + this.set('manualBridgeIpNotFound', true); + setTimeout(() => { this.set('manualBridgeIpNotFound', false); }, 5000); + }).then(() => { + this.set('bridgeIp', manualBridgeIp); + }); + } + } + }, }); diff --git a/app/pods/components/color-picker/component.js b/app/pods/components/color-picker/component.js index bfcd79b..dd95379 100644 --- a/app/pods/components/color-picker/component.js +++ b/app/pods/components/color-picker/component.js @@ -1,12 +1,16 @@ -import Em from 'ember'; +import Ember from 'ember'; -export default Em.Component.extend({ +const { + Component, + $ +} = Ember; + +export default Component.extend({ classNames: ['colorpicker'], - rgb: null, - canvas: null, canvasContext: null, + pressingDown: false, mouseUp(){ this.set('pressingDown', false); @@ -19,12 +23,13 @@ export default Em.Component.extend({ }, mouseDown(event){ - var canvasOffset = Em.$(this.get('canvas')).offset(); - var canvasX = Math.floor(event.pageX - canvasOffset.left), canvasY = Math.floor(event.pageY - canvasOffset.top); + let canvasOffset = $(this.get('canvas')).offset(), + canvasX = Math.floor(event.pageX - canvasOffset.left), + canvasY = Math.floor(event.pageY - canvasOffset.top); // get current pixel - var imageData = this.get('canvasContext').getImageData(canvasX, canvasY, 1, 1); - var pixel = imageData.data; + let imageData = this.get('canvasContext').getImageData(canvasX, canvasY, 1, 1), + pixel = imageData.data; this.set('pressingDown', true); @@ -33,12 +38,10 @@ export default Em.Component.extend({ } }, - pressingDown: false, - // https://dzone.com/articles/creating-your-own-html5 didInsertElement(){ // handle color changes - var canvas = Em.$('#picker')[0], + let canvas = $('#picker')[0], canvasContext = canvas.getContext('2d'), image = new Image(); diff --git a/app/pods/components/delete-group-modal/component.js b/app/pods/components/delete-group-modal/component.js index 418c354..c9ac9be 100644 --- a/app/pods/components/delete-group-modal/component.js +++ b/app/pods/components/delete-group-modal/component.js @@ -1,19 +1,24 @@ -import Em from 'ember'; +import Ember from 'ember'; -export default Em.Component.extend({ +const { + Component, + $ +} = Ember; + +export default Component.extend({ actions: { close: function(){ this.sendAction(); }, delete: function(){ - var groupId = this.get('groupId'); + let groupId = this.get('groupId'); - Em.$.ajax(this.get('apiURL') + '/groups/' + groupId, { + $.ajax(this.get('apiURL') + '/groups/' + groupId, { contentType: 'application/json', type: 'DELETE' }); - var groupsData = this.get('groupsData'), newGroupsData = []; + let groupsData = this.get('groupsData'), newGroupsData = []; for (let key in groupsData) { if(groupsData.hasOwnProperty(key) && groupsData[key].name !== this.get('groupName') ){ newGroupsData[key] = groupsData[key]; diff --git a/app/pods/components/groups-list/component.js b/app/pods/components/groups-list/component.js index ba8c0b5..b5f2f08 100644 --- a/app/pods/components/groups-list/component.js +++ b/app/pods/components/groups-list/component.js @@ -1,13 +1,73 @@ -import Em from 'ember'; +import Ember from 'ember'; -export default Em.Component.extend({ +const { + Component, + observer, + computed, + isEmpty, + isNone, +} = Ember; + +export default Component.extend({ classNames: ['dropdown-menu'], elementId: 'groupList', - tagName: null, - groupIdSelection: null, + groupsArrData: computed('groupsData', 'groupIdSelection', function(){ + let groupsData = this.get('groupsData'), lightsData = this.get('lightsData'), groupsArrData = [], ids = [], groupIdSelection = this.get('groupIdSelection'); + + for (let key in lightsData) { + if(lightsData.hasOwnProperty(key) && lightsData[key].state.reachable){ + ids.push(key); + } + } + groupsArrData.push({name: 'All', data: {lights: ids, key: '0' }, rowClass: groupIdSelection === '0' ? 'groupRow selectedRow' : 'groupRow', deletable: false}); + + for (let key in groupsData) { + if (groupsData.hasOwnProperty(key)) { + let rowClass = 'groupRow'; + + if(key === groupIdSelection){ + rowClass += ' selectedRow'; + } + + groupsArrData.push({name: groupsData[key].name, data: {lights: groupsData[key].lights, key: key}, rowClass: rowClass, deletable: true}); + } + } + + return groupsArrData; + }), + + onGroupIdSelectionChanged: observer('groupIdSelection', 'groupsArrData', function(){ + let groupIdSelection = this.get('groupIdSelection'), + lights = []; + + this.get('groupsArrData').some(function(group){ + if(group.data.key === groupIdSelection){ + lights = group.data.lights; + return true; + } + }); + + this.get('storage').set('huegasm.selectedGroup', groupIdSelection); + + if(!isNone(groupIdSelection) && !isEmpty(lights)){ + this.set('activeLights', lights); + } + }), + + didInsertElement(){ + let selectGroup = '0', + storageItem = this.get('storage').get('huegasm.selectedGroup'); + + if(storageItem){ + selectGroup = storageItem; + } + + this.set('groupIdSelection', selectGroup); + }, + actions: { selectGroup(selection){ this.set('groupIdSelection', selection); @@ -22,57 +82,5 @@ export default Em.Component.extend({ toggleAddGroupsModal(){ this.toggleProperty('isShowingAddGroupsModal'); } - }, - - groupsArrData: function(){ - var groupsData = this.get('groupsData'), lightsData = this.get('lightsData'), groupsArrData = [], ids = [], groupIdSelection = this.get('groupIdSelection'); - - for (let key in lightsData) { - if(lightsData.hasOwnProperty(key) && lightsData[key].state.reachable){ - ids.push(key); - } - } - groupsArrData.push({name: 'All', data: {lights: ids, key: '0' }, rowClass: groupIdSelection === '0' ? 'groupRow selectedRow' : 'groupRow', deletable: false}); - - for (let key in groupsData) { - if (groupsData.hasOwnProperty(key)) { - var rowClass = 'groupRow'; - - if(key === groupIdSelection){ - rowClass += ' selectedRow'; - } - - groupsArrData.push({name: groupsData[key].name, data: {lights: groupsData[key].lights, key: key}, rowClass: rowClass, deletable: true}); - } - } - - return groupsArrData; - }.property('groupsData', 'groupIdSelection'), - - onGroupIdSelectionChanged: function(){ - var groupIdSelection = this.get('groupIdSelection'), lights = []; - - this.get('groupsArrData').some(function(group){ - if(group.data.key === groupIdSelection){ - lights = group.data.lights; - return true; - } - }); - - this.get('storage').set('huegasm.selectedGroup', groupIdSelection); - - if(!Em.isNone(groupIdSelection) && !Em.isEmpty(lights)){ - this.set('activeLights', lights); - } - }.observes('groupIdSelection', 'groupsArrData'), - - didInsertElement(){ - var selectGroup = '0', storageItem = this.get('storage').get('huegasm.selectedGroup'); - - if(storageItem){ - selectGroup = storageItem; - } - - this.set('groupIdSelection', selectGroup); } }); diff --git a/app/pods/components/hue-controls/component.js b/app/pods/components/hue-controls/component.js index 0138ca7..6db718c 100644 --- a/app/pods/components/hue-controls/component.js +++ b/app/pods/components/hue-controls/component.js @@ -1,27 +1,136 @@ -import Em from 'ember'; +import Ember from 'ember'; -export default Em.Component.extend({ +const { + Component, + observer, + computed, + isEmpty, + isNone, + run, + $ +} = Ember; + +export default Component.extend({ classNames: ['container-fluid'], elementId: 'hueControls', - bridgeIp: null, manualBridgeIp: null, bridgeUsername: null, - updateGroupsData: true, groupsData: null, lightsData: null, - activeLights: [], + tabList: ["Lights", "Music"], + selectedTab: 1, + pauseLightUpdates: false, + + lightsTabSelected: computed.equal('selectedTab', 0), + musicTabSelected: computed.equal('selectedTab', 1), + + dimmerOnClass: computed('dimmerOn', function(){ + return this.get('dimmerOn') ? 'dimmerOn' : null; + }), + + ready: computed('lightsData', 'trial', function() { + return this.get('trial') || !isNone(this.get('lightsData')); + }), + + apiURL: computed('bridgeIp', 'bridgeUsername', function(){ + return 'http://' + this.get('bridgeIp') + '/api/' + this.get('bridgeUsername'); + }), + + tabData: computed('tabList', 'selectedTab', function(){ + let tabData = [], selectedTab = this.get('selectedTab'); + + this.get('tabList').forEach(function(tab, i){ + let selected = false; + + if(i === selectedTab){ + selected = true; + } + + tabData.push({"name": tab, "selected": selected }); + }); + + return tabData; + }), + + didInsertElement(){ + // here's a weird way to automatically initialize bootstrap tooltips + let observer = new MutationObserver(function(mutations) { + let haveTooltip = !mutations.every(function(mutation) { + return isEmpty(mutation.addedNodes) || isNone(mutation.addedNodes[0].classList) || mutation.addedNodes[0].classList.contains('tooltip'); + }); + + if(haveTooltip) { + run.once(this, function(){ + $('.bootstrapTooltip').tooltip(); + }); + } + }); + + observer.observe($('#hueControls')[0], {childList: true, subtree: true}); + }, + + init() { + this._super(); + + if(!this.get('trial')) { + this.doUpdateGroupsData(); + this.updateLightData(); + this.set('lightsDataIntervalHandle', setInterval(this.updateLightData.bind(this), 2000)); + } + + if (!isNone(this.get('storage').get('huegasm.selectedTab'))) { + this.set('selectedTab', this.get('storage').get('huegasm.selectedTab')); + } + }, + + onUpdateGroupsDataChange: observer('updateGroupsData', function(){ + if(this.get('updateGroupsData')){ + setTimeout(()=>{ this.doUpdateGroupsData(); }, 1000); + } + }), + + doUpdateGroupsData(){ + $.get(this.get('apiURL') + '/groups', (result, status)=>{ + if (status === 'success' ) { + this.set('groupsData', result); + } + }); + + this.toggleProperty('updateGroupsData'); + }, + + updateLightData(){ + let fail = ()=>{ + clearInterval(this.get('lightsDataIntervalHandle')); + + this.get('storage').remove('huegasm.bridgeIp'); + this.get('storage').remove('huegasm.bridgeUsername'); + + location.reload(); + }; + + if(!this.get('pauseLightUpdates')){ + $.get(this.get('apiURL') + '/lights', (result, status)=>{ + if(!isNone(result[0]) && !isNone(result[0].error)){ + fail(); + } else if (status === 'success' && JSON.stringify(this.get('lightsData')) !== JSON.stringify(result)) { + this.set('lightsData', result); + } + }).fail(fail); + } + }, actions: { changeTab(tabName){ - var index = this.get('tabList').indexOf(tabName); + let index = this.get('tabList').indexOf(tabName); this.set('selectedTab', index); this.get('storage').set('huegasm.selectedTab', index); }, clearBridge() { - var storage = this.get('storage'); + let storage = this.get('storage'); storage.remove('huegasm.bridgeUsername'); storage.remove('huegasm.bridgeIp'); location.reload(); @@ -31,10 +140,10 @@ export default Em.Component.extend({ location.reload(); }, startIntro(){ - var INTRO = introJs, + let INTRO = introJs, intro = INTRO(), - playerBottom = Em.$('#playerBottom'), - beatDetectionAreaArrowIcon = Em.$('#beatDetectionAreaArrowIcon'); + playerBottom = $('#playerBottom'), + beatDetectionAreaArrowIcon = $('#beatDetectionAreaArrowIcon'); this.set('dimmerOn', false); @@ -88,12 +197,12 @@ export default Em.Component.extend({ 'You may toggle a light\'s state by clicking on it.' }, { - element: Em.$('.settingsItem')[0], + element: $('.settingsItem')[0], intro: 'The Groups menu allows for saving and quickly selecting groups of lights.', position: 'left' }, { - element: Em.$('.settingsItem')[1], + element: $('.settingsItem')[1], intro: 'A few miscellaneous settings can be found here.

' + 'WARNING: clearing application settings will restore the application to its original state. This will even delete your playlist and any saved song beat preferences.', position: 'left' @@ -110,15 +219,15 @@ export default Em.Component.extend({ // it's VERY ugly but it works intro.onchange((element) => { if(element.id === 'musicTab' || element.id === 'playlist' || element.id === 'playerArea' || element.id === 'beatOptionRow' || element.id === 'beatOptionButtonGroup' || element.id === 'beatContainer' || element.id === 'usingMicAudioTooltip'){ - Em.$('#musicTab').removeClass('hidden'); - Em.$('#lightsTab').addClass('hidden'); - Em.$('.navigationItem').eq(0).removeClass('active'); - Em.$('.navigationItem').eq(1).addClass('active'); + $('#musicTab').removeClass('hidden'); + $('#lightsTab').addClass('hidden'); + $('.navigationItem').eq(0).removeClass('active'); + $('.navigationItem').eq(1).addClass('active'); } else { - Em.$('#lightsTab').removeClass('hidden'); - Em.$('#musicTab').addClass('hidden'); - Em.$('.navigationItem').eq(1).removeClass('active'); - Em.$('.navigationItem').eq(0).addClass('active'); + $('#lightsTab').removeClass('hidden'); + $('#musicTab').addClass('hidden'); + $('.navigationItem').eq(1).removeClass('active'); + $('.navigationItem').eq(0).addClass('active'); } if(element.id === 'musicTab' || element.id === 'playlist' || element.id === 'playerArea'){ @@ -134,16 +243,16 @@ export default Em.Component.extend({ beatDetectionAreaArrowIcon.removeClass('keyboard-arrow-down').addClass('keyboard-arrow-up'); } } else if(element.id === 'dimmer'){ - Em.$(document).click(); + $(document).click(); } }); - var onFinish = ()=>{ + let onFinish = ()=>{ this.set('activeTab', 1); - Em.$('#musicTab').removeClass('hidden'); - Em.$('#lightsTab').addClass('hidden'); - Em.$('.navigationItem').eq(0).removeClass('active'); - Em.$('.navigationItem').eq(1).addClass('active'); + $('#musicTab').removeClass('hidden'); + $('#lightsTab').addClass('hidden'); + $('.navigationItem').eq(0).removeClass('active'); + $('.navigationItem').eq(1).addClass('active'); if(beatDetectionAreaArrowIcon.hasClass('keyboard-arrow-up')){ playerBottom.show(); @@ -151,7 +260,7 @@ export default Em.Component.extend({ playerBottom.hide(); } }, onExit = ()=>{ - var dimmer = Em.$('#dimmer'); + let dimmer = $('#dimmer'); onFinish(); dimmer.popover({ @@ -167,114 +276,11 @@ export default Em.Component.extend({ // skip hidden/missing elements intro.onafterchange((element)=>{ - var elem = Em.$(element); + let elem = $(element); if(elem.html() === ''){ - Em.$('.introjs-nextbutton').click(); + $('.introjs-nextbutton').click(); } }).onexit(onExit).oncomplete(onFinish).start(); } - }, - - apiURL: function(){ - return 'http://' + this.get('bridgeIp') + '/api/' + this.get('bridgeUsername'); - }.property('bridgeIp', 'bridgeUsername'), - - didInsertElement(){ - // here's a weird way to automatically initialize bootstrap tooltips - var observer = new MutationObserver(function(mutations) { - var haveTooltip = !mutations.every(function(mutation) { - return Em.isEmpty(mutation.addedNodes) || Em.isNone(mutation.addedNodes[0].classList) || mutation.addedNodes[0].classList.contains('tooltip'); - }); - - if(haveTooltip) { - Em.run.once(this, function(){ - Em.$('.bootstrapTooltip').tooltip(); - }); - } - }); - - observer.observe(Em.$('#hueControls')[0], {childList: true, subtree: true}); - }, - - init() { - this._super(); - - if(!this.get('trial')) { - this.doUpdateGroupsData(); - this.updateLightData(); - this.set('lightsDataIntervalHandle', setInterval(this.updateLightData.bind(this), 2000)); - } - - if (!Em.isNone(this.get('storage').get('huegasm.selectedTab'))) { - this.set('selectedTab', this.get('storage').get('huegasm.selectedTab')); - } - }, - - onUpdateGroupsDataChange: function(){ - if(this.get('updateGroupsData')){ - setTimeout(()=>{ this.doUpdateGroupsData(); }, 1000); - } - }.observes('updateGroupsData'), - - doUpdateGroupsData(){ - Em.$.get(this.get('apiURL') + '/groups', (result, status)=>{ - if (status === 'success' ) { - this.set('groupsData', result); - } - }); - - this.toggleProperty('updateGroupsData'); - }, - - tabList: ["Lights", "Music"], - selectedTab: 1, - tabData: function(){ - var tabData = [], selectedTab = this.get('selectedTab'); - - this.get('tabList').forEach(function(tab, i){ - var selected = false; - - if(i === selectedTab){ - selected = true; - } - - tabData.push({"name": tab, "selected": selected }); - }); - - return tabData; - }.property('tabList', 'selectedTab'), - - lightsTabSelected: Em.computed.equal('selectedTab', 0), - musicTabSelected: Em.computed.equal('selectedTab', 1), - - pauseLightUpdates: false, - - updateLightData(){ - var fail = ()=>{ - clearInterval(this.get('lightsDataIntervalHandle')); - - this.get('storage').remove('huegasm.bridgeIp'); - this.get('storage').remove('huegasm.bridgeUsername'); - - location.reload(); - }; - - if(!this.get('pauseLightUpdates')){ - Em.$.get(this.get('apiURL') + '/lights', (result, status)=>{ - if(!Em.isNone(result[0]) && !Em.isNone(result[0].error)){ - fail(); - } else if (status === 'success' && JSON.stringify(this.get('lightsData')) !== JSON.stringify(result)) { - this.set('lightsData', result); - } - }).fail(fail); - } - }, - - dimmerOnClass: function(){ - return this.get('dimmerOn') ? 'dimmerOn' : null; - }.property('dimmerOn'), - - ready: function() { - return this.get('trial') || !Em.isNone(this.get('lightsData')); - }.property('lightsData', 'trial') + } }); diff --git a/app/pods/components/huegasm-app/component.js b/app/pods/components/huegasm-app/component.js index 758b67f..4602087 100644 --- a/app/pods/components/huegasm-app/component.js +++ b/app/pods/components/huegasm-app/component.js @@ -1,56 +1,55 @@ -import Em from 'ember'; +import Ember from 'ember'; -export default Em.Component.extend({ - actions: { - toggleDimmer(){ - this.toggleProperty('dimmerOn'); - }, - isReady(){ - this.set('ready', true); - } - }, +const { + Component, + computed, + isEmpty, + isNone, + $ +} = Ember; + +export default Component.extend({ bridgeIp: null, - bridgeUsername: null, - trial: false, - storage: null, - dimmerOn: false, - ready: false, - dimmerOnClass: function () { - var dimmerOn = this.get('dimmerOn'), + year: computed(function(){ + return new Date().getFullYear(); + }), + + dimmerOnClass: computed('dimmerOn', function(){ + let dimmerOn = this.get('dimmerOn'), storage = this.get('storage'), dimmerOnClass = null; if (dimmerOn) { - Em.$('body').addClass('dimmerOn'); - Em.$('html').addClass('dimmerOn'); + $('body').addClass('dimmerOn'); + $('html').addClass('dimmerOn'); dimmerOnClass = 'active'; } else { - Em.$('body').removeClass('dimmerOn'); - Em.$('html').removeClass('dimmerOn'); + $('body').removeClass('dimmerOn'); + $('html').removeClass('dimmerOn'); } storage.set('huegasm.dimmerOn', dimmerOn); return dimmerOnClass; - }.property('dimmerOn'), + }), init(){ this._super(); - var storage = new window.Locally.Store({compress: true}); + let storage = new window.Locally.Store({compress: true}); this.set('storage', storage); - if (!Em.isNone(storage.get('huegasm.dimmerOn'))) { + if (!isNone(storage.get('huegasm.dimmerOn'))) { this.set('dimmerOn', storage.get('huegasm.dimmerOn')); } - if (!Em.isEmpty(storage.get('huegasm.bridgeIp')) && !Em.isEmpty(storage.get('huegasm.bridgeUsername'))) { + if (!isEmpty(storage.get('huegasm.bridgeIp')) && !isEmpty(storage.get('huegasm.bridgeUsername'))) { this.setProperties({ bridgeIp: storage.get('huegasm.bridgeIp'), bridgeUsername: storage.get('huegasm.bridgeUsername') @@ -58,7 +57,12 @@ export default Em.Component.extend({ } }, - year: function () { - return new Date().getFullYear(); - }.property() + actions: { + toggleDimmer(){ + this.toggleProperty('dimmerOn'); + }, + isReady(){ + this.set('ready', true); + } + } }); diff --git a/app/pods/components/light-group/component.js b/app/pods/components/light-group/component.js index 7fce746..9ea9d2c 100644 --- a/app/pods/components/light-group/component.js +++ b/app/pods/components/light-group/component.js @@ -1,73 +1,26 @@ -import Em from 'ember'; +import Ember from 'ember'; -export default Em.Component.extend({ +const { + Component, + observer, + isEmpty, + $, + A +} = Ember; +export default Component.extend({ classNames: ['lightGroup'], - isHovering: false, - - lightsList: Em.A(), - - actions: { - clickLight(id, data){ - var light = Em.$('.light'+id); - - if(!light.hasClass('bootstrapTooltip')){ - light = light.parent(); - } - - if(light.hasClass('lightInactive')){ - light.addClass('lightActive').removeClass('lightInactive'); - } else if(light.hasClass('lightActive')){ - light.addClass('lightInactive').removeClass('lightActive'); - } - - this.sendAction('action', id, data); - }, - lightStartHover(id){ - var hoveredLight = this.get('lightsList').filter(function(light){ - return light.activeClass !== 'unreachable' && light.id === id[0]; - }); - - if(!Em.isEmpty(hoveredLight) && this.get('noHover') !== true){ - Em.$.ajax(this.get('apiURL') + '/lights/' + id + '/state', { - data: JSON.stringify({"alert": "lselect"}), - contentType: 'application/json', - type: 'PUT' - }); - } - - this.set('isHovering', true); - }, - lightStopHover(id){ - var hoveredLight = this.get('lightsList').filter(function(light){ - return light.activeClass !== 'unreachable' && light.id === id[0]; - }); - - if(!Em.isEmpty(hoveredLight) && this.get('noHover') !== true){ - Em.$.ajax(this.get('apiURL') + '/lights/' + id + '/state', { - data: JSON.stringify({"alert": "none"}), - contentType: 'application/json', - type: 'PUT' - }); - } - - this.set('isHovering', false); - this.onLightsDataChange(); - } - }, - - didInsertElement() { - if(this.get('lightsData')){ - this.onLightsDataChange(); - } - }, + lightsList: A(), // list of all the lights in the hue system - onLightsDataChange: function(){ + onLightsDataChange: observer('lightsData', 'activeLights.[]', 'dimmerOn', function(){ if(!this.get('isHovering')){ - var lightsData = this.get('lightsData'), lightsList = Em.A(), type; - for (var key in lightsData) { + let lightsData = this.get('lightsData'), + lightsList = A(), + type; + + for (let key in lightsData) { if (lightsData.hasOwnProperty(key) && lightsData[key].state.reachable) { switch(lightsData[key].modelid){ case 'LCT001': @@ -110,7 +63,7 @@ export default Em.Component.extend({ type = 'a19'; } - var activeClass = 'lightActive'; + let activeClass = 'lightActive'; if(!this.get('activeLights').contains(key)){ activeClass = 'lightInactive'; @@ -122,5 +75,60 @@ export default Em.Component.extend({ this.set('lightsList', lightsList); } - }.observes('lightsData', 'activeLights.[]', 'dimmerOn') + }), + + didInsertElement() { + if(this.get('lightsData')){ + this.onLightsDataChange(); + } + }, + + actions: { + clickLight(id, data){ + let light = $('.light'+id); + + if(!light.hasClass('bootstrapTooltip')){ + light = light.parent(); + } + + if(light.hasClass('lightInactive')){ + light.addClass('lightActive').removeClass('lightInactive'); + } else if(light.hasClass('lightActive')){ + light.addClass('lightInactive').removeClass('lightActive'); + } + + this.sendAction('action', id, data); + }, + lightStartHover(id){ + let hoveredLight = this.get('lightsList').filter(function(light){ + return light.activeClass !== 'unreachable' && light.id === id[0]; + }); + + if(!isEmpty(hoveredLight) && this.get('noHover') !== true){ + $.ajax(this.get('apiURL') + '/lights/' + id + '/state', { + data: JSON.stringify({"alert": "lselect"}), + contentType: 'application/json', + type: 'PUT' + }); + } + + this.set('isHovering', true); + }, + lightStopHover(id){ + let hoveredLight = this.get('lightsList').filter(function(light){ + return light.activeClass !== 'unreachable' && light.id === id[0]; + }); + + if(!isEmpty(hoveredLight) && this.get('noHover') !== true){ + $.ajax(this.get('apiURL') + '/lights/' + id + '/state', { + data: JSON.stringify({"alert": "none"}), + contentType: 'application/json', + type: 'PUT' + }); + } + + this.set('isHovering', false); + this.onLightsDataChange(); + } + } }); diff --git a/app/pods/components/lights-tab/component.js b/app/pods/components/lights-tab/component.js index d8cacfa..99a1966 100644 --- a/app/pods/components/lights-tab/component.js +++ b/app/pods/components/lights-tab/component.js @@ -1,6 +1,13 @@ -import Em from 'ember'; +import Ember from 'ember'; -export default Em.Component.extend({ +const { + Component, + observer, + computed, + $ +} = Ember; + +export default Component.extend({ classNames: ['col-sm-8', 'col-sm-offset-2', 'col-xs-12'], classNameBindings: ['active::hidden'], elementId: 'lightsTab', @@ -12,9 +19,150 @@ export default Em.Component.extend({ colorPickerDisplayed: false, + rgb: [255, 255, 255], + + lightsOn: false, + + // COLOR LOOP related stuff + colorLoopOn: false, + + lightsOnTxt: computed('lightsOn', function(){ + return this.get('lightsOn') ? 'On' : 'Off'; + }), + + colorloopOnTxt: computed('colorLoopOn', function(){ + return this.get('colorLoopOn') ? 'On' : 'Off'; + }), + + colorRowAction: computed('strobeOn', function() { + if (this.get('trial')) { + return null; + } + + return 'toggleColorpicker'; + }), + + // determines the average brightness of the hue system for the brightness slider + lightsBrightness: computed('lightsData', function(){ + let lightsData = this.get('lightsData'), activeLights = this.get('activeLights'), lightsBrightness = 0; + + activeLights.forEach(function(light){ + lightsBrightness += lightsData[light].state.bri; + }); + + return lightsBrightness/activeLights.length; + }), + + brightnessControlDisabled: computed.not('lightsOn'), + + onColorLoopOnChange: observer('colorLoopOn', function(){ + let lightsData = this.get('lightsData'), + activeLights = this.get('activeLights'), + colorLoopsOn = this.get('colorLoopOn'), + effect = colorLoopsOn ? 'colorloop' : 'none'; + + let colorLoopsOnSystem = activeLights.some(function(light) { + return lightsData[light].state.effect === 'colorloop'; + }); + + // if the internal lights state is different than the one from lightsData ( user manually toggled the switch ), send the request to change the bulbs state + if(colorLoopsOn !== colorLoopsOnSystem){ + activeLights.forEach((light)=>{ + if(this.get('lightsData')[light].state.effect !== effect) { + $.ajax(this.get('apiURL') + '/lights/' + light + '/state', { + data: JSON.stringify({'effect': effect }), + contentType: 'application/json', + type: 'PUT' + }); + } + }); + } + }), + + rgbPreview: observer('rgb', function() { + let rgb = this.get('rgb'), + xy = this.rgbToXy(rgb[0], rgb[1], rgb[2]); + + this.set('colorLoopOn', false); + + this.get('activeLights').forEach((light) => { + $.ajax(this.get('apiURL') + '/lights/' + light + '/state', { + data: JSON.stringify({"xy": xy}), + contentType: 'application/json', + type: 'PUT' + }); + }); + + this.set('colorLoopOn', false); + $('.color').css('background', 'rgb(' + rgb[0] + ',' + rgb[1] + ',' + rgb[2] + ')'); + }), + + // determines whether the lights are on/off for the lights switch + lightsOnChange: observer('lightsData.@each.state.on', 'activeLights.[]', function(){ + if(!this.get('strobeOn')){ + let lightsData = this.get('lightsData'), lightsOn = this.get('activeLights').some(function(light) { + return lightsData[light].state.on === true; + }); + + this.set('lightsOn', lightsOn); + } + }), + + onLightsOnChange: observer('lightsOn', function(){ + let lightsData = this.get('lightsData'), activeLights = this.get('activeLights'), lightsOn = this.get('lightsOn'), self = this; + + let lightsOnSystem = activeLights.some(function(light) { + return lightsData[light].state.on === true; + }); + + // if the internal lights state is different than the one from lightsData ( user manually toggled the switch ), send the request to change the bulbs state + if(lightsOn !== lightsOnSystem){ + activeLights.forEach(function (light) { + $.ajax(self.get('apiURL') + '/lights/' + light + '/state', { + data: JSON.stringify({"on": lightsOn}), + contentType: 'application/json', + type: 'PUT' + }); + }); + } + }), + + onBrightnessChanged: observer('lightsBrightness', function(){ + let lightsData = this.get('lightsData'), lightsBrightnessSystem = false, lightsBrightness = this.get('lightsBrightness'), activeLights = this.get('activeLights'), self = this; + + activeLights.forEach(function(light){ + lightsBrightnessSystem += lightsData[light].state.bri; + }); + + lightsBrightnessSystem /= activeLights.length; + + // if the internal lights state is different than the one from lightsData ( user manually toggled the switch ), send the request to change the bulbs state + if(lightsBrightness !== lightsBrightnessSystem){ + activeLights.forEach(function(light){ + $.ajax(self.get('apiURL') + '/lights/' + light + '/state', { + data: JSON.stringify({"bri": lightsBrightness}), + contentType: 'application/json', + type: 'PUT' + }); + }); + } + }), + + didInsertElement() { + $(document).click((event)=>{ + if(this.get('colorPickerDisplayed') && !event.target.classList.contains('color') && !$(event.target).closest('.colorpicker, #colorRow').length) { + this.toggleProperty('colorPickerDisplayed'); + } + }); + + $(document).on('click', '#colorRow', () => { + this.send('toggleColorpicker'); + }); + }, + actions: { clickLight(light){ - var activeLights = this.get('activeLights'), + let activeLights = this.get('activeLights'), lightId = activeLights.indexOf(light); if(lightId !== -1){ @@ -23,7 +171,7 @@ export default Em.Component.extend({ activeLights.pushObject(light); // sync the current light settings to the newly added light - var options = {on: this.get('lightsOn'), bri: this.get('lightsBrightness'), effect: this.get('colorLoopOn') ? 'colorloop' : 'none'}, + let options = {on: this.get('lightsOn'), bri: this.get('lightsBrightness'), effect: this.get('colorLoopOn') ? 'colorloop' : 'none'}, rgb = this.get('rgb'); if(rgb[0] !== 255 && rgb[1] !== 255 && rgb[2] !== 255) { @@ -32,7 +180,7 @@ export default Em.Component.extend({ options['transitiontime'] = 0; - Em.$.ajax(this.get('apiURL') + '/lights/' + light + '/state', { + $.ajax(this.get('apiURL') + '/lights/' + light + '/state', { data: JSON.stringify(options), contentType: 'application/json', type: 'PUT' @@ -44,146 +192,6 @@ export default Em.Component.extend({ } }, - didInsertElement() { - Em.$(document).click((event)=>{ - if(this.get('colorPickerDisplayed') && !event.target.classList.contains('color') && !Em.$(event.target).closest('.colorpicker, #colorRow').length) { - this.toggleProperty('colorPickerDisplayed'); - } - }); - - Em.$(document).on('click', '#colorRow', () => { - this.send('toggleColorpicker'); - }); - }, - - rgb: [255, 255, 255], - rgbPreview: function() { - var rgb = this.get('rgb'), - xy = this.rgbToXy(rgb[0], rgb[1], rgb[2]); - - this.set('colorLoopOn', false); - - this.get('activeLights').forEach((light) => { - Em.$.ajax(this.get('apiURL') + '/lights/' + light + '/state', { - data: JSON.stringify({"xy": xy}), - contentType: 'application/json', - type: 'PUT' - }); - }); - - this.set('colorLoopOn', false); - Em.$('.color').css('background', 'rgb(' + rgb[0] + ',' + rgb[1] + ',' + rgb[2] + ')'); - }.observes('rgb'), - - colorRowAction: function() { - if (this.get('trial')) { - return null; - } - - return 'toggleColorpicker'; - }.property('trial'), - - // COLOR LOOP related stuff - colorLoopOn: false, - - onColorLoopOnChange: function(){ - var lightsData = this.get('lightsData'), - activeLights = this.get('activeLights'), - colorLoopsOn = this.get('colorLoopOn'), - effect = colorLoopsOn ? 'colorloop' : 'none'; - - var colorLoopsOnSystem = activeLights.some(function(light) { - return lightsData[light].state.effect === 'colorloop'; - }); - - // if the internal lights state is different than the one from lightsData ( user manually toggled the switch ), send the request to change the bulbs state - if(colorLoopsOn !== colorLoopsOnSystem){ - activeLights.forEach((light)=>{ - if(this.get('lightsData')[light].state.effect !== effect) { - Em.$.ajax(this.get('apiURL') + '/lights/' + light + '/state', { - data: JSON.stringify({'effect': effect }), - contentType: 'application/json', - type: 'PUT' - }); - } - }); - } - }.observes('colorLoopOn'), - - lightsOn: false, - - // determines whether the lights are on/off for the lights switch - lightsOnCHange: function(){ - if(!this.get('strobeOn')){ - var lightsData = this.get('lightsData'), lightsOn = this.get('activeLights').some(function(light) { - return lightsData[light].state.on === true; - }); - - this.set('lightsOn', lightsOn); - } - }.observes('lightsData.@each.state.on', 'activeLights.[]'), - - // determines the average brightness of the hue system for the brightness slider - lightsBrightness: function(){ - var lightsData = this.get('lightsData'), activeLights = this.get('activeLights'), lightsBrightness = 0; - - activeLights.forEach(function(light){ - lightsBrightness += lightsData[light].state.bri; - }); - - return lightsBrightness/activeLights.length; - }.property('lightsData'), - - brightnessControlDisabled: Em.computed.not('lightsOn'), - - onLightsOnChange: function(){ - var lightsData = this.get('lightsData'), activeLights = this.get('activeLights'), lightsOn = this.get('lightsOn'), self = this; - - var lightsOnSystem = activeLights.some(function(light) { - return lightsData[light].state.on === true; - }); - - // if the internal lights state is different than the one from lightsData ( user manually toggled the switch ), send the request to change the bulbs state - if(lightsOn !== lightsOnSystem){ - activeLights.forEach(function (light) { - Em.$.ajax(self.get('apiURL') + '/lights/' + light + '/state', { - data: JSON.stringify({"on": lightsOn}), - contentType: 'application/json', - type: 'PUT' - }); - }); - } - }.observes('lightsOn'), - - onBrightnessChanged: function(){ - var lightsData = this.get('lightsData'), lightsBrightnessSystem = false, lightsBrightness = this.get('lightsBrightness'), activeLights = this.get('activeLights'), self = this; - - activeLights.forEach(function(light){ - lightsBrightnessSystem += lightsData[light].state.bri; - }); - - lightsBrightnessSystem /= activeLights.length; - - // if the internal lights state is different than the one from lightsData ( user manually toggled the switch ), send the request to change the bulbs state - if(lightsBrightness !== lightsBrightnessSystem){ - activeLights.forEach(function(light){ - Em.$.ajax(self.get('apiURL') + '/lights/' + light + '/state', { - data: JSON.stringify({"bri": lightsBrightness}), - contentType: 'application/json', - type: 'PUT' - }); - }); - } - }.observes('lightsBrightness'), - - lightsOnTxt: function(){ - return this.get('lightsOn') ? 'On' : 'Off'; - }.property('lightsOn'), - - colorloopOnTxt: function(){ - return this.get('colorLoopOn') ? 'On' : 'Off'; - }.property('colorLoopOn'), - // **************** STROBE LIGHT START **************** strobeOn: false, @@ -193,12 +201,12 @@ export default Em.Component.extend({ preStrobeOnLightsDataCache: null, lastStrobeLight: 0, - onStrobeOnChange: function () { - var lightsData = this.get('lightsData'), self = this; + onStrobeOnChange: observer('strobeOn', function () { + let lightsData = this.get('lightsData'), self = this; if (this.get('strobeOn')) { this.set('preStrobeOnLightsDataCache', lightsData); - var stobeInitRequestData = {'sat': this.get('strobeSat'), 'transitiontime': 0}; + let stobeInitRequestData = {'sat': this.get('strobeSat'), 'transitiontime': 0}; for (let key in lightsData) { if (lightsData.hasOwnProperty(key)) { @@ -206,7 +214,7 @@ export default Em.Component.extend({ stobeInitRequestData.on = false; } - Em.$.ajax(this.get('apiURL') + '/lights/' + key + '/state', { + $.ajax(this.get('apiURL') + '/lights/' + key + '/state', { data: JSON.stringify(stobeInitRequestData), contentType: 'application/json', type: 'PUT' @@ -216,8 +224,8 @@ export default Em.Component.extend({ this.set('strobeOnInervalHandle', setInterval(this.strobeStep.bind(this), 200)); } else { // revert the light system to pre-strobe - var preStrobeOnLightsDataCache = this.get('preStrobeOnLightsDataCache'), updateLight = function (lightIndx) { - Em.$.ajax(self.get('apiURL') + '/lights/' + lightIndx + '/state', { + let preStrobeOnLightsDataCache = this.get('preStrobeOnLightsDataCache'), updateLight = function (lightIndx) { + $.ajax(self.get('apiURL') + '/lights/' + lightIndx + '/state', { data: JSON.stringify({ 'on': preStrobeOnLightsDataCache[lightIndx].state.on, 'sat': preStrobeOnLightsDataCache[lightIndx].state.sat @@ -236,10 +244,10 @@ export default Em.Component.extend({ setTimeout(()=>{this.onColorLoopOnChange();}, 2000); clearInterval(this.get('strobeOnInervalHandle')); } - }.observes('strobeOn'), + }), strobeStep() { - var lastStrobeLight = (this.get('lastStrobeLight') + 1) % (this.get('activeLights').length + 1), + let lastStrobeLight = (this.get('lastStrobeLight') + 1) % (this.get('activeLights').length + 1), turnOnOptions = {'on': true, 'transitiontime': 0, 'alert': 'select'}; // random light if in cololoop mode @@ -247,12 +255,12 @@ export default Em.Component.extend({ turnOnOptions.hue = Math.floor(Math.random() * 65535); } - Em.$.ajax(this.get('apiURL') + '/lights/' + lastStrobeLight + '/state', { + $.ajax(this.get('apiURL') + '/lights/' + lastStrobeLight + '/state', { data: JSON.stringify(turnOnOptions), contentType: 'application/json', type: 'PUT' }); - Em.$.ajax(this.get('apiURL') + '/lights/' + lastStrobeLight + '/state', { + $.ajax(this.get('apiURL') + '/lights/' + lastStrobeLight + '/state', { data: JSON.stringify({'on': false, 'transitiontime': 0}), contentType: 'application/json', type: 'PUT' @@ -261,18 +269,18 @@ export default Em.Component.extend({ this.set('lastStrobeLight', lastStrobeLight); }, - strobeOnTxt: function () { + strobeOnTxt: computed('strobeOn', function () { return this.get('strobeOn') ? 'On' : 'Off'; - }.property('strobeOn'), + }), - dimmerOnClass: function(){ + dimmerOnClass: computed('dimmerOn', function(){ return this.get('dimmerOn') ? 'dimmerOn' : null; - }.property('dimmerOn'), + }), // **************** STROBE LIGHT FINISH **************** // http://www.developers.meethue.com/documentation/color-conversions-rgb-xy rgbToXy(red, green, blue){ - var X, Y, Z, x, y; + let X, Y, Z, x, y; // normalize red = Number((red/255)); @@ -296,7 +304,7 @@ export default Em.Component.extend({ }, xyToRgb(x, y){ - var r, g, b, X, Y = 1.0, Z; + let r, g, b, X, Y = 1.0, Z; X = (Y / y) * x; Z = (Y / y) * (1 - x - y); diff --git a/app/pods/components/music-tab/component.js b/app/pods/components/music-tab/component.js index a66edd2..521a677 100644 --- a/app/pods/components/music-tab/component.js +++ b/app/pods/components/music-tab/component.js @@ -1,15 +1,439 @@ -import Em from 'ember'; +import Ember from 'ember'; import helperMixin from './mixins/helpers'; import visualizerMixin from './mixins/visualizer'; -export default Em.Component.extend(helperMixin, visualizerMixin, { - onActiveChange: function(){ +const { + Component, + observer, + isEmpty, + isNone, + $ +} = Ember; + +export default Component.extend(helperMixin, visualizerMixin, { + onActiveChange: observer('active', function(){ if(this.get('active')){ - Em.$('#playNotification').removeClass('fadeOut'); - Em.$('#beatSpeakerCenterOuter').removeClass('vibrateOuter'); - Em.$('#beatSpeakerCenterInner').removeClass('vibrateInner'); + $('#playNotification').removeClass('fadeOut'); + $('#beatSpeakerCenterOuter').removeClass('vibrateOuter'); + $('#beatSpeakerCenterInner').removeClass('vibrateInner'); } - }.observes('active'), + }), + + onAmbienceModeChange: observer('ambienceMode', 'playing', function(){ + if(this.get('ambienceMode') && this.get('playing')) { + this.set('ambienceModeHandle', setInterval(()=> {this.doAmbienceLightChange();}, 5000)); + this.setProperties({ + 'colorloopMode': false, + 'flashingTransitions': false + }); + } else if(this.get('ambienceModeHandle')) { + this.get('activeLights').forEach((light)=>{ + $.ajax(this.get('apiURL') + '/lights/' + light + '/state', { + data: JSON.stringify({'on': true}), + contentType: 'application/json', + type: 'PUT' + }); + }); + + clearInterval(this.get('ambienceModeHandle')); + this.set('ambienceModeHandle', null); + } + }), + + updatePageTitle: observer('playQueuePointer', function(){ + let title = 'Huegasm', playQueuePointer = this.get('playQueuePointer'), playQueue = this.get('playQueue'); + + if(playQueuePointer !== -1){ + let song = playQueue[playQueuePointer]; + if(song.title){ + title = song.title; + + if(song.artist){ + title += (' - ' + song.artist); + } + } else { + title = song.fileName; + } + + title += '- Huegasm'; + } + + document.title = title; + }), + + changePlayerControl(name, value, saveBeatPrefs){ + this.set(name, value); + + if(name === 'threshold'){ + this.get('kick').set({threshold: value}); + } + + if(saveBeatPrefs && this.get('usingLocalAudio') && this.get('playQueuePointer') !== -1){ + this.saveSongBeatPreferences(); + } + + this.get('storage').set('huegasm.' + name, value); + }, + + saveSongBeatPreferences() { + let song = this.get('playQueue')[this.get('playQueuePointer')]; + if(song) { + let title = isEmpty(song.artist) ? song.fileName : song.artist + '-' + song.title, + songBeatPreferences = this.get('songBeatPreferences'); + + songBeatPreferences[title] = {threshold: this.get('threshold')}; + + this.set('usingBeatPreferences', true); + this.get('storage').set('huegasm.songBeatPreferences', songBeatPreferences); + } + }, + + loadSongBeatPreferences() { + let song = this.get('playQueue')[this.get('playQueuePointer')], + title = isEmpty(song.artist) ? song.fileName : song.artist + '-' + song.title, + songBeatPreferences = this.get('songBeatPreferences'), + preference = songBeatPreferences[title], + oldBeatPrefCache = this.get('oldBeatPrefCache'), + newOldBeatPrefCache = null; + + if(!isNone(preference)) { // load existing beat prefs + newOldBeatPrefCache = {threshold: this.get('threshold')}; + + this.changePlayerControl('threshold', preference.threshold); + this.set('usingBeatPreferences', true); + } else if(!isNone(oldBeatPrefCache)) { // revert to using beat prefs before the remembered song + this.changePlayerControl('threshold', oldBeatPrefCache.threshold); + this.set('usingBeatPreferences', false); + } + + this.set('oldBeatPrefCache', newOldBeatPrefCache); + }, + + doAmbienceLightChange(justOneLight){ + let activeLights = this.get('activeLights'), + lightsData = this.get('lightsData'), + workedLights = this.get('ambienceWorkedLights'), + hueRange = this.get('hueRange'), + ambienceWorkedLightsHandles = this.get('ambienceWorkedLightsHandles'), + lightOff = (light)=>{ + if(this.get('ambienceMode') && this.get('playing')){ + $.ajax(this.get('apiURL') + '/lights/' + light + '/state', { + data: JSON.stringify({'on': false, 'transitiontime': 20}), + contentType: 'application/json', + type: 'PUT' + }); + } + }, + lights = [], + transitionTime = Math.floor(Math.random()*20), + iterations = justOneLight ? 1 : activeLights.length/2; + + // pick some random lights + for(let i=0; i < iterations; i++){ + let l = activeLights[Math.floor(Math.random()*activeLights.length)]; + + if(!lights.contains(l) && !workedLights.contains(l)){ + lights.push(l); + workedLights.push(l); + } else if(justOneLight && workedLights.length !== activeLights.length){ // work a light if we only need one + while(workedLights.contains(l)){ + l = activeLights[Math.floor(Math.random()*activeLights.length)]; + } + + lights.push(l); + workedLights.push(l); + } + } + + lights.forEach((light)=>{ + let options = {'hue': Math.floor(Math.random()*(hueRange[1] - hueRange[0] + 1)+hueRange[0]), 'bri': Math.floor(Math.random()*200) + 1, 'transitiontime': transitionTime}; + + if(lightsData[light].state.on === false){ + options.on = true; + } + + $.ajax(this.get('apiURL') + '/lights/' + light + '/state', { + data: JSON.stringify(options), + contentType: 'application/json', + type: 'PUT' + }); + + // stop the light from turning off + if(ambienceWorkedLightsHandles[light]){ + clearTimeout(ambienceWorkedLightsHandles[light]); + delete ambienceWorkedLightsHandles[light]; + } + + // turn the light off after it's been idle for a while + ambienceWorkedLightsHandles[light] = setTimeout(()=>{ + lightOff(light); + workedLights.removeObject(light); + delete ambienceWorkedLightsHandles[light]; + }, transitionTime * 100 + 1000); + }); + }, + + startUsingMic() { + navigator.getUserMedia( + {audio: true}, + (stream) => { + this.changePlayerControl('audioMode', 1); + let dancer = this.get('dancer'); + + if(dancer.audio && dancer.audio.pause) { + dancer.pause(); + } + + this.setProperties({ + volumeCache: this.get('volume'), + playing: true, + audioStream: stream + }); + + document.title = 'Listening to Mic - Huegasm'; + + dancer.load(stream, this.get('micBoost'), true); + this.set('usingBeatPreferences', false); + + // much more sensitive beat preference settings are needed for mic mode + this.setProperties({ + oldThreshold: this.get('threshold'), + threshold: 0.1 + }); + + dancer.setVolume(0); + }, + (err) => { + if(err.name === 'DevicesNotFoundError'){ + this.get('notify').alert({html: this.get('notFoundHtml')}); + } + + console.log('Error during navigator.getUserMedia: ' + err.name + ', ' + err.message + ', ' + err.constraintName); + } + ); + }, + + clearCurrentAudio(resetPointer) { + let dancer = this.get('dancer'); + + if(dancer.audio.pause) { + dancer.pause(); + } + + if(resetPointer){ + this.set('playQueuePointer', -1); + } + + this.setProperties({ + timeElapsed: 0, + timeTotal: 0, + playing: false + }); + }, + + dragOver() { + let dragLeaveTimeoutHandle = this.get('dragLeaveTimeoutHandle'); + this.set('dragging', true); + + if (dragLeaveTimeoutHandle) { + clearTimeout(dragLeaveTimeoutHandle); + } + }, + + dragLeave(){ + // need to delay the dragLeave notification to avoid flickering ( hovering over some page elements causes this event to be sent ) + this.set('dragLeaveTimeoutHandle', setTimeout(()=>{ self.set('dragging', false); }, 500)); + }, + + simulateKick(/*mag, ratioKickMag*/) { + let activeLights = this.get('activeLights'), + lightsData = this.get('lightsData'), + color = null, + transitiontime = this.get('flashingTransitions'), + stimulateLight = (light, brightness, hue) => { + let options = {'bri': brightness}; + + if(transitiontime) { + options['transitiontime'] = 0; + } else { + options['transitiontime'] = 1; + } + + if(!isNone(hue)) { + options.hue = hue; + } + + if(lightsData[light].state.on === false){ + options.on = true; + } + + $.ajax(this.get('apiURL') + '/lights/' + light + '/state', { + data: JSON.stringify(options), + contentType: 'application/json', + type: 'PUT' + }); + }, + timeToBriOff = 100; + + if(activeLights.length > 0 && !this.get('ambienceMode')){ + let lastLightBopIndex = this.get('lastLightBopIndex'), + lightBopIndex, + brightnessOnBeat = 254, + light; + + lightBopIndex = Math.floor(Math.random() * activeLights.length); + + // let's try not to select the same light twice in a row + if(activeLights.length > 1) { + while(lightBopIndex === lastLightBopIndex) { + lightBopIndex = Math.floor(Math.random() * activeLights.length); + } + } + + light = activeLights[lightBopIndex]; + this.set('lastLightBopIndex', lightBopIndex); + + if(!this.get('colorloopMode')) { + let hueRange = this.get('hueRange'); + + color = Math.floor(Math.random()*(hueRange[1] - hueRange[0] + 1)+hueRange[0]); + } + + if(transitiontime){ + timeToBriOff = 80; + } + + stimulateLight(light, brightnessOnBeat, color); + setTimeout(stimulateLight, timeToBriOff, light, 1); + } + + this.set('paused', true); + setTimeout(() => { + this.set('paused', false); + }, 150); + + if(this.get('ambienceMode') && activeLights.length > 0){ + this.doAmbienceLightChange(true); + } + + //work the music beat area - simulate the speaker vibration by running a CSS animation on it + $('#beatSpeakerCenterOuter').removeClass('vibrateOuter').prop('offsetWidth', $('#beatSpeakerCenterOuter').prop('offsetWidth')).addClass('vibrateOuter'); + $('#beatSpeakerCenterInner').removeClass('vibrateInner').prop('offsetWidth', $('#beatSpeakerCenterInner').prop('offsetWidth')).addClass('vibrateInner'); + }, + + init() { + this._super(); + + window.requestAnimationFrame = window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || window.msRequestAnimationFrame; + window.cancelAnimationFrame = window.cancelAnimationFrame || window.webkitCancelAnimationFrame || window.mozCancelAnimationFrame || window.msCancelAnimationFrame; + navigator.getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia || navigator.msGetUserMedia; + + let dancer = new Dancer(), + storage = this.get('storage'), + kick = dancer.createKick({ + threshold: this.get('threshold'), + onKick: (mag, ratioKickMag) => { + if (this.get('paused') === false) { + this.simulateKick(mag, ratioKickMag); + } + } + }); + + kick.on(); + + this.setProperties({ + dancer: dancer, + kick: kick + }); + + if(navigator.getUserMedia === undefined){ + this.set('usingMicSupported', false); + } + + ['volume', 'shuffle', 'repeat', 'volumeMuted', 'threshold', 'playerBottomDisplayed', 'audioMode', 'songBeatPreferences', 'firstVisit', 'currentVisName', 'playQueue', 'playQueuePointer', 'micBoost', 'flashingTransitions', 'colorloopMode', 'ambienceMode', 'hueRange'].forEach((item)=>{ + if (!isNone(storage.get('huegasm.' + item))) { + let itemVal = storage.get('huegasm.' + item); + + if(isNone(this.actions[item+'Changed'])){ + this.set(item, itemVal); + } else { + this.send(item + 'Changed', itemVal); + } + } + }); + + SC.initialize({ + client_id: this.get('SC_CLIENT_ID') + }); + }, + + didInsertElement() { + this._super(); + + let self = this; + + // file input code + $('#fileInput').on('change', function () { + let files = this.files; + self.send('handleNewFiles', files); + this.value = null; // reset in case upload the second file again + }); + + $(document).on('click', '.alert', (event)=>{ + $(event.target).addClass('removed'); + }); + + // prevent space/text selection when the user repeatedly clicks on the center + $('#beatContainer').on('mousedown', '#beatSpeakerCenterInner', function(event) { + event.preventDefault(); + }); + + $(document).keypress((event) => { + if(event.which === 32 && event.target.type !== 'text'){ + this.send('play'); + } + }); + + this.$().on('drop', '#playListArea', (event)=>{ + this.send('dropFiles', event.dataTransfer.files); + }); + + // control the volume by scrolling up/down + $('#playerArea').on('mousewheel', (event)=>{ + if(this.get('playQueueNotEmpty') && !this.get('usingMicAudio')) { + let scrollSize = 5; + + if(event.deltaY < 0) { + scrollSize *= -1; + } + let newVolume = this.get('volume') + scrollSize; + + this.send('volumeChanged', newVolume < 0 ? 0 : newVolume); + event.preventDefault(); + } + }); + + // demo tracks + if(this.get('firstVisit')){ + this.send('handleNewSoundCloudURL', 'https://soundcloud.com/mrsuicidesheep/candyland-speechless-feat-rkcb'); + this.send('handleNewSoundCloudURL', 'https://soundcloud.com/mrsuicidesheep/vallis-alps-young-feki-remix'); + this.send('handleNewSoundCloudURL', 'https://soundcloud.com/mrsuicidesheep/andrew-luce-when-to-love-you-feat-chelsea-cutler'); + this.send('handleNewSoundCloudURL', 'https://soundcloud.com/mrsuicidesheep/ahh-ooh-carefree-with-me'); + this.send('handleNewSoundCloudURL', 'https://soundcloud.com/mrsuicidesheep/crywolf-slow-burn'); + this.send('handleNewSoundCloudURL', 'https://soundcloud.com/mrsuicidesheep/clozee-red-forest'); + this.send('handleNewSoundCloudURL', 'https://soundcloud.com/mrsuicidesheep/elo-method-subranger-solace'); + this.send('handleNewSoundCloudURL', 'https://soundcloud.com/mrsuicidesheep/90-pounds-of-pete-waited-too-long-feat-devon-baldwin'); + this.send('handleNewSoundCloudURL', 'https://soundcloud.com/mrsuicidesheep/draper-eyes-open'); + + this.get('storage').set('huegasm.firstVisit', false); + + this.sendAction(); + } + + if(!this.get('playerBottomDisplayed')) { + $('#playerBottom').hide(); + } + }, actions: { clearPlaylist(){ @@ -19,7 +443,7 @@ export default Em.Component.extend(helperMixin, visualizerMixin, { this.set('currentVisName', name); }, hideTooltip(){ - Em.$('.bootstrapTooltip').tooltip('hide'); + $('.bootstrapTooltip').tooltip('hide'); }, gotoSCURL(URL){ // need to pause the music since soundcloud is going to start playing this song anyways @@ -30,37 +454,37 @@ export default Em.Component.extend(helperMixin, visualizerMixin, { this.send('gotoURL', URL); }, gotoURL(URL){ - Em.$('.tooltip').remove(); + $('.tooltip').remove(); window.open(URL, '_blank'); }, handleNewSoundCloudURL(URL){ if(URL) { SC.resolve(URL).then((resultObj)=>{ - var processResult = (result)=>{ - if(result.kind === 'user'){ - this.get('notify').alert({html: this.get('scUserNotSupportedHtml')}); - } else if(result.kind === 'track') { - if(result.streamable === true){ - var picture = null; + let processResult = (result)=>{ + if(result.kind === 'user'){ + this.get('notify').alert({html: this.get('scUserNotSupportedHtml')}); + } else if(result.kind === 'track') { + if(result.streamable === true){ + let picture = null; - if(result.artwork_url){ - picture = result.artwork_url.replace('large', 't67x67'); - } else if(result.user.avatar_url){ - picture = result.user.avatar_url; + if(result.artwork_url){ + picture = result.artwork_url.replace('large', 't67x67'); + } else if(result.user.avatar_url){ + picture = result.user.avatar_url; + } + + this.get('playQueue').pushObject({url: result.stream_url + '?client_id=' + this.get('SC_CLIENT_ID'), fileName: result.title + ' - ' + result.user.username, artist: result.user.username, scUrl: result.permalink_url, title: result.title, artworkUrl: result.artwork_url, picture: picture }); + } else { + failedSongs.push(result.title); + } + } else if(result.kind === 'playlist'){ + if(result.streamable === true){ + result.tracks.forEach(processResult); + } else { + failedSongs.push(result.title); } - - this.get('playQueue').pushObject({url: result.stream_url + '?client_id=' + this.get('SC_CLIENT_ID'), fileName: result.title + ' - ' + result.user.username, artist: result.user.username, scUrl: result.permalink_url, title: result.title, artworkUrl: result.artwork_url, picture: picture }); - } else { - failedSongs.push(result.title); } - } else if(result.kind === 'playlist'){ - if(result.streamable === true){ - result.tracks.forEach(processResult); - } else { - failedSongs.push(result.title); - } - } - }, + }, failedSongs = []; if(resultObj instanceof Array){ @@ -91,11 +515,11 @@ export default Em.Component.extend(helperMixin, visualizerMixin, { this.toggleProperty('isShowingAddSoundCloudModal'); }, useLocalAudio(){ - var audioStream = this.get('audioStream'); + let audioStream = this.get('audioStream'); this.changePlayerControl('audioMode', 0); - if(!Em.isNone(audioStream)){ - var tracks = audioStream.getVideoTracks(); + if(!isNone(audioStream)){ + let tracks = audioStream.getVideoTracks(); if (tracks && tracks[0] && tracks[0].stop) { tracks[0].stop(); } @@ -117,7 +541,7 @@ export default Em.Component.extend(helperMixin, visualizerMixin, { } // restore the old beat preferences ( before the user went into mic mode ) - if(!Em.isNone(this.get('oldThreshold'))){ + if(!isNone(this.get('oldThreshold'))){ this.set('threshold', this.get('oldThreshold')); } @@ -135,14 +559,14 @@ export default Em.Component.extend(helperMixin, visualizerMixin, { this.changePlayerControl('playerBottomDisplayed', !this.get('playerBottomDisplayed')); }, goToSong(index, playSong, scrollToSong){ - var dancer = this.get('dancer'), playQueue = this.get('playQueue'); + let dancer = this.get('dancer'), playQueue = this.get('playQueue'); if(dancer.audio) { this.clearCurrentAudio(true); } - if(!Em.isNone(playQueue[index])) { - var audio = new Audio(); + if(!isNone(playQueue[index])) { + let audio = new Audio(); audio.src = this.get('playQueue')[index].url; audio.crossOrigin = "anonymous"; @@ -151,7 +575,7 @@ export default Em.Component.extend(helperMixin, visualizerMixin, { this.set('soundCloudFuckUps', 0); }; audio.onerror = (event)=>{ - var playQueuePointer =this.get('playQueuePointer'), + let playQueuePointer =this.get('playQueuePointer'), song = this.get('playQueue')[playQueuePointer]; if(this.get('soundCloudFuckUps') >= this.get('maxSoundCloudFuckUps')) { @@ -194,10 +618,10 @@ export default Em.Component.extend(helperMixin, visualizerMixin, { if(scrollToSong){ // this is just a bad workaround to make sure that the track has been rendered to the playlist - Em.run.later(()=>{ - var track = Em.$('.track'+index), playListArea = Em.$('#playListArea'); + run.later(()=>{ + let track = $('.track'+index), playListArea = $('#playListArea'); - if(!Em.isNone(track) && !Em.isNone(track.offset())) { + if(!isNone(track) && !isNone(track.offset())) { playListArea.animate({ scrollTop: track.offset().top - playListArea.offset().top + playListArea.scrollTop() }); @@ -210,21 +634,21 @@ export default Em.Component.extend(helperMixin, visualizerMixin, { this.get('playQueue').removeAt(index); // need to manually remove the tooltip - Em.$('body .tooltip').remove(); + $('body .tooltip').remove(); if(index === this.get('playQueuePointer')) { this.send('goToSong', index, true, true); } }, playerAreaPlay(){ - if(Em.isEmpty(Em.$('#playerControls:hover')) && this.get('playQueuePointer') !== -1 ){ + if(isEmpty($('#playerControls:hover')) && this.get('playQueuePointer') !== -1 ){ this.send('play'); this.set('fadeOutNotification', true); - Em.$('#playNotification').removeClass('fadeOut').prop('offsetWidth', Em.$('#playNotification').prop('offsetWidth')).addClass('fadeOut'); + $('#playNotification').removeClass('fadeOut').prop('offsetWidth', $('#playNotification').prop('offsetWidth')).addClass('fadeOut'); } }, play(replayPause) { - var dancer = this.get('dancer'), + let dancer = this.get('dancer'), playQueuePointer = this.get('playQueuePointer'); if(playQueuePointer !== -1 ) { @@ -235,7 +659,7 @@ export default Em.Component.extend(helperMixin, visualizerMixin, { this.set('timeElapsed', Math.floor(dancer.getTime())); } } else { - var timeTotal = this.get('timeTotal'); + let timeTotal = this.get('timeTotal'); if(this.get('volumeMuted')) { dancer.setVolume(0); @@ -249,7 +673,7 @@ export default Em.Component.extend(helperMixin, visualizerMixin, { return; } - Em.$(window).trigger('resize'); // workaround to redraw the canvas for the vitualizer + $(window).trigger('resize'); // workaround to redraw the canvas for the vitualizer dancer.play(); } @@ -269,7 +693,7 @@ export default Em.Component.extend(helperMixin, visualizerMixin, { } }, next(repeatAll) { - var playQueuePointer = this.get('playQueuePointer'), + let playQueuePointer = this.get('playQueuePointer'), playQueue = this.get('playQueue'), nextSong = (playQueuePointer + 1), repeat = this.get('repeat'), @@ -282,7 +706,7 @@ export default Em.Component.extend(helperMixin, visualizerMixin, { nextSong = playQueuePointer; } } else if(shuffle){ // next shuffle song - var shufflePlayed = this.get('shufflePlayed'); + let shufflePlayed = this.get('shufflePlayed'); // played all the song in shuffle mode if(shufflePlayed.length === playQueue.length){ @@ -312,11 +736,11 @@ export default Em.Component.extend(helperMixin, visualizerMixin, { if(this.get('timeElapsed') > 5) { this.send('seekChanged', 0); } else { - var nextSong = this.get('playQueuePointer'), + let nextSong = this.get('playQueuePointer'), playQueue = this.get('playQueue'); - if(this.get('shuffle') && !Em.isNone(playQueue[nextSong])) { // go to the previously shuffled song - var shufflePlayed = this.get('shufflePlayed'), + if(this.get('shuffle') && !isNone(playQueue[nextSong])) { // go to the previously shuffled song + let shufflePlayed = this.get('shufflePlayed'), shuffledSongIndx = this.get('shufflePlayed').indexOf(playQueue[nextSong].url), i = 0; @@ -349,15 +773,15 @@ export default Em.Component.extend(helperMixin, visualizerMixin, { } }, seekChanged(position) { - var dancer = this.get('dancer'); + let dancer = this.get('dancer'); if(dancer.audio){ dancer.audio.currentTime = Math.floor(this.get('timeTotal') * position / 100); } }, volumeMutedChanged(value) { - var dancer = this.get('dancer'), - volumeMuted = Em.isNone(value) ? !this.get('volumeMuted') : value; + let dancer = this.get('dancer'), + volumeMuted = isNone(value) ? !this.get('volumeMuted') : value; this.changePlayerControl('volumeMuted', volumeMuted); @@ -370,13 +794,13 @@ export default Em.Component.extend(helperMixin, visualizerMixin, { } }, addLocalAudio: function () { - Em.$('#fileInput').click(); + $('#fileInput').click(); }, shuffleChanged(value) { - this.changePlayerControl('shuffle', Em.isNone(value) ? !this.get('shuffle') : value); + this.changePlayerControl('shuffle', isNone(value) ? !this.get('shuffle') : value); }, repeatChanged(value) { - this.changePlayerControl('repeat', Em.isNone(value) ? (this.get('repeat') + 1) % 3 : value); + this.changePlayerControl('repeat', isNone(value) ? (this.get('repeat') + 1) % 3 : value); }, playerBottomDisplayedChanged(value) { this.changePlayerControl('playerBottomDisplayed', value); @@ -424,15 +848,15 @@ export default Em.Component.extend(helperMixin, visualizerMixin, { this.set('draggingOverPlayListArea', false); }, handleNewFiles(files){ - var self = this, + let self = this, playQueue = this.get('playQueue'), - updatePlayQueue = function(){ - var tags = ID3.getAllTags("local"), + updatePlayQueue = ()=>{ + let tags = ID3.getAllTags("local"), picture = null; if(tags.picture){ - var base64String = ""; - for (var i = 0; i < tags.picture.data.length; i++) { + let base64String = ""; + for (let i = 0; i < tags.picture.data.length; i++) { base64String += String.fromCharCode(tags.picture.data[i]); } @@ -448,9 +872,9 @@ export default Em.Component.extend(helperMixin, visualizerMixin, { } }; - for (var key in files) { + for (let key in files) { if (files.hasOwnProperty(key)) { - var file = files[key]; + let file = files[key]; if(file.type.startsWith('audio')) { ID3.loadTags("local", updatePlayQueue.bind(file),{ @@ -461,422 +885,5 @@ export default Em.Component.extend(helperMixin, visualizerMixin, { } } } - }, - - changePlayerControl(name, value, saveBeatPrefs){ - this.set(name, value); - - if(name === 'threshold'){ - this.get('kick').set({threshold: value}); - } - - if(saveBeatPrefs && this.get('usingLocalAudio') && this.get('playQueuePointer') !== -1){ - this.saveSongBeatPreferences(); - } - - this.get('storage').set('huegasm.' + name, value); - }, - - saveSongBeatPreferences() { - var song = this.get('playQueue')[this.get('playQueuePointer')]; - if(song) { - var title = Em.isEmpty(song.artist) ? song.fileName : song.artist + '-' + song.title, - songBeatPreferences = this.get('songBeatPreferences'); - - songBeatPreferences[title] = {threshold: this.get('threshold')}; - - this.set('usingBeatPreferences', true); - this.get('storage').set('huegasm.songBeatPreferences', songBeatPreferences); - } - }, - - loadSongBeatPreferences() { - var song = this.get('playQueue')[this.get('playQueuePointer')], - title = Em.isEmpty(song.artist) ? song.fileName : song.artist + '-' + song.title, - songBeatPreferences = this.get('songBeatPreferences'), - preference = songBeatPreferences[title], - oldBeatPrefCache = this.get('oldBeatPrefCache'), - newOldBeatPrefCache = null; - - if(!Em.isNone(preference)) { // load existing beat prefs - newOldBeatPrefCache = {threshold: this.get('threshold')}; - - this.changePlayerControl('threshold', preference.threshold); - this.set('usingBeatPreferences', true); - } else if(!Em.isNone(oldBeatPrefCache)) { // revert to using beat prefs before the remembered song - this.changePlayerControl('threshold', oldBeatPrefCache.threshold); - this.set('usingBeatPreferences', false); - } - - this.set('oldBeatPrefCache', newOldBeatPrefCache); - }, - - doAmbienceLightChange: function(justOneLight){ - var activeLights = this.get('activeLights'), - lightsData = this.get('lightsData'), - workedLights = this.get('ambienceWorkedLights'), - hueRange = this.get('hueRange'), - ambienceWorkedLightsHandles = this.get('ambienceWorkedLightsHandles'), - lightOff = (light)=>{ - if(this.get('ambienceMode') && this.get('playing')){ - Em.$.ajax(this.get('apiURL') + '/lights/' + light + '/state', { - data: JSON.stringify({'on': false, 'transitiontime': 20}), - contentType: 'application/json', - type: 'PUT' - }); - } - }, - lights = [], - transitionTime = Math.floor(Math.random()*20), - iterations = justOneLight ? 1 : activeLights.length/2; - - // pick some random lights - for(let i=0; i < iterations; i++){ - let l = activeLights[Math.floor(Math.random()*activeLights.length)]; - - if(!lights.contains(l) && !workedLights.contains(l)){ - lights.push(l); - workedLights.push(l); - } else if(justOneLight && workedLights.length !== activeLights.length){ // work a light if we only need one - while(workedLights.contains(l)){ - l = activeLights[Math.floor(Math.random()*activeLights.length)]; - } - - lights.push(l); - workedLights.push(l); - } - } - - lights.forEach((light)=>{ - var options = {'hue': Math.floor(Math.random()*(hueRange[1] - hueRange[0] + 1)+hueRange[0]), 'bri': Math.floor(Math.random()*200) + 1, 'transitiontime': transitionTime}; - - if(lightsData[light].state.on === false){ - options.on = true; - } - - Em.$.ajax(this.get('apiURL') + '/lights/' + light + '/state', { - data: JSON.stringify(options), - contentType: 'application/json', - type: 'PUT' - }); - - // stop the light from turning off - if(ambienceWorkedLightsHandles[light]){ - clearTimeout(ambienceWorkedLightsHandles[light]); - delete ambienceWorkedLightsHandles[light]; - } - - // turn the light off after it's been idle for a while - ambienceWorkedLightsHandles[light] = setTimeout(()=>{ - lightOff(light); - workedLights.removeObject(light); - delete ambienceWorkedLightsHandles[light]; - }, transitionTime * 100 + 1000); - }); - }, - - onAmbienceModeChange: function() { - if(this.get('ambienceMode') && this.get('playing')) { - this.set('ambienceModeHandle', setInterval(()=> {this.doAmbienceLightChange();}, 5000)); - this.setProperties({ - 'colorloopMode': false, - 'flashingTransitions': false - }); - } else if(this.get('ambienceModeHandle')) { - this.get('activeLights').forEach((light)=>{ - Em.$.ajax(this.get('apiURL') + '/lights/' + light + '/state', { - data: JSON.stringify({'on': true}), - contentType: 'application/json', - type: 'PUT' - }); - }); - - clearInterval(this.get('ambienceModeHandle')); - this.set('ambienceModeHandle', null); - } - }.observes('ambienceMode', 'playing'), - - startUsingMic() { - navigator.getUserMedia( - {audio: true}, - (stream) => { - this.changePlayerControl('audioMode', 1); - var dancer = this.get('dancer'); - - if(dancer.audio && dancer.audio.pause) { - dancer.pause(); - } - - this.setProperties({ - volumeCache: this.get('volume'), - playing: true, - audioStream: stream - }); - - document.title = 'Listening to Mic - Huegasm'; - - dancer.load(stream, this.get('micBoost'), true); - this.set('usingBeatPreferences', false); - - // much more sensitive beat preference settings are needed for mic mode - this.setProperties({ - oldThreshold: this.get('threshold'), - threshold: 0.1 - }); - - dancer.setVolume(0); - }, - (err) => { - if(err.name === 'DevicesNotFoundError'){ - this.get('notify').alert({html: this.get('notFoundHtml')}); - } - - console.log('Error during navigator.getUserMedia: ' + err.name + ', ' + err.message + ', ' + err.constraintName); - } - ); - }, - - updatePageTitle: function(){ - var title = 'Huegasm', playQueuePointer = this.get('playQueuePointer'), playQueue = this.get('playQueue'); - - if(playQueuePointer !== -1){ - var song = playQueue[playQueuePointer]; - if(song.title){ - title = song.title; - - if(song.artist){ - title += (' - ' + song.artist); - } - } else { - title = song.fileName; - } - - title += '- Huegasm'; - } - - document.title = title; - }.observes('playQueuePointer'), - - clearCurrentAudio(resetPointer) { - var dancer = this.get('dancer'); - - if(dancer.audio.pause) { - dancer.pause(); - } - - if(resetPointer){ - this.set('playQueuePointer', -1); - } - - this.setProperties({ - timeElapsed: 0, - timeTotal: 0, - playing: false - }); - }, - - dragOver() { - var dragLeaveTimeoutHandle = this.get('dragLeaveTimeoutHandle'); - this.set('dragging', true); - - if (dragLeaveTimeoutHandle) { - clearTimeout(dragLeaveTimeoutHandle); - } - }, - - dragLeave(){ - // need to delay the dragLeave notification to avoid flickering ( hovering over some page elements causes this event to be sent ) - var self = this; - this.set('dragLeaveTimeoutHandle', setTimeout(function(){ self.set('dragging', false); }, 500)); - }, - - simulateKick(/*mag, ratioKickMag*/) { - var activeLights = this.get('activeLights'), - lightsData = this.get('lightsData'), - color = null, - transitiontime = this.get('flashingTransitions'), - stimulateLight = (light, brightness, hue) => { - var options = {'bri': brightness}; - - if(transitiontime) { - options['transitiontime'] = 0; - } else { - options['transitiontime'] = 1; - } - - if(!Em.isNone(hue)) { - options.hue = hue; - } - - if(lightsData[light].state.on === false){ - options.on = true; - } - - Em.$.ajax(this.get('apiURL') + '/lights/' + light + '/state', { - data: JSON.stringify(options), - contentType: 'application/json', - type: 'PUT' - }); - }, - timeToBriOff = 100; - - if(activeLights.length > 0 && !this.get('ambienceMode')){ - var lastLightBopIndex = this.get('lastLightBopIndex'), - lightBopIndex, - brightnessOnBeat = 254, - light; - - lightBopIndex = Math.floor(Math.random() * activeLights.length); - - // let's try not to select the same light twice in a row - if(activeLights.length > 1) { - while(lightBopIndex === lastLightBopIndex) { - lightBopIndex = Math.floor(Math.random() * activeLights.length); - } - } - - light = activeLights[lightBopIndex]; - this.set('lastLightBopIndex', lightBopIndex); - - if(!this.get('colorloopMode')) { - var hueRange = this.get('hueRange'); - - color = Math.floor(Math.random()*(hueRange[1] - hueRange[0] + 1)+hueRange[0]); - } - - if(transitiontime){ - timeToBriOff = 80; - } - - stimulateLight(light, brightnessOnBeat, color); - setTimeout(stimulateLight, timeToBriOff, light, 1); - } - - this.set('paused', true); - setTimeout(() => { - this.set('paused', false); - }, 150); - - if(this.get('ambienceMode') && activeLights.length > 0){ - this.doAmbienceLightChange(true); - } - - //work the music beat area - simulate the speaker vibration by running a CSS animation on it - Em.$('#beatSpeakerCenterOuter').removeClass('vibrateOuter').prop('offsetWidth', Em.$('#beatSpeakerCenterOuter').prop('offsetWidth')).addClass('vibrateOuter'); - Em.$('#beatSpeakerCenterInner').removeClass('vibrateInner').prop('offsetWidth', Em.$('#beatSpeakerCenterInner').prop('offsetWidth')).addClass('vibrateInner'); - }, - - init() { - this._super(); - - window.requestAnimationFrame = window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || window.msRequestAnimationFrame; - window.cancelAnimationFrame = window.cancelAnimationFrame || window.webkitCancelAnimationFrame || window.mozCancelAnimationFrame || window.msCancelAnimationFrame; - navigator.getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia || navigator.msGetUserMedia; - - var dancer = new Dancer(), - storage = this.get('storage'), - kick = dancer.createKick({ - threshold: this.get('threshold'), - onKick: (mag, ratioKickMag) => { - if (this.get('paused') === false) { - this.simulateKick(mag, ratioKickMag); - } - } - }); - - kick.on(); - - this.setProperties({ - dancer: dancer, - kick: kick - }); - - if(navigator.getUserMedia === undefined){ - this.set('usingMicSupported', false); - } - - ['volume', 'shuffle', 'repeat', 'volumeMuted', 'threshold', 'playerBottomDisplayed', 'audioMode', 'songBeatPreferences', 'firstVisit', 'currentVisName', 'playQueue', 'playQueuePointer', 'micBoost', 'flashingTransitions', 'colorloopMode', 'ambienceMode', 'hueRange'].forEach((item)=>{ - if (!Em.isNone(storage.get('huegasm.' + item))) { - var itemVal = storage.get('huegasm.' + item); - - if(Em.isNone(this.actions[item+'Changed'])){ - this.set(item, itemVal); - } else { - this.send(item + 'Changed', itemVal); - } - } - }); - - SC.initialize({ - client_id: this.get('SC_CLIENT_ID') - }); - }, - - didInsertElement() { - this._super(); - - var self = this; - - // file input code - Em.$('#fileInput').on('change', function () { - var files = this.files; - self.send('handleNewFiles', files); - this.value = null; // reset in case upload the second file again - }); - - Em.$(document).on('click', '.alert', (event)=>{ - Em.$(event.target).addClass('removed'); - }); - - // prevent space/text selection when the user repeatedly clicks on the center - Em.$('#beatContainer').on('mousedown', '#beatSpeakerCenterInner', function(event) { - event.preventDefault(); - }); - - Em.$(document).keypress((event) => { - if(event.which === 32 && event.target.type !== 'text'){ - this.send('play'); - } - }); - - this.$().on('drop', '#playListArea', (event)=>{ - this.send('dropFiles', event.dataTransfer.files); - }); - - // control the volume by scrolling up/down - Em.$('#playerArea').on('mousewheel', (event)=>{ - if(this.get('playQueueNotEmpty') && !this.get('usingMicAudio')) { - var scrollSize = 5; - - if(event.deltaY < 0) { - scrollSize *= -1; - } - var newVolume = this.get('volume') + scrollSize; - - this.send('volumeChanged', newVolume < 0 ? 0 : newVolume); - event.preventDefault(); - } - }); - - // demo tracks - if(this.get('firstVisit')){ - this.send('handleNewSoundCloudURL', 'https://soundcloud.com/mrsuicidesheep/candyland-speechless-feat-rkcb'); - this.send('handleNewSoundCloudURL', 'https://soundcloud.com/mrsuicidesheep/vallis-alps-young-feki-remix'); - this.send('handleNewSoundCloudURL', 'https://soundcloud.com/mrsuicidesheep/andrew-luce-when-to-love-you-feat-chelsea-cutler'); - this.send('handleNewSoundCloudURL', 'https://soundcloud.com/mrsuicidesheep/ahh-ooh-carefree-with-me'); - this.send('handleNewSoundCloudURL', 'https://soundcloud.com/mrsuicidesheep/crywolf-slow-burn'); - this.send('handleNewSoundCloudURL', 'https://soundcloud.com/mrsuicidesheep/clozee-red-forest'); - this.send('handleNewSoundCloudURL', 'https://soundcloud.com/mrsuicidesheep/elo-method-subranger-solace'); - this.send('handleNewSoundCloudURL', 'https://soundcloud.com/mrsuicidesheep/90-pounds-of-pete-waited-too-long-feat-devon-baldwin'); - this.send('handleNewSoundCloudURL', 'https://soundcloud.com/mrsuicidesheep/draper-eyes-open'); - - this.get('storage').set('huegasm.firstVisit', false); - - this.sendAction(); - } - - if(!this.get('playerBottomDisplayed')) { - Em.$('#playerBottom').hide(); - } } }); diff --git a/app/pods/components/music-tab/mixins/helpers.js b/app/pods/components/music-tab/mixins/helpers.js index 410d006..771211a 100644 --- a/app/pods/components/music-tab/mixins/helpers.js +++ b/app/pods/components/music-tab/mixins/helpers.js @@ -1,13 +1,24 @@ -import Em from 'ember'; +import Ember from 'ember'; -export default Em.Mixin.create({ +const { + Mixin, + observer, + computed, + isNone, + $, + inject, + on, + A +} = Ember; + +export default Mixin.create({ classNames: ['col-sm-10', 'col-sm-offset-1', 'col-xs-12'], classNameBindings: ['active::hidden'], elementId: 'musicTab', dancer: null, - notify: Em.inject.service('notify'), + notify: inject.service('notify'), beatOptions: { threshold: { @@ -80,16 +91,16 @@ export default Em.Mixin.create({ oldThreshold: null, playQueuePointer: -1, - playQueue: Em.A(), + playQueue: A(), timeElapsed: 0, timeTotal: 0, lastLightBopIndex: 0, - usingMicSupported: false, + // 0 - local, 1 - mic, possibly more to come audioMode: 0, - usingLocalAudio: Em.computed.equal('audioMode', 0), - usingMicAudio: Em.computed.equal('audioMode', 1), + usingLocalAudio: computed.equal('audioMode', 0), + usingMicAudio: computed.equal('audioMode', 1), playerBottomDisplayed: false, dragging: false, @@ -104,52 +115,6 @@ export default Em.Mixin.create({ ambienceMode: false, flashingTransitions: false, - SC_CLIENT_ID: 'aeec0034f58ecd85c2bd1deaecc41594', - notFoundHtml: '', - scUserNotSupportedHtml: '', - tooManySoundCloudFuckUps: '', - notStreamableHtml(fileNames){ - var html = ''; - - return html; - }, - urlNotFoundHtml(url){ - return ''; - }, - failedToPlayFileHtml(fileName){ - return ''; - }, - failedToDecodeFileHtml(fileName){ - return ''; - }, - - scUrl: function(){ - var rtn = null, - currentSong = this.get('playQueue')[this.get('playQueuePointer')]; - - if(currentSong && currentSong.scUrl && !this.get('usingMicAudio')){ - rtn = currentSong.scUrl; - } - - return rtn; - }.property('playQueuePointer', 'playQueue.[]', 'usingMicAudio'), - - playQueueEmpty: Em.computed.empty('playQueue'), - playQueueNotEmpty: Em.computed.notEmpty('playQueue'), - playQueueMultiple: function(){ - return this.get('playQueue').length > 1; - }.property('playQueue.[]'), - - seekPosition: function() { - var timeTotal = this.get('timeTotal'), timeElapsed = this.get('timeElapsed'); - - if (timeTotal === 0) { - return 0; - } - - return timeElapsed/timeTotal*100; - }.property('timeElapsed', 'timeTotal'), - // 0 - no repeat, 1 - repeat all, 2 - repeat one repeat: 0, shuffle: false, @@ -171,15 +136,65 @@ export default Em.Mixin.create({ soundCloudFuckUps: 0, maxSoundCloudFuckUps: 3, - largeArtworkPic: function(){ - var pic = null, + // used to insure that we don't replay the same thing multiple times in shuffle mode + shufflePlayed: [], + + SC_CLIENT_ID: 'aeec0034f58ecd85c2bd1deaecc41594', + notFoundHtml: '', + scUserNotSupportedHtml: '', + tooManySoundCloudFuckUps: '', + notStreamableHtml(fileNames){ + let html = ''; + + return html; + }, + urlNotFoundHtml(url){ + return ''; + }, + failedToPlayFileHtml(fileName){ + return ''; + }, + failedToDecodeFileHtml(fileName){ + return ''; + }, + + scUrl: computed('playQueuePointer', 'playQueue.[]', 'usingMicAudio', function(){ + let rtn = null, + currentSong = this.get('playQueue')[this.get('playQueuePointer')]; + + if(currentSong && currentSong.scUrl && !this.get('usingMicAudio')){ + rtn = currentSong.scUrl; + } + + return rtn; + }), + + playQueueEmpty: computed.empty('playQueue'), + playQueueNotEmpty: computed.notEmpty('playQueue'), + playQueueMultiple: computed('playQueue.[]', function(){ + return this.get('playQueue').length > 1; + }), + + seekPosition: computed('timeElapsed', 'timeTotal', function(){ + let timeTotal = this.get('timeTotal'), + timeElapsed = this.get('timeElapsed'); + + if (timeTotal === 0) { + return 0; + } + + return timeElapsed/timeTotal*100; + }), + + largeArtworkPic: computed('playQueuePointer', 'usingMicAudio', 'currentVisName', function(){ + let pic = null, currentVisName = this.get('currentVisName'), usingMicAudio = this.get('usingMicAudio'), playQueuePointer = this.get('playQueuePointer'), playQueue = this.get('playQueue'); if(playQueuePointer !== -1 && !usingMicAudio && currentVisName === 'None'){ - var song = playQueue[playQueuePointer]; + let song = playQueue[playQueuePointer]; if(song.scUrl){ pic = song.picture.replace('67x67', '500x500'); } else { @@ -188,31 +203,29 @@ export default Em.Mixin.create({ } return pic; - }.property('playQueuePointer', 'usingMicAudio', 'currentVisName'), + }), - // used to insure that we don't replay the same thing multiple times in shuffle mode - shufflePlayed: [], - pauseLightUpdates: function(){ + pauseLightUpdates: computed('playing', function(){ return this.get('playing'); - }.property('playing'), + }), - micIcon: function () { - if (this.get('usingMicAudio')) { + micIcon: computed('usingMicAudio', function(){ + if(this.get('usingMicAudio')) { return 'mic'; } return 'mic-off'; - }.property('usingMicAudio'), + }), - repeatIcon: function () { - if (this.get('repeat') === 2) { + repeatIcon: computed('repeat', function() { + if(this.get('repeat') === 2) { return 'repeat-one'; } return 'repeat'; - }.property('repeat'), + }), - playingIcon: function () { + playingIcon: computed('playing', function() { if(this.get('playing')){ return 'pause'; } else if(this.get('timeElapsed') === this.get('timeTotal') && this.get('timeTotal') !== 0){ @@ -220,10 +233,10 @@ export default Em.Mixin.create({ } else { return 'play-arrow'; } - }.property('playing'), + }), - playListAreaClass: function(){ - var classes = 'cursorPointer'; + playListAreaClass: computed('dragging', 'draggingOverPlayListArea', 'dimmerOn', function(){ + let classes = 'cursorPointer'; if(this.get('dragging')){ classes += ' dragHereHighlight'; @@ -238,40 +251,40 @@ export default Em.Mixin.create({ } return classes; - }.property('dragging', 'draggingOverPlayListArea', 'dimmerOn'), + }), - dimmerOnClass: function(){ + dimmerOnClass: computed('dimmerOn', function(){ return this.get('dimmerOn') ? 'dimmerOn' : null; - }.property('dimmerOn'), + }), - volumeMutedClass: function(){ - var classes = 'playerControllIcon volumeButton'; + volumeMutedClass: computed('volumeMuted', function(){ + let classes = 'playerControllIcon volumeButton'; if(this.get('volumeMuted')){ classes += ' active'; } return classes; - }.property('volumeMuted'), + }), - usingLocalAudioClass: function() { + usingLocalAudioClass: computed('usingLocalAudio', function(){ return this.get('usingLocalAudio') ? 'playerControllIcon active' : 'playerControllIcon'; - }.property('usingLocalAudio'), + }), - usingMicAudioClass: function() { + usingMicAudioClass: computed('usingMicAudio', function(){ return this.get('usingMicAudio') ? 'playerControllIcon active' : 'playerControllIcon'; - }.property('usingMicAudio'), + }), - repeatClass: function () { + repeatClass: computed('repeat', function(){ return this.get('repeat') !== 0 ? 'playerControllIcon active' : 'playerControllIcon'; - }.property('repeat'), + }), - shuffleClass: function () { + shuffleClass: computed('shuffle', function(){ return this.get('shuffle') ? 'playerControllIcon active' : 'playerControllIcon'; - }.property('shuffle'), + }), - volumeIcon: function () { - var volume = this.get('volume'); + volumeIcon: computed('volumeMuted', 'volume', function() { + let volume = this.get('volume'); if (this.get('volumeMuted')) { return "volume-off"; @@ -282,21 +295,37 @@ export default Em.Mixin.create({ } else { return 'volume-mute'; } - }.property('volumeMuted', 'volume'), + }), - onColorloopModeChange: function(){ - var colorLoop = ((this.get('playing') || this.get('usingMicAudio')) && this.get('colorloopMode')) ? true : false; + beatDetectionAreaArrowIcon: computed('playerBottomDisplayed', function(){ + if(!this.get('playerBottomDisplayed')){ + return 'keyboard-arrow-down'; + } else { + return 'keyboard-arrow-up'; + } + }), + + timeElapsedTxt: computed('timeElapsed', function(){ + return this.formatTime(this.get('timeElapsed')); + }), + + timeTotalTxt: computed('timeTotal', function() { + return this.formatTime(this.get('timeTotal')); + }), + + onColorloopModeChange: observer('colorloopMode', 'usingMicAudio', 'playing', function(){ + let colorLoop = ((this.get('playing') || this.get('usingMicAudio')) && this.get('colorloopMode')) ? true : false; this.set('colorLoopOn', colorLoop); - }.observes('colorloopMode', 'usingMicAudio', 'playing'), + }), - onOptionChange: function(self, option){ + onOptionChange: observer('flashingTransitions', 'playQueue.[]', 'playQueuePointer', 'colorloopMode', 'ambienceMode', function(self, option){ option = option.replace('.[]', ''); this.get('storage').set('huegasm.' + option, this.get(option)); - }.observes('flashingTransitions', 'playQueue.[]', 'playQueuePointer', 'colorloopMode', 'ambienceMode'), + }), - onRepeatChange: function () { - var tooltipTxt = 'Repeat all', type = 'repeat'; + onRepeatChange: on('init', observer('repeat', function () { + let tooltipTxt = 'Repeat all', type = 'repeat'; if (this.get(type) === 1) { tooltipTxt = 'Repeat one'; @@ -305,20 +334,20 @@ export default Em.Mixin.create({ } this.changeTooltipText(type, tooltipTxt); - }.observes('repeat').on('init'), + })), - onUsingMicAudioChange: function(){ - var tooltipTxt = 'Listen to audio through mic', type = 'usingMicAudio'; + onUsingMicAudioChange: on('init', observer('usingMicAudio', function(){ + let tooltipTxt = 'Listen to audio through mic', type = 'usingMicAudio'; if (this.get(type)) { tooltipTxt = 'Listen to audio files'; } this.changeTooltipText(type, tooltipTxt); - }.observes('usingMicAudio').on('init'), + })), - onShuffleChange: function () { - var tooltipTxt = 'Shuffle', type = 'shuffle'; + onShuffleChange: on('init', observer('shuffle', function () { + let tooltipTxt = 'Shuffle', type = 'shuffle'; if (this.get(type)) { this.get('shufflePlayed').clear(); @@ -326,10 +355,10 @@ export default Em.Mixin.create({ } this.changeTooltipText(type, tooltipTxt); - }.observes('shuffle').on('init'), + })), - onVolumeMutedChange: function () { - var tooltipTxt = 'Mute', type = 'volumeMuted', + onVolumeMutedChange: on('init', observer('volumeMuted', function() { + let tooltipTxt = 'Mute', type = 'volumeMuted', volumeMuted = this.get(type), dancer = this.get('dancer'), volume=0; @@ -345,11 +374,11 @@ export default Em.Mixin.create({ } this.changeTooltipText(type, tooltipTxt); - }.observes('volumeMuted').on('init'), + })), - onPrevChange: function() { + onPrevChange: observer('timeElapsed', 'playQueueNotEmpty', 'playQueue.[]', function() { if(this.get('playQueueNotEmpty')){ - var tooltipTxt = 'Previous', type = 'prev'; + let tooltipTxt = 'Previous', type = 'prev'; if(this.get('timeElapsed') > 5 || this.get('playQueue').length === 1) { tooltipTxt = 'Replay'; @@ -357,10 +386,10 @@ export default Em.Mixin.create({ this.changeTooltipText(type, tooltipTxt); } - }.observes('timeElapsed', 'playQueueNotEmpty', 'playQueue.[]'), + }), - onPlayingChange: function () { - var tooltipTxt = 'Play', type = 'playing'; + onPlayingChange: on('init', observer('playing', function () { + let tooltipTxt = 'Play', type = 'playing'; if (this.get(type)) { tooltipTxt = 'Pause'; @@ -369,35 +398,19 @@ export default Em.Mixin.create({ } this.changeTooltipText(type, tooltipTxt); - }.observes('playing').on('init'), + })), changeTooltipText(type, text) { // change the tooltip text if it's already visible - Em.$('#' + type + 'Tooltip + .tooltip .tooltip-inner').html(text); + $('#' + type + 'Tooltip + .tooltip .tooltip-inner').html(text); //change the tooltip text for hover - Em.$('#' + type + 'Tooltip').attr('data-original-title', text); + $('#' + type + 'Tooltip').attr('data-original-title', text); - if(Em.isNone(this.get(type + 'TooltipTxt'))) { + if(isNone(this.get(type + 'TooltipTxt'))) { this.set(type + 'TooltipTxt', text); } }, - beatDetectionAreaArrowIcon: function(){ - if(!this.get('playerBottomDisplayed')){ - return 'keyboard-arrow-down'; - } else { - return 'keyboard-arrow-up'; - } - }.property('playerBottomDisplayed'), - - timeElapsedTxt: function(){ - return this.formatTime(this.get('timeElapsed')); - }.property('timeElapsed'), - - timeTotalTxt: function() { - return this.formatTime(this.get('timeTotal')); - }.property('timeTotal'), - formatTime(time){ return this.pad(Math.floor(time/60), 2) + ':' + this.pad(time%60, 2); }, diff --git a/app/pods/components/music-tab/mixins/visualizer.js b/app/pods/components/music-tab/mixins/visualizer.js index 98ac505..f6c5fa4 100644 --- a/app/pods/components/music-tab/mixins/visualizer.js +++ b/app/pods/components/music-tab/mixins/visualizer.js @@ -1,27 +1,33 @@ -import Em from 'ember'; +import Ember from 'ember'; -export default Em.Mixin.create({ +const { + Mixin, + observer, + $ +} = Ember; + +export default Mixin.create({ currentVisName: 'None', visNames: ['None', 'Bars', 'Wave'], - onCurrentVisNameChange: function () { - var currentVisName = this.get('currentVisName'); + onCurrentVisNameChange: observer('currentVisName', function () { + let currentVisName = this.get('currentVisName'); if(currentVisName === 'None'){ - var canvasEl = Em.$('#visualization')[0], + let canvasEl = $('#visualization')[0], ctx = canvasEl.getContext('2d'); ctx.clearRect(0, 0, canvasEl.width, canvasEl.height); } this.get('storage').set('huegasm.currentVisName', currentVisName); - }.observes('currentVisName'), + }), didInsertElement(){ - var dancer = this.get('dancer'), - canvas = Em.$('#visualization')[0], - playerArea = Em.$('#playerArea'), + let dancer = this.get('dancer'), + canvas = $('#visualization')[0], + playerArea = $('#playerArea'), ctx = canvas.getContext('2d'), spacing = 2, h = playerArea.height(), w; @@ -30,17 +36,17 @@ export default Em.Mixin.create({ // must be done to preserver resolution so that things don't appear blurry // note that the height is set to 400px via css so it doesn't need to be recalculated - var syncCanvasHeight = ()=>{ + let syncCanvasHeight = ()=>{ w = playerArea.width(); canvas.width = w; }; syncCanvasHeight(); - Em.$(window).on('resize', syncCanvasHeight); + $(window).on('resize', syncCanvasHeight); dancer.bind('update', () => { - var currentVisName = this.get('currentVisName'), + let currentVisName = this.get('currentVisName'), gradient = ctx.createLinearGradient(0, 0, 0, h), pageHidden = document.hidden || document.msHidden || document.webkitHidden || document.mozHidden; @@ -60,7 +66,7 @@ export default Em.Mixin.create({ ctx.lineWidth = 1; ctx.strokeStyle = gradient; - var waveform = dancer.getWaveform(); + let waveform = dancer.getWaveform(); ctx.beginPath(); ctx.moveTo(0, h / 2); @@ -78,7 +84,7 @@ export default Em.Mixin.create({ gradient.addColorStop(0.2, '#F12B24'); ctx.fillStyle = gradient; - var spectrum = dancer.getSpectrum(); + let spectrum = dancer.getSpectrum(); for (let i = 0, l = spectrum.length; i < l && i < count; i++) { ctx.fillRect(i * ( spacing + width ), h, width, -spectrum[i] * h - 60); } diff --git a/app/styles/app.scss b/app/styles/app.scss index f8a8758..25df08e 100644 --- a/app/styles/app.scss +++ b/app/styles/app.scss @@ -12,7 +12,7 @@ $secondaryThemeColor: #F12B24; $glowingText: 0 0 2px #fff, 0 0 8px #fff, 0 0 20px #228DFF; $dimmerOnButtonColor: #404040; - // BRIDGE FINDER +// BRIDGE FINDER html { min-height: 100%; height: auto;