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