You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
684 lines
14 KiB
684 lines
14 KiB
/*jslint laxbreak:true */
|
|
/*jslint laxcomma:true */
|
|
/*jslint loopfunc:true */
|
|
/*jslint strict:true */
|
|
/*jslint browser:true */
|
|
/*jslint devel:true */
|
|
define([
|
|
"underscore"
|
|
, "jquery"
|
|
, "backbone"
|
|
|
|
, "modules/webaudio"
|
|
|
|
, "libs/mimes"
|
|
|
|
, "text!../../templates/player/layout.html"
|
|
, "text!../../templates/player/player-status.html"
|
|
]
|
|
, function (
|
|
_
|
|
, $
|
|
, Backbone
|
|
|
|
, WebAudio
|
|
|
|
, MIMES
|
|
|
|
, tmplPlayerLayout
|
|
, tmplPlayerStatus
|
|
) {
|
|
"use strict";
|
|
|
|
if (!_.isFunction(Array.prototype.shuffle)) {
|
|
Array.prototype.shuffle = function() {
|
|
var len = this.length
|
|
, i = len
|
|
;
|
|
|
|
if ( len === 0 ) {
|
|
return false;
|
|
}
|
|
|
|
while (i--) {
|
|
var p = parseInt(Math.random()*len, 10);
|
|
var t = this[i];
|
|
this[i] = this[p];
|
|
this[p] = t;
|
|
}
|
|
};
|
|
}
|
|
|
|
var
|
|
|
|
Player = Backbone.View.extend({
|
|
elViewport: null
|
|
, $elViewport: null
|
|
, $elVolumeInput: null
|
|
|
|
, playIndex: []
|
|
|
|
, playCursor: -1
|
|
|
|
, webAudio: null
|
|
|
|
, _stateRandom: null
|
|
|
|
, _stateRepeat: null
|
|
|
|
, _statesRepeat: {
|
|
none: null
|
|
, one: false
|
|
, all: true
|
|
}
|
|
|
|
, _stateMute: null
|
|
|
|
, _stateVolume: null
|
|
|
|
, _stateAudio: null
|
|
|
|
, _state: null
|
|
|
|
, _states: {
|
|
stopped: 0
|
|
, loading: 1
|
|
, playing: 2
|
|
, paused: 3
|
|
}
|
|
|
|
, debouncers: {}
|
|
|
|
, events: {
|
|
//"click #add-friend": "showPrompt",
|
|
"click div:first-child > a.buttonPrev:not(.disabled)":
|
|
"__buttonPrevious"
|
|
, "click div:first-child > a.buttonNext:not(.disabled)":
|
|
"__buttonNext"
|
|
, "click div:first-child > a.buttonPlay:not(.disabled)":
|
|
"__buttonPlayPause"
|
|
// Volume Down
|
|
, "click div:first-child > .playerVolumeWrapper > a:first-child:not(.disabled)":
|
|
"__buttonVolumeDown"
|
|
// Volume Up
|
|
, "click div:first-child > .playerVolumeWrapper > a:last-child:not(.disabled)":
|
|
"__buttonVolumeUp"
|
|
// Volume click
|
|
, "click div:first-child > .playerVolumeWrapper > input:not(disabled)":
|
|
"__buttonVolumeClick"
|
|
// Seek input
|
|
, "click div:last-child > .playerProgressWrapper > progress":
|
|
"__buttonSeek"
|
|
}
|
|
|
|
, initialize: function (options) {
|
|
var that = this
|
|
;
|
|
|
|
_.bindAll(this
|
|
// Public
|
|
, "init"
|
|
, "setPlayIndex"
|
|
, "sortPlayIndex"
|
|
, "setRandomState"
|
|
, "setRepeatState"
|
|
, "playItem"
|
|
, "playAtIndex"
|
|
// Events
|
|
, "__buttonPlayPause"
|
|
, "__buttonNext"
|
|
, "__buttonPrevious"
|
|
, "__buttonVolumeUp"
|
|
, "__buttonVolumeDown"
|
|
, "__buttonVolumeClick"
|
|
, "__buttonSeek"
|
|
// Private
|
|
, "_waCreate"
|
|
, "_waBindEvents"
|
|
, "__waStateChanged"
|
|
, "__waEnded"
|
|
, "__waTimeUpdate"
|
|
, "_playerPlay"
|
|
, "_playerLoading"
|
|
, "_playNext"
|
|
, "_playPrevious"
|
|
, "_playerPause"
|
|
, "_playerUnpause"
|
|
, "_playerStop"
|
|
, "_playerStart"
|
|
, "_getVolume"
|
|
, "_setVolume"
|
|
, "_seek"
|
|
|
|
, "_playBtnSetState"
|
|
, "_disableControls"
|
|
, "_enableControls"
|
|
, "_getItemData"
|
|
, "_getItemMediaUrl"
|
|
, "_setViewport"
|
|
, "_progressSetLoadding"
|
|
, "_updateViewportProgress"
|
|
);
|
|
|
|
Player.__super__.initialize.apply(that);
|
|
|
|
that.$el.html(tmplPlayerLayout);
|
|
|
|
that.elViewport = document.createElement("div");
|
|
that.$elViewport = $(that.elViewport);
|
|
|
|
that._setViewport();
|
|
|
|
that.$elVolumeInput = that.$el.find(".playerVolumeWrapper > input");
|
|
|
|
that.$el.append(that.elViewport);
|
|
|
|
that._waCreate();
|
|
|
|
return that;
|
|
}
|
|
|
|
/**
|
|
* PUBLIC
|
|
**/
|
|
, init: function () {
|
|
var that = this
|
|
;
|
|
|
|
that._enableControls();
|
|
|
|
that.$elVolumeInput.val(that.webAudio.obj.volume);
|
|
}
|
|
|
|
, setPlayIndex: function (index, cursor) {
|
|
var that = this
|
|
;
|
|
|
|
if (that.webAudio._state === that.webAudio._states.paused) {
|
|
that.webAudio._state = that.webAudio._states.stopped;
|
|
that._playerStop();
|
|
}
|
|
|
|
if (that.webAudio._state < that.webAudio._states.loading) {
|
|
console.debug("Setting Play Index");
|
|
that._playIndex = index;
|
|
that._playCursor = cursor || 0;
|
|
|
|
if (that._stateRandom) {
|
|
that.setRandomState(that._stateRandom);
|
|
}
|
|
|
|
return that._playIndex;
|
|
} else {
|
|
return undefined;
|
|
}
|
|
}
|
|
|
|
, sortPlayIndex: function (index) {
|
|
var that = this
|
|
;
|
|
|
|
// TODO
|
|
}
|
|
|
|
, setRandomState: function (random) {
|
|
var that = this
|
|
;
|
|
|
|
if (random === true) {
|
|
//var previousItem = that._playIndex[that._playCursor];
|
|
that._playIndexOrdered = _.clone(that._playIndex);
|
|
that._playIndex.shuffle();
|
|
|
|
// TODO: Set cursor back to the item that was playing
|
|
//if (that._playCursor !== -1 && that.webAudio._state >= that.webAudio._states.loading) {
|
|
// var newCursor =
|
|
//}
|
|
|
|
that._playCursor = -1;
|
|
} else {
|
|
that._playIndex = _.clone(that._playIndexOrdered);
|
|
|
|
delete that._playIndexOrdered;
|
|
}
|
|
|
|
return that._stateRandom = random;
|
|
}
|
|
|
|
, setRepeatState: function (repeat) {
|
|
var that = this
|
|
;
|
|
|
|
return that._stateRepeat = repeat;
|
|
}
|
|
|
|
, playItem: function (item) {
|
|
var that = this
|
|
;
|
|
|
|
return that._playerPlay(item);
|
|
}
|
|
|
|
, playAtIndex: function (index) {
|
|
var that = this
|
|
;
|
|
|
|
that._playCursor = index;
|
|
that.playItem(that._playIndex[that._playCursor]);
|
|
}
|
|
/**
|
|
* EVENTS
|
|
**/
|
|
, __buttonPlayPause: function (event) {
|
|
var that = this
|
|
, $target = $(event.target)
|
|
;
|
|
|
|
switch (that.webAudio._state) {
|
|
case that.webAudio._states.stopped:
|
|
that._playNext();
|
|
break;
|
|
|
|
case that.webAudio._states.playing:
|
|
that._playerPause();
|
|
break;
|
|
|
|
case that.webAudio._states.paused:
|
|
that._playerUnpause();
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
, __buttonNext: function (event) {
|
|
var that = this
|
|
;
|
|
|
|
that._playNext();
|
|
}
|
|
|
|
, __buttonPrevious: function (event) {
|
|
var that = this
|
|
;
|
|
|
|
that._playPrevious();
|
|
}
|
|
|
|
, __buttonVolumeUp: function (event) {
|
|
var that = this
|
|
, volume = that._getVolume()
|
|
;
|
|
|
|
if (volume < 1) {
|
|
if ((volume+0.1) > 1) {
|
|
volume = 1;
|
|
} else {
|
|
volume+= 0.1;
|
|
}
|
|
}
|
|
|
|
that._setVolume(volume);
|
|
}
|
|
|
|
, __buttonVolumeDown: function (event) {
|
|
var that = this
|
|
, volume = that._getVolume()
|
|
;
|
|
|
|
if (volume > 0) {
|
|
if ((volume-0.1) < 0) {
|
|
volume = 0;
|
|
} else{
|
|
volume-= 0.1;
|
|
}
|
|
}
|
|
|
|
that._setVolume(volume);
|
|
|
|
}
|
|
|
|
, __buttonVolumeClick: function (event) {
|
|
var that = this
|
|
, clientX = event.clientX
|
|
, left = event.target.offsetLeft
|
|
, clickoffset = clientX - left
|
|
, fraction = clickoffset/event.target.offsetWidth
|
|
;
|
|
|
|
that._setVolume(fraction);
|
|
}
|
|
|
|
, __buttonSeek: function (event) {
|
|
var that = this
|
|
;
|
|
|
|
if (_.isUndefined(that.debouncers["__buttonSeek"])) {
|
|
var clientX = event.clientX
|
|
, left = event.target.offsetLeft
|
|
, clickoffset = clientX - left
|
|
, percent = clickoffset/event.target.offsetWidth
|
|
, seekTime = percent * that.webAudio.obj.duration
|
|
;
|
|
|
|
that._seek(seekTime);
|
|
|
|
that.debouncers["__buttonSeek"] = setTimeout(function __fnPlayerViewButtonSeekDebounce () {
|
|
clearTimeout(that.debouncers["__buttonSeek"]);
|
|
|
|
delete that.debouncers["__buttonSeek"];
|
|
}, 800);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* PRIVATE
|
|
**/
|
|
// Audio Player
|
|
, _waCreate: function () {
|
|
var that = this
|
|
;
|
|
|
|
that.webAudio = new WebAudio();
|
|
|
|
that._waBindEvents();
|
|
|
|
return that.webAudio;
|
|
}
|
|
|
|
, _waBindEvents: function () {
|
|
var that = this
|
|
;
|
|
|
|
that.webAudio.on("state", function __fnPlayerEventWebAudioState (state) {
|
|
that.__waStateChanged.apply(that, arguments);
|
|
});
|
|
|
|
that.webAudio.on("ended", function __fnPlayerEventWebAudioState (state) {
|
|
that.__waEnded.apply(that, arguments);
|
|
});
|
|
|
|
that.webAudio.on("timeupdate", function __fnPlayerEventWebAudioState () {
|
|
that.__waTimeUpdate.apply(that, arguments);
|
|
});
|
|
}
|
|
|
|
, __waStateChanged: function (state) {
|
|
var that = this
|
|
;
|
|
|
|
that._playBtnSetState(state);
|
|
|
|
switch (state) {
|
|
case that.webAudio._states.stopped:
|
|
console.debug("Player State Stopped");
|
|
//that._setViewport();
|
|
that._enableControls();
|
|
break;
|
|
case that.webAudio._states.error:
|
|
console.debug("Player State error");
|
|
that._disableControls();
|
|
break;
|
|
case that.webAudio._states.loading:
|
|
console.debug("Player State loading");
|
|
that._progressSetLoadding();
|
|
that._disableControls();
|
|
break;
|
|
case that.webAudio._states.playing:
|
|
console.debug("Player State playing");
|
|
that._enableControls();
|
|
break;
|
|
case that.webAudio._states.paused:
|
|
console.debug("Player State paused");
|
|
that._enableControls();
|
|
break;
|
|
}
|
|
}
|
|
|
|
, __waEnded: function () {
|
|
var that = this
|
|
;
|
|
|
|
that._playNext();
|
|
}
|
|
|
|
, __waTimeUpdate: function (event) {
|
|
var that = this
|
|
;
|
|
|
|
that._updateViewportProgress(event.target.currentTime, event.target.duration);
|
|
}
|
|
|
|
// Player Controls
|
|
, _playerPlay: function (item) {
|
|
var that = this
|
|
;
|
|
|
|
that.webAudio.loadMedia(that._getItemMediaUrl(item), MIMES.get(item.type), function __fnPlayerViewCbLoaded () {
|
|
that._setViewport(item);
|
|
});
|
|
}
|
|
|
|
, _playerLoading: function () {
|
|
var that = this
|
|
;
|
|
|
|
}
|
|
|
|
, _playNext: function () {
|
|
var that = this
|
|
;
|
|
|
|
switch (that._stateRepeat) {
|
|
case that._statesRepeat.all:
|
|
case that._statesRepeat.none:
|
|
if (that._stateRepeat === that._statesRepeat.all
|
|
&& that._playIndex.length === (that._playCursor + 1)) {
|
|
that._playCursor = -1;
|
|
}
|
|
|
|
if (that._playIndex.length > (that._playCursor + 1)) {
|
|
that._playerPlay(that._playIndex[++that._playCursor]);
|
|
} else {
|
|
that._playerStop();
|
|
}
|
|
break;
|
|
case that._statesRepeat.one:
|
|
that._playerPlay(that._playIndex[that._playCursor]);
|
|
break;
|
|
}
|
|
}
|
|
|
|
, _playPrevious: function () {
|
|
var that = this
|
|
;
|
|
|
|
switch (that._stateRepeat) {
|
|
case that._statesRepeat.all:
|
|
case that._statesRepeat.none:
|
|
if (that._stateRepeat === that._statesRepeat.all
|
|
&& that._playCursor === 0) {
|
|
that._playCursor = that._playIndex.length;
|
|
}
|
|
|
|
if (that._playCursor > 0) {
|
|
that._playerPlay(that._playIndex[--that._playCursor]);
|
|
} else {
|
|
that._playerStop();
|
|
}
|
|
break;
|
|
case that._statesRepeat.one:
|
|
that._playerPlay(that._playIndex[that._playCursor]);
|
|
break;
|
|
}
|
|
}
|
|
|
|
, _playerPause: function () {
|
|
var that = this
|
|
;
|
|
|
|
that.webAudio.obj.pause();
|
|
}
|
|
|
|
, _playerUnpause: function () {
|
|
var that = this
|
|
;
|
|
|
|
that.webAudio.obj.play();
|
|
}
|
|
|
|
, _playerStop: function () {
|
|
var that = this
|
|
;
|
|
|
|
that.webAudio.obj.pause();
|
|
that.webAudio.obj.src = "";
|
|
that.webAudio.obj.innetHTML = "";
|
|
|
|
that._playCursor = -1;
|
|
|
|
that._setViewport();
|
|
}
|
|
|
|
, _playerStart: function () {
|
|
var that = this
|
|
;
|
|
|
|
}
|
|
|
|
, _getVolume: function () {
|
|
var that = this
|
|
;
|
|
|
|
return that.webAudio.obj.volume;
|
|
}
|
|
|
|
, _setVolume: function (volume) {
|
|
var that = this
|
|
;
|
|
|
|
that.$elVolumeInput.val(volume);
|
|
return that.webAudio.obj.volume = volume;
|
|
}
|
|
|
|
, _seek: function (seek) {
|
|
var that = this
|
|
;
|
|
|
|
if (that.webAudio._state === that.webAudio._states.playing) {
|
|
console.debug("Seeking player to " + seek);
|
|
|
|
that.webAudio.obj.currentTime = seek;
|
|
}
|
|
}
|
|
|
|
// Viewport methods
|
|
, _playBtnSetState: function (state) {
|
|
var that = this
|
|
, $btn = that.$el.find("a.buttonPlay");
|
|
|
|
$btn.removeClass("paused");
|
|
$btn.removeClass("playing");
|
|
$btn.removeClass("stopped");
|
|
|
|
switch (state) {
|
|
case that.webAudio._states.stopped:
|
|
case that.webAudio._states.paused:
|
|
$btn.addClass("paused");
|
|
break;
|
|
|
|
case that.webAudio._states.loading:
|
|
case that.webAudio._states.ready:
|
|
case that.webAudio._states.playing:
|
|
$btn.addClass("playing");
|
|
break;
|
|
|
|
case -1:
|
|
$btn.addClass("stopped");
|
|
break;
|
|
}
|
|
}
|
|
|
|
, _disableControls: function () {
|
|
var that = this
|
|
;
|
|
|
|
that.$el.find(".enabled").addClass("disabled");
|
|
that.$el.find(".enabled").removeClass("enabled");
|
|
that.$el.find("[enabled]").attr("disabled", true);
|
|
that.$el.find("[enabled]").removeAttr("enabled");
|
|
//that.$el.find("progress").attr("disabled", true);
|
|
}
|
|
|
|
, _enableControls: function () {
|
|
var that = this
|
|
;
|
|
|
|
that.$el.find(".disabled").addClass("enabled");
|
|
that.$el.find(".disabled").removeClass("disabled");
|
|
that.$el.find("[disabled]").attr("enabled", true);
|
|
that.$el.find("[disabled]").removeAttr("disabled");
|
|
//that.$el.find("progress").removeAttr("disabled");
|
|
}
|
|
|
|
/*, _getItemData: function (item) {
|
|
var that = this
|
|
;
|
|
console.log(that.options.servers.get(item.hostname).client.collections);
|
|
return that.options.servers.get(item.hostname).client.collections.databases[item.id].get("dmap_listing").get(item.dbId);
|
|
}
|
|
*/
|
|
, _getItemMediaUrl: function (item) {
|
|
var that = this
|
|
;
|
|
|
|
return that.options.servers.get(item.hostname).client.urlMedia(item.id, item.type, item.dbId);
|
|
}
|
|
|
|
, _setViewport: function (item) {
|
|
var that = this
|
|
;
|
|
|
|
item = item || {};
|
|
|
|
that.$elViewport.html(_.template(tmplPlayerStatus)(item));
|
|
}
|
|
|
|
, _progressSetLoadding: function () {
|
|
var that = this
|
|
, $progress = that.$el.find(".playerProgressWrapper > progress")
|
|
;
|
|
|
|
$progress.removeAttr("value");
|
|
$progress.attr("disabled", true);
|
|
}
|
|
|
|
, _updateViewportProgress: function (progress, time) {
|
|
var that = this
|
|
;
|
|
|
|
progress = progress || 0;
|
|
time = time || 1;
|
|
|
|
if (that.$el.find("> .playerProgressWrapper")) {
|
|
var left = (time - progress)
|
|
, frac = progress / time
|
|
, precent = frac * 100
|
|
, elapsed = Math.floor(progress / 60).toFixed().pad(2, "0") + ":" + (progress % 60).toFixed().pad(2, "0")
|
|
, remain = "-" + Math.floor(left / 60).toFixed().pad(2, "0") + ":" + (left % 60).toFixed().pad(2, "0")
|
|
, $progress = that.$el.find(".playerProgressWrapper > progress")
|
|
;
|
|
|
|
that.$el.find(".playerProgressWrapper > span:first-child").text(elapsed);
|
|
that.$el.find(".playerProgressWrapper > span:last-child").text(remain);
|
|
|
|
$progress.removeAttr("disabled");
|
|
$progress.val(frac);
|
|
}
|
|
|
|
}
|
|
});
|
|
|
|
return Player;
|
|
}); |