|
2761 | 2761 | this.$transcriptToolbar.append($('<label for="autoscroll-transcript-checkbox">' + this.tt.autoScroll + ': </label>'), this.$autoScrollTranscriptCheckbox); |
2762 | 2762 |
|
2763 | 2763 | // Add field for selecting a transcript language |
2764 | | - // This will be deleted in FUCK if there are no languages |
| 2764 | + // This will be deleted in initialize.js > recreatePlayer() if there are no languages |
2765 | 2765 | this.$transcriptLanguageSelect = $('<select id="transcript-language-select">'); |
2766 | 2766 | // Add a default "Unknown" option; this will be deleted later if there are any |
2767 | 2767 | // elements with a language. |
|
2984 | 2984 | }); |
2985 | 2985 |
|
2986 | 2986 | this.$transcriptLanguageSelect.change(function () { |
| 2987 | + |
2987 | 2988 | var language = thisObj.$transcriptLanguageSelect.val(); |
| 2989 | + |
| 2990 | + // set language of all content (chapters, captions & descriptions) |
| 2991 | + // to match selection (if languages are avaialable) |
| 2992 | + for (var ii in thisObj.chapters) { |
| 2993 | + if (thisObj.chapters[ii].language === language) { |
| 2994 | + thisObj.transcriptChapters = thisObj.chapters[ii]; |
| 2995 | + } |
| 2996 | + } |
2988 | 2997 | for (var ii in thisObj.captions) { |
2989 | 2998 | if (thisObj.captions[ii].language === language) { |
2990 | 2999 | thisObj.transcriptCaptions = thisObj.captions[ii]; |
|
4136 | 4145 | // called via setupTracks() only if there is track with kind="descriptions" |
4137 | 4146 | // prepares for delivery of text description , in case it's needed |
4138 | 4147 | // whether and how it's delivered is controlled within description.js > initDescription() |
4139 | | - var trackLang = track.getAttribute('srclang'); |
| 4148 | + |
| 4149 | + // srcLang should always be included with <track>, but HTML5 spec doesn't require it |
| 4150 | + // if not provided, assume track is the same language as the default player language |
| 4151 | + var trackLang = track.getAttribute('srclang') || this.lang; |
| 4152 | + |
4140 | 4153 | this.hasClosedDesc = true; |
4141 | 4154 | this.currentDescription = -1; |
4142 | 4155 | this.descriptions.push({ |
|
4146 | 4159 | }; |
4147 | 4160 |
|
4148 | 4161 | AblePlayer.prototype.setupChapters = function (track, cues) { |
| 4162 | + |
4149 | 4163 | // NOTE: WebVTT supports nested timestamps (to form an outline) |
4150 | 4164 | // This is not currently supported. |
4151 | | - var i=0; |
| 4165 | + |
| 4166 | + // srcLang should always be included with <track>, but HTML5 spec doesn't require it |
| 4167 | + // if not provided, assume track is the same language as the default player language |
| 4168 | + var trackLang = track.getAttribute('srclang') || this.lang; |
| 4169 | + |
4152 | 4170 | this.hasChapters = true; |
| 4171 | + |
| 4172 | + // TODO: Add support for multiple languages of chapters |
| 4173 | + // Replace the following line with the commented block that follows |
| 4174 | + // Haven't done this because it will have a big effect downstream |
| 4175 | + // on all chapter processing |
4153 | 4176 | this.chapters = cues; |
| 4177 | + |
| 4178 | + /* // new |
| 4179 | + this.chapters.push({ |
| 4180 | + cues: cues, |
| 4181 | + language: trackLang |
| 4182 | + }); |
| 4183 | + */ |
| 4184 | + |
4154 | 4185 | if (this.chaptersDivLocation) { |
4155 | 4186 | this.populateChaptersDiv(); |
4156 | 4187 | } |
|
7294 | 7325 |
|
7295 | 7326 | // Takes a cue and returns the caption text to display for it. |
7296 | 7327 | AblePlayer.prototype.flattenCueForCaption = function (cue) { |
| 7328 | + |
7297 | 7329 | var result = []; |
7298 | 7330 |
|
7299 | 7331 | var flattenComponent = function (component) { |
|
7318 | 7350 | for (var ii in cue.components.children) { |
7319 | 7351 | result.push(flattenComponent(cue.components.children[ii])); |
7320 | 7352 | } |
7321 | | - |
7322 | 7353 | return result.join(''); |
7323 | 7354 | }; |
7324 | 7355 |
|
|
7704 | 7735 | return; |
7705 | 7736 | } |
7706 | 7737 | // Update transcript. |
| 7738 | + var chapters; |
7707 | 7739 | var captions; |
7708 | 7740 | var descriptions; |
7709 | 7741 | var captionLang; |
| 7742 | + |
| 7743 | + // setup captions |
7710 | 7744 | if (this.transcriptCaptions) { |
7711 | 7745 | // use this independently of this.selectedCaptions |
7712 | 7746 | // user might want captions in one language, transcript in another |
|
7717 | 7751 | captionLang = this.captionLang; |
7718 | 7752 | captions = this.selectedCaptions.cues; |
7719 | 7753 | } |
| 7754 | + |
| 7755 | + // setup chapters |
| 7756 | + if (this.transcriptChapters) { |
| 7757 | + chapters = this.transcriptChapters; |
| 7758 | + } |
| 7759 | + else if (this.chapters.length > 0) { |
| 7760 | + // Try and match the caption language. |
| 7761 | + if (captionLang) { |
| 7762 | + for (var ii in this.chapters) { |
| 7763 | + if (this.chapters[ii].language === captionLang) { |
| 7764 | + chapters = this.chapters[ii]; |
| 7765 | + } |
| 7766 | + } |
| 7767 | + } |
| 7768 | + if (typeof chapters === 'undefined') { |
| 7769 | + chapters = this.chapters; |
| 7770 | + } |
| 7771 | + } |
| 7772 | + |
| 7773 | + // setup descriptions |
7720 | 7774 | if (this.transcriptDescriptions) { |
7721 | 7775 | descriptions = this.transcriptDescriptions.cues; |
7722 | 7776 | } |
|
7733 | 7787 | descriptions = this.descriptions[0].cues; |
7734 | 7788 | } |
7735 | 7789 | } |
7736 | | - var div = this.generateTranscript(captions || [], descriptions || []); |
| 7790 | + |
| 7791 | + var div = this.generateTranscript(chapters || [], captions || [], descriptions || []); |
7737 | 7792 | this.$transcriptDiv.html(div); |
7738 | 7793 |
|
7739 | 7794 | var thisObj = this; |
|
7786 | 7841 | } |
7787 | 7842 | }; |
7788 | 7843 |
|
7789 | | - AblePlayer.prototype.generateTranscript = function(captions, descriptions) { |
7790 | | - var thisObj = this; |
| 7844 | + AblePlayer.prototype.generateTranscript = function(chapters, captions, descriptions) { |
7791 | 7845 |
|
7792 | | - var main = $('<div class="able-transcript-container"></div>'); |
| 7846 | + var thisObj = this; |
7793 | 7847 |
|
7794 | | - // TODO: Make scrolling optional? |
| 7848 | + var $main = $('<div class="able-transcript-container"></div>'); |
7795 | 7849 |
|
7796 | | - var transcriptTitle = 'Transcript'; |
| 7850 | + var transcriptTitle = this.tt.prefMenuTranscript; |
7797 | 7851 | if (typeof this.transcriptTitle !== 'undefined') { |
7798 | 7852 | transcriptTitle = this.transcriptTitle; |
7799 | 7853 | } |
7800 | 7854 | else if (this.lyricsMode) { |
7801 | | - transcriptTitle = 'Lyrics'; |
| 7855 | + transcriptTitle = 'Lyrics'; // TODO: Localize this |
7802 | 7856 | } |
7803 | 7857 |
|
7804 | 7858 | if (typeof this.transcriptDivLocation === 'undefined' && transcriptTitle != '') { |
7805 | 7859 | // only add an HTML heading to internal transcript |
7806 | 7860 | // external transcript is expected to have its own heading |
7807 | 7861 | var headingNumber = this.playerHeadingLevel; |
7808 | 7862 | headingNumber += 1; |
| 7863 | + var chapterHeadingNumber = headingNumber + 1; |
| 7864 | + |
| 7865 | + if (headingNumber <= 6) { |
| 7866 | + var transcriptHeading = 'h' + headingNumber.toString(); |
| 7867 | + } |
| 7868 | + else { |
| 7869 | + var transcriptHeading = 'div'; |
| 7870 | + } |
| 7871 | + // var transcriptHeadingTag = '<' + transcriptHeading + ' class="able-transcript-heading">'; |
| 7872 | + var $transcriptHeadingTag = $('<' + transcriptHeading + '>'); |
| 7873 | + $transcriptHeadingTag.addClass('able-transcript-heading'); |
7809 | 7874 | if (headingNumber > 6) { |
7810 | | - headingNumber = 6; |
| 7875 | + $transcriptHeadingTag.attr({ |
| 7876 | + 'role': 'heading', |
| 7877 | + 'aria-level': headingNumber |
| 7878 | + }); |
7811 | 7879 | } |
7812 | | - var transcriptHeading = 'h' + headingNumber.toString(); |
7813 | | - var transcriptHeadingTag = '<' + transcriptHeading + ' class="able-transcript-heading">'; |
7814 | | - transcriptHeadingTag += transcriptTitle; |
7815 | | - transcriptHeadingTag += '</' + transcriptHeading + '>'; |
7816 | | - main.append(transcriptHeadingTag); |
| 7880 | + $transcriptHeadingTag.text(transcriptTitle); |
| 7881 | + |
| 7882 | + $main.append($transcriptHeadingTag); |
7817 | 7883 | } |
7818 | 7884 |
|
| 7885 | + var nextChapter = 0; |
7819 | 7886 | var nextCap = 0; |
7820 | 7887 | var nextDesc = 0; |
7821 | 7888 |
|
| 7889 | + var addChapter = function(div, chap) { |
| 7890 | + |
| 7891 | + if (chapterHeadingNumber <= 6) { |
| 7892 | + var chapterHeading = 'h' + chapterHeadingNumber.toString(); |
| 7893 | + } |
| 7894 | + else { |
| 7895 | + var chapterHeading = 'div'; |
| 7896 | + } |
| 7897 | + |
| 7898 | + var $chapterHeadingTag = $('<' + chapterHeading + '>',{ |
| 7899 | + 'class': 'able-transcript-chapter-heading' |
| 7900 | + }); |
| 7901 | + if (chapterHeadingNumber > 6) { |
| 7902 | + $chapterHeadingTag.attr({ |
| 7903 | + 'role': 'heading', |
| 7904 | + 'aria-level': chapterHeadingNumber |
| 7905 | + }); |
| 7906 | + } |
| 7907 | + |
| 7908 | + var flattenComponentForChapter = function(comp) { |
| 7909 | + var result = []; |
| 7910 | + if (comp.type === 'string') { |
| 7911 | + result.push(comp.value); |
| 7912 | + } |
| 7913 | + else { |
| 7914 | + for (var ii in comp.children) { |
| 7915 | + result = result.concat(flattenComponentForChapter(comp.children[ii])); |
| 7916 | + } |
| 7917 | + } |
| 7918 | + return result; |
| 7919 | + } |
| 7920 | + |
| 7921 | + var $chapSpan = $('<span>',{ |
| 7922 | + 'class': 'able-transcript-seekpoint' |
| 7923 | + }); |
| 7924 | + for (var ii in chap.components.children) { |
| 7925 | + var results = flattenComponentForChapter(chap.components.children[ii]); |
| 7926 | + for (var jj in results) { |
| 7927 | + $chapSpan.append(results[jj]); |
| 7928 | + } |
| 7929 | + } |
| 7930 | + $chapSpan.attr('data-start', chap.start.toString()); |
| 7931 | + $chapSpan.attr('data-end', chap.end.toString()); |
| 7932 | + $chapterHeadingTag.append($chapSpan); |
| 7933 | + |
| 7934 | + div.append($chapterHeadingTag); |
| 7935 | + }; |
| 7936 | + |
7822 | 7937 | var addDescription = function(div, desc) { |
7823 | 7938 | var descDiv = $('<div class="able-desc"><span class="hidden">Description: </span></div>'); |
7824 | 7939 |
|
|
7922 | 8037 | div.append('\n'); |
7923 | 8038 | }; |
7924 | 8039 |
|
7925 | | - while ((nextCap < captions.length) || (nextDesc < descriptions.length)) { |
7926 | | - if ((nextCap < captions.length) && (nextDesc < descriptions.length)) { |
7927 | | - if (descriptions[nextDesc].start <= captions[nextCap].start) { |
7928 | | - addDescription(main, descriptions[nextDesc]); |
| 8040 | + // keep looping as long as any one of the three arrays has content |
| 8041 | + while ((nextChapter < chapters.length) || (nextDesc < descriptions.length) || (nextCap < captions.length)) { |
| 8042 | + |
| 8043 | + if ((nextChapter < chapters.length) && (nextDesc < descriptions.length) && (nextCap < captions.length)) { |
| 8044 | + // they all three have content |
| 8045 | + var firstStart = Math.min(chapters[nextChapter].start,descriptions[nextDesc].start,captions[nextCap].start); |
| 8046 | + } |
| 8047 | + else if ((nextChapter < chapters.length) && (nextDesc < descriptions.length)) { |
| 8048 | + // chapters & descriptions have content |
| 8049 | + var firstStart = Math.min(chapters[nextChapter].start,descriptions[nextDesc].start); |
| 8050 | + } |
| 8051 | + else if ((nextChapter < chapters.length) && (nextCap < captions.length)) { |
| 8052 | + // chapters & captions have content |
| 8053 | + var firstStart = Math.min(chapters[nextChapter].start,captions[nextCap].start); |
| 8054 | + } |
| 8055 | + else if ((nextDesc < descriptions.length) && (nextCap < captions.length)) { |
| 8056 | + // descriptions & captions have content |
| 8057 | + var firstStart = Math.min(descriptions[nextDesc].start,captions[nextCap].start); |
| 8058 | + } |
| 8059 | + else { |
| 8060 | + var firstStart = null; |
| 8061 | + } |
| 8062 | + if (firstStart !== null) { |
| 8063 | + if (typeof chapters[nextChapter] !== 'undefined' && chapters[nextChapter].start === firstStart) { |
| 8064 | + addChapter($main, chapters[nextChapter]); |
| 8065 | + nextChapter += 1; |
| 8066 | + } |
| 8067 | + else if (typeof descriptions[nextDesc] !== 'undefined' && descriptions[nextDesc].start === firstStart) { |
| 8068 | + addDescription($main, descriptions[nextDesc]); |
7929 | 8069 | nextDesc += 1; |
7930 | 8070 | } |
7931 | 8071 | else { |
7932 | | - addCaption(main, captions[nextCap]); |
| 8072 | + addCaption($main, captions[nextCap]); |
7933 | 8073 | nextCap += 1; |
7934 | 8074 | } |
7935 | 8075 | } |
7936 | | - else if (nextCap < captions.length) { |
7937 | | - addCaption(main, captions[nextCap]); |
7938 | | - nextCap += 1; |
7939 | | - } |
7940 | | - else if (nextDesc < descriptions.length) { |
7941 | | - addDescription(main, descriptions[nextDesc]); |
7942 | | - nextDesc += 1; |
| 8076 | + else { |
| 8077 | + if (nextChapter < chapters.length) { |
| 8078 | + addCaption($main, chapters[nextChapter]); |
| 8079 | + nextChapter += 1; |
| 8080 | + } |
| 8081 | + else if (nextDesc < descriptions.length) { |
| 8082 | + addDescription($main, descriptions[nextDesc]); |
| 8083 | + nextDesc += 1; |
| 8084 | + } |
| 8085 | + else if (nextCap < captions.length) { |
| 8086 | + addCaption($main, captions[nextCap]); |
| 8087 | + nextCap += 1; |
| 8088 | + } |
7943 | 8089 | } |
7944 | 8090 | } |
7945 | 8091 |
|
7946 | | - return main; |
| 8092 | + return $main; |
7947 | 8093 | }; |
7948 | 8094 | })(jQuery); |
7949 | 8095 |
|
|
0 commit comments