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