We're all friends here. Open sourcing huegasm and copying it over from a private repo. Some things to note:

- I never intended to open source this code as I was writting it
- Some of the code could likely be better componetized and made prettier
- No, I didn't use ember data and just made ad-hoc calls with $.ajax() because I found it to be more flexible for working with external APIs
- I've now moved on to newer projects and will not be working on huegasm at all...if at all
- Let me know if something is fucky and I'll try fix it...enjoy ( ͡° ͜ʖ ͡°)
This commit is contained in:
Egor 2016-03-20 21:44:17 -07:00
commit 90094cabc9
125 changed files with 6622 additions and 3 deletions

4
.bowerrc Normal file
View file

@ -0,0 +1,4 @@
{
"directory": "bower_components",
"analytics": false
}

34
.editorconfig Normal file
View file

@ -0,0 +1,34 @@
# 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
[*.js]
indent_style = space
indent_size = 2
[*.hbs]
insert_final_newline = false
indent_style = space
indent_size = 2
[*.css]
indent_style = space
indent_size = 2
[*.html]
indent_style = space
indent_size = 2
[*.{diff,md}]
trim_trailing_whitespace = false

10
.ember-cli Normal file
View 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
}

18
.gitignore vendored Normal file
View file

@ -0,0 +1,18 @@
# 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/

37
.jshintrc Normal file
View 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,
"esnext": true,
"unused": true
}

23
.travis.yml Normal file
View file

@ -0,0 +1,23 @@
---
language: node_js
node_js:
- "0.12"
sudo: false
cache:
directories:
- node_modules
before_install:
- export PATH=/usr/local/phantomjs-2.0.0/bin:$PATH
- "npm config set spin false"
- "npm install -g npm@^2"
install:
- npm install -g bower
- npm install
- bower install
script:
- npm test

3
.watchmanconfig Normal file
View file

@ -0,0 +1,3 @@
{
"ignore_dirs": ["tmp", "dist"]
}

View file

@ -1,4 +1,13 @@
# huegasm
www.huegasm.com
# Huegasm
Music awesomeness for hue lights.
This repository here is strictly for hosting huegasm on github pages.
# TODO
## FEATURES
## BUGS
## POSSIBLE FUTURE FEATURES
- better visualizations
- beat settings by interval
- auto beat detection mode
- display player time when hovering over seek bar

18
app/app.js Normal file
View 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;

63
app/index.html Normal file
View file

@ -0,0 +1,63 @@
<!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">
<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="57x57" href="/apple-touch-icon-57x57.png">
<link rel="apple-touch-icon" sizes="60x60" href="/apple-touch-icon-60x60.png">
<link rel="apple-touch-icon" sizes="72x72" href="/apple-touch-icon-72x72.png">
<link rel="apple-touch-icon" sizes="76x76" href="/apple-touch-icon-76x76.png">
<link rel="apple-touch-icon" sizes="114x114" href="/apple-touch-icon-114x114.png">
<link rel="apple-touch-icon" sizes="120x120" href="/apple-touch-icon-120x120.png">
<link rel="apple-touch-icon" sizes="144x144" href="/apple-touch-icon-144x144.png">
<link rel="apple-touch-icon" sizes="152x152" href="/apple-touch-icon-152x152.png">
<link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon-180x180.png">
<link rel="icon" type="image/png" href="/favicon-32x32.png" sizes="32x32">
<link rel="icon" type="image/png" href="/favicon-194x194.png" sizes="194x194">
<link rel="icon" type="image/png" href="/favicon-96x96.png" sizes="96x96">
<link rel="icon" type="image/png" href="/android-chrome-192x192.png" sizes="192x192">
<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="msapplication-TileColor" content="#da532c">
<meta name="msapplication-TileImage" content="/mstile-144x144.png">
<meta name="theme-color" content="#ffffff">
<link rel="stylesheet" integrity="" href="assets/vendor.css">
<link rel="stylesheet" integrity="" href="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="assets/vendor.js"></script>
<script src="assets/huegasm.js"></script>
{{content-for 'body-footer'}}
</body>
</html>

View file

@ -0,0 +1,61 @@
import Em from 'ember';
export default Em.Component.extend({
actions: {
close: function(){
this.sendAction();
},
save: function(){
var newGroupData = {"name": this.get('groupName'), "lights": this.get('selectedLights')}, newGroupsData = this.get('groupsData');
Em.$.ajax(this.get('apiURL') + '/groups', {
data: JSON.stringify(newGroupData),
contentType: 'application/json',
type: 'POST'
});
// crappy code to redraw the lights
newGroupsData['9999'] = newGroupData;
this.setProperties({
updateGroupsData: true,
groupsData: newGroupsData
});
this.sendAction();
},
clickLight: function(id) {
var selectedLights = this.get('selectedLights');
if(selectedLights.contains(id)){
selectedLights.removeObject(id);
} else {
selectedLights.pushObject(id);
}
}
},
didInsertElement: function() {
Em.$(document).keypress((event) => {
if(!this.get('saveDisabled') && event.which === 13) {
this.send('save');
}
});
},
groupName: null,
selectedLights: [],
onIsShowingModalChange: function(){
if(this.get('isShowingModal')){
this.setProperties({
selectedLights: [],
groupName: null
});
}
}.observes('isShowingModal'),
saveDisabled: function(){
return Em.isNone(this.get('groupName')) || Em.isEmpty(this.get('selectedLights')) || Em.isEmpty(this.get('groupName').trim());
}.property('groupName', 'selectedLights.[]')
});

View file

@ -0,0 +1,12 @@
{{#if isShowingModal}}
{{#modal-dialog close="close" alignment="center" translucentOverlay=true}}
{{light-group lightsData=lightsData activeLights=selectedLights action="clickLight" apiURL=apiURL noHover=true}}
{{paper-input label="Group name" value=groupName max="32" max-errortext="The group name cannot exceed 32 characters"}}
{{#paper-button action="close"}}Close{{/paper-button}}
{{#paper-button class="pull-right" action="save" disabled=saveDisabled primary=true}}Save{{/paper-button}}
{{/modal-dialog}}
{{/if}}

View file

@ -0,0 +1,38 @@
import Em from 'ember';
export default Em.Component.extend({
actions: {
close () {
this.sendAction();
},
add (){
this.sendAction('action', this.get('url'));
}
},
url: null,
onIsShowingModalChange: function(){
if(this.get('isShowingModal')){
this.set('url', null);
setTimeout(()=>{
Em.$('md-input-container input').focus();
}, 500);
}
}.observes('isShowingModal'),
didInsertElement: function() {
var self = this;
Em.$(document).keypress(function(event) {
if(!self.get('saveDisabled') && event.which === 13) {
self.send('add');
}
});
},
saveDisabled: function(){
return Em.isNone(this.get('url')) || Em.isEmpty(this.get('url').trim());
}.property('url')
});

View file

@ -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">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 action="close"}}Close{{/paper-button}}
{{#paper-button class="pull-right" action="add" disabled=saveDisabled primary=true}}Add Music{{/paper-button}}
{{/modal-dialog}}
{{/if}}

View file

@ -0,0 +1,149 @@
import Em from 'ember';
export default Em.Component.extend({
classNames: ['container', 'bridgeFinder'],
bridgeIp: null,
trial: false,
bridgeUsername: null,
bridgeFindStatus: null,
bridgeFindSuccess: Em.computed.equal('bridgeFindStatus', 'success'),
bridgeFindMultiple: Em.computed.equal('bridgeFindStatus', 'multiple'),
bridgeFindFail: Em.computed.equal('bridgeFindStatus', 'fail'),
// 30 seconds
bridgeUsernamePingMaxTime: 30000,
bridgeUsernamePingIntervalTime: 1000,
bridgeUserNamePingIntervalProgress: 0,
bridgePingIntervalHandle: null,
bridgeAuthenticateReachedStatus: null,
manualBridgeIp: null,
manualBridgeIpNotFound: false,
multipleBridgeIps: [],
error: false,
actions: {
retry(){
this.onBridgeIpChange();
},
findBridgeByIp() {
var manualBridgeIp = this.get('manualBridgeIp');
if (manualBridgeIp.toLowerCase() === 'trial' || manualBridgeIp.toLowerCase() === 'offline') {
this.setProperties({
trial: true,
bridgeIp: 'trial',
bridgeUsername: 'trial'
});
} else {
Em.$.ajax('http://' + manualBridgeIp + '/api', {
data: JSON.stringify({"devicetype": "huegasm"}),
contentType: 'application/json',
type: 'POST'
}).fail(() => {
this.set('manualBridgeIpNotFound', true);
setTimeout(() => { this.set('manualBridgeIpNotFound', false); }, 5000);
}).then(() => {
this.set('bridgeIp', manualBridgeIp);
});
}
}
},
didInsertElement() {
var self = this;
Em.$(document).keypress(function(event) {
if(!Em.isNone(self.get('manualBridgeIp')) && event.which === 13) {
self.send('findBridgeByIp');
}
});
},
// find the bridge ip here
init() {
this._super();
if(this.get('bridgeIp') === null) {
Em.$.ajax('https://www.meethue.com/api/nupnp', {
timeout: 30000
})
.done((result, status)=> {
var bridgeFindStatus = 'fail';
if (status === 'success' && result.length === 1) {
this.set('bridgeIp', result[0].internalipaddress);
this.get('storage').set('huegasm.bridgeIp', result[0].internalipaddress);
bridgeFindStatus = 'success';
} else if (result.length > 1) {
var multipleBridgeIps = this.get('multipleBridgeIps');
result.forEach(function (item) {
multipleBridgeIps.pushObject(item.internalipaddress);
});
bridgeFindStatus = 'multiple';
} else {
bridgeFindStatus = 'fail';
}
this.set('bridgeFindStatus', bridgeFindStatus);
})
.fail(()=>{
this.set('bridgeFindStatus', 'fail');
});
}
},
// try to authenticate against the bridge here
onBridgeIpChange: function () {
if(!this.get('trial') && !this.get('isAuthenticating')) {
this.setProperties({
bridgePingIntervalHandle: setInterval(this.pingBridgeUser.bind(this), this.get('bridgeUsernamePingIntervalTime')),
bridgeUserNamePingIntervalProgress: 0
});
}
}.observes('bridgeIp').on('init'),
pingBridgeUser() {
var bridgeIp = this.get('bridgeIp'),
bridgeUserNamePingIntervalProgress = this.get('bridgeUserNamePingIntervalProgress'),
bridgeUsernamePingMaxTime = this.get('bridgeUsernamePingMaxTime');
if (bridgeIp !== null && bridgeUserNamePingIntervalProgress < 100) {
Em.$.ajax('http://' + bridgeIp + '/api', {
data: JSON.stringify({"devicetype": "huegasm"}),
contentType: 'application/json',
type: 'POST'
}).done((result, status)=>{
if (status === 'success' && !result[0].error) {
this.clearBridgePingIntervalHandle();
this.set('bridgeUsername', result[0].success.username);
this.get('storage').set('huegasm.bridgeUsername', result[0].success.username);
}
this.set('bridgeAuthenticateReachedStatus', status);
}).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);
},
isAuthenticating: function(){
return this.get('bridgePingIntervalHandle') !== null;
}.property('bridgePingIntervalHandle')
});

View file

@ -0,0 +1,54 @@
<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">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/">Google Chrome</a> or <a href="https://www.mozilla.org/en-US/firefox/new/">Firefox</a></p>.
{{else}}
<img src="assets/images/pressButtonBridge.png" id="pressButtonBridgeImg">
{{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. <a class="noTextDecoration" href="#" {{action 'retry'}}>RETRY</a></p>
{{/if}}
{{/if}}
{{else}}
{{#unless bridgeFindStatus}}
{{paper-progress-circular}}
<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="bridgeButtonGroup">
{{#each multipleBridgeIps as |bridge|}}
{{#paper-radio value=bridge selected=bridgeIp}}{{bridge}}{{/paper-radio}}
{{/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="bridgeInput">
{{paper-input label="Hue bridge IP address" value=manualBridgeIp}}
{{#paper-button action="findBridgeByIp" raised=true primary=true}}Find{{/paper-button}}
</span>
{{#if manualBridgeIpNotFound}}
<p class="bg-danger">
Could not find a bridge with that IP address.
</p>
{{/if}}
{{/if}}
{{/if}}
{{/if}}
{{/unless}}

View file

@ -0,0 +1,55 @@
import Em from 'ember';
export default Em.Component.extend({
classNames: ['colorpicker'],
rgb: null,
canvas: null,
canvasContext: null,
mouseUp(){
this.set('pressingDown', false);
},
mouseMove(event){
if (this.get('pressingDown')) {
this.mouseDown(event);
}
},
mouseDown(event){
var canvasOffset = Em.$(this.get('canvas')).offset();
var canvasX = Math.floor(event.pageX - canvasOffset.left), canvasY = Math.floor(event.pageY - canvasOffset.top);
// get current pixel
var imageData = this.get('canvasContext').getImageData(canvasX, canvasY, 1, 1);
var pixel = imageData.data;
this.set('pressingDown', true);
if (!(pixel[0] === 0 && pixel[1] === 0 && pixel[2] === 0)) {
this.set('rgb', [pixel[0], pixel[1], pixel[2]]);
}
},
pressingDown: false,
// https://dzone.com/articles/creating-your-own-html5
didInsertElement(){
// handle color changes
var canvas = Em.$('#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
});
}
});

View file

@ -0,0 +1 @@
<canvas id="picker" width="256" height="256"></canvas>

View file

@ -0,0 +1,35 @@
import Em from 'ember';
export default Em.Component.extend({
actions: {
close: function(){
this.sendAction();
},
delete: function(){
var groupId = this.get('groupId');
Em.$.ajax(this.get('apiURL') + '/groups/' + groupId, {
contentType: 'application/json',
type: 'DELETE'
});
var groupsData = this.get('groupsData'), newGroupsData = [];
for (let key in groupsData) {
if(groupsData.hasOwnProperty(key) && groupsData[key].name !== this.get('groupName') ){
newGroupsData[key] = groupsData[key];
}
}
if(groupId === this.get('groupIdSelection')){
this.set('groupIdSelection', '0');
}
this.setProperties({
updateGroupsData: true,
groupsData: newGroupsData
});
this.sendAction();
}
}
});

View file

@ -0,0 +1,10 @@
{{#if isShowingModal}}
{{#modal-dialog close="close" alignment="center" translucentOverlay=true}}
<p>Are you sure you want to delete group "{{groupName}}"?</p>
{{#paper-button action="close"}}Close{{/paper-button}}
{{#paper-button class="pull-right" action="delete" primary=true}}Delete{{/paper-button}}
{{/modal-dialog}}
{{/if}}

View file

@ -0,0 +1,78 @@
import Em from 'ember';
export default Em.Component.extend({
classNames: ['dropdown-menu'],
elementId: 'groupList',
tagName: null,
groupIdSelection: null,
actions: {
selectGroup(selection){
this.set('groupIdSelection', selection);
},
toggleConfirmDeleteGroupsModal(groupName, groupId){
this.setProperties({
deleteGroupName: groupName,
deleteGroupId: groupId
});
this.toggleProperty('isShowingConfirmDeleteModal');
},
toggleAddGroupsModal(){
this.toggleProperty('isShowingAddGroupsModal');
}
},
groupsArrData: function(){
var groupsData = this.get('groupsData'), lightsData = this.get('lightsData'), groupsArrData = [], ids = [], groupIdSelection = this.get('groupIdSelection');
for (let key in lightsData) {
if(lightsData.hasOwnProperty(key) && lightsData[key].state.reachable){
ids.push(key);
}
}
groupsArrData.push({name: 'All', data: {lights: ids, key: '0' }, rowClass: groupIdSelection === '0' ? 'groupRow selectedRow' : 'groupRow', deletable: false});
for (let key in groupsData) {
if (groupsData.hasOwnProperty(key)) {
var rowClass = 'groupRow';
if(key === groupIdSelection){
rowClass += ' selectedRow';
}
groupsArrData.push({name: groupsData[key].name, data: {lights: groupsData[key].lights, key: key}, rowClass: rowClass, deletable: true});
}
}
return groupsArrData;
}.property('groupsData', 'groupIdSelection'),
onGroupIdSelectionChanged: function(){
var groupIdSelection = this.get('groupIdSelection'), lights = [];
this.get('groupsArrData').some(function(group){
if(group.data.key === groupIdSelection){
lights = group.data.lights;
return true;
}
});
this.get('storage').set('huegasm.selectedGroup', groupIdSelection);
if(!Em.isNone(groupIdSelection) && !Em.isEmpty(lights)){
this.set('activeLights', lights);
}
}.observes('groupIdSelection', 'groupsArrData'),
didInsertElement(){
var selectGroup = '0', storageItem = this.get('storage').get('huegasm.selectedGroup');
if(storageItem){
selectGroup = storageItem;
}
this.set('groupIdSelection', selectGroup);
}
});

View file

@ -0,0 +1,15 @@
{{#paper-list}}
{{#paper-item class="newGroupRow"}}
<div class="newGroup" {{action "toggleAddGroupsModal"}}>{{paper-icon icon="group-add"}} Add a new group</div>
{{/paper-item}}
{{#each groupsArrData as |group|}}
{{#paper-item class=group.rowClass}}
<div class="groupSelect" {{action "selectGroup" group.data.key}}>{{group.name}}</div> {{#if group.deletable}}<span data-toggle="tooltip" data-placement="top auto" title="Remove Group" class="bootstrapTooltip removeButton cursorPointer" {{action "toggleConfirmDeleteGroupsModal" group.name group.data.key}}>{{paper-icon icon="close"}}</span>{{/if}}
{{/paper-item}}
{{/each}}
{{/paper-list}}
{{add-group-modal lightsData=lightsData groupsData=groupsData isShowingModal=isShowingAddGroupsModal apiURL=apiURL updateGroupsData=updateGroupsData action="toggleAddGroupsModal"}}
{{delete-group-modal groupName=deleteGroupName groupId=deleteGroupId groupsData=groupsData isShowingModal=isShowingConfirmDeleteModal apiURL=apiURL updateGroupsData=updateGroupsData groupIdSelection=groupIdSelection action="toggleConfirmDeleteGroupsModal"}}

View file

@ -0,0 +1,280 @@
import Em from 'ember';
export default Em.Component.extend({
classNames: ['container-fluid'],
elementId: 'hueControls',
bridgeIp: null,
manualBridgeIp: null,
bridgeUsername: null,
updateGroupsData: true,
groupsData: null,
lightsData: null,
activeLights: [],
actions: {
changeTab(tabName){
var index = this.get('tabList').indexOf(tabName);
this.set('selectedTab', index);
this.get('storage').set('huegasm.selectedTab', index);
},
clearBridge() {
var storage = this.get('storage');
storage.remove('huegasm.bridgeUsername');
storage.remove('huegasm.bridgeIp');
location.reload();
},
clearAllSettings() {
this.get('storage').clear();
location.reload();
},
startIntro(){
var INTRO = introJs,
intro = INTRO(),
playerBottom = Em.$('#playerBottom'),
beatDetectionAreaArrowIcon = Em.$('#beatDetectionAreaArrowIcon');
this.set('dimmerOn', false);
intro.setOptions({
steps: [
{
intro: 'Welcome! This short tutorial will introduce you to Huegasm.'
},
{
element: '#musicTab',
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 or through the <b>Groups</b> menu dropdown.</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: '#playerArea',
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: '#beatOptionRow',
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: '#beatContainer',
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: '#lightsTab',
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: '#activeLights',
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: Em.$('.settingsItem')[0],
intro: 'The Groups menu allows for saving and quickly selecting groups of lights.',
position: 'left'
},
{
element: Em.$('.settingsItem')[1],
intro: 'A few miscellaneous settings can be found here.<br><br>' +
'<b>WARNING</b>: clearing application settings will restore the application to its original state. This will even delete your playlist and any saved song beat preferences.',
position: 'left'
},
{
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
intro.onchange((element) => {
if(element.id === 'musicTab' || element.id === 'playlist' || element.id === 'playerArea' || element.id === 'beatOptionRow' || element.id === 'beatOptionButtonGroup' || element.id === 'beatContainer' || element.id === 'usingMicAudioTooltip'){
Em.$('#musicTab').removeClass('hidden');
Em.$('#lightsTab').addClass('hidden');
Em.$('.navigationItem').eq(0).removeClass('active');
Em.$('.navigationItem').eq(1).addClass('active');
} else {
Em.$('#lightsTab').removeClass('hidden');
Em.$('#musicTab').addClass('hidden');
Em.$('.navigationItem').eq(1).removeClass('active');
Em.$('.navigationItem').eq(0).addClass('active');
}
if(element.id === 'musicTab' || element.id === 'playlist' || element.id === 'playerArea'){
playerBottom.hide();
if(beatDetectionAreaArrowIcon.hasClass('keyboard-arrow-up')){
beatDetectionAreaArrowIcon.removeClass('keyboard-arrow-up').addClass('keyboard-arrow-down');
}
} else if(element.id === 'beatOptionRow' || element.id === 'beatOptionButtonGroup' || element.id === 'beatContainer'){
playerBottom.show();
if(beatDetectionAreaArrowIcon.hasClass('keyboard-arrow-down')){
beatDetectionAreaArrowIcon.removeClass('keyboard-arrow-down').addClass('keyboard-arrow-up');
}
} else if(element.id === 'dimmer'){
Em.$(document).click();
}
});
var onFinish = ()=>{
this.set('activeTab', 1);
Em.$('#musicTab').removeClass('hidden');
Em.$('#lightsTab').addClass('hidden');
Em.$('.navigationItem').eq(0).removeClass('active');
Em.$('.navigationItem').eq(1).addClass('active');
if(beatDetectionAreaArrowIcon.hasClass('keyboard-arrow-up')){
playerBottom.show();
} else {
playerBottom.hide();
}
}, onExit = ()=>{
var dimmer = Em.$('#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)=>{
var elem = Em.$(element);
if(elem.html() === '<!---->'){
Em.$('.introjs-nextbutton').click();
}
}).onexit(onExit).oncomplete(onFinish).start();
}
},
apiURL: function(){
return 'http://' + this.get('bridgeIp') + '/api/' + this.get('bridgeUsername');
}.property('bridgeIp', 'bridgeUsername'),
didInsertElement(){
// here's a weird way to automatically initialize bootstrap tooltips
var observer = new MutationObserver(function(mutations) {
var haveTooltip = !mutations.every(function(mutation) {
return Em.isEmpty(mutation.addedNodes) || Em.isNone(mutation.addedNodes[0].classList) || mutation.addedNodes[0].classList.contains('tooltip');
});
if(haveTooltip) {
Em.run.once(this, function(){
Em.$('.bootstrapTooltip').tooltip();
});
}
});
observer.observe(Em.$('#hueControls')[0], {childList: true, subtree: true});
},
init() {
this._super();
if(!this.get('trial')) {
this.doUpdateGroupsData();
this.updateLightData();
this.set('lightsDataIntervalHandle', setInterval(this.updateLightData.bind(this), 2000));
}
if (!Em.isNone(this.get('storage').get('huegasm.selectedTab'))) {
this.set('selectedTab', this.get('storage').get('huegasm.selectedTab'));
}
},
onUpdateGroupsDataChange: function(){
if(this.get('updateGroupsData')){
setTimeout(()=>{ this.doUpdateGroupsData(); }, 1000);
}
}.observes('updateGroupsData'),
doUpdateGroupsData(){
Em.$.get(this.get('apiURL') + '/groups', (result, status)=>{
if (status === 'success' ) {
this.set('groupsData', result);
}
});
this.toggleProperty('updateGroupsData');
},
tabList: ["Lights", "Music"],
selectedTab: 1,
tabData: function(){
var tabData = [], selectedTab = this.get('selectedTab');
this.get('tabList').forEach(function(tab, i){
var selected = false;
if(i === selectedTab){
selected = true;
}
tabData.push({"name": tab, "selected": selected });
});
return tabData;
}.property('tabList', 'selectedTab'),
lightsTabSelected: Em.computed.equal('selectedTab', 0),
musicTabSelected: Em.computed.equal('selectedTab', 1),
pauseLightUpdates: false,
updateLightData(){
var fail = ()=>{
clearInterval(this.get('lightsDataIntervalHandle'));
this.get('storage').remove('huegasm.bridgeIp');
this.get('storage').remove('huegasm.bridgeUsername');
location.reload();
};
if(!this.get('pauseLightUpdates')){
Em.$.get(this.get('apiURL') + '/lights', (result, status)=>{
if(!Em.isNone(result[0]) && !Em.isNone(result[0].error)){
fail();
} else if (status === 'success' && JSON.stringify(this.get('lightsData')) !== JSON.stringify(result)) {
this.set('lightsData', result);
}
}).fail(fail);
}
},
dimmerOnClass: function(){
return this.get('dimmerOn') ? 'dimmerOn' : null;
}.property('dimmerOn'),
ready: function() {
return this.get('trial') || !Em.isNone(this.get('lightsData'));
}.property('lightsData', 'trial')
});

View file

@ -0,0 +1,34 @@
{{#if ready}}
<div class="row navigation">
<div class="col-sm-4 col-sm-offset-4 col-xs-8">
{{#each tabData as |tab|}}
<span class="navigationItem cursorPointer {{if tab.selected "active"}} text-uppercase" {{action "changeTab" tab.name}}>{{tab.name}}</span>
{{/each}}
</div>
<div id="settings" class="col-xs-4">
<div class="settingsItem">
<span data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">{{paper-icon icon="group" class=dimmerOnClass}}Groups <span class="caret"></span>
</span>
{{groups-list lightsData=lightsData groupsData=groupsData activeLights=activeLights apiURL=apiURL updateGroupsData=updateGroupsData groupControlDisplayed=groupControlDisplayed storage=storage}}
</div>
<div class="settingsItem">
<span data-toggle="dropdown" aria-haspopup="true" aria-expanded="false" id="settingsItemSettings">
{{paper-icon icon="settings" class=dimmerOnClass}}Settings <span class="caret"></span>
</span>
<ul id="appSettings" 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>
</div>
{{lights-tab apiURL=apiURL lightsData=lightsData activeLights=activeLights 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}}

View file

@ -0,0 +1,64 @@
import Em from 'ember';
export default Em.Component.extend({
actions: {
toggleDimmer(){
this.toggleProperty('dimmerOn');
},
isReady(){
this.set('ready', true);
}
},
bridgeIp: null,
bridgeUsername: null,
trial: false,
storage: null,
dimmerOn: false,
ready: false,
dimmerOnClass: function () {
var dimmerOn = this.get('dimmerOn'),
storage = this.get('storage'),
dimmerOnClass = null;
if (dimmerOn) {
Em.$('body').addClass('dimmerOn');
Em.$('html').addClass('dimmerOn');
dimmerOnClass = 'active';
} else {
Em.$('body').removeClass('dimmerOn');
Em.$('html').removeClass('dimmerOn');
}
storage.set('huegasm.dimmerOn', dimmerOn);
return dimmerOnClass;
}.property('dimmerOn'),
init(){
this._super();
var storage = new window.Locally.Store({compress: true});
this.set('storage', storage);
if (!Em.isNone(storage.get('huegasm.dimmerOn'))) {
this.set('dimmerOn', storage.get('huegasm.dimmerOn'));
}
if (!Em.isEmpty(storage.get('huegasm.bridgeIp')) && !Em.isEmpty(storage.get('huegasm.bridgeUsername'))) {
this.setProperties({
bridgeIp: storage.get('huegasm.bridgeIp'),
bridgeUsername: storage.get('huegasm.bridgeUsername')
});
}
},
year: function () {
return new Date().getFullYear();
}.property()
});

View file

@ -0,0 +1,24 @@
{{#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="readyBlock">
<div class="title"><img src="assets/images/logo.png" alt="Huegasm"></div>
<p id="intro">Your lights, meet your music. Huegasm.</p>
<p id="introParagraph">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="embedContainerWrapper">
<div class="embedContainer">
<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 action="isReady" class="goButton center-block"}}Go!{{/paper-button}}
</div>
{{/if}}
{{/if}}
<footer id="footer">
<p><span class="relative"><span id="dimmer" {{action "toggleDimmer"}} class={{dimmerOnClass}}>
</span>© {{year}} <a href="http://egorphilippov.me" target="_blank">Egor Philippov</a></span></p>
</footer>

View file

@ -0,0 +1,126 @@
import Em from 'ember';
export default Em.Component.extend({
classNames: ['lightGroup'],
isHovering: false,
lightsList: Em.A(),
actions: {
clickLight(id, data){
var light = Em.$('.light'+id);
if(!light.hasClass('bootstrapTooltip')){
light = light.parent();
}
if(light.hasClass('lightInactive')){
light.addClass('lightActive').removeClass('lightInactive');
} else if(light.hasClass('lightActive')){
light.addClass('lightInactive').removeClass('lightActive');
}
this.sendAction('action', id, data);
},
lightStartHover(id){
var hoveredLight = this.get('lightsList').filter(function(light){
return light.activeClass !== 'unreachable' && light.id === id[0];
});
if(!Em.isEmpty(hoveredLight) && this.get('noHover') !== true){
Em.$.ajax(this.get('apiURL') + '/lights/' + id + '/state', {
data: JSON.stringify({"alert": "lselect"}),
contentType: 'application/json',
type: 'PUT'
});
}
this.set('isHovering', true);
},
lightStopHover(id){
var hoveredLight = this.get('lightsList').filter(function(light){
return light.activeClass !== 'unreachable' && light.id === id[0];
});
if(!Em.isEmpty(hoveredLight) && this.get('noHover') !== true){
Em.$.ajax(this.get('apiURL') + '/lights/' + id + '/state', {
data: JSON.stringify({"alert": "none"}),
contentType: 'application/json',
type: 'PUT'
});
}
this.set('isHovering', false);
this.onLightsDataChange();
}
},
didInsertElement() {
if(this.get('lightsData')){
this.onLightsDataChange();
}
},
// list of all the lights in the hue system
onLightsDataChange: function(){
if(!this.get('isHovering')){
var lightsData = this.get('lightsData'), lightsList = Em.A(), type;
for (var key in lightsData) {
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';
}
var activeClass = 'lightActive';
if(!this.get('activeLights').contains(key)){
activeClass = 'lightInactive';
}
lightsList.push({type: type, name: lightsData[key].name, id: key, data: lightsData[key], activeClass: activeClass});
}
}
this.set('lightsList', lightsList);
}
}.observes('lightsData', 'activeLights.[]', 'dimmerOn')
});

View file

@ -0,0 +1,5 @@
{{#each lightsList as |light|}}
<div class="{{light.activeClass}} bootstrapTooltip light{{light.id}}" data-toggle="tooltip" data-placement="top auto" data-title={{light.name}} {{action "clickLight" light.id light.data}} {{action "lightStartHover" light.id on="mouseEnter"}} {{action "lightStopHover" light.id on="mouseLeave"}}>
<img class="hueLight" width="40" src="assets/images/lights/{{light.type}}{{if dimmerOn "w"}}.svg">
</div>
{{/each}}

View file

@ -0,0 +1,358 @@
import Em from 'ember';
export default Em.Component.extend({
classNames: ['col-sm-8', 'col-sm-offset-2', 'col-xs-12'],
classNameBindings: ['active::hidden'],
elementId: 'lightsTab',
activeLights: [],
lightsData: null,
lightsDataIntervalHandle: null,
colorPickerDisplayed: false,
actions: {
clickLight(light){
var activeLights = this.get('activeLights'),
lightId = activeLights.indexOf(light);
if(lightId !== -1){
activeLights.removeObject(light);
} else {
activeLights.pushObject(light);
// sync the current light settings to the newly added light
var options = {on: this.get('lightsOn'), bri: this.get('lightsBrightness'), effect: this.get('colorLoopOn') ? 'colorloop' : 'none'},
rgb = this.get('rgb');
if(rgb[0] !== 255 && rgb[1] !== 255 && rgb[2] !== 255) {
options['xy'] = this.rgbToXy(rgb[0], rgb[1], rgb[2]);
}
options['transitiontime'] = 0;
Em.$.ajax(this.get('apiURL') + '/lights/' + light + '/state', {
data: JSON.stringify(options),
contentType: 'application/json',
type: 'PUT'
});
}
},
toggleColorpicker() {
this.toggleProperty('colorPickerDisplayed');
}
},
didInsertElement() {
Em.$(document).click((event)=>{
if(this.get('colorPickerDisplayed') && !event.target.classList.contains('color') && !Em.$(event.target).closest('.colorpicker, #colorRow').length) {
this.toggleProperty('colorPickerDisplayed');
}
});
Em.$(document).on('click', '#colorRow', () => {
this.send('toggleColorpicker');
});
},
rgb: [255, 255, 255],
rgbPreview: function() {
var rgb = this.get('rgb'),
xy = this.rgbToXy(rgb[0], rgb[1], rgb[2]);
this.set('colorLoopOn', false);
this.get('activeLights').forEach((light) => {
Em.$.ajax(this.get('apiURL') + '/lights/' + light + '/state', {
data: JSON.stringify({"xy": xy}),
contentType: 'application/json',
type: 'PUT'
});
});
this.set('colorLoopOn', false);
Em.$('.color').css('background', 'rgb(' + rgb[0] + ',' + rgb[1] + ',' + rgb[2] + ')');
}.observes('rgb'),
colorRowAction: function() {
if (this.get('trial')) {
return null;
}
return 'toggleColorpicker';
}.property('trial'),
// COLOR LOOP related stuff
colorLoopOn: false,
onColorLoopOnChange: function(){
var lightsData = this.get('lightsData'),
activeLights = this.get('activeLights'),
colorLoopsOn = this.get('colorLoopOn'),
effect = colorLoopsOn ? 'colorloop' : 'none';
var colorLoopsOnSystem = activeLights.some(function(light) {
return lightsData[light].state.effect === 'colorloop';
});
// if the internal lights state is different than the one from lightsData ( user manually toggled the switch ), send the request to change the bulbs state
if(colorLoopsOn !== colorLoopsOnSystem){
activeLights.forEach((light)=>{
if(this.get('lightsData')[light].state.effect !== effect) {
Em.$.ajax(this.get('apiURL') + '/lights/' + light + '/state', {
data: JSON.stringify({'effect': effect }),
contentType: 'application/json',
type: 'PUT'
});
}
});
}
}.observes('colorLoopOn'),
lightsOn: false,
// determines whether the lights are on/off for the lights switch
lightsOnCHange: function(){
if(!this.get('strobeOn')){
var lightsData = this.get('lightsData'), lightsOn = this.get('activeLights').some(function(light) {
return lightsData[light].state.on === true;
});
this.set('lightsOn', lightsOn);
}
}.observes('lightsData.@each.state.on', 'activeLights.[]'),
// determines the average brightness of the hue system for the brightness slider
lightsBrightness: function(){
var lightsData = this.get('lightsData'), activeLights = this.get('activeLights'), lightsBrightness = 0;
activeLights.forEach(function(light){
lightsBrightness += lightsData[light].state.bri;
});
return lightsBrightness/activeLights.length;
}.property('lightsData'),
brightnessControlDisabled: Em.computed.not('lightsOn'),
onLightsOnChange: function(){
var lightsData = this.get('lightsData'), activeLights = this.get('activeLights'), lightsOn = this.get('lightsOn'), self = this;
var lightsOnSystem = activeLights.some(function(light) {
return lightsData[light].state.on === true;
});
// if the internal lights state is different than the one from lightsData ( user manually toggled the switch ), send the request to change the bulbs state
if(lightsOn !== lightsOnSystem){
activeLights.forEach(function (light) {
Em.$.ajax(self.get('apiURL') + '/lights/' + light + '/state', {
data: JSON.stringify({"on": lightsOn}),
contentType: 'application/json',
type: 'PUT'
});
});
}
}.observes('lightsOn'),
onBrightnessChanged: function(){
var lightsData = this.get('lightsData'), lightsBrightnessSystem = false, lightsBrightness = this.get('lightsBrightness'), activeLights = this.get('activeLights'), self = this;
activeLights.forEach(function(light){
lightsBrightnessSystem += lightsData[light].state.bri;
});
lightsBrightnessSystem /= activeLights.length;
// if the internal lights state is different than the one from lightsData ( user manually toggled the switch ), send the request to change the bulbs state
if(lightsBrightness !== lightsBrightnessSystem){
activeLights.forEach(function(light){
Em.$.ajax(self.get('apiURL') + '/lights/' + light + '/state', {
data: JSON.stringify({"bri": lightsBrightness}),
contentType: 'application/json',
type: 'PUT'
});
});
}
}.observes('lightsBrightness'),
lightsOnTxt: function(){
return this.get('lightsOn') ? 'On' : 'Off';
}.property('lightsOn'),
colorloopOnTxt: function(){
return this.get('colorLoopOn') ? 'On' : 'Off';
}.property('colorLoopOn'),
// **************** STROBE LIGHT START ****************
strobeOn: false,
strobeOnInervalHandle: null,
strobeSat: 0,
preStrobeOnLightsDataCache: null,
lastStrobeLight: 0,
onStrobeOnChange: function () {
var lightsData = this.get('lightsData'), self = this;
if (this.get('strobeOn')) {
this.set('preStrobeOnLightsDataCache', lightsData);
var stobeInitRequestData = {'sat': this.get('strobeSat'), 'transitiontime': 0};
for (let key in lightsData) {
if (lightsData.hasOwnProperty(key)) {
if (lightsData[key].state.on) {
stobeInitRequestData.on = false;
}
Em.$.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
var preStrobeOnLightsDataCache = this.get('preStrobeOnLightsDataCache'), updateLight = function (lightIndx) {
Em.$.ajax(self.get('apiURL') + '/lights/' + lightIndx + '/state', {
data: JSON.stringify({
'on': preStrobeOnLightsDataCache[lightIndx].state.on,
'sat': preStrobeOnLightsDataCache[lightIndx].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'));
}
}.observes('strobeOn'),
strobeStep() {
var 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);
}
Em.$.ajax(this.get('apiURL') + '/lights/' + lastStrobeLight + '/state', {
data: JSON.stringify(turnOnOptions),
contentType: 'application/json',
type: 'PUT'
});
Em.$.ajax(this.get('apiURL') + '/lights/' + lastStrobeLight + '/state', {
data: JSON.stringify({'on': false, 'transitiontime': 0}),
contentType: 'application/json',
type: 'PUT'
});
this.set('lastStrobeLight', lastStrobeLight);
},
strobeOnTxt: function () {
return this.get('strobeOn') ? 'On' : 'Off';
}.property('strobeOn'),
dimmerOnClass: function(){
return this.get('dimmerOn') ? 'dimmerOn' : null;
}.property('dimmerOn'),
// **************** STROBE LIGHT FINISH ****************
// http://www.developers.meethue.com/documentation/color-conversions-rgb-xy
rgbToXy(red, green, blue){
var X, Y, Z, x, y;
// 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){
var 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];
}
});

View file

@ -0,0 +1,42 @@
{{#paper-list}}
{{#paper-item class="item"}}
{{light-group lightsData=lightsData activeLights=activeLights action='clickLight' apiURL=apiURL classNames="horizontalLightGroup" dimmerOn=dimmerOn id="activeLights"}}
{{/paper-item}}
{{#paper-item}}
{{paper-icon icon="power-settings-new" class=dimmerOnClass}}
<p data-toggle="tooltip" data-placement="bottom auto" class="bootstrapTooltip lightCtrlTooltip" data-title="Turn the selected lights on/off">Power</p>
{{#paper-switch checked=lightsOn disabled=trial skipProxy=trial}} {{lightsOnTxt}} {{/paper-switch}}
{{/paper-item}}
{{#paper-item}}
{{paper-icon icon="brightness-4" class=dimmerOnClass}}
<p data-toggle="tooltip" data-placement="bottom auto" class="bootstrapTooltip lightCtrlTooltip" 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="colorRow"}}
{{paper-icon icon="color-lens" class=dimmerOnClass}}
<p data-toggle="tooltip" data-placement="bottom auto" class="bootstrapTooltip lightCtrlTooltip" data-title="The color of the selected lights">Color</p>
{{/paper-item}}
<div class="relative">
{{#paper-button raised=true class="color" action="toggleColorpicker" disabled=trial}}{{/paper-button}}
{{#if colorPickerDisplayed}}
{{color-picker lightsData=lightsData activeLights=activeLights rgb=rgb}}
{{/if}}
</div>
{{#paper-item}}
{{paper-icon icon="flare" class=dimmerOnClass}}
<p data-toggle="tooltip" data-placement="bottom auto" class="bootstrapTooltip lightCtrlTooltip" data-title="Selected lights will flash in sequential order">Strobe</p>
{{#paper-switch checked=strobeOn disabled=trial skipProxy=trial}} {{strobeOnTxt}} {{/paper-switch}}
{{/paper-item}}
{{#paper-item}}
{{paper-icon icon="color-lens" class=dimmerOnClass}} {{paper-icon icon="loop" id="loopAddition" class=dimmerOnClass}}
<p data-toggle="tooltip" data-placement="bottom auto" class="bootstrapTooltip lightCtrlTooltip" data-title="Selected lights will slowly cycle through all the colors">Colorloop</p>
{{#paper-switch checked=colorLoopOn disabled=trial skipProxy=trial}} {{colorloopOnTxt}} {{/paper-switch}}
{{/paper-item}}
{{/paper-list}}

View file

@ -0,0 +1,883 @@
import Em from 'ember';
import helperMixin from './mixins/helpers';
import visualizerMixin from './mixins/visualizer';
export default Em.Component.extend(helperMixin, visualizerMixin, {
onActiveChange: function(){
if(this.get('active')){
Em.$('#playNotification').removeClass('fadeOut');
Em.$('#beatSpeakerCenterOuter').removeClass('vibrateOuter');
Em.$('#beatSpeakerCenterInner').removeClass('vibrateInner');
}
}.observes('active'),
actions: {
clearPlaylist(){
this.get('playQueue').clear();
},
setVisName(name){
this.set('currentVisName', name);
},
hideTooltip(){
Em.$('.bootstrapTooltip').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){
Em.$('.tooltip').remove();
window.open(URL, '_blank');
},
handleNewSoundCloudURL(URL){
if(URL) {
SC.resolve(URL).then((resultObj)=>{
var processResult = (result)=>{
if(result.kind === 'user'){
this.get('notify').alert({html: this.get('scUserNotSupportedHtml')});
} else if(result.kind === 'track') {
if(result.streamable === true){
var picture = null;
if(result.artwork_url){
picture = result.artwork_url.replace('large', 't67x67');
} else if(result.user.avatar_url){
picture = result.user.avatar_url;
}
this.get('playQueue').pushObject({url: result.stream_url + '?client_id=' + this.get('SC_CLIENT_ID'), fileName: result.title + ' - ' + result.user.username, artist: result.user.username, scUrl: result.permalink_url, title: result.title, artworkUrl: result.artwork_url, picture: picture });
} else {
failedSongs.push(result.title);
}
} else if(result.kind === 'playlist'){
if(result.streamable === true){
result.tracks.forEach(processResult);
} else {
failedSongs.push(result.title);
}
}
},
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(){
var audioStream = this.get('audioStream');
this.changePlayerControl('audioMode', 0);
if(!Em.isNone(audioStream)){
var 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(!Em.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(){
this.$('#playerBottom').slideToggle();
this.changePlayerControl('playerBottomDisplayed', !this.get('playerBottomDisplayed'));
},
goToSong(index, playSong, scrollToSong){
var dancer = this.get('dancer'), playQueue = this.get('playQueue');
if(dancer.audio) {
this.clearCurrentAudio(true);
}
if(!Em.isNone(playQueue[index])) {
var 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)=>{
var 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
Em.run.later(()=>{
var track = Em.$('.track'+index), playListArea = Em.$('#playListArea');
if(!Em.isNone(track) && !Em.isNone(track.offset())) {
playListArea.animate({
scrollTop: track.offset().top - playListArea.offset().top + playListArea.scrollTop()
});
}
}, 1000);
}
}
},
removeAudio(index){
this.get('playQueue').removeAt(index);
// need to manually remove the tooltip
Em.$('body .tooltip').remove();
if(index === this.get('playQueuePointer')) {
this.send('goToSong', index, true, true);
}
},
playerAreaPlay(){
if(Em.isEmpty(Em.$('#playerControls:hover')) && this.get('playQueuePointer') !== -1 ){
this.send('play');
this.set('fadeOutNotification', true);
Em.$('#playNotification').removeClass('fadeOut').prop('offsetWidth', Em.$('#playNotification').prop('offsetWidth')).addClass('fadeOut');
}
},
play(replayPause) {
var 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 {
var 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;
}
Em.$(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) {
var 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
var 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.contains(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 {
var nextSong = this.get('playQueuePointer'),
playQueue = this.get('playQueue');
if(this.get('shuffle') && !Em.isNone(playQueue[nextSong])) { // go to the previously shuffled song
var shufflePlayed = this.get('shufflePlayed'),
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) {
var dancer = this.get('dancer');
if(dancer.audio){
dancer.audio.currentTime = Math.floor(this.get('timeTotal') * position / 100);
}
},
volumeMutedChanged(value) {
var dancer = this.get('dancer'),
volumeMuted = Em.isNone(value) ? !this.get('volumeMuted') : value;
this.changePlayerControl('volumeMuted', volumeMuted);
if(this.get('playing')){
if(volumeMuted){
dancer.setVolume(0);
} else {
dancer.setVolume(this.get('volume')/100);
}
}
},
addLocalAudio: function () {
Em.$('#fileInput').click();
},
shuffleChanged(value) {
this.changePlayerControl('shuffle', Em.isNone(value) ? !this.get('shuffle') : value);
},
repeatChanged(value) {
this.changePlayerControl('repeat', Em.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);
},
playListAreaDragOver(){
this.set('draggingOverPlayListArea', true);
},
playListAreaDragLeave(){
this.set('draggingOverPlayListArea', false);
},
handleNewFiles(files){
var self = this,
playQueue = this.get('playQueue'),
updatePlayQueue = function(){
var tags = ID3.getAllTags("local"),
picture = null;
if(tags.picture){
var base64String = "";
for (var 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 (var key in files) {
if (files.hasOwnProperty(key)) {
var file = files[key];
if(file.type.startsWith('audio')) {
ID3.loadTags("local", updatePlayQueue.bind(file),{
dataReader: new FileAPIReader(file),
tags: ['title', 'artist', 'album', 'track', 'picture']
});
}
}
}
}
},
changePlayerControl(name, value, saveBeatPrefs){
this.set(name, value);
if(name === 'threshold'){
this.get('kick').set({threshold: value});
}
if(saveBeatPrefs && this.get('usingLocalAudio') && this.get('playQueuePointer') !== -1){
this.saveSongBeatPreferences();
}
this.get('storage').set('huegasm.' + name, value);
},
saveSongBeatPreferences() {
var song = this.get('playQueue')[this.get('playQueuePointer')];
if(song) {
var title = Em.isEmpty(song.artist) ? song.fileName : song.artist + '-' + song.title,
songBeatPreferences = this.get('songBeatPreferences');
songBeatPreferences[title] = {threshold: this.get('threshold')};
this.set('usingBeatPreferences', true);
this.get('storage').set('huegasm.songBeatPreferences', songBeatPreferences);
}
},
loadSongBeatPreferences() {
var song = this.get('playQueue')[this.get('playQueuePointer')],
title = Em.isEmpty(song.artist) ? song.fileName : song.artist + '-' + song.title,
songBeatPreferences = this.get('songBeatPreferences'),
preference = songBeatPreferences[title],
oldBeatPrefCache = this.get('oldBeatPrefCache'),
newOldBeatPrefCache = null;
if(!Em.isNone(preference)) { // load existing beat prefs
newOldBeatPrefCache = {threshold: this.get('threshold')};
this.changePlayerControl('threshold', preference.threshold);
this.set('usingBeatPreferences', true);
} else if(!Em.isNone(oldBeatPrefCache)) { // revert to using beat prefs before the remembered song
this.changePlayerControl('threshold', oldBeatPrefCache.threshold);
this.set('usingBeatPreferences', false);
}
this.set('oldBeatPrefCache', newOldBeatPrefCache);
},
doAmbienceLightChange: function(justOneLight){
var activeLights = this.get('activeLights'),
lightsData = this.get('lightsData'),
workedLights = this.get('ambienceWorkedLights'),
hueRange = this.get('hueRange'),
ambienceWorkedLightsHandles = this.get('ambienceWorkedLightsHandles'),
lightOff = (light)=>{
if(this.get('ambienceMode') && this.get('playing')){
Em.$.ajax(this.get('apiURL') + '/lights/' + light + '/state', {
data: JSON.stringify({'on': false, 'transitiontime': 20}),
contentType: 'application/json',
type: 'PUT'
});
}
},
lights = [],
transitionTime = Math.floor(Math.random()*20),
iterations = justOneLight ? 1 : activeLights.length/2;
// pick some random lights
for(let i=0; i < iterations; i++){
let l = activeLights[Math.floor(Math.random()*activeLights.length)];
if(!lights.contains(l) && !workedLights.contains(l)){
lights.push(l);
workedLights.push(l);
} else if(justOneLight && workedLights.length !== activeLights.length){ // work a light if we only need one
while(workedLights.contains(l)){
l = activeLights[Math.floor(Math.random()*activeLights.length)];
}
lights.push(l);
workedLights.push(l);
}
}
lights.forEach((light)=>{
var options = {'hue': Math.floor(Math.random()*(hueRange[1] - hueRange[0] + 1)+hueRange[0]), 'bri': Math.floor(Math.random()*200) + 1, 'transitiontime': transitionTime};
if(lightsData[light].state.on === false){
options.on = true;
}
Em.$.ajax(this.get('apiURL') + '/lights/' + light + '/state', {
data: JSON.stringify(options),
contentType: 'application/json',
type: 'PUT'
});
// stop the light from turning off
if(ambienceWorkedLightsHandles[light]){
clearTimeout(ambienceWorkedLightsHandles[light]);
delete ambienceWorkedLightsHandles[light];
}
// turn the light off after it's been idle for a while
ambienceWorkedLightsHandles[light] = setTimeout(()=>{
lightOff(light);
workedLights.removeObject(light);
delete ambienceWorkedLightsHandles[light];
}, transitionTime * 100 + 1000);
});
},
onAmbienceModeChange: function() {
if(this.get('ambienceMode') && this.get('playing')) {
this.set('ambienceModeHandle', setInterval(()=> {this.doAmbienceLightChange();}, 5000));
this.setProperties({
'colorloopMode': false,
'flashingTransitions': false
});
} else if(this.get('ambienceModeHandle')) {
this.get('activeLights').forEach((light)=>{
Em.$.ajax(this.get('apiURL') + '/lights/' + light + '/state', {
data: JSON.stringify({'on': true}),
contentType: 'application/json',
type: 'PUT'
});
});
clearInterval(this.get('ambienceModeHandle'));
this.set('ambienceModeHandle', null);
}
}.observes('ambienceMode', 'playing'),
startUsingMic() {
navigator.getUserMedia(
{audio: true},
(stream) => {
this.changePlayerControl('audioMode', 1);
var dancer = this.get('dancer');
if(dancer.audio && dancer.audio.pause) {
dancer.pause();
}
this.setProperties({
volumeCache: this.get('volume'),
playing: true,
audioStream: stream
});
document.title = 'Listening to Mic - Huegasm';
dancer.load(stream, this.get('micBoost'), true);
this.set('usingBeatPreferences', false);
// much more sensitive beat preference settings are needed for mic mode
this.setProperties({
oldThreshold: this.get('threshold'),
threshold: 0.1
});
dancer.setVolume(0);
},
(err) => {
if(err.name === 'DevicesNotFoundError'){
this.get('notify').alert({html: this.get('notFoundHtml')});
}
console.log('Error during navigator.getUserMedia: ' + err.name + ', ' + err.message + ', ' + err.constraintName);
}
);
},
updatePageTitle: function(){
var title = 'Huegasm', playQueuePointer = this.get('playQueuePointer'), playQueue = this.get('playQueue');
if(playQueuePointer !== -1){
var song = playQueue[playQueuePointer];
if(song.title){
title = song.title;
if(song.artist){
title += (' - ' + song.artist);
}
} else {
title = song.fileName;
}
title += '- Huegasm';
}
document.title = title;
}.observes('playQueuePointer'),
clearCurrentAudio(resetPointer) {
var dancer = this.get('dancer');
if(dancer.audio.pause) {
dancer.pause();
}
if(resetPointer){
this.set('playQueuePointer', -1);
}
this.setProperties({
timeElapsed: 0,
timeTotal: 0,
playing: false
});
},
dragOver() {
var dragLeaveTimeoutHandle = this.get('dragLeaveTimeoutHandle');
this.set('dragging', true);
if (dragLeaveTimeoutHandle) {
clearTimeout(dragLeaveTimeoutHandle);
}
},
dragLeave(){
// need to delay the dragLeave notification to avoid flickering ( hovering over some page elements causes this event to be sent )
var self = this;
this.set('dragLeaveTimeoutHandle', setTimeout(function(){ self.set('dragging', false); }, 500));
},
simulateKick(/*mag, ratioKickMag*/) {
var activeLights = this.get('activeLights'),
lightsData = this.get('lightsData'),
color = null,
transitiontime = this.get('flashingTransitions'),
stimulateLight = (light, brightness, hue) => {
var options = {'bri': brightness};
if(transitiontime) {
options['transitiontime'] = 0;
} else {
options['transitiontime'] = 1;
}
if(!Em.isNone(hue)) {
options.hue = hue;
}
if(lightsData[light].state.on === false){
options.on = true;
}
Em.$.ajax(this.get('apiURL') + '/lights/' + light + '/state', {
data: JSON.stringify(options),
contentType: 'application/json',
type: 'PUT'
});
},
timeToBriOff = 100;
if(activeLights.length > 0 && !this.get('ambienceMode')){
var lastLightBopIndex = this.get('lastLightBopIndex'),
lightBopIndex,
brightnessOnBeat = 254,
light;
lightBopIndex = Math.floor(Math.random() * activeLights.length);
// let's try not to select the same light twice in a row
if(activeLights.length > 1) {
while(lightBopIndex === lastLightBopIndex) {
lightBopIndex = Math.floor(Math.random() * activeLights.length);
}
}
light = activeLights[lightBopIndex];
this.set('lastLightBopIndex', lightBopIndex);
if(!this.get('colorloopMode')) {
var hueRange = this.get('hueRange');
color = Math.floor(Math.random()*(hueRange[1] - hueRange[0] + 1)+hueRange[0]);
}
if(transitiontime){
timeToBriOff = 80;
}
stimulateLight(light, brightnessOnBeat, color);
setTimeout(stimulateLight, timeToBriOff, light, 1);
}
this.set('paused', true);
setTimeout(() => {
this.set('paused', false);
}, 150);
if(this.get('ambienceMode') && activeLights.length > 0){
this.doAmbienceLightChange(true);
}
//work the music beat area - simulate the speaker vibration by running a CSS animation on it
Em.$('#beatSpeakerCenterOuter').removeClass('vibrateOuter').prop('offsetWidth', Em.$('#beatSpeakerCenterOuter').prop('offsetWidth')).addClass('vibrateOuter');
Em.$('#beatSpeakerCenterInner').removeClass('vibrateInner').prop('offsetWidth', Em.$('#beatSpeakerCenterInner').prop('offsetWidth')).addClass('vibrateInner');
},
init() {
this._super();
window.requestAnimationFrame = window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || window.msRequestAnimationFrame;
window.cancelAnimationFrame = window.cancelAnimationFrame || window.webkitCancelAnimationFrame || window.mozCancelAnimationFrame || window.msCancelAnimationFrame;
navigator.getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia || navigator.msGetUserMedia;
var dancer = new Dancer(),
storage = this.get('storage'),
kick = dancer.createKick({
threshold: this.get('threshold'),
onKick: (mag, ratioKickMag) => {
if (this.get('paused') === false) {
this.simulateKick(mag, ratioKickMag);
}
}
});
kick.on();
this.setProperties({
dancer: dancer,
kick: kick
});
if(navigator.getUserMedia === undefined){
this.set('usingMicSupported', false);
}
['volume', 'shuffle', 'repeat', 'volumeMuted', 'threshold', 'playerBottomDisplayed', 'audioMode', 'songBeatPreferences', 'firstVisit', 'currentVisName', 'playQueue', 'playQueuePointer', 'micBoost', 'flashingTransitions', 'colorloopMode', 'ambienceMode', 'hueRange'].forEach((item)=>{
if (!Em.isNone(storage.get('huegasm.' + item))) {
var itemVal = storage.get('huegasm.' + item);
if(Em.isNone(this.actions[item+'Changed'])){
this.set(item, itemVal);
} else {
this.send(item + 'Changed', itemVal);
}
}
});
SC.initialize({
client_id: this.get('SC_CLIENT_ID')
});
},
didInsertElement() {
this._super();
var self = this;
// file input code
Em.$('#fileInput').on('change', function () {
var files = this.files;
self.send('handleNewFiles', files);
this.value = null; // reset in case upload the second file again
});
Em.$(document).on('click', '.alert', (event)=>{
Em.$(event.target).addClass('removed');
});
// prevent space/text selection when the user repeatedly clicks on the center
Em.$('#beatContainer').on('mousedown', '#beatSpeakerCenterInner', function(event) {
event.preventDefault();
});
Em.$(document).keypress((event) => {
if(event.which === 32 && event.target.type !== 'text'){
this.send('play');
}
});
this.$().on('drop', '#playListArea', (event)=>{
this.send('dropFiles', event.dataTransfer.files);
});
// control the volume by scrolling up/down
Em.$('#playerArea').on('mousewheel', (event)=>{
if(this.get('playQueueNotEmpty') && !this.get('usingMicAudio')) {
var scrollSize = 5;
if(event.deltaY < 0) {
scrollSize *= -1;
}
var newVolume = this.get('volume') + scrollSize;
this.send('volumeChanged', newVolume < 0 ? 0 : newVolume);
event.preventDefault();
}
});
// demo tracks
if(this.get('firstVisit')){
this.send('handleNewSoundCloudURL', 'https://soundcloud.com/mrsuicidesheep/candyland-speechless-feat-rkcb');
this.send('handleNewSoundCloudURL', 'https://soundcloud.com/mrsuicidesheep/vallis-alps-young-feki-remix');
this.send('handleNewSoundCloudURL', 'https://soundcloud.com/mrsuicidesheep/andrew-luce-when-to-love-you-feat-chelsea-cutler');
this.send('handleNewSoundCloudURL', 'https://soundcloud.com/mrsuicidesheep/ahh-ooh-carefree-with-me');
this.send('handleNewSoundCloudURL', 'https://soundcloud.com/odesza/light-feat-little-dragon');
this.send('handleNewSoundCloudURL', 'https://soundcloud.com/mrsuicidesheep/crywolf-slow-burn');
this.send('handleNewSoundCloudURL', 'https://soundcloud.com/mrsuicidesheep/clozee-red-forest');
this.send('handleNewSoundCloudURL', 'https://soundcloud.com/mrsuicidesheep/elo-method-subranger-solace');
this.send('handleNewSoundCloudURL', 'https://soundcloud.com/mrsuicidesheep/90-pounds-of-pete-waited-too-long-feat-devon-baldwin');
this.send('handleNewSoundCloudURL', 'https://soundcloud.com/mrsuicidesheep/draper-eyes-open');
this.get('storage').set('huegasm.firstVisit', false);
this.sendAction();
}
if(!this.get('playerBottomDisplayed')) {
Em.$('#playerBottom').hide();
}
}
});

View file

@ -0,0 +1,406 @@
import Em from 'ember';
export default Em.Mixin.create({
classNames: ['col-sm-10', 'col-sm-offset-1', 'col-xs-12'],
classNameBindings: ['active::hidden'],
elementId: 'musicTab',
dancer: null,
notify: Em.inject.service('notify'),
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: Em.A(),
timeElapsed: 0,
timeTotal: 0,
lastLightBopIndex: 0,
usingMicSupported: false,
// 0 - local, 1 - mic, possibly more to come
audioMode: 0,
usingLocalAudio: Em.computed.equal('audioMode', 0),
usingMicAudio: Em.computed.equal('audioMode', 1),
playerBottomDisplayed: false,
dragging: false,
draggingOverPlayListArea: false,
dragLeaveTimeoutHandle: null,
ambienceModeHandle: null,
audioStream: null,
dimmerOn: false,
isShowingAddSoundCloudModal: false,
colorloopMode: false,
ambienceMode: false,
flashingTransitions: false,
SC_CLIENT_ID: 'aeec0034f58ecd85c2bd1deaecc41594',
notFoundHtml: '<div class="alert alert-danger" role="alert">A microphone was not found.</div>',
scUserNotSupportedHtml: '<div class="alert alert-danger" role="alert">SoundCloud user URLs are not supported.</div>',
tooManySoundCloudFuckUps: '<div class="alert alert-danger" role="alert">The SoundCloud API is not seving the audio properly. More details <a href="https://www.soundcloudcommunity.com/soundcloud/topics/some-soundcloud-cdn-hosted-tracks-dont-have-access-control-allow-origin-header" target="_blank">HERE</a>.</div>',
notStreamableHtml(fileNames){
var html = '<div class="alert alert-danger" role="alert">The following file(s) could not be added because they are not allowed to be streamed:<br>' + fileNames.toString().replace(/,/g, '<br>') + '</div>';
return html;
},
urlNotFoundHtml(url){
return '<div class="alert alert-danger" role="alert">The URL ( ' + url + ' ) could not be resolved.</div>';
},
failedToPlayFileHtml(fileName){
return '<div class="alert alert-danger" role="alert">Failed to play file ( ' + fileName + ' ).</div>';
},
failedToDecodeFileHtml(fileName){
return '<div class="alert alert-danger" role="alert">Failed to decode file ( ' + fileName + ' ).</div>';
},
scUrl: function(){
var rtn = null,
currentSong = this.get('playQueue')[this.get('playQueuePointer')];
if(currentSong && currentSong.scUrl && !this.get('usingMicAudio')){
rtn = currentSong.scUrl;
}
return rtn;
}.property('playQueuePointer', 'playQueue.[]', 'usingMicAudio'),
playQueueEmpty: Em.computed.empty('playQueue'),
playQueueNotEmpty: Em.computed.notEmpty('playQueue'),
playQueueMultiple: function(){
return this.get('playQueue').length > 1;
}.property('playQueue.[]'),
seekPosition: function() {
var timeTotal = this.get('timeTotal'), timeElapsed = this.get('timeElapsed');
if (timeTotal === 0) {
return 0;
}
return timeElapsed/timeTotal*100;
}.property('timeElapsed', 'timeTotal'),
// 0 - no repeat, 1 - repeat all, 2 - repeat one
repeat: 0,
shuffle: false,
volumeMuted: false,
volume: 100,
// beat detection related pausing
paused: false,
// audio: playing or paused
playing: false,
fadeOutNotification: false,
songBeatPreferences: {},
usingBeatPreferences: false,
oldBeatPrefCache: null,
storage: null,
firstVisit: true,
ambienceWorkedLights: [],
ambienceWorkedLightsHandles: {},
soundCloudFuckUps: 0,
maxSoundCloudFuckUps: 3,
largeArtworkPic: function(){
var pic = null,
currentVisName = this.get('currentVisName'),
usingMicAudio = this.get('usingMicAudio'),
playQueuePointer = this.get('playQueuePointer'),
playQueue = this.get('playQueue');
if(playQueuePointer !== -1 && !usingMicAudio && currentVisName === 'None'){
var song = playQueue[playQueuePointer];
if(song.scUrl){
pic = song.picture.replace('67x67', '500x500');
} else {
pic = song.picture;
}
}
return pic;
}.property('playQueuePointer', 'usingMicAudio', 'currentVisName'),
// used to insure that we don't replay the same thing multiple times in shuffle mode
shufflePlayed: [],
pauseLightUpdates: function(){
return this.get('playing');
}.property('playing'),
micIcon: function () {
if (this.get('usingMicAudio')) {
return 'mic';
}
return 'mic-off';
}.property('usingMicAudio'),
repeatIcon: function () {
if (this.get('repeat') === 2) {
return 'repeat-one';
}
return 'repeat';
}.property('repeat'),
playingIcon: 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';
}
}.property('playing'),
playListAreaClass: function(){
var classes = 'cursorPointer';
if(this.get('dragging')){
classes += ' dragHereHighlight';
}
if(this.get('draggingOverPlayListArea')){
classes += ' draggingOver';
}
if(this.get('dimmerOn')){
classes += ' dimmerOn';
}
return classes;
}.property('dragging', 'draggingOverPlayListArea', 'dimmerOn'),
dimmerOnClass: function(){
return this.get('dimmerOn') ? 'dimmerOn' : null;
}.property('dimmerOn'),
volumeMutedClass: function(){
var classes = 'playerControllIcon volumeButton';
if(this.get('volumeMuted')){
classes += ' active';
}
return classes;
}.property('volumeMuted'),
usingLocalAudioClass: function() {
return this.get('usingLocalAudio') ? 'playerControllIcon active' : 'playerControllIcon';
}.property('usingLocalAudio'),
usingMicAudioClass: function() {
return this.get('usingMicAudio') ? 'playerControllIcon active' : 'playerControllIcon';
}.property('usingMicAudio'),
repeatClass: function () {
return this.get('repeat') !== 0 ? 'playerControllIcon active' : 'playerControllIcon';
}.property('repeat'),
shuffleClass: function () {
return this.get('shuffle') ? 'playerControllIcon active' : 'playerControllIcon';
}.property('shuffle'),
volumeIcon: function () {
var 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';
}
}.property('volumeMuted', 'volume'),
onColorloopModeChange: function(){
var colorLoop = ((this.get('playing') || this.get('usingMicAudio')) && this.get('colorloopMode')) ? true : false;
this.set('colorLoopOn', colorLoop);
}.observes('colorloopMode', 'usingMicAudio', 'playing'),
onOptionChange: function(self, option){
option = option.replace('.[]', '');
this.get('storage').set('huegasm.' + option, this.get(option));
}.observes('flashingTransitions', 'playQueue.[]', 'playQueuePointer', 'colorloopMode', 'ambienceMode'),
onRepeatChange: function () {
var tooltipTxt = 'Repeat all', type = 'repeat';
if (this.get(type) === 1) {
tooltipTxt = 'Repeat one';
} else if (this.get(type) === 2) {
tooltipTxt = 'Repeat off';
}
this.changeTooltipText(type, tooltipTxt);
}.observes('repeat').on('init'),
onUsingMicAudioChange: function(){
var tooltipTxt = 'Listen to audio through mic', type = 'usingMicAudio';
if (this.get(type)) {
tooltipTxt = 'Listen to audio files';
}
this.changeTooltipText(type, tooltipTxt);
}.observes('usingMicAudio').on('init'),
onShuffleChange: function () {
var tooltipTxt = 'Shuffle', type = 'shuffle';
if (this.get(type)) {
this.get('shufflePlayed').clear();
tooltipTxt = 'Unshuffle';
}
this.changeTooltipText(type, tooltipTxt);
}.observes('shuffle').on('init'),
onVolumeMutedChange: function () {
var 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);
}.observes('volumeMuted').on('init'),
onPrevChange: function() {
if(this.get('playQueueNotEmpty')){
var tooltipTxt = 'Previous', type = 'prev';
if(this.get('timeElapsed') > 5 || this.get('playQueue').length === 1) {
tooltipTxt = 'Replay';
}
this.changeTooltipText(type, tooltipTxt);
}
}.observes('timeElapsed', 'playQueueNotEmpty', 'playQueue.[]'),
onPlayingChange: function () {
var 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);
}.observes('playing').on('init'),
changeTooltipText(type, text) {
// change the tooltip text if it's already visible
Em.$('#' + type + 'Tooltip + .tooltip .tooltip-inner').html(text);
//change the tooltip text for hover
Em.$('#' + type + 'Tooltip').attr('data-original-title', text);
if(Em.isNone(this.get(type + 'TooltipTxt'))) {
this.set(type + 'TooltipTxt', text);
}
},
beatDetectionAreaArrowIcon: function(){
if(!this.get('playerBottomDisplayed')){
return 'keyboard-arrow-down';
} else {
return 'keyboard-arrow-up';
}
}.property('playerBottomDisplayed'),
timeElapsedTxt: function(){
return this.formatTime(this.get('timeElapsed'));
}.property('timeElapsed'),
timeTotalTxt: function() {
return this.formatTime(this.get('timeTotal'));
}.property('timeTotal'),
formatTime(time){
return this.pad(Math.floor(time/60), 2) + ':' + this.pad(time%60, 2);
},
pad(num, size){ return ('000000000' + num).substr(-size); }
});

View file

@ -0,0 +1,88 @@
import Em from 'ember';
export default Em.Mixin.create({
currentVisName: 'None',
visNames: ['None', 'Bars', 'Wave'],
onCurrentVisNameChange: function () {
var currentVisName = this.get('currentVisName');
if(currentVisName === 'None'){
var canvasEl = Em.$('#visualization')[0],
ctx = canvasEl.getContext('2d');
ctx.clearRect(0, 0, canvasEl.width, canvasEl.height);
}
this.get('storage').set('huegasm.currentVisName', currentVisName);
}.observes('currentVisName'),
didInsertElement(){
var dancer = this.get('dancer'),
canvas = Em.$('#visualization')[0],
playerArea = Em.$('#playerArea'),
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
var syncCanvasHeight = ()=>{
w = playerArea.width();
canvas.width = w;
};
syncCanvasHeight();
Em.$(window).on('resize', syncCanvasHeight);
dancer.bind('update', () => {
var 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;
var 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;
var spectrum = dancer.getSpectrum();
for (let i = 0, l = spectrum.length; i < l && i < count; i++) {
ctx.fillRect(i * ( spacing + width ), h, width, -spectrum[i] * h - 60);
}
}
});
}
});

View file

@ -0,0 +1,207 @@
<div class="row" id="step1">
<div id="playerArea" class="col-sm-8 col-xs-12 {{if (eq "None" currentVisName) "displayIcon"}}" {{action "playerAreaPlay"}}>
<canvas id="visualization"></canvas>
<div id="artwork"><img src={{largeArtworkPic}}></div>
<div id="playNotification" class="material-icons {{if fadeOutNotification "fadeOut"}} {{if playing "play-arrow" "pause"}}"></div>
<div id="playerControls">
{{#if usingLocalAudio}}
{{range-slider start=seekPosition min=0 max=100 id="seekSlider" slide="seekChanged"}}
{{#if playQueueNotEmpty}}
<span data-toggle="tooltip" data-placement="top" class="bootstrapTooltip" id="prevTooltip"
data-title={{prevTooltipTxt}} {{action "previous"}}>{{paper-icon icon="skip-previous" class="playerControllIcon"}}</span><!--
-->{{/if}}<!--
--><span data-toggle="tooltip" data-placement="top" id="playingTooltip" class="bootstrapTooltip"
data-title={{playingTooltipTxt}} {{action "play"}}>{{paper-icon icon=playingIcon class="playerControllIcon"}}</span><!--
-->{{#if playQueueMultiple}}<!--
--><span data-toggle="tooltip" data-placement="top" class="bootstrapTooltip"
data-title="Next" {{action "next" true}}>{{paper-icon icon="skip-next" action="" class="playerControllIcon"}}</span><!--
-->{{/if}}<!--
--><span data-toggle="tooltip" data-placement="top" class="bootstrapTooltip" id="volumeMutedTooltip"
data-title={{volumeMutedTooltipTxt}} {{action "volumeMutedChanged"}}>{{paper-icon icon=volumeIcon class=volumeMutedClass}}</span><!--
-->{{range-slider start=volume min=0 max=100 slide="volumeChanged" id="volumeBar" class="hidden-xs"}}
<div id="playerTimeControls">{{timeElapsedTxt}} / {{timeTotalTxt}}</div>
{{/if}}
<span class="pull-right">
{{#if scUrl}}
<a href="#" data-toggle="tooltip" data-placement="top" class="soundCloudLink bootstrapTooltip" 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="bootstrapTooltip" data-title="Visualizations" {{action "hideTooltip"}}>
{{paper-icon icon="remove-red-eye" class="playerControllIcon"}}
</span>
</span>
<ul class="dropdown-menu visualizersMenu">
{{#each visNames as |name|}}
<li><a href="#" {{action "setVisName" name}}>{{name}} {{#if (eq currentVisName name)}}{{paper-icon icon="check"}}{{/if}}</a></li>
{{/each}}
</ul>
</span>
</span>
</div>
</div>
<div id="playlist" class="col-sm-4 col-xs-12">
<input id="fileInput" type="file" accept="audio/*" multiple="true"/>
<div id="playListControls">
{{#if usingLocalAudio}}
<button class="dropdown-toggle pull-right addNewMusic" type="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="true">
Add new music
<span class="caret"></span>
</button>
<ul id="addMusicChoices" class="dropdown-menu">
<li><a href="#" {{action "addLocalAudio"}}>Local</a></li>
<li><a href="#" {{action "toggleIsShowingAddSoundCloudModal"}}>SoundCloud</a></li>
</ul>
{{!--{{#if (and usingLocalAudio playQueueNotEmpty)}}
<div id="extraOptionsMenu" class="hidden-xs">
<span data-toggle="tooltip" data-placement="bottom auto" class="bootstrapTooltip" id="shuffleTooltip" data-title={{shuffleTooltipTxt}} {{action "shuffleChanged"}}>{{paper-icon icon="shuffle" class=shuffleClass}}</span>
<span data-toggle="tooltip" data-placement="bottom auto" class="bootstrapTooltip" id="repeatTooltip" data-title={{repeatTooltipTxt}} {{action "repeatChanged"}}>{{paper-icon icon=repeatIcon class=repeatClass}}</span>
<span data-toggle="tooltip" data-placement="bottom auto" class="bootstrapTooltip" data-title="Clear playlist" {{action "clearPlaylist"}}>{{paper-icon icon="clear-all" class="playerControllIcon"}}</span>
</div>
{{/if}} --}}
{{/if}}
{{#if usingMicSupported}}
<span data-toggle="tooltip" data-placement="bottom auto" class="bootstrapTooltip" id="usingMicAudioTooltip" data-title={{usingMicAudioTooltipTxt}} {{action "useMicAudio"}}>{{paper-icon icon=micIcon class=usingMicAudioClass}}</span>
{{/if}}
{{#if usingLocalAudio}}
<span data-toggle="tooltip" data-placement="bottom auto" class="bootstrapTooltip" id="shuffleTooltip" data-title={{shuffleTooltipTxt}} {{action "shuffleChanged"}}>{{paper-icon icon="shuffle" class=shuffleClass}}</span>
<span data-toggle="tooltip" data-placement="bottom auto" class="bootstrapTooltip" id="repeatTooltip" data-title={{repeatTooltipTxt}} {{action "repeatChanged"}}>{{paper-icon icon=repeatIcon class=repeatClass}}</span>
<span data-toggle="tooltip" data-placement="bottom auto" class="bootstrapTooltip" data-title="Clear playlist" {{action "clearPlaylist"}}>{{paper-icon icon="clear-all" class="playerControllIcon"}}</span>
{{/if}}
</div>
{{#if usingMicAudio}}
<div id="playAreaMic" class="{{if dimmerOn "dimmerOn"}}">
{{paper-icon icon="mic" class=dimmerOnClass}}
</div>
{{else}}
{{#if usingLocalAudio}}
<div id="playListArea" class={{playListAreaClass}} {{action "addLocalAudio"}} {{action "playListAreaDragOver" on="dragOver"}} {{action "playListAreaDragLeave" 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 icon="library-music" class=dimmerOnClass}}
{{/if}}
{{#each playQueue as |item index|}}
<div class="playlistItem cursorPointer track{{index}} {{if (eq index playQueuePointer) "active"}} {{if dragging "hidden"}}" {{action "goToSong" index true bubbles=false}}>
{{#if item.picture}}
<img class="albumArt" src={{item.picture}}>
{{else}}
<img class="albumArt" src="assets/images/missingArtwork.png">
{{/if}}
<div class="songInfo">
{{#if item.title}}
<div class="songTitle">{{item.title}}</div>
<div class="songArtist">
{{#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="audioRemoveButton cursorPointer bootstrapTooltip" {{action "removeAudio" index bubbles=false}}>{{paper-icon icon="close"}}</span>
</div>
{{/each}}
</div>
{{/if}}
{{/if}}
</div>
</div>
<div id="slideToggle" class="text-center cursorPointer row" {{action "slideTogglePlayerBottom"}}>
<div class="col-xs-offset-5 col-xs-2">
{{paper-icon icon=beatDetectionAreaArrowIcon id="beatDetectionAreaArrowIcon"}}
</div>
</div>
<div id="playerBottom" class="row {{if dimmerOn "dimmerOn"}}">
<div id="beatArea" 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="bootstrapTooltip" id="saveBeatPreferencesStar">
{{paper-icon class=dimmerOnClass icon="star"}}
</span>
{{/if}}
<div class="row" id="beatOptionRow">
<div class="beatOption 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="optionDescription bootstrapTooltip">Sensitivity</span>
{{range-slider start=threshold orientation="vertical" step=beatOptions.threshold.step range=beatOptions.threshold.range slide="thresholdChanged" pips=beatOptions.threshold.pips}}
</div>
<div class="beatOption 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="optionDescription bootstrapTooltip">Hue Range</span>
{{range-slider start=hueRange orientation="vertical" step=beatOptions.hueRange.step range=beatOptions.hueRange.range slide="hueRangeChanged" pips=beatOptions.hueRange.pips}}
</div>
{{#if usingMicAudio}}
<div class="beatOption col-xs-4">
<span data-toggle="tooltip" data-placement="bottom" data-title="The coefficient to boost the microphone signal by" class="optionDescription bootstrapTooltip">Mic Boost</span>
{{range-slider start=micBoost orientation="vertical" step=beatOptions.micBoost.step range=beatOptions.micBoost.range slide="micBoostChanged" pips=beatOptions.micBoost.pips}}
</div>
{{/if}}
<div id="lightOption" class="beatOption col-xs-4">
<span data-toggle="tooltip" data-placement="bottom auto" data-title="Quickly flash the lights on beat" class="bootstrapTooltip" {{action "hideTooltip" on="mouseLeave"}}>
{{#paper-checkbox checked=flashingTransitions}}Flashing Transitions{{/paper-checkbox}}
</span>
<span data-toggle="tooltip" data-placement="bottom auto" data-title="Slowly cycle the lights through all the colors" class="bootstrapTooltip" {{action "hideTooltip" on="mouseLeave"}}>
{{#paper-checkbox checked=colorloopMode}}Colorloop{{/paper-checkbox}}
</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="bootstrapTooltip" {{action "hideTooltip" on="mouseLeave"}}>
{{#paper-checkbox checked=ambienceMode}}Ambience{{/paper-checkbox}}
</span>--}}
</div>
</div>
</div>
<div id="beatContainer" class="col-sm-5 hidden-xs">
<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="beatSpeakerCenterOuter">
<div id="beatSpeakerCenterInner" class="cursorPointer" {{action "clickSpeaker"}}>
</div>
</div>
</div>
</div>
</div>
{{ember-notify closeAfter=100000 classPrefix="customNotify"}}
{{add-soundcloud-sound-modal action="handleNewSoundCloudURL" isShowingModal=isShowingAddSoundCloudModal}}

3
app/resolver.js Normal file
View file

@ -0,0 +1,3 @@
import Resolver from 'ember-resolver';
export default Resolver;

11
app/router.js Normal file
View file

@ -0,0 +1,11 @@
import Ember from 'ember';
import config from './config/environment';
const Router = Ember.Router.extend({
location: config.locationType
});
Router.map(function() {
});
export default Router;

1169
app/styles/app.scss Normal file

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,153 @@
/* Variables */
$centersize: 80px;
$center1size: 205px;
$bezelsize: 240px;
$vibratesize: $centersize*1.06;
$vibratemargin:-$vibratesize/2;
$vibrateblurinner: 3px;
$vibrateblurouter: 2px;
/* 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;
}
/* Keyframes */
@keyframes vibrateInner {
50% {
-webkit-filter: blur($vibrateblurinner);
transform: scale(1.05);
}
}
@keyframes vibrateOuter {
0% {
-webkit-filter: blur($vibrateblurouter);
filter: blur($vibrateblurouter);
}
30% {
-webkit-filter: blur(0);
filter: blur(0);
}
50% {
-webkit-filter: blur($vibrateblurouter);
filter: blur($vibrateblurouter);
}
65% {
-webkit-filter: blur(0);
filter: blur(0);
}
70% {
-webkit-filter: blur($vibrateblurouter);
filter: blur($vibrateblurouter);
}
80% {
-webkit-filter: blur($vibrateblurouter);
filter: blur($vibrateblurouter);
}
100% {
-webkit-filter: blur($vibrateblurouter);
filter: blur($vibrateblurouter);
}
}
#beatSpeakerCenterInner {
@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);
}
.vibrateInner{
-webkit-animation: vibrateInner 0.15s linear 1;
animation: vibrateInner 0.15s linear 1;
}
#beatSpeakerCenterOuter {
@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%);
}
.vibrateOuter {
-webkit-animation: vibrateOuter 0.15s linear 1;
animation: vibrateOuter 0.15s linear 1;
}
.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%;
}

View file

@ -0,0 +1 @@
{{huegasm-app}}

BIN
assets/icon.psd Normal file

Binary file not shown.

BIN
assets/intro.psd Normal file

Binary file not shown.

BIN
assets/missingArtwork.psd Normal file

Binary file not shown.

Binary file not shown.

19
bower.json Normal file
View file

@ -0,0 +1,19 @@
{
"name": "huegasm",
"dependencies": {
"bootstrap-sass": "~3.3.5",
"ember": "~2.4.1",
"ember-cli-shims": "0.1.0",
"ember-cli-test-loader": "0.2.2",
"ember-load-initializers": "0.5.1",
"ember-qunit-notifications": "0.1.0",
"hammerjs": "~2.0.4",
"intro.js": "~1.1.1",
"JavaScript-ID3-Reader": "https://github.com/aadsm/JavaScript-ID3-Reader.git",
"jquery-mousewheel": "~3.1.13",
"locallyjs": "~0.3.2",
"matchMedia": "~0.2.0",
"nouislider": "^8.3.0",
"HackTimer": "https://github.com/turuslan/HackTimer.git#~1.0.0"
}
}

48
config/environment.js Normal file
View file

@ -0,0 +1,48 @@
/* jshint node: true */
module.exports = function(environment) {
var ENV = {
modulePrefix: 'huegasm',
podModulePrefix: 'huegasm/pods',
environment: environment,
baseURL: '/',
locationType: 'auto',
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.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.baseURL = '/';
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') {
//ENV.baseURL = '/huegasm';
}
return ENV;
};

32
ember-cli-build.js Normal file
View file

@ -0,0 +1,32 @@
/* 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/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/HackTimer/HackTimer.js');
app.import('bower_components/intro.js/intro.js');
app.import('bower_components/intro.js/introjs.css');
// Use `app.import` to add additional libraries to the generated
// output files.
//
// If you need to use different assets in different
// environments, specify an object as the first parameter. That
// object's keys should be the environment name and the values
// should be the asset to use in that environment.
//
// If the library that you are including contains AMD or ES6
// modules that you would like to import into your application
// please specify an object with the list of modules as keys
// along with the exports of each module as its value.
return app.toTree();
};

49
package.json Normal file
View file

@ -0,0 +1,49 @@
{
"name": "huegasm",
"version": "1.0.0",
"description": "Small description for huegasm goes here",
"private": true,
"directories": {
"doc": "doc",
"test": "tests"
},
"scripts": {
"start": "ember server",
"build": "ember build",
"test": "ember test"
},
"repository": "",
"engines": {
"node": ">= 0.10.0"
},
"author": "",
"license": "MIT",
"devDependencies": {
"broccoli-asset-rev": "^2.2.0",
"ember-ajax": "0.7.1",
"ember-cli": "^2.4.2",
"ember-cli-app-version": "^1.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.7.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-uglify": "^1.2.0",
"ember-cli-windows-addon": "^1.2.2",
"ember-data": "^2.4.0",
"ember-disable-proxy-controllers": "^1.0.1",
"ember-export-application-global": "^1.0.4",
"ember-load-initializers": "^0.5.0",
"ember-modal-dialog": "0.8.3",
"ember-notify": "^5.0.2",
"ember-paper": "^0.2.12",
"ember-resolver": "^2.0.3",
"ember-truth-helpers": "1.2.0",
"loader.js": "^4.0.0"
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.9 KiB

BIN
public/apple-touch-icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.2 KiB

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 292 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

12
public/browserconfig.xml Normal file
View file

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<browserconfig>
<msapplication>
<tile>
<square70x70logo src="/mstile-70x70.png"/>
<square150x150logo src="/mstile-150x150.png"/>
<square310x310logo src="/mstile-310x310.png"/>
<wide310x150logo src="/mstile-310x150.png"/>
<TileColor>#da532c</TileColor>
</tile>
</msapplication>
</browserconfig>

15
public/crossdomain.xml Normal file
View 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
public/favicon-16x16.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

BIN
public/favicon-194x194.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.2 KiB

BIN
public/favicon-32x32.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

BIN
public/favicon-96x96.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

BIN
public/favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

13
public/humans.txt Normal file
View file

@ -0,0 +1,13 @@
/* 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
/* 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

41
public/manifest.json Normal file
View file

@ -0,0 +1,41 @@
{
"name": "Huegasm",
"icons": [
{
"src": "\/android-chrome-36x36.png",
"sizes": "36x36",
"type": "image\/png",
"density": "0.75"
},
{
"src": "\/android-chrome-48x48.png",
"sizes": "48x48",
"type": "image\/png",
"density": "1.0"
},
{
"src": "\/android-chrome-72x72.png",
"sizes": "72x72",
"type": "image\/png",
"density": "1.5"
},
{
"src": "\/android-chrome-96x96.png",
"sizes": "96x96",
"type": "image\/png",
"density": "2.0"
},
{
"src": "\/android-chrome-144x144.png",
"sizes": "144x144",
"type": "image\/png",
"density": "3.0"
},
{
"src": "\/android-chrome-192x192.png",
"sizes": "192x192",
"type": "image\/png",
"density": "4.0"
}
]
}

BIN
public/mstile-144x144.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.2 KiB

BIN
public/mstile-150x150.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.7 KiB

BIN
public/mstile-310x150.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.9 KiB

Some files were not shown because too many files have changed in this diff Show more