From d6aa222f1f4332960657ec2a8b43a3f0b8890a0e Mon Sep 17 00:00:00 2001 From: Fergal Moran Date: Tue, 1 Dec 2015 21:18:24 +0000 Subject: [PATCH] Fixed (and restored) slider) --- bower.json | 2 - .../components/footer/footer.controller.js | 73 ++++++ client/app/components/footer/footer.html | 33 +++ client/app/components/navbar/navbar.html | 169 ++++++++++++ client/app/constants/audio.js | 9 +- client/app/services/audio/audio.service.js | 129 ++++++++++ client/assets/slider.js | 240 +++++++++++++++++- package.json | 2 +- 8 files changed, 644 insertions(+), 13 deletions(-) create mode 100755 client/app/components/footer/footer.controller.js create mode 100755 client/app/components/footer/footer.html create mode 100755 client/app/components/navbar/navbar.html create mode 100644 client/app/services/audio/audio.service.js diff --git a/bower.json b/bower.json index acaaef8..f171576 100755 --- a/bower.json +++ b/bower.json @@ -36,7 +36,6 @@ "notifyjs": "~0.3.2", "oauth-js": "~0.4.3", "remarkable-bootstrap-notify": "~3.1.3", - "seiyria-bootstrap-slider": "~5.0.13", "smalot-bootstrap-datetimepicker": "~2.3.4", "js-data-angular": "~3.1.0" }, @@ -46,7 +45,6 @@ }, "resolutions": { "angular": "~1.4.3", - "seiyria-bootstrap-slider": "~5.0.13", "angular-bootstrap": "~0.13.3", "js-data-angular": "~3.1.0" } diff --git a/client/app/components/footer/footer.controller.js b/client/app/components/footer/footer.controller.js new file mode 100755 index 0000000..d3651d1 --- /dev/null +++ b/client/app/components/footer/footer.controller.js @@ -0,0 +1,73 @@ +'use strict'; + +angular.module('dssWebApp') + .controller('FooterCtrl', function ($scope, AudioService, AUDIO_EVENTS, PLAYSTATES) { + var sliding = false; + $scope.mix = {}; + $scope.sliding = false; + $scope.vm = { + playState: PLAYSTATES.stopped, + playPos: 0, + max: 100, + currentVolume: AudioService.getVolume(), + timeCurrent: "00:00", + timeDuration: "00:00" + }; + + $('.currentVolume').slider({ + range: [0, 1], + step: 0.01, + start : 0.5, + handles: 1, + slide: function() { + var volume = Math.round($(this).val()* 100); + AudioService.setVolume(volume); + $('.volumeText').html('Volume: ' + (volume).toFixed(0) + ''); + $scope.vm.currentVolume = volume; + $scope.$apply(); + } + }); + $scope.playPause = function () { + switch ($scope.vm.playState) { + case PLAYSTATES.playing: + AudioService.pause(); + break; + case PLAYSTATES.paused: + AudioService.resume(); + break; + default: + break; + } + event.stopPropagation(); + }; + + $scope.$on(AUDIO_EVENTS.audioStart, function (event, mix, id, duration) { + $scope.mix = mix; + $scope.vm.playState = PLAYSTATES.playing; + $scope.vm.timeDuration = duration/1000; + }); + + $scope.$on(AUDIO_EVENTS.audioPause, function (event, id) { + $scope.vm.playState = PLAYSTATES.paused; + }); + + $scope.$on(AUDIO_EVENTS.audioResume, function (event, id) { + $scope.vm.playState = PLAYSTATES.playing; + }); + + $scope.$on(AUDIO_EVENTS.audioProgress, function (event, duration, position) { + if (position) + $scope.vm.timeCurrent = position / 1000; + if (duration) + $scope.vm.timeDuration = duration/1000; + $scope.vm.playPos = (position / duration) * 100; + if (!$scope.$$phase) { + $scope.$apply(); + } + }); + $scope.doSeek = function($event){ + var $bar = $(event.currentTarget); + var newPosition = ((event.pageX - ($bar.offset().left)) / ($bar.width()) * 100); + AudioService.setPosition(newPosition, false); + } + }); diff --git a/client/app/components/footer/footer.html b/client/app/components/footer/footer.html new file mode 100755 index 0000000..3a56199 --- /dev/null +++ b/client/app/components/footer/footer.html @@ -0,0 +1,33 @@ + diff --git a/client/app/components/navbar/navbar.html b/client/app/components/navbar/navbar.html new file mode 100755 index 0000000..596d02a --- /dev/null +++ b/client/app/components/navbar/navbar.html @@ -0,0 +1,169 @@ +
+ Deep|South|Sounds + + + +
+ diff --git a/client/app/constants/audio.js b/client/app/constants/audio.js index c986a1d..1b2f261 100755 --- a/client/app/constants/audio.js +++ b/client/app/constants/audio.js @@ -1,11 +1,6 @@ 'use strict'; angular.module('dssWebApp') - .constant('STORAGE', { - authBackend: 'dss_at_backend', - authToken: 'dss_at_token', - authServerToken: 'dss_at_server_token', - authLocalToken: 'dss_at_local_token', - authServerSession: 'dss_at_server_session', - authRefreshToken: 'refresh_token' + .constant('AUDIO_CONSTANTS', { + volume: 'dss_au_volume' }); diff --git a/client/app/services/audio/audio.service.js b/client/app/services/audio/audio.service.js new file mode 100644 index 0000000..4d6f7e8 --- /dev/null +++ b/client/app/services/audio/audio.service.js @@ -0,0 +1,129 @@ +'use strict'; + +angular.module('dssWebApp') + .service('AudioService', function AudioService($rootScope, $interval, $q, logger, AUDIO_EVENTS, AUDIO_CONSTANTS) { + // AngularJS will instantiate a singleton by calling "new" on this function + var _currentMix; + var _currentSound; + var _soundId; + + soundManager.setup({ + html5PollingInterval: 50, + flashVersion: 9, + debugMode: false, + defaultOptions: { + volume: _getStoredVolume() + } + }); + + function _getStoredVolume() { + return Math.round(localStorage.getItem(AUDIO_CONSTANTS.volume) || 50); + } + + function _setStoredVolume(volume) { + localStorage.setItem(AUDIO_CONSTANTS.volume, Math.round(volume)); + } + + function makeSound(url) { + var sound = soundManager.createSound({ + url: url, + whileplaying: function () { + if (this.duration) { + $rootScope.$broadcast(AUDIO_EVENTS.audioProgress, this.durationEstimate, this.position, this.elapsed); + } + }, + onplay: function () { + $rootScope.$broadcast(AUDIO_EVENTS.audioStart, _currentMix, _soundId, this.duration); + $rootScope.isPlaying = true; + }, + onstop: function () { + $rootScope.$broadcast(AUDIO_EVENTS.audioStop, _soundId); + }, + onfinish: function () { + $rootScope.$broadcast(AUDIO_EVENTS.audioFinish, _soundId); + } + + }); + return sound; + } + + this.playLive = function (url, title) { + var _this = this; + return $q(function (resolve, reject) { + if ($rootScope.radioPlaying) { + _this.stop(); + $rootScope.radioPlaying = false; + $rootScope.radioLoading = false; + resolve(true); + } else { + $rootScope.safeApply(); + _this.stop(); + _currentSound = soundManager.createSound({ + url: url, + onload: function () { + resolve(); + } + }); + _currentSound.play({ + url: url + }); + _soundId = title; + } + }); + }; + this.play = function (mix, url) { + var _this = this; + return $q(function (resolve, reject) { + if (soundManager.canPlayURL(url)) { + _this.stop(); + _currentMix = mix; + _currentSound = makeSound(url); + _currentSound.play({ + url: mix.stream_url, + position: 0 + }); + _soundId = mix.slug; + resolve(); + } else { + $rootScope.$broadcast(AUDIO_EVENTS.audioFailed, _soundId); + } + }); + }; + this.pause = function () { + if (_currentSound) { + _currentSound.togglePause(); + $rootScope.$broadcast(AUDIO_EVENTS.audioPause, _soundId); + } + }; + this.resume = function () { + if (_currentSound) { + _currentSound.togglePause(); + $rootScope.$broadcast(AUDIO_EVENTS.audioResume, _soundId); + } + }; + this.stop = function () { + if (_currentSound) { + _currentSound.stop(); + $rootScope.$broadcast(AUDIO_EVENTS.audioStop, _soundId); + } + }; + this.setPosition = function (position, absolute) { + if (absolute) + _currentSound.setPosition(position); + else + _currentSound.setPosition((_currentSound.durationEstimate / 100) * position); + _currentSound._iO.whileplaying.apply(_currentSound); + }; + this.isPlaying = function (mix) { + return _soundId === mix.slug; + }; + this.setVolume = function (volume) { + if (_currentSound) { + _currentSound.setVolume(volume); + } + _setStoredVolume(volume); + }; + this.getVolume = function (volume) { + return _getStoredVolume(); + }; + }); diff --git a/client/assets/slider.js b/client/assets/slider.js index 7afd1aa..0c2fb8c 100644 --- a/client/assets/slider.js +++ b/client/assets/slider.js @@ -1,3 +1,237 @@ -/** - * Created by fergalm on 30/11/15. - */ +(function ($) { + $.fn.slider = function (options, flag) { + var EVENT = window.navigator.msPointerEnabled ? 2 : "ontouchend" in document ? 3 : 1; + if (window.debug && console) { + console.log(EVENT) + } + function call(f, scope, args) { + if (typeof f === "function") { + f.call(scope, args) + } + } + + var percentage = { + to: function (range, value) { + value = range[0] < 0 ? value + Math.abs(range[0]) : value - range[0]; + return (value * 100) / this._length(range) + }, from: function (range, value) { + return (value * 100) / this._length(range) + }, is: function (range, value) { + return ((value * this._length(range)) / 100) + range[0] + }, _length: function (range) { + return (range[0] > range[1] ? range[0] - range[1] : range[1] - range[0]) + } + }; + + function correct(proposal, slider, handle) { + var setup = slider.data("setup"), handles = setup.handles, settings = setup.settings, pos = setup.pos; + proposal = proposal < 0 ? 0 : proposal > 100 ? 100 : proposal; + if (settings.handles == 2) { + if (handle.is(":first-child")) { + var other = parseFloat(handles[1][0].style[pos]) - settings.margin; + proposal = proposal > other ? other : proposal + } else { + var other = parseFloat(handles[0][0].style[pos]) + settings.margin; + proposal = proposal < other ? other : proposal + } + } + if (settings.step) { + var per = percentage.from(settings.range, settings.step); + proposal = Math.round(proposal / per) * per + } + return proposal + } + + function client(f) { + try { + return [(f.clientX || f.originalEvent.clientX || f.originalEvent.touches[0].clientX), (f.clientY || f.originalEvent.clientY || f.originalEvent.touches[0].clientY)] + } catch (e) { + return ["x", "y"] + } + } + + function place(handle, pos) { + return parseFloat(handle[0].style[pos]) + } + + var defaults = {handles: 1, serialization: {to: ["", ""], resolution: 0.01}}; + methods = { + create: function () { + return this.each(function () { + function setHandle(handle, to, slider) { + handle.css(pos, to + "%").data("input").val(percentage.is(settings.range, to).toFixed(res)) + } + + var settings = $.extend(defaults, options), handlehtml = "", slider = $(this).data("_isnS_", true), handles = [], pos, orientation, classes = "", num = function (e) { + return !isNaN(parseFloat(e)) && isFinite(e) + }, split = (settings.serialization.resolution = settings.serialization.resolution || 0.01).toString().split("."), res = split[0] == 1 ? 0 : split[1].length; + settings.start = num(settings.start) ? [settings.start, 0] : settings.start; + $.each(settings, function (a, b) { + if (num(b)) { + settings[a] = parseFloat(b) + } else { + if (typeof b == "object" && num(b[0])) { + b[0] = parseFloat(b[0]); + if (num(b[1])) { + b[1] = parseFloat(b[1]) + } + } + } + var e = false; + b = typeof b == "undefined" ? "x" : b; + switch (a) { + case"range": + case"start": + e = b.length != 2 || !num(b[0]) || !num(b[1]); + break; + case"handles": + e = (b < 1 || b > 2 || !num(b)); + break; + case"connect": + e = b != "lower" && b != "upper" && typeof b != "boolean"; + break; + case"orientation": + e = (b != "vertical" && b != "horizontal"); + break; + case"margin": + case"step": + e = typeof b != "undefined" && !num(b); + break; + case"serialization": + e = typeof b != "object" || !num(b.resolution) || (typeof b.to == "object" && b.to.length < settings.handles); + break; + case"slide": + e = typeof b != "function"; + break + } + if (e && console) { + console.error("Bad input for " + a + " on slider:", slider) + } + }); + settings.margin = settings.margin ? percentage.from(settings.range, settings.margin) : 0; + if (settings.serialization.to instanceof jQuery || typeof settings.serialization.to == "string" || settings.serialization.to === false) { + settings.serialization.to = [settings.serialization.to] + } + if (settings.orientation == "vertical") { + classes += "vertical"; + pos = "top"; + orientation = 1 + } else { + classes += "horizontal"; + pos = "left"; + orientation = 0 + } + classes += settings.connect ? settings.connect == "lower" ? " connect lower" : " connect" : ""; + slider.addClass(classes); + for (var i = 0; i < settings.handles; i++) { + handles[i] = slider.append(handlehtml).children(":last"); + var setTo = percentage.to(settings.range, settings.start[i]); + handles[i].css(pos, setTo + "%"); + if (setTo == 100 && handles[i].is(":first-child")) { + handles[i].css("z-index", 2) + } + var bind = ".slider", onEvent = (EVENT === 1 ? "mousedown" : EVENT === 2 ? "MSPointerDown" : "touchstart") + bind + "X", moveEvent = (EVENT === 1 ? "mousemove" : EVENT === 2 ? "MSPointerMove" : "touchmove") + bind, offEvent = (EVENT === 1 ? "mouseup" : EVENT === 2 ? "MSPointerUp" : "touchend") + bind; + handles[i].find("div").on(onEvent, function (e) { + $("body").bind("selectstart" + bind, function () { + return false + }); + if (!slider.hasClass("disabled")) { + $("body").addClass("TOUCH"); + var handle = $(this).addClass("active").parent(), unbind = handle.add($(document)).add("body"), originalPosition = parseFloat(handle[0].style[pos]), originalClick = client(e), previousClick = originalClick, previousProposal = false; + $(document).on(moveEvent, function (f) { + f.preventDefault(); + var currentClick = client(f); + if (currentClick[0] == "x") { + return + } + currentClick[0] -= originalClick[0]; + currentClick[1] -= originalClick[1]; + var movement = [previousClick[0] != currentClick[0], previousClick[1] != currentClick[1]], proposal = originalPosition + ((currentClick[orientation] * 100) / (orientation ? slider.height() : slider.width())); + proposal = correct(proposal, slider, handle); + if (movement[orientation] && proposal != previousProposal) { + handle.css(pos, proposal + "%").data("input").val(percentage.is(settings.range, proposal).toFixed(res)); + call(settings.slide, slider.data("_n", true)); + previousProposal = proposal; + handle.css("z-index", handles.length == 2 && proposal == 100 && handle.is(":first-child") ? 2 : 1) + } + previousClick = currentClick + }).on(offEvent, function () { + unbind.off(bind); + $("body").removeClass("TOUCH"); + if (slider.find(".active").removeClass("active").end().data("_n")) { + slider.data("_n", false).change() + } + }) + } + }).on("click", function (e) { + e.stopPropagation() + }) + } + if (EVENT == 1) { + slider.on("click", function (f) { + if (!slider.hasClass("disabled")) { + var currentClick = client(f), proposal = ((currentClick[orientation] - slider.offset()[pos]) * 100) / (orientation ? slider.height() : slider.width()), handle = handles.length > 1 ? (currentClick[orientation] < (handles[0].offset()[pos] + handles[1].offset()[pos]) / 2 ? handles[0] : handles[1]) : handles[0]; + setHandle(handle, correct(proposal, slider, handle), slider); + call(settings.slide, slider); + slider.change() + } + }) + } + for (var i = 0; i < handles.length; i++) { + var val = percentage.is(settings.range, place(handles[i], pos)).toFixed(res); + if (typeof settings.serialization.to[i] == "string") { + handles[i].data("input", slider.append('').find("input:last").val(val).change(function (a) { + a.stopPropagation() + })) + } else { + if (settings.serialization.to[i] == false) { + handles[i].data("input", { + val: function (a) { + if (typeof a != "undefined") { + this.handle.data("noUiVal", a) + } else { + return this.handle.data("noUiVal") + } + }, handle: handles[i] + }) + } else { + handles[i].data("input", settings.serialization.to[i].data("handleNR", i).val(val).change(function () { + var arr = [null, null]; + arr[$(this).data("handleNR")] = $(this).val(); + slider.val(arr) + })) + } + } + } + $(this).data("setup", {settings: settings, handles: handles, pos: pos, res: res}) + }) + }, val: function () { + if (typeof arguments[0] !== "undefined") { + var val = typeof arguments[0] == "number" ? [arguments[0]] : arguments[0]; + return this.each(function () { + var setup = $(this).data("setup"); + for (var i = 0; i < setup.handles.length; i++) { + if (val[i] != null) { + var proposal = correct(percentage.to(setup.settings.range, val[i]), $(this), setup.handles[i]); + setup.handles[i].css(setup.pos, proposal + "%").data("input").val(percentage.is(setup.settings.range, proposal).toFixed(setup.res)) + } + } + }) + } else { + var handles = $(this).data("setup").handles, re = []; + for (var i = 0; i < handles.length; i++) { + re.push(parseFloat(handles[i].data("input").val())) + } + return re.length == 1 ? re[0] : re + } + }, disabled: function () { + return flag ? $(this).addClass("disabled") : $(this).removeClass("disabled") + } + }; + var $_val = jQuery.fn.val; + jQuery.fn.val = function () { + return this.data("_isnS_") ? methods.val.apply(this, arguments) : $_val.apply(this, arguments) + }; + return options == "disabled" ? methods.disabled.apply(this) : methods.create.apply(this) + } +})(jQuery); \ No newline at end of file diff --git a/package.json b/package.json index 21bc8e0..9c87cbc 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "dssweb", - "version": "2.4.0", + "version": "3.0.1", "main": "server/app.js", "dependencies": { "body-parser": "~1.5.0",