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     plugins: {},
 91     logo: '',
 92     link: ''
 93   }, this.options);
 94 
 95   // Call the minplayer display constructor.
 96   minplayer.display.prototype.construct.call(this);
 97 
 98   // Initialize all plugins.
 99   var plugin = null;
100   for (var pluginName in this.options.plugins) {
101     plugin = this.options.plugins[pluginName];
102     if (minplayer[plugin]) {
103       plugin = minplayer[plugin];
104       if (plugin[this.options.template] && plugin[this.options.template].init) {
105         plugin[this.options.template].init(this);
106       }
107       else if (plugin.init) {
108         plugin.init(this);
109       }
110     }
111   }
112 
113   // Set the plugin name within the options.
114   this.options.pluginName = 'player';
115 
116   /** The controller for this player. */
117   this.controller = this.create('controller');
118 
119   /** The play loader for this player. */
120   this.playLoader = this.create('playLoader');
121 
122   /** Add the logo for the player. */
123   if (this.options.logo && this.elements.logo) {
124 
125     var code = '';
126     if (this.options.link) {
127       code += '<a target="_blank" href="' + this.options.link + '">';
128     }
129     code += '<img src="' + this.options.logo + '" >';
130     if (this.options.link) {
131       code += '</a>';
132     }
133     this.logo = this.elements.logo.append(code);
134   }
135 
136   /** Variable to store the current media player. */
137   this.currentPlayer = 'html5';
138 
139   // Add key events to the window.
140   this.addKeyEvents();
141 
142   // Called to add events.
143   this.addEvents();
144 
145   // Now load these files.
146   this.load(this.getFiles());
147 
148   // The player is ready.
149   this.ready();
150 };
151 
152 /**
153  * Set the focus for this player.
154  *
155  * @param {boolean} focus If the player is in focus or not.
156  */
157 minplayer.prototype.setFocus = function(focus) {
158 
159   // Tell all plugins about this.
160   minplayer.get.call(this, this.options.id, null, function(plugin) {
161     plugin.onFocus(focus);
162   });
163 
164   // Trigger an event that a focus event has occured.
165   this.trigger('playerFocus', focus);
166 };
167 
168 /**
169  * Called when an error occurs.
170  *
171  * @param {object} plugin The plugin you wish to bind to.
172  */
173 minplayer.prototype.bindTo = function(plugin) {
174   plugin.ubind(this.uuid + ':error', (function(player) {
175     return function(event, data) {
176       if (player.currentPlayer == 'html5') {
177         minplayer.player = 'minplayer';
178         player.options.file.player = 'minplayer';
179         player.loadPlayer();
180       }
181       else {
182         player.showError(data);
183       }
184     };
185   })(this));
186 
187   // Bind to the fullscreen event.
188   plugin.ubind(this.uuid + ':fullscreen', (function(player) {
189     return function(event, data) {
190       player.resize();
191     };
192   })(this));
193 };
194 
195 /**
196  * We need to bind to events we are interested in.
197  */
198 minplayer.prototype.addEvents = function() {
199 
200   // Keep track if we are inside the player or not.
201   var inside = false;
202 
203   // Set the focus when they enter the player.
204   this.display.bind('mouseenter', (function(player) {
205     return function() {
206       inside = true;
207       player.setFocus(true);
208     };
209   })(this));
210 
211 
212   this.display.bind('mouseleave', (function(player) {
213     return function() {
214       inside = false;
215       player.setFocus(false);
216     };
217   })(this));
218 
219   var moveThrottle = false;
220   this.display.bind('mousemove', (function(player) {
221     return function() {
222       if (!moveThrottle) {
223         moveThrottle = setTimeout(function() {
224           moveThrottle = false;
225           if (inside) {
226             player.setFocus(true);
227           }
228         }, 300);
229       }
230     };
231   })(this));
232 
233   minplayer.get.call(this, this.options.id, null, (function(player) {
234     return function(plugin) {
235       player.bindTo(plugin);
236     };
237   })(this));
238 };
239 
240 /**
241  * Sets an error on the player.
242  *
243  * @param {string} error The error to display on the player.
244  */
245 minplayer.prototype.showError = function(error) {
246   if (typeof error !== 'object') {
247     error = error || '';
248     if (this.elements.error) {
249 
250       // Set the error text.
251       this.elements.error.text(error);
252       if (error) {
253         // Show the error message.
254         this.elements.error.show();
255 
256         // Only show this error for a time interval.
257         setTimeout((function(player) {
258           return function() {
259             player.elements.error.hide('slow');
260           };
261         })(this), 5000);
262       }
263       else {
264         this.elements.error.hide();
265       }
266     }
267   }
268 };
269 
270 /**
271  * Adds key events to the player.
272  */
273 minplayer.prototype.addKeyEvents = function() {
274   jQuery(document).bind('keydown', (function(player) {
275     return function(event) {
276       switch (event.keyCode) {
277         case 113: // ESC
278         case 27:  // Q
279           if (player.isFullScreen()) {
280             player.fullscreen(false);
281           }
282           break;
283       }
284     };
285   })(this));
286 };
287 
288 /**
289  * Returns all the media files available for this player.
290  *
291  * @return {array} All the media files for this player.
292  */
293 minplayer.prototype.getFiles = function() {
294 
295   // If they provide the files in the options, use those first.
296   if (this.options.files) {
297     return this.options.files;
298   }
299 
300   if (this.options.file) {
301     return this.options.file;
302   }
303 
304   var files = [];
305   var mediaSrc = null;
306 
307   // Get the files involved...
308   if (this.elements.media) {
309     mediaSrc = this.elements.media.attr('src');
310     if (mediaSrc) {
311       files.push({'path': mediaSrc});
312     }
313     jQuery('source', this.elements.media).each(function() {
314       files.push({
315         'path': jQuery(this).attr('src'),
316         'mimetype': jQuery(this).attr('type'),
317         'codecs': jQuery(this).attr('codecs')
318       });
319     });
320   }
321 
322   return files;
323 };
324 
325 /**
326  * Returns the full media player object.
327  *
328  * @param {array} files An array of files to chose from.
329  * @return {object} The best media file to play in the current browser.
330  */
331 minplayer.getMediaFile = function(files) {
332 
333   // If there are no files then return null.
334   if (!files) {
335     return null;
336   }
337 
338   // If the file is already a file object then just return.
339   if ((typeof files === 'string') || files.path || files.id) {
340     return new minplayer.file(files);
341   }
342 
343   // Add the files and get the best player to play.
344   var bestPriority = 0, mFile = null, file = null;
345   for (var i in files) {
346     if (files.hasOwnProperty(i)) {
347       file = new minplayer.file(files[i]);
348       if (file.player && (file.priority > bestPriority)) {
349         mFile = file;
350       }
351     }
352   }
353 
354   // Return the best minplayer file.
355   return mFile;
356 };
357 
358 /**
359  * Loads a media player based on the current file.
360  *
361  * @return {boolean} If a new player was loaded.
362  */
363 minplayer.prototype.loadPlayer = function() {
364 
365   // Do nothing if there isn't a file or anywhere to put it.
366   if (!this.options.file || (this.elements.display.length == 0)) {
367     return false;
368   }
369 
370   // If no player is set, then also return false.
371   if (!this.options.file.player) {
372     return false;
373   }
374 
375   // Reset the error.
376   this.showError();
377 
378   // Only destroy if the current player is different than the new player.
379   var player = this.options.file.player.toString();
380 
381   // If there isn't media or if the players are different.
382   if (!this.media || (player !== this.currentPlayer)) {
383 
384     // Set the current media player.
385     this.currentPlayer = player;
386 
387     // Do nothing if we don't have a display.
388     if (!this.elements.display) {
389       this.showError('No media display found.');
390       return;
391     }
392 
393     // Destroy the current media.
394     var queue = {};
395     if (this.media) {
396       queue = this.media.queue;
397       this.media.destroy();
398     }
399 
400     // Get the class name and create the new player.
401     pClass = minplayer.players[this.options.file.player];
402 
403     // Create the new media player.
404     this.options.mediaelement = this.elements.media;
405     this.media = new pClass(this.elements.display, this.options, queue);
406 
407     // Now get the media when it is ready.
408     this.get('media', (function(player) {
409       return function(media) {
410 
411         // Load the media.
412         media.load(player.options.file);
413         player.display.addClass('minplayer-player-' + media.mediaFile.player);
414       };
415     })(this));
416 
417     // Return that a new player is loaded.
418     return true;
419   }
420   // If the media object already exists...
421   else if (this.media) {
422 
423     // Now load the different media file.
424     this.media.options = this.options;
425     this.display.removeClass('minplayer-player-' + this.media.mediaFile.player);
426     this.media.load(this.options.file);
427     this.display.addClass('minplayer-player-' + this.media.mediaFile.player);
428     return false;
429   }
430 };
431 
432 /**
433  * Load a set of files or a single file for the media player.
434  *
435  * @param {array} files An array of files to chose from to load.
436  */
437 minplayer.prototype.load = function(files) {
438 
439   // Set the id and class.
440   var id = '', pClass = '';
441 
442   // If no file was provided, then get it.
443   this.options.files = files || this.options.files;
444   this.options.file = minplayer.getMediaFile(this.options.files);
445 
446   // Now load the player.
447   if (this.loadPlayer()) {
448 
449     // Add the events since we now have a player.
450     this.bindTo(this.media);
451 
452     // If the player isn't valid, then show an error.
453     if (this.options.file.mimetype && !this.options.file.player) {
454       this.showError('Cannot play media: ' + this.options.file.mimetype);
455     }
456   }
457 };
458 
459 /**
460  * Called when the player is resized.
461  */
462 minplayer.prototype.resize = function() {
463 
464   // Call onRezie for each plugin.
465   this.get(function(plugin) {
466     if (plugin.onResize) {
467       plugin.onResize();
468     }
469   });
470 };
471