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.bind('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.bind('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 // Set the focus when they enter the player. 201 this.display.bind('mouseenter', (function(player) { 202 return function() { 203 player.setFocus(true); 204 }; 205 })(this)); 206 207 this.display.bind('mouseleave', (function(player) { 208 return function() { 209 player.setFocus(false); 210 }; 211 })(this)); 212 213 var moveThrottle = false; 214 this.display.bind('mousemove', (function(player) { 215 return function() { 216 if (!moveThrottle) { 217 moveThrottle = setTimeout(function() { 218 moveThrottle = false; 219 player.setFocus(true); 220 }, 300); 221 } 222 }; 223 })(this)); 224 225 minplayer.get.call(this, this.options.id, null, (function(player) { 226 return function(plugin) { 227 player.bindTo(plugin); 228 }; 229 })(this)); 230 }; 231 232 /** 233 * Sets an error on the player. 234 * 235 * @param {string} error The error to display on the player. 236 */ 237 minplayer.prototype.showError = function(error) { 238 if (typeof error !== 'object') { 239 error = error || ''; 240 if (this.elements.error) { 241 242 // Set the error text. 243 this.elements.error.text(error); 244 if (error) { 245 // Show the error message. 246 this.elements.error.show(); 247 248 // Only show this error for a time interval. 249 setTimeout((function(player) { 250 return function() { 251 player.elements.error.hide('slow'); 252 }; 253 })(this), 5000); 254 } 255 else { 256 this.elements.error.hide(); 257 } 258 } 259 } 260 }; 261 262 /** 263 * Adds key events to the player. 264 */ 265 minplayer.prototype.addKeyEvents = function() { 266 jQuery(document).bind('keydown', (function(player) { 267 return function(event) { 268 switch (event.keyCode) { 269 case 113: // ESC 270 case 27: // Q 271 if (player.isFullScreen()) { 272 player.fullscreen(false); 273 } 274 break; 275 } 276 }; 277 })(this)); 278 }; 279 280 /** 281 * Returns all the media files available for this player. 282 * 283 * @return {array} All the media files for this player. 284 */ 285 minplayer.prototype.getFiles = function() { 286 287 // If they provide the files in the options, use those first. 288 if (this.options.files) { 289 return this.options.files; 290 } 291 292 if (this.options.file) { 293 return this.options.file; 294 } 295 296 var files = []; 297 var mediaSrc = null; 298 299 // Get the files involved... 300 if (this.elements.media) { 301 mediaSrc = this.elements.media.attr('src'); 302 if (mediaSrc) { 303 files.push({'path': mediaSrc}); 304 } 305 jQuery('source', this.elements.media).each(function() { 306 files.push({ 307 'path': jQuery(this).attr('src'), 308 'mimetype': jQuery(this).attr('type'), 309 'codecs': jQuery(this).attr('codecs') 310 }); 311 }); 312 } 313 314 return files; 315 }; 316 317 /** 318 * Returns the full media player object. 319 * 320 * @param {array} files An array of files to chose from. 321 * @return {object} The best media file to play in the current browser. 322 */ 323 minplayer.getMediaFile = function(files) { 324 325 // If there are no files then return null. 326 if (!files) { 327 return null; 328 } 329 330 // If the file is already a file object then just return. 331 if ((typeof files === 'string') || files.path || files.id) { 332 return new minplayer.file(files); 333 } 334 335 // Add the files and get the best player to play. 336 var bestPriority = 0, mFile = null, file = null; 337 for (var i in files) { 338 if (files.hasOwnProperty(i)) { 339 file = new minplayer.file(files[i]); 340 if (file.player && (file.priority > bestPriority)) { 341 mFile = file; 342 } 343 } 344 } 345 346 // Return the best minplayer file. 347 return mFile; 348 }; 349 350 /** 351 * Loads a media player based on the current file. 352 * 353 * @return {boolean} If a new player was loaded. 354 */ 355 minplayer.prototype.loadPlayer = function() { 356 357 // Do nothing if there isn't a file or anywhere to put it. 358 if (!this.options.file || (this.elements.display.length == 0)) { 359 return false; 360 } 361 362 // If no player is set, then also return false. 363 if (!this.options.file.player) { 364 return false; 365 } 366 367 // Reset the error. 368 this.showError(); 369 370 // Only destroy if the current player is different than the new player. 371 var player = this.options.file.player.toString(); 372 373 // If there isn't media or if the players are different. 374 if (!this.media || (player !== this.currentPlayer)) { 375 376 // Set the current media player. 377 this.currentPlayer = player; 378 379 // Do nothing if we don't have a display. 380 if (!this.elements.display) { 381 this.showError('No media display found.'); 382 return; 383 } 384 385 // Destroy the current media. 386 var queue = {}; 387 if (this.media) { 388 queue = this.media.queue; 389 this.media.destroy(); 390 } 391 392 // Get the class name and create the new player. 393 pClass = minplayer.players[this.options.file.player]; 394 395 // Create the new media player. 396 this.options.mediaelement = this.elements.media; 397 this.media = new pClass(this.elements.display, this.options, queue); 398 399 // Now get the media when it is ready. 400 this.get('media', (function(player) { 401 return function(media) { 402 403 // Load the media. 404 media.load(player.options.file); 405 player.display.addClass('minplayer-player-' + media.mediaFile.player); 406 }; 407 })(this)); 408 409 // Return that a new player is loaded. 410 return true; 411 } 412 // If the media object already exists... 413 else if (this.media) { 414 415 // Now load the different media file. 416 this.media.options = this.options; 417 this.display.removeClass('minplayer-player-' + this.media.mediaFile.player); 418 this.media.load(this.options.file); 419 this.display.addClass('minplayer-player-' + this.media.mediaFile.player); 420 return false; 421 } 422 }; 423 424 /** 425 * Load a set of files or a single file for the media player. 426 * 427 * @param {array} files An array of files to chose from to load. 428 */ 429 minplayer.prototype.load = function(files) { 430 431 // Set the id and class. 432 var id = '', pClass = ''; 433 434 // If no file was provided, then get it. 435 this.options.files = files || this.options.files; 436 this.options.file = minplayer.getMediaFile(this.options.files); 437 438 // Now load the player. 439 if (this.loadPlayer()) { 440 441 // Add the events since we now have a player. 442 this.bindTo(this.media); 443 444 // If the player isn't valid, then show an error. 445 if (this.options.file.mimetype && !this.options.file.player) { 446 this.showError('Cannot play media: ' + this.options.file.mimetype); 447 } 448 } 449 }; 450 451 /** 452 * Called when the player is resized. 453 */ 454 minplayer.prototype.resize = function() { 455 456 // Call onRezie for each plugin. 457 this.get(function(plugin) { 458 if (plugin.onResize) { 459 plugin.onResize(); 460 } 461 }); 462 }; 463