1 /** The minplayer namespace. */
  2 var minplayer = minplayer || {};
  3 
  4 /** All the media player implementations */
  5 minplayer.players = minplayer.players || {};
  6 
  7 /**
  8  * @constructor
  9  * @extends minplayer.display
 10  * @class The base media player class where all media players derive from.
 11  *
 12  * @param {object} context The jQuery context.
 13  * @param {object} options This components options.
 14  * @param {object} queue The event queue to pass events around.
 15  */
 16 minplayer.players.base = function(context, options, queue) {
 17 
 18   // Derive from display
 19   minplayer.display.call(this, 'media', context, options, queue);
 20 };
 21 
 22 /** Derive from minplayer.display. */
 23 minplayer.players.base.prototype = new minplayer.display();
 24 
 25 /** Reset the constructor. */
 26 minplayer.players.base.prototype.constructor = minplayer.players.base;
 27 
 28 /**
 29  * @see minplayer.display.getElements
 30  * @this minplayer.players.base
 31  * @return {object} The elements for this display.
 32  */
 33 minplayer.players.base.prototype.getElements = function() {
 34   var elements = minplayer.display.prototype.getElements.call(this);
 35   return jQuery.extend(elements, {
 36     media: this.options.mediaelement
 37   });
 38 };
 39 
 40 /**
 41  * Get the priority of this media player.
 42  *
 43  * @return {number} The priority of this media player.
 44  */
 45 minplayer.players.base.getPriority = function() {
 46   return 0;
 47 };
 48 
 49 /**
 50  * Returns the ID for the media being played.
 51  *
 52  * @param {object} file A {@link minplayer.file} object.
 53  * @return {string} The ID for the provided media.
 54  */
 55 minplayer.players.base.getMediaId = function(file) {
 56   return '';
 57 };
 58 
 59 /**
 60  * Determine if we can play the media file.
 61  *
 62  * @param {object} file A {@link minplayer.file} object.
 63  * @return {boolean} If this player can play this media type.
 64  */
 65 minplayer.players.base.canPlay = function(file) {
 66   return false;
 67 };
 68 
 69 /**
 70  * @see minplayer.plugin.construct
 71  * @this minplayer.players.base
 72  */
 73 minplayer.players.base.prototype.construct = function() {
 74 
 75   // Call the media display constructor.
 76   minplayer.display.prototype.construct.call(this);
 77 
 78   // Clear the media player.
 79   this.clear();
 80 
 81   /** The currently loaded media file. */
 82   this.mediaFile = this.options.file;
 83 
 84   // Get the player display object.
 85   if (!this.playerFound()) {
 86 
 87     // Remove the media element if found
 88     if (this.elements.media) {
 89       this.elements.media.remove();
 90     }
 91 
 92     // Create a new media player element.
 93     this.elements.media = jQuery(this.create());
 94     this.display.html(this.elements.media);
 95   }
 96 
 97   // Get the player object...
 98   this.player = this.getPlayer();
 99 
100   // Set the focus of the element based on if they click in or outside of it.
101   jQuery(document).bind('click', (function(player) {
102     return function(event) {
103       if (jQuery(event.target).closest('#' + player.options.id).length == 0) {
104         player.hasFocus = false;
105       }
106       else {
107         player.hasFocus = true;
108       }
109     };
110   })(this));
111 
112   // Bind to key events...
113   jQuery(document).bind('keydown', (function(player) {
114     return function(event) {
115       if (player.hasFocus) {
116         event.preventDefault();
117         switch (event.keyCode) {
118           case 32:  // SPACE
119           case 179: // GOOGLE play/pause button.
120             if (player.playing) {
121               player.pause();
122             }
123             else {
124               player.play();
125             }
126             break;
127           case 38:  // UP
128             player.setVolumeRelative(0.1);
129             break;
130           case 40:  // DOWN
131             player.setVolumeRelative(-0.1);
132             break;
133           case 37:  // LEFT
134           case 227: // GOOGLE TV REW
135             player.seekRelative(-0.05);
136             break;
137           case 39:  // RIGHT
138           case 228: // GOOGLE TV FW
139             player.seekRelative(0.05);
140             break;
141         }
142       }
143     };
144   })(this));
145 };
146 
147 /**
148  * @see minplayer.plugin.destroy.
149  */
150 minplayer.players.base.prototype.destroy = function() {
151   minplayer.plugin.prototype.destroy.call(this);
152   this.clear();
153 };
154 
155 /**
156  * Clears the media player.
157  */
158 minplayer.players.base.prototype.clear = function() {
159 
160   // Reset the ready flag.
161   this.playerReady = false;
162 
163   // Reset the player.
164   this.reset();
165 
166   // If the player exists, then unbind all events.
167   if (this.player) {
168     jQuery(this.player).unbind();
169   }
170 };
171 
172 /**
173  * Resets all variables.
174  */
175 minplayer.players.base.prototype.reset = function() {
176 
177   // The duration of the player.
178   this.duration = new minplayer.async();
179 
180   // The current play time of the player.
181   this.currentTime = new minplayer.async();
182 
183   // The amount of bytes loaded in the player.
184   this.bytesLoaded = new minplayer.async();
185 
186   // The total amount of bytes for the media.
187   this.bytesTotal = new minplayer.async();
188 
189   // The bytes that the download started with.
190   this.bytesStart = new minplayer.async();
191 
192   // The current volume of the player.
193   this.volume = new minplayer.async();
194 
195   // Reset focus.
196   this.hasFocus = false;
197 
198   // We are not playing.
199   this.playing = false;
200 
201   // We are not loading.
202   this.loading = false;
203 
204   // Tell everyone else we reset.
205   this.trigger('pause');
206   this.trigger('waiting');
207   this.trigger('progress', {loaded: 0, total: 0, start: 0});
208   this.trigger('timeupdate', {currentTime: 0, duration: 0});
209 };
210 
211 /**
212  * Called when the player is ready to recieve events and commands.
213  */
214 minplayer.players.base.prototype.onReady = function() {
215 
216   // Only continue if we are not already ready.
217   if (this.playerReady) {
218     return;
219   }
220 
221   // Set the ready flag.
222   this.playerReady = true;
223 
224   // Set the volume to the default.
225   this.setVolume(this.options.volume / 100);
226 
227   // Setup the progress interval.
228   this.loading = true;
229 
230   // Create a poll to get the progress.
231   this.poll((function(player) {
232     return function() {
233 
234       // Only do this if the play interval is set.
235       if (player.loading) {
236 
237         // Get the bytes loaded asynchronously.
238         player.getBytesLoaded(function(bytesLoaded) {
239 
240           // Get the bytes total asynchronously.
241           player.getBytesTotal(function(bytesTotal) {
242 
243             // Trigger an event about the progress.
244             if (bytesLoaded || bytesTotal) {
245 
246               // Get the bytes start, but don't require it.
247               var bytesStart = 0;
248               player.getBytesStart(function(val) {
249                 bytesStart = val;
250               });
251 
252               // Trigger a progress event.
253               player.trigger('progress', {
254                 loaded: bytesLoaded,
255                 total: bytesTotal,
256                 start: bytesStart
257               });
258 
259               // Say we are not longer loading if they are equal.
260               if (bytesLoaded >= bytesTotal) {
261                 player.loading = false;
262               }
263             }
264           });
265         });
266       }
267 
268       // Keep polling as long as its loading...
269       return player.loading;
270     };
271   })(this), 1000);
272 
273   // We are now ready.
274   this.ready();
275 
276   // Trigger that the load has started.
277   this.trigger('loadstart');
278 };
279 
280 /**
281  * Should be called when the media is playing.
282  */
283 minplayer.players.base.prototype.onPlaying = function() {
284 
285   // Trigger an event that we are playing.
286   this.trigger('playing');
287 
288   // Say that this player has focus.
289   this.hasFocus = true;
290 
291   // Set the playInterval to true.
292   this.playing = true;
293 
294   // Create a poll to get the timeupate.
295   this.poll((function(player) {
296     return function() {
297 
298       // Only do this if the play interval is set.
299       if (player.playing) {
300 
301         // Get the current time asyncrhonously.
302         player.getCurrentTime(function(currentTime) {
303 
304           // Get the duration asynchronously.
305           player.getDuration(function(duration) {
306 
307             // Convert these to floats.
308             currentTime = parseFloat(currentTime);
309             duration = parseFloat(duration);
310 
311             // Trigger an event about the progress.
312             if (currentTime || duration) {
313 
314               // Trigger an update event.
315               player.trigger('timeupdate', {
316                 currentTime: currentTime,
317                 duration: duration
318               });
319             }
320           });
321         });
322       }
323 
324       // Keep polling as long as it is playing.
325       return player.playing;
326     };
327   })(this), 1000);
328 };
329 
330 /**
331  * Should be called when the media is paused.
332  */
333 minplayer.players.base.prototype.onPaused = function() {
334 
335   // Trigger an event that we are paused.
336   this.trigger('pause');
337 
338   // Remove focus.
339   this.hasFocus = false;
340 
341   // Say we are not playing.
342   this.playing = false;
343 };
344 
345 /**
346  * Should be called when the media is complete.
347  */
348 minplayer.players.base.prototype.onComplete = function() {
349   // Stop the intervals.
350   this.playing = false;
351   this.loading = false;
352   this.hasFocus = false;
353   this.trigger('ended');
354 };
355 
356 /**
357  * Should be called when the media is done loading.
358  */
359 minplayer.players.base.prototype.onLoaded = function() {
360 
361   // If we should autoplay, then just play now.
362   if (this.options.autoplay) {
363     this.play();
364   }
365 
366   this.trigger('loadeddata');
367 };
368 
369 /**
370  * Should be called when the player is waiting.
371  */
372 minplayer.players.base.prototype.onWaiting = function() {
373   this.trigger('waiting');
374 };
375 
376 /**
377  * Called when an error occurs.
378  *
379  * @param {string} errorCode The error that was triggered.
380  */
381 minplayer.players.base.prototype.onError = function(errorCode) {
382   this.hasFocus = false;
383   this.trigger('error', errorCode);
384 };
385 
386 /**
387  * @see minplayer.players.base#isReady
388  * @return {boolean} Checks to see if the Flash is ready.
389  */
390 minplayer.players.base.prototype.isReady = function() {
391 
392   // Return that the player is set and the ready flag is good.
393   return (this.player && this.playerReady);
394 };
395 
396 /**
397  * Determines if the player should show the playloader.
398  *
399  * @return {bool} If this player implements its own playLoader.
400  */
401 minplayer.players.base.prototype.hasPlayLoader = function() {
402   return false;
403 };
404 
405 /**
406  * Returns if the media player is already within the DOM.
407  *
408  * @return {boolean} TRUE - if the player is in the DOM, FALSE otherwise.
409  */
410 minplayer.players.base.prototype.playerFound = function() {
411   return false;
412 };
413 
414 /**
415  * Creates the media player and inserts it in the DOM.
416  *
417  * @return {object} The media player entity.
418  */
419 minplayer.players.base.prototype.create = function() {
420   this.reset();
421   return null;
422 };
423 
424 /**
425  * Returns the media player object.
426  *
427  * @return {object} The media player object.
428  */
429 minplayer.players.base.prototype.getPlayer = function() {
430   return this.player;
431 };
432 
433 /**
434  * Loads a new media player.
435  *
436  * @param {object} file A {@link minplayer.file} object.
437  */
438 minplayer.players.base.prototype.load = function(file) {
439 
440   // Store the media file for future lookup.
441   var isString = (typeof this.mediaFile == 'string');
442   var path = isString ? this.mediaFile : this.mediaFile.path;
443   if (file && (file.path != path)) {
444     this.reset();
445     this.mediaFile = file;
446   }
447 };
448 
449 /**
450  * Play the loaded media file.
451  */
452 minplayer.players.base.prototype.play = function() {
453 };
454 
455 /**
456  * Pause the loaded media file.
457  */
458 minplayer.players.base.prototype.pause = function() {
459 };
460 
461 /**
462  * Stop the loaded media file.
463  */
464 minplayer.players.base.prototype.stop = function() {
465   this.playing = false;
466   this.loading = false;
467   this.hasFocus = false;
468 };
469 
470 /**
471  * Seeks to relative position.
472  *
473  * @param {number} pos Relative position.  -1 to 1 (percent), > 1 (seconds).
474  */
475 minplayer.players.base.prototype.seekRelative = function(pos) {
476 
477   // Get the current time asyncrhonously.
478   this.getCurrentTime((function(player) {
479     return function(currentTime) {
480 
481       // Get the duration asynchronously.
482       player.getDuration(function(duration) {
483 
484         // Only do this if we have a duration.
485         if (duration) {
486 
487           // Get the position.
488           var seekPos = 0;
489           if ((pos > -1) && (pos < 1)) {
490             seekPos = (currentTime / duration) + parseFloat(pos);
491           }
492           else {
493             seekPos = (currentTime + parseFloat(pos)) / duration;
494           }
495 
496           // Set the seek value.
497           player.seek(seekPos);
498         }
499       });
500     };
501   })(this));
502 };
503 
504 /**
505  * Seek the loaded media.
506  *
507  * @param {number} pos The position to seek the minplayer. 0 to 1.
508  */
509 minplayer.players.base.prototype.seek = function(pos) {
510 };
511 
512 /**
513  * Set the volume of the loaded minplayer.
514  *
515  * @param {number} vol -1 to 1 - The relative amount to increase or decrease.
516  */
517 minplayer.players.base.prototype.setVolumeRelative = function(vol) {
518 
519   // Get the volume
520   this.getVolume((function(player) {
521     return function(newVol) {
522       newVol += parseFloat(vol);
523       newVol = (newVol < 0) ? 0 : newVol;
524       newVol = (newVol > 1) ? 1 : newVol;
525       player.setVolume(newVol);
526     };
527   })(this));
528 };
529 
530 /**
531  * Set the volume of the loaded minplayer.
532  *
533  * @param {number} vol The volume to set the media. 0 to 1.
534  */
535 minplayer.players.base.prototype.setVolume = function(vol) {
536   this.trigger('volumeupdate', vol);
537 };
538 
539 /**
540  * Get the volume from the loaded media.
541  *
542  * @param {function} callback Called when the volume is determined.
543  * @return {number} The volume of the media; 0 to 1.
544  */
545 minplayer.players.base.prototype.getVolume = function(callback) {
546   return this.volume.get(callback);
547 };
548 
549 /**
550  * Get the current time for the media being played.
551  *
552  * @param {function} callback Called when the time is determined.
553  * @return {number} The volume of the media; 0 to 1.
554  */
555 minplayer.players.base.prototype.getCurrentTime = function(callback) {
556   return this.currentTime.get(callback);
557 };
558 
559 /**
560  * Return the duration of the loaded media.
561  *
562  * @param {function} callback Called when the duration is determined.
563  * @return {number} The duration of the loaded media.
564  */
565 minplayer.players.base.prototype.getDuration = function(callback) {
566   return this.duration.get(callback);
567 };
568 
569 /**
570  * Return the start bytes for the loaded media.
571  *
572  * @param {function} callback Called when the start bytes is determined.
573  * @return {int} The bytes that were started.
574  */
575 minplayer.players.base.prototype.getBytesStart = function(callback) {
576   return this.bytesStart.get(callback);
577 };
578 
579 /**
580  * Return the bytes of media loaded.
581  *
582  * @param {function} callback Called when the bytes loaded is determined.
583  * @return {int} The amount of bytes loaded.
584  */
585 minplayer.players.base.prototype.getBytesLoaded = function(callback) {
586   return this.bytesLoaded.get(callback);
587 };
588 
589 /**
590  * Return the total amount of bytes.
591  *
592  * @param {function} callback Called when the bytes total is determined.
593  * @return {int} The total amount of bytes for this media.
594  */
595 minplayer.players.base.prototype.getBytesTotal = function(callback) {
596   return this.bytesTotal.get(callback);
597 };
598