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