init huegasm_mobile
4
mobile/.bowerrc
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
{
|
||||
"directory": "bower_components",
|
||||
"analytics": false
|
||||
}
|
||||
20
mobile/.editorconfig
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
# EditorConfig helps developers define and maintain consistent
|
||||
# coding styles between different editors and IDEs
|
||||
# editorconfig.org
|
||||
|
||||
root = true
|
||||
|
||||
|
||||
[*]
|
||||
end_of_line = lf
|
||||
charset = utf-8
|
||||
trim_trailing_whitespace = true
|
||||
insert_final_newline = true
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
|
||||
[*.hbs]
|
||||
insert_final_newline = false
|
||||
|
||||
[*.{diff,md}]
|
||||
trim_trailing_whitespace = false
|
||||
10
mobile/.ember-cli
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
{
|
||||
/**
|
||||
Ember CLI sends analytics information by default. The data is completely
|
||||
anonymous, but there are times when you might want to disable this behavior.
|
||||
|
||||
Setting `disableAnalytics` to true will prevent any data from being sent.
|
||||
*/
|
||||
"disableAnalytics": true,
|
||||
"usePods": true
|
||||
}
|
||||
26
mobile/.gitignore
vendored
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
# See http://help.github.com/ignore-files/ for more about ignoring files.
|
||||
|
||||
# compiled output
|
||||
/dist
|
||||
/tmp
|
||||
|
||||
# dependencies
|
||||
/node_modules
|
||||
/bower_components
|
||||
|
||||
# misc
|
||||
/.sass-cache
|
||||
/connect.lock
|
||||
/coverage/*
|
||||
/libpeerconnection.log
|
||||
npm-debug.log
|
||||
testem.log
|
||||
/.idea/
|
||||
|
||||
ember-cordova/tmp-livereload
|
||||
ember-cordova/cordova/www/*
|
||||
!ember-cordova/cordova/www/.gitkeep
|
||||
ember-cordova/cordova/plugins/*
|
||||
!ember-cordova/cordova/plugins/.gitkeep
|
||||
ember-cordova/cordova/platforms/*
|
||||
!ember-cordova/cordova/platforms/.gitkeep
|
||||
37
mobile/.jshintrc
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
{
|
||||
"predef": [
|
||||
"document",
|
||||
"window",
|
||||
"-Promise",
|
||||
"Dancer",
|
||||
"ID3",
|
||||
"FileAPIReader",
|
||||
"SC",
|
||||
"introJs"
|
||||
],
|
||||
"browser": true,
|
||||
"boss": true,
|
||||
"curly": true,
|
||||
"debug": false,
|
||||
"devel": true,
|
||||
"eqeqeq": true,
|
||||
"evil": true,
|
||||
"forin": false,
|
||||
"immed": false,
|
||||
"laxbreak": false,
|
||||
"newcap": true,
|
||||
"noarg": true,
|
||||
"noempty": false,
|
||||
"nonew": false,
|
||||
"nomen": false,
|
||||
"onevar": false,
|
||||
"plusplus": false,
|
||||
"regexp": false,
|
||||
"undef": true,
|
||||
"sub": true,
|
||||
"strict": false,
|
||||
"white": false,
|
||||
"eqnull": true,
|
||||
"esversion": 6,
|
||||
"unused": true
|
||||
}
|
||||
24
mobile/.travis.yml
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
---
|
||||
language: node_js
|
||||
node_js:
|
||||
- "4"
|
||||
|
||||
sudo: false
|
||||
|
||||
cache:
|
||||
directories:
|
||||
- node_modules
|
||||
|
||||
before_install:
|
||||
- npm config set spin false
|
||||
- npm install -g bower
|
||||
- bower --version
|
||||
- npm install phantomjs-prebuilt
|
||||
- node_modules/phantomjs-prebuilt/bin/phantomjs --version
|
||||
|
||||
install:
|
||||
- npm install
|
||||
- bower install
|
||||
|
||||
script:
|
||||
- npm test
|
||||
3
mobile/.watchmanconfig
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
{
|
||||
"ignore_dirs": ["tmp", "dist"]
|
||||
}
|
||||
44
mobile/README.md
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
# Huegasm
|
||||
|
||||
This README outlines the details of collaborating on this Ember application.
|
||||
Music awesomeness for hue lights.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
You will need the following things properly installed on your computer.
|
||||
|
||||
* [Git](http://git-scm.com/)
|
||||
* [Node.js](http://nodejs.org/) (with NPM)
|
||||
* [Bower](http://bower.io/)
|
||||
* [Ember CLI](http://ember-cli.com/)
|
||||
* [PhantomJS](http://phantomjs.org/)
|
||||
|
||||
## Installation
|
||||
|
||||
* `git clone <repository-url>` this repository
|
||||
* `cd huegasm`
|
||||
* `npm install`
|
||||
* `bower install`
|
||||
|
||||
## Running / Development
|
||||
|
||||
* `ember serve`
|
||||
* Visit your app at [http://localhost:4200](http://localhost:4200).
|
||||
|
||||
### Code Generators
|
||||
|
||||
Make use of the many generators for code, try `ember help generate` for more details
|
||||
|
||||
### Building
|
||||
|
||||
* `ember build` (development)
|
||||
* `ember build --environment production` (production)
|
||||
|
||||
## Further Reading / Useful Links
|
||||
|
||||
* [ember.js](http://emberjs.com/)
|
||||
* [ember-cli](http://ember-cli.com/)
|
||||
* Development Browser Extensions
|
||||
* [ember inspector for chrome](https://chrome.google.com/webstore/detail/ember-inspector/bmdblncegkenkacieihfhpjfppoconhi)
|
||||
* [ember inspector for firefox](https://addons.mozilla.org/en-US/firefox/addon/ember-inspector/)
|
||||
|
||||
BIN
mobile/TODO
18
mobile/app/app.js
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
import Ember from 'ember';
|
||||
import Resolver from './resolver';
|
||||
import loadInitializers from 'ember-load-initializers';
|
||||
import config from './config/environment';
|
||||
|
||||
let App;
|
||||
|
||||
Ember.MODEL_FACTORY_INJECTIONS = true;
|
||||
|
||||
App = Ember.Application.extend({
|
||||
modulePrefix: config.modulePrefix,
|
||||
podModulePrefix: config.podModulePrefix,
|
||||
Resolver
|
||||
});
|
||||
|
||||
loadInitializers(App, config.modulePrefix);
|
||||
|
||||
export default App;
|
||||
67
mobile/app/index.html
Normal file
|
|
@ -0,0 +1,67 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<title>Huegasm</title>
|
||||
<meta name="description" content="Huegasm is a free web application for managing and synchronizing your Philips Hue lights with the beat of your music.">
|
||||
<meta name="keywords" content="huegasm,hue,philips hue,music player">
|
||||
<meta name="author" content="Egor Philippov">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
|
||||
<script type="application/ld+json">
|
||||
{
|
||||
"@context": "http://schema.org",
|
||||
"@type": "WebSite",
|
||||
"name": "Huegasm",
|
||||
"url": "http://www.huegasm.com/"
|
||||
}
|
||||
</script>
|
||||
|
||||
<!--Open graph meta tags -->
|
||||
<meta property="og:title" content="Huegasm" />
|
||||
<meta property="og:type" content="website" />
|
||||
<meta property="og:site_name" content="Huegasm" />
|
||||
<meta property="og:url" content="http://www.huegasm.com/" />
|
||||
<meta property="og:image" content="/mstile-144x144.png" />
|
||||
|
||||
<link type="text/plain" rel="author" href="http://www.huegasm.com/humans.txt" />
|
||||
|
||||
{{content-for 'head'}}
|
||||
|
||||
<link href='//fonts.googleapis.com/css?family=Slabo+27px|Open+Sans' rel='stylesheet' type='text/css'>
|
||||
|
||||
|
||||
<link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png">
|
||||
<link rel="icon" type="image/png" href="/favicon-32x32.png" sizes="32x32">
|
||||
<link rel="icon" type="image/png" href="/favicon-16x16.png" sizes="16x16">
|
||||
<link rel="manifest" href="/manifest.json">
|
||||
<link rel="mask-icon" href="/safari-pinned-tab.svg" color="#5bbad5">
|
||||
<meta name="theme-color" content="#ffffff">
|
||||
|
||||
<link rel="stylesheet" integrity="" href="{{rootURL}}assets/vendor.css">
|
||||
<link rel="stylesheet" integrity="" href="{{rootURL}}assets/huegasm.css">
|
||||
|
||||
{{content-for 'head-footer'}}
|
||||
|
||||
<script src="https://connect.soundcloud.com/sdk/sdk-3.0.0.js"></script>
|
||||
|
||||
<script>
|
||||
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
|
||||
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
|
||||
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
|
||||
})(window,document,'script','//www.google-analytics.com/analytics.js','ga');
|
||||
|
||||
ga('create', 'UA-69470561-1', 'auto');
|
||||
ga('send', 'pageview');
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
{{content-for 'body'}}
|
||||
|
||||
<script src="{{rootURL}}assets/vendor.js"></script>
|
||||
<script src="{{rootURL}}assets/huegasm.js"></script>
|
||||
|
||||
{{content-for 'body-footer'}}
|
||||
</body>
|
||||
</html>
|
||||
152
mobile/app/pods/components/bridge-finder/component.js
Normal file
|
|
@ -0,0 +1,152 @@
|
|||
import Ember from 'ember';
|
||||
|
||||
const {
|
||||
Component,
|
||||
observer,
|
||||
computed,
|
||||
on,
|
||||
isNone,
|
||||
$
|
||||
} = Ember;
|
||||
|
||||
export default Component.extend({
|
||||
elementId: 'bridge-finder',
|
||||
classNames: ['container'],
|
||||
bridgeIp: null,
|
||||
trial: false,
|
||||
bridgeUsername: null,
|
||||
bridgeFindStatus: null,
|
||||
bridgeFindSuccess: computed.equal('bridgeFindStatus', 'success'),
|
||||
bridgeFindMultiple: computed.equal('bridgeFindStatus', 'multiple'),
|
||||
bridgeFindFail: computed.equal('bridgeFindStatus', 'fail'),
|
||||
bridgeUsernamePingMaxTime: 30000, // 30 seconds
|
||||
bridgeUsernamePingIntervalTime: 1500,
|
||||
bridgeUserNamePingIntervalProgress: 0,
|
||||
bridgePingIntervalHandle: null,
|
||||
bridgeAuthenticateReachedStatus: null,
|
||||
manualBridgeIp: null,
|
||||
manualBridgeIpNotFound: false,
|
||||
multipleBridgeIps: [],
|
||||
error: false,
|
||||
isAuthenticating: computed.notEmpty('bridgePingIntervalHandle'),
|
||||
|
||||
// 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() {
|
||||
$(document).keypress((event)=>{
|
||||
if(!isNone(this.get('manualBridgeIp')) && event.which === 13) {
|
||||
this.send('findBridgeByIp');
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
// find the bridge ip here
|
||||
init() {
|
||||
this._super(...arguments);
|
||||
|
||||
if(this.get('bridgeIp') === null) {
|
||||
$.ajax('https://www.meethue.com/api/nupnp', {
|
||||
timeout: 30000
|
||||
})
|
||||
.done((result, status)=> {
|
||||
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) {
|
||||
let multipleBridgeIps = this.get('multipleBridgeIps');
|
||||
|
||||
result.forEach(function (item) {
|
||||
multipleBridgeIps.pushObject(item.internalipaddress);
|
||||
});
|
||||
|
||||
bridgeFindStatus = 'multiple';
|
||||
} else {
|
||||
bridgeFindStatus = 'fail';
|
||||
}
|
||||
|
||||
this.set('bridgeFindStatus', bridgeFindStatus);
|
||||
})
|
||||
.fail(()=>{
|
||||
this.set('bridgeFindStatus', 'fail');
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
pingBridgeUser() {
|
||||
let bridgeIp = this.get('bridgeIp'),
|
||||
bridgeUserNamePingIntervalProgress = this.get('bridgeUserNamePingIntervalProgress'),
|
||||
bridgeUsernamePingMaxTime = this.get('bridgeUsernamePingMaxTime');
|
||||
|
||||
if (bridgeIp !== null && bridgeUserNamePingIntervalProgress < 100) {
|
||||
$.ajax('http://' + bridgeIp + '/api', {
|
||||
data: JSON.stringify({"devicetype": "huegasm"}),
|
||||
contentType: 'application/json',
|
||||
type: 'POST'
|
||||
}).done((result, status)=>{
|
||||
if(!this.isDestroyed){
|
||||
this.set('bridgeAuthenticateReachedStatus', status);
|
||||
|
||||
if (status === 'success' && !result[0].error) {
|
||||
this.clearBridgePingIntervalHandle();
|
||||
this.get('storage').set('huegasm.bridgeUsername', result[0].success.username);
|
||||
this.set('bridgeUsername', result[0].success.username);
|
||||
}
|
||||
}
|
||||
}).fail(()=>{
|
||||
this.clearBridgePingIntervalHandle();
|
||||
this.set('error', true);
|
||||
});
|
||||
|
||||
this.incrementProperty('bridgeUserNamePingIntervalProgress', this.get('bridgeUsernamePingIntervalTime')/bridgeUsernamePingMaxTime*100);
|
||||
} else {
|
||||
this.clearBridgePingIntervalHandle();
|
||||
}
|
||||
},
|
||||
|
||||
clearBridgePingIntervalHandle(){
|
||||
clearInterval(this.get('bridgePingIntervalHandle'));
|
||||
this.set('bridgePingIntervalHandle', null);
|
||||
},
|
||||
|
||||
actions: {
|
||||
retry(){
|
||||
this.onBridgeIpChange();
|
||||
},
|
||||
chooseBridge(bridge){
|
||||
this.set('bridgeIp', bridge);
|
||||
},
|
||||
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);
|
||||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
});
|
||||
57
mobile/app/pods/components/bridge-finder/template.hbs
Normal file
|
|
@ -0,0 +1,57 @@
|
|||
<div class="title"><img src="assets/images/logo.png" alt="Huegasm"></div>
|
||||
{{#unless bridgeUsername}}
|
||||
{{#if bridgeIp}}
|
||||
{{#if error}}
|
||||
<p>Huegasm encountered a critical error while trying to connect to your bridge.<br><br>
|
||||
This likely happened because you're using an outdated browser and/or because your browser does not support <a href="https://en.wikipedia.org/wiki/Cross-origin_resource_sharing" target="_blank" rel="noopener noreferrer">CORS</a>. Feel free to contact me through the link at the bottom of the page if you feel like this is not the case.<br>
|
||||
For the best browsing experience on this site ( and every other one known to man ) please switch to <a href="https://www.google.com/chrome/" target="_blank" rel="noopener noreferrer">Google Chrome</a> or <a href="https://www.mozilla.org/en-US/firefox/new/" target="_blank" rel="noopener noreferrer">Firefox.</a></p>
|
||||
{{else}}
|
||||
<img src="assets/images/pressButtonBridge.png" id="press-bridge-button-img">
|
||||
{{paper-progress-linear warn=true value=bridgeUserNamePingIntervalProgress}}
|
||||
|
||||
{{#if isAuthenticating}}
|
||||
<p>
|
||||
Your bridge IP is <b>{{bridgeIp}}</b>
|
||||
<br>
|
||||
Press the button on your bridge to authenticate this application.
|
||||
</p>
|
||||
{{else}}
|
||||
<p>You failed to press the button in time. <a class="no-text-decoration" href="#" {{action 'retry'}}>RETRY</a></p>
|
||||
{{/if}}
|
||||
{{/if}}
|
||||
{{else}}
|
||||
{{#unless bridgeFindStatus}}
|
||||
{{paper-progress-circular diameter=100}}
|
||||
<p>Trying to find your bridge's IP.</p>
|
||||
{{/unless}}
|
||||
|
||||
{{#if bridgeFindMultiple}}
|
||||
<p>Found multiple hue bridges. <br>
|
||||
Please select the one you want to use for this application.</p>
|
||||
|
||||
<div id="bridge-button-group">
|
||||
{{#each multipleBridgeIps as |bridge|}}
|
||||
{{paper-radio value=bridge label=bridge onChange=(action "chooseBridge")}}
|
||||
{{/each}}
|
||||
</div>
|
||||
{{else}}
|
||||
{{#if bridgeFindFail}}
|
||||
<p>A hue bridge could not be automatically found on your network. <br>
|
||||
Enter one manually? <br><br>
|
||||
( or type <b>offline</b> to look around )
|
||||
</p>
|
||||
|
||||
<span id="bridge-input">
|
||||
{{paper-input label="Hue bridge IP address" value=manualBridgeIp onChange=(action (mut manualBridgeIp))}}
|
||||
{{paper-button onClick=(action "findBridgeByIp") raised=true primary=true label="Find"}}
|
||||
</span>
|
||||
|
||||
{{#if manualBridgeIpNotFound}}
|
||||
<p class="bg-danger">
|
||||
Could not find a bridge with that IP address.
|
||||
</p>
|
||||
{{/if}}
|
||||
{{/if}}
|
||||
{{/if}}
|
||||
{{/if}}
|
||||
{{/unless}}
|
||||
260
mobile/app/pods/components/hue-controls/component.js
Normal file
|
|
@ -0,0 +1,260 @@
|
|||
import Ember from 'ember';
|
||||
import ENV from 'huegasm/config/environment';
|
||||
|
||||
const {
|
||||
A,
|
||||
Component,
|
||||
computed,
|
||||
isEmpty,
|
||||
isNone,
|
||||
run,
|
||||
$
|
||||
} = Ember;
|
||||
|
||||
export default Component.extend({
|
||||
classNames: ['container-fluid'],
|
||||
elementId: 'hue-controls',
|
||||
lightsData: null,
|
||||
activeLights: A(),
|
||||
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 md-menu-origin' : 'md-menu-origin';
|
||||
}),
|
||||
|
||||
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.scheduleOnce('afterRender', function(){
|
||||
$('.bootstrap-tooltip').tooltip();
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
observer.observe($('#hue-controls')[0], {childList: true, subtree: true});
|
||||
},
|
||||
|
||||
init() {
|
||||
this._super(...arguments);
|
||||
|
||||
if(!this.get('trial')) {
|
||||
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'));
|
||||
}
|
||||
},
|
||||
|
||||
updateLightData(){
|
||||
let fail = ()=>{
|
||||
if(!ENV.ignoreFailures) {
|
||||
clearInterval(this.get('lightsDataIntervalHandle'));
|
||||
this.send('clearBridge');
|
||||
}
|
||||
};
|
||||
|
||||
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){
|
||||
let index = this.get('tabList').indexOf(tabName);
|
||||
this.set('selectedTab', index);
|
||||
this.get('storage').set('huegasm.selectedTab', index);
|
||||
},
|
||||
clearBridge() {
|
||||
let storage = this.get('storage');
|
||||
storage.remove('huegasm.bridgeUsername');
|
||||
storage.remove('huegasm.bridgeIp');
|
||||
location.reload();
|
||||
},
|
||||
clearAllSettings() {
|
||||
this.get('storage').clear();
|
||||
location.reload();
|
||||
},
|
||||
startIntro(){
|
||||
let INTRO = introJs,
|
||||
intro = INTRO(),
|
||||
playerBottom = $('#player-bottom'),
|
||||
beatDetectionAreaArrowIcon = $('#beat-detection-area-arrow-icon');
|
||||
|
||||
intro.setOptions({
|
||||
steps: [
|
||||
{
|
||||
intro: 'Welcome! This short tutorial will introduce you to Huegasm.'
|
||||
},
|
||||
{
|
||||
element: '#music-tab',
|
||||
intro: 'This is the music player. You\'ll use this to play music and synchronize it with your active lights.<br><br>' +
|
||||
'<i><b>TIP</b>: Control which lights are active through the <b>Lights</b> tab.</i>'
|
||||
},
|
||||
{
|
||||
element: '#playlist',
|
||||
intro: 'You can add and select music to play from your playlist here. You may listen to local audio files, stream music from soundcloud or stream directly from a connected microphone.<br><br>' +
|
||||
'<i><b>TIP</b>: Songs added through Soundcloud will be saved for when you visit this page again.</i>'
|
||||
},
|
||||
{
|
||||
element: '#player-area',
|
||||
intro: 'The audio playback may be controlled with the controls here. Basic music visualization effects may be shown here by selecting them from the menu ( eyeball icon in the bottom right ).'
|
||||
},
|
||||
{
|
||||
element: '#beat-option-row',
|
||||
intro: 'These are the settings for the music tab:<br>' +
|
||||
'<b>Sensitivity</b> - The sensitivity of the beat detector ( more sensitivity results in more registered beats )<br>' +
|
||||
'<b>Hue Range</b> - The hue range that the lights may change to on beat.<br>' +
|
||||
'<b>Flashing Transitions</b> - Quickly flash the lights on beat<br>' +
|
||||
'<b>Colorloop</b> - Slowly cycle the lights through all the colors while the music is playing<br>' +
|
||||
'<i><b>TIP</b>: Your sensitivity settings are saved per song as indicated by the red star icon in the top left corner. These settings they will be restored if you ever listen to the same song again.</i>',
|
||||
position: 'top'
|
||||
},
|
||||
{
|
||||
element: '#beat-container',
|
||||
intro: 'An interactive speaker that will bump when a beat is registered. <br><br>' +
|
||||
'<i><b>TIP</b>: Click on the center of the speaker to simulate a beat.</i>',
|
||||
position: 'top'
|
||||
},
|
||||
{
|
||||
element: '#lights-tab',
|
||||
intro: 'This is the lights tab. Here you\'ll be able to change various light properties:<br>' +
|
||||
'<b>Power</b> - Turn the selected lights on/off<br>' +
|
||||
'<b>Brightness</b> - The brightness level of the selected lights<br>' +
|
||||
'<b>Color</b> - The color of the selected lights<br>' +
|
||||
'<b>Strobe</b> - Selected lights will flash in sequential order<br>' +
|
||||
'<b>Colorloop</b> - Selected lights will slowly cycle through all the colors<br>'
|
||||
},
|
||||
{
|
||||
element: '#active-lights',
|
||||
intro: 'These icons represent the hue lights in your system. Active lights will be controlled by the application while the inactive lights will have a red X over them and will not be controlled.<br>' +
|
||||
'You may toggle a light\'s state by clicking on it.'
|
||||
},
|
||||
{
|
||||
element: '#settings',
|
||||
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: 'bottom'
|
||||
},
|
||||
{
|
||||
element: '#dimmer',
|
||||
intro: 'And that\'s it...Hope you enjoy the application. ;)<br><br>' +
|
||||
'<i><b>TIP</b>: click on the icon to switch to a darker theme.</i>',
|
||||
position: 'top'
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
// it's VERY ugly but it works... the jQuery massacre :'(
|
||||
intro.onchange((element) => {
|
||||
if(element.id === 'music-tab' || element.id === 'playlist' || element.id === 'player-area' || element.id === 'beat-option-row' || element.id === 'beat-option-button-group' || element.id === 'beat-container' || element.id === 'using-mic-audio-tooltip'){
|
||||
$('#music-tab').removeClass('hidden');
|
||||
$('#lights-tab').addClass('hidden');
|
||||
$('.navigation-item').eq(0).removeClass('active');
|
||||
$('.navigation-item').eq(1).addClass('active');
|
||||
} else {
|
||||
$('#lights-tab').removeClass('hidden');
|
||||
$('#music-tab').addClass('hidden');
|
||||
$('.navigation-item').eq(1).removeClass('active');
|
||||
$('.navigation-item').eq(0).addClass('active');
|
||||
}
|
||||
|
||||
if(element.id === 'music-tab' || element.id === 'playlist' || element.id === 'player-area'){
|
||||
playerBottom.hide();
|
||||
|
||||
if(beatDetectionAreaArrowIcon.hasClass('keyboard-arrow-up')){
|
||||
beatDetectionAreaArrowIcon.removeClass('keyboard-arrow-up').addClass('keyboard-arrow-down');
|
||||
}
|
||||
} else if(element.id === 'beat-option-row' || element.id === 'beat-option-button-group' || element.id === 'beat-container'){
|
||||
playerBottom.show();
|
||||
|
||||
if(beatDetectionAreaArrowIcon.hasClass('keyboard-arrow-down')){
|
||||
beatDetectionAreaArrowIcon.removeClass('keyboard-arrow-down').addClass('keyboard-arrow-up');
|
||||
}
|
||||
} else if(element.id === 'dimmer'){
|
||||
$(document).click();
|
||||
}
|
||||
});
|
||||
|
||||
let onFinish = ()=>{
|
||||
this.set('activeTab', 1);
|
||||
$('#music-tab').removeClass('hidden');
|
||||
$('#lights-tab').addClass('hidden');
|
||||
$('.navigation-item').eq(0).removeClass('active');
|
||||
$('.navigation-item').eq(1).addClass('active');
|
||||
|
||||
if(beatDetectionAreaArrowIcon.hasClass('keyboard-arrow-up')){
|
||||
playerBottom.show();
|
||||
} else {
|
||||
playerBottom.hide();
|
||||
}
|
||||
}, onExit = ()=>{
|
||||
let dimmer = $('#dimmer');
|
||||
|
||||
onFinish();
|
||||
dimmer.popover({
|
||||
trigger: 'manual',
|
||||
placement: 'top',
|
||||
content: 'Click on this icon to toggle the dark theme.'
|
||||
}).popover('show');
|
||||
|
||||
setTimeout(()=>{
|
||||
dimmer.popover('hide');
|
||||
}, 5000);
|
||||
};
|
||||
|
||||
// skip hidden/missing elements
|
||||
intro.onafterchange((element)=>{
|
||||
let elem = $(element);
|
||||
if(elem.html() === '<!---->'){
|
||||
$('.introjs-nextbutton').click();
|
||||
}
|
||||
|
||||
run.later(this, function() {
|
||||
$('.introjs-tooltip').velocity('scroll');
|
||||
}, 500);
|
||||
}).onexit(onExit).oncomplete(onFinish).start();
|
||||
}
|
||||
}
|
||||
});
|
||||
36
mobile/app/pods/components/hue-controls/template.hbs
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
{{#if ready}}
|
||||
<div id="navigation">
|
||||
{{#each tabData as |tab|}}
|
||||
<span class="navigation-item pointer text-uppercase {{if tab.selected "active"}}" {{action "changeTab" tab.name}}>{{tab.name}}</span>
|
||||
{{/each}}
|
||||
<div id="settings">
|
||||
<span data-toggle="dropdown" aria-haspopup="true" aria-expanded="false" id="settings-itemSettings">
|
||||
{{paper-icon "settings" class=dimmerOnClass id="settings-icon" size=28}}
|
||||
</span>
|
||||
|
||||
<ul id="settings-menu" class="dropdown-menu">
|
||||
<li {{action "clearBridge"}}>
|
||||
<a href="#">
|
||||
Switch bridge
|
||||
</a>
|
||||
</li>
|
||||
<li {{action "startIntro"}}>
|
||||
<a href="#">
|
||||
Restart tutorial
|
||||
</a>
|
||||
</li>
|
||||
<li {{action "clearAllSettings"}}>
|
||||
<a href="#">
|
||||
Reset settings
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{light-group lightsData=lightsData activeLights=activeLights syncLight=syncLight apiURL=apiURL dimmerOn=dimmerOn storage=storage}}
|
||||
|
||||
{{lights-tab apiURL=apiURL lightsData=lightsData activeLights=activeLights syncLight=syncLight trial=trial active=lightsTabSelected colorLoopOn=colorLoopOn dimmerOn=dimmerOn}}
|
||||
|
||||
{{music-tab apiURL=apiURL lightsData=lightsData activeLights=activeLights active=musicTabSelected pauseLightUpdates=pauseLightUpdates dimmerOn=dimmerOn storage=storage colorLoopOn=colorLoopOn action="startIntro"}}
|
||||
{{/if}}
|
||||
68
mobile/app/pods/components/huegasm-app/component.js
Normal file
|
|
@ -0,0 +1,68 @@
|
|||
import Ember from 'ember';
|
||||
|
||||
const {
|
||||
Component,
|
||||
computed,
|
||||
isEmpty,
|
||||
isNone,
|
||||
$
|
||||
} = Ember;
|
||||
|
||||
export default Component.extend({
|
||||
bridgeIp: null,
|
||||
bridgeUsername: null,
|
||||
trial: false,
|
||||
storage: null,
|
||||
dimmerOn: false,
|
||||
ready: false,
|
||||
|
||||
year: computed(function(){
|
||||
return new Date().getFullYear();
|
||||
}),
|
||||
|
||||
dimmerOnClass: computed('dimmerOn', function(){
|
||||
let dimmerOn = this.get('dimmerOn'),
|
||||
storage = this.get('storage'),
|
||||
dimmerOnClass = null;
|
||||
|
||||
if (dimmerOn) {
|
||||
$('body').addClass('dimmerOn');
|
||||
$('html').addClass('dimmerOn');
|
||||
dimmerOnClass = 'active';
|
||||
} else {
|
||||
$('body').removeClass('dimmerOn');
|
||||
$('html').removeClass('dimmerOn');
|
||||
}
|
||||
|
||||
storage.set('huegasm.dimmerOn', dimmerOn);
|
||||
|
||||
return dimmerOnClass;
|
||||
}),
|
||||
|
||||
init(){
|
||||
this._super(...arguments);
|
||||
|
||||
let storage = new window.Locally.Store({compress: true});
|
||||
this.set('storage', storage);
|
||||
|
||||
if (!isNone(storage.get('huegasm.dimmerOn'))) {
|
||||
this.set('dimmerOn', storage.get('huegasm.dimmerOn'));
|
||||
}
|
||||
|
||||
if (!isEmpty(storage.get('huegasm.bridgeIp')) && !isEmpty(storage.get('huegasm.bridgeUsername'))) {
|
||||
this.setProperties({
|
||||
bridgeIp: storage.get('huegasm.bridgeIp'),
|
||||
bridgeUsername: storage.get('huegasm.bridgeUsername')
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
actions: {
|
||||
toggleDimmer(){
|
||||
this.toggleProperty('dimmerOn');
|
||||
},
|
||||
isReady(){
|
||||
this.set('ready', true);
|
||||
}
|
||||
}
|
||||
});
|
||||
39
mobile/app/pods/components/huegasm-app/template.hbs
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
{{#if bridgeUsername}}
|
||||
{{hue-controls bridgeIp=bridgeIp bridgeUsername=bridgeUsername trial=trial dimmerOn=dimmerOn storage=storage}}
|
||||
{{else}}
|
||||
{{#if ready}}
|
||||
{{bridge-finder bridgeIp=bridgeIp bridgeUsername=bridgeUsername trial=trial storage=storage}}
|
||||
{{else}}
|
||||
<div class="ready-block">
|
||||
<div class="title">
|
||||
<img src="assets/images/logo.png" alt="Huegasm">
|
||||
</div>
|
||||
|
||||
<p id="intro">
|
||||
Your lights, meet your music. Huegasm.
|
||||
</p>
|
||||
<p id="intro-paragraph">
|
||||
Huegasm is a free web application for managing and synchronizing your <a target="_blank" href="http://www2.meethue.com">Philips Hue lights</a> with the beat of your music.
|
||||
</p>
|
||||
|
||||
<div class="embed-container-wrapper">
|
||||
<div class="embed-container">
|
||||
<iframe width="560" height="315" src="https://www.youtube.com/embed/zi9J6Qg-MPw" frameborder="0" allowfullscreen></iframe>
|
||||
</div>
|
||||
</div>
|
||||
{{paper-button raised=true primary=true onClick=(action "isReady") class="go-button center-block" label="Go!"}}
|
||||
</div>
|
||||
{{/if}}
|
||||
{{/if}}
|
||||
|
||||
<footer id="footer">
|
||||
<div id="dimmer" {{action "toggleDimmer"}} class={{dimmerOnClass}}></div>
|
||||
|
||||
<div id="footer-text">
|
||||
© {{year}}
|
||||
|
||||
<a href="http://www.egorphilippov.me" target="_blank" rel="noopener noreferrer">
|
||||
Egor Philippov
|
||||
</a>
|
||||
</div>
|
||||
</footer>
|
||||
157
mobile/app/pods/components/light-group/component.js
Normal file
|
|
@ -0,0 +1,157 @@
|
|||
import Ember from 'ember';
|
||||
|
||||
const {
|
||||
A,
|
||||
Component,
|
||||
computed,
|
||||
isEmpty,
|
||||
isNone,
|
||||
observer,
|
||||
$
|
||||
} = Ember;
|
||||
|
||||
export default Component.extend({
|
||||
elementId: 'active-lights',
|
||||
classNames: ['light-group', 'horizontal-light-group'],
|
||||
isHovering: false,
|
||||
activeLights: A(),
|
||||
|
||||
// list of all the lights in the hue system
|
||||
lightsList: computed('lightsData', 'activeLights.[]', 'dimmerOn', function(){
|
||||
let lightsData = this.get('lightsData'),
|
||||
activeLights = this.get('activeLights'),
|
||||
dimmerOn = this.get('dimmerOn'),
|
||||
lightsList = A(),
|
||||
type,
|
||||
activeClass;
|
||||
|
||||
for (let key in lightsData) {
|
||||
activeClass = 'light-active';
|
||||
|
||||
if (lightsData.hasOwnProperty(key) && lightsData[key].state.reachable) {
|
||||
switch(lightsData[key].modelid){
|
||||
case 'LCT001':
|
||||
type = 'a19';
|
||||
break;
|
||||
case 'LCT002':
|
||||
type = 'br30';
|
||||
break;
|
||||
case 'LCT003':
|
||||
type = 'gu10';
|
||||
break;
|
||||
case 'LST001':
|
||||
type = 'lightstrip';
|
||||
break;
|
||||
case 'LLC010':
|
||||
type = 'lc_iris';
|
||||
break;
|
||||
case 'LLC011':
|
||||
type = 'lc_bloom';
|
||||
break;
|
||||
case 'LLC012':
|
||||
type = 'lc_bloom';
|
||||
break;
|
||||
case 'LLC006':
|
||||
type = 'lc_iris';
|
||||
break;
|
||||
case 'LLC007':
|
||||
type = 'lc_aura';
|
||||
break;
|
||||
case 'LLC013':
|
||||
type = 'storylight';
|
||||
break;
|
||||
case 'LWB004':
|
||||
type ='a19';
|
||||
break;
|
||||
case 'LLC020':
|
||||
type = 'huego';
|
||||
break;
|
||||
default:
|
||||
type = 'a19';
|
||||
}
|
||||
|
||||
if(dimmerOn){
|
||||
type += 'w';
|
||||
}
|
||||
|
||||
if(!activeLights.includes(key)){
|
||||
activeClass = 'light-inactive';
|
||||
}
|
||||
|
||||
lightsList.push({type: type, name: lightsData[key].name, id: key, data: lightsData[key], activeClass: activeClass});
|
||||
}
|
||||
}
|
||||
|
||||
return lightsList;
|
||||
}),
|
||||
|
||||
onActiveLightsChange: observer('activeLights.[]', function(){
|
||||
this.get('storage').set('huegasm.activeLights', this.get('activeLights'));
|
||||
}),
|
||||
|
||||
init(){
|
||||
this._super(...arguments);
|
||||
|
||||
let lightsData = this.get('lightsData'),
|
||||
activeLights = this.get('activeLights'),
|
||||
activeLightsCache = this.get('storage').get('huegasm.activeLights');
|
||||
|
||||
if(!isNone(activeLightsCache)){
|
||||
activeLightsCache.forEach(function(i){
|
||||
if (lightsData.hasOwnProperty(i) && lightsData[i].state.reachable) {
|
||||
activeLights.pushObject(i);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
for (let key in lightsData) {
|
||||
if (lightsData.hasOwnProperty(key) && lightsData[key].state.reachable) {
|
||||
activeLights.pushObject(key);
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
actions: {
|
||||
clickLight(id){
|
||||
let activeLights = this.get('activeLights'),
|
||||
lightId = activeLights.indexOf(id);
|
||||
|
||||
if(lightId !== -1){
|
||||
activeLights.removeObject(id);
|
||||
} else {
|
||||
activeLights.pushObject(id);
|
||||
this.set('syncLight', id);
|
||||
}
|
||||
},
|
||||
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);
|
||||
}
|
||||
}
|
||||
});
|
||||
5
mobile/app/pods/components/light-group/template.hbs
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
{{#each lightsList as |light|}}
|
||||
<div class="{{light.activeClass}} bootstrap-tooltip light{{light.id}}" data-toggle="tooltip" data-placement="top auto" data-title={{light.name}} {{action "clickLight" light.id}} {{action "lightStartHover" light.id on="mouseEnter"}} {{action "lightStopHover" light.id on="mouseLeave"}}>
|
||||
<img class="hueLight" width="40" src="assets/images/lights/{{light.type}}.svg">
|
||||
</div>
|
||||
{{/each}}
|
||||
|
|
@ -0,0 +1,58 @@
|
|||
import Ember from 'ember';
|
||||
|
||||
const {
|
||||
Component,
|
||||
$
|
||||
} = Ember;
|
||||
|
||||
export default Component.extend({
|
||||
elementId: 'color-picker',
|
||||
rgb: null,
|
||||
canvas: null,
|
||||
canvasContext: null,
|
||||
pressingDown: false,
|
||||
|
||||
mouseUp(){
|
||||
this.set('pressingDown', false);
|
||||
},
|
||||
|
||||
mouseMove(event){
|
||||
if (this.get('pressingDown')) {
|
||||
this.mouseDown(event);
|
||||
}
|
||||
},
|
||||
|
||||
mouseDown(event){
|
||||
let canvasOffset = $(this.get('canvas')).offset(),
|
||||
canvasX = Math.floor(event.pageX - canvasOffset.left),
|
||||
canvasY = Math.floor(event.pageY - canvasOffset.top);
|
||||
|
||||
// get current pixel
|
||||
let imageData = this.get('canvasContext').getImageData(canvasX, canvasY, 1, 1),
|
||||
pixel = imageData.data;
|
||||
|
||||
this.set('pressingDown', true);
|
||||
|
||||
if (!(pixel[0] === 0 && pixel[1] === 0 && pixel[2] === 0)) {
|
||||
this.set('rgb', [pixel[0], pixel[1], pixel[2]]);
|
||||
}
|
||||
},
|
||||
|
||||
// https://dzone.com/articles/creating-your-own-html5
|
||||
didInsertElement(){
|
||||
// handle color changes
|
||||
let canvas = $('#picker')[0],
|
||||
canvasContext = canvas.getContext('2d'),
|
||||
image = new Image();
|
||||
|
||||
image.src = 'assets/images/colormap.png';
|
||||
image.onload = function () {
|
||||
canvasContext.drawImage(image, 0, 0, image.width, image.height); // draw the image on the canvas
|
||||
};
|
||||
|
||||
this.setProperties({
|
||||
canvas: canvas,
|
||||
canvasContext: canvasContext
|
||||
});
|
||||
}
|
||||
});
|
||||
|
|
@ -0,0 +1 @@
|
|||
<canvas id="picker" width="256" height="256"></canvas>
|
||||
364
mobile/app/pods/components/lights-tab/component.js
Normal file
|
|
@ -0,0 +1,364 @@
|
|||
import Ember from 'ember';
|
||||
|
||||
const {
|
||||
Component,
|
||||
observer,
|
||||
computed,
|
||||
on,
|
||||
$
|
||||
} = Ember;
|
||||
|
||||
export default Component.extend({
|
||||
classNames: ['col-sm-8', 'col-sm-offset-2', 'col-xs-12'],
|
||||
classNameBindings: ['active::hidden'],
|
||||
elementId: 'lights-tab',
|
||||
|
||||
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: on('init', 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');
|
||||
|
||||
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((light)=>{
|
||||
$.ajax(this.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');
|
||||
|
||||
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((light)=>{
|
||||
$.ajax(this.get('apiURL') + '/lights/' + light + '/state', {
|
||||
data: JSON.stringify({"bri": lightsBrightness}),
|
||||
contentType: 'application/json',
|
||||
type: 'PUT'
|
||||
});
|
||||
});
|
||||
}
|
||||
}),
|
||||
|
||||
// sync the current light settings to the newly added light
|
||||
onaActiveLightsChange: observer('syncLight', function(){
|
||||
let options = {
|
||||
on: this.get('lightsOn'),
|
||||
bri: this.get('lightsBrightness'),
|
||||
effect: this.get('colorLoopOn') ? 'colorloop' : 'none'
|
||||
}, rgb = this.get('rgb'),
|
||||
syncLight = this.get('syncLight');
|
||||
|
||||
if(rgb[0] !== 255 && rgb[1] !== 255 && rgb[2] !== 255) {
|
||||
options['xy'] = this.rgbToXy(rgb[0], rgb[1], rgb[2]);
|
||||
}
|
||||
|
||||
options['transitiontime'] = 0;
|
||||
|
||||
$.ajax(this.get('apiURL') + '/lights/' + syncLight + '/state', {
|
||||
data: JSON.stringify(options),
|
||||
contentType: 'application/json',
|
||||
type: 'PUT'
|
||||
});
|
||||
}),
|
||||
|
||||
didInsertElement() {
|
||||
$(document).click((event)=>{
|
||||
if(this.get('colorPickerDisplayed') && !event.target.classList.contains('color') && !$(event.target).closest('#color-picker, #color-row').length) {
|
||||
this.toggleProperty('colorPickerDisplayed');
|
||||
}
|
||||
});
|
||||
|
||||
$(document).on('click', '#color-row', () => {
|
||||
this.send('toggleColorPicker');
|
||||
});
|
||||
},
|
||||
|
||||
actions: {
|
||||
toggleColorPicker() {
|
||||
this.toggleProperty('colorPickerDisplayed');
|
||||
}
|
||||
},
|
||||
|
||||
// **************** STROBE LIGHT START ****************
|
||||
strobeOn: false,
|
||||
|
||||
strobeOnInervalHandle: null,
|
||||
strobeSat: 0,
|
||||
preStrobeOnLightsDataCache: null,
|
||||
lastStrobeLight: 0,
|
||||
|
||||
onStrobeOnChange: observer('strobeOn', function () {
|
||||
let lightsData = this.get('lightsData');
|
||||
|
||||
if (this.get('strobeOn')) {
|
||||
this.set('preStrobeOnLightsDataCache', lightsData);
|
||||
let stobeInitRequestData = {'sat': this.get('strobeSat'), 'transitiontime': 0};
|
||||
|
||||
for (let key in lightsData) {
|
||||
if (lightsData.hasOwnProperty(key)) {
|
||||
if (lightsData[key].state.on) {
|
||||
stobeInitRequestData.on = false;
|
||||
}
|
||||
|
||||
$.ajax(this.get('apiURL') + '/lights/' + key + '/state', {
|
||||
data: JSON.stringify(stobeInitRequestData),
|
||||
contentType: 'application/json',
|
||||
type: 'PUT'
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
this.set('strobeOnInervalHandle', setInterval(this.strobeStep.bind(this), 200));
|
||||
} else { // revert the light system to pre-strobe
|
||||
let preStrobeOnLightsDataCache = this.get('preStrobeOnLightsDataCache'), updateLight = (lightIndex)=> {
|
||||
$.ajax(this.get('apiURL') + '/lights/' + lightIndex + '/state', {
|
||||
data: JSON.stringify({
|
||||
'on': preStrobeOnLightsDataCache[lightIndex].state.on,
|
||||
'sat': preStrobeOnLightsDataCache[lightIndex].state.sat
|
||||
}),
|
||||
contentType: 'application/json',
|
||||
type: 'PUT'
|
||||
});
|
||||
};
|
||||
|
||||
for (let key in lightsData) {
|
||||
if (lightsData.hasOwnProperty(key)) {
|
||||
setTimeout(updateLight, 2000, key);
|
||||
}
|
||||
}
|
||||
|
||||
setTimeout(()=>{this.onColorLoopOnChange();}, 2000);
|
||||
clearInterval(this.get('strobeOnInervalHandle'));
|
||||
}
|
||||
}),
|
||||
|
||||
strobeStep() {
|
||||
let lastStrobeLight = (this.get('lastStrobeLight') + 1) % (this.get('activeLights').length + 1),
|
||||
turnOnOptions = {'on': true, 'transitiontime': 0, 'alert': 'select'};
|
||||
|
||||
// random light if in cololoop mode
|
||||
if(this.get('colorLoopOn')) {
|
||||
turnOnOptions.hue = Math.floor(Math.random() * 65535);
|
||||
}
|
||||
|
||||
$.ajax(this.get('apiURL') + '/lights/' + lastStrobeLight + '/state', {
|
||||
data: JSON.stringify(turnOnOptions),
|
||||
contentType: 'application/json',
|
||||
type: 'PUT'
|
||||
});
|
||||
$.ajax(this.get('apiURL') + '/lights/' + lastStrobeLight + '/state', {
|
||||
data: JSON.stringify({'on': false, 'transitiontime': 0}),
|
||||
contentType: 'application/json',
|
||||
type: 'PUT'
|
||||
});
|
||||
|
||||
this.set('lastStrobeLight', lastStrobeLight);
|
||||
},
|
||||
|
||||
strobeOnTxt: computed('strobeOn', function () {
|
||||
return this.get('strobeOn') ? 'On' : 'Off';
|
||||
}),
|
||||
|
||||
dimmerOnClass: computed('dimmerOn', function(){
|
||||
return this.get('dimmerOn') ? 'dimmerOn' : null;
|
||||
}),
|
||||
|
||||
// **************** STROBE LIGHT FINISH ****************
|
||||
// http://www.developers.meethue.com/documentation/color-conversions-rgb-xy
|
||||
rgbToXy(red, green, blue){
|
||||
let X, Y, Z, x, y;
|
||||
|
||||
// normalize
|
||||
red = Number((red/255));
|
||||
green = Number((green/255));
|
||||
blue = Number((blue/255));
|
||||
|
||||
// gamma correction
|
||||
red = (red > 0.04045) ? Math.pow((red + 0.055) / (1.0 + 0.055), 2.4) : (red / 12.92);
|
||||
green = (green > 0.04045) ? Math.pow((green + 0.055) / (1.0 + 0.055), 2.4) : (green / 12.92);
|
||||
blue = (blue > 0.04045) ? Math.pow((blue + 0.055) / (1.0 + 0.055), 2.4) : (blue / 12.92);
|
||||
|
||||
// RGB to XYZ
|
||||
X = red * 0.664511 + green * 0.154324 + blue * 0.162028;
|
||||
Y = red * 0.283881 + green * 0.668433 + blue * 0.047685;
|
||||
Z = red * 0.000088 + green * 0.072310 + blue * 0.986039;
|
||||
|
||||
x = X / (X + Y + Z);
|
||||
y = Y / (X + Y + Z);
|
||||
|
||||
return [x,y];
|
||||
},
|
||||
|
||||
xyToRgb(x, y){
|
||||
let r, g, b, X, Y = 1.0, Z;
|
||||
|
||||
X = (Y / y) * x;
|
||||
Z = (Y / y) * (1 - x - y);
|
||||
|
||||
r = X * 1.656492 - Y * 0.354851 - Z * 0.255038;
|
||||
g = X * -0.707196 + Y * 1.655397 + Z * 0.036152;
|
||||
b = X * 0.051713 - Y * 0.121364 + Z * 1.011530;
|
||||
|
||||
if (r > b && r > g && r > 1.0) {
|
||||
// red is too big
|
||||
g = g / r;
|
||||
b = b / r;
|
||||
r = 1.0;
|
||||
} else if (g > b && g > r && g > 1.0) {
|
||||
// green is too big
|
||||
r = r / g;
|
||||
b = b / g;
|
||||
g = 1.0;
|
||||
} else if (b > r && b > g && b > 1.0) {
|
||||
// blue is too big
|
||||
r = r / b;
|
||||
g = g / b;
|
||||
b = 1.0;
|
||||
}
|
||||
|
||||
r = (r <= 0.0031308) ? 12.92 * r : 1.055 * Math.pow(r, (1.0 / 2.4)) - 0.055;
|
||||
g = (g <= 0.0031308) ? 12.92 * g : 1.055 * Math.pow(g, (1.0 / 2.4)) - 0.055;
|
||||
b = (b <= 0.0031308) ? 12.92 * b : 1.055 * Math.pow(b, (1.0 / 2.4)) - 0.055;
|
||||
|
||||
if (r > b && r > g) {
|
||||
// red is biggest
|
||||
if (r > 1.0) {
|
||||
g = g / r;
|
||||
b = b / r;
|
||||
r = 1.0;
|
||||
}
|
||||
} else if (g > b && g > r) {
|
||||
// green is biggest
|
||||
if (g > 1.0) {
|
||||
r = r / g;
|
||||
b = b / g;
|
||||
g = 1.0;
|
||||
}
|
||||
} else if (b > r && b > g) {
|
||||
// blue is biggest
|
||||
if (b > 1.0) {
|
||||
r = r / b;
|
||||
g = g / b;
|
||||
b = 1.0;
|
||||
}
|
||||
}
|
||||
|
||||
r = r * 255;
|
||||
g = g * 255;
|
||||
b = b * 255;
|
||||
|
||||
return [r, g, b];
|
||||
}
|
||||
});
|
||||
38
mobile/app/pods/components/lights-tab/template.hbs
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
{{#paper-list}}
|
||||
{{#paper-item}}
|
||||
{{paper-icon "power-settings-new" class=dimmerOnClass}}
|
||||
<p data-toggle="tooltip" data-placement="bottom auto" class="bootstrap-tooltip lights-control-tooltip" data-title="Turn the selected lights on/off">Power</p>
|
||||
{{paper-switch value=lightsOn onChange=(action (mut lightsOn)) disabled=trial skipProxy=trial label=lightsOnTxt}}
|
||||
{{/paper-item}}
|
||||
|
||||
{{#paper-item}}
|
||||
{{paper-icon "brightness-4" class=dimmerOnClass}}
|
||||
<p data-toggle="tooltip" data-placement="bottom auto" class="bootstrap-tooltip lights-control-tooltip" data-title="The brightness level of the selected lights">Brightness</p>
|
||||
{{paper-slider flex=true min='1' max='254' value=lightsBrightness disabled=brightnessControlDisabled}}
|
||||
{{/paper-item}}
|
||||
|
||||
{{#paper-item elementId="color-row"}}
|
||||
{{paper-icon "color-lens" class=dimmerOnClass}}
|
||||
<p data-toggle="tooltip" data-placement="bottom auto" class="bootstrap-tooltip lights-control-tooltip" data-title="The color of the selected lights">Color</p>
|
||||
{{/paper-item}}
|
||||
|
||||
<div class="relative">
|
||||
{{paper-button raised=true class="color" onClick=(action "toggleColorPicker") disabled=trial}}
|
||||
|
||||
{{#if colorPickerDisplayed}}
|
||||
{{lights-tab/color-picker lightsData=lightsData activeLights=activeLights rgb=rgb}}
|
||||
{{/if}}
|
||||
</div>
|
||||
|
||||
{{#paper-item}}
|
||||
{{paper-icon "flare" class=dimmerOnClass}}
|
||||
<p data-toggle="tooltip" data-placement="bottom auto" class="bootstrap-tooltip lights-control-tooltip" data-title="Selected lights will flash in sequential order">Strobe</p>
|
||||
{{paper-switch value=strobeOn onChange=(action (mut strobeOn)) disabled=trial skipProxy=trial label=strobeOnTxt}}
|
||||
{{/paper-item}}
|
||||
|
||||
{{#paper-item}}
|
||||
{{paper-icon "color-lens" class=dimmerOnClass}} {{paper-icon "loop" id="loop-addition" class=dimmerOnClass}}
|
||||
<p data-toggle="tooltip" data-placement="bottom auto" class="bootstrap-tooltip lights-control-tooltip" data-title="Selected lights will slowly cycle through all the colors">Colorloop</p>
|
||||
{{paper-switch value=colorLoopOn onChange=(action (mut colorLoopOn)) disabled=trial skipProxy=trial label=colorloopOnTxt}}
|
||||
{{/paper-item}}
|
||||
{{/paper-list}}
|
||||
|
|
@ -0,0 +1,45 @@
|
|||
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');
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
actions: {
|
||||
close () {
|
||||
this.sendAction();
|
||||
},
|
||||
add (){
|
||||
this.sendAction('action', this.get('url'));
|
||||
}
|
||||
}
|
||||
});
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
{{#if isShowingModal}}
|
||||
{{#modal-dialog close="close" alignment="center" translucentOverlay=true attachment="center" targetAttachment="center"}}
|
||||
|
||||
<p>Enter a <a href="https://soundcloud.com" target="_blank" rel="noopener noreferrer">SoundCloud</a> track or playlist/set URL</p>
|
||||
<p>( ex. https://soundcloud.com/mrsuicidesheep/tracks )</p>
|
||||
|
||||
{{paper-input label="SoundCloud URL" icon="search" value=url}}
|
||||
|
||||
{{paper-button onClick=(action "close") label="Close"}}
|
||||
{{paper-button class="pull-right" onClick=(action "add") disabled=saveDisabled primary=true label="Add Music"}}
|
||||
|
||||
{{/modal-dialog}}
|
||||
{{/if}}
|
||||
890
mobile/app/pods/components/music-tab/component.js
Normal file
|
|
@ -0,0 +1,890 @@
|
|||
import Ember from 'ember';
|
||||
import helperMixin from './mixins/helpers';
|
||||
import visualizerMixin from './mixins/visualizer';
|
||||
|
||||
const {
|
||||
Component,
|
||||
observer,
|
||||
isEmpty,
|
||||
isNone,
|
||||
$,
|
||||
run
|
||||
} = Ember;
|
||||
|
||||
export default Component.extend(helperMixin, visualizerMixin, {
|
||||
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.includes(l) && !workedLights.includes(l)){
|
||||
lights.push(l);
|
||||
workedLights.push(l);
|
||||
} else if(justOneLight && workedLights.length !== activeLights.length){ // work a light if we only need one
|
||||
while(workedLights.includes(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(()=>{ this.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
|
||||
$('#beat-speaker-center-outer').velocity({blur: 3}, 100).velocity({blur: 0}, 100);
|
||||
$('#beat-speaker-center-inner').velocity({scale: 1.05}, 100).velocity({scale: 1}, 100);
|
||||
},
|
||||
|
||||
init() {
|
||||
this._super(...arguments);
|
||||
|
||||
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() {
|
||||
let self = this;
|
||||
|
||||
// file input code
|
||||
$('#file-input').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
|
||||
$('#beat-container').on('mousedown', '#beat-speaker-center-inner', function(event) {
|
||||
event.preventDefault();
|
||||
});
|
||||
|
||||
$(document).keypress((event) => {
|
||||
if(event.which === 32 && event.target.type !== 'text'){
|
||||
this.send('play');
|
||||
}
|
||||
});
|
||||
|
||||
this.$().on('drop', '#play-list-area', (event)=>{
|
||||
this.send('dropFiles', event.dataTransfer.files);
|
||||
});
|
||||
|
||||
// control the volume by scrolling up/down
|
||||
$('#player-area').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')) {
|
||||
$('#player-bottom').hide();
|
||||
}
|
||||
},
|
||||
|
||||
actions: {
|
||||
clearPlaylist(){
|
||||
this.get('playQueue').clear();
|
||||
},
|
||||
setVisName(name){
|
||||
this.set('currentVisName', name);
|
||||
},
|
||||
hideTooltip(){
|
||||
$('.bootstrap-tooltip').tooltip('hide');
|
||||
},
|
||||
gotoSCURL(URL){
|
||||
// need to pause the music since soundcloud is going to start playing this song anyways
|
||||
if(this.get('playing')){
|
||||
this.send('play');
|
||||
}
|
||||
|
||||
this.send('gotoURL', URL);
|
||||
},
|
||||
gotoURL(URL){
|
||||
$('.tooltip').remove();
|
||||
window.open(URL, '_blank');
|
||||
},
|
||||
handleNewSoundCloudURL(URL){
|
||||
if(URL) {
|
||||
SC.resolve(URL).then((resultObj)=>{
|
||||
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;
|
||||
}
|
||||
|
||||
$.get(picture)
|
||||
.done(()=>{
|
||||
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, picture: picture });
|
||||
}).fail(()=>{ // no picture
|
||||
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 });
|
||||
});
|
||||
} 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){
|
||||
resultObj.forEach(processResult);
|
||||
} else {
|
||||
processResult(resultObj);
|
||||
}
|
||||
|
||||
if(failedSongs.length > 0) {
|
||||
this.get('notify').alert({html: this.get('notStreamableHtml')(failedSongs)});
|
||||
}
|
||||
|
||||
if(this.get('playQueuePointer') === -1){
|
||||
if(this.get('firstVisit')){
|
||||
this.send('goToSong', 0);
|
||||
} else {
|
||||
this.send('next');
|
||||
}
|
||||
}
|
||||
}, () => {
|
||||
this.get('notify').alert({html: this.get('urlNotFoundHtml')(URL)});
|
||||
});
|
||||
}
|
||||
|
||||
this.set('isShowingAddSoundCloudModal', false);
|
||||
},
|
||||
toggleIsShowingAddSoundCloudModal() {
|
||||
this.toggleProperty('isShowingAddSoundCloudModal');
|
||||
},
|
||||
useLocalAudio(){
|
||||
let audioStream = this.get('audioStream');
|
||||
this.changePlayerControl('audioMode', 0);
|
||||
|
||||
if(!isNone(audioStream)){
|
||||
let tracks = audioStream.getVideoTracks();
|
||||
if (tracks && tracks[0] && tracks[0].stop) {
|
||||
tracks[0].stop();
|
||||
}
|
||||
|
||||
if (audioStream.stop) {
|
||||
// deprecated, may be removed in future
|
||||
audioStream.stop();
|
||||
}
|
||||
|
||||
this.setProperties({
|
||||
audioStream: null,
|
||||
playing: false
|
||||
});
|
||||
}
|
||||
|
||||
if(this.get('playQueuePointer') !== -1) {
|
||||
this.send('goToSong', this.get('playQueuePointer'));
|
||||
this.send('volumeChanged', this.get('volume'));
|
||||
}
|
||||
|
||||
// restore the old beat preferences ( before the user went into mic mode )
|
||||
if(!isNone(this.get('oldThreshold'))){
|
||||
this.set('threshold', this.get('oldThreshold'));
|
||||
}
|
||||
|
||||
document.title = 'Huegasm';
|
||||
},
|
||||
useMicAudio() {
|
||||
if(this.get('usingMicAudio')) {
|
||||
this.send('useLocalAudio');
|
||||
} else {
|
||||
this.startUsingMic();
|
||||
}
|
||||
},
|
||||
slideTogglePlayerBottom(){
|
||||
let elem = this.$('#player-bottom');
|
||||
|
||||
elem.velocity(elem.is(':visible') ? 'slideUp' : 'slideDown', { duration: 300 });
|
||||
this.changePlayerControl('playerBottomDisplayed', !this.get('playerBottomDisplayed'));
|
||||
},
|
||||
goToSong(index, playSong, scrollToSong){
|
||||
let dancer = this.get('dancer'), playQueue = this.get('playQueue');
|
||||
|
||||
if(dancer.audio) {
|
||||
this.clearCurrentAudio(true);
|
||||
}
|
||||
|
||||
if(!isNone(playQueue[index])) {
|
||||
let audio = new Audio();
|
||||
audio.src = this.get('playQueue')[index].url;
|
||||
|
||||
audio.crossOrigin = "anonymous";
|
||||
audio.oncanplay = ()=>{
|
||||
this.set('timeTotal', Math.floor(audio.duration));
|
||||
this.set('soundCloudFuckUps', 0);
|
||||
};
|
||||
audio.onerror = (event)=>{
|
||||
let playQueuePointer =this.get('playQueuePointer'),
|
||||
song = this.get('playQueue')[playQueuePointer];
|
||||
|
||||
if(this.get('soundCloudFuckUps') >= this.get('maxSoundCloudFuckUps')) {
|
||||
this.get('notify').alert({html: this.get('tooManySoundCloudFuckUps')});
|
||||
this.send('play');
|
||||
this.set('soundCloudFuckUps', 0);
|
||||
} else {
|
||||
if(song.local){
|
||||
this.send('removeAudio', playQueuePointer);
|
||||
} else {
|
||||
this.send('next', true);
|
||||
}
|
||||
|
||||
if(event.target.error.code === 2){
|
||||
this.get('notify').alert({html: this.get('failedToDecodeFileHtml')(song.fileName)});
|
||||
} else {
|
||||
this.get('notify').alert({html: this.get('failedToPlayFileHtml')(song.fileName)});
|
||||
}
|
||||
|
||||
this.set('usingBeatPreferences', false);
|
||||
this.incrementProperty('soundCloudFuckUps');
|
||||
}
|
||||
};
|
||||
audio.ontimeupdate = ()=>{
|
||||
this.set('timeElapsed', Math.floor(audio.currentTime));
|
||||
};
|
||||
audio.onended = ()=> {
|
||||
this.send('next');
|
||||
};
|
||||
|
||||
dancer.load(audio, 1);
|
||||
|
||||
this.set('playQueuePointer', index);
|
||||
|
||||
this.loadSongBeatPreferences();
|
||||
|
||||
if(playSong){
|
||||
this.send('play');
|
||||
}
|
||||
|
||||
if(scrollToSong){
|
||||
// this is just a bad workaround to make sure that the track has been rendered to the playlist
|
||||
run.next(this, ()=>{
|
||||
$('.track'+index).velocity('scroll', { container: $('#play-list-area'), duration: 200 });
|
||||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
removeAudio(index){
|
||||
this.get('playQueue').removeAt(index);
|
||||
|
||||
// need to manually remove the tooltip
|
||||
$('body .tooltip').remove();
|
||||
|
||||
if(index === this.get('playQueuePointer')) {
|
||||
this.send('goToSong', index, true, true);
|
||||
}
|
||||
},
|
||||
playerAreaPlay(){
|
||||
if(isEmpty($('#player-controls:hover')) && this.get('playQueuePointer') !== -1 ){
|
||||
this.send('play');
|
||||
|
||||
$('#play-notification').velocity({opacity: 0.8, scale: 1}, 0).velocity({opacity: 0, scale: 3}, 500);
|
||||
}
|
||||
},
|
||||
play(replayPause) {
|
||||
let dancer = this.get('dancer'),
|
||||
playQueuePointer = this.get('playQueuePointer');
|
||||
|
||||
if(playQueuePointer !== -1 ) {
|
||||
if (this.get('playing')) {
|
||||
dancer.pause();
|
||||
|
||||
if(!replayPause){
|
||||
this.set('timeElapsed', Math.floor(dancer.getTime()));
|
||||
}
|
||||
} else {
|
||||
let timeTotal = this.get('timeTotal');
|
||||
|
||||
if(this.get('volumeMuted')) {
|
||||
dancer.setVolume(0);
|
||||
} else {
|
||||
dancer.setVolume(this.get('volume')/100);
|
||||
}
|
||||
|
||||
// replay song
|
||||
if(this.get('timeElapsed') === timeTotal && timeTotal !== 0){
|
||||
this.send('next', true);
|
||||
return;
|
||||
}
|
||||
|
||||
$(window).trigger('resize'); // workaround to redraw the canvas for the vitualizer
|
||||
|
||||
dancer.play();
|
||||
}
|
||||
|
||||
this.onColorloopModeChange();
|
||||
this.toggleProperty('playing');
|
||||
}
|
||||
},
|
||||
volumeChanged(value) {
|
||||
this.changePlayerControl('volume', value);
|
||||
if(this.get('playing')) {
|
||||
this.get('dancer').setVolume(value/100);
|
||||
}
|
||||
|
||||
if(this.get('volume') > 0 && this.get('volumeMuted')){
|
||||
this.changePlayerControl('volumeMuted', false);
|
||||
}
|
||||
},
|
||||
next(repeatAll) {
|
||||
let playQueuePointer = this.get('playQueuePointer'),
|
||||
playQueue = this.get('playQueue'),
|
||||
nextSong = (playQueuePointer + 1),
|
||||
repeat = this.get('repeat'),
|
||||
shuffle = this.get('shuffle');
|
||||
|
||||
if(repeat === 2){ // repeating one song takes precedence over shuffling
|
||||
if(playQueuePointer === -1 && playQueue.length > 0) {
|
||||
nextSong = 0;
|
||||
} else {
|
||||
nextSong = playQueuePointer;
|
||||
}
|
||||
} else if(shuffle){ // next shuffle song
|
||||
let shufflePlayed = this.get('shufflePlayed');
|
||||
|
||||
// played all the song in shuffle mode
|
||||
if(shufflePlayed.length === playQueue.length){
|
||||
shufflePlayed.clear();
|
||||
this.send('play', true);
|
||||
return;
|
||||
}
|
||||
|
||||
// we're going to assume that the song URL is the id
|
||||
do {
|
||||
nextSong = Math.floor(Math.random() * playQueue.length);
|
||||
} while(shufflePlayed.includes(playQueue[nextSong].url));
|
||||
|
||||
shufflePlayed.pushObject(playQueue[nextSong].url);
|
||||
} else if(nextSong > playQueue.length-1){
|
||||
if(repeat === 1 || repeatAll){
|
||||
nextSong = nextSong % playQueue.length;
|
||||
} else {
|
||||
this.send('play', true);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
this.send('goToSong', nextSong, true, true);
|
||||
},
|
||||
previous() {
|
||||
if(this.get('timeElapsed') > 5) {
|
||||
this.send('seekChanged', 0);
|
||||
} else {
|
||||
let nextSong = this.get('playQueuePointer'),
|
||||
playQueue = this.get('playQueue');
|
||||
|
||||
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;
|
||||
|
||||
if(shufflePlayed.length > 0 && shuffledSongIndx !== -1){ // only if there was one
|
||||
nextSong = shuffledSongIndx - 1;
|
||||
|
||||
if(nextSong < 0){
|
||||
nextSong = shufflePlayed.length - 1;
|
||||
}
|
||||
|
||||
playQueue.some(function(item){ // try to find the previous song id
|
||||
if(item.url === shufflePlayed[nextSong]){
|
||||
nextSong = i;
|
||||
return true;
|
||||
}
|
||||
i++;
|
||||
|
||||
return false;
|
||||
});
|
||||
}
|
||||
} else {
|
||||
nextSong--;
|
||||
|
||||
if(nextSong < 0) {
|
||||
nextSong = playQueue.length - 1;
|
||||
}
|
||||
}
|
||||
|
||||
this.send('goToSong', nextSong, true, true);
|
||||
}
|
||||
},
|
||||
seekChanged(position) {
|
||||
let dancer = this.get('dancer');
|
||||
|
||||
if(dancer.audio){
|
||||
dancer.audio.currentTime = Math.floor(this.get('timeTotal') * position / 100);
|
||||
}
|
||||
},
|
||||
volumeMutedChanged(value) {
|
||||
let dancer = this.get('dancer'),
|
||||
volumeMuted = isNone(value) ? !this.get('volumeMuted') : value;
|
||||
|
||||
this.changePlayerControl('volumeMuted', volumeMuted);
|
||||
|
||||
if(this.get('playing')){
|
||||
if(volumeMuted){
|
||||
dancer.setVolume(0);
|
||||
} else {
|
||||
dancer.setVolume(this.get('volume')/100);
|
||||
}
|
||||
}
|
||||
},
|
||||
addLocalAudio: function () {
|
||||
$('#file-input').click();
|
||||
},
|
||||
shuffleChanged(value) {
|
||||
this.changePlayerControl('shuffle', isNone(value) ? !this.get('shuffle') : value);
|
||||
},
|
||||
repeatChanged(value) {
|
||||
this.changePlayerControl('repeat', isNone(value) ? (this.get('repeat') + 1) % 3 : value);
|
||||
},
|
||||
playerBottomDisplayedChanged(value) {
|
||||
this.changePlayerControl('playerBottomDisplayed', value);
|
||||
},
|
||||
thresholdChanged(value) {
|
||||
this.changePlayerControl('threshold', value, true);
|
||||
},
|
||||
hueRangeChanged(value) {
|
||||
this.changePlayerControl('hueRange', value);
|
||||
},
|
||||
micBoostChanged(value) {
|
||||
this.set('micBoost', value);
|
||||
this.get('storage').set('huegasm.micBoost', value);
|
||||
|
||||
if(this.get('usingMicAudio')) {
|
||||
this.get('dancer').setBoost(value);
|
||||
}
|
||||
},
|
||||
audioModeChanged(value){
|
||||
if(value === 1) {
|
||||
this.startUsingMic();
|
||||
} else if(value === 0) {
|
||||
this.send('useLocalAudio');
|
||||
} else {
|
||||
this.set('audioMode', value);
|
||||
}
|
||||
},
|
||||
playQueuePointerChanged(value){
|
||||
this.send('goToSong', value, false, true);
|
||||
},
|
||||
clickSpeaker(){
|
||||
this.simulateKick(1);
|
||||
},
|
||||
dropFiles(files){
|
||||
this.setProperties({
|
||||
dragging: false,
|
||||
draggingOverPlayListArea: false
|
||||
});
|
||||
this.send('handleNewFiles', files);
|
||||
},
|
||||
playerListAreaDragOver(){
|
||||
this.set('draggingOverPlayListArea', true);
|
||||
},
|
||||
playerListAreaDragLeave(){
|
||||
this.set('draggingOverPlayListArea', false);
|
||||
},
|
||||
handleNewFiles(files){
|
||||
let self = this,
|
||||
playQueue = this.get('playQueue'),
|
||||
updatePlayQueue = function(){
|
||||
let tags = ID3.getAllTags("local"),
|
||||
picture = null;
|
||||
|
||||
if(tags.picture){
|
||||
let base64String = "";
|
||||
for (let i = 0; i < tags.picture.data.length; i++) {
|
||||
base64String += String.fromCharCode(tags.picture.data[i]);
|
||||
}
|
||||
|
||||
picture = "data:" + tags.picture.format + ";base64," + window.btoa(base64String);
|
||||
}
|
||||
|
||||
playQueue.pushObject({
|
||||
fileName: this.name.replace(/\.[^/.]+$/, ""),
|
||||
url: URL.createObjectURL(this),
|
||||
artist: tags.artist,
|
||||
title: tags.title,
|
||||
picture: picture,
|
||||
local: true
|
||||
});
|
||||
|
||||
ID3.clearAll();
|
||||
|
||||
if(self.get('playQueuePointer') === -1){
|
||||
self.send('next');
|
||||
}
|
||||
};
|
||||
|
||||
for (let key in files) {
|
||||
if (files.hasOwnProperty(key)) {
|
||||
let file = files[key];
|
||||
|
||||
if(file.type.startsWith('audio')) {
|
||||
ID3.loadTags("local", updatePlayQueue.bind(file),{
|
||||
dataReader: new FileAPIReader(file),
|
||||
tags: ['title', 'artist', 'album', 'track', 'picture']
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
422
mobile/app/pods/components/music-tab/mixins/helpers.js
Normal file
|
|
@ -0,0 +1,422 @@
|
|||
import Ember from 'ember';
|
||||
|
||||
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: 'music-tab',
|
||||
|
||||
dancer: null,
|
||||
|
||||
notify: inject.service('notify'),
|
||||
|
||||
beatOptions: {
|
||||
threshold: {
|
||||
range: {min: 0, max: 0.5},
|
||||
step: 0.01,
|
||||
defaultValue: 0.3,
|
||||
pips: {
|
||||
mode: 'values',
|
||||
values: [0, 0.25, 0.5],
|
||||
density: 10,
|
||||
format: {
|
||||
to: function ( value ) {
|
||||
if(value === 0) {
|
||||
value = 'More';
|
||||
} else if(value === 0.25) {
|
||||
value = '';
|
||||
} else {
|
||||
value = 'Less';
|
||||
}
|
||||
|
||||
return value;
|
||||
},
|
||||
from: function ( value ) { return value; }
|
||||
}
|
||||
}
|
||||
},
|
||||
hueRange: {
|
||||
range: {min: 0, max: 65535},
|
||||
step: 1,
|
||||
defaultValue: 0.3,
|
||||
pips: {
|
||||
mode: 'values',
|
||||
values: [0, 25500, 46920, 65535],
|
||||
density: 10,
|
||||
format: {
|
||||
to: function ( value ) {
|
||||
if(value === 0 || value === 65535) {
|
||||
value = 'Red';
|
||||
} else if(value === 25500 ) {
|
||||
value = 'Green';
|
||||
} else {
|
||||
value = 'Blue';
|
||||
}
|
||||
|
||||
return value;
|
||||
},
|
||||
from: function ( value ) { return value; }
|
||||
}
|
||||
}
|
||||
},
|
||||
micBoost: {
|
||||
range: {min: 1, max: 11},
|
||||
step: 0.5,
|
||||
defaultValue: 5,
|
||||
pips: {
|
||||
mode: 'positions',
|
||||
values: [0,20,40,60,80,100],
|
||||
density: 10,
|
||||
format: {
|
||||
to: function ( value ) {return 'x'+value;},
|
||||
from: function ( value ) { return value; }
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
threshold: 0.3,
|
||||
hueRange: [0, 65535],
|
||||
micBoost: 5,
|
||||
oldThreshold: null,
|
||||
|
||||
playQueuePointer: -1,
|
||||
playQueue: A(),
|
||||
timeElapsed: 0,
|
||||
timeTotal: 0,
|
||||
lastLightBopIndex: 0,
|
||||
usingMicSupported: false,
|
||||
|
||||
// 0 - local, 1 - mic, possibly more to come
|
||||
audioMode: 0,
|
||||
usingLocalAudio: computed.equal('audioMode', 0),
|
||||
usingMicAudio: computed.equal('audioMode', 1),
|
||||
|
||||
playerBottomDisplayed: false,
|
||||
dragging: false,
|
||||
draggingOverPlayListArea: false,
|
||||
dragLeaveTimeoutHandle: null,
|
||||
ambienceModeHandle: null,
|
||||
audioStream: null,
|
||||
dimmerOn: false,
|
||||
isShowingAddSoundCloudModal: false,
|
||||
|
||||
colorloopMode: false,
|
||||
ambienceMode: false,
|
||||
flashingTransitions: false,
|
||||
|
||||
// 0 - no repeat, 1 - repeat all, 2 - repeat one
|
||||
repeat: 0,
|
||||
shuffle: false,
|
||||
volumeMuted: false,
|
||||
volume: 100,
|
||||
// beat detection related pausing
|
||||
paused: false,
|
||||
// audio: playing or paused
|
||||
playing: false,
|
||||
songBeatPreferences: {},
|
||||
usingBeatPreferences: false,
|
||||
oldBeatPrefCache: null,
|
||||
storage: null,
|
||||
firstVisit: true,
|
||||
ambienceWorkedLights: [],
|
||||
ambienceWorkedLightsHandles: {},
|
||||
|
||||
soundCloudFuckUps: 0,
|
||||
maxSoundCloudFuckUps: 3,
|
||||
|
||||
// used to insure that we don't replay the same thing multiple times in shuffle mode
|
||||
shufflePlayed: [],
|
||||
|
||||
// noUiSlider connection specification
|
||||
filledConnect: [true, false],
|
||||
hueRangeConnect: [false, true, 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" rel="noopener noreferrer">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'){
|
||||
let song = playQueue[playQueuePointer];
|
||||
if(song.scUrl && !isNone(song.picture)){
|
||||
pic = song.picture.replace('67x67', '500x500');
|
||||
} else {
|
||||
pic = song.picture;
|
||||
}
|
||||
}
|
||||
|
||||
return pic;
|
||||
}),
|
||||
|
||||
pauseLightUpdates: computed('playing', function(){
|
||||
return this.get('playing');
|
||||
}),
|
||||
|
||||
micIcon: computed('usingMicAudio', function(){
|
||||
if(this.get('usingMicAudio')) {
|
||||
return 'mic';
|
||||
}
|
||||
|
||||
return 'mic-off';
|
||||
}),
|
||||
|
||||
repeatIcon: computed('repeat', function() {
|
||||
if(this.get('repeat') === 2) {
|
||||
return 'repeat-one';
|
||||
}
|
||||
|
||||
return 'repeat';
|
||||
}),
|
||||
|
||||
playingIcon: computed('playing', function() {
|
||||
if(this.get('playing')){
|
||||
return 'pause';
|
||||
} else if(this.get('timeElapsed') === this.get('timeTotal') && this.get('timeTotal') !== 0){
|
||||
return 'replay';
|
||||
} else {
|
||||
return 'play-arrow';
|
||||
}
|
||||
}),
|
||||
|
||||
playListAreaClass: computed('dragging', 'draggingOverPlayListArea', 'dimmerOn', function(){
|
||||
let classes = 'pointer';
|
||||
|
||||
if(this.get('dragging')){
|
||||
classes += ' drag-here-highlight';
|
||||
}
|
||||
|
||||
if(this.get('draggingOverPlayListArea')){
|
||||
classes += ' dragging-over';
|
||||
}
|
||||
|
||||
if(this.get('dimmerOn')){
|
||||
classes += ' dimmerOn';
|
||||
}
|
||||
|
||||
return classes;
|
||||
}),
|
||||
|
||||
dimmerOnClass: computed('dimmerOn', function(){
|
||||
return this.get('dimmerOn') ? 'dimmerOn' : null;
|
||||
}),
|
||||
|
||||
volumeMutedClass: computed('volumeMuted', function(){
|
||||
let classes = 'player-control-icon volumeButton';
|
||||
|
||||
if(this.get('volumeMuted')){
|
||||
classes += ' active';
|
||||
}
|
||||
|
||||
return classes;
|
||||
}),
|
||||
|
||||
usingLocalAudioClass: computed('usingLocalAudio', function(){
|
||||
return this.get('usingLocalAudio') ? 'player-control-icon active' : 'player-control-icon';
|
||||
}),
|
||||
|
||||
usingMicAudioClass: computed('usingMicAudio', function(){
|
||||
return this.get('usingMicAudio') ? 'player-control-icon active' : 'player-control-icon';
|
||||
}),
|
||||
|
||||
repeatClass: computed('repeat', function(){
|
||||
return this.get('repeat') !== 0 ? 'player-control-icon active' : 'player-control-icon';
|
||||
}),
|
||||
|
||||
shuffleClass: computed('shuffle', function(){
|
||||
return this.get('shuffle') ? 'player-control-icon active' : 'player-control-icon';
|
||||
}),
|
||||
|
||||
volumeIcon: computed('volumeMuted', 'volume', function() {
|
||||
let volume = this.get('volume');
|
||||
|
||||
if (this.get('volumeMuted')) {
|
||||
return "volume-off";
|
||||
} else if (volume >= 70) {
|
||||
return "volume-up";
|
||||
} else if (volume > 10) {
|
||||
return "volume-down";
|
||||
} else {
|
||||
return 'volume-mute';
|
||||
}
|
||||
}),
|
||||
|
||||
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);
|
||||
}),
|
||||
|
||||
onOptionChange: observer('flashingTransitions', 'playQueue.[]', 'playQueuePointer', 'colorloopMode', 'ambienceMode', function(self, option){
|
||||
option = option.replace('.[]', '');
|
||||
this.get('storage').set('huegasm.' + option, this.get(option));
|
||||
}),
|
||||
|
||||
onRepeatChange: on('init', observer('repeat', function () {
|
||||
let tooltipTxt = 'Repeat all', type = 'repeat';
|
||||
|
||||
if (this.get(type) === 1) {
|
||||
tooltipTxt = 'Repeat one';
|
||||
} else if (this.get(type) === 2) {
|
||||
tooltipTxt = 'Repeat off';
|
||||
}
|
||||
|
||||
this.changeTooltipText(type, tooltipTxt);
|
||||
})),
|
||||
|
||||
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);
|
||||
})),
|
||||
|
||||
onShuffleChange: on('init', observer('shuffle', function () {
|
||||
let tooltipTxt = 'Shuffle', type = 'shuffle';
|
||||
|
||||
if (this.get(type)) {
|
||||
this.get('shufflePlayed').clear();
|
||||
tooltipTxt = 'Unshuffle';
|
||||
}
|
||||
|
||||
this.changeTooltipText(type, tooltipTxt);
|
||||
})),
|
||||
|
||||
onVolumeMutedChange: on('init', observer('volumeMuted', function() {
|
||||
let tooltipTxt = 'Mute', type = 'volumeMuted',
|
||||
volumeMuted = this.get(type), dancer = this.get('dancer'),
|
||||
volume=0;
|
||||
|
||||
if (volumeMuted) {
|
||||
tooltipTxt = 'Unmute';
|
||||
volume = 0;
|
||||
} else {
|
||||
volume = this.get('volume')/100;
|
||||
}
|
||||
|
||||
if(this.get('playing')){
|
||||
dancer.setVolume(volume);
|
||||
}
|
||||
|
||||
this.changeTooltipText(type, tooltipTxt);
|
||||
})),
|
||||
|
||||
onPrevChange: on('init', observer('timeElapsed', 'playQueueNotEmpty', 'playQueue.[]', function() {
|
||||
if(this.get('playQueueNotEmpty')){
|
||||
let tooltipTxt = 'Previous', type = 'prev';
|
||||
|
||||
if(this.get('timeElapsed') > 5 || this.get('playQueue').length === 1) {
|
||||
tooltipTxt = 'Replay';
|
||||
}
|
||||
|
||||
this.changeTooltipText(type, tooltipTxt);
|
||||
}
|
||||
})),
|
||||
|
||||
onPlayingChange: on('init', observer('playing', function () {
|
||||
let tooltipTxt = 'Play', type = 'playing';
|
||||
|
||||
if (this.get(type)) {
|
||||
tooltipTxt = 'Pause';
|
||||
} else if(this.get('timeElapsed') === this.get('timeTotal') && this.get('timeTotal') !== 0){
|
||||
tooltipTxt = 'Replay';
|
||||
}
|
||||
|
||||
this.changeTooltipText(type, tooltipTxt);
|
||||
})),
|
||||
|
||||
changeTooltipText(type, text) {
|
||||
// change the tooltip text if it's already visible
|
||||
$('#' + type + 'Tooltip + .tooltip .tooltip-inner').html(text);
|
||||
//change the tooltip text for hover
|
||||
$('#' + type + 'Tooltip').attr('data-original-title', text);
|
||||
|
||||
if(isNone(this.get(type + 'TooltipTxt'))) {
|
||||
this.set(type + 'TooltipTxt', text);
|
||||
}
|
||||
},
|
||||
|
||||
formatTime(time){
|
||||
return this.pad(Math.floor(time/60), 2) + ':' + this.pad(time%60, 2);
|
||||
},
|
||||
|
||||
pad(num, size){ return ('000000000' + num).substr(-size); }
|
||||
});
|
||||
94
mobile/app/pods/components/music-tab/mixins/visualizer.js
Normal file
|
|
@ -0,0 +1,94 @@
|
|||
import Ember from 'ember';
|
||||
|
||||
const {
|
||||
Mixin,
|
||||
observer,
|
||||
$
|
||||
} = Ember;
|
||||
|
||||
export default Mixin.create({
|
||||
currentVisName: 'None',
|
||||
|
||||
visNames: ['None', 'Bars', 'Wave'],
|
||||
|
||||
onCurrentVisNameChange: observer('currentVisName', function () {
|
||||
let currentVisName = this.get('currentVisName');
|
||||
|
||||
if(currentVisName === 'None'){
|
||||
let canvasEl = $('#visualization')[0],
|
||||
ctx = canvasEl.getContext('2d');
|
||||
|
||||
ctx.clearRect(0, 0, canvasEl.width, canvasEl.height);
|
||||
}
|
||||
|
||||
this.get('storage').set('huegasm.currentVisName', currentVisName);
|
||||
}),
|
||||
|
||||
didInsertElement(){
|
||||
let dancer = this.get('dancer'),
|
||||
canvas = $('#visualization')[0],
|
||||
playerArea = $('#player-area'),
|
||||
ctx = canvas.getContext('2d'),
|
||||
spacing = 2,
|
||||
h = playerArea.height(), w;
|
||||
|
||||
canvas.height = h;
|
||||
|
||||
// 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
|
||||
let syncCanvasHeight = ()=>{
|
||||
w = playerArea.width();
|
||||
canvas.width = w;
|
||||
};
|
||||
|
||||
syncCanvasHeight();
|
||||
|
||||
$(window).on('resize', syncCanvasHeight);
|
||||
|
||||
dancer.bind('update', () => {
|
||||
let currentVisName = this.get('currentVisName'),
|
||||
gradient = ctx.createLinearGradient(0, 0, 0, h),
|
||||
pageHidden = document.hidden || document.msHidden || document.webkitHidden || document.mozHidden;
|
||||
|
||||
// dont do anything if the page is hidden or no visualization
|
||||
if(currentVisName === 'None' || pageHidden || !this.get('active')){
|
||||
return;
|
||||
}
|
||||
|
||||
ctx.clearRect(0, 0, w, h);
|
||||
|
||||
if (currentVisName === 'Wave') {
|
||||
let width = 3,
|
||||
count = 1024;
|
||||
|
||||
gradient.addColorStop(0.6, 'white');
|
||||
gradient.addColorStop(0, '#0036FA');
|
||||
|
||||
ctx.lineWidth = 1;
|
||||
ctx.strokeStyle = gradient;
|
||||
let waveform = dancer.getWaveform();
|
||||
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(0, h / 2);
|
||||
for (let i = 0, l = waveform.length; i < l && i < count; i++) {
|
||||
ctx.lineTo(i * ( spacing + width ), ( h / 2 ) + waveform[i] * ( h / 2 ));
|
||||
}
|
||||
ctx.stroke();
|
||||
ctx.closePath();
|
||||
} else if (currentVisName === 'Bars') {
|
||||
let width = 4,
|
||||
count = 128;
|
||||
|
||||
gradient.addColorStop(1, '#0f0');
|
||||
gradient.addColorStop(0.6, '#ff0');
|
||||
gradient.addColorStop(0.2, '#F12B24');
|
||||
|
||||
ctx.fillStyle = gradient;
|
||||
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);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
212
mobile/app/pods/components/music-tab/template.hbs
Normal file
|
|
@ -0,0 +1,212 @@
|
|||
<div class="row" id="step1">
|
||||
<div id="player-area" class="col-sm-8 col-xs-12 {{if (eq "None" currentVisName) "display-icon"}}" {{action "playerAreaPlay"}}>
|
||||
<canvas id="visualization"></canvas>
|
||||
<div id="artwork"><img src={{largeArtworkPic}}></div>
|
||||
<div id="play-notification" class="material-icons {{if playing "play-arrow" "pause"}}"></div>
|
||||
|
||||
<div id="player-controls">
|
||||
{{#if usingLocalAudio}}
|
||||
{{range-slider start=seekPosition min=0 max=100 connect=filledConnect id="seek-slider" on-slide="seekChanged"}}
|
||||
|
||||
{{#if playQueueNotEmpty}}
|
||||
<span data-toggle="tooltip" data-placement="top" class="bootstrap-tooltip" id="prevTooltip"
|
||||
data-title={{prevTooltipTxt}} {{action "previous"}}>{{paper-icon "skip-previous" class="player-control-icon"}}</span><!--
|
||||
-->{{/if}}<!--
|
||||
--><span data-toggle="tooltip" data-placement="top" id="playingTooltip" class="bootstrap-tooltip"
|
||||
data-title={{playingTooltipTxt}} {{action "play"}}>{{paper-icon playingIcon class="player-control-icon"}}</span><!--
|
||||
-->{{#if playQueueMultiple}}<!--
|
||||
--><span data-toggle="tooltip" data-placement="top" class="bootstrap-tooltip"
|
||||
data-title="Next" {{action "next" true}}>{{paper-icon "skip-next" action="" class="player-control-icon"}}</span><!--
|
||||
-->{{/if}}<!--
|
||||
--><span data-toggle="tooltip" data-placement="top" class="bootstrap-tooltip" id="volumeMutedTooltip"
|
||||
data-title={{volumeMutedTooltipTxt}} {{action "volumeMutedChanged"}}>{{paper-icon icon=volumeIcon class=volumeMutedClass}}</span><!--
|
||||
-->{{range-slider start=volume min=0 max=100 connect=filledConnect on-slide="volumeChanged" id="volume-bar" class="hidden-xs"}}
|
||||
|
||||
<div id="player-time-controls">{{timeElapsedTxt}} / {{timeTotalTxt}}</div>
|
||||
{{/if}}
|
||||
|
||||
<span class="pull-right">
|
||||
{{#if scUrl}}
|
||||
<a href="#" data-toggle="tooltip" data-placement="top" class="sound-cloud-link bootstrap-tooltip" data-title="Listen on SoundCloud" {{action "gotoSCURL" scUrl}}>
|
||||
<img src="assets/images/sc-white.png" class="hidden-xs" />
|
||||
<img src="assets/images/sc-white-sm.png" class="visible-xs-inline" />
|
||||
</a>
|
||||
{{/if}}
|
||||
<span class="dropup">
|
||||
<span class="dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="true">
|
||||
<span data-toggle="tooltip" data-placement="top" class="bootstrap-tooltip" data-title="Visualizations" {{action "hideTooltip"}}>
|
||||
{{paper-icon "remove-red-eye" class="player-control-icon"}}
|
||||
</span>
|
||||
</span>
|
||||
|
||||
<ul class="dropdown-menu visualizers-menu">
|
||||
{{#each visNames as |name|}}
|
||||
<li>
|
||||
<a href="#" {{action "setVisName" name}}>{{name}}
|
||||
{{#if (eq currentVisName name)}}
|
||||
{{paper-icon "check" classNames=dimmerOnClass}}
|
||||
{{/if}}
|
||||
</a>
|
||||
</li>
|
||||
{{/each}}
|
||||
</ul>
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="playlist" class="col-sm-4 col-xs-12">
|
||||
<input id="file-input" type="file" accept="audio/*" multiple="true"/>
|
||||
|
||||
<div id="play-list-controls">
|
||||
{{#if usingLocalAudio}}
|
||||
<button class="dropdown-toggle pull-right add-new-music" type="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="true">
|
||||
Add new music
|
||||
<span class="caret"></span>
|
||||
</button>
|
||||
|
||||
<ul id="add-music-choices" class="dropdown-menu">
|
||||
<li><a href="#" {{action "addLocalAudio"}}>Local</a></li>
|
||||
<li><a href="#" {{action "toggleIsShowingAddSoundCloudModal"}}>SoundCloud</a></li>
|
||||
</ul>
|
||||
{{/if}}
|
||||
|
||||
{{#if usingMicSupported}}
|
||||
<span data-toggle="tooltip" data-placement="bottom auto" class="bootstrap-tooltip" id="using-mic-audio-tooltip" data-title={{usingMicAudioTooltipTxt}} {{action "useMicAudio"}}>{{paper-icon icon=micIcon class=usingMicAudioClass}}</span>
|
||||
{{/if}}
|
||||
|
||||
{{#if usingLocalAudio}}
|
||||
<span data-toggle="tooltip" data-placement="bottom auto" class="bootstrap-tooltip" id="shuffleTooltip" data-title={{shuffleTooltipTxt}} {{action "shuffleChanged"}}>{{paper-icon "shuffle" class=shuffleClass}}</span>
|
||||
<span data-toggle="tooltip" data-placement="bottom auto" class="bootstrap-tooltip" id="repeatTooltip" data-title={{repeatTooltipTxt}} {{action "repeatChanged"}}>{{paper-icon repeatIcon class=repeatClass}}</span>
|
||||
<span data-toggle="tooltip" data-placement="bottom auto" class="bootstrap-tooltip" data-title="Clear playlist" {{action "clearPlaylist"}}>{{paper-icon "clear-all" class="player-control-icon"}}</span>
|
||||
{{/if}}
|
||||
</div>
|
||||
|
||||
{{#if usingMicAudio}}
|
||||
<div id="play-area-mic" class="{{if dimmerOn "dimmerOn"}}">
|
||||
{{paper-icon "mic" class=dimmerOnClass}}
|
||||
</div>
|
||||
{{else}}
|
||||
{{#if usingLocalAudio}}
|
||||
<div id="play-list-area" class={{playListAreaClass}} {{action "addLocalAudio"}} {{action "playerListAreaDragOver" on="dragOver"}} {{action "playerListAreaDragLeave" on="dragLeave"}} {{action "dropFiles" on="drop"}}>
|
||||
{{#if (or playQueueEmpty dragging)}}
|
||||
<div id="dragHere">
|
||||
{{#if dragging}}
|
||||
Drag your music files here
|
||||
{{else}}
|
||||
Add your music files here
|
||||
{{/if}}
|
||||
</div>
|
||||
{{paper-icon "library-music" class=dimmerOnClass}}
|
||||
{{/if}}
|
||||
|
||||
{{#each playQueue as |item index|}}
|
||||
<div class="playlist-item pointer track{{index}} {{if (eq index playQueuePointer) "active"}} {{if dragging "hidden"}}" {{action "goToSong" index true bubbles=false}}>
|
||||
{{#if item.picture}}
|
||||
<img class="album-art" src={{item.picture}}>
|
||||
{{else}}
|
||||
<img class="album-art" src="assets/images/missingArtwork.png">
|
||||
{{/if}}
|
||||
|
||||
<div class="song-info">
|
||||
{{#if item.title}}
|
||||
<div class="song-title">{{item.title}}</div>
|
||||
<div class="song-artist">
|
||||
{{#if item.artistUrl}}
|
||||
<a href="#" {{action "gotoURL" item.artistUrl bubbles=false}}>{{item.artist}}</a>
|
||||
{{else}}
|
||||
{{item.artist}}
|
||||
{{/if}}
|
||||
</div>
|
||||
{{else}}
|
||||
{{item.fileName}}
|
||||
{{/if}}
|
||||
</div>
|
||||
|
||||
<span data-toggle="tooltip" data-placement="bottom auto" data-title="Remove" data-container="body" class="audio-remove-button pointer bootstrap-tooltip" {{action "removeAudio" index bubbles=false}}>{{paper-icon "close" classNames="close"}}</span>
|
||||
</div>
|
||||
{{/each}}
|
||||
</div>
|
||||
{{/if}}
|
||||
{{/if}}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="slide-toggle" class="text-center pointer row" {{action "slideTogglePlayerBottom"}}>
|
||||
<div class="col-xs-offset-5 col-xs-2">
|
||||
{{paper-icon beatDetectionAreaArrowIcon id="beat-detection-area-arrow-icon"}}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="player-bottom" class="row {{if dimmerOn "dimmerOn"}}">
|
||||
<div id="beat-area" class="col-sm-7 col-xs-12">
|
||||
{{#if usingBeatPreferences}}
|
||||
<span data-toggle="tooltip" data-placement="bottom" data-title="Using the saved sensitivity preference from the last time you listened to this song" class="bootstrap-tooltip" id="save-beat-preferences-star">
|
||||
{{paper-icon "star" class=dimmerOnClass}}
|
||||
</span>
|
||||
{{/if}}
|
||||
|
||||
<div class="row" id="beat-option-row">
|
||||
<div class="beat-option col-xs-4">
|
||||
<span data-toggle="tooltip" data-placement="bottom" data-title="The sensitivity of the beat detector ( more sensitivity results in more registered beats )" class="option-description bootstrap-tooltip">
|
||||
Sensitivity
|
||||
</span>
|
||||
|
||||
{{range-slider start=threshold orientation="vertical" step=beatOptions.threshold.step range=beatOptions.threshold.range on-slide="thresholdChanged" pips=beatOptions.threshold.pips}}
|
||||
</div>
|
||||
|
||||
<div class="beat-option col-xs-4">
|
||||
<span data-toggle="tooltip" data-placement="bottom" data-title="The range of hues ( colors ) that the lights may change to on beat." class="option-description bootstrap-tooltip">
|
||||
Hue Range
|
||||
</span>
|
||||
|
||||
{{range-slider start=hueRange orientation="vertical" step=beatOptions.hueRange.step range=beatOptions.hueRange.range connect=hueRangeConnect on-slide="hueRangeChanged" pips=beatOptions.hueRange.pips}}
|
||||
</div>
|
||||
|
||||
{{#if usingMicAudio}}
|
||||
<div class="beat-option col-xs-4">
|
||||
<span data-toggle="tooltip" data-placement="bottom" data-title="The coefficient to boost the microphone signal by" class="option-description bootstrap-tooltip">
|
||||
Mic Boost
|
||||
</span>
|
||||
|
||||
{{range-slider start=micBoost orientation="vertical" step=beatOptions.micBoost.step range=beatOptions.micBoost.range on-slide="micBoostChanged" pips=beatOptions.micBoost.pips}}
|
||||
</div>
|
||||
{{/if}}
|
||||
|
||||
<div id="light-option" class="beat-option col-xs-4">
|
||||
<span data-toggle="tooltip" data-placement="bottom auto" data-title="Quickly flash the lights on beat" class="bootstrap-tooltip" {{action "hideTooltip" on="mouseLeave"}}>
|
||||
{{paper-checkbox value=flashingTransitions onChange=(action (mut flashingTransitions)) label="Flashing Transitions"}}
|
||||
</span>
|
||||
|
||||
<span data-toggle="tooltip" data-placement="bottom auto" data-title="Slowly cycle the lights through all the colors" class="bootstrap-tooltip" {{action "hideTooltip" on="mouseLeave"}}>
|
||||
{{paper-checkbox value=colorloopMode onChange=(action (mut colorloopMode)) label="Colorloop"}}
|
||||
</span>
|
||||
|
||||
{{!--<span data-toggle="tooltip" data-placement="bottom auto" data-title="Periodically turn the lights on and off to create a cool looking ambience" class="bootstrap-tooltip" {{action "hideTooltip" on="mouseLeave"}}>
|
||||
{{#paper-checkbox checked=ambienceMode}}Ambience{{/paper-checkbox}}
|
||||
</span>--}}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="beat-container" class="col-sm-5 col-xs-12">
|
||||
<div class="bezel">
|
||||
<div class="rivet1"></div>
|
||||
<div class="rivet2"></div>
|
||||
<div class="rivet3"></div>
|
||||
<div class="rivet4"></div>
|
||||
<div class="rivet5"></div>
|
||||
<div class="rivet6"></div>
|
||||
<div class="rivet7"></div>
|
||||
<div class="rivet8"></div>
|
||||
|
||||
<div id="beat-speaker-center-outer">
|
||||
<div id="beat-speaker-center-inner" class="pointer" {{action "clickSpeaker"}}></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{ember-notify messageStyle='bootstrap' closeAfter=5000}}
|
||||
|
||||
{{music-tab/add-soundcloud-sound-modal action="handleNewSoundCloudURL" isShowingModal=isShowingAddSoundCloudModal}}
|
||||
3
mobile/app/resolver.js
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
import Resolver from 'ember-resolver';
|
||||
|
||||
export default Resolver;
|
||||
12
mobile/app/router.js
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
import Ember from 'ember';
|
||||
import config from './config/environment';
|
||||
|
||||
const Router = Ember.Router.extend({
|
||||
location: config.locationType,
|
||||
rootURL: config.rootURL
|
||||
});
|
||||
|
||||
Router.map(function() {
|
||||
});
|
||||
|
||||
export default Router;
|
||||
112
mobile/app/styles/app.scss
Normal file
|
|
@ -0,0 +1,112 @@
|
|||
@import 'ember-modal-dialog/ember-modal-structure';
|
||||
@import 'ember-modal-dialog/ember-modal-appearance';
|
||||
|
||||
@import 'huegasm-variables';
|
||||
|
||||
@import 'bootstrap'; // used to take out bootstrap scss modules that we don't need
|
||||
@import 'paper';
|
||||
|
||||
@import 'bridge-finder';
|
||||
@import 'common';
|
||||
@import 'dimmer';
|
||||
@import 'fancy-speaker';
|
||||
@import 'introjs';
|
||||
@import 'hue-controls';
|
||||
@import 'light-group';
|
||||
@import 'music-tab';
|
||||
@import 'noui-slider';
|
||||
|
||||
html {
|
||||
min-height: 100%;
|
||||
height: auto;
|
||||
}
|
||||
|
||||
body {
|
||||
margin-bottom: $footerHeight;
|
||||
position: static;
|
||||
}
|
||||
|
||||
body, button {
|
||||
font-family: 'Slabo 27px', serif;
|
||||
}
|
||||
|
||||
.ember-app {
|
||||
padding-bottom: 50px;
|
||||
}
|
||||
|
||||
#footer {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
bottom: 0;
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
height: $footerHeight;
|
||||
}
|
||||
|
||||
#footer-text {
|
||||
display: inline-block;
|
||||
position: relative;
|
||||
bottom: 10px;
|
||||
a {
|
||||
margin-left: 5px;
|
||||
}
|
||||
}
|
||||
|
||||
.alert {
|
||||
margin-bottom: 0;
|
||||
border: none;
|
||||
}
|
||||
|
||||
|
||||
.title {
|
||||
margin-bottom: 20px;
|
||||
img {
|
||||
width: 200px;
|
||||
}
|
||||
}
|
||||
|
||||
button.md-warn {
|
||||
background: $secondaryThemeColor;
|
||||
}
|
||||
|
||||
div.ember-modal-dialog {
|
||||
padding: 20px;
|
||||
color: black;
|
||||
md-input-container input {
|
||||
color: black !important;
|
||||
}
|
||||
md-input-container label {
|
||||
color: rgba(0, 0, 0, 0.26);
|
||||
}
|
||||
}
|
||||
|
||||
.dropdown-menu {
|
||||
font-size: 14px;
|
||||
a {
|
||||
line-height: 2 !important;
|
||||
}
|
||||
|
||||
}
|
||||
// fancy webkit scrollbars
|
||||
::-webkit-scrollbar {
|
||||
-webkit-appearance: none;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar:vertical {
|
||||
width: 12px;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar:horizontal {
|
||||
height: 12px;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-thumb {
|
||||
background-color: rgba(0, 0, 0, .5);
|
||||
border-radius: 10px;
|
||||
border: 2px solid #ffffff;
|
||||
-webkit-box-shadow: inset 0 0 6px rgba(0,0,0,0.5);
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-track {
|
||||
background-color: #ffffff;
|
||||
}
|
||||
56
mobile/app/styles/bootstrap.scss
vendored
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
/*!
|
||||
* Bootstrap v3.3.7 (http://getbootstrap.com)
|
||||
* Copyright 2011-2016 Twitter, Inc.
|
||||
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
|
||||
*/
|
||||
|
||||
// Core variables and mixins
|
||||
@import "bower_components/bootstrap-sass/assets/stylesheets/bootstrap/variables";
|
||||
@import "bower_components/bootstrap-sass/assets/stylesheets/bootstrap/mixins";
|
||||
|
||||
// Reset and dependencies
|
||||
@import "bower_components/bootstrap-sass/assets/stylesheets/bootstrap/normalize";
|
||||
//@import "bower_components/bootstrap-sass/assets/stylesheets/bootstrap/print";
|
||||
//@import "bower_components/bootstrap-sass/assets/stylesheets/bootstrap/glyphicons";
|
||||
|
||||
// Core CSS
|
||||
@import "bower_components/bootstrap-sass/assets/stylesheets/bootstrap/scaffolding";
|
||||
@import "bower_components/bootstrap-sass/assets/stylesheets/bootstrap/type";
|
||||
//@import "bower_components/bootstrap-sass/assets/stylesheets/bootstrap/code";
|
||||
@import "bower_components/bootstrap-sass/assets/stylesheets/bootstrap/grid";
|
||||
//@import "bower_components/bootstrap-sass/assets/stylesheets/bootstrap/tables";
|
||||
@import "bower_components/bootstrap-sass/assets/stylesheets/bootstrap/forms";
|
||||
@import "bower_components/bootstrap-sass/assets/stylesheets/bootstrap/buttons";
|
||||
|
||||
// Components
|
||||
//@import "bower_components/bootstrap-sass/assets/stylesheets/bootstrap/component-animations";
|
||||
@import "bower_components/bootstrap-sass/assets/stylesheets/bootstrap/dropdowns";
|
||||
//@import "bower_components/bootstrap-sass/assets/stylesheets/bootstrap/button-groups";
|
||||
@import "bower_components/bootstrap-sass/assets/stylesheets/bootstrap/input-groups";
|
||||
//@import "bower_components/bootstrap-sass/assets/stylesheets/bootstrap/navs";
|
||||
//@import "bower_components/bootstrap-sass/assets/stylesheets/bootstrap/navbar";
|
||||
//@import "bower_components/bootstrap-sass/assets/stylesheets/bootstrap/breadcrumbs";
|
||||
//@import "bower_components/bootstrap-sass/assets/stylesheets/bootstrap/pagination";
|
||||
//@import "bower_components/bootstrap-sass/assets/stylesheets/bootstrap/pager";
|
||||
//@import "bower_components/bootstrap-sass/assets/stylesheets/bootstrap/labels";
|
||||
//@import "bower_components/bootstrap-sass/assets/stylesheets/bootstrap/badges";
|
||||
//@import "bower_components/bootstrap-sass/assets/stylesheets/bootstrap/jumbotron";
|
||||
//@import "bower_components/bootstrap-sass/assets/stylesheets/bootstrap/thumbnails";
|
||||
@import "bower_components/bootstrap-sass/assets/stylesheets/bootstrap/alerts";
|
||||
//@import "bower_components/bootstrap-sass/assets/stylesheets/bootstrap/progress-bars";
|
||||
//@import "bower_components/bootstrap-sass/assets/stylesheets/bootstrap/media";
|
||||
//@import "bower_components/bootstrap-sass/assets/stylesheets/bootstrap/list-group";
|
||||
//@import "bower_components/bootstrap-sass/assets/stylesheets/bootstrap/panels";
|
||||
//@import "bower_components/bootstrap-sass/assets/stylesheets/bootstrap/responsive-embed";
|
||||
//@import "bower_components/bootstrap-sass/assets/stylesheets/bootstrap/wells";
|
||||
//@import "bower_components/bootstrap-sass/assets/stylesheets/bootstrap/close";
|
||||
|
||||
// Components w/ JavaScript
|
||||
//@import "bower_components/bootstrap-sass/assets/stylesheets/bootstrap/modals";
|
||||
@import "bower_components/bootstrap-sass/assets/stylesheets/bootstrap/tooltip";
|
||||
@import "bower_components/bootstrap-sass/assets/stylesheets/bootstrap/popovers";
|
||||
//@import "bower_components/bootstrap-sass/assets/stylesheets/bootstrap/carousel";
|
||||
|
||||
// Utility classes
|
||||
@import "bower_components/bootstrap-sass/assets/stylesheets/bootstrap/utilities";
|
||||
@import "bower_components/bootstrap-sass/assets/stylesheets/bootstrap/responsive-utilities";
|
||||
73
mobile/app/styles/bridge-finder.scss
Normal file
|
|
@ -0,0 +1,73 @@
|
|||
#press-bridge-button-img {
|
||||
width: 200px;
|
||||
margin: 0 auto 30px auto;
|
||||
display: inherit;
|
||||
}
|
||||
|
||||
#bridge-button-group {
|
||||
width: 150px;
|
||||
margin: 30px auto;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
#bridge-input md-input-container{
|
||||
max-width: 200px;
|
||||
margin: 30px auto;
|
||||
}
|
||||
|
||||
#intro {
|
||||
font-size: 22px;
|
||||
}
|
||||
|
||||
#intro-paragraph {
|
||||
margin-bottom: 20px;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
#bridge-finder, .ready-block {
|
||||
text-align: center;
|
||||
padding: 10px 15px 0;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
#bridge-finder .md-bar {
|
||||
background-color: $secondaryThemeColor !important;
|
||||
}
|
||||
|
||||
// preloading image
|
||||
.ready-block:after {
|
||||
display: none;
|
||||
content: url(images/pressButtonBridge.png);
|
||||
}
|
||||
|
||||
.embed-container {
|
||||
position:relative;
|
||||
padding-bottom:56.25%;
|
||||
padding-top:30px;
|
||||
height:0;
|
||||
overflow:hidden;
|
||||
}
|
||||
|
||||
.embed-container-wrapper {
|
||||
max-width: 550px;
|
||||
margin: auto;
|
||||
}
|
||||
|
||||
.embed-container iframe, .embed-container object, .embed-container embed {
|
||||
position:absolute;
|
||||
top:0;
|
||||
left:0;
|
||||
width:100%;
|
||||
height:100%;
|
||||
}
|
||||
|
||||
.go-button {
|
||||
margin: 20px 0;
|
||||
border-radius: 100% !important;
|
||||
width: 100px;
|
||||
height: 100px;
|
||||
font-size: 28px;
|
||||
&:hover {
|
||||
background: darken(#3f51b5, 10%) !important;
|
||||
}
|
||||
}
|
||||
15
mobile/app/styles/common.scss
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
.text-left {
|
||||
text-align: left !important;
|
||||
}
|
||||
|
||||
.relative {
|
||||
position: relative !important;
|
||||
}
|
||||
|
||||
.no-text-decoration {
|
||||
text-decoration: none !important;
|
||||
}
|
||||
|
||||
.pointer {
|
||||
cursor: pointer;
|
||||
}
|
||||
86
mobile/app/styles/dimmer.scss
Normal file
|
|
@ -0,0 +1,86 @@
|
|||
div.dimmerOn {
|
||||
color: $whitish !important;
|
||||
background: $blackish !important;
|
||||
}
|
||||
|
||||
html.dimmerOn {
|
||||
color: white;
|
||||
background: $blackish;
|
||||
}
|
||||
|
||||
body.dimmerOn {
|
||||
color: $whitish;
|
||||
background: $blackish;
|
||||
md-input-container {
|
||||
label {
|
||||
color: #3f51b5 !important;
|
||||
}
|
||||
.md-input {
|
||||
color: $whitish !important;
|
||||
border-color: #3f51b5 !important;
|
||||
}
|
||||
}
|
||||
.md-track {
|
||||
background: $whitish;
|
||||
}
|
||||
.color {
|
||||
border: 1px solid white;
|
||||
}
|
||||
.playlist-item, .dropdown-menu, .add-new-music {
|
||||
color: $whitish;
|
||||
background-color: $dimmerOnButtonColor;
|
||||
}
|
||||
.dropdown-menu {
|
||||
a {
|
||||
color: $whitish;
|
||||
&:hover {
|
||||
background-color: darken($dimmerOnButtonColor, 10%) !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
.playlist-item {
|
||||
&.active {
|
||||
background: darken($dimmerOnButtonColor, 15%) !important;
|
||||
}
|
||||
&:hover {
|
||||
background: darken($dimmerOnButtonColor, 10%);
|
||||
}
|
||||
.audio-remove-button .paper-icon {
|
||||
color: $whitish !important;
|
||||
&:hover {
|
||||
color: white !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
svg {
|
||||
-webkit-filter: drop-shadow(0 0 5px #228DFF);
|
||||
}
|
||||
.md-container {
|
||||
color: $whitish;
|
||||
}
|
||||
.add-new-music:hover {
|
||||
background: darken($dimmerOnButtonColor, 5%);
|
||||
}
|
||||
.popover-content {
|
||||
color: black !important;
|
||||
}
|
||||
.md-bar {
|
||||
background-color: darken(white, 60%) !important;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.paper-icon.dimmerOn {
|
||||
color: inherit !important;
|
||||
text-shadow: $glowingText;
|
||||
opacity: 0.9 !important;
|
||||
}
|
||||
|
||||
#dimmer {
|
||||
display: inline-block;
|
||||
cursor: pointer;
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
background: url(/favicon-96x96.png) center center no-repeat;
|
||||
background-size: 40px 40px;
|
||||
}
|
||||
102
mobile/app/styles/fancy-speaker.scss
Normal file
|
|
@ -0,0 +1,102 @@
|
|||
/* Variables */
|
||||
$centersize: 80px;
|
||||
$center1size: 205px;
|
||||
$bezelsize: 240px;
|
||||
|
||||
/* Extenders */
|
||||
%base {
|
||||
border-radius: 100%;
|
||||
}
|
||||
%rivet {
|
||||
position: absolute;
|
||||
height: 8px;
|
||||
width: 8px;
|
||||
background-color: #555;
|
||||
border-radius: 100%;
|
||||
box-shadow: inset 0 0 3px #000, 0 0 2px #000;
|
||||
}
|
||||
|
||||
#beat-speaker-center-inner {
|
||||
@extend %base;
|
||||
height: $centersize;
|
||||
width: $centersize;
|
||||
position: absolute;
|
||||
bottom: 47px;
|
||||
right: 47px;
|
||||
-webkit-filter: blur(1px);
|
||||
filter: blur(1px);
|
||||
background: rgb(0,0,0);
|
||||
background: -moz-radial-gradient(center, ellipse cover, rgba(0,0,0,1) 0%, rgba(79,79,79,1) 0%, rgba(0,0,0,1) 100%);
|
||||
background: -webkit-gradient(radial, center center, 0px, center center, 100%, color-stop(0%,rgba(0,0,0,1)), color-stop(0%,rgba(79,79,79,1)), color-stop(100%,rgba(0,0,0,1)));
|
||||
background: -webkit-radial-gradient(center, ellipse cover, rgba(0,0,0,1) 0%,rgba(79,79,79,1) 0%,rgba(0,0,0,1) 100%);
|
||||
background: -o-radial-gradient(center, ellipse cover, rgba(0,0,0,1) 0%,rgba(79,79,79,1) 0%,rgba(0,0,0,1) 100%);
|
||||
background: -ms-radial-gradient(center, ellipse cover, rgba(0,0,0,1) 0%,rgba(79,79,79,1) 0%,rgba(0,0,0,1) 100%);
|
||||
background: radial-gradient(ellipse at center, rgba(0,0,0,1) 0%,rgba(79,79,79,1) 0%,rgba(0,0,0,1) 100%);
|
||||
box-shadow: 0 0 10px rgba(0, 0, 0, 1);
|
||||
}
|
||||
#beat-speaker-center-outer {
|
||||
@extend %base;
|
||||
position: absolute;
|
||||
top: 16px;
|
||||
left: 16px;
|
||||
height: $center1size;
|
||||
width: $center1size;
|
||||
border: 15px solid #333;
|
||||
box-shadow: -3px -3px 15px rgba(0, 0, 0, 0.4), inset -3px -3px 15px rgba(0, 0, 0, 0.5);
|
||||
background: -moz-linear-gradient(130deg, rgba(117, 117, 117, 1) 55%, rgba(220, 220, 220, 1) 100%);
|
||||
background: -webkit-linear-gradient(130deg, rgba(117, 117, 117, 1) 55%, rgba(220, 220, 220, 1) 100%);
|
||||
background: -o-linear-gradient(130deg, rgba(117, 117, 117, 1) 55%, rgba(220, 220, 220, 1) 100%);
|
||||
background: -ms-linear-gradient(130deg, rgba(117, 117, 117, 1) 55%, rgba(220, 220, 220, 1) 100%);
|
||||
background: linear-gradient(130deg, rgba(117, 117, 117, 1) 55%, rgba(220, 220, 220, 1) 100%);
|
||||
}
|
||||
.bezel {
|
||||
@extend %base;
|
||||
margin: 0 auto;
|
||||
height: $bezelsize;
|
||||
width: $bezelsize;
|
||||
position: relative;
|
||||
background-color: #A8A8A8;
|
||||
box-shadow: 0 0 10px rgba(0, 0, 0, 0.8), inset 3px 3px 10px rgba(0, 0, 0, 0.8), 0 0 2px rgba(0, 0, 0, 0.8), inset 0 0 30px -5px rgba(0, 0, 0, 0.8);
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
}
|
||||
.rivet1 {
|
||||
@extend %rivet;
|
||||
top: 6px;
|
||||
left: 50%;
|
||||
}
|
||||
.rivet2 {
|
||||
@extend %rivet;
|
||||
bottom: 6px;
|
||||
left: 50%;
|
||||
}
|
||||
.rivet3 {
|
||||
@extend %rivet;
|
||||
top: 50%;
|
||||
left: 6px;
|
||||
}
|
||||
.rivet4 {
|
||||
@extend %rivet;
|
||||
top: 50%;
|
||||
right: 6px;
|
||||
}
|
||||
.rivet5 {
|
||||
@extend %rivet;
|
||||
top: 18%;
|
||||
left: 13.7%;
|
||||
}
|
||||
.rivet6 {
|
||||
@extend %rivet;
|
||||
top: 18%;
|
||||
right: 13.5%;
|
||||
}
|
||||
.rivet7 {
|
||||
@extend %rivet;
|
||||
bottom: 17%;
|
||||
left: 13.5%;
|
||||
}
|
||||
.rivet8 {
|
||||
@extend %rivet;
|
||||
bottom: 17%;
|
||||
right: 13.5%;
|
||||
}
|
||||
115
mobile/app/styles/hue-controls.scss
Normal file
|
|
@ -0,0 +1,115 @@
|
|||
#lights-tab {
|
||||
min-height: 350px;
|
||||
.paper-icon {
|
||||
line-height: 0.8 !important;
|
||||
}
|
||||
}
|
||||
|
||||
.lights-control-tooltip + .tooltip {
|
||||
left: 0 !important;
|
||||
}
|
||||
|
||||
#color-row {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
#hue-controls {
|
||||
max-width: 1200px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
// preload images
|
||||
#hue-controls:after, md-progress-circular:after {
|
||||
display: none;
|
||||
content: url(images/colormap.png) url(images/missingArtwork.png) url(images/sc-white.png) url(/favicon-96x96.png) url(images/lights/a19.svg) url(images/lights/a19w.svg) url(images/lights/br30.svg) url(images/lights/br30w.svg) url(images/lights/gu10.svg) url(images/lights/gu10w.svg) url(images/lights/huego.svg) url(images/lights/huegow.svg) url(images/lights/lc_aura.svg) url(images/lights/lc_auraw.svg) url(images/lights/lc_bloom.svg) url(images/lights/lc_bloomw.svg) url(images/lights/lc_iris.svg) url(images/lights/lc_irisw.svg) url(images/lights/lightstrip.svg) url(images/lights/lightstripw.svg) url(images/lights/storylight.svg) url(images/lights/storylightw.svg);
|
||||
}
|
||||
|
||||
#navigation {
|
||||
padding: 15px 0;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.navigation-item {
|
||||
font-size: 18px;
|
||||
padding: 0 10px 0 10px;
|
||||
&.active {
|
||||
font-weight: bold;
|
||||
cursor: default;
|
||||
text-decoration: none !important;
|
||||
}
|
||||
&:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
}
|
||||
|
||||
.color {
|
||||
border: 1px solid rgba(0, 0, 0, 0.5);
|
||||
position: absolute;
|
||||
top: -53px;
|
||||
right: 0;
|
||||
}
|
||||
|
||||
#color-picker {
|
||||
padding: 5px;
|
||||
background: rgba(0, 0, 0, 0.7);
|
||||
box-shadow: 5px 10px 15px 5px rgba(0, 0, 0, 0.3);
|
||||
color: #FFFFFF;
|
||||
position: absolute;
|
||||
width: 266px;
|
||||
height: 266px;
|
||||
right: 6px;
|
||||
top: -9px;
|
||||
z-index: 3;
|
||||
}
|
||||
|
||||
#picker {
|
||||
cursor: crosshair;
|
||||
}
|
||||
|
||||
#loop-addition {
|
||||
position: absolute;
|
||||
left: 35px;
|
||||
top: 15px;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
#settings {
|
||||
z-index: 3;
|
||||
text-align: right;
|
||||
cursor: pointer;
|
||||
float: right;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
#settings-menu {
|
||||
position: absolute;
|
||||
box-shadow: 1px 10px 15px 1px rgba(0, 0, 0, 0.3);
|
||||
left: -135px;
|
||||
top: 25px;
|
||||
border: none;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
#settings-icon {
|
||||
transition: 0.1s all ease-in-out;
|
||||
&:hover {
|
||||
-webkit-transform: scale(1.1);
|
||||
transform: scale(1.1);
|
||||
}
|
||||
&:before {
|
||||
transition: 0.1s all ease-in-out;
|
||||
}
|
||||
}
|
||||
|
||||
@media(min-width:767px) {
|
||||
#lights-tab {
|
||||
font-size: 20px;
|
||||
min-height: 450px;
|
||||
.paper-icon {
|
||||
font-size: 24px;
|
||||
}
|
||||
.md-list-item-inner {
|
||||
height: 75px;
|
||||
}
|
||||
}
|
||||
}
|
||||
10
mobile/app/styles/huegasm-variables.scss
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
$playerHeight: 400px;
|
||||
$playerDefaultIconColor: #BBBBBB;
|
||||
$footerHeight: 50px;
|
||||
$playerBottomHeight: 255px;
|
||||
$secondaryThemeColor: #F12B24;
|
||||
$glowingText: 0 0 2px #fff, 0 0 4px #fff, 0 0 20px #228DFF;
|
||||
$dimmerOnButtonColor: #404040;
|
||||
$blackish: #242424;
|
||||
$whitish: #e0e0e0;
|
||||
$paperThemeColor: #3f51b5;
|
||||
22
mobile/app/styles/introjs.scss
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
.introjs-overlay {
|
||||
background: black;
|
||||
}
|
||||
|
||||
#settings.introjs-fixParent {
|
||||
position: inherit !important;
|
||||
}
|
||||
|
||||
.introjs-tooltip {
|
||||
color: black;
|
||||
}
|
||||
|
||||
.introjs-skipbutton {
|
||||
color: $secondaryThemeColor;
|
||||
}
|
||||
|
||||
.introjs-bullets ul li a.active {
|
||||
position: relative;
|
||||
height: 10px;
|
||||
width: 10px;
|
||||
top: -2px;
|
||||
}
|
||||
59
mobile/app/styles/light-group.scss
Normal file
|
|
@ -0,0 +1,59 @@
|
|||
.light-group {
|
||||
margin: 0 auto;
|
||||
text-align: center;
|
||||
.tooltip.top {
|
||||
margin-top: 4px;
|
||||
margin-left: 0;
|
||||
}
|
||||
div {
|
||||
display: inline-block;
|
||||
}
|
||||
}
|
||||
|
||||
.light-inactive {
|
||||
cursor: pointer;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.light-inactive::before {
|
||||
font-weight: bold;
|
||||
position: absolute;
|
||||
content: "X";
|
||||
top: -10px;
|
||||
left: 5px;
|
||||
font-size: 40px;
|
||||
color: rgba(255, 0, 0, 0.37);
|
||||
font-family: cursive;
|
||||
}
|
||||
|
||||
.horizontal-light-group {
|
||||
.light-inactive::before {
|
||||
top: -9px;
|
||||
left: 6px;
|
||||
}
|
||||
.tooltip.top {
|
||||
margin-top: 1px;
|
||||
margin-left: 2px;
|
||||
}
|
||||
}
|
||||
|
||||
.light-active {
|
||||
cursor: pointer;
|
||||
img {
|
||||
transition-duration: 0.3s;
|
||||
transition-property: transform;
|
||||
box-shadow: 0 0 1px rgba(0, 0, 0, 0);
|
||||
}
|
||||
img:hover {
|
||||
-webkit-transform: scale(1.2);
|
||||
transform: scale(1.2);
|
||||
}
|
||||
}
|
||||
|
||||
.ember-modal-overlay.translucent {
|
||||
background-color: rgba(0, 0, 0, 0.50);
|
||||
}
|
||||
|
||||
.remove-button {
|
||||
margin: 10px 0 10px 60px;
|
||||
}
|
||||
403
mobile/app/styles/music-tab.scss
Normal file
|
|
@ -0,0 +1,403 @@
|
|||
.row {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
#beat-option-row {
|
||||
height: 250px;
|
||||
}
|
||||
|
||||
#light-option {
|
||||
text-align: left !important;
|
||||
top: 20%;
|
||||
-webkit-transform: translateY(-20%);
|
||||
transform: translateY(-20%);
|
||||
.tooltip {
|
||||
margin-top: 5px !important;
|
||||
}
|
||||
}
|
||||
|
||||
#music-tab {
|
||||
padding: 0;
|
||||
margin: 10px 0 $footerHeight + 20px;
|
||||
}
|
||||
|
||||
#slide-toggle {
|
||||
color: $playerDefaultIconColor;
|
||||
background: #730B07;
|
||||
div .paper-icon {
|
||||
color: inherit !important;
|
||||
}
|
||||
}
|
||||
|
||||
#slide-toggle:hover{
|
||||
color: lighten($playerDefaultIconColor, 30%) !important;;
|
||||
}
|
||||
|
||||
#player-controls {
|
||||
transition: all 0.2s ease-in-out;
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
padding: 5px 10px;
|
||||
width: 100%;
|
||||
color: white !important;
|
||||
z-index: 20;
|
||||
background: linear-gradient(rgba(0, 0, 0, 0), rgba(0, 0, 0, 1));
|
||||
.tooltip.top {
|
||||
margin-top: -17px;
|
||||
}
|
||||
.tooltip-arrow {
|
||||
display: none;
|
||||
}
|
||||
.play-arrow, .pause, .replay {
|
||||
font-size: 30px;
|
||||
}
|
||||
}
|
||||
|
||||
#player-time-controls {
|
||||
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
|
||||
font-size: 14px;
|
||||
display: inline-block;
|
||||
margin-left: 1em;
|
||||
}
|
||||
|
||||
.player-control-icon {
|
||||
color: $playerDefaultIconColor !important;
|
||||
transition-duration: 0.1s;
|
||||
margin-right: 5px;
|
||||
}
|
||||
|
||||
.player-control-icon.active {
|
||||
color: $secondaryThemeColor !important;
|
||||
}
|
||||
|
||||
.player-control-icon:hover {
|
||||
color: white !important;
|
||||
}
|
||||
|
||||
#play-notification {
|
||||
position: relative;
|
||||
color: white;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
opacity: 0;
|
||||
background: black;
|
||||
border-radius: 100%;
|
||||
}
|
||||
|
||||
#player-area {
|
||||
height: $playerHeight;
|
||||
background-color: black;
|
||||
display: inline-block;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
#playlist {
|
||||
height: $playerHeight;
|
||||
background-color: #1E1E1E;
|
||||
padding: 0 5px 0 5px;
|
||||
}
|
||||
|
||||
#player-area * .noUi-origin {
|
||||
background-color: black;
|
||||
border-radius: 5px;
|
||||
}
|
||||
|
||||
#player-area * .noUi-base {
|
||||
background-color: black;
|
||||
border-radius: 5px;
|
||||
}
|
||||
|
||||
#volume-bar {
|
||||
width: 5em;
|
||||
height: 0.4em;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
#player-area * .noUi-handle::after, #player-area * .noUi-handle::before {
|
||||
content: none;
|
||||
}
|
||||
|
||||
#seek-slider {
|
||||
height: 6px;
|
||||
margin-bottom: 10px;
|
||||
transition-duration: 0.2s;
|
||||
}
|
||||
|
||||
#seek-slider:hover {
|
||||
height: 8px;
|
||||
}
|
||||
|
||||
#seek-slider:hover * .noUi-handle {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
#seek-slider * .noUi-handle {
|
||||
border: none;
|
||||
height: 13px;
|
||||
width: 13px;
|
||||
border-radius: 50%;
|
||||
top: -4px;
|
||||
left: -6px;
|
||||
opacity: 0;
|
||||
transition-duration: 0.1s;
|
||||
background-color: $secondaryThemeColor !important;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
#play-list-controls {
|
||||
min-height: 30px;
|
||||
margin-top: 5px;
|
||||
border-bottom: 1px solid #3a3a3a;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
#play-list-area, #play-area-mic {
|
||||
background-color: white;
|
||||
width: 100%;
|
||||
height: 350px;
|
||||
margin: 10px auto 0 auto;
|
||||
border-radius: 5px;
|
||||
transition: 0.1s all ease-in-out;
|
||||
position: relative;
|
||||
overflow: auto;
|
||||
#dragHere {
|
||||
position: absolute;
|
||||
top: 27%;
|
||||
font-size: 20px;
|
||||
text-align: center;
|
||||
width: 100%;
|
||||
}
|
||||
.library-music, .mic {
|
||||
position: absolute;
|
||||
top: 40%;
|
||||
font-size: 100px;
|
||||
opacity: 0.5;
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
|
||||
.song-artist {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
#play-list-area.drag-here-highlight {
|
||||
background-color: white;
|
||||
border: 5px dotted #5383ff;
|
||||
}
|
||||
|
||||
#play-list-area.dragging-over {
|
||||
background-color: darken(white, 5%);
|
||||
box-shadow: inset 0 0 20px 0 rgba(0, 0, 0, 1);
|
||||
}
|
||||
|
||||
#file-input {
|
||||
width: 1px;
|
||||
height: 1px;
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
.playlist-item {
|
||||
border-bottom: 1px solid rgba(128, 128, 128, 0.3);
|
||||
border-top: 1px solid rgba(128,128,128,0.3);
|
||||
height: 62px;
|
||||
font-family: 'Open Sans', sans-serif;
|
||||
padding: 0 20px 0 5px;
|
||||
position: relative;
|
||||
color: black;
|
||||
background: darken(white, 5%);
|
||||
.close {
|
||||
font-size: 18px;
|
||||
}
|
||||
.album-art {
|
||||
height: 60px;
|
||||
float: left;
|
||||
margin-right: 5px;
|
||||
border: 1px solid rgba(0, 0, 0, 0.5);
|
||||
}
|
||||
.song-info {
|
||||
.song-title {
|
||||
max-height: 40px;
|
||||
overflow: hidden;
|
||||
}
|
||||
.song-artist {
|
||||
max-height: 20px;
|
||||
overflow: hidden;
|
||||
}
|
||||
}
|
||||
.audio-remove-button {
|
||||
position: absolute;
|
||||
top: 20px;
|
||||
right: 5px;
|
||||
}
|
||||
}
|
||||
|
||||
.playlist-item.active {
|
||||
background: darken(white, 15%) !important;
|
||||
border-top: 1px solid $secondaryThemeColor;
|
||||
border-bottom: 1px solid $secondaryThemeColor;
|
||||
}
|
||||
|
||||
.playlist-item:hover {
|
||||
background: darken(white, 10%);
|
||||
.close {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
|
||||
#beat-area {
|
||||
height: $playerBottomHeight;
|
||||
position: relative;
|
||||
padding: 0;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.star {
|
||||
cursor: auto !important;
|
||||
}
|
||||
|
||||
#beat-option-button-group {
|
||||
margin: 20px 0 10px 0;
|
||||
}
|
||||
|
||||
.beat-option {
|
||||
padding: 5px 0;
|
||||
text-align: center;
|
||||
md-switch {
|
||||
margin: 0;
|
||||
}
|
||||
.option-description {
|
||||
font-size: 16px;
|
||||
}
|
||||
button {
|
||||
margin-top: 0;
|
||||
}
|
||||
.tooltip {
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
|
||||
#player-bottom {
|
||||
color: black;
|
||||
border: 1px solid black;
|
||||
width: 100%;
|
||||
background: white;
|
||||
}
|
||||
|
||||
#beat-container {
|
||||
padding: 0;
|
||||
height: $playerBottomHeight;
|
||||
}
|
||||
|
||||
#beat-area .light-group {
|
||||
margin: 10px 20px 0 40px;
|
||||
float: right;
|
||||
div {
|
||||
display: block;
|
||||
padding: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
#add-music-choices {
|
||||
min-width: initial;
|
||||
right: 0;
|
||||
left: initial;
|
||||
width: 100px;
|
||||
top: 25px;
|
||||
}
|
||||
|
||||
.add-new-music {
|
||||
padding: 0 5px 0 10px;
|
||||
font-size: 16px;
|
||||
border-radius: 5px;
|
||||
background: #f8f8f8;
|
||||
border: none;
|
||||
}
|
||||
|
||||
.add-new-music:hover {
|
||||
background: darken(#f8f8f8, 5%);
|
||||
}
|
||||
|
||||
.sound-cloud-link {
|
||||
margin-right: 5px;
|
||||
}
|
||||
|
||||
#visualization {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
}
|
||||
|
||||
#save-beat-preferences-star {
|
||||
position: absolute;
|
||||
top: 3px;
|
||||
z-index: 1000;
|
||||
md-icon {
|
||||
color: $secondaryThemeColor !important;
|
||||
font-size: 25px;
|
||||
}
|
||||
}
|
||||
|
||||
.visualizers-menu {
|
||||
left: -135px;
|
||||
}
|
||||
|
||||
.display-icon {
|
||||
background: url(/favicon-96x96.png) center center no-repeat;
|
||||
background-size: 80px 80px;
|
||||
}
|
||||
|
||||
#artwork {
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
overflow: hidden;
|
||||
img {
|
||||
display: block;
|
||||
margin: 0 auto;
|
||||
max-height: 400px;
|
||||
}
|
||||
}
|
||||
|
||||
.keyboard-arrow-down {
|
||||
font-size: 20px;
|
||||
}
|
||||
|
||||
.visualizers-menu .paper-icon {
|
||||
margin-left: 10px;
|
||||
position: relative;
|
||||
top: -4px;
|
||||
}
|
||||
|
||||
.close {
|
||||
font-size: 18px !important;
|
||||
color: rgb(51, 51, 51);
|
||||
display: none;
|
||||
opacity: 1;
|
||||
text-shadow: none;
|
||||
&:hover {
|
||||
color: darken(#333333, 5%) !important;
|
||||
opacity: 1
|
||||
}
|
||||
}
|
||||
|
||||
.ember-notify-default.ember-notify-cn {
|
||||
top: 0;
|
||||
bottom: initial;
|
||||
}
|
||||
|
||||
// mobile overrides
|
||||
@media(max-width:767px) {
|
||||
#seek-slider {
|
||||
height: 8px;
|
||||
.noUi-handle {
|
||||
opacity: 1 !important;
|
||||
}
|
||||
}
|
||||
#seek-slider {
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
.beat-option {
|
||||
text-align: center !important;
|
||||
}
|
||||
}
|
||||
56
mobile/app/styles/noui-slider.scss
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
.noUi-value-vertical {
|
||||
margin-top: -10px;
|
||||
transform: none;
|
||||
}
|
||||
|
||||
.noUi-value-vertical, .noUi-pips {
|
||||
color: inherit !important;
|
||||
}
|
||||
|
||||
.noUi-vertical .noUi-handle {
|
||||
border: 1px solid #A3A0A0;
|
||||
width: 26px;
|
||||
}
|
||||
|
||||
.noUi-vertical .noUi-handle:after, .noUi-vertical .noUi-handle:before{
|
||||
background: grey;
|
||||
}
|
||||
|
||||
.noUi-base {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.noUi-connect {
|
||||
background-color: $secondaryThemeColor;
|
||||
}
|
||||
|
||||
.noUi-handle {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.noUi-horizontal .noUi-handle {
|
||||
width: 0.4em;
|
||||
height: 1.3em;
|
||||
left: -0.071em;
|
||||
top: -0.550em;
|
||||
transition-duration: 0.1s;
|
||||
background: $playerDefaultIconColor !important;
|
||||
}
|
||||
|
||||
.noUi-horizontal .noUi-handle:hover {
|
||||
background: white !important;
|
||||
}
|
||||
|
||||
.noUi-target {
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.noUi-base {
|
||||
background-color: #ADADAD;
|
||||
border: 1px solid #797979;
|
||||
}
|
||||
|
||||
.noUi-vertical {
|
||||
height: 200px;
|
||||
margin: 15px auto 10px;
|
||||
}
|
||||
62
mobile/app/styles/paper.scss
Normal file
|
|
@ -0,0 +1,62 @@
|
|||
@import 'ember-paper';
|
||||
|
||||
.paper-icon {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
md-checkbox .md-icon, .md-off, .md-on {
|
||||
border-color: inherit !important;
|
||||
}
|
||||
|
||||
md-checkbox.md-default-theme.md-checked .md-icon {
|
||||
background: $secondaryThemeColor;
|
||||
}
|
||||
|
||||
md-checkbox .md-label {
|
||||
width: 125px;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.md-button {
|
||||
flex-direction: unset;
|
||||
span {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
md-switch[disabled=disabled], md-switch[disabled=disabled] .md-container, md-slider[disabled=disabled] {
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
md-progress-circular {
|
||||
margin: 0 auto 20px auto !important;
|
||||
}
|
||||
|
||||
md-progress-linear {
|
||||
margin-bottom: 50px !important;
|
||||
}
|
||||
|
||||
md-slider {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
md-slider.md-default-theme .md-thumb:after {
|
||||
border-color: $secondaryThemeColor;
|
||||
background-color: $secondaryThemeColor;
|
||||
}
|
||||
|
||||
md-icon {
|
||||
color: rgba(0, 0, 0, 0.54) !important;
|
||||
}
|
||||
|
||||
md-switch.md-default-theme.md-checked .md-thumb {
|
||||
background-color: $secondaryThemeColor;
|
||||
}
|
||||
|
||||
@media(max-width:500px) {
|
||||
md-checkbox .md-label {
|
||||
width: 100px;
|
||||
}
|
||||
}
|
||||
1
mobile/app/templates/application.hbs
Normal file
|
|
@ -0,0 +1 @@
|
|||
{{huegasm-app}}
|
||||
19
mobile/bower.json
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
{
|
||||
"name": "huegasm",
|
||||
"dependencies": {
|
||||
"HackTimer": "https://github.com/turuslan/HackTimer.git#~1.0.0",
|
||||
"JavaScript-ID3-Reader": "https://github.com/aadsm/JavaScript-ID3-Reader.git",
|
||||
"bootstrap-sass": "^3.3.5",
|
||||
"ember": "beta",
|
||||
"ember-cli-shims": "^0.1.0",
|
||||
"ember-load-initializers": "0.5.1",
|
||||
"ember-qunit-notifications": "0.1.0",
|
||||
"hammer.js": "^2.0.8",
|
||||
"intro.js": "^2.1.0",
|
||||
"jquery-mousewheel": "^3.1.13",
|
||||
"locallyjs": "^0.3.2",
|
||||
"matchMedia": "^0.3.0",
|
||||
"nouislider": "^9.0.0",
|
||||
"velocity": "^1.3.1"
|
||||
}
|
||||
}
|
||||
49
mobile/config/environment.js
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
/* jshint node: true */
|
||||
|
||||
module.exports = function(environment) {
|
||||
var ENV = {
|
||||
modulePrefix: 'huegasm',
|
||||
podModulePrefix: 'huegasm/pods',
|
||||
environment: environment,
|
||||
rootURL: '/',
|
||||
locationType: 'hash',
|
||||
ignoreFailures: false,
|
||||
EmberENV: {
|
||||
FEATURES: {
|
||||
// Here you can enable experimental features on an ember canary build
|
||||
// e.g. 'with-controller': true
|
||||
}
|
||||
},
|
||||
|
||||
APP: {
|
||||
// Here you can pass flags/options to your application instance
|
||||
// when it is created
|
||||
}
|
||||
};
|
||||
|
||||
if (environment === 'development') {
|
||||
ENV.ignoreFailures = true;
|
||||
// ENV.APP.LOG_RESOLVER = true;
|
||||
// ENV.APP.LOG_ACTIVE_GENERATION = true;
|
||||
// ENV.APP.LOG_TRANSITIONS = true;
|
||||
// ENV.APP.LOG_TRANSITIONS_INTERNAL = true;
|
||||
// ENV.APP.LOG_VIEW_LOOKUPS = true;
|
||||
}
|
||||
|
||||
if (environment === 'test') {
|
||||
// Testem prefers this...
|
||||
ENV.locationType = 'none';
|
||||
|
||||
// keep test console output quieter
|
||||
ENV.APP.LOG_ACTIVE_GENERATION = false;
|
||||
ENV.APP.LOG_VIEW_LOOKUPS = false;
|
||||
|
||||
ENV.APP.rootElement = '#ember-testing';
|
||||
}
|
||||
|
||||
if (environment === 'production') {
|
||||
|
||||
}
|
||||
|
||||
return ENV;
|
||||
};
|
||||
21
mobile/ember-cli-build.js
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
/* global require, module */
|
||||
var EmberApp = require('ember-cli/lib/broccoli/ember-app');
|
||||
|
||||
module.exports = function(defaults) {
|
||||
var app = new EmberApp(defaults);
|
||||
|
||||
app.import('vendor/dancer.js');
|
||||
|
||||
app.import('bower_components/bootstrap-sass/assets/javascripts/bootstrap/tooltip.js');
|
||||
app.import('bower_components/bootstrap-sass/assets/javascripts/bootstrap/dropdown.js');
|
||||
app.import('bower_components/bootstrap-sass/assets/javascripts/bootstrap/popover.js');
|
||||
app.import('bower_components/HackTimer/HackTimer.js');
|
||||
app.import('bower_components/intro.js/intro.js');
|
||||
app.import('bower_components/intro.js/introjs.css');
|
||||
app.import('bower_components/JavaScript-ID3-Reader/dist/id3-minimized.js');
|
||||
app.import('bower_components/jquery-mousewheel/jquery.mousewheel.js');
|
||||
app.import('bower_components/locallyjs/dist/locally.min.js');
|
||||
app.import('bower_components/velocity/velocity.js');
|
||||
|
||||
return app.toTree();
|
||||
};
|
||||
26
mobile/ember-cordova/cordova/config.xml
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
<?xml version='1.0' encoding='utf-8'?>
|
||||
<widget id="huegasm_mobile" version="1.0.0" xmlns="http://www.w3.org/ns/widgets" xmlns:cdv="http://cordova.apache.org/ns/1.0">
|
||||
<name>huegasm_mobile</name>
|
||||
<description>
|
||||
A sample Apache Cordova application that responds to the deviceready event.
|
||||
</description>
|
||||
<author email="dev@cordova.apache.org" href="http://cordova.io">
|
||||
Apache Cordova Team
|
||||
</author>
|
||||
<content src="index.html" />
|
||||
<plugin name="cordova-plugin-whitelist" spec="1" />
|
||||
<access origin="*" />
|
||||
<allow-intent href="http://*/*" />
|
||||
<allow-intent href="https://*/*" />
|
||||
<allow-intent href="tel:*" />
|
||||
<allow-intent href="sms:*" />
|
||||
<allow-intent href="mailto:*" />
|
||||
<allow-intent href="geo:*" />
|
||||
<platform name="android">
|
||||
<allow-intent href="market:*" />
|
||||
</platform>
|
||||
<platform name="ios">
|
||||
<allow-intent href="itms:*" />
|
||||
<allow-intent href="itms-apps:*" />
|
||||
</platform>
|
||||
</widget>
|
||||
23
mobile/ember-cordova/cordova/hooks/README.md
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
<!--
|
||||
#
|
||||
# Licensed to the Apache Software Foundation (ASF) under one
|
||||
# or more contributor license agreements. See the NOTICE file
|
||||
# distributed with this work for additional information
|
||||
# regarding copyright ownership. The ASF licenses this file
|
||||
# to you under the Apache License, Version 2.0 (the
|
||||
# "License"); you may not use this file except in compliance
|
||||
# with the License. You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing,
|
||||
# software distributed under the License is distributed on an
|
||||
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
# KIND, either express or implied. See the License for the
|
||||
# specific language governing permissions and limitations
|
||||
# under the License.
|
||||
#
|
||||
-->
|
||||
# Cordova Hooks
|
||||
|
||||
Cordova Hooks represent special scripts which could be added by application and plugin developers or even by your own build system to customize cordova commands. See Hooks Guide for more details: http://cordova.apache.org/docs/en/edge/guide_appdev_hooks_index.md.html#Hooks%20Guide.
|
||||
0
mobile/ember-cordova/cordova/platforms/.gitkeep
Normal file
0
mobile/ember-cordova/cordova/plugins/.gitkeep
Normal file
0
mobile/ember-cordova/cordova/www/.gitkeep
Normal file
47
mobile/package.json
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
{
|
||||
"name": "huegasm_mobile",
|
||||
"version": "1.0.0",
|
||||
"description": "Huegasm is a free web application for managing and synchronizing your Philips Hue lights with the beat of your music.",
|
||||
"private": true,
|
||||
"directories": {
|
||||
"doc": "doc",
|
||||
"test": "tests"
|
||||
},
|
||||
"scripts": {
|
||||
"start": "ember server",
|
||||
"build": "ember build",
|
||||
"test": "ember test"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.10.0"
|
||||
},
|
||||
"author": "Egor Philippov",
|
||||
"license": "MIT",
|
||||
"devDependencies": {
|
||||
"broccoli-asset-rev": "^2.2.0",
|
||||
"ember-ajax": "^2.0.1",
|
||||
"ember-cli": "^2.8.0",
|
||||
"ember-cli-app-version": "^2.0.0",
|
||||
"ember-cli-babel": "^5.1.5",
|
||||
"ember-cli-dependency-checker": "^1.2.0",
|
||||
"ember-cli-htmlbars": "^1.0.1",
|
||||
"ember-cli-htmlbars-inline-precompile": "^0.3.1",
|
||||
"ember-cli-inject-live-reload": "^1.3.1",
|
||||
"ember-cli-nouislider": "^0.10.0",
|
||||
"ember-cli-qunit": "^1.2.1",
|
||||
"ember-cli-release": "0.2.8",
|
||||
"ember-cli-sass": "^5.2.1",
|
||||
"ember-cli-sri": "^2.1.0",
|
||||
"ember-cli-test-loader": "^1.1.0",
|
||||
"ember-cli-uglify": "^1.2.0",
|
||||
"ember-cordova": "0.3.5",
|
||||
"ember-export-application-global": "^1.0.4",
|
||||
"ember-load-initializers": "^0.5.0",
|
||||
"ember-modal-dialog": "^0.9.0",
|
||||
"ember-notify": "^5.0.4",
|
||||
"ember-paper": "^1.0.0-alpha.5",
|
||||
"ember-resolver": "^2.0.3",
|
||||
"ember-truth-helpers": "^1.2.0",
|
||||
"loader.js": "^4.0.7"
|
||||
}
|
||||
}
|
||||
BIN
mobile/public/android-chrome-192x192.png
Normal file
|
After Width: | Height: | Size: 5.5 KiB |
BIN
mobile/public/android-chrome-512x512.png
Normal file
|
After Width: | Height: | Size: 14 KiB |
BIN
mobile/public/apple-touch-icon.png
Normal file
|
After Width: | Height: | Size: 4.2 KiB |
BIN
mobile/public/assets/images/colormap.png
Normal file
|
After Width: | Height: | Size: 6.2 KiB |
32
mobile/public/assets/images/lights/a19.svg
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 16.0.4, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" width="500px"
|
||||
height="500px" viewBox="0 0 500 500" enable-background="new 0 0 500 500" xml:space="preserve">
|
||||
<g id="Layer_1" display="none">
|
||||
<rect display="inline" width="500" height="500"/>
|
||||
</g>
|
||||
<g id="Layer_2">
|
||||
<g>
|
||||
<polyline fill="none" stroke="#000000" stroke-width="4.8" stroke-miterlimit="10" points="341.939,163.139 247.72,163.139
|
||||
246.876,163.139 158.423,163.139 "/>
|
||||
<polyline fill="none" stroke="#000000" stroke-width="4.8" stroke-miterlimit="10" points="300.502,364.005 248.822,364.005
|
||||
248.376,364.005 199.884,364.005 "/>
|
||||
|
||||
<polyline fill="none" stroke="#000000" stroke-width="4.8" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="10" points="
|
||||
304.533,379.239 248.705,384.044 248.212,384.067 195.853,388.567 "/>
|
||||
|
||||
<polyline fill="none" stroke="#000000" stroke-width="4.8" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="10" points="
|
||||
305.307,395.106 249.525,399.888 249.033,399.958 196.673,404.435 "/>
|
||||
|
||||
<polyline fill="none" stroke="#000000" stroke-width="4.8" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="10" points="
|
||||
304.533,410.974 248.705,415.755 248.212,415.802 195.853,420.302 "/>
|
||||
<path fill="none" stroke="#000000" stroke-width="4.8" stroke-miterlimit="10" d="M342.314,163.139
|
||||
c0,0,12.234-21.234,26.039-61.078c4.781-14.344,6.398-32.953-19.664-45.703c-22.734-11.133-69.75-16.969-97.922-16.477
|
||||
c-28.219-0.492-75.234,5.344-97.969,16.477c-26.062,12.75-24.469,31.359-19.688,45.703c13.828,39.844,26.062,61.078,26.062,61.078
|
||||
c29.766,93.003,27.633,183.851,27.633,183.851l14.883,17.016c0,0,0,55.781,0,63.211s11.672,12.773,20.695,14.883
|
||||
c1.594,3.188,6.398,8.508,11.695,14.344c7.125,2.391,26.204,2.391,33.329,0c5.297-5.836,10.078-11.156,11.672-14.344
|
||||
c9.047-2.109,20.742-7.453,20.742-14.883s0-63.211,0-63.211l14.883-17.016C314.705,346.989,312.549,256.142,342.314,163.139z"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.2 KiB |
32
mobile/public/assets/images/lights/a19w.svg
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 16.0.4, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" width="500px"
|
||||
height="500px" viewBox="0 0 500 500" enable-background="new 0 0 500 500" xml:space="preserve">
|
||||
<g id="Layer_1" display="none">
|
||||
<rect display="inline" width="500" height="500"/>
|
||||
</g>
|
||||
<g id="Layer_2">
|
||||
<g>
|
||||
<polyline fill="none" stroke="#FFFFFF" stroke-width="4.8" stroke-miterlimit="10" points="341.939,163.139 247.72,163.139
|
||||
246.876,163.139 158.423,163.139 "/>
|
||||
<polyline fill="none" stroke="#FFFFFF" stroke-width="4.8" stroke-miterlimit="10" points="300.502,364.005 248.822,364.005
|
||||
248.376,364.005 199.884,364.005 "/>
|
||||
|
||||
<polyline fill="none" stroke="#FFFFFF" stroke-width="4.8" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="10" points="
|
||||
304.533,379.239 248.705,384.044 248.212,384.067 195.853,388.567 "/>
|
||||
|
||||
<polyline fill="none" stroke="#FFFFFF" stroke-width="4.8" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="10" points="
|
||||
305.307,395.106 249.525,399.888 249.033,399.958 196.673,404.435 "/>
|
||||
|
||||
<polyline fill="none" stroke="#FFFFFF" stroke-width="4.8" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="10" points="
|
||||
304.533,410.974 248.705,415.755 248.212,415.802 195.853,420.302 "/>
|
||||
<path fill="none" stroke="#FFFFFF" stroke-width="4.8" stroke-miterlimit="10" d="M342.314,163.139
|
||||
c0,0,12.234-21.234,26.039-61.078c4.781-14.344,6.398-32.953-19.664-45.703c-22.734-11.133-69.75-16.969-97.922-16.477
|
||||
c-28.219-0.492-75.234,5.344-97.969,16.477c-26.062,12.75-24.469,31.359-19.688,45.703c13.828,39.844,26.062,61.078,26.062,61.078
|
||||
c29.766,93.003,27.633,183.851,27.633,183.851l14.883,17.016c0,0,0,55.781,0,63.211s11.672,12.773,20.695,14.883
|
||||
c1.594,3.188,6.398,8.508,11.695,14.344c7.125,2.391,26.204,2.391,33.329,0c5.297-5.836,10.078-11.156,11.672-14.344
|
||||
c9.047-2.109,20.742-7.453,20.742-14.883s0-63.211,0-63.211l14.883-17.016C314.705,346.989,312.549,256.142,342.314,163.139z"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.2 KiB |
48
mobile/public/assets/images/lights/br30.svg
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 16.0.4, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" width="500px"
|
||||
height="500px" viewBox="0 0 500 500" enable-background="new 0 0 500 500" xml:space="preserve">
|
||||
<g id="Layer_1" display="none">
|
||||
<rect display="inline" width="500" height="500"/>
|
||||
</g>
|
||||
<g id="Layer_2">
|
||||
<g>
|
||||
<path fill="none" stroke="#000000" stroke-width="4.8" stroke-miterlimit="10" d="M276.748,445.245c0,0-4.406,6.281-6,8.625
|
||||
c-1.57,2.344-3.047,9.586-20.602,9h0.023c-17.555,0.586-18.984-6.656-20.578-9c-1.547-2.344-7.078-8.273-7.078-8.273"/>
|
||||
<path fill="none" stroke="#000000" stroke-width="4.8" stroke-miterlimit="10" d="M250.17,377.042c0,0,36.844,0.422,40.969-3.188
|
||||
c4.383-3.844,5.25-15.539,5.25-15.539s3.516-82.336,17.391-114.746c12.656-29.695,36.188-58.852,36.188-58.852
|
||||
s24.234-4.711,35.25-34.945c11.016-30.211,12.094-47.367,12.094-47.367H247.709H101.436c0,0,1.125,17.156,12.117,47.367
|
||||
c11.016,30.234,35.297,34.945,35.297,34.945s23.484,29.156,36.164,58.852c13.875,32.41,17.391,114.746,17.391,114.746
|
||||
s0.891,11.695,5.297,15.539C211.779,377.464,250.17,377.042,250.17,377.042z"/>
|
||||
<path fill="none" stroke="#000000" stroke-width="4.8" stroke-miterlimit="10" d="M98.881,87.405h302.273
|
||||
c0,0,8.531-54.352-151.734-54.352S98.881,87.405,98.881,87.405z"/>
|
||||
<polyline fill="none" stroke="#000000" stroke-width="4.8" stroke-miterlimit="10" points="113.951,60.265 246.654,60.265
|
||||
385.381,60.265 "/>
|
||||
<path fill="none" stroke="#000000" stroke-width="4.8" stroke-miterlimit="10" d="M148.029,184.858
|
||||
c0,0,52.711,8.648,101.367,8.648c48.633,0,101.391-8.648,101.391-8.648"/>
|
||||
<path fill="none" stroke="#000000" stroke-width="4.8" stroke-miterlimit="10" d="M207.396,386.815"/>
|
||||
<path fill="none" stroke="#000000" stroke-width="4.8" stroke-miterlimit="10" d="M206.646,386.136"/>
|
||||
<path fill="none" stroke="#000000" stroke-width="4.8" stroke-miterlimit="10" d="M209.154,424.784
|
||||
c1.125,0.984,2.203,5.367,0.094,7.195c-2.133,1.758,0,3.164,0,3.164c0.117,1.992,18.914,2.414,41.953,0.938
|
||||
c21.609-1.383,39.258-4.008,41.273-5.977c0,0,1.875-2.578,0-3.797c-1.852-1.242-4.172-4.289-2.812-6.844"/>
|
||||
<path fill="none" stroke="#000000" stroke-width="4.8" stroke-miterlimit="10" d="M210.795,412.573
|
||||
c0.281,2.438,1.547,5.062-3.188,8.227c-2.344,1.523,0,3.164,0,3.164c4.734,2.344,18.891,2.438,41.953,0.938
|
||||
c21.562-1.383,39.258-3.984,41.297-5.953c0,0,1.828-2.578,0-3.797c-1.875-1.266-3.094-4.219-2.859-6.867"/>
|
||||
<path fill="none" stroke="#000000" stroke-width="4.8" stroke-miterlimit="10" d="M210.795,400.737
|
||||
c0.281,2.461,1.547,5.086-3.188,8.227c-2.344,1.523,0,3.188,0,3.188c4.734,2.32,18.891,2.391,41.953,0.938
|
||||
c21.562-1.406,39.258-4.008,41.297-6c0,0,1.828-2.531,0-3.773c-1.875-1.242-3.094-4.219-2.859-6.891"/>
|
||||
<path fill="none" stroke="#000000" stroke-width="4.8" stroke-miterlimit="10" d="M210.795,388.597
|
||||
c0.281,2.461,1.547,5.086-3.188,8.203c-2.344,1.523,0,3.211,0,3.211c4.734,2.297,18.891,2.438,41.953,0.938
|
||||
c21.562-1.406,39.258-3.984,41.297-5.977c0,0,1.828-2.555,0-3.82c-1.875-1.219-3.094-4.195-2.859-6.844"/>
|
||||
<path fill="none" stroke="#000000" stroke-width="4.8" stroke-miterlimit="10" d="M212.576,436.995c0,0,1.594,7.312,12.141,9.047
|
||||
c10.547,1.781,17.672,1.359,24.375,1.359s13.828,0.188,24.422-1.359c12.047-1.734,14.812-14.391,14.812-14.391"/>
|
||||
<path fill="none" stroke="#000000" stroke-width="4.8" stroke-miterlimit="10" d="M290.998,375.964c0,0,0,6.727-1.008,7.711
|
||||
c-1.008,1.031-3.352,1.242-3.352,1.242s-74.859,5.484-78.328,2.836c-2.062-1.523-1.758-11.789-1.758-11.789"/>
|
||||
|
||||
<line fill="none" stroke="#000000" stroke-width="4.8" stroke-miterlimit="10" x1="137.436" y1="87.405" x2="143.365" y2="100.483"/>
|
||||
|
||||
<line fill="none" stroke="#000000" stroke-width="4.8" stroke-miterlimit="10" x1="361.732" y1="87.405" x2="355.826" y2="100.483"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 4 KiB |
48
mobile/public/assets/images/lights/br30w.svg
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 16.0.4, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" width="500px"
|
||||
height="500px" viewBox="0 0 500 500" enable-background="new 0 0 500 500" xml:space="preserve">
|
||||
<g id="Layer_1" display="none">
|
||||
<rect display="inline" width="500" height="500"/>
|
||||
</g>
|
||||
<g id="Layer_2">
|
||||
<g>
|
||||
<path fill="none" stroke="#FFFFFF" stroke-width="4.8" stroke-miterlimit="10" d="M276.748,445.245c0,0-4.406,6.281-6,8.625
|
||||
c-1.57,2.344-3.047,9.586-20.602,9h0.023c-17.555,0.586-18.984-6.656-20.578-9c-1.547-2.344-7.078-8.273-7.078-8.273"/>
|
||||
<path fill="none" stroke="#FFFFFF" stroke-width="4.8" stroke-miterlimit="10" d="M250.17,377.042c0,0,36.844,0.422,40.969-3.188
|
||||
c4.383-3.844,5.25-15.539,5.25-15.539s3.516-82.336,17.391-114.746c12.656-29.695,36.188-58.852,36.188-58.852
|
||||
s24.234-4.711,35.25-34.945c11.016-30.211,12.094-47.367,12.094-47.367H247.709H101.436c0,0,1.125,17.156,12.117,47.367
|
||||
c11.016,30.234,35.297,34.945,35.297,34.945s23.484,29.156,36.164,58.852c13.875,32.41,17.391,114.746,17.391,114.746
|
||||
s0.891,11.695,5.297,15.539C211.779,377.464,250.17,377.042,250.17,377.042z"/>
|
||||
<path fill="none" stroke="#FFFFFF" stroke-width="4.8" stroke-miterlimit="10" d="M98.881,87.405h302.273
|
||||
c0,0,8.531-54.352-151.734-54.352S98.881,87.405,98.881,87.405z"/>
|
||||
<polyline fill="none" stroke="#FFFFFF" stroke-width="4.8" stroke-miterlimit="10" points="113.951,60.265 246.654,60.265
|
||||
385.381,60.265 "/>
|
||||
<path fill="none" stroke="#FFFFFF" stroke-width="4.8" stroke-miterlimit="10" d="M148.029,184.858
|
||||
c0,0,52.711,8.648,101.367,8.648c48.633,0,101.391-8.648,101.391-8.648"/>
|
||||
<path fill="none" stroke="#FFFFFF" stroke-width="4.8" stroke-miterlimit="10" d="M207.396,386.815"/>
|
||||
<path fill="none" stroke="#FFFFFF" stroke-width="4.8" stroke-miterlimit="10" d="M206.646,386.136"/>
|
||||
<path fill="none" stroke="#FFFFFF" stroke-width="4.8" stroke-miterlimit="10" d="M209.154,424.784
|
||||
c1.125,0.984,2.203,5.367,0.094,7.195c-2.133,1.758,0,3.164,0,3.164c0.117,1.992,18.914,2.414,41.953,0.938
|
||||
c21.609-1.383,39.258-4.008,41.273-5.977c0,0,1.875-2.578,0-3.797c-1.852-1.242-4.172-4.289-2.812-6.844"/>
|
||||
<path fill="none" stroke="#FFFFFF" stroke-width="4.8" stroke-miterlimit="10" d="M210.795,412.573
|
||||
c0.281,2.438,1.547,5.062-3.188,8.227c-2.344,1.523,0,3.164,0,3.164c4.734,2.344,18.891,2.438,41.953,0.938
|
||||
c21.562-1.383,39.258-3.984,41.297-5.953c0,0,1.828-2.578,0-3.797c-1.875-1.266-3.094-4.219-2.859-6.867"/>
|
||||
<path fill="none" stroke="#FFFFFF" stroke-width="4.8" stroke-miterlimit="10" d="M210.795,400.737
|
||||
c0.281,2.461,1.547,5.086-3.188,8.227c-2.344,1.523,0,3.188,0,3.188c4.734,2.32,18.891,2.391,41.953,0.938
|
||||
c21.562-1.406,39.258-4.008,41.297-6c0,0,1.828-2.531,0-3.773c-1.875-1.242-3.094-4.219-2.859-6.891"/>
|
||||
<path fill="none" stroke="#FFFFFF" stroke-width="4.8" stroke-miterlimit="10" d="M210.795,388.597
|
||||
c0.281,2.461,1.547,5.086-3.188,8.203c-2.344,1.523,0,3.211,0,3.211c4.734,2.297,18.891,2.438,41.953,0.938
|
||||
c21.562-1.406,39.258-3.984,41.297-5.977c0,0,1.828-2.555,0-3.82c-1.875-1.219-3.094-4.195-2.859-6.844"/>
|
||||
<path fill="none" stroke="#FFFFFF" stroke-width="4.8" stroke-miterlimit="10" d="M212.576,436.995c0,0,1.594,7.312,12.141,9.047
|
||||
c10.547,1.781,17.672,1.359,24.375,1.359s13.828,0.188,24.422-1.359c12.047-1.734,14.812-14.391,14.812-14.391"/>
|
||||
<path fill="none" stroke="#FFFFFF" stroke-width="4.8" stroke-miterlimit="10" d="M290.998,375.964c0,0,0,6.727-1.008,7.711
|
||||
c-1.008,1.031-3.352,1.242-3.352,1.242s-74.859,5.484-78.328,2.836c-2.062-1.523-1.758-11.789-1.758-11.789"/>
|
||||
|
||||
<line fill="none" stroke="#FFFFFF" stroke-width="4.8" stroke-miterlimit="10" x1="137.436" y1="87.405" x2="143.365" y2="100.483"/>
|
||||
|
||||
<line fill="none" stroke="#FFFFFF" stroke-width="4.8" stroke-miterlimit="10" x1="361.732" y1="87.405" x2="355.826" y2="100.483"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 4 KiB |
18
mobile/public/assets/images/lights/gu10.svg
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 16.0.4, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" width="500px"
|
||||
height="500px" viewBox="0 0 500 500" enable-background="new 0 0 500 500" xml:space="preserve">
|
||||
<g id="Layer_1" display="none">
|
||||
<rect display="inline" width="500" height="500"/>
|
||||
</g>
|
||||
<g id="Layer_2">
|
||||
<path fill="none" stroke="#000000" stroke-width="4.8" stroke-miterlimit="10" d="M251.452,55.93
|
||||
c-4.196,0-88.923-0.258-129.892,8.508v12c0,0,3.984,3.492,10.992,4.992c0,0,0.516,95.555,13.008,134.555
|
||||
c12.516,39,18,40.523,27.516,50.015c9.516,9.516,20.508,20.016,20.508,40.5c0,20.531,0,84.047,0,84.047l23.508,19.5h3v14.508
|
||||
h-6.984v15.516h26.484v-15.516h-6v-15.023h17.672h0.353h17.649v15.023h-6v15.516h26.531v-15.516h-7.031v-14.508h3l23.531-19.5
|
||||
c0,0,0-63.516,0-84.047c0-20.484,10.992-30.984,20.484-40.5c9.516-9.492,15.023-11.016,27.516-50.015
|
||||
c12.516-39,13.031-134.555,13.031-134.555c6.984-1.5,10.992-4.992,10.992-4.992v-12C340.352,55.672,255.625,55.93,251.452,55.93z"
|
||||
/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.2 KiB |
18
mobile/public/assets/images/lights/gu10w.svg
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 16.0.4, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" width="500px"
|
||||
height="500px" viewBox="0 0 500 500" enable-background="new 0 0 500 500" xml:space="preserve">
|
||||
<g id="Layer_1" display="none">
|
||||
<rect display="inline" width="500" height="500"/>
|
||||
</g>
|
||||
<g id="Layer_2">
|
||||
<path fill="none" stroke="#FFFFFF" stroke-width="4.8" stroke-miterlimit="10" d="M251.452,55.93
|
||||
c-4.196,0-88.923-0.258-129.892,8.508v12c0,0,3.984,3.492,10.992,4.992c0,0,0.516,95.555,13.008,134.555
|
||||
c12.516,39,18,40.523,27.516,50.015c9.516,9.516,20.508,20.016,20.508,40.5c0,20.531,0,84.047,0,84.047l23.508,19.5h3v14.508
|
||||
h-6.984v15.516h26.484v-15.516h-6v-15.023h17.672h0.353h17.649v15.023h-6v15.516h26.531v-15.516h-7.031v-14.508h3l23.531-19.5
|
||||
c0,0,0-63.516,0-84.047c0-20.484,10.992-30.984,20.484-40.5c9.516-9.492,15.023-11.016,27.516-50.015
|
||||
c12.516-39,13.031-134.555,13.031-134.555c6.984-1.5,10.992-4.992,10.992-4.992v-12C340.352,55.672,255.625,55.93,251.452,55.93z"
|
||||
/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.2 KiB |
23
mobile/public/assets/images/lights/huego.svg
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 16.0.4, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" width="500px"
|
||||
height="500px" viewBox="0 0 500 500" enable-background="new 0 0 500 500" xml:space="preserve">
|
||||
<g id="Layer_1" display="none">
|
||||
<rect display="inline" width="500" height="500"/>
|
||||
</g>
|
||||
<g id="Layer_2">
|
||||
<g>
|
||||
<path fill="none" stroke="#000000" stroke-width="4.8" stroke-linecap="round" stroke-miterlimit="10" d="M185.782,359.98
|
||||
c-4.172,5.203-10.594,8.531-17.766,8.531c-12.562,0-22.734-10.172-22.734-22.734c0-1.922,0.234-3.797,0.703-5.578"/>
|
||||
<path fill="none" stroke="#000000" stroke-width="4.8" stroke-linecap="round" stroke-miterlimit="10" d="M444.813,174.026
|
||||
c0,106.829-86.625,197.345-193.453,197.345c-38.86,0-75.047-11.438-105.375-31.172c-53.016-34.5-88.078-99.423-88.078-167.392"/>
|
||||
<path fill="none" stroke="#000000" stroke-width="4.8" stroke-miterlimit="10" d="M251.501,126.073
|
||||
c106.828,0.328,193.359,21.797,193.312,47.953c-0.094,26.062-86.766,47.016-193.594,46.688
|
||||
c-106.829-0.281-193.36-21.75-193.313-47.906C58,146.698,144.625,125.745,251.501,126.073z"/>
|
||||
<path fill="none" stroke="#000000" stroke-width="4.8" stroke-miterlimit="10" d="M251.454,131.417
|
||||
c98.203,0.281,177.75,16.969,177.703,37.266c-0.141,20.344-79.734,36.609-177.891,36.328
|
||||
c-98.204-0.281-177.75-16.969-177.704-37.312C73.61,147.401,153.297,131.089,251.454,131.417z"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.6 KiB |
23
mobile/public/assets/images/lights/huegow.svg
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 16.0.4, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" width="500px"
|
||||
height="500px" viewBox="0 0 500 500" enable-background="new 0 0 500 500" xml:space="preserve">
|
||||
<g id="Layer_1" display="none">
|
||||
<rect display="inline" width="500" height="500"/>
|
||||
</g>
|
||||
<g id="Layer_2">
|
||||
<g>
|
||||
<path fill="none" stroke="#FFFFFF" stroke-width="4.8" stroke-linecap="round" stroke-miterlimit="10" d="M185.782,359.98
|
||||
c-4.172,5.203-10.594,8.531-17.766,8.531c-12.562,0-22.734-10.172-22.734-22.734c0-1.922,0.234-3.797,0.703-5.578"/>
|
||||
<path fill="none" stroke="#FFFFFF" stroke-width="4.8" stroke-linecap="round" stroke-miterlimit="10" d="M444.813,174.026
|
||||
c0,106.829-86.625,197.345-193.453,197.345c-38.86,0-75.047-11.438-105.375-31.172c-53.016-34.5-88.078-99.423-88.078-167.392"/>
|
||||
<path fill="none" stroke="#FFFFFF" stroke-width="4.8" stroke-miterlimit="10" d="M251.501,126.073
|
||||
c106.828,0.328,193.359,21.797,193.312,47.953c-0.094,26.062-86.766,47.016-193.594,46.688
|
||||
c-106.829-0.281-193.36-21.75-193.313-47.906C58,146.698,144.625,125.745,251.501,126.073z"/>
|
||||
<path fill="none" stroke="#FFFFFF" stroke-width="4.8" stroke-miterlimit="10" d="M251.454,131.417
|
||||
c98.203,0.281,177.75,16.969,177.703,37.266c-0.141,20.344-79.734,36.609-177.891,36.328
|
||||
c-98.204-0.281-177.75-16.969-177.704-37.312C73.61,147.401,153.297,131.089,251.454,131.417z"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.6 KiB |
32
mobile/public/assets/images/lights/lc_aura.svg
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 16.0.4, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" width="500px"
|
||||
height="500px" viewBox="0 0 500 500" enable-background="new 0 0 500 500" xml:space="preserve">
|
||||
<g id="Layer_1" display="none">
|
||||
<rect display="inline" width="500" height="500"/>
|
||||
</g>
|
||||
<g id="Layer_2">
|
||||
<g>
|
||||
<path fill="none" stroke="#000000" stroke-width="4.8" stroke-miterlimit="10" d="M114.314,82.195
|
||||
c26.32-1.031,66.891,15.984,120.938,54.375c18,12.844,85.828,65.906,132.093,124.313c36.234,44.25,72.211,104.344,53.719,129.469
|
||||
c-4.453,5.719-18.516,15.703-67.359-5.438c-48.891-21.094-105-65.766-139.148-97.641c-28.266-25.266-70.43-69.891-105.023-126.141
|
||||
C94.58,134.742,72.97,83.695,114.314,82.195z"/>
|
||||
<path fill="none" stroke="#000000" stroke-width="4.8" stroke-miterlimit="10" d="M421.063,390.352
|
||||
c-4.453,5.719-18.516,15.703-67.359-5.438c-48.891-21.094-105-65.766-139.148-97.641c-28.266-25.266-70.43-69.891-105.023-126.141
|
||||
c-11.695-20.625-27.422-56.344-14.062-71.625c-8.18,9.328-8.531,21.75-2.789,44.812c5.789,23.109,18.586,78.75,20.766,89.578
|
||||
c2.203,10.734,18.211,59.907,13.945,99.329c-3.141,22.312-0.281,35.625,9.938,44.859c10.219,9.281,13.125,12.094,57,7.734
|
||||
c21.938-2.25,29.438-2.906,91.593,7.312c53.766,9.188,94.312,16.734,106.148,16.453S413.376,399.117,421.063,390.352z"/>
|
||||
<path fill="none" stroke="#000000" stroke-width="4.8" stroke-miterlimit="10" d="M145.392,119.929
|
||||
c20.203-0.797,51.328,12.328,92.766,41.672c13.781,9.891,65.789,50.578,101.273,95.391
|
||||
c27.773,33.891,56.789,83.016,42.633,102.328c-3.422,4.312-19.031,16.734-56.484,0.516c-37.477-16.125-84.281-54-110.46-78.422
|
||||
c-21.68-19.406-54.258-53.954-80.789-97.079C122.892,164.132,113.705,121.054,145.392,119.929z"/>
|
||||
<path fill="none" stroke="#000000" stroke-width="4.8" stroke-miterlimit="10" d="M192.267,185.601
|
||||
c10.406-0.422,26.484,6.422,47.953,21.75c7.125,5.156,34.007,26.391,52.382,49.735c14.367,17.672,29.391,43.266,22.055,53.297
|
||||
c-1.781,2.25-9.844,8.719-29.203,0.328c-19.383-8.438-43.593-28.219-57.14-40.969c-11.203-10.125-28.055-28.079-41.789-50.579
|
||||
C180.595,208.617,175.861,186.164,192.267,185.601z"/>
|
||||
<path fill="none" stroke="#000000" stroke-width="4.8" stroke-miterlimit="10" d="M114.08,226.57
|
||||
c10.477,21.047,24,50.344,68.906,92.344c44.906,42.047,81.609,57.703,99.07,63.562"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.5 KiB |
32
mobile/public/assets/images/lights/lc_auraw.svg
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 16.0.4, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" width="500px"
|
||||
height="500px" viewBox="0 0 500 500" enable-background="new 0 0 500 500" xml:space="preserve">
|
||||
<g id="Layer_1" display="none">
|
||||
<rect display="inline" width="500" height="500"/>
|
||||
</g>
|
||||
<g id="Layer_2">
|
||||
<g>
|
||||
<path fill="none" stroke="#FFFFFF" stroke-width="4.8" stroke-miterlimit="10" d="M114.314,82.195
|
||||
c26.32-1.031,66.891,15.984,120.938,54.375c18,12.844,85.828,65.906,132.093,124.313c36.234,44.25,72.211,104.344,53.719,129.469
|
||||
c-4.453,5.719-18.516,15.703-67.359-5.438c-48.891-21.094-105-65.766-139.148-97.641c-28.266-25.266-70.43-69.891-105.023-126.141
|
||||
C94.58,134.742,72.97,83.695,114.314,82.195z"/>
|
||||
<path fill="none" stroke="#FFFFFF" stroke-width="4.8" stroke-miterlimit="10" d="M421.063,390.352
|
||||
c-4.453,5.719-18.516,15.703-67.359-5.438c-48.891-21.094-105-65.766-139.148-97.641c-28.266-25.266-70.43-69.891-105.023-126.141
|
||||
c-11.695-20.625-27.422-56.344-14.062-71.625c-8.18,9.328-8.531,21.75-2.789,44.812c5.789,23.109,18.586,78.75,20.766,89.578
|
||||
c2.203,10.734,18.211,59.907,13.945,99.329c-3.141,22.312-0.281,35.625,9.938,44.859c10.219,9.281,13.125,12.094,57,7.734
|
||||
c21.938-2.25,29.438-2.906,91.593,7.312c53.766,9.188,94.312,16.734,106.148,16.453S413.376,399.117,421.063,390.352z"/>
|
||||
<path fill="none" stroke="#FFFFFF" stroke-width="4.8" stroke-miterlimit="10" d="M145.392,119.929
|
||||
c20.203-0.797,51.328,12.328,92.766,41.672c13.781,9.891,65.789,50.578,101.273,95.391
|
||||
c27.773,33.891,56.789,83.016,42.633,102.328c-3.422,4.312-19.031,16.734-56.484,0.516c-37.477-16.125-84.281-54-110.46-78.422
|
||||
c-21.68-19.406-54.258-53.954-80.789-97.079C122.892,164.132,113.705,121.054,145.392,119.929z"/>
|
||||
<path fill="none" stroke="#FFFFFF" stroke-width="4.8" stroke-miterlimit="10" d="M192.267,185.601
|
||||
c10.406-0.422,26.484,6.422,47.953,21.75c7.125,5.156,34.007,26.391,52.382,49.735c14.367,17.672,29.391,43.266,22.055,53.297
|
||||
c-1.781,2.25-9.844,8.719-29.203,0.328c-19.383-8.438-43.593-28.219-57.14-40.969c-11.203-10.125-28.055-28.079-41.789-50.579
|
||||
C180.595,208.617,175.861,186.164,192.267,185.601z"/>
|
||||
<path fill="none" stroke="#FFFFFF" stroke-width="4.8" stroke-miterlimit="10" d="M114.08,226.57
|
||||
c10.477,21.047,24,50.344,68.906,92.344c44.906,42.047,81.609,57.703,99.07,63.562"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.5 KiB |
30
mobile/public/assets/images/lights/lc_bloom.svg
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 16.0.4, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" width="500px"
|
||||
height="500px" viewBox="0 0 500 500" enable-background="new 0 0 500 500" xml:space="preserve">
|
||||
<g id="Layer_1" display="none">
|
||||
<rect display="inline" width="500" height="500"/>
|
||||
</g>
|
||||
<g id="Layer_2">
|
||||
<g>
|
||||
<path fill="none" stroke="#000000" stroke-width="4.8" stroke-miterlimit="10" d="M287.609,219.602
|
||||
c32.25,29.578,49.617,63.141,38.719,75c-10.852,11.859-45.844-2.484-78.091-32.109c-32.273-29.578-49.594-63.188-38.719-75.094
|
||||
C220.393,175.539,255.338,189.93,287.609,219.602z"/>
|
||||
<path fill="none" stroke="#000000" stroke-width="4.8" stroke-miterlimit="10" d="M299.422,203.102
|
||||
c54.656,50.203,84.516,106.594,66.656,126.094c-17.859,19.453-76.641-5.344-131.341-55.5
|
||||
c-54.656-50.109-84.516-106.641-66.656-126.094C185.94,128.102,244.721,152.945,299.422,203.102z"/>
|
||||
<path fill="none" stroke="#000000" stroke-width="4.8" stroke-miterlimit="10" d="M313.953,187.445
|
||||
c75.984,69.703,117.422,148.125,92.625,175.219c-24.844,27.047-106.547-7.453-182.529-77.156
|
||||
c-75.961-69.656-117.445-148.125-92.625-175.219C156.245,83.242,237.948,117.789,313.953,187.445z"/>
|
||||
<path fill="none" stroke="#000000" stroke-width="4.8" stroke-miterlimit="10" d="M149.752,298.773
|
||||
c0,0-39.656-40.125-45.422-108.609c-3.211-33.938,12.844-87.844,37.078-92.156c24.305-4.312,38.391,2.203,61.406,11.203
|
||||
c0,0,44.016,19.078,100.263,66.047c56.203,46.969,94.5,102.891,111.844,146.156c1.969,4.828,12.469,28.781,0.188,45.656
|
||||
c-3.492,4.781-17.906,15-22.594,16.969c-5.719,2.391-20.578,11.625-57.234,15.656c0,0-45.422,6.422-104.623-29.719
|
||||
c0,0-3.844,7.031-6.047,10.219c-2.25,3.188-48.633,11.859-87,8.016c-38.391-3.844-55.031-13.781-55.031-13.781s-1.266,0,0-2.578
|
||||
C83.846,369.32,149.752,298.773,149.752,298.773z"/>
|
||||
<path fill="none" stroke="#000000" stroke-width="4.8" stroke-miterlimit="10" d="M149.752,298.773
|
||||
c0,0,39.656,48.609,80.906,71.203"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.2 KiB |
30
mobile/public/assets/images/lights/lc_bloomw.svg
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 16.0.4, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" width="500px"
|
||||
height="500px" viewBox="0 0 500 500" enable-background="new 0 0 500 500" xml:space="preserve">
|
||||
<g id="Layer_1" display="none">
|
||||
<rect display="inline" width="500" height="500"/>
|
||||
</g>
|
||||
<g id="Layer_2">
|
||||
<g>
|
||||
<path fill="none" stroke="#FFFFFF" stroke-width="4.8" stroke-miterlimit="10" d="M287.609,219.602
|
||||
c32.25,29.578,49.617,63.141,38.719,75c-10.852,11.859-45.844-2.484-78.091-32.109c-32.273-29.578-49.594-63.188-38.719-75.094
|
||||
C220.393,175.539,255.338,189.93,287.609,219.602z"/>
|
||||
<path fill="none" stroke="#FFFFFF" stroke-width="4.8" stroke-miterlimit="10" d="M299.422,203.102
|
||||
c54.656,50.203,84.516,106.594,66.656,126.094c-17.859,19.453-76.641-5.344-131.341-55.5
|
||||
c-54.656-50.109-84.516-106.641-66.656-126.094C185.94,128.102,244.721,152.945,299.422,203.102z"/>
|
||||
<path fill="none" stroke="#FFFFFF" stroke-width="4.8" stroke-miterlimit="10" d="M313.953,187.445
|
||||
c75.984,69.703,117.422,148.125,92.625,175.219c-24.844,27.047-106.547-7.453-182.529-77.156
|
||||
c-75.961-69.656-117.445-148.125-92.625-175.219C156.245,83.242,237.948,117.789,313.953,187.445z"/>
|
||||
<path fill="none" stroke="#FFFFFF" stroke-width="4.8" stroke-miterlimit="10" d="M149.752,298.773
|
||||
c0,0-39.656-40.125-45.422-108.609c-3.211-33.938,12.844-87.844,37.078-92.156c24.305-4.312,38.391,2.203,61.406,11.203
|
||||
c0,0,44.016,19.078,100.263,66.047c56.203,46.969,94.5,102.891,111.844,146.156c1.969,4.828,12.469,28.781,0.188,45.656
|
||||
c-3.492,4.781-17.906,15-22.594,16.969c-5.719,2.391-20.578,11.625-57.234,15.656c0,0-45.422,6.422-104.623-29.719
|
||||
c0,0-3.844,7.031-6.047,10.219c-2.25,3.188-48.633,11.859-87,8.016c-38.391-3.844-55.031-13.781-55.031-13.781s-1.266,0,0-2.578
|
||||
C83.846,369.32,149.752,298.773,149.752,298.773z"/>
|
||||
<path fill="none" stroke="#FFFFFF" stroke-width="4.8" stroke-miterlimit="10" d="M149.752,298.773
|
||||
c0,0,39.656,48.609,80.906,71.203"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.2 KiB |
29
mobile/public/assets/images/lights/lc_iris.svg
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 16.0.4, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" width="500px"
|
||||
height="500px" viewBox="0 0 500 500" enable-background="new 0 0 500 500" xml:space="preserve">
|
||||
<g id="Layer_1" display="none">
|
||||
<rect display="inline" width="500" height="500"/>
|
||||
</g>
|
||||
<g id="Layer_2">
|
||||
<g>
|
||||
<path fill="none" stroke="#000000" stroke-width="4.8" stroke-miterlimit="10" d="M441.079,276.902
|
||||
c-11.391,22.406-65.766,30-182.438-56.388c-59.576-46.547-97.919-101.297-98.904-133.031c0.047-5.812,0.188-14.156,7.5-20.156
|
||||
c-26.297,23.531-115.406,123-103.828,222.56c11.625,99.656,102.234,142.125,144.281,147.234
|
||||
c26.625,3.188,57.326,0.047,86.294-11.625C355.438,400.793,414.267,339.105,441.079,276.902z"/>
|
||||
<path fill="none" stroke="#000000" stroke-width="4.8" stroke-miterlimit="10" d="M365.61,232.936
|
||||
c-5.672,11.203-32.859,13.875-91.219-29.297c-29.857-23.344-46.873-45.281-47.435-63.328c0.047-4.969-1.828-17.578,15.938-17.812
|
||||
c18.513,0.234,49.357,13.125,82.732,42.047C342.735,177.999,375.501,211.092,365.61,232.936z"/>
|
||||
<path fill="none" stroke="#000000" stroke-width="4.8" stroke-miterlimit="10" d="M195.597,59.827
|
||||
c36.984,0.562,98.763,26.25,165.513,84.234c56.953,48.562,92.344,105.937,79.969,132.935
|
||||
c-11.391,22.406-65.766,30.047-182.484-56.388c-59.576-46.594-97.966-101.344-98.951-133.125
|
||||
C159.738,77.639,160.113,60.342,195.597,59.827z"/>
|
||||
<path fill="none" stroke="#000000" stroke-width="4.8" stroke-miterlimit="10" d="M136.535,348.434
|
||||
c-2.766-0.094-5.344,0.516-7.734,2.062c-4.969,3.422-9.094,17.203,6.469,29.812c15.609,12.562,32.672,18.938,41.859,11.578
|
||||
c3.656-2.953,3.516-7.172,1.219-11.812 M172.16,267.434c-7.406,24.047-14.766,62.625-40.594,85.875
|
||||
c-1.359,6.375,0,16.969,11.203,24.75c11.203,7.828,19.594,11.625,30.75,8.812c3.422-6.609,40.406-51.891,76.64-60.234
|
||||
M102.785,147.483c1.031,15.703,9.094,77.812,106.5,151.169c96.091,72.422,154.169,71.203,178.732,58.641"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.1 KiB |
29
mobile/public/assets/images/lights/lc_irisw.svg
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 16.0.4, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" width="500px"
|
||||
height="500px" viewBox="0 0 500 500" enable-background="new 0 0 500 500" xml:space="preserve">
|
||||
<g id="Layer_1" display="none">
|
||||
<rect display="inline" width="500" height="500"/>
|
||||
</g>
|
||||
<g id="Layer_2">
|
||||
<g>
|
||||
<path fill="none" stroke="#FFFFFF" stroke-width="4.8" stroke-miterlimit="10" d="M441.079,276.902
|
||||
c-11.391,22.406-65.766,30-182.438-56.388c-59.576-46.547-97.919-101.297-98.904-133.031c0.047-5.812,0.188-14.156,7.5-20.156
|
||||
c-26.297,23.531-115.406,123-103.828,222.56c11.625,99.656,102.234,142.125,144.281,147.234
|
||||
c26.625,3.188,57.326,0.047,86.294-11.625C355.438,400.793,414.267,339.105,441.079,276.902z"/>
|
||||
<path fill="none" stroke="#FFFFFF" stroke-width="4.8" stroke-miterlimit="10" d="M365.61,232.936
|
||||
c-5.672,11.203-32.859,13.875-91.219-29.297c-29.857-23.344-46.873-45.281-47.435-63.328c0.047-4.969-1.828-17.578,15.938-17.812
|
||||
c18.513,0.234,49.357,13.125,82.732,42.047C342.735,177.999,375.501,211.092,365.61,232.936z"/>
|
||||
<path fill="none" stroke="#FFFFFF" stroke-width="4.8" stroke-miterlimit="10" d="M195.597,59.827
|
||||
c36.984,0.562,98.763,26.25,165.513,84.234c56.953,48.562,92.344,105.937,79.969,132.935
|
||||
c-11.391,22.406-65.766,30.047-182.484-56.388c-59.576-46.594-97.966-101.344-98.951-133.125
|
||||
C159.738,77.639,160.113,60.342,195.597,59.827z"/>
|
||||
<path fill="none" stroke="#FFFFFF" stroke-width="4.8" stroke-miterlimit="10" d="M136.535,348.434
|
||||
c-2.766-0.094-5.344,0.516-7.734,2.062c-4.969,3.422-9.094,17.203,6.469,29.812c15.609,12.562,32.672,18.938,41.859,11.578
|
||||
c3.656-2.953,3.516-7.172,1.219-11.812 M172.16,267.434c-7.406,24.047-14.766,62.625-40.594,85.875
|
||||
c-1.359,6.375,0,16.969,11.203,24.75c11.203,7.828,19.594,11.625,30.75,8.812c3.422-6.609,40.406-51.891,76.64-60.234
|
||||
M102.785,147.483c1.031,15.703,9.094,77.812,106.5,151.169c96.091,72.422,154.169,71.203,178.732,58.641"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.1 KiB |
45
mobile/public/assets/images/lights/lightstrip.svg
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 16.0.4, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" width="500px"
|
||||
height="500px" viewBox="0 0 500 500" enable-background="new 0 0 500 500" xml:space="preserve">
|
||||
<g id="Layer_1" display="none">
|
||||
<rect display="inline" width="500" height="500"/>
|
||||
</g>
|
||||
<g id="Layer_2">
|
||||
<g>
|
||||
<path fill="none" stroke="#000000" stroke-width="6.9898" stroke-miterlimit="10" d="M404.109,402.393v-64.711H186.448
|
||||
c0,0-92.288,2.23-92.288-61.205c0,57.201,0,59.703,0,59.703s-2.002,66.213,95.292,66.213
|
||||
C266.178,402.393,404.109,402.393,404.109,402.393z"/>
|
||||
|
||||
<rect x="127.516" y="116.977" fill="none" stroke="#000000" stroke-width="6.9898" stroke-miterlimit="10" width="15.563" height="15.518"/>
|
||||
|
||||
<rect x="187.722" y="116.977" fill="none" stroke="#000000" stroke-width="6.9898" stroke-miterlimit="10" width="15.563" height="15.518"/>
|
||||
|
||||
<rect x="247.926" y="116.977" fill="none" stroke="#000000" stroke-width="6.9898" stroke-miterlimit="10" width="15.499" height="15.518"/>
|
||||
|
||||
<rect x="308.09" y="116.977" fill="none" stroke="#000000" stroke-width="6.9898" stroke-miterlimit="10" width="15.541" height="15.518"/>
|
||||
<polygon fill="none" stroke="#000000" stroke-width="6.9898" stroke-miterlimit="10" points="380.354,149.172 366.339,142.506
|
||||
366.111,126.192 380.127,132.836 "/>
|
||||
|
||||
<rect x="361.311" y="365.078" fill="none" stroke="#000000" stroke-width="6.9898" stroke-miterlimit="10" width="15.54" height="15.562"/>
|
||||
|
||||
<rect x="301.104" y="365.078" fill="none" stroke="#000000" stroke-width="6.9898" stroke-miterlimit="10" width="15.54" height="15.562"/>
|
||||
|
||||
<rect x="240.919" y="365.078" fill="none" stroke="#000000" stroke-width="6.9898" stroke-miterlimit="10" width="15.566" height="15.562"/>
|
||||
|
||||
<rect x="180.737" y="365.078" fill="none" stroke="#000000" stroke-width="6.9898" stroke-miterlimit="10" width="15.563" height="15.562"/>
|
||||
<polygon fill="none" stroke="#000000" stroke-width="6.9898" stroke-miterlimit="10" points="130.884,350.697 144.945,357.342
|
||||
144.809,373.678 130.748,367.035 "/>
|
||||
<path fill="none" stroke="#000000" stroke-width="6.9898" stroke-miterlimit="10" d="M94.114,91.924v64.666h217.707
|
||||
c0,0,83.278-5.506,92.288,61.207c0-57.18,0-59.705,0-59.705s2.002-66.168-95.291-66.168
|
||||
C232.045,91.924,94.114,91.924,94.114,91.924z"/>
|
||||
<path fill="none" stroke="#000000" stroke-width="6.9898" stroke-miterlimit="10" d="M129.382,326.67
|
||||
c0.819-11.195,7.281-26.826,34.494-35.654c20.068-6.508,139.185-13.289,139.185-13.289s97.498-1.25,100.911-60.75
|
||||
c-3.709-25.414-18.293-40.182-34.812-48.76c-0.5,11.832-6.735,28.146-34.768,35.041c-38.612,9.51-139.208,13.287-139.208,13.287
|
||||
s-98.659-1.684-100.957,61.728C94.934,303.779,110.906,318.342,129.382,326.67z"/>
|
||||
<path fill="none" stroke="#000000" stroke-width="6.9898" stroke-miterlimit="10" d="M94.16,279.957
|
||||
c0,0.182-0.045,0.342-0.045,0.547c0,0.158,0.045,0.295,0.045,0.477C94.16,280.617,94.16,280.299,94.16,279.957z"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 3.1 KiB |
45
mobile/public/assets/images/lights/lightstripw.svg
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 16.0.4, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" width="500px"
|
||||
height="500px" viewBox="0 0 500 500" enable-background="new 0 0 500 500" xml:space="preserve">
|
||||
<g id="Layer_1" display="none">
|
||||
<rect display="inline" width="500" height="500"/>
|
||||
</g>
|
||||
<g id="Layer_2">
|
||||
<g>
|
||||
<path fill="none" stroke="#FFFFFF" stroke-width="6.9898" stroke-miterlimit="10" d="M404.109,402.393v-64.711H186.448
|
||||
c0,0-92.288,2.23-92.288-61.205c0,57.201,0,59.703,0,59.703s-2.002,66.213,95.292,66.213
|
||||
C266.178,402.393,404.109,402.393,404.109,402.393z"/>
|
||||
|
||||
<rect x="127.516" y="116.977" fill="none" stroke="#FFFFFF" stroke-width="6.9898" stroke-miterlimit="10" width="15.563" height="15.518"/>
|
||||
|
||||
<rect x="187.722" y="116.977" fill="none" stroke="#FFFFFF" stroke-width="6.9898" stroke-miterlimit="10" width="15.563" height="15.518"/>
|
||||
|
||||
<rect x="247.926" y="116.977" fill="none" stroke="#FFFFFF" stroke-width="6.9898" stroke-miterlimit="10" width="15.499" height="15.518"/>
|
||||
|
||||
<rect x="308.09" y="116.977" fill="none" stroke="#FFFFFF" stroke-width="6.9898" stroke-miterlimit="10" width="15.541" height="15.518"/>
|
||||
<polygon fill="none" stroke="#FFFFFF" stroke-width="6.9898" stroke-miterlimit="10" points="380.354,149.172 366.339,142.506
|
||||
366.111,126.192 380.127,132.836 "/>
|
||||
|
||||
<rect x="361.311" y="365.078" fill="none" stroke="#FFFFFF" stroke-width="6.9898" stroke-miterlimit="10" width="15.54" height="15.562"/>
|
||||
|
||||
<rect x="301.104" y="365.078" fill="none" stroke="#FFFFFF" stroke-width="6.9898" stroke-miterlimit="10" width="15.54" height="15.562"/>
|
||||
|
||||
<rect x="240.919" y="365.078" fill="none" stroke="#FFFFFF" stroke-width="6.9898" stroke-miterlimit="10" width="15.566" height="15.562"/>
|
||||
|
||||
<rect x="180.737" y="365.078" fill="none" stroke="#FFFFFF" stroke-width="6.9898" stroke-miterlimit="10" width="15.563" height="15.562"/>
|
||||
<polygon fill="none" stroke="#FFFFFF" stroke-width="6.9898" stroke-miterlimit="10" points="130.884,350.697 144.945,357.342
|
||||
144.809,373.678 130.748,367.035 "/>
|
||||
<path fill="none" stroke="#FFFFFF" stroke-width="6.9898" stroke-miterlimit="10" d="M94.114,91.924v64.666h217.707
|
||||
c0,0,83.278-5.506,92.288,61.207c0-57.18,0-59.705,0-59.705s2.002-66.168-95.291-66.168
|
||||
C232.045,91.924,94.114,91.924,94.114,91.924z"/>
|
||||
<path fill="none" stroke="#FFFFFF" stroke-width="6.9898" stroke-miterlimit="10" d="M129.382,326.67
|
||||
c0.819-11.195,7.281-26.826,34.494-35.654c20.068-6.508,139.185-13.289,139.185-13.289s97.498-1.25,100.911-60.75
|
||||
c-3.709-25.414-18.293-40.182-34.812-48.76c-0.5,11.832-6.735,28.146-34.768,35.041c-38.612,9.51-139.208,13.287-139.208,13.287
|
||||
s-98.659-1.684-100.957,61.728C94.934,303.779,110.906,318.342,129.382,326.67z"/>
|
||||
<path fill="none" stroke="#FFFFFF" stroke-width="6.9898" stroke-miterlimit="10" d="M94.16,279.957
|
||||
c0,0.182-0.045,0.342-0.045,0.547c0,0.158,0.045,0.295,0.045,0.477C94.16,280.617,94.16,280.299,94.16,279.957z"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 3.1 KiB |
42
mobile/public/assets/images/lights/storylight.svg
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 16.0.4, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" width="500px"
|
||||
height="500px" viewBox="0 0 500 500" enable-background="new 0 0 500 500" xml:space="preserve">
|
||||
<g id="Layer_1" display="none">
|
||||
<rect display="inline" width="500" height="500"/>
|
||||
</g>
|
||||
<g id="Layer_2">
|
||||
<g>
|
||||
<path fill="none" stroke="#000000" stroke-width="4.8" stroke-miterlimit="10" d="M249.167,347.182
|
||||
c46.219,49.922,139.031,114.234,209.227,74.719c49.617-28.172,21.727-114.234-6.75-152.016
|
||||
c-38.648-52.129-67.758-73.879-84.258-84.004c-2.602-2.016-7.875-5.203-1.852-5.203c6,0,35.156,3.188,34.969-29.672
|
||||
c-0.211-32.906-39.305-77.25-78.961-95.906c-39.68-18.609-72.375-6.562-66.188,25.359c6.188,32.016,34.219,59.438,46.828,69.797
|
||||
c2.625,2.438,5.812,6-1.312,3.938c-7.172-2.062-41.766-9.984-65.812-2.25c-24.094,7.688-32.156,21.234-35.156,25.172
|
||||
c-0.188,1.547-2.836,4.5-5.086-1.5s-16.359-48.094-68.062-78.188C96.026,79.521,50.229,66.208,23.534,91.943
|
||||
s9.398,84.234,32.344,102.281s76.898,57.895,124.828,32.907c2.836-1.312,8.086-6.563,9.023,2.813
|
||||
C190.667,239.367,196.128,288.588,249.167,347.182z"/>
|
||||
<path fill="none" stroke="#000000" stroke-width="4.8" stroke-miterlimit="10" d="M176.792,229.006
|
||||
c-1.734,22.175-12.984,95.16,64.828,169.597c77.859,74.438,188.531,62.203,227.016,15.656"/>
|
||||
<path fill="none" stroke="#000000" stroke-width="4.8" stroke-miterlimit="10" d="M210.917,363.447
|
||||
c-8.859,7.969-31.406,30.562-33.703,32.766c-2.25,2.297-15.469,11.859-3.656,19.172c11.859,7.359,30.984,15.75,70.805,14.953
|
||||
c8.461,0,24.375-1.734,33.633-4.219"/>
|
||||
<path fill="none" stroke="#000000" stroke-width="4.8" stroke-miterlimit="10" d="M176.464,396.916
|
||||
c5.859,6.656,19.172,24.188,85.734,18.891"/>
|
||||
<path fill="none" stroke="#000000" stroke-width="4.8" stroke-miterlimit="10" d="M235.62,161.927
|
||||
c-27.094,9.562-83.109,78.284,40.594,201.52c36.867,32.156,126.727,88.922,180.891,44.578
|
||||
c54.117-44.391-14.297-139.172-25.594-153.047c-5.766-7.125-31.781-40.551-71.438-66.192
|
||||
C322.245,164.318,269.37,148.896,235.62,161.927z"/>
|
||||
<path fill="none" stroke="#000000" stroke-width="4.8" stroke-miterlimit="10" d="M251.183,193.99
|
||||
c-21.797,7.734-66.891,63.051,32.672,162.239c29.672,25.828,101.977,71.484,145.547,35.812
|
||||
c43.594-35.719-11.484-111.984-20.578-123.188c-4.641-5.719-25.547-32.627-57.492-53.254
|
||||
C320.909,195.912,278.37,183.49,251.183,193.99z"/>
|
||||
<path fill="none" stroke="#000000" stroke-width="4.8" stroke-miterlimit="10" d="M52.011,92.13
|
||||
c-17.812,5.391-49.875,36.516,20.953,100.5c20.016,15.984,68.977,44.393,98.414,22.219c29.461-22.172-4.734-69.188-10.898-76.172
|
||||
c-3.117-3.562-20.297-20.578-41.883-33.375C97.995,93.115,70.386,85.662,52.011,92.13z"/>
|
||||
<path fill="none" stroke="#000000" stroke-width="4.8" stroke-miterlimit="10" d="M282.894,58.943
|
||||
c-16.57,4.172-30.914,30.516,20.508,80.297c14.531,12.516,55.969,40.5,77.344,23.25c21.375-17.297-8.062-55.969-12.867-61.078
|
||||
c-4.055-4.312-17.039-18.562-32.695-28.547C320.229,63.349,296.229,53.88,282.894,58.943z"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 3.2 KiB |
42
mobile/public/assets/images/lights/storylightw.svg
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 16.0.4, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" width="500px"
|
||||
height="500px" viewBox="0 0 500 500" enable-background="new 0 0 500 500" xml:space="preserve">
|
||||
<g id="Layer_1" display="none">
|
||||
<rect display="inline" width="500" height="500"/>
|
||||
</g>
|
||||
<g id="Layer_2">
|
||||
<g>
|
||||
<path fill="none" stroke="#FFFFFF" stroke-width="4.8" stroke-miterlimit="10" d="M249.167,347.182
|
||||
c46.219,49.922,139.031,114.234,209.227,74.719c49.617-28.172,21.727-114.234-6.75-152.016
|
||||
c-38.648-52.129-67.758-73.879-84.258-84.004c-2.602-2.016-7.875-5.203-1.852-5.203c6,0,35.156,3.188,34.969-29.672
|
||||
c-0.211-32.906-39.305-77.25-78.961-95.906c-39.68-18.609-72.375-6.562-66.188,25.359c6.188,32.016,34.219,59.438,46.828,69.797
|
||||
c2.625,2.438,5.812,6-1.312,3.938c-7.172-2.062-41.766-9.984-65.812-2.25c-24.094,7.688-32.156,21.234-35.156,25.172
|
||||
c-0.188,1.547-2.836,4.5-5.086-1.5s-16.359-48.094-68.062-78.188C96.026,79.521,50.229,66.208,23.534,91.943
|
||||
s9.398,84.234,32.344,102.281s76.898,57.895,124.828,32.907c2.836-1.312,8.086-6.563,9.023,2.813
|
||||
C190.667,239.367,196.128,288.588,249.167,347.182z"/>
|
||||
<path fill="none" stroke="#FFFFFF" stroke-width="4.8" stroke-miterlimit="10" d="M176.792,229.006
|
||||
c-1.734,22.175-12.984,95.16,64.828,169.597c77.859,74.438,188.531,62.203,227.016,15.656"/>
|
||||
<path fill="none" stroke="#FFFFFF" stroke-width="4.8" stroke-miterlimit="10" d="M210.917,363.447
|
||||
c-8.859,7.969-31.406,30.562-33.703,32.766c-2.25,2.297-15.469,11.859-3.656,19.172c11.859,7.359,30.984,15.75,70.805,14.953
|
||||
c8.461,0,24.375-1.734,33.633-4.219"/>
|
||||
<path fill="none" stroke="#FFFFFF" stroke-width="4.8" stroke-miterlimit="10" d="M176.464,396.916
|
||||
c5.859,6.656,19.172,24.188,85.734,18.891"/>
|
||||
<path fill="none" stroke="#FFFFFF" stroke-width="4.8" stroke-miterlimit="10" d="M235.62,161.927
|
||||
c-27.094,9.562-83.109,78.284,40.594,201.52c36.867,32.156,126.727,88.922,180.891,44.578
|
||||
c54.117-44.391-14.297-139.172-25.594-153.047c-5.766-7.125-31.781-40.551-71.438-66.192
|
||||
C322.245,164.318,269.37,148.896,235.62,161.927z"/>
|
||||
<path fill="none" stroke="#FFFFFF" stroke-width="4.8" stroke-miterlimit="10" d="M251.183,193.99
|
||||
c-21.797,7.734-66.891,63.051,32.672,162.239c29.672,25.828,101.977,71.484,145.547,35.812
|
||||
c43.594-35.719-11.484-111.984-20.578-123.188c-4.641-5.719-25.547-32.627-57.492-53.254
|
||||
C320.909,195.912,278.37,183.49,251.183,193.99z"/>
|
||||
<path fill="none" stroke="#FFFFFF" stroke-width="4.8" stroke-miterlimit="10" d="M52.011,92.13
|
||||
c-17.812,5.391-49.875,36.516,20.953,100.5c20.016,15.984,68.977,44.393,98.414,22.219c29.461-22.172-4.734-69.188-10.898-76.172
|
||||
c-3.117-3.562-20.297-20.578-41.883-33.375C97.995,93.115,70.386,85.662,52.011,92.13z"/>
|
||||
<path fill="none" stroke="#FFFFFF" stroke-width="4.8" stroke-miterlimit="10" d="M282.894,58.943
|
||||
c-16.57,4.172-30.914,30.516,20.508,80.297c14.531,12.516,55.969,40.5,77.344,23.25c21.375-17.297-8.062-55.969-12.867-61.078
|
||||
c-4.055-4.312-17.039-18.562-32.695-28.547C320.229,63.349,296.229,53.88,282.894,58.943z"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 3.2 KiB |
BIN
mobile/public/assets/images/logo.png
Normal file
|
After Width: | Height: | Size: 28 KiB |
BIN
mobile/public/assets/images/missingArtwork.png
Normal file
|
After Width: | Height: | Size: 1.8 KiB |
BIN
mobile/public/assets/images/pressButtonBridge.png
Normal file
|
After Width: | Height: | Size: 6.9 KiB |
BIN
mobile/public/assets/images/sc-white-sm.png
Normal file
|
After Width: | Height: | Size: 292 B |
BIN
mobile/public/assets/images/sc-white.png
Normal file
|
After Width: | Height: | Size: 3.4 KiB |
9
mobile/public/browserconfig.xml
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<browserconfig>
|
||||
<msapplication>
|
||||
<tile>
|
||||
<square150x150logo src="/mstile-150x150.png"/>
|
||||
<TileColor>#da532c</TileColor>
|
||||
</tile>
|
||||
</msapplication>
|
||||
</browserconfig>
|
||||
15
mobile/public/crossdomain.xml
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
<?xml version="1.0"?>
|
||||
<!DOCTYPE cross-domain-policy SYSTEM "http://www.adobe.com/xml/dtds/cross-domain-policy.dtd">
|
||||
<cross-domain-policy>
|
||||
<!-- Read this: www.adobe.com/devnet/articles/crossdomain_policy_file_spec.html -->
|
||||
|
||||
<!-- Most restrictive policy: -->
|
||||
<site-control permitted-cross-domain-policies="none"/>
|
||||
|
||||
<!-- Least restrictive policy: -->
|
||||
<!--
|
||||
<site-control permitted-cross-domain-policies="all"/>
|
||||
<allow-access-from domain="*" to-ports="*" secure="false"/>
|
||||
<allow-http-request-headers-from domain="*" headers="*" secure="false"/>
|
||||
-->
|
||||
</cross-domain-policy>
|
||||
BIN
mobile/public/favicon-16x16.png
Normal file
|
After Width: | Height: | Size: 1 KiB |
BIN
mobile/public/favicon-32x32.png
Normal file
|
After Width: | Height: | Size: 1.7 KiB |
BIN
mobile/public/favicon-96x96.png
Normal file
|
After Width: | Height: | Size: 3.8 KiB |
BIN
mobile/public/favicon.ico
Normal file
|
After Width: | Height: | Size: 15 KiB |
14
mobile/public/humans.txt
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
/* TEAM */
|
||||
Your title: Egor Philippov
|
||||
Site: https://www.linkedin.com/pub/egor-philippov/7b/220/148
|
||||
Location: Vancouver, Canada.
|
||||
|
||||
/* THANKS */
|
||||
Edmond Cheung - favicons + huegasm logo
|
||||
Liviu Antonescu - filming + video editing of the intro
|
||||
Olamide Omorodion - business consultation
|
||||
|
||||
/* SITE */
|
||||
Last update: 2015
|
||||
Standards: HTML5, CSS3
|
||||
Components: ember, jQuery, bootstrap, font-awesome, intro.js, locallyjs, nouislider, dancer.js, jquery-mousewheel, ember paper, ember notify, JavaScript-ID3-Reader
|
||||
17
mobile/public/manifest.json
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
{
|
||||
"name": "Huegasm",
|
||||
"icons": [
|
||||
{
|
||||
"src": "\/android-chrome-192x192.png",
|
||||
"sizes": "192x192",
|
||||
"type": "image\/png"
|
||||
},
|
||||
{
|
||||
"src": "\/android-chrome-512x512.png",
|
||||
"sizes": "512x512",
|
||||
"type": "image\/png"
|
||||
}
|
||||
],
|
||||
"theme_color": "#ffffff",
|
||||
"display": "standalone"
|
||||
}
|
||||
BIN
mobile/public/mstile-150x150.png
Normal file
|
After Width: | Height: | Size: 3.8 KiB |
3
mobile/public/robots.txt
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
# http://www.robotstxt.org
|
||||
User-agent: *
|
||||
Disallow:
|
||||
69
mobile/public/safari-pinned-tab.svg
Normal file
|
|
@ -0,0 +1,69 @@
|
|||
<?xml version="1.0" standalone="no"?>
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN"
|
||||
"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
|
||||
<svg version="1.0" xmlns="http://www.w3.org/2000/svg"
|
||||
width="759.000000pt" height="759.000000pt" viewBox="0 0 759.000000 759.000000"
|
||||
preserveAspectRatio="xMidYMid meet">
|
||||
<metadata>
|
||||
Created by potrace 1.11, written by Peter Selinger 2001-2013
|
||||
</metadata>
|
||||
<g transform="translate(0.000000,759.000000) scale(0.100000,-0.100000)"
|
||||
fill="#000000" stroke="none">
|
||||
<path d="M3187 7544 c-1 -1 -78 -4 -172 -8 -93 -3 -197 -8 -230 -11 -33 -2
|
||||
-98 -7 -145 -10 -47 -3 -98 -7 -115 -9 -16 -2 -64 -7 -105 -11 -41 -4 -88 -9
|
||||
-105 -11 -16 -2 -52 -6 -80 -9 -78 -10 -175 -24 -210 -32 -11 -2 -29 -6 -40
|
||||
-7 -88 -15 -304 -65 -320 -74 -5 -4 -17 -7 -25 -9 -27 -4 -164 -63 -230 -100
|
||||
-157 -86 -331 -234 -401 -343 -18 -27 -35 -50 -38 -50 -3 0 -15 -18 -25 -40
|
||||
-11 -22 -23 -40 -28 -40 -5 0 -7 -4 -3 -9 3 -5 1 -12 -4 -15 -17 -11 -71 -162
|
||||
-98 -274 -20 -87 -26 -134 -27 -247 0 -77 2 -156 6 -175 8 -36 24 -110 32
|
||||
-145 18 -76 62 -200 97 -273 70 -147 79 -164 123 -247 23 -44 62 -118 86 -165
|
||||
24 -47 57 -106 72 -132 15 -27 25 -48 22 -48 -4 0 -1 -6 5 -13 6 -8 25 -40 42
|
||||
-73 17 -32 45 -84 61 -114 63 -115 168 -320 164 -320 -2 0 5 -24 15 -52 36
|
||||
-97 70 -193 74 -208 2 -8 15 -49 30 -90 65 -188 150 -504 139 -522 -3 -4 -1
|
||||
-8 3 -8 5 0 9 -8 9 -17 1 -19 6 -53 24 -143 6 -30 13 -71 16 -90 2 -19 6 -44
|
||||
9 -55 4 -20 12 -81 20 -145 2 -19 6 -46 9 -60 8 -45 21 -176 40 -425 2 -27 7
|
||||
-86 10 -130 25 -312 91 -475 254 -626 l70 -65 -9 -54 c-6 -30 -9 -84 -9 -120
|
||||
2 -134 2 -167 1 -235 0 -38 1 -79 3 -90 9 -59 16 -118 21 -186 3 -41 14 -108
|
||||
25 -148 39 -138 82 -204 240 -362 126 -126 193 -184 285 -246 33 -22 83 -65
|
||||
110 -95 28 -30 73 -72 100 -93 53 -40 162 -94 205 -101 14 -3 36 -9 50 -14 14
|
||||
-4 43 -12 65 -16 22 -4 47 -9 55 -10 65 -14 125 -17 424 -19 252 -1 355 1 403
|
||||
11 35 8 76 16 91 19 25 4 48 9 90 19 144 34 328 155 397 260 9 13 31 31 50 40
|
||||
61 29 149 103 248 209 101 107 126 139 169 217 30 54 66 156 74 208 3 20 9 53
|
||||
14 72 5 19 12 55 15 79 4 24 13 60 20 80 44 112 55 186 59 385 l4 196 75 79
|
||||
c41 43 83 92 93 107 10 16 21 31 24 34 3 3 18 30 32 60 49 102 82 228 93 360
|
||||
6 67 15 147 21 169 3 14 19 74 28 106 1 6 10 37 20 70 9 33 23 83 31 110 8 28
|
||||
24 82 35 120 12 39 23 78 25 89 26 123 169 573 266 833 196 528 198 534 194
|
||||
592 -4 92 -65 142 -190 158 -57 7 -2564 10 -2610 3 -16 -3 -34 -8 -40 -13 -71
|
||||
-54 -89 -91 -81 -166 6 -59 17 -128 36 -221 6 -30 13 -64 14 -75 2 -11 8 -45
|
||||
14 -75 6 -30 13 -68 16 -85 2 -16 16 -88 30 -160 14 -71 27 -141 30 -155 2
|
||||
-14 7 -32 10 -40 3 -8 8 -30 11 -47 3 -18 8 -48 11 -65 10 -59 44 -116 82
|
||||
-140 l37 -23 480 -1 c467 -1 480 -1 477 -20 -3 -24 -2 -19 -40 -135 -17 -53
|
||||
-33 -109 -36 -123 -3 -14 -8 -36 -11 -47 -4 -12 -8 -33 -10 -48 -1 -14 -6 -55
|
||||
-10 -91 -7 -65 -12 -137 -14 -198 -1 -26 -14 -47 -66 -100 -112 -117 -137
|
||||
-146 -170 -197 -38 -60 -83 -168 -90 -221 -4 -22 -13 -64 -22 -94 -8 -30 -15
|
||||
-57 -15 -60 0 -3 -4 -13 -10 -23 -5 -10 -11 -26 -13 -35 -3 -9 -10 -37 -16
|
||||
-62 -8 -29 -11 -128 -11 -277 1 -199 -1 -233 -14 -244 -21 -17 -55 -37 -76
|
||||
-45 -10 -3 -18 -10 -18 -15 0 -5 -6 -9 -13 -9 -7 0 -38 -24 -68 -53 l-55 -52
|
||||
-115 0 -115 0 -52 47 c-29 25 -76 62 -105 81 l-52 35 1 63 c1 35 5 89 9 119
|
||||
11 80 12 120 2 165 -5 22 -10 49 -12 60 -2 11 -8 35 -13 53 -6 17 -9 32 -6 32
|
||||
3 0 0 8 -5 18 -5 9 -12 56 -15 102 -15 241 -65 360 -222 530 -23 25 -60 65
|
||||
-83 90 -22 24 -41 53 -42 65 0 11 -2 27 -3 35 -2 8 -6 60 -10 115 -11 152 -15
|
||||
196 -21 240 -4 22 -8 54 -10 70 -2 17 -18 114 -35 215 -37 216 -40 235 -45
|
||||
275 -2 17 -5 31 -6 33 -3 4 -20 89 -25 127 -1 11 -3 20 -4 20 -2 0 -4 12 -13
|
||||
55 -3 14 -8 32 -11 40 -3 8 -8 32 -11 53 -3 21 -10 47 -15 58 -6 10 -8 19 -6
|
||||
19 3 0 -1 18 -9 40 -8 22 -14 41 -13 43 3 6 -60 209 -129 417 -61 182 -170
|
||||
446 -199 480 -7 8 -13 18 -13 22 -2 16 -66 143 -73 146 -4 2 -8 10 -8 18 0 11
|
||||
-182 377 -364 730 -15 31 -24 59 -20 64 5 5 25 11 44 14 19 3 46 8 60 11 35 6
|
||||
98 15 145 20 22 2 49 6 60 8 26 6 158 22 250 32 304 30 538 38 1085 38 473 0
|
||||
581 -3 945 -29 50 -3 113 -7 140 -9 93 -6 176 -15 333 -35 151 -20 188 -25
|
||||
242 -35 14 -2 45 -7 70 -10 25 -3 54 -7 65 -9 11 -2 48 -9 82 -16 l61 -12 48
|
||||
-107 c56 -124 83 -165 126 -187 53 -27 105 -24 208 13 52 19 102 36 110 38 8
|
||||
2 15 4 15 5 0 1 7 3 15 5 23 6 183 64 205 75 28 13 148 50 159 49 5 0 12 3 15
|
||||
8 3 4 50 23 105 41 108 37 138 58 168 116 16 32 19 51 14 105 -15 169 -58 317
|
||||
-135 461 -150 283 -407 500 -695 586 -103 31 -198 54 -295 70 -18 3 -43 8 -55
|
||||
10 -11 2 -48 9 -81 14 -33 5 -69 11 -80 13 -11 3 -47 8 -80 11 -33 4 -67 9
|
||||
-75 11 -8 2 -42 6 -75 10 -73 7 -141 15 -175 20 -14 2 -59 7 -100 10 -41 3
|
||||
-97 8 -125 10 -27 3 -88 7 -135 9 -159 9 -295 18 -330 23 -32 5 -1178 12
|
||||
-1183 7z"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 4.5 KiB |
13
mobile/testem.js
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
/*jshint node:true*/
|
||||
module.exports = {
|
||||
"framework": "qunit",
|
||||
"test_page": "tests/index.html?hidepassed",
|
||||
"disable_watching": true,
|
||||
"launch_in_ci": [
|
||||
"PhantomJS"
|
||||
],
|
||||
"launch_in_dev": [
|
||||
"PhantomJS",
|
||||
"Chrome"
|
||||
]
|
||||
};
|
||||
52
mobile/tests/.jshintrc
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
{
|
||||
"predef": [
|
||||
"document",
|
||||
"window",
|
||||
"location",
|
||||
"setTimeout",
|
||||
"$",
|
||||
"-Promise",
|
||||
"define",
|
||||
"console",
|
||||
"visit",
|
||||
"exists",
|
||||
"fillIn",
|
||||
"click",
|
||||
"keyEvent",
|
||||
"triggerEvent",
|
||||
"find",
|
||||
"findWithAssert",
|
||||
"wait",
|
||||
"DS",
|
||||
"andThen",
|
||||
"currentURL",
|
||||
"currentPath",
|
||||
"currentRouteName"
|
||||
],
|
||||
"node": false,
|
||||
"browser": false,
|
||||
"boss": true,
|
||||
"curly": true,
|
||||
"debug": false,
|
||||
"devel": false,
|
||||
"eqeqeq": true,
|
||||
"evil": true,
|
||||
"forin": false,
|
||||
"immed": false,
|
||||
"laxbreak": false,
|
||||
"newcap": true,
|
||||
"noarg": true,
|
||||
"noempty": false,
|
||||
"nonew": false,
|
||||
"nomen": false,
|
||||
"onevar": false,
|
||||
"plusplus": false,
|
||||
"regexp": false,
|
||||
"undef": true,
|
||||
"sub": true,
|
||||
"strict": false,
|
||||
"white": false,
|
||||
"eqnull": true,
|
||||
"esversion": 6,
|
||||
"unused": true
|
||||
}
|
||||
5
mobile/tests/helpers/destroy-app.js
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
import Ember from 'ember';
|
||||
|
||||
export default function destroyApp(application) {
|
||||
Ember.run(application, 'destroy');
|
||||
}
|
||||
23
mobile/tests/helpers/module-for-acceptance.js
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
import { module } from 'qunit';
|
||||
import Ember from 'ember';
|
||||
import startApp from '../helpers/start-app';
|
||||
import destroyApp from '../helpers/destroy-app';
|
||||
|
||||
const { RSVP: { Promise } } = Ember;
|
||||
|
||||
export default function(name, options = {}) {
|
||||
module(name, {
|
||||
beforeEach() {
|
||||
this.application = startApp();
|
||||
|
||||
if (options.beforeEach) {
|
||||
return options.beforeEach.apply(this, arguments);
|
||||
}
|
||||
},
|
||||
|
||||
afterEach() {
|
||||
let afterEach = options.afterEach && options.afterEach.apply(this, arguments);
|
||||
return Promise.resolve(afterEach).then(() => destroyApp(this.application));
|
||||
}
|
||||
});
|
||||
}
|
||||
11
mobile/tests/helpers/resolver.js
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
import Resolver from '../../resolver';
|
||||
import config from '../../config/environment';
|
||||
|
||||
const resolver = Resolver.create();
|
||||
|
||||
resolver.namespace = {
|
||||
modulePrefix: config.modulePrefix,
|
||||
podModulePrefix: config.podModulePrefix
|
||||
};
|
||||
|
||||
export default resolver;
|
||||
18
mobile/tests/helpers/start-app.js
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
import Ember from 'ember';
|
||||
import Application from '../../app';
|
||||
import config from '../../config/environment';
|
||||
|
||||
export default function startApp(attrs) {
|
||||
let application;
|
||||
|
||||
let attributes = Ember.merge({}, config.APP);
|
||||
attributes = Ember.merge(attributes, attrs); // use defaults, but you can override;
|
||||
|
||||
Ember.run(() => {
|
||||
application = Application.create(attributes);
|
||||
application.setupForTesting();
|
||||
application.injectTestHelpers();
|
||||
});
|
||||
|
||||
return application;
|
||||
}
|
||||
33
mobile/tests/index.html
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<title>Huegasm Tests</title>
|
||||
<meta name="description" content="">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
|
||||
{{content-for "head"}}
|
||||
{{content-for "test-head"}}
|
||||
|
||||
<link rel="stylesheet" href="{{rootURL}}assets/vendor.css">
|
||||
<link rel="stylesheet" href="{{rootURL}}assets/huegasm.css">
|
||||
<link rel="stylesheet" href="{{rootURL}}assets/test-support.css">
|
||||
|
||||
{{content-for "head-footer"}}
|
||||
{{content-for "test-head-footer"}}
|
||||
</head>
|
||||
<body>
|
||||
{{content-for "body"}}
|
||||
{{content-for "test-body"}}
|
||||
|
||||
<script src="{{rootURL}}testem.js" integrity=""></script>
|
||||
<script src="{{rootURL}}assets/vendor.js"></script>
|
||||
<script src="{{rootURL}}assets/test-support.js"></script>
|
||||
<script src="{{rootURL}}assets/huegasm.js"></script>
|
||||
<script src="{{rootURL}}assets/tests.js"></script>
|
||||
|
||||
{{content-for "body-footer"}}
|
||||
{{content-for "test-body-footer"}}
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
import { moduleForComponent, test } from 'ember-qunit';
|
||||
import hbs from 'htmlbars-inline-precompile';
|
||||
|
||||
moduleForComponent('add-soundcloud-sound-modal', 'Integration | Component | add soundcloud sound modal', {
|
||||
integration: true
|
||||
});
|
||||
|
||||
test('it renders', function(assert) {
|
||||
assert.expect(2);
|
||||
|
||||
// Set any properties with this.set('myProperty', 'value');
|
||||
// Handle any actions with this.on('myAction', function(val) { ... });
|
||||
|
||||
this.render(hbs`{{add-soundcloud-sound-modal}}`);
|
||||
|
||||
assert.equal(this.$().text().trim(), '');
|
||||
|
||||
// Template block usage:
|
||||
this.render(hbs`
|
||||
{{#add-soundcloud-sound-modal}}
|
||||
template block text
|
||||
{{/add-soundcloud-sound-modal}}
|
||||
`);
|
||||
|
||||
assert.equal(this.$().text().trim(), 'template block text');
|
||||
});
|
||||