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