|
17 | 17 | var thisObj, vidcapContainer, prefsGroups, i; |
18 | 18 | thisObj = this; |
19 | 19 |
|
20 | | - // create $mediaContainer and $ableDiv and wrap them around the media element |
| 20 | + // create three wrappers and wrap them around the media element. From inner to outer: |
| 21 | + // $mediaContainer - contains the original media element |
| 22 | + // $ableDiv - contains the media player and all its objects (e.g., captions, controls, descriptions) |
| 23 | + // $ableWrapper - contains additional widgets (e.g., transcript window, sign window) |
21 | 24 | this.$mediaContainer = this.$media.wrap('<div class="able-media-container"></div>').parent(); |
22 | 25 | this.$ableDiv = this.$mediaContainer.wrap('<div class="able"></div>').parent(); |
| 26 | + this.$ableWrapper = this.$ableDiv.wrap('<div class="able-wrapper"></div>').parent(); |
23 | 27 | // width and height of this.$mediaContainer are not updated when switching to full screen |
24 | 28 | // However, I don't think they're needed at all. Commented out on 4/12/15, but |
25 | 29 | // preserved here just in case there are unanticipated problems... |
|
154 | 158 | }; |
155 | 159 |
|
156 | 160 | AblePlayer.prototype.injectTranscriptArea = function() { |
| 161 | + |
| 162 | + var thisObj = this; |
| 163 | + |
157 | 164 | this.$transcriptArea = $('<div>', { |
158 | | - 'class': 'able-transcript-area' |
| 165 | + 'class': 'able-transcript-area', |
| 166 | + 'tabindex': '-1' |
159 | 167 | }); |
160 | 168 |
|
161 | 169 | this.$transcriptToolbar = $('<div>', { |
162 | | - 'class': 'able-transcript-toolbar' |
| 170 | + 'class': 'able-window-toolbar' |
163 | 171 | }); |
164 | 172 |
|
165 | 173 | this.$transcriptDiv = $('<div>', { |
|
179 | 187 | this.$transcriptLanguageSelect.append(this.$unknownTranscriptOption); |
180 | 188 | this.$transcriptLanguageSelect.prop('disabled', true); |
181 | 189 |
|
182 | | - var floatRight = $('<div style="float: right;">'); |
183 | | - this.$transcriptLanguageSelectContainer = floatRight; |
| 190 | + var languageSelectWrapper = $('<div class="transcript-language-select-wrapper">'); |
| 191 | + this.$transcriptLanguageSelectContainer = languageSelectWrapper; |
184 | 192 |
|
185 | | - floatRight.append($('<label for="transcript-language-select">' + this.tt.language + ': </label>'), this.$transcriptLanguageSelect); |
186 | | - this.$transcriptToolbar.append(floatRight); |
| 193 | + languageSelectWrapper.append($('<label for="transcript-language-select">' + this.tt.language + ': </label>'), this.$transcriptLanguageSelect); |
| 194 | + this.$transcriptToolbar.append(languageSelectWrapper); |
187 | 195 |
|
188 | 196 | this.$transcriptArea.append(this.$transcriptToolbar, this.$transcriptDiv); |
189 | 197 |
|
190 | | - // If client has provided separate transcript location, put it there instead. |
| 198 | + // If client has provided separate transcript location, put it there. |
| 199 | + // Otherwise append it to the body |
191 | 200 | if (this.transcriptDivLocation) { |
192 | 201 | $('#' + this.transcriptDivLocation).append(this.$transcriptArea); |
193 | 202 | } |
194 | | - else if (this.$ableColumnRight) { |
195 | | - this.$ableColumnRight.prepend(this.$transcriptArea); |
196 | | - } |
197 | 203 | else { |
198 | | - this.splitPlayerIntoColumns('transcript'); |
| 204 | + this.$ableWrapper.append(this.$transcriptArea); |
| 205 | + } |
| 206 | + |
| 207 | + // make it draggable (popup only; NOT external transcript) |
| 208 | + if (!this.transcriptDivLocation) { |
| 209 | + this.initDragDrop('transcript'); |
| 210 | + if (this.prefTranscript === 1) { |
| 211 | + // transcript is on. Go ahead and position it |
| 212 | + this.positionDraggableWindow('transcript',this.getDefaultWidth('transcript')); |
| 213 | + } |
199 | 214 | } |
200 | 215 |
|
201 | 216 | // If client has provided separate transcript location, override user's preference for hiding transcript |
|
204 | 219 | } |
205 | 220 | }; |
206 | 221 |
|
| 222 | + AblePlayer.prototype.getDefaultWidth = function(which) { |
| 223 | + |
| 224 | + // return default width of resizable elements |
| 225 | + // these values are somewhat arbitrary, but seem to result in good usability |
| 226 | + // if users disagree, they can resize (and resposition) them |
| 227 | + if (which === 'transcript') { |
| 228 | + return 450; |
| 229 | + } |
| 230 | + else if (which === 'sign') { |
| 231 | + return 400; |
| 232 | + } |
| 233 | + }; |
| 234 | + |
207 | 235 | AblePlayer.prototype.populateChaptersDiv = function() { |
208 | 236 |
|
209 | 237 | var thisObj, headingLevel, headingType, headingId, $chaptersHeading, |
|
294 | 322 | this.$chaptersDiv.append($chaptersNav); |
295 | 323 | }; |
296 | 324 |
|
297 | | - AblePlayer.prototype.splitPlayerIntoColumns = function (feature) { |
298 | | - // feature is either 'transcript' or 'sign' |
299 | | - // if present, player is split into two column, with this feature in the right column |
300 | | - this.$ableColumnLeft = this.$ableDiv.wrap('<div class="able-column-left">').parent(); |
301 | | - this.$ableColumnLeft.width(this.playerWidth); |
302 | | - if (feature === 'transcript') { |
303 | | - this.$transcriptArea.insertAfter(this.$ableColumnLeft); |
304 | | - this.$ableColumnRight = this.$transcriptArea.wrap('<div class="able-column-right">').parent(); |
| 325 | + AblePlayer.prototype.positionDraggableWindow = function (which, width) { |
| 326 | + |
| 327 | + // which is either 'transcript' or 'sign' |
| 328 | + |
| 329 | + var cookie, cookiePos, $window, dragged, windowPos, currentWindowPos, firstTime; |
| 330 | + cookie = this.getCookie(); |
| 331 | + if (which === 'transcript') { |
| 332 | + $window = this.$transcriptArea; |
| 333 | + if (typeof cookie.transcript !== 'undefined') { |
| 334 | + cookiePos = cookie.transcript; |
| 335 | + } |
| 336 | + } |
| 337 | + else if (which === 'sign') { |
| 338 | + $window = this.$signWindow; |
| 339 | + if (typeof cookie.transcript !== 'undefined') { |
| 340 | + cookiePos = cookie.sign; |
| 341 | + } |
| 342 | + } |
| 343 | + if (typeof cookiePos !== 'undefined' && !($.isEmptyObject(cookiePos))) { |
| 344 | + // position window using stored values from cookie |
| 345 | + $window.css({ |
| 346 | + 'position': cookiePos['position'], |
| 347 | + 'width': cookiePos['width'], |
| 348 | + 'z-index': cookiePos['zindex'] |
| 349 | + }); |
| 350 | + if (cookiePos['position'] === 'absolute') { |
| 351 | + $window.css({ |
| 352 | + 'top': cookiePos['top'], |
| 353 | + 'left': cookiePos['left'] |
| 354 | + }); |
| 355 | + } |
| 356 | + // since cookie is not page-specific, z-index needs may vary across different pages |
| 357 | + this.updateZIndex(which); |
305 | 358 | } |
306 | | - else if (feature == 'sign') { |
307 | | - this.$signArea.insertAfter(this.$ableColumnLeft); |
308 | | - this.$ableColumnRight = this.$signArea.wrap('<div class="able-column-right">').parent(); |
| 359 | + else { |
| 360 | + // position window using default values |
| 361 | + windowPos = this.getOptimumPosition(which, width); |
| 362 | + if (typeof width === 'undefined') { |
| 363 | + width = this.getDefaultWidth(which); |
| 364 | + } |
| 365 | + $window.css({ |
| 366 | + 'position': windowPos[0], |
| 367 | + 'width': width, |
| 368 | + 'z-index': windowPos[1] |
| 369 | + }); |
| 370 | + if (windowPos[0] === 'absolute') { |
| 371 | + $window.css({ |
| 372 | + 'top': windowPos[2] + 'px', |
| 373 | + 'left': windowPos[3] + 'px', |
| 374 | + }); |
| 375 | + } |
309 | 376 | } |
310 | | - this.$ableColumnRight.width(this.playerWidth); |
311 | 377 | }; |
312 | 378 |
|
313 | | - AblePlayer.prototype.injectPoster = function ($element) { |
314 | | - |
315 | | - // get poster attribute from media element and append that as an img to $element |
316 | | - // currently only applies to YouTube and fallback |
317 | | - var poster; |
318 | | - |
319 | | - if (this.$media.attr('poster')) { |
320 | | - poster = this.$media.attr('poster'); |
321 | | - this.$posterImg = $('<img>',{ |
322 | | - 'class': 'able-poster', |
323 | | - 'src' : poster, |
324 | | - 'alt' : "", |
325 | | - 'role': "presentation", |
326 | | - 'width': this.playerWidth, |
327 | | - 'height': this.playerHeight |
328 | | - }); |
329 | | - $element.append(this.$posterImg); |
| 379 | + AblePlayer.prototype.getOptimumPosition = function (targetWindow, targetWidth) { |
| 380 | + |
| 381 | + // returns optimum position for targetWindow, as an array with the following structure: |
| 382 | + // 0 - CSS position ('absolute' or 'relative') |
| 383 | + // 1 - zindex |
| 384 | + // 2 - top |
| 385 | + // 3 - left |
| 386 | + // targetWindow is either 'transcript' or 'sign' |
| 387 | + // if there is room to the right of the player, position element there |
| 388 | + // else if there is room the left of the player, position element there |
| 389 | + // else position element beneath player |
| 390 | + |
| 391 | + var gap, position, ableWidth, ableHeight, ableOffset, ableTop, ableLeft, |
| 392 | + windowWidth, otherWindowWidth, zIndex; |
| 393 | + |
| 394 | + if (typeof targetWidth === 'undefined') { |
| 395 | + targetWidth = this.getDefaultWidth(targetWindow); |
330 | 396 | } |
331 | | - } |
| 397 | + |
| 398 | + gap = 5; // number of pixels to preserve between Able Player objects |
| 399 | + |
| 400 | + position = []; // position, top, left |
| 401 | + |
| 402 | + ableWidth = this.$ableDiv.width(); |
| 403 | + ableHeight = this.$ableDiv.height(); |
| 404 | + ableOffset = this.$ableDiv.offset(); |
| 405 | + ableTop = ableOffset.top; |
| 406 | + ableLeft = ableOffset.left; |
| 407 | + windowWidth = $(window).width(); |
| 408 | + otherWindowWidth = 0; // width of other visiable draggable windows will be added to this |
| 409 | + |
| 410 | + // get optimum zIndex for this window |
| 411 | + zIndex = this.getHighestZIndex(targetWindow); |
| 412 | + |
| 413 | + if (targetWindow === 'transcript') { |
| 414 | + if (typeof this.$signWindow !== 'undefined') { |
| 415 | + if (this.$signWindow.is(':visible')) { |
| 416 | + otherWindowWidth = this.$signWindow.width() + gap; |
| 417 | + } |
| 418 | + } |
| 419 | + } |
| 420 | + else if (targetWindow === 'sign') { |
| 421 | + if (typeof this.$transcriptArea !== 'undefined') { |
| 422 | + if (this.$transcriptArea.is(':visible')) { |
| 423 | + otherWindowWidth = this.$transcriptArea.width() + gap; |
| 424 | + } |
| 425 | + } |
| 426 | + } |
| 427 | + if (targetWidth < (windowWidth - (ableLeft + ableWidth + gap + otherWindowWidth))) { |
| 428 | + // there's room to the left of $ableDiv |
| 429 | + position[0] = 'absolute'; |
| 430 | + position[1] = zIndex; |
| 431 | + position[2] = 0; |
| 432 | + position[3] = ableWidth + otherWindowWidth + gap; |
| 433 | + } |
| 434 | + else if (targetWidth + gap < ableLeft) { |
| 435 | + // there's room to the right of $ableDiv |
| 436 | + position[0] = 'absolute'; |
| 437 | + position[1] = zIndex; |
| 438 | + position[2] = 0; |
| 439 | + position[3] = ableLeft - targetWidth - gap; |
| 440 | + } |
| 441 | + else { |
| 442 | + // position element below $ableDiv |
| 443 | + position[0] = 'relative'; |
| 444 | + // no need to define z-index, top, or left |
| 445 | + } |
| 446 | + return position; |
| 447 | + }; |
332 | 448 |
|
333 | 449 | AblePlayer.prototype.injectAlert = function () { |
334 | 450 |
|
|
421 | 537 | // Create popup div and append to player |
422 | 538 | // 'which' parameter is either 'captions', 'chapters', 'prefs', or 'X-window' (e.g., "sign-window") |
423 | 539 | AblePlayer.prototype.createPopup = function (which) { |
| 540 | + |
424 | 541 | var thisObj, $popup, $thisButton, $thisListItem, $prevButton, $nextButton, |
425 | 542 | selectedTrackIndex, selectedTrack; |
426 | 543 | thisObj = this; |
427 | 544 | $popup = $('<div>',{ |
428 | 545 | 'id': this.mediaId + '-' + which + '-menu', |
429 | 546 | 'class': 'able-popup' |
430 | 547 | }); |
431 | | - if (which == 'chapters' || which == 'prefs') { |
| 548 | + if (which === 'chapters' || which === 'prefs' || which === 'sign-window' || which === 'transcript-window') { |
432 | 549 | $popup.addClass('able-popup-no-radio'); |
433 | 550 | } |
434 | 551 | $popup.on('keydown',function (e) { |
|
844 | 961 | } |
845 | 962 |
|
846 | 963 | controlLayout['br'].push('preferences'); |
847 | | - // Help button eliminated in v2.3.4 - help text combined into Preferences dialog |
848 | | - // controlLayout['br'].push('help'); |
849 | 964 |
|
850 | 965 | // TODO: JW currently has a bug with fullscreen, anything that can be done about this? |
851 | 966 | if (this.mediaType === 'video' && this.allowFullScreen && this.player !== 'jw') { |
|
1091 | 1206 | } |
1092 | 1207 | else if (control === 'sign') { |
1093 | 1208 | this.$signButton = newButton; |
| 1209 | + // gray out sign button if sign language window is not active |
| 1210 | + if (!(this.$signWindow.is(':visible'))) { |
| 1211 | + this.$signButton.addClass('buttonOff'); |
| 1212 | + } |
1094 | 1213 | } |
1095 | 1214 | else if (control === 'descriptions') { |
1096 | 1215 | this.$descButton = newButton; |
|
1130 | 1249 | } |
1131 | 1250 |
|
1132 | 1251 | if (this.mediaType === 'video') { |
1133 | | - // As of v 2.3.4, no longer adding width and height on this.$vidCapContainer |
1134 | | - // CAN'T constrain the height if this.prefCaptionsPosition === 'below' |
1135 | | - // because the caption div below the video needs to be able to expand as needed |
1136 | | - // Checked the new setting in Firefox, Chrome, & IE and it seems to work w/o width & height |
1137 | | - /* |
1138 | | - // set width and height of div.able-vidcap-container |
1139 | | - vidcapStyles = { |
1140 | | - 'width': this.playerWidth+'px', |
1141 | | - 'height': this.playerHeight+'px' |
1142 | | - } |
1143 | | - if (this.$vidcapContainer) { |
1144 | | - this.$vidcapContainer.css(vidcapStyles); |
1145 | | - } |
1146 | | - */ |
| 1252 | + |
1147 | 1253 | if (this.$captionDiv) { |
1148 | 1254 | // set width of the captions container |
1149 | 1255 | this.$captionDiv.css('width',this.playerWidth+'px'); |
|
0 commit comments