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