modernizing part 2

This commit is contained in:
Egor 2016-10-07 00:14:33 -07:00
parent dc152fb2b3
commit 228b1cb417
14 changed files with 1293 additions and 1208 deletions

View file

@ -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.[]')
}
});

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -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.<br><br>' +
'<b>WARNING</b>: 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')
}
});

View file

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

View file

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

View file

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

File diff suppressed because it is too large Load diff

View file

@ -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: '<div class="alert alert-danger" role="alert">A microphone was not found.</div>',
scUserNotSupportedHtml: '<div class="alert alert-danger" role="alert">SoundCloud user URLs are not supported.</div>',
tooManySoundCloudFuckUps: '<div class="alert alert-danger" role="alert">The SoundCloud API is not seving the audio properly. More details <a href="https://www.soundcloudcommunity.com/soundcloud/topics/some-soundcloud-cdn-hosted-tracks-dont-have-access-control-allow-origin-header" target="_blank">HERE</a>.</div>',
notStreamableHtml(fileNames){
var html = '<div class="alert alert-danger" role="alert">The following file(s) could not be added because they are not allowed to be streamed:<br>' + fileNames.toString().replace(/,/g, '<br>') + '</div>';
return html;
},
urlNotFoundHtml(url){
return '<div class="alert alert-danger" role="alert">The URL ( ' + url + ' ) could not be resolved.</div>';
},
failedToPlayFileHtml(fileName){
return '<div class="alert alert-danger" role="alert">Failed to play file ( ' + fileName + ' ).</div>';
},
failedToDecodeFileHtml(fileName){
return '<div class="alert alert-danger" role="alert">Failed to decode file ( ' + fileName + ' ).</div>';
},
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: '<div class="alert alert-danger" role="alert">A microphone was not found.</div>',
scUserNotSupportedHtml: '<div class="alert alert-danger" role="alert">SoundCloud user URLs are not supported.</div>',
tooManySoundCloudFuckUps: '<div class="alert alert-danger" role="alert">The SoundCloud API is not seving the audio properly. More details <a href="https://www.soundcloudcommunity.com/soundcloud/topics/some-soundcloud-cdn-hosted-tracks-dont-have-access-control-allow-origin-header" target="_blank">HERE</a>.</div>',
notStreamableHtml(fileNames){
let html = '<div class="alert alert-danger" role="alert">The following file(s) could not be added because they are not allowed to be streamed:<br>' + fileNames.toString().replace(/,/g, '<br>') + '</div>';
return html;
},
urlNotFoundHtml(url){
return '<div class="alert alert-danger" role="alert">The URL ( ' + url + ' ) could not be resolved.</div>';
},
failedToPlayFileHtml(fileName){
return '<div class="alert alert-danger" role="alert">Failed to play file ( ' + fileName + ' ).</div>';
},
failedToDecodeFileHtml(fileName){
return '<div class="alert alert-danger" role="alert">Failed to decode file ( ' + fileName + ' ).</div>';
},
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);
},

View file

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

View file

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