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 * @param {object} file A {@link minplayer.file} object. 44 * @return {number} The priority of this media player. 45 */ 46 minplayer.players.base.getPriority = function(file) { 47 return 0; 48 }; 49 50 /** 51 * Returns the ID for the media being played. 52 * 53 * @param {object} file A {@link minplayer.file} object. 54 * @return {string} The ID for the provided media. 55 */ 56 minplayer.players.base.getMediaId = function(file) { 57 return ''; 58 }; 59 60 /** 61 * Determine if we can play the media file. 62 * 63 * @param {object} file A {@link minplayer.file} object. 64 * @return {boolean} If this player can play this media type. 65 */ 66 minplayer.players.base.canPlay = function(file) { 67 return false; 68 }; 69 70 /** 71 * @see minplayer.plugin.construct 72 * @this minplayer.players.base 73 */ 74 minplayer.players.base.prototype.construct = function() { 75 76 // Call the media display constructor. 77 minplayer.display.prototype.construct.call(this); 78 79 // Set the plugin name within the options. 80 this.options.pluginName = 'basePlayer'; 81 82 /** The currently loaded media file. */ 83 this.mediaFile = this.options.file; 84 85 // Make sure we always autoplay on streams. 86 this.options.autoplay = this.options.autoplay || !!this.mediaFile.stream; 87 88 // Clear the media player. 89 this.clear(); 90 91 // Get the player display object. 92 if (!this.playerFound()) { 93 94 // Add the new player. 95 this.addPlayer(); 96 } 97 98 // Get the player object... 99 this.player = this.getPlayer(); 100 101 // Toggle playing if they click. 102 minplayer.click(this.display, (function(player) { 103 return function() { 104 if (player.playing) { 105 player.pause(); 106 } 107 else { 108 player.play(); 109 } 110 }; 111 })(this)); 112 113 // Bind to key events... 114 jQuery(document).bind('keydown', (function(player) { 115 return function(event) { 116 if (player.hasFocus) { 117 event.preventDefault(); 118 switch (event.keyCode) { 119 case 32: // SPACE 120 case 179: // GOOGLE play/pause button. 121 if (player.playing) { 122 player.pause(); 123 } 124 else { 125 player.play(); 126 } 127 break; 128 case 38: // UP 129 player.setVolumeRelative(0.1); 130 break; 131 case 40: // DOWN 132 player.setVolumeRelative(-0.1); 133 break; 134 case 37: // LEFT 135 case 227: // GOOGLE TV REW 136 player.seekRelative(-0.05); 137 break; 138 case 39: // RIGHT 139 case 228: // GOOGLE TV FW 140 player.seekRelative(0.05); 141 break; 142 } 143 } 144 }; 145 })(this)); 146 147 // Make sure that we trigger onReady if autoload is false. 148 if (!this.options.autoload) { 149 this.onReady(); 150 } 151 }; 152 153 /** 154 * Adds the media player. 155 */ 156 minplayer.players.base.prototype.addPlayer = function() { 157 158 // Remove the media element if found 159 if (this.elements.media) { 160 this.elements.media.remove(); 161 } 162 163 // Create a new media player element. 164 this.elements.media = jQuery(this.create()); 165 this.display.html(this.elements.media); 166 }; 167 168 /** 169 * @see minplayer.plugin.destroy. 170 */ 171 minplayer.players.base.prototype.destroy = function() { 172 minplayer.plugin.prototype.destroy.call(this); 173 this.clear(); 174 }; 175 176 /** 177 * Clears the media player. 178 */ 179 minplayer.players.base.prototype.clear = function() { 180 181 // Reset the ready flag. 182 this.playerReady = false; 183 184 // Reset the player. 185 this.reset(); 186 187 // If the player exists, then unbind all events. 188 if (this.player) { 189 jQuery(this.player).unbind(); 190 } 191 }; 192 193 /** 194 * Resets all variables. 195 */ 196 minplayer.players.base.prototype.reset = function() { 197 198 // The duration of the player. 199 this.duration = new minplayer.async(); 200 201 // The current play time of the player. 202 this.currentTime = new minplayer.async(); 203 204 // The amount of bytes loaded in the player. 205 this.bytesLoaded = new minplayer.async(); 206 207 // The total amount of bytes for the media. 208 this.bytesTotal = new minplayer.async(); 209 210 // The bytes that the download started with. 211 this.bytesStart = new minplayer.async(); 212 213 // The current volume of the player. 214 this.volume = new minplayer.async(); 215 216 // Reset focus. 217 this.hasFocus = false; 218 219 // We are not playing. 220 this.playing = false; 221 222 // We are not loading. 223 this.loading = false; 224 225 // Tell everyone else we reset. 226 this.trigger('pause'); 227 this.trigger('waiting'); 228 this.trigger('progress', {loaded: 0, total: 0, start: 0}); 229 this.trigger('timeupdate', {currentTime: 0, duration: 0}); 230 }; 231 232 /** 233 * Called when the player is ready to recieve events and commands. 234 */ 235 minplayer.players.base.prototype.onReady = function() { 236 237 // Only continue if we are not already ready. 238 if (this.playerReady) { 239 return; 240 } 241 242 // Set the ready flag. 243 this.playerReady = true; 244 245 // Set the volume to the default. 246 this.setVolume(this.options.volume / 100); 247 248 // Setup the progress interval. 249 this.loading = true; 250 251 // Create a poll to get the progress. 252 this.poll('progress', (function(player) { 253 return function() { 254 255 // Only do this if the play interval is set. 256 if (player.loading) { 257 258 // Get the bytes loaded asynchronously. 259 player.getBytesLoaded(function(bytesLoaded) { 260 261 // Get the bytes total asynchronously. 262 player.getBytesTotal(function(bytesTotal) { 263 264 // Trigger an event about the progress. 265 if (bytesLoaded || bytesTotal) { 266 267 // Get the bytes start, but don't require it. 268 var bytesStart = 0; 269 player.getBytesStart(function(val) { 270 bytesStart = val; 271 }); 272 273 // Trigger a progress event. 274 player.trigger('progress', { 275 loaded: bytesLoaded, 276 total: bytesTotal, 277 start: bytesStart 278 }); 279 280 // Say we are not longer loading if they are equal. 281 if (bytesLoaded >= bytesTotal) { 282 player.loading = false; 283 } 284 } 285 }); 286 }); 287 } 288 289 // Keep polling as long as its loading... 290 return player.loading; 291 }; 292 })(this), 1000); 293 294 // We are now ready. 295 this.ready(); 296 297 // Trigger that the load has started. 298 this.trigger('loadstart'); 299 }; 300 301 /** 302 * Returns the amount of seconds you would like to seek. 303 * 304 * @return {number} The number of seconds we should seek. 305 */ 306 minplayer.players.base.prototype.getSeek = function() { 307 var seconds = 0, minutes = 0, hours = 0; 308 309 // See if they would like to seek. 310 if (minplayer.urlVars && minplayer.urlVars.seek) { 311 312 // Get the seconds. 313 seconds = minplayer.urlVars.seek.match(/([0-9])s/i); 314 if (seconds) { 315 seconds = parseInt(seconds[1], 10); 316 } 317 318 // Get the minutes. 319 minutes = minplayer.urlVars.seek.match(/([0-9])m/i); 320 if (minutes) { 321 seconds += (parseInt(minutes[1], 10) * 60); 322 } 323 324 // Get the hours. 325 hours = minplayer.urlVars.seek.match(/([0-9])h/i); 326 if (hours) { 327 seconds += (parseInt(hours[1], 10) * 3600); 328 } 329 330 // If no seconds were found, then just use the raw value. 331 if (!seconds) { 332 seconds = minplayer.urlVars.seek; 333 } 334 } 335 336 return seconds; 337 }; 338 339 /** 340 * Should be called when the media is playing. 341 */ 342 minplayer.players.base.prototype.onPlaying = function() { 343 344 // Trigger an event that we are playing. 345 this.trigger('playing'); 346 347 // Say that this player has focus. 348 this.hasFocus = true; 349 350 // Set the playInterval to true. 351 this.playing = true; 352 353 // Create a poll to get the timeupate. 354 this.poll('timeupdate', (function(player) { 355 return function() { 356 357 // Only do this if the play interval is set. 358 if (player.playing) { 359 360 // Get the current time asyncrhonously. 361 player.getCurrentTime(function(currentTime) { 362 363 // Get the duration asynchronously. 364 player.getDuration(function(duration) { 365 366 // Convert these to floats. 367 currentTime = parseFloat(currentTime); 368 duration = parseFloat(duration); 369 370 // Trigger an event about the progress. 371 if (currentTime || duration) { 372 373 // Trigger an update event. 374 player.trigger('timeupdate', { 375 currentTime: currentTime, 376 duration: duration 377 }); 378 } 379 }); 380 }); 381 } 382 383 // Keep polling as long as it is playing. 384 return player.playing; 385 }; 386 })(this), 500); 387 }; 388 389 /** 390 * Should be called when the media is paused. 391 */ 392 minplayer.players.base.prototype.onPaused = function() { 393 394 // Trigger an event that we are paused. 395 this.trigger('pause'); 396 397 // Remove focus. 398 this.hasFocus = false; 399 400 // Say we are not playing. 401 this.playing = false; 402 }; 403 404 /** 405 * Should be called when the media is complete. 406 */ 407 minplayer.players.base.prototype.onComplete = function() { 408 if (this.playing) { 409 this.onPaused(); 410 } 411 412 // Stop the intervals. 413 this.playing = false; 414 this.loading = false; 415 this.hasFocus = false; 416 this.trigger('ended'); 417 }; 418 419 /** 420 * Should be called when the media is done loading. 421 */ 422 minplayer.players.base.prototype.onLoaded = function() { 423 424 // If we should autoplay, then just play now. 425 if (this.options.autoplay) { 426 this.play(); 427 } 428 429 this.trigger('loadeddata'); 430 431 // See if they would like to seek. 432 var seek = this.getSeek(); 433 if (seek) { 434 this.getDuration((function(player) { 435 return function(duration) { 436 if (seek < duration) { 437 player.seek(seek); 438 player.play(); 439 } 440 }; 441 })(this)); 442 } 443 }; 444 445 /** 446 * Should be called when the player is waiting. 447 */ 448 minplayer.players.base.prototype.onWaiting = function() { 449 this.trigger('waiting'); 450 }; 451 452 /** 453 * Called when an error occurs. 454 * 455 * @param {string} errorCode The error that was triggered. 456 */ 457 minplayer.players.base.prototype.onError = function(errorCode) { 458 this.hasFocus = false; 459 this.trigger('error', errorCode); 460 }; 461 462 /** 463 * @see minplayer.players.base#isReady 464 * @return {boolean} Checks to see if the Flash is ready. 465 */ 466 minplayer.players.base.prototype.isReady = function() { 467 468 // Return that the player is set and the ready flag is good. 469 return (this.player && this.playerReady); 470 }; 471 472 /** 473 * Determines if the player should show the playloader. 474 * 475 * @param {string} preview The preview image. 476 * @return {bool} If this player implements its own playLoader. 477 */ 478 minplayer.players.base.prototype.hasPlayLoader = function(preview) { 479 return false; 480 }; 481 482 /** 483 * Determines if the player should show the controller. 484 * 485 * @return {bool} If this player implements its own controller. 486 */ 487 minplayer.players.base.prototype.hasController = function() { 488 return false; 489 }; 490 491 /** 492 * Returns if the media player is already within the DOM. 493 * 494 * @return {boolean} TRUE - if the player is in the DOM, FALSE otherwise. 495 */ 496 minplayer.players.base.prototype.playerFound = function() { 497 return false; 498 }; 499 500 /** 501 * Creates the media player and inserts it in the DOM. 502 * 503 * @return {object} The media player entity. 504 */ 505 minplayer.players.base.prototype.create = function() { 506 this.reset(); 507 return null; 508 }; 509 510 /** 511 * Returns the media player object. 512 * 513 * @return {object} The media player object. 514 */ 515 minplayer.players.base.prototype.getPlayer = function() { 516 return this.player; 517 }; 518 519 /** 520 * Loads a new media player. 521 * 522 * @param {object} file A {@link minplayer.file} object. 523 * @return {boolean} If this action was performed. 524 */ 525 minplayer.players.base.prototype.load = function(file) { 526 527 // Store the media file for future lookup. 528 var isString = (typeof this.mediaFile == 'string'); 529 var path = isString ? this.mediaFile : this.mediaFile.path; 530 if (file && this.isReady() && (file.path != path)) { 531 this.reset(); 532 this.mediaFile = file; 533 return true; 534 } 535 536 return false; 537 }; 538 539 /** 540 * Play the loaded media file. 541 * @return {boolean} If this action was performed. 542 */ 543 minplayer.players.base.prototype.play = function() { 544 this.options.autoload = true; 545 this.options.autoplay = true; 546 return this.isReady(); 547 }; 548 549 /** 550 * Pause the loaded media file. 551 * @return {boolean} If this action was performed. 552 */ 553 minplayer.players.base.prototype.pause = function() { 554 return this.isReady(); 555 }; 556 557 /** 558 * Stop the loaded media file. 559 * @return {boolean} If this action was performed. 560 */ 561 minplayer.players.base.prototype.stop = function() { 562 this.playing = false; 563 this.loading = false; 564 this.hasFocus = false; 565 return this.isReady(); 566 }; 567 568 /** 569 * Seeks to relative position. 570 * 571 * @param {number} pos Relative position. -1 to 1 (percent), > 1 (seconds). 572 */ 573 minplayer.players.base.prototype.seekRelative = function(pos) { 574 575 // Get the current time asyncrhonously. 576 this.getCurrentTime((function(player) { 577 return function(currentTime) { 578 579 // Get the duration asynchronously. 580 player.getDuration(function(duration) { 581 582 // Only do this if we have a duration. 583 if (duration) { 584 585 // Get the position. 586 var seekPos = 0; 587 if ((pos > -1) && (pos < 1)) { 588 seekPos = ((currentTime / duration) + parseFloat(pos)) * duration; 589 } 590 else { 591 seekPos = (currentTime + parseFloat(pos)); 592 } 593 594 // Set the seek value. 595 player.seek(seekPos); 596 } 597 }); 598 }; 599 })(this)); 600 }; 601 602 /** 603 * Seek the loaded media. 604 * 605 * @param {number} pos The position to seek the minplayer. 0 to 1. 606 * @return {boolean} If this action was performed. 607 */ 608 minplayer.players.base.prototype.seek = function(pos) { 609 return this.isReady(); 610 }; 611 612 /** 613 * Gets a value from the player. 614 * 615 * @param {string} getter The getter method on the player. 616 * @param {function} callback The callback function. 617 */ 618 minplayer.players.base.prototype.getValue = function(getter, callback) { 619 if (this.isReady()) { 620 var value = this.player[getter](); 621 if ((value !== undefined) && (value !== null)) { 622 callback(value); 623 } 624 } 625 }; 626 627 /** 628 * Set the volume of the loaded minplayer. 629 * 630 * @param {number} vol -1 to 1 - The relative amount to increase or decrease. 631 */ 632 minplayer.players.base.prototype.setVolumeRelative = function(vol) { 633 634 // Get the volume 635 this.getVolume((function(player) { 636 return function(newVol) { 637 newVol += parseFloat(vol); 638 newVol = (newVol < 0) ? 0 : newVol; 639 newVol = (newVol > 1) ? 1 : newVol; 640 player.setVolume(newVol); 641 }; 642 })(this)); 643 }; 644 645 /** 646 * Set the volume of the loaded minplayer. 647 * 648 * @param {number} vol The volume to set the media. 0 to 1. 649 * @return {boolean} If this action was performed. 650 */ 651 minplayer.players.base.prototype.setVolume = function(vol) { 652 this.trigger('volumeupdate', vol); 653 return this.isReady(); 654 }; 655 656 /** 657 * Get the volume from the loaded media. 658 * 659 * @param {function} callback Called when the volume is determined. 660 * @return {number} The volume of the media; 0 to 1. 661 */ 662 minplayer.players.base.prototype.getVolume = function(callback) { 663 return this.volume.get(callback); 664 }; 665 666 /** 667 * Get the current time for the media being played. 668 * 669 * @param {function} callback Called when the time is determined. 670 * @return {number} The volume of the media; 0 to 1. 671 */ 672 minplayer.players.base.prototype.getCurrentTime = function(callback) { 673 return this.currentTime.get(callback); 674 }; 675 676 /** 677 * Return the duration of the loaded media. 678 * 679 * @param {function} callback Called when the duration is determined. 680 * @return {number} The duration of the loaded media. 681 */ 682 minplayer.players.base.prototype.getDuration = function(callback) { 683 return this.duration.get(callback); 684 }; 685 686 /** 687 * Return the start bytes for the loaded media. 688 * 689 * @param {function} callback Called when the start bytes is determined. 690 * @return {int} The bytes that were started. 691 */ 692 minplayer.players.base.prototype.getBytesStart = function(callback) { 693 return this.bytesStart.get(callback); 694 }; 695 696 /** 697 * Return the bytes of media loaded. 698 * 699 * @param {function} callback Called when the bytes loaded is determined. 700 * @return {int} The amount of bytes loaded. 701 */ 702 minplayer.players.base.prototype.getBytesLoaded = function(callback) { 703 return this.bytesLoaded.get(callback); 704 }; 705 706 /** 707 * Return the total amount of bytes. 708 * 709 * @param {function} callback Called when the bytes total is determined. 710 * @return {int} The total amount of bytes for this media. 711 */ 712 minplayer.players.base.prototype.getBytesTotal = function(callback) { 713 return this.bytesTotal.get(callback); 714 }; 715