1 /** The minplayer namespace. */ 2 var minplayer = minplayer || {}; 3 4 /** All the media player implementations */ 5 minplayer.players = minplayer.players || {}; 6 7 /** 8 * @constructor 9 * @extends minplayer.display 10 * @class The base media player class where all media players derive from. 11 * 12 * @param {object} context The jQuery context. 13 * @param {object} options This components options. 14 * @param {object} queue The event queue to pass events around. 15 */ 16 minplayer.players.base = function(context, options, queue) { 17 18 // Derive from display 19 minplayer.display.call(this, 'media', context, options, queue); 20 }; 21 22 /** Derive from minplayer.display. */ 23 minplayer.players.base.prototype = new minplayer.display(); 24 25 /** Reset the constructor. */ 26 minplayer.players.base.prototype.constructor = minplayer.players.base; 27 28 /** 29 * @see minplayer.display.getElements 30 * @this minplayer.players.base 31 * @return {object} The elements for this display. 32 */ 33 minplayer.players.base.prototype.getElements = function() { 34 var elements = minplayer.display.prototype.getElements.call(this); 35 return jQuery.extend(elements, { 36 media: this.options.mediaelement 37 }); 38 }; 39 40 /** 41 * Get the priority of this media player. 42 * 43 * @return {number} The priority of this media player. 44 */ 45 minplayer.players.base.getPriority = function() { 46 return 0; 47 }; 48 49 /** 50 * Returns the ID for the media being played. 51 * 52 * @param {object} file A {@link minplayer.file} object. 53 * @return {string} The ID for the provided media. 54 */ 55 minplayer.players.base.getMediaId = function(file) { 56 return ''; 57 }; 58 59 /** 60 * Determine if we can play the media file. 61 * 62 * @param {object} file A {@link minplayer.file} object. 63 * @return {boolean} If this player can play this media type. 64 */ 65 minplayer.players.base.canPlay = function(file) { 66 return false; 67 }; 68 69 /** 70 * @see minplayer.plugin.construct 71 * @this minplayer.players.base 72 */ 73 minplayer.players.base.prototype.construct = function() { 74 75 // Call the media display constructor. 76 minplayer.display.prototype.construct.call(this); 77 78 // Clear the media player. 79 this.clear(); 80 81 /** The currently loaded media file. */ 82 this.mediaFile = this.options.file; 83 84 // Get the player display object. 85 if (!this.playerFound()) { 86 87 // Remove the media element if found 88 if (this.elements.media) { 89 this.elements.media.remove(); 90 } 91 92 // Create a new media player element. 93 this.elements.media = jQuery(this.create()); 94 this.display.html(this.elements.media); 95 } 96 97 // Get the player object... 98 this.player = this.getPlayer(); 99 100 // Set the focus of the element based on if they click in or outside of it. 101 jQuery(document).bind('click', (function(player) { 102 return function(event) { 103 if (jQuery(event.target).closest('#' + player.options.id).length == 0) { 104 player.hasFocus = false; 105 } 106 else { 107 player.hasFocus = true; 108 } 109 }; 110 })(this)); 111 112 // Bind to key events... 113 jQuery(document).bind('keydown', (function(player) { 114 return function(event) { 115 if (player.hasFocus) { 116 event.preventDefault(); 117 switch (event.keyCode) { 118 case 32: // SPACE 119 case 179: // GOOGLE play/pause button. 120 if (player.playing) { 121 player.pause(); 122 } 123 else { 124 player.play(); 125 } 126 break; 127 case 38: // UP 128 player.setVolumeRelative(0.1); 129 break; 130 case 40: // DOWN 131 player.setVolumeRelative(-0.1); 132 break; 133 case 37: // LEFT 134 case 227: // GOOGLE TV REW 135 player.seekRelative(-0.05); 136 break; 137 case 39: // RIGHT 138 case 228: // GOOGLE TV FW 139 player.seekRelative(0.05); 140 break; 141 } 142 } 143 }; 144 })(this)); 145 }; 146 147 /** 148 * @see minplayer.plugin.destroy. 149 */ 150 minplayer.players.base.prototype.destroy = function() { 151 minplayer.plugin.prototype.destroy.call(this); 152 this.clear(); 153 }; 154 155 /** 156 * Clears the media player. 157 */ 158 minplayer.players.base.prototype.clear = function() { 159 160 // Reset the ready flag. 161 this.playerReady = false; 162 163 // Reset the player. 164 this.reset(); 165 166 // If the player exists, then unbind all events. 167 if (this.player) { 168 jQuery(this.player).unbind(); 169 } 170 }; 171 172 /** 173 * Resets all variables. 174 */ 175 minplayer.players.base.prototype.reset = function() { 176 177 // The duration of the player. 178 this.duration = new minplayer.async(); 179 180 // The current play time of the player. 181 this.currentTime = new minplayer.async(); 182 183 // The amount of bytes loaded in the player. 184 this.bytesLoaded = new minplayer.async(); 185 186 // The total amount of bytes for the media. 187 this.bytesTotal = new minplayer.async(); 188 189 // The bytes that the download started with. 190 this.bytesStart = new minplayer.async(); 191 192 // The current volume of the player. 193 this.volume = new minplayer.async(); 194 195 // Reset focus. 196 this.hasFocus = false; 197 198 // We are not playing. 199 this.playing = false; 200 201 // We are not loading. 202 this.loading = false; 203 204 // Tell everyone else we reset. 205 this.trigger('pause'); 206 this.trigger('waiting'); 207 this.trigger('progress', {loaded: 0, total: 0, start: 0}); 208 this.trigger('timeupdate', {currentTime: 0, duration: 0}); 209 }; 210 211 /** 212 * Called when the player is ready to recieve events and commands. 213 */ 214 minplayer.players.base.prototype.onReady = function() { 215 216 // Only continue if we are not already ready. 217 if (this.playerReady) { 218 return; 219 } 220 221 // Set the ready flag. 222 this.playerReady = true; 223 224 // Set the volume to the default. 225 this.setVolume(this.options.volume / 100); 226 227 // Setup the progress interval. 228 this.loading = true; 229 230 // Create a poll to get the progress. 231 this.poll((function(player) { 232 return function() { 233 234 // Only do this if the play interval is set. 235 if (player.loading) { 236 237 // Get the bytes loaded asynchronously. 238 player.getBytesLoaded(function(bytesLoaded) { 239 240 // Get the bytes total asynchronously. 241 player.getBytesTotal(function(bytesTotal) { 242 243 // Trigger an event about the progress. 244 if (bytesLoaded || bytesTotal) { 245 246 // Get the bytes start, but don't require it. 247 var bytesStart = 0; 248 player.getBytesStart(function(val) { 249 bytesStart = val; 250 }); 251 252 // Trigger a progress event. 253 player.trigger('progress', { 254 loaded: bytesLoaded, 255 total: bytesTotal, 256 start: bytesStart 257 }); 258 259 // Say we are not longer loading if they are equal. 260 if (bytesLoaded >= bytesTotal) { 261 player.loading = false; 262 } 263 } 264 }); 265 }); 266 } 267 268 // Keep polling as long as its loading... 269 return player.loading; 270 }; 271 })(this), 1000); 272 273 // We are now ready. 274 this.ready(); 275 276 // Trigger that the load has started. 277 this.trigger('loadstart'); 278 }; 279 280 /** 281 * Should be called when the media is playing. 282 */ 283 minplayer.players.base.prototype.onPlaying = function() { 284 285 // Trigger an event that we are playing. 286 this.trigger('playing'); 287 288 // Say that this player has focus. 289 this.hasFocus = true; 290 291 // Set the playInterval to true. 292 this.playing = true; 293 294 // Create a poll to get the timeupate. 295 this.poll((function(player) { 296 return function() { 297 298 // Only do this if the play interval is set. 299 if (player.playing) { 300 301 // Get the current time asyncrhonously. 302 player.getCurrentTime(function(currentTime) { 303 304 // Get the duration asynchronously. 305 player.getDuration(function(duration) { 306 307 // Convert these to floats. 308 currentTime = parseFloat(currentTime); 309 duration = parseFloat(duration); 310 311 // Trigger an event about the progress. 312 if (currentTime || duration) { 313 314 // Trigger an update event. 315 player.trigger('timeupdate', { 316 currentTime: currentTime, 317 duration: duration 318 }); 319 } 320 }); 321 }); 322 } 323 324 // Keep polling as long as it is playing. 325 return player.playing; 326 }; 327 })(this), 1000); 328 }; 329 330 /** 331 * Should be called when the media is paused. 332 */ 333 minplayer.players.base.prototype.onPaused = function() { 334 335 // Trigger an event that we are paused. 336 this.trigger('pause'); 337 338 // Remove focus. 339 this.hasFocus = false; 340 341 // Say we are not playing. 342 this.playing = false; 343 }; 344 345 /** 346 * Should be called when the media is complete. 347 */ 348 minplayer.players.base.prototype.onComplete = function() { 349 // Stop the intervals. 350 this.playing = false; 351 this.loading = false; 352 this.hasFocus = false; 353 this.trigger('ended'); 354 }; 355 356 /** 357 * Should be called when the media is done loading. 358 */ 359 minplayer.players.base.prototype.onLoaded = function() { 360 361 // If we should autoplay, then just play now. 362 if (this.options.autoplay) { 363 this.play(); 364 } 365 366 this.trigger('loadeddata'); 367 }; 368 369 /** 370 * Should be called when the player is waiting. 371 */ 372 minplayer.players.base.prototype.onWaiting = function() { 373 this.trigger('waiting'); 374 }; 375 376 /** 377 * Called when an error occurs. 378 * 379 * @param {string} errorCode The error that was triggered. 380 */ 381 minplayer.players.base.prototype.onError = function(errorCode) { 382 this.hasFocus = false; 383 this.trigger('error', errorCode); 384 }; 385 386 /** 387 * @see minplayer.players.base#isReady 388 * @return {boolean} Checks to see if the Flash is ready. 389 */ 390 minplayer.players.base.prototype.isReady = function() { 391 392 // Return that the player is set and the ready flag is good. 393 return (this.player && this.playerReady); 394 }; 395 396 /** 397 * Determines if the player should show the playloader. 398 * 399 * @return {bool} If this player implements its own playLoader. 400 */ 401 minplayer.players.base.prototype.hasPlayLoader = function() { 402 return false; 403 }; 404 405 /** 406 * Returns if the media player is already within the DOM. 407 * 408 * @return {boolean} TRUE - if the player is in the DOM, FALSE otherwise. 409 */ 410 minplayer.players.base.prototype.playerFound = function() { 411 return false; 412 }; 413 414 /** 415 * Creates the media player and inserts it in the DOM. 416 * 417 * @return {object} The media player entity. 418 */ 419 minplayer.players.base.prototype.create = function() { 420 this.reset(); 421 return null; 422 }; 423 424 /** 425 * Returns the media player object. 426 * 427 * @return {object} The media player object. 428 */ 429 minplayer.players.base.prototype.getPlayer = function() { 430 return this.player; 431 }; 432 433 /** 434 * Loads a new media player. 435 * 436 * @param {object} file A {@link minplayer.file} object. 437 */ 438 minplayer.players.base.prototype.load = function(file) { 439 440 // Store the media file for future lookup. 441 var isString = (typeof this.mediaFile == 'string'); 442 var path = isString ? this.mediaFile : this.mediaFile.path; 443 if (file && (file.path != path)) { 444 this.reset(); 445 this.mediaFile = file; 446 } 447 }; 448 449 /** 450 * Play the loaded media file. 451 */ 452 minplayer.players.base.prototype.play = function() { 453 }; 454 455 /** 456 * Pause the loaded media file. 457 */ 458 minplayer.players.base.prototype.pause = function() { 459 }; 460 461 /** 462 * Stop the loaded media file. 463 */ 464 minplayer.players.base.prototype.stop = function() { 465 this.playing = false; 466 this.loading = false; 467 this.hasFocus = false; 468 }; 469 470 /** 471 * Seeks to relative position. 472 * 473 * @param {number} pos Relative position. -1 to 1 (percent), > 1 (seconds). 474 */ 475 minplayer.players.base.prototype.seekRelative = function(pos) { 476 477 // Get the current time asyncrhonously. 478 this.getCurrentTime((function(player) { 479 return function(currentTime) { 480 481 // Get the duration asynchronously. 482 player.getDuration(function(duration) { 483 484 // Only do this if we have a duration. 485 if (duration) { 486 487 // Get the position. 488 var seekPos = 0; 489 if ((pos > -1) && (pos < 1)) { 490 seekPos = (currentTime / duration) + parseFloat(pos); 491 } 492 else { 493 seekPos = (currentTime + parseFloat(pos)) / duration; 494 } 495 496 // Set the seek value. 497 player.seek(seekPos); 498 } 499 }); 500 }; 501 })(this)); 502 }; 503 504 /** 505 * Seek the loaded media. 506 * 507 * @param {number} pos The position to seek the minplayer. 0 to 1. 508 */ 509 minplayer.players.base.prototype.seek = function(pos) { 510 }; 511 512 /** 513 * Set the volume of the loaded minplayer. 514 * 515 * @param {number} vol -1 to 1 - The relative amount to increase or decrease. 516 */ 517 minplayer.players.base.prototype.setVolumeRelative = function(vol) { 518 519 // Get the volume 520 this.getVolume((function(player) { 521 return function(newVol) { 522 newVol += parseFloat(vol); 523 newVol = (newVol < 0) ? 0 : newVol; 524 newVol = (newVol > 1) ? 1 : newVol; 525 player.setVolume(newVol); 526 }; 527 })(this)); 528 }; 529 530 /** 531 * Set the volume of the loaded minplayer. 532 * 533 * @param {number} vol The volume to set the media. 0 to 1. 534 */ 535 minplayer.players.base.prototype.setVolume = function(vol) { 536 this.trigger('volumeupdate', vol); 537 }; 538 539 /** 540 * Get the volume from the loaded media. 541 * 542 * @param {function} callback Called when the volume is determined. 543 * @return {number} The volume of the media; 0 to 1. 544 */ 545 minplayer.players.base.prototype.getVolume = function(callback) { 546 return this.volume.get(callback); 547 }; 548 549 /** 550 * Get the current time for the media being played. 551 * 552 * @param {function} callback Called when the time is determined. 553 * @return {number} The volume of the media; 0 to 1. 554 */ 555 minplayer.players.base.prototype.getCurrentTime = function(callback) { 556 return this.currentTime.get(callback); 557 }; 558 559 /** 560 * Return the duration of the loaded media. 561 * 562 * @param {function} callback Called when the duration is determined. 563 * @return {number} The duration of the loaded media. 564 */ 565 minplayer.players.base.prototype.getDuration = function(callback) { 566 return this.duration.get(callback); 567 }; 568 569 /** 570 * Return the start bytes for the loaded media. 571 * 572 * @param {function} callback Called when the start bytes is determined. 573 * @return {int} The bytes that were started. 574 */ 575 minplayer.players.base.prototype.getBytesStart = function(callback) { 576 return this.bytesStart.get(callback); 577 }; 578 579 /** 580 * Return the bytes of media loaded. 581 * 582 * @param {function} callback Called when the bytes loaded is determined. 583 * @return {int} The amount of bytes loaded. 584 */ 585 minplayer.players.base.prototype.getBytesLoaded = function(callback) { 586 return this.bytesLoaded.get(callback); 587 }; 588 589 /** 590 * Return the total amount of bytes. 591 * 592 * @param {function} callback Called when the bytes total is determined. 593 * @return {int} The total amount of bytes for this media. 594 */ 595 minplayer.players.base.prototype.getBytesTotal = function(callback) { 596 return this.bytesTotal.get(callback); 597 }; 598