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