(function ($) { AblePlayer.prototype.updateCaption = function (time) { if (!this.usingYouTubeCaptions && (typeof this.$captionsWrapper !== 'undefined')) { if (this.captionsOn) { this.$captionsWrapper.show(); this.showCaptions(time || this.getElapsed()); } else if (this.$captionsWrapper) { this.$captionsWrapper.hide(); this.prefCaptions = 0; } } }; // Returns the function used when a caption is clicked in the captions menu. // Not called if user clicks "Captions off". Instead, that triggers getCaptionOffFunction() AblePlayer.prototype.getCaptionClickFunction = function (track) { var thisObj = this; return function () { thisObj.selectedCaptions = track; thisObj.captionLang = track.language; thisObj.currentCaption = -1; if (thisObj.usingYouTubeCaptions) { if (thisObj.captionsOn) { if (typeof thisObj.ytCaptionModule !== 'undefined') { // captions are already on. Just need to change the language thisObj.youTubePlayer.setOption(thisObj.ytCaptionModule, 'track', {'languageCode': thisObj.captionLang}); } else { // need to wait for caption module to be loaded to change the language // caption module will be loaded after video starts playing, triggered by onApiChange event // at that point, thosObj.captionLang will be passed to the module as the default language } } else { // captions are off (i.e., captions module has been unloaded; need to reload it) // user's selected language will be reset after module has successfully loaded // (the onApiChange event will be fired -- see initialize.js > initYouTubePlayer()) thisObj.resettingYouTubeCaptions = true; thisObj.youTubePlayer.loadModule(thisObj.ytCaptionModule); } } else { thisObj.syncTrackLanguages('captions',thisObj.captionLang); if (!this.swappingSrc) { thisObj.updateCaption(); thisObj.showDescription(thisObj.getElapsed()); } } thisObj.captionsOn = true; // stopgap to prevent spacebar in Firefox from reopening popup // immediately after closing it (used in handleCaptionToggle()) thisObj.hidingPopup = true; thisObj.captionsPopup.hide(); // Ensure stopgap gets cancelled if handleCaptionToggle() isn't called // e.g., if user triggered button with Enter or mouse click, not spacebar setTimeout(function() { thisObj.hidingPopup = false; }, 100); thisObj.$ccButton.focus(); // save preference to cookie thisObj.prefCaptions = 1; thisObj.updateCookie('prefCaptions'); thisObj.refreshControls(); } }; // Returns the function used when the "Captions Off" button is clicked in the captions tooltip. AblePlayer.prototype.getCaptionOffFunction = function () { var thisObj = this; return function () { if (thisObj.player == 'youtube') { thisObj.youTubePlayer.unloadModule(thisObj.ytCaptionModule); } thisObj.captionsOn = false; thisObj.currentCaption = -1; // stopgap to prevent spacebar in Firefox from reopening popup // immediately after closing it (used in handleCaptionToggle()) thisObj.hidingPopup = true; thisObj.captionsPopup.hide(); // Ensure stopgap gets cancelled if handleCaptionToggle() isn't called // e.g., if user triggered button with Enter or mouse click, not spacebar setTimeout(function() { thisObj.hidingPopup = false; }, 100); thisObj.$ccButton.focus(); // save preference to cookie thisObj.prefCaptions = 0; thisObj.updateCookie('prefCaptions'); if (!this.swappingSrc) { thisObj.refreshControls(); thisObj.updateCaption(); } } }; AblePlayer.prototype.showCaptions = function(now) { var c, thisCaption, captionText; var cues; if (this.selectedCaptions) { cues = this.selectedCaptions.cues; } else if (this.captions.length >= 1) { cues = this.captions[0].cues; } else { cues = []; } for (c = 0; c < cues.length; c++) { if ((cues[c].start <= now) && (cues[c].end > now)) { thisCaption = c; break; } } if (typeof thisCaption !== 'undefined') { if (this.currentCaption !== thisCaption) { // it's time to load the new caption into the container div captionText = this.flattenCueForCaption(cues[thisCaption]).replace('\n', '
'); this.$captionsDiv.html(captionText); this.currentCaption = thisCaption; if (captionText.length === 0) { // hide captionsDiv; otherwise background-color is visible due to padding this.$captionsDiv.css('display','none'); } else { this.$captionsDiv.css('display','inline-block'); } } } else { this.$captionsDiv.html(''); this.currentCaption = -1; } }; AblePlayer.prototype.flattenCueForCaption = function (cue) { // Takes a cue and returns the caption text to display // Also used for chapters // Support for 'i' and 'b' tags added in 2.3.66 // TODO: Add support for 'c' (class) and 'ruby' // c (class): Some text // Classes can be used to modify other tags too (e.g., ) // If tag, should be rendered as a // ruby: http://www.w3schools.com/tags/tag_ruby.asp // WebVTT also supports 'u' (underline) // I see no reason to support that in Able Player. // If it's available authors are likely to use it incorrectly // where or should be used instead // Here are the rare use cases where an underline is appropriate on the web: // http://html5doctor.com/u-element/ var result = []; var flattenComponent = function (component) { var result = [], ii; if (component.type === 'string') { result.push(component.value); } else if (component.type === 'v') { result.push('(' + component.value + ')'); for (ii = 0; ii < component.children.length; ii++) { result.push(flattenComponent(component.children[ii])); } } else if (component.type === 'i') { result.push(''); for (ii = 0; ii < component.children.length; ii++) { result.push(flattenComponent(component.children[ii])); } result.push(''); } else if (component.type === 'b') { result.push(''); for (ii = 0; ii < component.children.length; ii++) { result.push(flattenComponent(component.children[ii])); } result.push(''); } else { for (ii = 0; ii < component.children.length; ii++) { result.push(flattenComponent(component.children[ii])); } } return result.join(''); }; if (typeof cue.components !== 'undefined') { for (var ii = 0; ii < cue.components.children.length; ii++) { result.push(flattenComponent(cue.components.children[ii])); } } return result.join(''); }; AblePlayer.prototype.getCaptionsOptions = function(pref) { var options = []; switch (pref) { case 'prefCaptionsFont': options[0] = this.tt.serif; options[1] = this.tt.sans; options[3] = this.tt.cursive; options[4] = this.tt.fantasy; options[2] = this.tt.monospace; break; case 'prefCaptionsColor': case 'prefCaptionsBGColor': // HTML color values must be in English options[0] = ['white',this.tt.white]; options[1] = ['yellow',this.tt.yellow]; options[2] = ['green',this.tt.green]; options[3] = ['cyan',this.tt.cyan]; options[4] = ['blue',this.tt.blue]; options[5] = ['magenta',this.tt.magenta]; options[6] = ['red',this.tt.red]; options[7] = ['black',this.tt.black]; break; case 'prefCaptionsSize': options[0] = '75%'; options[1] = '100%'; options[2] = '125%'; options[3] = '150%'; options[4] = '200%'; break; case 'prefCaptionsOpacity': options[0] = '0%'; options[1] = '25%'; options[2] = '50%'; options[3] = '75%'; options[4] = '100%'; break; case 'prefCaptionsStyle': options[0] = this.tt.captionsStylePopOn; options[1] = this.tt.captionsStyleRollUp; break; case 'prefCaptionsPosition': options[0] = 'overlay'; options[1] = 'below'; break; } return options; }; AblePlayer.prototype.translatePrefs = function(pref, value, outputFormat) { // translate current value of pref to a value supported by outputformat if (outputFormat == 'youtube') { if (pref === 'size') { // YouTube font sizes are a range from -1 to 3 (0 = default) switch (value) { case '75%': return -1; case '100%': return 0; case '125%': return 1; case '150%': return 2; case '200%': return 3; } } } return false; } AblePlayer.prototype.stylizeCaptions = function($element, pref) { // $element is the jQuery element containing the captions // this function handles stylizing of the sample caption text in the Prefs dialog // plus the actual production captions // TODO: consider applying the same user prefs to visible text-based description var property, newValue, opacity, lineHeight; if (typeof $element !== 'undefined') { if (pref == 'prefCaptionsPosition') { this.positionCaptions(); } else if (typeof pref !== 'undefined') { // just change the one property that user just changed if (pref === 'prefCaptionsFont') { property = 'font-family'; } else if (pref === 'prefCaptionsSize') { property = 'font-size'; } else if (pref === 'prefCaptionsColor') { property = 'color'; } else if (pref === 'prefCaptionsBGColor') { property = 'background-color'; } else if (pref === 'prefCaptionsOpacity') { property = 'opacity'; } if (pref === 'prefCaptionsOpacity') { newValue = parseFloat($('#' + this.mediaId + '_' + pref).val()) / 100.0; } else { newValue = $('#' + this.mediaId + '_' + pref).val(); } $element.css(property, newValue); } else { // no property was specified, update all styles with current saved prefs opacity = parseFloat(this.prefCaptionsOpacity) / 100.0; $element.css({ 'font-family': this.prefCaptionsFont, 'font-size': this.prefCaptionsSize, 'color': this.prefCaptionsColor, 'background-color': this.prefCaptionsBGColor, 'opacity': opacity }); if ($element === this.$captionsDiv) { if (typeof this.$captionsWrapper !== 'undefined') { lineHeight = parseInt(this.prefCaptionsSize,10) + 25; this.$captionsWrapper.css('line-height',lineHeight + '%'); } } if (this.prefCaptionsPosition === 'below') { // also need to add the background color to the wrapper div if (typeof this.$captionsWrapper !== 'undefined') { this.$captionsWrapper.css({ 'background-color': this.prefCaptionsBGColor, 'opacity': '1' }); } } else if (this.prefCaptionsPosition === 'overlay') { // no background color for overlay wrapper, captions are displayed in-line if (typeof this.$captionsWrapper !== 'undefined') { this.$captionsWrapper.css({ 'background-color': 'transparent', 'opacity': '' }); } } this.positionCaptions(); } } }; AblePlayer.prototype.positionCaptions = function(position) { // set caption position to either 'overlay' or 'below' // if position parameter was passed to this function, use that // otherwise use user preference if (typeof position === 'undefined') { position = this.prefCaptionsPosition; } if (typeof this.$captionsWrapper !== 'undefined') { if (position == 'below') { this.$captionsWrapper.removeClass('able-captions-overlay').addClass('able-captions-below'); // also need to update in-line styles this.$captionsWrapper.css({ 'background-color': this.prefCaptionsBGColor, 'opacity': '1' }); } else { this.$captionsWrapper.removeClass('able-captions-below').addClass('able-captions-overlay'); this.$captionsWrapper.css({ 'background-color': 'transparent', 'opacity': '' }); } } }; })(jQuery);