123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570 |
- $(function() {
- /**
- * Ws is a wrapper of the WebSocket API
- * @class Ws
- */
- var Ws = function(options, managerOptions) {
- options = options || {};
- managerOptions = managerOptions || {};
- if (!options.url)
- throw "Cannot open a socket without a url";
- //@TODO: add try catch & retry
- this.onMessageCallbacks = [];
- this.socket = new WebSocket(options.url);
- this.url = options.url;
- this.maxTry = 4;
- this.recoTry = 0;
- };
- /**
- * @method init
- */
- Ws.prototype.init = function() {
- var self = this;
- this.socket.onopen = function() {
- self._onOpen(this);
- };
- this.socket.onmessage = function(e) {
- self.onMessage(e);
- };
- this.socket.onclose = function() {
- self._onClose();
- };
- this.socket.onerror = function(error) {
- self._onError(error);
- };
- };
- /**
- * @method _onOpen
- * @param {Object} e
- * @private
- */
- Ws.prototype._onOpen = function(e) {
- console.log(e);
- this.recoTry = 0;
- };
- /**
- * On message call message callbacks
- * @method onMessage
- * @param {Object} e
- */
- Ws.prototype.onMessage = function(e) {
- //@TODO: is json?
- var message = $.parseJSON(e.data);
- for (var i = 0, length = this.onMessageCallbacks.length; i < length; i++) {
- var cb = this.onMessageCallbacks[i];
- if (typeof cb === 'function') {
- cb(message);
- }
- }
- };
- /**
- * @method _onError
- * @param {Object} e
- * @private
- */
- Ws.prototype._onError = function(e) {
- console.log(e);
- };
- /**
- * @method _onClose
- * @param {Object} e
- * @private
- */
- Ws.prototype._onClose = function(e) {
- console.log(e);
- //try to reco?
- if (this.maxTry > this.recoTry) {
- return;
- }
- this.socket = new WebSocket(this.url);
- this.init();
- this.recoTry++;
- };
- /**
- * Send stringify & send message to the socket
- * @method sendMessage
- * @param message
- */
- Ws.prototype.sendMessage = function(message) {
- message = JSON.stringify(message);
- this.socket.send(message);
- };
- /**
- * @class PlayerControl
- */
- var PlayerControl = function(options) {
- options = options || {};
- if (!options.socket instanceof Ws)
- throw "You need to provide a socket instance";
- if (!options.element || !options.element.length)
- throw "Element not found";
- this._playing = false;
- this._vClicked = false;
- this._progDragged = false;
- this._progClicked = false;
- this.element = options.element;
- this.socket = options.socket;
- this.duration = 7200;
- this.currentTime = 0;
- this.volume = 0.5;
- this._previousVolume = 1;
- this.timeInterval = null;
- this.enableInterval = false;
- };
- /**
- * @method init
- */
- PlayerControl.prototype.init = function() {
- //Add event listener
- var self = this;
- //Update time
- self.updateTime();
- var buttonHeight = self.element.find('.volume-button').height();
- var y = buttonHeight * this.volume * 10;
- self.updateVolume(y);
- var progWidth = this.element.find('.progress').width();
- var x = (progWidth / self.duration) * self.currentTime;
- self.updateProgress(x);
- $(window).bind('mouseup', function(e) {
- self._progClicked = false;
- self._progDragged = false;
- self._vClicked = false;
- self._dragVolume = false;
- });
- // Shortcuts
- $(window).bind('keydown', function(e) {
- switch (e.which) {
- case 32:
- e.preventDefault();
- self.playPause();
- break;
- case 37: // left
- var time = self.currentTime - (self.duration / 100);
- self.seekTo(true, time);
- break;
- case 39: // right
- var time = self.currentTime + (self.duration / 100);
- self.seekTo(true, time);
- break;
- case 38: // up
- e.preventDefault();
- var buttonHeight = self.element.find('.volume-button').height();
- var y = buttonHeight * (self.volume + 0.1) * 10;
- self.updateVolume(y);
- break;
- case 40: // down
- e.preventDefault();
- var buttonHeight = self.element.find('.volume-button').height();
- var y = buttonHeight * (self.volume - 0.1) * 10;
- self.updateVolume(y);
- break;
- default:
- return; // exit this handler for other keys
- }
- });
- this.element.find('.play-pause').bind('click', function() {
- self.playPause();
- });
- this.element.find('.progress').bind('mousedown', function(e) {
- self.mousedownProgress(e);
- });
- this.element.bind('mousemove', function(e) {
- if (self._progClicked) {
- self.dragProgress(e);
- }
- if (self._vClicked) {
- self.dragVolume(e);
- }
- });
- this.element.find('.volume-bar-holder').bind('mousedown', function(e) {
- self._vClicked = true;
- var y = self.element.find('.volume-bar-holder').height() - (e.pageY - self.element.find('.volume-bar-holder').offset().top);
- self.updateVolume(y);
- });
- this.element.find('.volume-icon').bind('mousedown', function() {
- if (self.volume) {
- self._previousVolume = self.volume;
- self.volume = 0;
- } else {
- self.volume = self._previousVolume;
- }
- var buttonHeight = self.element.find('.volume-button').height();
- var y = buttonHeight * self.volume * 10;
- self.updateVolume(y);
- });
- };
- /**
- * @method updateVolume
- * @param {number} y
- */
- PlayerControl.prototype.updateVolume = function(y) {
- var buttonHeight = this.element.find('.volume-button').height();
- var barHolderHeight = this.element.find('.volume-bar-holder').height();
- if (y > barHolderHeight) {
- y = barHolderHeight;
- }
- this.element.find('.volume-bar').css({
- height: y + 'px'
- });
- //between 1 and 0
- this.volume = this.element.find('.volume-bar').height() / barHolderHeight;
- this.animateVolume();
- };
- /**
- * @method playPause
- */
- PlayerControl.prototype.playPause = function() {
- if (this.isEnded()) {
- return this.pause();
- }
- if (this._playing) {
- this.pause(true);
- }
- else {
- this.play(true);
- }
- };
- /**
- * @method animateVolume
- */
- PlayerControl.prototype.animateVolume = function() {
- var volumeIcon = this.element.find('.volume-icon');
- volumeIcon.removeClass().addClass('volume-icon v-change-' + parseInt(this.volume * 10));
- };
- /**
- * @method getMouseProgressPosition
- * @param {Object} e - DOM event
- * @returns {number}
- */
- PlayerControl.prototype.getMouseProgressPosition = function(e) {
- return e.pageX - this.element.find('.progress').offset().left;
- };
- /**
- * @method mousedownProgress
- * @param {Object} e - DOM event
- */
- PlayerControl.prototype.mousedownProgress = function(e) {
- var progWidth = this.element.find('.progress').width();
- this._progClicked = true;
- var x = this.getMouseProgressPosition(e);
- this.currentTime = Math.round((x / progWidth) * this.duration);
- this.seekTo(true, this.currentTime);
- };
- /**
- * @method dragProgress
- * @param {Object} e - DOM event
- */
- PlayerControl.prototype.dragProgress = function(e) {
- this._progDragged = true;
- var progMove = 0;
- var x = this.getMouseProgressPosition(e);
- var progWidth = this.element.find('.progress').width();
- if (this._playing && (this.currentTime < this.duration)) {
- this.play();
- }
- if (x <= 0) {
- progMove = 0;
- this.currentTime = 0;
- }
- else if (x > progWidth) {
- this.currentTime = this.duration;
- progMove = progWidth;
- }
- else {
- progMove = x;
- this.currentTime = Math.round((x / progWidth) * this.duration);
- }
- this.seekTo(true, this.currentTime);
- };
- /**
- * @method dragVolume
- * @param {Object} e - DOM event
- */
- PlayerControl.prototype.dragVolume = function(e) {
- this._dragVolume = true;
- var volHeight = this.element.find('.volume-bar-holder').height();
- var y = volHeight - (e.pageY - this.element.find('.volume-bar-holder').offset().top);
- var volMove = 0;
- if (y <= 0) {
- volMove = 0;
- } else if (y > this.element.find('.volume-bar-holder').height() ||
- (y / volHeight) === 1) {
- volMove = volHeight;
- } else {
- volMove = y;
- }
- this.updateVolume(volMove);
- };
- /**
- * @method updateTime
- * @param {number} currentTime
- */
- PlayerControl.prototype.updateTime = function(currentTime) {
- var progWidth = this.element.find('.progress').width();
- currentTime = currentTime ||
- Math.round(($('.progress-bar').width() / progWidth) * this.duration);
- currentTime /= 1000;
- var duration = this.duration / 1000;
- var seconds = 0,
- minutes = Math.floor(currentTime / 60),
- tminutes = Math.round(duration / 60),
- tseconds = Math.round((duration) - (tminutes * 60));
- seconds = Math.round(currentTime) - (60 * minutes);
- if (seconds > 59) {
- seconds = Math.round(currentTime) - (60 * minutes);
- if (seconds === 60) {
- minutes = Math.round(currentTime / 60);
- seconds = 0;
- }
- }
- // Set a zero before the number if less than 10.
- if (seconds < 10) {
- seconds = '0' + seconds;
- }
- if (tseconds < 10) {
- tseconds = '0' + tseconds;
- }
- this.element.find('.ctime').html(minutes + ':' + seconds);
- this.element.find('.ttime').html(tminutes + ':' + tseconds);
- };
- /**
- * @method updateProgress
- * @param {number} x
- */
- PlayerControl.prototype.updateProgress = function(x) {
- var buttonWidth = this.element.find('.progress-button').width();
- this.element.find('.progress-bar').css({
- width: x
- });
- this.element.find('.progress-button').css({
- left: x - (2 * buttonWidth) - (buttonWidth / 2) + 'px'
- });
- };
- /**
- * @method isEnded
- * @returns {boolean}
- */
- PlayerControl.prototype.isEnded = function() {
- return this.currentTime >= this.duration;
- };
- /**
- * @method play
- * @param {boolean} send
- */
- PlayerControl.prototype.play = function(send) {
- var self = this;
- var progWidth = this.element.find('.progress').width();
- var delay = 1000;
- if (this.timeInterval) {
- clearInterval(this.timeInterval);
- }
- //@TODO: use requestFrame instead
- this._playing = true;
- this.element
- .find('.play-pause')
- .addClass('pause')
- .removeClass('play');
- if (send) {
- this.socket.sendMessage({
- type: 'pause'
- });
- }
- if (!this.enableInterval) {
- return;
- }
- this.timeInterval = setInterval(function() {
- if (self.isEnded()) {
- self.currentTime = self.duration;
- self.pause();
- return clearInterval(this.timeInterval);
- }
- self.currentTime += 1;
- self.updateTime(self.currentTime);
- var x = (progWidth / self.duration) * self.currentTime;
- self.updateProgress(x);
- }, delay);
- };
- /**
- * @method pause
- * @param {boolean} send
- */
- PlayerControl.prototype.pause = function(send) {
- if (this.timeInterval) {
- clearInterval(this.timeInterval);
- }
- this._playing = false;
- this.element
- .find('.play-pause')
- .addClass('play')
- .removeClass('pause');
- if (send) {
- this.socket.sendMessage({
- type: 'pause',
- currentTime: this.currentTime,
- media: {
- id: this.id
- }
- });
- }
- };
- /**
- * @method seekTo
- * @param {boolean} send
- * @param {number} time
- */
- PlayerControl.prototype.seekTo = function(send, time) {
- //@TODO: make a debounce when dragging & key
- var progWidth = this.element.find('.progress').width();
- if (time < 0) {
- time = 0;
- }
- if (time > this.duration) {
- time = this.duration;
- }
- this.currentTime = time;
- if (this._playing) {
- this.play();
- }
- var x = (progWidth / this.duration) * this.currentTime;
- this.updateProgress(x);
- this.updateTime(this.currentTime);
- if (send) {
- this.socket.sendMessage({
- type: 'seekTo',
- currentTime: time
- });
- }
- };
- /**
- * @method goTo
- * @param {boolean} send
- */
- PlayerControl.prototype.goTo = function(send) {
- this.socket.sendMessage({
- type: 'goTo'
- });
- };
- /**
- * @method playing
- * @param {Object} message
- */
- PlayerControl.prototype.playing = function(message) {
- this.currentTime = message.currentTime;
- this.duration = message.media.duration;
- this.title = message.media.title;
- this.id = message.media.id;
- var titleElement = this.element.find('.title');
- titleElement.text(this.title);
- this.play();
- }
- /**
- * Instanciation of the Ws class
- */
- //@TODO: This URL need to be updated at runtime
- var URL = 'ws://192.168.0.14:8888';
- var socket = new Ws({
- url: URL
- });
- socket.init();
- var playerControl = new PlayerControl({
- socket: socket,
- element: $('.player-control')
- });
- playerControl.init();
- /**
- * Map which method is allowed by event type
- */
- var TYPE_MAP = {
- play: function(message) {
- playerControl.play(message);
- },
- pause: function(message) {
- playerControl.pause(message);
- },
- playing: function(message) {
- playerControl.playing(message);
- },
- seekTo: function(message) {
- playerControl.seekTo(null, message.currentTime);
- }
- };
- /**
- * Manage incoming messages
- */
- socket.onMessageCallbacks.push(function(message) {
- var key = 'type';
- var type = message[key];
- if (!type || typeof TYPE_MAP[type] !== 'function')
- return;
- TYPE_MAP[type](message);
- });
- });
|