add volume control to the player
authorymh <ymh.work@gmail.com>
Mon, 19 Dec 2016 21:58:02 +0100
changeset 477 ce52f0fca330
parent 476 9cffc7f32f14
child 478 b621657bb436
add volume control to the player
cms/app-client/app/components/player-sound-control.js
cms/app-client/app/styles/app.scss
cms/app-client/app/styles/components/player-component.scss
cms/app-client/app/styles/components/player-sound-control.scss
cms/app-client/app/templates/components/player-component.hbs
cms/app-client/app/templates/components/player-sound-control.hbs
cms/app-client/ember-cli-build.js
cms/app-client/tests/integration/components/player-sound-control-test.js
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cms/app-client/app/components/player-sound-control.js	Mon Dec 19 21:58:02 2016 +0100
@@ -0,0 +1,118 @@
+import Ember from 'ember';
+import * as d3s from 'd3-scale';
+
+const speakerClassScale = d3s.scaleQuantize()
+  .domain([0, 1])
+  .range(['fa-volume-down', 'fa-volume-up']);
+
+
+export default Ember.Component.extend({
+  classNames: ['player-sound-control'],
+
+  popcorn: null,
+
+  volume: Ember.computed('popcorn', {
+    get: function() {
+      let popcorn = this.get('popcorn');
+      if(!popcorn) {
+        return 0;
+      }
+      return popcorn.volume();
+    },
+    set: function(key, value) {
+      let popcorn = this.get('popcorn');
+      if(!popcorn) {
+        return 0;
+      }
+      const newValue = Math.min(1.0, Math.max(0, value));
+      popcorn.volume(newValue);
+      return newValue;
+    }
+  }),
+
+  muted: Ember.computed('popcorn', {
+    get: function() {
+      let popcorn = this.get('popcorn');
+      if(!popcorn) {
+        return false;
+      }
+      return popcorn.muted();
+    },
+    set: function(key, value) {
+      let popcorn = this.get('popcorn');
+      if(!popcorn) {
+        return false;
+      }
+      if(value) {
+        popcorn.mute();
+      } else {
+        popcorn.unmute();
+      }
+      return value;
+    }
+
+  }),
+
+  popcornObserver: Ember.observer('popcorn', function() {
+    let popcorn = this.get('popcorn');
+    if(!popcorn) {
+      return;
+    }
+    popcorn.on('volumechange', () => {  this.notifyPropertyChange('volume'); this.notifyPropertyChange('muted'); });
+
+  }),
+
+  speakerClass: Ember.computed('volume', 'muted', function() {
+    const volume = this.get('volume');
+    const muted = this.get('muted');
+    if(muted || volume === 0 ) {
+      return "fa-volume-off";
+    } else {
+      return speakerClassScale(volume);
+    }
+  }),
+
+  mouseEnter: function() {
+    let baseOffset = this.$("#sound-control-speaker").offset();
+    if(this.get('muted')) {
+      return;
+    }
+    this.$("#sound-control-scale").show(400);
+    this.$("#sound-control-scale").offset({ top: baseOffset.top + 55, left: baseOffset.left});
+  },
+  mouseLeave: function() {
+    this.$("#sound-control-scale").hide(400);
+  },
+
+  didInsertElement: function() {
+    this.set('volumeSliderHandler', this.$(".volume-slider").on("input change", (event) => { this.set('volume',Ember.$(event.target).val()); }));
+  },
+
+  willDestroyElement: function() {
+    let volumeSliderHandler = this.get('volumeSliderHandler');
+    if(volumeSliderHandler) {
+      this.$(".volume-slider").off({"input change": volumeSliderHandler});
+    }
+  },
+
+  actions: {
+    muteToggle() {
+      this.set('muted', !this.get('muted'));
+    },
+    clickMinus() {
+      if(this.get('muted')) {
+        return;
+      }
+      let volume = this.get('volume');
+      this.set('volume', volume - 0.1);
+    },
+    clickPlus() {
+      if(this.get('muted')) {
+        return;
+      }
+      let volume = this.get('volume');
+      this.set('volume', volume + 0.1);
+    }
+  }
+
+});
--- a/cms/app-client/app/styles/app.scss	Sun Dec 18 01:13:51 2016 +0100
+++ b/cms/app-client/app/styles/app.scss	Mon Dec 19 21:58:02 2016 +0100
@@ -65,6 +65,7 @@
 @import 'components/playlist-component';
 @import 'components/discourses-component';
 @import 'components/player-component';
+@import 'components/player-sound-control';
 @import 'components/toolbar-component';
 @import 'components/notice-component';
 @import 'components/notice-location-component';
--- a/cms/app-client/app/styles/components/player-component.scss	Sun Dec 18 01:13:51 2016 +0100
+++ b/cms/app-client/app/styles/components/player-component.scss	Mon Dec 19 21:58:02 2016 +0100
@@ -80,7 +80,7 @@
 
 .player-component #audio .progress {
     position: relative;
-    margin: 0px 50px;
+    margin: 0px 20px 0px 50px;
     background-color: transparent;
     border-radius: 0px;
     box-shadow: none;
@@ -106,7 +106,7 @@
 }
 
 .player-component #audio .progress .bar {
-    width: 226px;
+    width: 214px;
     height: 3px;
     background-color: $corpus-light-blue;
     margin: -1px 60px 0 60px;
@@ -135,7 +135,7 @@
 .player-component #audio .meta p {
     float: left;
     width: 170px;
-    margin: 0px 0px 0px 50px;
+    margin: 0px 0px 0px 45px;
     padding: 0px;
     box-sizing: border-box;
     top: 50%;
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cms/app-client/app/styles/components/player-sound-control.scss	Mon Dec 19 21:58:02 2016 +0100
@@ -0,0 +1,189 @@
+.player-sound-control {
+
+  & {
+    margin: 0px 19px 0px 0px;
+    font-size: 20px;
+  }
+
+  #sound-control-speaker {
+    width: 2em;
+    line-height: 80px;
+  }
+
+  #sound-control-speaker i {
+    cursor: pointer;
+    vertical-align: middle;
+  }
+
+  #sound-control-speaker i::before {
+    font-size: 20px;
+  }
+
+  #sound-control-speaker i.fa-times::before {
+    font-size: 16px;
+  }
+
+  #sound-control-speaker i.fa-volume-off+i.fa-times {
+    display:inline-block;
+  }
+
+  #sound-control-speaker i.fa-times {
+    display:none;
+  }
+
+  #sound-control-scale {
+    background-color: $corpus-white;
+    width: 1em;
+    height: 90px;
+    position: fixed;
+    z-index: 1;
+    display: none;
+  }
+
+  #sound-control-scale:hover {
+    display: block;
+  }
+
+  .sound-control-scale-indicator {
+    color: $corpus-black;
+    text-align: center;
+    display: table;
+    margin: 0 auto;
+    cursor: pointer;
+    -webkit-touch-callout: none; /* iOS Safari */
+      -webkit-user-select: none; /* Chrome/Safari/Opera */
+       -khtml-user-select: none; /* Konqueror */
+         -moz-user-select: none; /* Firefox */
+          -ms-user-select: none; /* Internet Explorer/Edge */
+              user-select: none;
+  }
+
+  .sound-control-scale-indicator[disabled] {
+    color: $corpus-grey;
+    cursor: default;
+    pointer-events: none;
+  }
+
+  // taken from http://brennaobrien.com/blog/2014/05/style-input-type-range-in-every-browser.html
+
+  .volume-slider {
+    height: 5px;
+    width: 50px;
+    position: absolute;
+
+    /*removes default webkit styles*/
+    -webkit-appearance: none;
+
+    transform: rotate(270deg) translate(-15px, -20px);
+    -ms-transform: rotate(270deg) translate(30px, 20px);
+    transform-origin: bottom;
+
+  }
+  .volume-slider::-webkit-slider-runnable-track {
+    height: 5px;
+    width: 50px;
+    background: #ddd;
+    border: none;
+    border-radius: 3px;
+    margin-top: 11px;
+  }
+  .volume-slider::-webkit-slider-thumb {
+    -webkit-appearance: none;
+    border: none;
+    height: 11px;
+    width: 11px;
+    border-radius: 50%;
+    background: $corpus-blue;
+    margin-top: -3px;
+  }
+
+  .volume-slider[disabled]::-webkit-slider-thumb {
+    background: $corpus-grey;
+  }
+
+  .volume-slider:focus {
+    outline: none;
+  }
+  .volume-slider:focus::-webkit-slider-runnable-track {
+    background: #ccc;
+  }
+
+  .volume-slider::-moz-range-track {
+    width: 50px;
+    height: 5px;
+    background: #ddd;
+    border: none;
+    border-radius: 2px;
+  }
+
+  .volume-slider::-moz-range-progress {
+    background-color: $corpus-light-blue;
+  }
+
+  .volume-slider::-moz-range-thumb {
+    border: none;
+    height: 11px;
+    width: 11px;
+    border-radius: 50%;
+    background: $corpus-blue;
+  }
+
+  .volume-slider[disabled]::-moz-range-thumb {
+    background: $corpus-grey;
+  }
+
+
+  /*hide the outline behind the border*/
+  .volume-slider:-moz-focusring {
+    outline: 1px solid $corpus-white;
+    outline-offset: -1px;
+  }
+
+  // .volume-slider::-ms-track {
+  //   display:none;
+  //   width: 50px;
+  //   height: 5px;
+
+  //   /*remove bg colour from the track, we'll use ms-fill-lower and ms-fill-upper instead */
+  //   background-color: transparent;
+
+  //   /*leave room for the larger thumb to overflow with a transparent border */
+  //   border-color: transparent;
+  //   border-width: 6px 0;
+
+  //   /*remove default tick marks*/
+  //   color: transparent;
+  // }
+  // .volume-slider::-ms-fill-lower {
+  //   background-color: $corpus-light-blue;
+  //   border-radius: 11px;
+  // }
+  // .volume-slider::-ms-fill-upper {
+  //   background-color: #ddd;
+  //   border-radius: 11px;
+  // }
+  // .volume-slider::-ms-thumb {
+  //   border: none;
+  //   height: 11px;
+  //   width: 11px;
+  //   border-radius: 50%;
+  //   background: $corpus-blue;
+  // }
+  // .volume-slider[disabled]::-ms-thumb {
+  //   background: $corpus-grey;
+  // }
+
+  // .volume-slider:focus::-ms-fill-lower {
+  //   background: $corpus-light-blue;
+  // }
+  // .volume-slider:focus::-ms-fill-upper {
+  //   background: #ccc;
+  // }
+
+  #sound-control-scale-minus {
+    position: absolute;
+    top: 67px;
+    left: 7px;
+  }
+
+}
--- a/cms/app-client/app/templates/components/player-component.hbs	Sun Dec 18 01:13:51 2016 +0100
+++ b/cms/app-client/app/templates/components/player-component.hbs	Mon Dec 19 21:58:02 2016 +0100
@@ -13,6 +13,7 @@
     <span class="bar" onclick={{action 'setTime'}}><span class="value"></span></span>
     <span class="remaining">- {{to-minutes remaining}}</span>
   </div>
+  {{ player-sound-control popcorn=popcorn }}
   <div class="controls extra">
   {{#if player.model.video}}
     {{#if player.videoscreen}}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cms/app-client/app/templates/components/player-sound-control.hbs	Mon Dec 19 21:58:02 2016 +0100
@@ -0,0 +1,15 @@
+{{!--<span id="sound-control-speaker" class="fa {{speakerClass}}" {{action 'muteToggle'}} aria-hidden="true"></span>--}}
+<div id="sound-control-speaker" aria-hidden="true"><i class="fa {{speakerClass}}" {{action 'muteToggle'}}></i><i class="fa fa-times" {{action 'muteToggle'}}></i></div>
+<div id="sound-control-scale">
+  <div id="sound-control-scale-plus" class="sound-control-scale-indicator" {{ action 'clickPlus' }} disabled={{muted}}>+</div>
+  {{input
+    type="range"
+    min="0"
+    max="1"
+    step="0.05"
+    class="volume-slider"
+    value=volume
+    disabled=muted
+  }}
+  <div id="sound-control-scale-minus" class="sound-control-scale-indicator" {{ action 'clickMinus' }} disabled={{muted}}>-</div>
+</div>
--- a/cms/app-client/ember-cli-build.js	Sun Dec 18 01:13:51 2016 +0100
+++ b/cms/app-client/ember-cli-build.js	Mon Dec 19 21:58:02 2016 +0100
@@ -50,7 +50,6 @@
     });
     app.import('bower_components/interval-tree2/dist/interval-tree.js');
 
-
     //shims
     app.import('vendor/shims/ammaps.js', {
         exports: {
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cms/app-client/tests/integration/components/player-sound-control-test.js	Mon Dec 19 21:58:02 2016 +0100
@@ -0,0 +1,24 @@
+import { moduleForComponent, test } from 'ember-qunit';
+import hbs from 'htmlbars-inline-precompile';
+
+moduleForComponent('player-sound-control', 'Integration | Component | player sound control', {
+  integration: true
+});
+
+test('it renders', function(assert) {
+  // Set any properties with this.set('myProperty', 'value');
+  // Handle any actions with this.on('myAction', function(val) { ... });
+
+  this.render(hbs`{{player-sound-control}}`);
+
+  assert.equal(this.$().text().trim(), '');
+
+  // Template block usage:
+  this.render(hbs`
+    {{#player-sound-control}}
+      template block text
+    {{/player-sound-control}}
+  `);
+
+  assert.equal(this.$().text().trim(), 'template block text');
+});