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 || $(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  * Define a way to debug.
 63  */
 64 minplayer.console = console || {log: function(data) {}};
 65 
 66 /**
 67  * @see minplayer.plugin.construct
 68  */
 69 minplayer.prototype.construct = function() {
 70 
 71   // Allow them to provide arguments based off of the DOM attributes.
 72   jQuery.each(this.context[0].attributes, (function(player) {
 73     return function(index, attr) {
 74       player.options[attr.name] = player.options[attr.name] || attr.value;
 75     };
 76   })(this));
 77 
 78   // Make sure we provide default options...
 79   this.options = jQuery.extend({
 80     id: 'player',
 81     build: false,
 82     wmode: 'transparent',
 83     preload: true,
 84     autoplay: false,
 85     loop: false,
 86     width: '100%',
 87     height: '350px',
 88     debug: false,
 89     volume: 80,
 90     files: [],
 91     file: '',
 92     preview: '',
 93     attributes: {}
 94   }, this.options);
 95 
 96   // Call the minplayer display constructor.
 97   minplayer.display.prototype.construct.call(this);
 98 
 99   /** The controller for this player. */
100   this.controller = this.create('controller');
101 
102   /** The play loader for this player. */
103   this.playLoader = this.create('playLoader');
104 
105   /** Variable to store the current media player. */
106   this.currentPlayer = 'html5';
107 
108   // Add key events to the window.
109   this.addKeyEvents();
110 
111   // Now load these files.
112   this.load(this.getFiles());
113 
114   // Add the player events.
115   this.addEvents();
116 
117   // The player is ready.
118   this.ready();
119 };
120 
121 /**
122  * We need to bind to events we are interested in.
123  */
124 minplayer.prototype.addEvents = function() {
125   minplayer.get.call(this, this.options.id, null, (function(player) {
126     return function(plugin) {
127 
128       // Bind to the error event.
129       plugin.bind('error', function(event, data) {
130 
131         // If an error occurs within the html5 media player, then try
132         // to fall back to the flash player.
133         if (player.currentPlayer == 'html5') {
134           player.options.file.player = 'minplayer';
135           player.loadPlayer();
136         }
137         else {
138           player.error(data);
139         }
140       });
141 
142       // Bind to the fullscreen event.
143       plugin.bind('fullscreen', function(event, data) {
144         player.resize();
145       });
146     };
147   })(this));
148 };
149 
150 /**
151  * Sets an error on the player.
152  *
153  * @param {string} error The error to display on the player.
154  */
155 minplayer.prototype.error = function(error) {
156   error = error || '';
157   if (this.elements.error) {
158 
159     // Set the error text.
160     this.elements.error.text(error);
161     if (error) {
162       this.elements.error.show();
163     }
164     else {
165       this.elements.error.hide();
166     }
167   }
168 };
169 
170 /**
171  * Adds key events to the player.
172  */
173 minplayer.prototype.addKeyEvents = function() {
174   jQuery(document).bind('keydown', (function(player) {
175     return function(event) {
176       switch (event.keyCode) {
177         case 113: // ESC
178         case 27:  // Q
179           if (player.isFullScreen()) {
180             player.fullscreen(false);
181           }
182           break;
183       }
184     };
185   })(this));
186 };
187 
188 /**
189  * Returns all the media files available for this player.
190  *
191  * @return {array} All the media files for this player.
192  */
193 minplayer.prototype.getFiles = function() {
194   var files = [];
195   var mediaSrc = null;
196 
197   // Get the files involved...
198   if (this.elements.media) {
199     mediaSrc = this.elements.media.attr('src');
200     if (mediaSrc) {
201       files.push({'path': mediaSrc});
202     }
203     jQuery('source', this.elements.media).each(function() {
204       files.push({
205         'path': jQuery(this).attr('src'),
206         'mimetype': jQuery(this).attr('type'),
207         'codecs': jQuery(this).attr('codecs')
208       });
209     });
210   }
211 
212   return files;
213 };
214 
215 /**
216  * Returns the full media player object.
217  * @param {array} files An array of files to chose from.
218  * @return {object} The best media file to play in the current browser.
219  */
220 minplayer.prototype.getMediaFile = function(files) {
221 
222   // If there are no files then return null.
223   if (!files) {
224     return null;
225   }
226 
227   // If the file is a single string, then return the file object.
228   if (typeof files === 'string') {
229     return new minplayer.file({'path': files});
230   }
231 
232   // If the file is already a file object then just return.
233   if (files.path || files.id) {
234     return new minplayer.file(files);
235   }
236 
237   // Add the files and get the best player to play.
238   var i = files.length, bestPriority = 0, mFile = null, file = null;
239   while (i--) {
240     file = files[i];
241 
242     // Get the minplayer file object.
243     if (typeof file === 'string') {
244       file = new minplayer.file({'path': file});
245     }
246     else {
247       file = new minplayer.file(file);
248     }
249 
250     // Determine the best file for this browser.
251     if (file.priority > bestPriority) {
252       mFile = file;
253     }
254   }
255 
256   // Return the best minplayer file.
257   return mFile;
258 };
259 
260 /**
261  * Loads a media player based on the current file.
262  */
263 minplayer.prototype.loadPlayer = function() {
264 
265   // Do nothing if there isn't a file.
266   if (!this.options.file) {
267     this.error('No media found.');
268     return;
269   }
270 
271   if (!this.options.file.player) {
272     this.error('Cannot play media: ' + this.options.file.mimetype);
273     return;
274   }
275 
276   // Reset the error.
277   this.error();
278 
279   // Only destroy if the current player is different than the new player.
280   var player = this.options.file.player.toString();
281 
282   // If there isn't media or if the players are different.
283   if (!this.media || (player !== this.currentPlayer)) {
284 
285     // Set the current media player.
286     this.currentPlayer = player;
287 
288     // Do nothing if we don't have a display.
289     if (!this.elements.display) {
290       this.error('No media display found.');
291       return;
292     }
293 
294     // Destroy the current media.
295     var queue = {};
296     if (this.media) {
297       queue = this.media.queue;
298       this.media.destroy();
299     }
300 
301     // Get the class name and create the new player.
302     pClass = minplayer.players[this.options.file.player];
303 
304     // Create the new media player.
305     this.options.mediaelement = this.elements.media;
306     this.media = new pClass(this.elements.display, this.options, queue);
307 
308     // Now get the media when it is ready.
309     this.get('media', (function(player) {
310       return function(media) {
311 
312         // Load the media.
313         media.load(player.options.file);
314       };
315     })(this));
316   }
317   // If the media object already exists...
318   else if (this.media) {
319 
320     // Now load the different media file.
321     this.media.load(this.options.file);
322   }
323 };
324 
325 /**
326  * Load a set of files or a single file for the media player.
327  *
328  * @param {array} files An array of files to chose from to load.
329  */
330 minplayer.prototype.load = function(files) {
331 
332   // Set the id and class.
333   var id = '', pClass = '';
334 
335   // If no file was provided, then get it.
336   this.options.files = files || this.options.files;
337   this.options.file = this.getMediaFile(this.options.files);
338 
339   // Now load the player.
340   this.loadPlayer();
341 };
342 
343 /**
344  * Called when the player is resized.
345  */
346 minplayer.prototype.resize = function() {
347 
348   // Call onRezie for each plugin.
349   this.get(function(plugin) {
350     if (plugin.onResize) {
351       plugin.onResize();
352     }
353   });
354 };
355