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