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