      1 // Add a way to instanciate using jQuery prototype.
      2 if (!jQuery.fn.minplayer) {
      3
      4   /**
      5    * @constructor
      6    *
      7    * Define a jQuery minplayer prototype.
      8    *
      9    * @param {object} options The options for this jQuery prototype.
     10    * @return {Array} jQuery object.
     11    */
     12   jQuery.fn.minplayer = function(options) {
     13     return jQuery(this).each(function() {
     14       options = options || {};
     15       options.id = options.id || jQuery(this).attr('id') || Math.random();
     16       if (!minplayer.plugins[options.id]) {
     17         options.template = options.template || 'default';
     18         if (minplayer[options.template]) {
     19           new minplayer[options.template](jQuery(this), options);
     20         }
     21         else {
     22           new minplayer(jQuery(this), options);
     23         }
     24       }
     25     });
     26   };
     27 }
     28
     29 /**
     30  * @constructor
     31  * @extends minplayer.display
     32  * @class The core media player class which governs the media player
     33  * functionality.
     34  *
     35  * <p><strong>Usage:</strong>
     36  * <pre><code>
     37  *
     38  *   // Create a media player.
     39  *   var player = jQuery("#player").minplayer({
     40  *
     41  *   });
     42  *
     43  * </code></pre>
     44  * </p>
     45  *
     46  * @param {object} context The jQuery context.
     47  * @param {object} options This components options.
     48  */
     49 minplayer = jQuery.extend(function(context, options) {
     50
     51   // Derive from display
     52   minplayer.display.call(this, 'player', context, options);
     53 }, minplayer);
     54
     55 /** Derive from minplayer.display. */
     56 minplayer.prototype = new minplayer.display();
     57
     58 /** Reset the constructor. */
     59 minplayer.prototype.constructor = minplayer;
     60
     61 /**
     62  * @see minplayer.plugin.construct
     63  */
     64 minplayer.prototype.construct = function() {
     65
     66   // Allow them to provide arguments based off of the DOM attributes.
     67   jQuery.each(this.context[0].attributes, (function(player) {
     68     return function(index, attr) {
     69       player.options[attr.name] = player.options[attr.name] || attr.value;
     70     };
     71   })(this));
     72
     73   // Make sure we provide default options...
     74   this.options = jQuery.extend({
     75     id: 'player',
     76     build: false,
     77     wmode: 'transparent',
     78     preload: true,
     79     autoplay: false,
     80     autoload: true,
     81     loop: false,
     82     width: '100%',
     83     height: '350px',
     84     debug: false,
     85     volume: 80,
     86     files: null,
     87     file: '',
     88     preview: '',
     89     attributes: {},
     90     logo: '',
     91     link: '',
     92     width: '100%',
     93     height: '100%'
     94   }, this.options);
     95
     96   // Call the minplayer display constructor.
     97   minplayer.display.prototype.construct.call(this);
     98
     99   // Set the plugin name within the options.
    100   this.options.pluginName = 'player';
    101
    102   /** The controller for this player. */
    103   this.controller = this.create('controller');
    104
    105   /** The play loader for this player. */
    106   this.playLoader = this.create('playLoader');
    107
    108   // Set the focus of the element based on if they click in or outside of it.
    109   minplayer.click(document, (function(player) {
    110     return function(event) {
    111       var target = jQuery(event.target);
    112       var focus = !(target.closest('#' + player.options.id).length == 0);
    113       minplayer.get.call(this, player.options.id, null, function(plugin) {
    114         plugin.onFocus(focus);
    115       });
    116     };
    117   })(this));
    118
    119   /** Add the logo for the player. */
    120   if (this.options.logo && this.elements.logo) {
    121
    122     var code = '';
    123     if (this.options.link) {
    124       code += '<a target="_blank" href="' + this.options.link + '">';
    125     }
    126     code += '<img src="' + this.options.logo + '" >';
    127     if (this.options.link) {
    128       code += '</a>';
    129     }
    130     this.logo = this.elements.logo.append(code);
    131   }
    132
    133   /** Variable to store the current media player. */
    134   this.currentPlayer = 'html5';
    135
    136   // Add key events to the window.
    137   this.addKeyEvents();
    138
    139   // Called to add events.
    140   this.addEvents();
    141
    142   // Now load these files.
    143   this.load(this.getFiles());
    144
    145   // The player is ready.
    146   this.ready();
    147 };
    148
    149 /**
    150  * Called when an error occurs.
    151  *
    152  * @param {object} plugin The plugin you wish to bind to.
    153  */
    154 minplayer.prototype.bindTo = function(plugin) {
    155   plugin.bind('error', (function(player) {
    156     return function(event, data) {
    157       if (player.currentPlayer == 'html5') {
    158         minplayer.player = 'minplayer';
    159         player.options.file.player = 'minplayer';
    160         player.loadPlayer();
    161       }
    162       else {
    163         player.showError(data);
    164       }
    165     };
    166   })(this));
    167
    168   // Bind to the fullscreen event.
    169   plugin.bind('fullscreen', (function(player) {
    170     return function(event, data) {
    171       player.resize();
    172     };
    173   })(this));
    174 };
    175
    176 /**
    177  * We need to bind to events we are interested in.
    178  */
    179 minplayer.prototype.addEvents = function() {
    180   minplayer.get.call(this, this.options.id, null, (function(player) {
    181     return function(plugin) {
    182       player.bindTo(plugin);
    183     };
    184   })(this));
    185 };
    186
    187 /**
    188  * Sets an error on the player.
    189  *
    190  * @param {string} error The error to display on the player.
    191  */
    192 minplayer.prototype.showError = function(error) {
    193   if (typeof error !== 'object') {
    194     error = error || '';
    195     if (this.elements.error) {
    196
    197       // Set the error text.
    198       this.elements.error.text(error);
    199       if (error) {
    200         // Show the error message.
    201         this.elements.error.show();
    202
    203         // Only show this error for a time interval.
    204         setTimeout((function(player) {
    205           return function() {
    206             player.elements.error.hide('slow');
    207           };
    208         })(this), 5000);
    209       }
    210       else {
    211         this.elements.error.hide();
    212       }
    213     }
    214   }
    215 };
    216
    217 /**
    218  * Adds key events to the player.
    219  */
    220 minplayer.prototype.addKeyEvents = function() {
    221   jQuery(document).bind('keydown', (function(player) {
    222     return function(event) {
    223       switch (event.keyCode) {
    224         case 113: // ESC
    225         case 27:  // Q
    226           if (player.isFullScreen()) {
    227             player.fullscreen(false);
    228           }
    229           break;
    230       }
    231     };
    232   })(this));
    233 };
    234
    235 /**
    236  * Returns all the media files available for this player.
    237  *
    238  * @return {array} All the media files for this player.
    239  */
    240 minplayer.prototype.getFiles = function() {
    241
    242   // If they provide the files in the options, use those first.
    243   if (this.options.files) {
    244     return this.options.files;
    245   }
    246
    247   if (this.options.file) {
    248     return this.options.file;
    249   }
    250
    251   var files = [];
    252   var mediaSrc = null;
    253
    254   // Get the files involved...
    255   if (this.elements.media) {
    256     mediaSrc = this.elements.media.attr('src');
    257     if (mediaSrc) {
    258       files.push({'path': mediaSrc});
    259     }
    260     jQuery('source', this.elements.media).each(function() {
    261       files.push({
    262         'path': jQuery(this).attr('src'),
    263         'mimetype': jQuery(this).attr('type'),
    264         'codecs': jQuery(this).attr('codecs')
    265       });
    266     });
    267   }
    268
    269   return files;
    270 };
    271
    272 /**
    273  * Returns the full media player object.
    274  *
    275  * @param {array} files An array of files to chose from.
    276  * @return {object} The best media file to play in the current browser.
    277  */
    278 minplayer.getMediaFile = function(files) {
    279
    280   // If there are no files then return null.
    281   if (!files) {
    282     return null;
    283   }
    284
    285   // If the file is already a file object then just return.
    286   if ((typeof files === 'string') || files.path || files.id) {
    287     return new minplayer.file(files);
    288   }
    289
    290   // Add the files and get the best player to play.
    291   var bestPriority = 0, mFile = null, file = null;
    292   for (var i in files) {
    293     if (files.hasOwnProperty(i)) {
    294       file = new minplayer.file(files[i]);
    295       if (file.player && (file.priority > bestPriority)) {
    296         mFile = file;
    297       }
    298     }
    299   }
    300
    301   // Return the best minplayer file.
    302   return mFile;
    303 };
    304
    305 /**
    306  * Loads a media player based on the current file.
    307  *
    308  * @return {boolean} If a new player was loaded.
    309  */
    310 minplayer.prototype.loadPlayer = function() {
    311
    312   // Do nothing if there isn't a file or anywhere to put it.
    313   if (!this.options.file || (this.elements.display.length == 0)) {
    314     return false;
    315   }
    316
    317   // If no player is set, then also return false.
    318   if (!this.options.file.player) {
    319     return false;
    320   }
    321
    322   // Reset the error.
    323   this.showError();
    324
    325   // Only destroy if the current player is different than the new player.
    326   var player = this.options.file.player.toString();
    327
    328   // If there isn't media or if the players are different.
    329   if (!this.media || (player !== this.currentPlayer)) {
    330
    331     // Set the current media player.
    332     this.currentPlayer = player;
    333
    334     // Do nothing if we don't have a display.
    335     if (!this.elements.display) {
    336       this.showError('No media display found.');
    337       return;
    338     }
    339
    340     // Destroy the current media.
    341     var queue = {};
    342     if (this.media) {
    343       queue = this.media.queue;
    344       this.media.destroy();
    345     }
    346
    347     // Get the class name and create the new player.
    348     pClass = minplayer.players[this.options.file.player];
    349
    350     // Create the new media player.
    351     this.options.mediaelement = this.elements.media;
    352     this.media = new pClass(this.elements.display, this.options, queue);
    353
    354     // Now get the media when it is ready.
    355     this.get('media', (function(player) {
    356       return function(media) {
    357
    358         // Load the media.
    359         media.load(player.options.file);
    360         player.display.addClass('minplayer-player-' + media.mediaFile.player);
    361       };
    362     })(this));
    363
    364     // Return that a new player is loaded.
    365     return true;
    366   }
    367   // If the media object already exists...
    368   else if (this.media) {
    369
    370     // Now load the different media file.
    371     this.media.options = this.options;
    372     this.display.removeClass('minplayer-player-' + this.media.mediaFile.player);
    373     this.media.load(this.options.file);
    374     this.display.addClass('minplayer-player-' + this.media.mediaFile.player);
    375     return false;
    376   }
    377 };
    378
    379 /**
    380  * Load a set of files or a single file for the media player.
    381  *
    382  * @param {array} files An array of files to chose from to load.
    383  */
    384 minplayer.prototype.load = function(files) {
    385
    386   // Set the id and class.
    387   var id = '', pClass = '';
    388
    389   // If no file was provided, then get it.
    390   this.options.files = files || this.options.files;
    391   this.options.file = minplayer.getMediaFile(this.options.files);
    392
    393   // Now load the player.
    394   if (this.loadPlayer()) {
    395
    396     // Add the events since we now have a player.
    397     this.bindTo(this.media);
    398
    399     // If the player isn't valid, then show an error.
    400     if (this.options.file.mimetype && !this.options.file.player) {
    401       this.showError('Cannot play media: ' + this.options.file.mimetype);
    402     }
    403   }
    404 };
    405
    406 /**
    407  * Called when the player is resized.
    408  */
    409 minplayer.prototype.resize = function() {
    410
    411   // Call onRezie for each plugin.
    412   this.get(function(plugin) {
    413     if (plugin.onResize) {
    414       plugin.onResize();
    415     }
    416   });
    417 };
    418