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