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