diff --git a/css/VideoPlayer.css b/css/VideoPlayer.css index 4c6b8af..0447238 100644 --- a/css/VideoPlayer.css +++ b/css/VideoPlayer.css @@ -34,6 +34,119 @@ .fl-theme-uio-by .fl-videoPlayer-video-element:focus, .fl-theme-uio-bw .fl-videoPlayer-video-element:focus { outline-color: #000000; } +/* + * Error message areas + */ +.fl-videoPlayer-videoError { + border: 2px solid #999999; + width: 32em; + height: 20em; + background: url("../images/error/exclamation.png") no-repeat 50% 30% rgba(0,0,0,.5); +} +.fl-theme-uio-yb .fl-videoPlayer-videoError { + background-image: url("../images/error/exclamation-yellow.png"); +} +.fl-theme-uio-wb .fl-videoPlayer-videoError { + background-image: url("../images/error/exclamation-white.png"); +} +.fl-theme-uio-by .fl-videoPlayer-videoError, +.fl-theme-uio-bw .fl-videoPlayer-videoError { + background-image: url("../images/error/exclamation-black.png"); +} + +.fl-videoPlayer-videoError .fl-errorPanel-container { + margin-top: 40%; + text-align: center; + color: #FFFFFF; +} +.fl-videoPlayer-videoError .fl-errorPanel-message { + text-transform: lowercase; + font-style: italic; + text-shadow: 1px 1px 0 rgba(0,0,0,.3); +} +.fl-theme-uio-yb .fl-videoPlayer-videoError .fl-errorPanel-message, +.fl-theme-uio-wb .fl-videoPlayer-videoError .fl-errorPanel-message, +.fl-theme-uio-by .fl-videoPlayer-videoError .fl-errorPanel-message, +.fl-theme-uio-bw .fl-videoPlayer-videoError .fl-errorPanel-message { + text-shadow: none; +} +.fl-videoPlayer-videoError .fl-errorPanel-retryButton { + border: none; + background: url("../images/error/2xretry.png") no-repeat left center; + padding-left: 1.5em; + font-size: inherit; + height: 2em; + background-size: contain; + text-transform: uppercase; + pointer-events: all; + cursor: pointer; + color: #FFFFFF; + text-shadow: 1px 1px 0 rgba(0,0,0,.3); +} +.fl-videoPlayer-videoError .fl-errorPanel-retryButton:hover { + background-color: rgba(0, 0, 0, 0.25); + box-shadow: inset 0 0 0.25em rgba(0,0,0,.5); + background-image: url("../images/error/2xretry-hover.png"); +} +.fl-theme-yb .fl-videoPlayer-videoError .fl-errorPanel-retryButton { + background-image: url("../images/error/2xretry-yellow.png"); +} +.fl-theme-yb .fl-videoPlayer-videoError .fl-errorPanel-retryButton:hover { + background-image: url("../images/error/2xretry-yellow-hover.png"); + box-shadow: none; + border: 1px solid #FFFF00; +} +.fl-theme-wb .fl-videoPlayer-videoError .fl-errorPanel-retryButton { + background-image: url("../images/error/2xretry-white.png"); +} +.fl-theme-wb .fl-videoPlayer-videoError .fl-errorPanel-retryButton:hover { + background-image: url("../images/error/2xretry-white-hover.png"); + box-shadow: none; + border: 1px solid #FFFFFF; +} +.fl-theme-by .fl-videoPlayer-videoError .fl-errorPanel-retryButton, +.fl-theme-bw .fl-videoPlayer-videoError .fl-errorPanel-retryButton { + background-image: url("../images/error/2xretry-black.png"); +} +.fl-theme-by .fl-videoPlayer-videoError .fl-errorPanel-retryButton:hover, +.fl-theme-bw .fl-videoPlayer-videoError .fl-errorPanel-retryButton:hover { + background-image: url("../images/error/2xretry-black-hover.png"); + box-shadow: none; + border: 1px solid #000000; +} +.fl-theme-uio-yb .fl-videoPlayer-videoError .fl-errorPanel-retryButton:focus { + background-color: #FFFF00 !important; + background-image: url("../images/error/2xretry-black-hover.png"); +} +.fl-theme-uio-yb .fl-videoPlayer-videoError .fl-errorPanel-retryButton:focus span { + color: #000000 !important; + background-color: #FFFF00 !important; +} +.fl-theme-uio-wb .fl-videoPlayer-videoError .fl-errorPanel-retryButton:focus { + background-color: #FFFFFF !important; + background-image: url("../images/error/2xretry-black-hover.png"); +} +.fl-theme-uio-wb .fl-videoPlayer-videoError .fl-errorPanel-retryButton:focus span { + color: #000000 !important; + background-color: #FFFFFF !important; +} +.fl-theme-uio-by .fl-videoPlayer-videoError .fl-errorPanel-retryButton:focus { + background-color: #000000 !important; + background-image: url("../images/error/2xretry-yellow-hover.png"); +} +.fl-theme-uio-by .fl-videoPlayer-videoError .fl-errorPanel-retryButton:focus span { + color: #FFFF00 !important; + background-color: #000000 !important; +} +.fl-theme-uio-bw .fl-videoPlayer-videoError .fl-errorPanel-retryButton:focus { + background-color: #000000 !important; + background-image: url("../images/error/2xretry-white-hover.png"); +} +.fl-theme-uio-bw .fl-videoPlayer-videoError .fl-errorPanel-retryButton:focus span { + color: #FFFFFF !important; + background-color: #000000 !important; +} + /* * Controller area */ @@ -66,7 +179,8 @@ cursor: pointer; background-color: rgba(0, 0, 0, 0); } -.fl-videoPlayer-button:focus { +.fl-videoPlayer-button:focus, +.fl-videoPlayer-videoError .fl-errorPanel-retryButton:focus { background-color: #3195C7; } .fl-theme-uio-yb .fl-videoPlayer-button:focus { background-color: #FFFF00 !important; } @@ -563,10 +677,12 @@ ul.fl-videoPlayer-transcripts-languageList li { .fl-theme-uio-bw .fl-videoPlayer-overlay, .fl-theme-uio-bw .fl-videoPlayer-captionArea, .fl-theme-uio-bw .captionator-cue-canvas { background-color: transparent !important; } -.fl-videoPlayer-caption-captionText { +.fl-videoPlayer-caption-captionText, +.fl-videoPlayer-captionError { color: white; background-color: black; opacity: 0.7; + padding: 0.5em; } .fl-videoPlayer-controller-menu-captions { @@ -576,6 +692,61 @@ ul.fl-videoPlayer-transcripts-languageList li { z-index: 101; /* To make our div showin on top since captionator has 100 so */ } +.fl-videoPlayer-captionError { + position: absolute; + bottom: 0; + margin: 0 10%; + width: 80% +} +.fl-videoPlayer-captionError .fl-errorPanel-message { + width: 95%; +} +.fl-videoPlayer-captionError .fl-errorPanel-dismissButton { + pointer-events: visible; + cursor: pointer; + border: none; + background: url("../images/error/2Xcloseerror-blackonwhite.png") no-repeat center center; + background-color: transparent !important; + height: 1.5em; + width: 1.5em; + background-size: cover; + margin-top: -1em; + margin-right: -1em; + font-size: inherit; +} +.fl-videoPlayer-captionError .fl-errorPanel-dismissButton:hover { + background-image: url("../images/error/2Xcloseerror-blackonwhite-hover.png"); +} +.fl-videoPlayer-captionError .fl-errorPanel-dismissButton:focus { + background-image: url("../images/error/2Xcloseerror-whiteonblack.png"); +} +.fl-theme-uio-yb .fl-videoPlayer-captionError .fl-errorPanel-dismissButton { + background-image: url("../images/error/2Xcloseerror-blackonyellow.png"); +} +.fl-theme-uio-yb .fl-videoPlayer-captionError .fl-errorPanel-dismissButton:hover { + background-image: url("../images/error/2Xcloseerror-blackonyellow-hover.png"); +} +.fl-theme-uio-yb .fl-videoPlayer-captionError .fl-errorPanel-dismissButton:focus { + background-image: url("../images/error/2Xcloseerror-yellowonblack.png"); +} +.fl-theme-uio-by .fl-videoPlayer-captionError .fl-errorPanel-dismissButton { + background-image: url("../images/error/2Xcloseerror-yellowonblack.png"); +} +.fl-theme-uio-by .fl-videoPlayer-captionError .fl-errorPanel-dismissButton:hover { + background-image: url("../images/error/2Xcloseerror-yellowonblack-hover.png"); +} +.fl-theme-uio-by .fl-videoPlayer-captionError .fl-errorPanel-dismissButton:focus { + background-image: url("../images/error/2Xcloseerror-blackonyellow.png"); +} +.fl-theme-uio-bw .fl-videoPlayer-captionError .fl-errorPanel-dismissButton { + background-image: url("../images/error/2Xcloseerror-whiteonblack.png"); +} +.fl-theme-uio-bw .fl-videoPlayer-captionError .fl-errorPanel-dismissButton:hover { + background-image: url("../images/error/2Xcloseerror-whiteonblack-hover.png"); +} +.fl-theme-uio-bw .fl-videoPlayer-captionError .fl-errorPanel-dismissButton:focus { + background-image: url("../images/error/2Xcloseerror-blackonwhite.png"); +} /* * Transcript area @@ -596,6 +767,10 @@ ul.fl-videoPlayer-transcripts-languageList li { width: 65%; } +.fl-videoPlayer-transcriptError .fl-errorPanel-message { + text-align: center; + margin: 3em; +} .fl-videoPlayer-transcript-text { word-spacing: 0.1em; overflow-x: hidden; diff --git a/demos/Mammals.html b/demos/Mammals.html index 157e8d6..0f812ae 100644 --- a/demos/Mammals.html +++ b/demos/Mammals.html @@ -36,6 +36,7 @@ + diff --git a/demos/VideoPlayer.html b/demos/VideoPlayer.html index 78d5064..f105e37 100644 --- a/demos/VideoPlayer.html +++ b/demos/VideoPlayer.html @@ -35,6 +35,7 @@ + diff --git a/demos/videos/ReorganizeFuture/ReorganizeFuture.fr.vtt b/demos/videos/ReorganizeFuture/ReorganizeFuture.fr.vtt index bcc90b5..bc8e54a 100644 --- a/demos/videos/ReorganizeFuture/ReorganizeFuture.fr.vtt +++ b/demos/videos/ReorganizeFuture/ReorganizeFuture.fr.vtt @@ -77,357 +77,357 @@ et nous développons des stratégies pour faire face à cette diversité. Nous essayons de rendre les choses plus simples, 20 -00:00:55.600 -> 00:00:57.500 +00:00:55.600 --> 00:00:57.500 moins complexe, moins chaotique. 21 -00:00:57.500 -> 00:01:00.864 +00:00:57.500 --> 00:01:00.864 Une autre partie de la condition humaine est que nous 22 -00:01:00.864 -> 00:01:03.185 +00:01:00.864 --> 00:01:03.185 essayer de trouver communité et connexions. 23 -00:01:03.200 -> 00:01:09.538 +00:01:03.200 --> 00:01:09.538 Nous formons des groupes formels et informels avec des critères explicites et implicites. 24 -00:01:09.554 -> 00:01:13.979 +00:01:09.554 --> 00:01:13.979 Nous organisons, nous créons des catégories, on filtre, on étiquette. 25 -00:01:13.979 -> 00:01:19.679 +00:01:13.979 --> 00:01:19.679 A notre plus précaires et accablé nous divisons en deux, nous créons des binaires: 26 -00:01:19.679 -> 00:01:21.867 +00:01:19.679 --> 00:01:21.867 masculin, féminin 27 -00:01:21.867 -> 00:01:24.215 +00:01:21.867 --> 00:01:24.215 handicapés, normale 28 -00:01:24.215 -> 00:01:27.031 +00:01:24.215 --> 00:01:27.031 gauche, droite 29 -00:01:27.031 -> 00:01:29.579 +00:01:27.031 --> 00:01:29.579 nous, eux. 30 -00:01:29.579 -> 00:01:34.982 +00:01:29.579 --> 00:01:34.982 Cela se traduit tout dans les questions de qui appartient et qui est exclu. 31 -00:01:34.982 -> 00:01:37.536 +00:01:34.982 --> 00:01:37.536 L'adhésion à des groupes peuvent être auto assignés, 32 -00:01:37.536 -> 00:01:40.467 +00:01:37.536 --> 00:01:40.467 peut être imposée, peut-être même policée. 33 -00:01:40.467 -> 00:01:44.179 +00:01:40.467 --> 00:01:44.179 Les groupes sont utilisés pour affirmer ou d'attribuer des privilèges et des pouvoirs. 34 -00:01:44.179 -> 00:01:46.929 +00:01:44.179 --> 00:01:46.929 Nous utilisons des groupes de juger 35 -00:01:46.929 -> 00:01:49.225 +00:01:46.929 --> 00:01:49.225 valeurs sont attribuées à des groupes 36 -00:01:49.225 -> 00:01:51.887 +00:01:49.225 --> 00:01:51.887 souvent des caractéristiques qui n'ont rien à voir avec 37 -00:01:51.887 -> 00:01:53.356 +00:01:51.887 --> 00:01:53.356 les propriétés originales des groupes fondateurs du 38 -00:01:53.356 -> 00:01:55.982 +00:01:53.356 --> 00:01:55.982 sont généralisés à tous les individus dans le groupe. 39 -00:01:55.982 -> 00:02:00.071 +00:01:55.982 --> 00:02:00.071 Parfois, les gens qui sont dans un groupe imposé 40 -00:02:00.071 -> 00:02:02.831 +00:02:00.071 --> 00:02:02.831 prendre la propriété du groupe et de la réforme 41 -00:02:02.831 -> 00:02:04.423 +00:02:02.831 --> 00:02:04.423 les classifications et les valeurs de l'intérieur. 42 -00:02:04.423 -> 00:02:08.446 +00:02:04.423 --> 00:02:08.446 Parfois, quelqu'un a l'audace 43 -00:02:08.446 -> 00:02:11.746 +00:02:08.446 --> 00:02:11.746 pour sortir de la catégorie, nous avons la mettre dans 44 -00:02:11.746 -> 00:02:16.910 +00:02:11.746 --> 00:02:16.910 mais pour préserver notre catégorie, nous pouvons la renvoyer comme une anomalie. 45 -00:02:16.910 -> 00:02:20.031 +00:02:16.910 --> 00:02:20.031 Certains groupes sont plus fluides tandis que d'autres sont plus fixes. 46 -00:02:20.031 -> 00:02:23.561 +00:02:20.031 --> 00:02:23.561 Nous les groupes se forment pas seulement, mais des groupes de groupes 47 -00:02:23.561 -> 00:02:25.662 +00:02:23.561 --> 00:02:25.662 et des groupes, des groupes, des groupes. 48 -00:02:25.662 -> 00:02:29.100 +00:02:25.662 --> 00:02:29.100 L'adhésion à un groupe peut nous accorder l'adhésion à d'autres groupes. 49 -00:02:29.100 -> 00:02:33.036 +00:02:29.100 --> 00:02:33.036 Mais malgré tout cela, nous sommes diversifiés 50 -00:02:33.036 -> 00:02:34.859 +00:02:33.036 --> 00:02:34.859 nous sommes complexe 51 -00:02:34.859 -> 00:02:36.452 +00:02:34.859 --> 00:02:36.452 nous sommes chaotique. 52 -00:02:36.452 -> 00:02:38.818 +00:02:36.452 --> 00:02:38.818 Individuellement, nous sommes différents 53 -00:02:38.818 -> 00:02:40.441 +00:02:38.818 --> 00:02:40.441 au fil du temps, dans des contextes différents 54 -00:02:40.441 -> 00:02:42.356 +00:02:40.441 --> 00:02:42.356 dans des rôles différents, dans des groupes différents. 55 -00:02:42.356 -> 00:02:45.429 +00:02:42.356 --> 00:02:45.429 Nous devons affirmer notre spécificité 56 -00:02:45.429 -> 00:02:47.864 +00:02:45.429 --> 00:02:47.864 nous avons besoin de former et de perfectionner notre identité. 57 -00:02:47.864 -> 00:02:50.914 +00:02:47.864 --> 00:02:50.914 Nous luttons avec l'identité qui nous est imposé. 58 -00:02:50.914 -> 00:02:56.367 +00:02:50.914 --> 00:02:56.367 Généralement, les gens ne s'intègrent pas facilement dans les catégories assignées 59 -00:02:56.367 -> 00:02:58.982 +00:02:56.367 --> 00:02:58.982 et pourtant nous persistons à les affecter. 60 -00:02:58.982 -> 00:03:02.631 +00:02:58.982 --> 00:03:02.631 Et puis, quelque chose de nouveau arrive 61 -00:03:02.631 -> 00:03:05.415 +00:03:02.631 --> 00:03:05.415 et secoue nos groupes, nos catégories et nos règles 62 -00:03:05.415 -> 00:03:08.266 +00:03:05.415 --> 00:03:08.266 et nous avons besoin d'ajuster, de reconstruire et de repenser. 63 -00:03:08.266 -> 00:03:12.533 +00:03:08.266 --> 00:03:12.533 Quelque chose comme, réseaux et des trucs numérique. 64 -00:03:12.533 -> 00:03:15.471 +00:03:12.533 --> 00:03:15.471 Ce nouveau monde numérique et connecté 65 -00:03:15.471 -> 00:03:17.875 +00:03:15.471 --> 00:03:17.875 remet en question la façon dont nous les choses de groupe 66 -00:03:17.875 -> 00:03:20.759 +00:03:17.875 --> 00:03:20.759 et les défis nos excuses pour laisser les gens sortir. 67 -00:03:20.759 -> 00:03:25.469 +00:03:20.759 --> 00:03:25.469 Le numérique change notre vision du temps, d'espace et de distance 68 -00:03:25.469 -> 00:03:31.085 +00:03:25.469 --> 00:03:31.085 et par extension notre point de vue du design, ce qui est possible et quelles choses coût. 69 -00:03:31.085 -> 00:03:36.048 +00:03:31.085 --> 00:03:36.048 Things Digital sont en plastique, mutable, malléable et adaptable. 70 -00:03:36.048 -> 00:03:39.500 +00:03:36.048 --> 00:03:39.500 Avant, tout le monde ne pouvait en forme 71 -00:03:39.500 -> 00:03:42.167 +00:03:39.500 --> 00:03:42.167 permettre à quelqu'un de quelqu'un d'autre était destiné à l'écart. 72 -00:03:42.167 -> 00:03:46.067 +00:03:42.167 --> 00:03:46.067 . Dans le numérique, la chambre est très extensible 73 -00:03:46.067 -> 00:03:49.767 +00:03:46.067 --> 00:03:49.767 Avant, ce que nous avons créé ne pouvait pas convenir à tous 74 -00:03:49.767 -> 00:03:51.777 +00:03:49.767 --> 00:03:51.777 . Alors nous avons fait l'adapter le plus grand groupe 75 -00:03:51.777 -> 00:03:54.533 +00:03:51.777 --> 00:03:54.533 Nous l'avons fait pour le groupe appelé moyen ou typique 76 -00:03:54.533 -> 00:03:58.267 +00:03:54.533 --> 00:03:58.267 cette gauche à tous de ne pas en moyenne ou typique. 77 -00:03:58.267 -> 00:04:03.399 +00:03:58.267 --> 00:04:03.399 Dans la réalité numérique des choses que nous faisons peut reconfigurer, adapter 78 -00:04:03.399 -> 00:04:06.274 +00:04:03.399 --> 00:04:06.274 et prendre une forme qui est le mieux pour chaque individu. 79 -00:04:06.274 -> 00:04:11.900 +00:04:06.274 --> 00:04:11.900 Dans le monde solide, chaque copie coûte presque le même que l'original. 80 -00:04:11.900 -> 00:04:14.351 +00:04:11.900 --> 00:04:14.351 la consommation a réellement consommé. 81 -00:04:14.351 -> 00:04:18.567 +00:04:14.351 --> 00:04:18.567 Dans le monde numérique, nous pouvons copier presque sans coût. 82 -00:04:18.567 -> 00:04:21.000 +00:04:18.567 --> 00:04:21.000 La consommation ne consomme plus. 83 -00:04:21.000 -> 00:04:24.522 +00:04:21.000 --> 00:04:24.522 Avant, il a fallu beaucoup de temps et d'effort 84 -00:04:24.522 -> 00:04:27.233 +00:04:24.522 --> 00:04:27.233 pour livrer des choses, surtout pour les gens très loin. 85 -00:04:27.233 -> 00:04:30.933 +00:04:27.233 --> 00:04:30.933 Maintenant, il est aussi facile de livrer des choses dans le monde 86 -00:04:30.933 -> 00:04:33.133 +00:04:30.933 --> 00:04:33.133 . Comme il est de livrer des choses à côté 87 -00:04:33.133 -> 00:04:36.852 +00:04:33.133 --> 00:04:36.852 Avant, si on ne place pas les choses dans un endroit fixe 88 -00:04:33.133 -> 00:04:36.852 +00:04:33.133 --> 00:04:36.852 nous aurions du mal à les retrouver. 89 -00:04:39.533 -> 00:04:43.633 +00:04:39.533 --> 00:04:43.633 Maintenant, nous pouvons les placer n'importe où sur le réseau et 90 -00:04:43.633 -> 00:04:46.267 +00:04:43.633 --> 00:04:46.267 les récupérer n'importe où sur le réseau. 91 -00:04:46.267 -> 00:04:50.133 +00:04:46.267 --> 00:04:50.133 Avant, nous avions besoin d'étiqueter les choses clairement et simplement 92 -00:04:50.133 -> 00:04:52.800 +00:04:50.133 --> 00:04:52.800 . Afin que nous puissions les reconnaître et de savoir quoi faire avec eux 93 -00:04:52.800 -> 00:04:56.449 +00:04:52.800 --> 00:04:56.449 Maintenant nous pouvons voir une description de chaque personne ou une chose 94 -00:04:56.449 -> 00:04:59.027 +00:04:56.449 --> 00:04:59.027 ce qui est utile et pertinente à notre but. 95 -00:04:59.027 -> 00:05:03.020 +00:04:59.027 --> 00:05:03.020 Et en passant, nous avons appris que 96 -00:05:03.020 -> 00:05:06.367 +00:05:03.020 --> 00:05:06.367 l'inclusion et l'égalité sont bons pour nous tous. 97 -00:05:06.367 -> 00:05:09.359 +00:05:06.367 --> 00:05:09.359 Nous sommes tous sains, plus riches et plus sage 98 -00:05:09.359 -> 00:05:12.200 +00:05:09.359 --> 00:05:12.200 quand notre société est inclusive et égalitaire. 99 -00:05:12.200 -> 00:05:15.367 +00:05:12.200 --> 00:05:15.367 Nous avons également découvert que les divers groupes 100 -00:05:15.367 -> 00:05:18.936 +00:05:15.367 --> 00:05:18.936 sont plus innovantes et créatives, et mieux à la planification et la prévision. 101 -00:05:18.936 -> 00:05:23.733 +00:05:18.936 --> 00:05:23.733 Nous avons expérimenté avec la nouvelle organisation comme 102 -00:05:23.733 -> 00:05:26.333 +00:05:23.733 --> 00:05:26.333 le plus populaire, pour être ignoré 103 -00:05:26.333 -> 00:05:28.700 +00:05:26.333 --> 00:05:28.700 ami, pas un ami. 104 -00:05:28.700 -> 00:05:31.033 +00:05:28.700 --> 00:05:31.033 Mais nous pouvons faire mieux. 105 -00:05:31.033 -> 00:05:33.267 +00:05:31.033 --> 00:05:33.267 Nous pouvons nous permettre d'être généreux dans notre conception 106 -00:05:33.267 -> 00:05:35.400 +00:05:33.267 --> 00:05:35.400 nous avons moins d'excuses à exclure. 107 -00:05:35.400 -> 00:05:37.567 +00:05:35.400 --> 00:05:37.567 Nous pouvons être fidèles à notre diversité. 108 -00:05:37.567 -> 00:05:43.067 +00:05:37.567 --> 00:05:43.067 Peut-être maintenant, nous pouvons trouver un moyen de faire de la place pour nous tous. \ No newline at end of file diff --git a/html/captionError_template.html b/html/captionError_template.html new file mode 100644 index 0000000..8737eab --- /dev/null +++ b/html/captionError_template.html @@ -0,0 +1,8 @@ +
+
+ error message +
+ +
diff --git a/html/transcriptError_template.html b/html/transcriptError_template.html new file mode 100644 index 0000000..5458099 --- /dev/null +++ b/html/transcriptError_template.html @@ -0,0 +1,3 @@ +
+ error message +
diff --git a/html/videoError_template.html b/html/videoError_template.html new file mode 100644 index 0000000..8592457 --- /dev/null +++ b/html/videoError_template.html @@ -0,0 +1,8 @@ +
+
+ error message +
+ +
diff --git a/html/videoPlayer_template.html b/html/videoPlayer_template.html index 00212d1..edd58ce 100644 --- a/html/videoPlayer_template.html +++ b/html/videoPlayer_template.html @@ -1,12 +1,15 @@
+
-
+
+
+
@@ -68,8 +71,10 @@
+
+
diff --git a/images/error/2Xcloseerror-blackonwhite-hover.png b/images/error/2Xcloseerror-blackonwhite-hover.png new file mode 100644 index 0000000..7ef051e Binary files /dev/null and b/images/error/2Xcloseerror-blackonwhite-hover.png differ diff --git a/images/error/2Xcloseerror-blackonwhite.png b/images/error/2Xcloseerror-blackonwhite.png new file mode 100644 index 0000000..50e2ae0 Binary files /dev/null and b/images/error/2Xcloseerror-blackonwhite.png differ diff --git a/images/error/2Xcloseerror-blackonyellow-hover.png b/images/error/2Xcloseerror-blackonyellow-hover.png new file mode 100644 index 0000000..d94cd4f Binary files /dev/null and b/images/error/2Xcloseerror-blackonyellow-hover.png differ diff --git a/images/error/2Xcloseerror-blackonyellow.png b/images/error/2Xcloseerror-blackonyellow.png new file mode 100644 index 0000000..5984648 Binary files /dev/null and b/images/error/2Xcloseerror-blackonyellow.png differ diff --git a/images/error/2Xcloseerror-whiteonblack-hover.png b/images/error/2Xcloseerror-whiteonblack-hover.png new file mode 100644 index 0000000..5c4cea5 Binary files /dev/null and b/images/error/2Xcloseerror-whiteonblack-hover.png differ diff --git a/images/error/2Xcloseerror-whiteonblack.png b/images/error/2Xcloseerror-whiteonblack.png new file mode 100644 index 0000000..4962f4e Binary files /dev/null and b/images/error/2Xcloseerror-whiteonblack.png differ diff --git a/images/error/2Xcloseerror-yellowonblack-hover.png b/images/error/2Xcloseerror-yellowonblack-hover.png new file mode 100644 index 0000000..8d983ad Binary files /dev/null and b/images/error/2Xcloseerror-yellowonblack-hover.png differ diff --git a/images/error/2Xcloseerror-yellowonblack.png b/images/error/2Xcloseerror-yellowonblack.png new file mode 100644 index 0000000..fe3c3b5 Binary files /dev/null and b/images/error/2Xcloseerror-yellowonblack.png differ diff --git a/images/error/2Xretry-black-hover.png b/images/error/2Xretry-black-hover.png new file mode 100644 index 0000000..0fbc934 Binary files /dev/null and b/images/error/2Xretry-black-hover.png differ diff --git a/images/error/2Xretry-black.png b/images/error/2Xretry-black.png new file mode 100644 index 0000000..935f79a Binary files /dev/null and b/images/error/2Xretry-black.png differ diff --git a/images/error/2Xretry-hover.png b/images/error/2Xretry-hover.png new file mode 100644 index 0000000..dc3d9d8 Binary files /dev/null and b/images/error/2Xretry-hover.png differ diff --git a/images/error/2Xretry-white-hover.png b/images/error/2Xretry-white-hover.png new file mode 100644 index 0000000..38d8a47 Binary files /dev/null and b/images/error/2Xretry-white-hover.png differ diff --git a/images/error/2Xretry-white.png b/images/error/2Xretry-white.png new file mode 100644 index 0000000..dc3c699 Binary files /dev/null and b/images/error/2Xretry-white.png differ diff --git a/images/error/2Xretry-yellow-hover.png b/images/error/2Xretry-yellow-hover.png new file mode 100644 index 0000000..27f4778 Binary files /dev/null and b/images/error/2Xretry-yellow-hover.png differ diff --git a/images/error/2Xretry-yellow.png b/images/error/2Xretry-yellow.png new file mode 100644 index 0000000..372c0e4 Binary files /dev/null and b/images/error/2Xretry-yellow.png differ diff --git a/images/error/2xretry.png b/images/error/2xretry.png new file mode 100644 index 0000000..74722a2 Binary files /dev/null and b/images/error/2xretry.png differ diff --git a/images/error/closeerror-blackonwhite-hover.png b/images/error/closeerror-blackonwhite-hover.png new file mode 100644 index 0000000..db17b3d Binary files /dev/null and b/images/error/closeerror-blackonwhite-hover.png differ diff --git a/images/error/closeerror-blackonwhite.png b/images/error/closeerror-blackonwhite.png new file mode 100644 index 0000000..cccd2ab Binary files /dev/null and b/images/error/closeerror-blackonwhite.png differ diff --git a/images/error/closeerror-blackonyellow-hover.png b/images/error/closeerror-blackonyellow-hover.png new file mode 100644 index 0000000..bc54891 Binary files /dev/null and b/images/error/closeerror-blackonyellow-hover.png differ diff --git a/images/error/closeerror-blackonyellow.png b/images/error/closeerror-blackonyellow.png new file mode 100644 index 0000000..613181c Binary files /dev/null and b/images/error/closeerror-blackonyellow.png differ diff --git a/images/error/closeerror-whiteonblack-hover.png b/images/error/closeerror-whiteonblack-hover.png new file mode 100644 index 0000000..e6a0a28 Binary files /dev/null and b/images/error/closeerror-whiteonblack-hover.png differ diff --git a/images/error/closeerror-whiteonblack.png b/images/error/closeerror-whiteonblack.png new file mode 100644 index 0000000..7c98537 Binary files /dev/null and b/images/error/closeerror-whiteonblack.png differ diff --git a/images/error/closeerror-yellowonblack-hover.png b/images/error/closeerror-yellowonblack-hover.png new file mode 100644 index 0000000..c1d6624 Binary files /dev/null and b/images/error/closeerror-yellowonblack-hover.png differ diff --git a/images/error/closeerror-yellowonblack.png b/images/error/closeerror-yellowonblack.png new file mode 100644 index 0000000..1294966 Binary files /dev/null and b/images/error/closeerror-yellowonblack.png differ diff --git a/images/error/exclamation-black.png b/images/error/exclamation-black.png new file mode 100644 index 0000000..3237151 Binary files /dev/null and b/images/error/exclamation-black.png differ diff --git a/images/error/exclamation-white.png b/images/error/exclamation-white.png new file mode 100644 index 0000000..82aa98a Binary files /dev/null and b/images/error/exclamation-white.png differ diff --git a/images/error/exclamation-yellow.png b/images/error/exclamation-yellow.png new file mode 100644 index 0000000..39f474d Binary files /dev/null and b/images/error/exclamation-yellow.png differ diff --git a/images/error/exclamation.png b/images/error/exclamation.png new file mode 100644 index 0000000..717deb7 Binary files /dev/null and b/images/error/exclamation.png differ diff --git a/images/error/retry-black-hover.png b/images/error/retry-black-hover.png new file mode 100644 index 0000000..5e2762c Binary files /dev/null and b/images/error/retry-black-hover.png differ diff --git a/images/error/retry-black.png b/images/error/retry-black.png new file mode 100644 index 0000000..dfb3a79 Binary files /dev/null and b/images/error/retry-black.png differ diff --git a/images/error/retry-hover.png b/images/error/retry-hover.png new file mode 100644 index 0000000..fb7891d Binary files /dev/null and b/images/error/retry-hover.png differ diff --git a/images/error/retry-white-hover.png b/images/error/retry-white-hover.png new file mode 100644 index 0000000..7509b8b Binary files /dev/null and b/images/error/retry-white-hover.png differ diff --git a/images/error/retry-white.png b/images/error/retry-white.png new file mode 100644 index 0000000..cb03fd9 Binary files /dev/null and b/images/error/retry-white.png differ diff --git a/images/error/retry-yellow-hover.png b/images/error/retry-yellow-hover.png new file mode 100644 index 0000000..b015a92 Binary files /dev/null and b/images/error/retry-yellow-hover.png differ diff --git a/images/error/retry-yellow.png b/images/error/retry-yellow.png new file mode 100644 index 0000000..69fb527 Binary files /dev/null and b/images/error/retry-yellow.png differ diff --git a/images/error/retry.png b/images/error/retry.png new file mode 100644 index 0000000..18d85c8 Binary files /dev/null and b/images/error/retry.png differ diff --git a/js/ErrorPanel.js b/js/ErrorPanel.js new file mode 100644 index 0000000..214bc3f --- /dev/null +++ b/js/ErrorPanel.js @@ -0,0 +1,102 @@ +/* +Copyright 2012 OCAD University + +Licensed under the Educational Community License (ECL), Version 2.0 or the New +BSD license. You may not use this file except in compliance with one these +Licenses. + +You may obtain a copy of the ECL 2.0 License and BSD License at +https://github.com/fluid-project/infusion/raw/master/Infusion-LICENSE.txt +*/ + +/*global jQuery, fluid*/ + +// JSLint options +/*jslint white: true, funcinvoke: true, undef: true, newcap: true, nomen: true, regexp: true, bitwise: true, browser: true, forin: true, maxerr: 100, indent: 4 */ + + +(function ($) { + + /*************************************************************** + * A simple component for rendering messages when errors happen. + ***************************************************************/ + fluid.defaults("fluid.errorPanel", { + gradeNames: ["fluid.viewComponent", "autoInit"], + preInitFunction: "fluid.errorPanel.preInit", + finalInitFunction: "fluid.errorPanel.finalInit", + selectors: { + message: ".flc-errorPanel-message", + dismissButton: ".flc-errorPanel-dismissButton", + dismissButtonText: ".flc-errorPanel-dismissButton-text", + retryButton: ".flc-errorPanel-retryButton", + retryButtonText: ".flc-errorPanel-retryButton-text" + }, + templates: { + panel: { + href: "errorPanel_template.html" + } + }, + strings: { + messageTemplate: "Sorry, %0 %1 is not currently available.", // %0 = language, %1 = medium + dismissLabel: "Dismiss error", + retryLabel: "Retry action" + }, + styles: { + hidden: "fl-hidden" + }, + events: { + onRetry: null, + onReady: null + } + }); + + fluid.errorPanel.preInit = function (that) { + /** + * @param {Object} values A collection of token keys and values. + * Keys and values can be of any data type that can be coerced into a string. + * Arrays will work here as well. + */ + that.show = function (values) { + that.locate("message").text(fluid.stringTemplate(that.options.strings.messageTemplate, fluid.makeArray(values))); + that.container.show(); + }; + + that.hide = function () { + that.container.hide(); + }; + }; + + fluid.errorPanel.processTemplate = function (that) { + if (that.options.templates.panel.fetchError) { + fluid.log("couldn't fetch error message template"); + fluid.log("status: " + that.options.templates.panel.fetchError.status + + ", textStatus: " + that.options.templates.panel.fetchError.textStatus + + ", errorThrown: " + that.options.templates.panel.fetchError.errorThrown); + return; + } + + that.container.append(that.options.templates.panel.resourceText); + that.locate("dismissButtonText").text(that.options.strings.dismissLabel); + that.locate("retryButtonText").text(that.options.strings.retryLabel); + + that.locate("dismissButton").click(that.hide); + + that.locate("retryButton").click(function (ev) { + ev.preventDefault(); + that.events.onRetry.fire(); + }); + + that.events.onReady.fire(that); + }; + + fluid.errorPanel.finalInit = function (that) { + that.container.hide(); + if (!that.options.templates.panel.resourceText) { + fluid.fetchResources(that.options.templates, function (res) { + fluid.errorPanel.processTemplate(that); + }); + } else { + fluid.errorPanel.processTemplate(that); + } + }; +})(jQuery); diff --git a/js/VideoPlayer.js b/js/VideoPlayer.js index 0e7ee1b..2718433 100644 --- a/js/VideoPlayer.js +++ b/js/VideoPlayer.js @@ -154,18 +154,20 @@ https://github.com/fluid-project/infusion/raw/master/Infusion-LICENSE.txt } }, events: { - onCurrentTranscriptChanged: "{videoPlayer}.events.onCurrentTranscriptChanged", + afterCurrentTranscriptChanged: "{videoPlayer}.events.afterCurrentTranscriptChanged", onTranscriptHide: "{videoPlayer}.events.onTranscriptHide", onTranscriptShow: "{videoPlayer}.events.onTranscriptShow", onTranscriptElementChange: "{videoPlayer}.events.onTranscriptElementChange", - onTranscriptsLoaded: "{videoPlayer}.events.onTranscriptsLoaded" + onTranscriptsLoaded: "{videoPlayer}.events.onTranscriptsLoaded", + onTranscriptLoadError: "{videoPlayer}.events.onTranscriptLoadError" } } } }, events: { onLoadedMetadata: "{videoPlayer}.events.onLoadedMetadata", - onMediaReady: "{videoPlayer}.events.onMediaReady" + onMediaReady: "{videoPlayer}.events.onMediaReady", + onMediaLoadError: "{videoPlayer}.events.onMediaLoadError" }, sources: "{videoPlayer}.options.video.sources" } @@ -185,7 +187,9 @@ https://github.com/fluid-project/infusion/raw/master/Infusion-LICENSE.txt onScrub: "{videoPlayer}.events.onScrub", afterScrub: "{videoPlayer}.events.afterScrub", onTranscriptsReady: "{videoPlayer}.events.canBindTranscriptMenu", - onCaptionsReady: "{videoPlayer}.events.canBindCaptionMenu" + onCaptionsReady: "{videoPlayer}.events.canBindCaptionMenu", + onCaptionLoadError: "{videoPlayer}.events.onCaptionLoadError", + onTranscriptLoadError: "{videoPlayer}.events.onTranscriptLoadError" } } }, @@ -198,7 +202,8 @@ https://github.com/fluid-project/infusion/raw/master/Infusion-LICENSE.txt applier: "{videoPlayer}.applier", captions: "{videoPlayer}.options.video.captions", events: { - onReady: "{videoPlayer}.events.onCaptionsReady" + onReady: "{videoPlayer}.events.onCaptionsReady", + onCaptionLoadError: "{videoPlayer}.events.onCaptionLoadError" } } } @@ -212,14 +217,17 @@ https://github.com/fluid-project/infusion/raw/master/Infusion-LICENSE.txt onViewReady: null, onLoadedMetadata: null, onMediaReady: null, + onMediaLoadError: null, onControllersReady: null, afterScrub: null, onStartScrub: null, onTemplateLoadError: null, - onCurrentTranscriptChanged: null, + onCaptionLoadError: null, + afterCurrentTranscriptChanged: null, onTranscriptHide: null, onTranscriptShow: null, onTranscriptElementChange: null, + onTranscriptLoadError: null, onReady: null, // public, time events @@ -263,7 +271,8 @@ https://github.com/fluid-project/infusion/raw/master/Infusion-LICENSE.txt caption: ".flc-videoPlayer-captionArea", controllers: ".flc-videoPlayer-controller", transcript: ".flc-videoPlayer-transcriptArea", - overlay: ".flc-videoPlayer-overlay" + overlay: ".flc-videoPlayer-overlay", + videoError: ".flc-videoPlayer-videoError" }, strings: { captionsOff: "Captions OFF", @@ -272,7 +281,7 @@ https://github.com/fluid-project/infusion/raw/master/Infusion-LICENSE.txt turnTranscriptsOff: "Turn Transcripts OFF", videoTitlePreface: "Video" }, - selectorsToIgnore: ["overlay", "caption", "videoPlayer", "transcript", "video", "videoContainer"], + selectorsToIgnore: ["overlay", "caption", "videoPlayer", "transcript", "video", "videoContainer", "videoError"], keyBindings: fluid.videoPlayer.defaultKeys, produceTree: "fluid.videoPlayer.produceTree", controls: "custom", @@ -304,6 +313,15 @@ https://github.com/fluid-project/infusion/raw/master/Infusion-LICENSE.txt videoPlayer: { forceCache: true, href: "../html/videoPlayer_template.html" + }, + videoError: { + href: "../html/videoError_template.html" + }, + transcriptError: { + href: "../html/transcriptError_template.html" + }, + captionError: { + href: "../html/captionError_template.html" } }, videoTitle: "unnamed video" @@ -410,10 +428,15 @@ https://github.com/fluid-project/infusion/raw/master/Infusion-LICENSE.txt }); that.events.onLoadedMetadata.addListener(function () { + that.locate("videoError").hide(); that.resize(); - + bindKeyboardControl(that); }); + + that.events.onMediaLoadError.addListener(function () { + that.locate("video").hide(); + }); }; var bindVideoPlayerModel = function (that) { @@ -659,8 +682,8 @@ https://github.com/fluid-project/infusion/raw/master/Infusion-LICENSE.txt var seconds = parseFloat(millis) / 1000; seconds = seconds < 0 || isNaN(seconds) ? 0 : seconds; - var hours = parseInt(seconds / 3600); - var minutes = parseInt(seconds / 60) % 60; + var hours = parseInt(seconds / 3600, 10); + var minutes = parseInt(seconds / 60, 10) % 60; seconds = (seconds % 60).toFixed(3); // Return result of type HH:MM:SS.mmm @@ -689,16 +712,24 @@ https://github.com/fluid-project/infusion/raw/master/Infusion-LICENSE.txt return vtt; }; - fluid.videoPlayer.fetchAmaraJson = function (videoUrl, callback) { + fluid.videoPlayer.fetchAmaraJson = function (videoUrl, success, error) { // No point continuing because we can't get a useful JSONP response without the url and a callback - if (!videoUrl || !callback) { + if (!videoUrl || !success) { return; } // Hard coded URL to amara here var url = encodeURI("http://www.universalsubtitles.org/api/1.0/subtitles/?video_url=" + videoUrl + "&callback=?"); - $.getJSON(url, callback); + $.ajax({ + url: url, + dataType: 'json', + success: success, + timeout: 1500, // only this timeout will force the error function to be called + error: error + }); + + }; })(jQuery); diff --git a/js/VideoPlayer_html5Captionator.js b/js/VideoPlayer_html5Captionator.js index 956de31..a296469 100644 --- a/js/VideoPlayer_html5Captionator.js +++ b/js/VideoPlayer_html5Captionator.js @@ -33,7 +33,8 @@ https://source.fluidproject.org/svn/LICENSE.txt events: { afterTrackElCreated: null, onTracksReady: null, - onReady: null + onReady: null, + onCaptionLoadError: null }, elPaths: { currentCaptions: "currentTracks.captions", @@ -42,7 +43,8 @@ https://source.fluidproject.org/svn/LICENSE.txt // TODO: Those selectors should come from the parent component!! selectors: { video: ".flc-videoPlayer-video", - caption: ".flc-videoPlayer-captionArea" + caption: ".flc-videoPlayer-captionArea", + captionError: ".flc-videoPlayer-captionError" }, listeners: { afterTrackElCreated: "fluid.videoPlayer.html5Captionator.waitForTracks", @@ -51,6 +53,20 @@ https://source.fluidproject.org/svn/LICENSE.txt createTrackFns: { "text/amarajson": "fluid.videoPlayer.html5Captionator.createAmaraTrack", "text/vtt": "fluid.videoPlayer.html5Captionator.createVttTrack" + }, + components: { + captionError: { + type: "fluid.errorPanel", + options: { + strings: { + messageTemplate: "Sorry, %0 captions currently unavailable", + dismissLabel: "Dismiss error" + }, + templates: { + panel: "{videoPlayer}.options.templates.captionError" + } + } + } } }); @@ -83,6 +99,16 @@ https://source.fluidproject.org/svn/LICENSE.txt if (display) { fluid.videoPlayer.html5Captionator.showCurrentTrack(that.readIndirect("elPaths.currentCaptions"), tracks, that.options.captions); + + // captionator doesn't fire any events or support a configurable error callback, + // so we have to wait a bit and check the track's readyState + setTimeout(function () { + fluid.each($("track", that.locate("video")), function (element, key) { + if (element.track.readyState === captionator.TextTrack.ERROR) { + that.events.onCaptionLoadError.fire(key, that.options.captions[key], true); + } + }); + }, 3000); } else { fluid.videoPlayer.html5Captionator.hideAllTracks(tracks); } @@ -129,7 +155,12 @@ https://source.fluidproject.org/svn/LICENSE.txt that.events.afterTrackElCreated.fire(that); }; - fluid.videoPlayer.fetchAmaraJson(opts.src, afterFetch); + var errorHandler = function () { + that.events.onCaptionLoadError.fire(key, opts, false); + that.events.afterTrackElCreated.fire(that); + }; + + fluid.videoPlayer.fetchAmaraJson(opts.src, afterFetch, errorHandler); }; fluid.videoPlayer.html5Captionator.createVttTrack = function (that, key, opts) { @@ -156,4 +187,17 @@ https://source.fluidproject.org/svn/LICENSE.txt that.events.onReady.fire(that, fluid.allocateSimpleId(that.locate("caption"))); }; + fluid.demands("captionError", "fluid.videoPlayer.html5Captionator", { + container: "{html5Captionator}.dom.captionError", + options: { + listeners: { + "{html5Captionator}.events.onCaptionLoadError": { + listener: "{captionError}.show", + args: "{arguments}.1.label" + }, + "{html5Captionator}.events.onTracksReady": "{captionError}.hide" + } + } + }); + })(jQuery); diff --git a/js/VideoPlayer_media.js b/js/VideoPlayer_media.js index 53daffc..08637ab 100644 --- a/js/VideoPlayer_media.js +++ b/js/VideoPlayer_media.js @@ -19,6 +19,8 @@ https://github.com/fluid-project/infusion/raw/master/Infusion-LICENSE.txt (function ($) { + fluid.registerNamespace("fluid.videoPlayer.media"); + /********************************************************************************* * Video Player Media * * * @@ -39,13 +41,30 @@ https://github.com/fluid-project/infusion/raw/master/Infusion-LICENSE.txt transcript: { type: "fluid.videoPlayer.transcript", createOnEvent: "onMediaReady" + }, + videoError: { + type: "fluid.errorPanel", + options: { + strings: { + messageTemplate: "Problem loading video", + retryLabel: "Retry" + }, + templates: { + panel: "{videoPlayer}.options.templates.videoError" + }, + events: { + onRetry: "{media}.events.onRetry" + } + } } }, finalInitFunction: "fluid.videoPlayer.media.finalInit", preInitFunction: "fluid.videoPlayer.media.preInit", events: { onLoadedMetadata: null, - onMediaReady: null + onMediaReady: null, + onMediaLoadError: null, + onRetry: null }, sourceRenderers: { "video/mp4": "fluid.videoPlayer.media.createSourceMarkup.html5SourceTag", @@ -57,6 +76,12 @@ https://github.com/fluid-project/infusion/raw/master/Infusion-LICENSE.txt sources: [] }); + fluid.videoPlayer.media.reloadSources = function (that) { + $("source", that.container).detach(); + fluid.videoPlayer.media.renderSources(that); + that.container.show().load(); + }; + fluid.videoPlayer.media.createSourceMarkup = { html5SourceTag: function (videoPlayer, mediaSource) { var sourceTag = $(""); @@ -73,7 +98,7 @@ https://github.com/fluid-project/infusion/raw/master/Infusion-LICENSE.txt } }; - var renderSources = function (that) { + fluid.videoPlayer.media.renderSources = function (that) { $.each(that.options.sources, function (idx, source) { var renderer = that.options.sourceRenderers[source.type]; if ($.isFunction(renderer)) { @@ -189,6 +214,10 @@ https://github.com/fluid-project/infusion/raw/master/Infusion-LICENSE.txt }); + mediaElementVideo.addEventListener("error", function (err) { + that.events.onMediaLoadError.fire(); + }); + // Fire onMediaReady here rather than finalInit() because the instantiation // of the media element object is asynchronous that.events.onMediaReady.fire(that); @@ -246,9 +275,24 @@ https://github.com/fluid-project/infusion/raw/master/Infusion-LICENSE.txt }; fluid.videoPlayer.media.finalInit = function (that) { - renderSources(that); + fluid.videoPlayer.media.renderSources(that); bindMediaModel(that); bindMediaDOMEvents(that); + + that.events.onRetry.addListener(function () { + $("source", that.container).detach(); + fluid.videoPlayer.media.renderSources(that); + that.container.show().load(); + }); }; + fluid.demands("videoError", ["fluid.videoPlayer", "fluid.videoPlayer.intervalEventsConductor"], { + container: "{videoPlayer}.dom.videoError", + options: { + listeners: { + "{media}.events.onMediaLoadError": "{videoError}.show" + } + } + }); + })(jQuery); diff --git a/js/VideoPlayer_transcript.js b/js/VideoPlayer_transcript.js index 220796d..8b7d308 100644 --- a/js/VideoPlayer_transcript.js +++ b/js/VideoPlayer_transcript.js @@ -39,13 +39,26 @@ https://github.com/fluid-project/infusion/raw/master/Infusion-LICENSE.txt transcriptEventBinder: { type: "fluid.videoPlayer.eventBinder", createOnEvent: "onReady" + }, + transcriptError: { + type: "fluid.errorPanel", + createOnEvent: "onReady", + options: { + strings: { + messageTemplate: "Sorry, %0 transcripts currently unavailable" + }, + templates: { + panel: "{videoPlayer}.options.templates.transcriptError" + } + } } }, events: { onTranscriptsLoaded: null, - onLoadTranscriptError: null, + onTranscriptLoadError: null, onIntervalChange: null, - onCurrentTranscriptChanged: null, + onCurrentTranscriptChanging: null, + afterCurrentTranscriptChanged: null, onTranscriptHide: null, onTranscriptShow: null, onTranscriptElementChange: null, @@ -72,14 +85,18 @@ https://github.com/fluid-project/infusion/raw/master/Infusion-LICENSE.txt selectors: { languageDropdown: ".flc-videoPlayer-transcripts-language-dropdown", closeButton: ".flc-videoPlayer-transcripts-close-button", - transcriptText: ".flc-videoPlayer-transcript-text" + transcriptText: ".flc-videoPlayer-transcript-text", + transcriptError: ".flc-videoPlayer-transcriptError" }, - selectorsToIgnore: ["closeButton", "transcriptText"], + selectorsToIgnore: ["closeButton", "transcriptText", "transcriptError"], styles: { element: "fl-videoPlayer-transcript-element", highlight: "fl-videoPlayer-transcript-element-highlight", selected: "fl-videoPlayer-transcript-element-selected" }, + strings: { + loading: "loading..." + }, transcriptElementIdPrefix: "flc-videoPlayer-transcript-element" }); @@ -97,6 +114,7 @@ https://github.com/fluid-project/infusion/raw/master/Infusion-LICENSE.txt // Update visibility of the transcript area based on the flag "model.displayTranscripts" fluid.videoPlayer.transcript.switchTranscriptArea = function (that) { if (that.model.displayTranscripts) { + fluid.videoPlayer.transcript.prepareTranscript(that); fluid.videoPlayer.transcript.showTranscriptArea(that); that.events.onTranscriptShow.fire(); } else { @@ -181,7 +199,7 @@ https://github.com/fluid-project/infusion/raw/master/Infusion-LICENSE.txt }; fluid.videoPlayer.transcript.displayTranscript = function (that, transcriptText) { - that.locate("transcriptText").html(transcriptText); + that.locate("transcriptText").html(transcriptText).show(); that.updateTranscriptHighlight(); $('span[id|="' + that.options.transcriptElementIdPrefix + '"]').click(function (evt) { @@ -266,12 +284,18 @@ https://github.com/fluid-project/infusion/raw/master/Infusion-LICENSE.txt fluid.videoPlayer.transcript.loadTranscript = function (that, currentIndex) { var transcriptSource = that.options.transcripts[currentIndex]; if (transcriptSource) { + transcriptSource.transcriptText = that.options.strings.loading; + var errorHandler = function () { + transcriptSource.transcriptText = null; + that.events.onTranscriptLoadError.fire(that, transcriptSource); + }; // Handle Universal Subtitles JSON files for transcripts if (transcriptSource.type === "text/amarajson") { - fluid.videoPlayer.fetchAmaraJson(transcriptSource.src, function (data) { + var successHandler = function (data) { fluid.videoPlayer.transcript.parseTranscriptFile(that, data, currentIndex, fluid.identity, "text", "start_time", "end_time"); - }); + }; + fluid.videoPlayer.fetchAmaraJson(transcriptSource.src, successHandler, errorHandler); } else { var opts = { type: "GET", @@ -279,10 +303,7 @@ https://github.com/fluid-project/infusion/raw/master/Infusion-LICENSE.txt success: function (data) { fluid.videoPlayer.transcript.parseTranscriptFile(that, data, currentIndex, that.convertToMilli, "transcript", "inTime", "outTime"); }, - error: function () { - fluid.log("Error loading transcript: " + transcriptSource.src + ". Are you sure this file exists?"); - that.events.onLoadTranscriptError.fire(transcriptSource); - } + error: errorHandler }; if (transcriptSource.type !== "JSONcc") { @@ -368,6 +389,7 @@ https://github.com/fluid-project/infusion/raw/master/Infusion-LICENSE.txt // actual choice of track hasn't changed return; } + that.events.onCurrentTranscriptChanging.fire(); fluid.videoPlayer.transcript.prepareTranscript(that); @@ -377,12 +399,15 @@ https://github.com/fluid-project/infusion/raw/master/Infusion-LICENSE.txt that.locate("languageDropdown").find("option[value='" + currentTranscriptIndex + "']").attr("selected", "selected"); that.updateTranscriptHighlight(); - that.events.onCurrentTranscriptChanged.fire(currentTranscriptIndex); + that.events.afterCurrentTranscriptChanged.fire(currentTranscriptIndex); }); that.events.onTranscriptsLoaded.addListener(function (intervalList) { that.transcriptInterval.setIntervalList(intervalList); }); + that.events.onTranscriptLoadError.addListener(function () { + that.locate("transcriptText").hide(); + }); that.events.onIntervalChange.addListener(function (currentInterval, previousInterval) { if (currentInterval !== that.model.transcriptIntervalId) { @@ -429,7 +454,6 @@ https://github.com/fluid-project/infusion/raw/master/Infusion-LICENSE.txt fluid.videoPlayer.transcript.bindTranscriptDOMEvents(that); fluid.videoPlayer.transcript.bindTranscriptModel(that); - fluid.videoPlayer.transcript.prepareTranscript(that); fluid.videoPlayer.transcript.switchTranscriptArea(that); that.transcriptTextId = function () { @@ -440,4 +464,18 @@ https://github.com/fluid-project/infusion/raw/master/Infusion-LICENSE.txt that.events.onReady.fire(that); }; + fluid.demands("transcriptError", "fluid.videoPlayer.intervalEventsConductor", { + container: "{transcript}.dom.transcriptError", + options: { + listeners: { + "{transcript}.events.onTranscriptLoadError": { + listener: "{transcriptError}.show", + args: "{arguments}.1.label" + }, + "{transcript}.events.onTranscriptsLoaded": "{transcriptError}.hide", + "{transcript}.events.onCurrentTranscriptChanging": "{transcriptError}.hide" + } + } + }); + })(jQuery); diff --git a/tests/all-tests.html b/tests/all-tests.html index 031343d..6ffc760 100644 --- a/tests/all-tests.html +++ b/tests/all-tests.html @@ -12,6 +12,7 @@ QUnit.testSuites([ "./html/MenuButton-test.html", "./html/ToggleButton-test.html", + "./html/ErrorPanel-test.html", "./html/VideoFramework-test.html", "./html/VideoPlayerAria-test.html", "./html/VideoPlayerControls-test.html", @@ -21,7 +22,8 @@ "./html/VideoPlayerIntervalEventsConductor-test.html", "./html/VideoPlayerIntervalEventsConductorIntegration-test.html", "./html/VideoPlayer-test.html", - "./html/VideoPlayerIntegration-test.html" + "./html/VideoPlayerIntegration-test.html", + "./html/VideoPlayerErrors-test.html" ]); diff --git a/tests/html/ErrorPanel-test.html b/tests/html/ErrorPanel-test.html new file mode 100644 index 0000000..95d7bbe --- /dev/null +++ b/tests/html/ErrorPanel-test.html @@ -0,0 +1,40 @@ + + + + + Error Panel Test Suite + + + + + + + + + + + + + + + + + + + +

Error Panel Test Suite

+

+
+

+
    + +
    + +
    +
    +
    + +
    + + + diff --git a/tests/html/VideoPlayer-test.html b/tests/html/VideoPlayer-test.html index 81ac03c..e844892 100644 --- a/tests/html/VideoPlayer-test.html +++ b/tests/html/VideoPlayer-test.html @@ -14,6 +14,7 @@ + diff --git a/tests/html/VideoPlayerAria-test.html b/tests/html/VideoPlayerAria-test.html index 85269f5..720318e 100644 --- a/tests/html/VideoPlayerAria-test.html +++ b/tests/html/VideoPlayerAria-test.html @@ -14,6 +14,7 @@ + @@ -24,6 +25,7 @@ + diff --git a/tests/html/VideoPlayerControls-test.html b/tests/html/VideoPlayerControls-test.html index 7174fd3..78309e6 100644 --- a/tests/html/VideoPlayerControls-test.html +++ b/tests/html/VideoPlayerControls-test.html @@ -15,6 +15,7 @@ + diff --git a/tests/html/VideoPlayerErrors-test.html b/tests/html/VideoPlayerErrors-test.html new file mode 100644 index 0000000..accf607 --- /dev/null +++ b/tests/html/VideoPlayerErrors-test.html @@ -0,0 +1,49 @@ + + + + + Video Player Error Handling Test Suite + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

    Video Player Error Handling Test Suite

    +

    +
    +

    +
      +
      + +
      + +
      + + + diff --git a/tests/html/VideoPlayerHTML5Captionator-test.html b/tests/html/VideoPlayerHTML5Captionator-test.html index a4ca941..f51c9d1 100644 --- a/tests/html/VideoPlayerHTML5Captionator-test.html +++ b/tests/html/VideoPlayerHTML5Captionator-test.html @@ -14,6 +14,7 @@ + diff --git a/tests/html/VideoPlayerIntegration-test.html b/tests/html/VideoPlayerIntegration-test.html index dbb2870..3382eff 100644 --- a/tests/html/VideoPlayerIntegration-test.html +++ b/tests/html/VideoPlayerIntegration-test.html @@ -14,6 +14,7 @@ + diff --git a/tests/html/VideoPlayerTranscript-test.html b/tests/html/VideoPlayerTranscript-test.html index 4e549f8..2440415 100644 --- a/tests/html/VideoPlayerTranscript-test.html +++ b/tests/html/VideoPlayerTranscript-test.html @@ -8,6 +8,7 @@ + @@ -36,6 +37,7 @@

      +
      diff --git a/tests/html/VideoPlayerTranscriptIntegration-test.html b/tests/html/VideoPlayerTranscriptIntegration-test.html index 75e0aaa..c6be76a 100644 --- a/tests/html/VideoPlayerTranscriptIntegration-test.html +++ b/tests/html/VideoPlayerTranscriptIntegration-test.html @@ -14,6 +14,7 @@ + diff --git a/tests/html/errorPanel_template.html b/tests/html/errorPanel_template.html new file mode 100644 index 0000000..5e9fb61 --- /dev/null +++ b/tests/html/errorPanel_template.html @@ -0,0 +1,11 @@ +
      +
      + error message +
      + + +
      diff --git a/tests/html/errorPanel_template_noDismiss.html b/tests/html/errorPanel_template_noDismiss.html new file mode 100644 index 0000000..d84228b --- /dev/null +++ b/tests/html/errorPanel_template_noDismiss.html @@ -0,0 +1,8 @@ +
      +
      + error message +
      + +
      diff --git a/tests/html/errorPanel_template_noRetry.html b/tests/html/errorPanel_template_noRetry.html new file mode 100644 index 0000000..5f1d6d5 --- /dev/null +++ b/tests/html/errorPanel_template_noRetry.html @@ -0,0 +1,8 @@ +
      +
      + error message +
      + +
      diff --git a/tests/js/ErrorPanelTests.js b/tests/js/ErrorPanelTests.js new file mode 100644 index 0000000..13bdd37 --- /dev/null +++ b/tests/js/ErrorPanelTests.js @@ -0,0 +1,179 @@ +/* +Copyright 2012 OCAD University + +Licensed under the Educational Community License (ECL), Version 2.0 or the New +BSD license. You may not use this file except in compliance with one these +Licenses. + +You may obtain a copy of the ECL 2.0 License and BSD License at +https://github.com/fluid-project/infusion/raw/master/Infusion-LICENSE.txt +*/ + +// Declare dependencies +/*global fluid, jqUnit, jQuery, start*/ + +// JSLint options +/*jslint white: true, funcinvoke: true, undef: true, newcap: true, nomen: true, regexp: true, bitwise: true, browser: true, forin: true, maxerr: 100, indent: 4 */ + +fluid.registerNamespace("fluid.tests"); + +(function ($) { + $(document).ready(function () { + + fluid.registerNamespace("fluid.tests"); + + var errorPanelTests = new jqUnit.TestCase("Error Panel Tests"); + + errorPanelTests.asyncTest("Basic functioning", function () { + var panel = fluid.errorPanel(".panel1", { + listeners: { + onReady: function (that) { + jqUnit.notVisible("Initially, error panel should be hidden", ".panel1"); + jqUnit.exists("Error panel should populated with template", ".panel1 .flc-errorPanel-message"); + var testStrings = ["string1", "string2"]; + var expectedString = fluid.stringTemplate(panel.options.strings.messageTemplate, testStrings); + panel.show(testStrings); + jqUnit.isVisible("After show, error panel should be visible", ".panel1"); + jqUnit.assertEquals("Panel should contain correct error message", expectedString, $(".panel1 .flc-errorPanel-message").text()); + jqUnit.assertEquals("Dismiss should have correct label", that.options.strings.dismissLabel, $(".panel1 .flc-errorPanel-dismissButton-text").text()); + jqUnit.assertEquals("Retry should have correct label", that.options.strings.retryLabel, $(".panel1 .flc-errorPanel-retryButton-text").text()); + + panel.hide(); + jqUnit.notVisible("After hide, error panel should not be visible", ".panel1"); + + testStrings = ["string3", "string4"]; + expectedString = fluid.stringTemplate(panel.options.strings.messageTemplate, testStrings); + panel.show(testStrings); + jqUnit.isVisible("After show with new values, error panel should be visible", ".panel1"); + jqUnit.assertEquals("Panel should contain correct error message", expectedString, $(".panel1 .flc-errorPanel-message").text()); + + panel.show(); + jqUnit.assertEquals("After show with no values, panel should contain correct error message", panel.options.strings.messageTemplate, $(".panel1 .flc-errorPanel-message").text()); + start(); + } + } + }); + }); + + errorPanelTests.asyncTest("Custom string template", function () { + var panel = fluid.errorPanel(".panel1", { + strings: { + messageTemplate: "This template has %0 configurable %1" + }, + listeners: { + onReady: function (that) { + var testStrings = ["two", "things"]; + var expectedString = "This template has two configurable things"; + panel.show(testStrings); + jqUnit.assertEquals("Panel should contain correct error message", expectedString, $(".panel1 .flc-errorPanel-message").text()); + start(); + } + } + }); + }); + + fluid.tests.retryCallback = function () { + jqUnit.assertTrue("retry callback is called", true); + }; + errorPanelTests.asyncTest("Interactions", function () { + jqUnit.expect(3); + var panel = fluid.errorPanel(".panel0", { + listeners: { + onRetry: fluid.tests.retryCallback, + onReady: function (that) { + panel.show(); + jqUnit.isVisible("After show, error panel should be visible", ".panel0"); + + $(".panel0 .flc-errorPanel-retryButton").click(); + + $(".panel0 .flc-errorPanel-dismissButton").click(); + jqUnit.notVisible("After clicking dismiss button, error panel should not be visible", ".panel0"); + + start(); + } + } + }); + }); + + errorPanelTests.asyncTest("Custom template (no dismiss)", function () { + jqUnit.expect(2); + var panel = fluid.errorPanel(".panel0", { + templates: { + panel: { + href: "errorPanel_template_noDismiss.html" + } + }, + listeners: { + onRetry: fluid.tests.retryCallback, + onReady: function (that) { + panel.show(); + jqUnit.isVisible("After show, error panel should be visible", ".panel0"); + + $(".panel0 .flc-errorPanel-retryButton").click(); + + start(); + } + } + }); + }); + + errorPanelTests.asyncTest("Custom template (no retry)", function () { + jqUnit.expect(2); + var panel = fluid.errorPanel(".panel0", { + templates: { + panel: { + href: "errorPanel_template_noRetry.html" + } + }, + listeners: { + onReady: function (that) { + panel.show(); + jqUnit.isVisible("After show, error panel should be visible", ".panel0"); + + $(".panel0 .flc-errorPanel-dismissButton").click(); + jqUnit.notVisible("After clicking dismiss button, error panel should not be visible", ".panel0"); + + start(); + } + } + }); + }); + + errorPanelTests.asyncTest("Multiple panels", function () { + var count = 0; + var panels = []; + var testFunction = function (that) { + if (count === 3) { + fluid.each(panels, function (value, key) { + jqUnit.notVisible("Initially, error panel should be hidden", ".panel" + key); + jqUnit.exists("Error panel should populated with template", ".panel" + key + " .flc-errorPanel-message"); + }); + panels[1].show(); + jqUnit.isVisible("After showing one panel, it should be visible", ".panel1"); + jqUnit.notVisible("Other panels should not be visible", ".panel0"); + jqUnit.notVisible("Other panels should not be visible", ".panel2"); + + var testStrings = ["string1", "string2"]; + var expectedString = fluid.stringTemplate(panels[0].options.strings.messageTemplate, testStrings); + panels[0].show(testStrings); + jqUnit.assertEquals("After showing another panel with different message, first panel's message should be correct", panels[1].options.strings.messageTemplate, $(".panel1 .flc-errorPanel-message").text()); + jqUnit.assertEquals("Sirst panel's message should be correct", expectedString, $(".panel0 .flc-errorPanel-message").text()); + + start(); + } + }; + var panelCount = function (that) { + count++; + testFunction(that); + }; + for (var i = 0; i < 3; i++) { + panels[i] = fluid.errorPanel(".panel" + i, { + listeners: { + onReady: panelCount + } + }); + } + }); + + }); +})(jQuery); \ No newline at end of file diff --git a/tests/js/TestUtils.js b/tests/js/TestUtils.js index 62f5c0c..8a21b94 100644 --- a/tests/js/TestUtils.js +++ b/tests/js/TestUtils.js @@ -63,8 +63,16 @@ fluid.registerNamespace("fluid.testUtils"); }, templates: { videoPlayer: { - forceCache: true, href: "../../html/videoPlayer_template.html" + }, + videoError: { + href: "errorPanel_template.html" + }, + transcriptError: { + href: "errorPanel_template.html" + }, + captionError: { + href: "errorPanel_template.html" } } }; diff --git a/tests/js/VideoPlayerAriaTests.js b/tests/js/VideoPlayerAriaTests.js index 4e0d5e9..6e3040c 100644 --- a/tests/js/VideoPlayerAriaTests.js +++ b/tests/js/VideoPlayerAriaTests.js @@ -23,64 +23,6 @@ https://github.com/fluid-project/infusion/raw/master/Infusion-LICENSE.txt var videoPlayerARIATests = new jqUnit.TestCase("Video Player ARIA Tests"); - var baseOpts = { - video: { - sources: [ - { - src: "TestVideo.mp4", - type: "video/mp4" - }, - { - src: "../../demos/videos/ReorganizeFuture/ReorganizeFuture.webm", - type: "video/webm" - } - ], - captions: [ - { - src: "TestCaptions.en.vtt", - type: "text/vtt", - srclang: "en", - label: "English" - }, - { - src: "TestCaptions.fr.vtt", - type: "text/vtt", - srclang: "fr", - label: "French" - } - ], - transcripts: [ - { - src: "TestTranscripts.en.json", - type: "JSONcc", - srclang: "en", - label: "English" - }, - { - src: "TestTranscripts.fr.json", - type: "JSONcc", - srclang: "fr", - label: "French" - } - ] - }, - templates: { - videoPlayer: { - forceCache: true, - href: "../../html/videoPlayer_template.html" - } - } - }; - - var initVideoPlayer = function () { - var opts = fluid.copy(baseOpts); - var container = arguments[0]; - for (var index = 1; index < arguments.length; index++) { - $.extend(true, opts, arguments[index]); - } - return fluid.videoPlayer(container, opts); - }; - fluid.tests.videoPlayer.checkAriaControlsAttr = function (controlsToTest) { fluid.each(controlsToTest, function (spec, index) { jqUnit.expect(2); @@ -137,7 +79,7 @@ https://github.com/fluid-project/infusion/raw/master/Infusion-LICENSE.txt onTranscriptsLoaded: "fluid.tests.videoPlayer.testAriaControlsAttrs" } }; - initVideoPlayer($(".videoPlayer-aria"), testOpts); + fluid.testUtils.initVideoPlayer($(".videoPlayer-aria"), testOpts); }); }); diff --git a/tests/js/VideoPlayerErrorsTests.js b/tests/js/VideoPlayerErrorsTests.js new file mode 100644 index 0000000..e55dde1 --- /dev/null +++ b/tests/js/VideoPlayerErrorsTests.js @@ -0,0 +1,241 @@ +/* +Copyright 2012 OCAD University + +Licensed under the Educational Community License (ECL), Version 2.0 or the New +BSD license. You may not use this file except in compliance with one these +Licenses. + +You may obtain a copy of the ECL 2.0 License and BSD License at +https://github.com/fluid-project/infusion/raw/master/Infusion-LICENSE.txt + +*/ + +// Declare dependencies +/*global fluid, jqUnit, jQuery, start*/ + +// JSLint options +/*jslint white: true, funcinvoke: true, undef: true, newcap: true, nomen: true, regexp: true, bitwise: true, browser: true, forin: true, maxerr: 100, indent: 4 */ + +(function ($) { + $(document).ready(function () { + + fluid.registerNamespace("fluid.tests"); + + var testsCompleted; + var setup = function () { + testsCompleted = false; + }; + + var timeoutId; + var videoPlayerErrorsTests = new jqUnit.TestCase("Video Player Error Handling Tests", setup); + + var captionItemSelector = ".flc-videoPlayer-captionControls-container .flc-videoPlayer-language"; + var transcriptItemSelector = ".flc-videoPlayer-transcriptControls-container .flc-videoPlayer-language"; + + fluid.tests.testRetryCallback = function () { + jqUnit.assertTrue("Retrycallback is called", true); + testsCompleted = true; + clearTimeout(timeoutId); + start(); + }; + + /* Using custom baseOpts here because we specifically don't want the default base opts */ + var baseOpts = { + video: { + sources: [ + { + src: "../../demos/videos/ReorganizeFuture/ReorganizeFuture.webm", + type: "video/webm" + } + ] + }, + templates: { + videoPlayer: { + forceCache: true, + href: "../../html/videoPlayer_template.html" + }, + videoError: { + href: "../../html/videoError_template.html" + }, + transcriptError: { + href: "../../html/transcriptError_template.html" + }, + captionError: { + href: "../../html/captionError_template.html" + } + }, + model: { + currentTracks: { + transcripts: [0] + } + }, + components: { + media: { + options: { + components: { + videoError: { + options: { + listeners: { + onRetry: "fluid.tests.testRetryCallback" + } + } + } + } + } + } + } + }; + + fluid.tests.initVideoPlayer = function () { + var opts = fluid.copy(baseOpts); + var container = arguments[0]; + for (var index = 1; index < arguments.length; index++) { + $.extend(true, opts, arguments[index]); + } + return fluid.videoPlayer(container, opts); + }; + + fluid.tests.makeListenersForClickTriggeredTest = function (selector, errorEventName, listenerFn) { + var obj = { + onReady: function (tjat) { + $(selector).click(); + } + }; + obj[errorEventName] = { + listener: listenerFn, + priority: "last" + }; + return obj; + }; + + fluid.tests.runTestWithTimeout = function (config) { + videoPlayerErrorsTests.asyncTest(config.desc, function () { + jqUnit.expect(config.expect); + fluid.tests.initVideoPlayer($(".videoPlayer-errors"), config.opts); + timeoutId = setTimeout(function () { + if (!testsCompleted) { + jqUnit.assertFalse("Expected error event or callback didn't fire", true); + start(); + } + }, 5000); + }); + }; + + fluid.tests.runTestWithTimeout({ + desc: "Video load error", + expect: 3, + opts: { + video: { + sources: [ + { + src: "bad.video.webm", + type: "video/webm" + } + ] + }, + listeners: { + onMediaLoadError: { + listener: function (that) { + jqUnit.assertTrue("Error event fires", true); + jqUnit.assertTrue("Error message is displayed", $(".flc-videoPlayer-videoError .flc-errorPanel-message").text().length > 0); + // trigger the retry callback + $(".flc-videoPlayer-videoError .flc-errorPanel-retryButton").click(); + }, + priority: "last" + } + } + } + }); + + var testTranscriptLoadError = function (that) { + jqUnit.isVisible("Transcript are should be visible", $(".flc-videoPlayer-transcriptArea")); + jqUnit.isVisible("Transcript are should container error message", $(".flc-videoPlayer-transcriptArea .flc-videoPlayer-transcriptError")); + jqUnit.notVisible("Transcript are should not container transcript text", $(".flc-videoPlayer-transcriptArea .flc-videoPlayer-transcript-text")); + testsCompleted = true; + clearTimeout(timeoutId); + start(); + }; + + fluid.tests.runTestWithTimeout({ + desc: "Transcript (amara) load error", + expect: 3, + opts: { + video: { + transcripts: [ + { + src: "bad.amara.url", + type: "text/amarajson", + srclang: "en", + label: "English" + } + ] + }, + listeners: fluid.tests.makeListenersForClickTriggeredTest(transcriptItemSelector, "onTranscriptLoadError", testTranscriptLoadError) + } + }); + + fluid.tests.runTestWithTimeout({ + desc: "Transcript (non-amara) load error", + expect: 3, + opts: { + video: { + transcripts: [ + { + src: "bad.json.url", + type: "JSONcc", + srclang: "en", + label: "English" + } + ] + }, + listeners: fluid.tests.makeListenersForClickTriggeredTest(transcriptItemSelector, "onTranscriptLoadError", testTranscriptLoadError) + } + }); + + var testCaptionLoadError = function (that) { + jqUnit.isVisible("Caption error message should be visible", $(".flc-videoPlayer-captionArea .flc-videoPlayer-captionError")); + $(".flc-videoPlayer-captionArea .flc-videoPlayer-captionError .flc-errorPanel-dismissButton").click(); + jqUnit.notVisible("After dismiss, caption error message should not be visible", $(".flc-videoPlayer-captionArea .flc-videoPlayer-captionError")); + testsCompleted = true; + clearTimeout(timeoutId); + start(); + }; + + fluid.tests.runTestWithTimeout({ + desc: "Caption (amara) load error", + expect: 2, + opts: { + video: { + captions: [ + { + src: "bad.amara.url", + type: "text/amarajson", + srclang: "en", + label: "English" + } + ] + }, + listeners: fluid.tests.makeListenersForClickTriggeredTest(captionItemSelector, "onCaptionLoadError", testCaptionLoadError) + } + }); + + fluid.tests.runTestWithTimeout({ + desc: "Caption (non-amara) load error", + expect: 2, + opts: { + video: { + captions: [ + { + src: "bad.vtt.url", + type: "text/vtt", + srclang: "en", + label: "English" + } + ] + }, + listeners: fluid.tests.makeListenersForClickTriggeredTest(captionItemSelector, "onCaptionLoadError", testCaptionLoadError) + } + }); + + }); +})(jQuery); diff --git a/tests/js/VideoPlayerHTML5CaptionatorTests.js b/tests/js/VideoPlayerHTML5CaptionatorTests.js index 787e599..d0656b8 100644 --- a/tests/js/VideoPlayerHTML5CaptionatorTests.js +++ b/tests/js/VideoPlayerHTML5CaptionatorTests.js @@ -87,6 +87,15 @@ https://github.com/fluid-project/infusion/raw/master/Infusion-LICENSE.txt templates: { videoPlayer: { href: "../../html/videoPlayer_template.html" + }, + videoError: { + href: "errorPanel_template.html" + }, + transcriptError: { + href: "errorPanel_template.html" + }, + captionError: { + href: "errorPanel_template.html" } } }; diff --git a/tests/js/VideoPlayerIntegrationTests.js b/tests/js/VideoPlayerIntegrationTests.js index 3aa43f4..c314dad 100644 --- a/tests/js/VideoPlayerIntegrationTests.js +++ b/tests/js/VideoPlayerIntegrationTests.js @@ -135,7 +135,7 @@ https://github.com/fluid-project/infusion/raw/master/Infusion-LICENSE.txt videoPlayerIntegrationTests.asyncTest("Switch transcript language buttons", function () { jqUnit.expect(3); - var initialTranscriptText; + var initialTranscriptText = null; var testedTranscriptSpanClick = false; fluid.videoPlayer.testTranscript = function (that) { @@ -153,7 +153,7 @@ https://github.com/fluid-project/infusion/raw/master/Infusion-LICENSE.txt // Depending on the connection with universal subtitle site, the test below may not get run with remote universal subtitle transcript files. if (!initialTranscriptText) { initialTranscriptText = transcriptTextArea.text(); - jqUnit.assertNotNull("The transcript text is filled in", initialTranscriptText); + jqUnit.assertNotNull("Initially, the transcript text is filled in", initialTranscriptText); } else { jqUnit.assertNotEquals("The transcript text is switched", transcriptTextArea.text(), initialTranscriptText); } diff --git a/tests/js/VideoPlayerTranscriptTests.js b/tests/js/VideoPlayerTranscriptTests.js index 440578d..a74911c 100644 --- a/tests/js/VideoPlayerTranscriptTests.js +++ b/tests/js/VideoPlayerTranscriptTests.js @@ -27,6 +27,17 @@ https://github.com/fluid-project/infusion/raw/master/Infusion-LICENSE.txt currentTracks: { transcripts: [0] } + }, + components: { + transcriptError: { + options: { + templates: { + panel: { + href: "errorPanel_template_noRetry.html" + } + } + } + } } }; @@ -195,5 +206,42 @@ https://github.com/fluid-project/infusion/raw/master/Infusion-LICENSE.txt }; var that = initTranscript(localTranscriptOpts, testOpts); }); + + var badDefaultTranscriptOpts = { + transcripts: [ + { + src: "bad.file", + type: "JSONcc", + srclang: "en", + label: "English" + }, + { + src: "TestTranscripts.fr.json", + type: "JSONcc", + srclang: "fr", + label: "French" + } + ] + }; + videoPlayerTranscriptTests.asyncTest("Load error", function () { + var testOpts = { + listeners: { + onReady: function (that) { + // show transcripts + that.applier.requestChange("displayTranscripts", true); + }, + onTranscriptLoadError: { + listener: function () { + jqUnit.isVisible("Initally, error message should be visible", $(".flc-videoPlayer-transcriptError")); + jqUnit.notVisible("Transcript text is hidden", $(".flc-videoPlayer-transcript-text")); + start(); + }, + priority: "last" + } + } + }; + initTranscript(badDefaultTranscriptOpts, testOpts); + }); + }); })(jQuery);