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