',{
'class' : 'able-tooltip',
'id' : tooltipId
}).hide();
$newButton.on('mouseenter focus',function(e) {
var label = $(this).attr('aria-label');
// get position of this button
var position = $(this).position();
var buttonHeight = $(this).height();
var buttonWidth = $(this).width();
var tooltipY = position.top - buttonHeight - 5;
var tooltipX = 0;
var tooltipStyle = {
left: '',
right: tooltipX + 'px',
top: tooltipY + 'px'
};
var tooltip = AblePlayer.localGetElementById($newButton[0], tooltipId).text(label).css(tooltipStyle);
thisObj.showTooltip(tooltip);
$(this).on('mouseleave blur',function() {
AblePlayer.localGetElementById($newButton[0], tooltipId).text('').hide();
});
});
// setup popup menu
$popup = this.setupPopups(windowName); // 'transcript-window' or 'sign-window'
// define vars and assemble all the parts
if (which === 'transcript') {
this.$transcriptAlert = $windowAlert;
this.$transcriptPopupButton = $newButton;
this.$transcriptPopup = $popup;
this.$transcriptToolbar.append($windowAlert,$newButton,$tooltip,$popup);
}
else if (which === 'sign') {
this.$signAlert = $windowAlert;
this.$signPopupButton = $newButton;
this.$signPopup = $popup;
this.$signToolbar.append($windowAlert,$newButton,$tooltip,$popup);
}
// handle button click
$newButton.on('click mousedown keydown',function(e) {
e.stopPropagation();
if (!thisObj.windowMenuClickRegistered && !thisObj.finishingDrag) {
// don't set windowMenuClickRegistered yet; that happens in handler function
thisObj.handleWindowButtonClick(which, e);
}
thisObj.finishingDrag = false;
});
this.addResizeDialog(which, $window);
};
AblePlayer.prototype.addResizeDialog = function (which, $window) {
var thisObj, $windowPopup, $windowButton,
widthId, heightId, startingWidth, startingHeight, aspectRatio,
$resizeForm, $resizeWrapper,
$resizeWidthDiv, $resizeWidthInput, $resizeWidthLabel,
$resizeHeightDiv, $resizeHeightInput, $resizeHeightLabel,
tempWidth, tempHeight,
$saveButton, $cancelButton, newWidth, newHeight, resizeDialog;
thisObj = this;
if (which === 'transcript') {
$windowPopup = this.$transcriptPopup;
$windowButton = this.$transcriptPopupButton;
}
else if (which === 'sign') {
$windowPopup = this.$signPopup;
$windowButton = this.$signPopupButton;
}
widthId = this.mediaId + '-resize-' + which + '-width';
heightId = this.mediaId + '-resize-' + which + '-height';
startingWidth = $window.width();
startingHeight = $window.height();
aspectRatio = startingWidth / startingHeight;
$resizeForm = $('
',{
'class' : 'able-resize-form'
});
// inner container for all content, will be assigned to modal div's aria-describedby
$resizeWrapper = $('
');
// width field
$resizeWidthDiv = $('
');
$resizeWidthInput = $('
',{
'type': 'text',
'id': widthId,
'value': startingWidth
});
$resizeWidthLabel = $('
',{
'for': widthId
}).text(this.tt.width);
// height field
$resizeHeightDiv = $('
');
$resizeHeightInput = $(' ',{
'type': 'text',
'id': heightId,
'value': startingHeight
});
$resizeHeightLabel = $('',{
'for': heightId
}).text(this.tt.height);
if (which === 'sign') {
// make height a read-only field
// and calculate its value based on width to preserve aspect ratio
$resizeHeightInput.prop('readonly',true);
$resizeWidthInput.on('input',function() {
tempWidth = $(this).val();
tempHeight = Math.round(tempWidth/aspectRatio, 0);
$resizeHeightInput.val(tempHeight);
})
}
// Add save and cancel buttons.
$saveButton = $('' + this.tt.save + ' ');
$cancelButton = $('' + this.tt.cancel + ' ');
$saveButton.on('click',function () {
newWidth = $('#' + widthId).val();
newHeight = $('#' + heightId).val();
if (newWidth !== startingWidth || newHeight !== startingHeight) {
$window.css({
'width': newWidth + 'px',
'height': newHeight + 'px'
});
thisObj.updateCookie(which);
}
resizeDialog.hide();
$windowPopup.hide();
$windowButton.focus();
});
$cancelButton.on('click',function () {
resizeDialog.hide();
$windowPopup.hide();
$windowButton.focus();
});
// Now assemble all the parts
$resizeWidthDiv.append($resizeWidthLabel,$resizeWidthInput);
$resizeHeightDiv.append($resizeHeightLabel,$resizeHeightInput);
$resizeWrapper.append($resizeWidthDiv,$resizeHeightDiv);
$resizeForm.append($resizeWrapper,' ',$saveButton,$cancelButton);
// must be appended to the BODY!
// otherwise when aria-hidden="true" is applied to all background content
// that will include an ancestor of the dialog,
// which will render the dialog unreadable by screen readers
$('body').append($resizeForm);
resizeDialog = new AccessibleDialog($resizeForm, $windowButton, 'alert', this.tt.windowResizeHeading, $resizeWrapper, this.tt.closeButtonLabel, '20em');
if (which === 'transcript') {
this.transcriptResizeDialog = resizeDialog;
}
else if (which === 'sign') {
this.signResizeDialog = resizeDialog;
}
};
AblePlayer.prototype.handleWindowButtonClick = function (which, e) {
var thisObj, $windowPopup, $windowButton, $toolbar, popupTop;
thisObj = this;
if (which === 'transcript') {
$windowPopup = this.$transcriptPopup;
$windowButton = this.$transcriptPopupButton;
$toolbar = this.$transcriptToolbar;
}
else if (which === 'sign') {
$windowPopup = this.$signPopup;
$windowButton = this.$signPopupButton;
$toolbar = this.$signToolbar;
}
if (e.type === 'keydown') {
// user pressed a key
if (e.which === 32 || e.which === 13) {
// this was Enter or space
this.windowMenuClickRegistered = true;
}
else if (e.which === 27) { // escape
// hide the popup menu
$windowPopup.hide('fast', function() {
// also reset the Boolean
thisObj.windowMenuClickRegistered = false;
// also restore menu items to their original state
$windowPopup.find('li').removeClass('able-focus').attr('tabindex','-1');
// also return focus to window options button
$windowButton.focus();
});
}
else {
return false;
}
}
else {
// this was a mouse event
this.windowMenuClickRegistered = true;
}
if ($windowPopup.is(':visible')) {
$windowPopup.hide(200,'',function() {
thisObj.windowMenuClickRegistered = false; // reset
});
$windowPopup.find('li').removeClass('able-focus');
$windowButton.attr('aria-expanded','false').focus();
}
else {
// first, be sure window is on top
this.updateZIndex(which);
popupTop = $windowButton.position().top + $windowButton.outerHeight();
$windowPopup.css('top', popupTop);
$windowPopup.show(200,'',function() {
$windowButton.attr('aria-expanded','true');
$(this).find('li').first().focus().addClass('able-focus');
thisObj.windowMenuClickRegistered = false; // reset
});
}
};
AblePlayer.prototype.handleMenuChoice = function (which, choice, e) {
var thisObj, $window, $windowPopup, $windowButton, resizeDialog, $thisRadio;
thisObj = this;
if (which === 'transcript') {
$window = this.$transcriptArea;
$windowPopup = this.$transcriptPopup;
$windowButton = this.$transcriptPopupButton;
resizeDialog = this.transcriptResizeDialog;
}
else if (which === 'sign') {
$window = this.$signWindow;
$windowPopup = this.$signPopup;
$windowButton = this.$signPopupButton;
resizeDialog = this.signResizeDialog;
}
if (e.type === 'keydown') {
if (e.which === 27) { // escape
// hide the popup menu
$windowPopup.hide('fast', function() {
// also reset the Boolean
thisObj.windowMenuClickRegistered = false;
// also restore menu items to their original state
$windowPopup.find('li').removeClass('able-focus').attr('tabindex','-1');
// also return focus to window options button
$windowButton.focus();
});
return false;
}
else {
// all other keys will be handled by upstream functions
return false;
}
}
// hide the popup menu
$windowPopup.hide('fast', function() {
// also reset the boolean
thisObj.windowMenuClickRegistered = false;
// also restore menu items to their original state
$windowPopup.find('li').removeClass('able-focus').attr('tabindex','-1');
});
if (choice !== 'close') {
$windowButton.focus();
}
if (choice === 'move') {
if (!this.showedAlert(which)) {
this.showAlert(this.tt.windowMoveAlert,which);
if (which === 'transcript') {
this.showedTranscriptAlert = true;
}
else if (which === 'sign') {
this.showedSignAlert = true;
}
}
if (e.type === 'keydown') {
this.dragDevice = 'keyboard';
}
else {
this.dragDevice = 'mouse';
}
this.startDrag(which, $window);
$windowPopup.hide().parent().focus();
}
else if (choice == 'resize') {
// resize through the menu uses a form, not drag
resizeDialog.show();
}
else if (choice == 'close') {
// close window, place focus on corresponding button on controller bar
if (which === 'transcript') {
this.handleTranscriptToggle();
}
else if (which === 'sign') {
this.handleSignToggle();
}
}
};
AblePlayer.prototype.startDrag = function(which, $element) {
var thisObj, $windowPopup, zIndex, startPos, newX, newY;
thisObj = this;
this.$activeWindow = $element;
this.dragging = true;
if (which === 'transcript') {
$windowPopup = this.$transcriptPopup;
}
else if (which === 'sign') {
$windowPopup = this.$signPopup;
}
if (!this.showedAlert(which)) {
this.showAlert(this.tt.windowMoveAlert,which);
if (which === 'transcript') {
this.showedTranscriptAlert = true;
}
else if (which === 'sign') {
this.showedSignAlert = true;
}
}
// if window's popup menu is open, close it
if ($windowPopup.is(':visible')) {
$windowPopup.hide();
}
// be sure this window is on top
this.updateZIndex(which);
// get starting position of element
startPos = this.$activeWindow.position();
this.dragStartX = startPos.left;
this.dragStartY = startPos.top;
if (typeof this.startMouseX === 'undefined') {
this.dragDevice = 'keyboard';
this.dragKeyX = this.dragStartX;
this.dragKeyY = this.dragStartY;
// add stopgap to prevent the Enter that triggered startDrag() from also triggering dragEnd()
this.startingDrag = true;
}
else {
this.dragDevice = 'mouse';
// get offset between mouse position and top left corner of draggable element
this.dragOffsetX = this.startMouseX - this.dragStartX;
this.dragOffsetY = this.startMouseY - this.dragStartY;
}
// prepare element for dragging
this.$activeWindow.addClass('able-drag').css({
'position': 'absolute',
'top': this.dragStartY + 'px',
'left': this.dragStartX + 'px'
}).focus();
// add device-specific event listeners
if (this.dragDevice === 'mouse') {
$(document).on('mousemove',function(e) {
if (thisObj.dragging) {
// calculate new top left based on current mouse position - offset
newX = e.pageX - thisObj.dragOffsetX;
newY = e.pageY - thisObj.dragOffsetY;
thisObj.resetDraggedObject( newX, newY );
}
});
}
else if (this.dragDevice === 'keyboard') {
this.$activeWindow.on('keydown',function(e) {
if (thisObj.dragging) {
thisObj.dragKeys(which, e);
}
});
}
return false;
};
AblePlayer.prototype.dragKeys = function(which, e) {
var key, keySpeed;
var thisObj = this;
// stopgap to prevent firing on initial Enter or space
// that selected "Move" from menu
if (this.startingDrag) {
this.startingDrag = false;
return false;
}
key = e.which;
keySpeed = 10; // pixels per keypress event
switch (key) {
case 37: // left
case 63234:
this.dragKeyX -= keySpeed;
break;
case 38: // up
case 63232:
this.dragKeyY -= keySpeed;
break;
case 39: // right
case 63235:
this.dragKeyX += keySpeed;
break;
case 40: // down
case 63233:
this.dragKeyY += keySpeed;
break;
case 13: // enter
case 27: // escape
this.endDrag(which);
return false;
default:
return false;
}
this.resetDraggedObject(this.dragKeyX,this.dragKeyY);
if (e.preventDefault) {
e.preventDefault();
}
return false;
};
AblePlayer.prototype.resetDraggedObject = function ( x, y) {
this.$activeWindow.css({
'left': x + 'px',
'top': y + 'px'
});
},
AblePlayer.prototype.resizeObject = function ( which, width, height ) {
var innerHeight;
// which is either 'transcript' or 'sign'
this.$activeWindow.css({
'width': width + 'px',
'height': height + 'px'
});
if (which === 'transcript') {
// $activeWindow is the outer $transcriptArea
// but the inner able-transcript also needs to be resized proporitionally
// (it's 50px less than its outer container)
innerHeight = height - 50;
this.$transcriptDiv.css('height', innerHeight + 'px');
}
};
AblePlayer.prototype.endDrag = function(which) {
var $window, $windowPopup, $windowButton;
if (which === 'transcript') {
$windowPopup = this.$transcriptPopup;
$windowButton = this.$transcriptPopupButton;
}
else if (which === 'sign') {
$windowPopup = this.$signPopup;
$windowButton = this.$signPopupButton;
}
$(document).off('mousemove mouseup');
this.$activeWindow.off('keydown').removeClass('able-drag');
if (this.dragDevice === 'keyboard') {
$windowButton.focus();
}
this.dragging = false;
// save final position of dragged element
this.updateCookie(which);
// reset starting mouse positions
this.startMouseX = undefined;
this.startMouseY = undefined;
// Boolean to stop stray events from firing
this.windowMenuClickRegistered = false;
this.finishingDrag = true; // will be reset after window click event
// finishingDrag should e reset after window click event,
// which is triggered automatically after mouseup
// However, in case that's not reliable in some browsers
// need to ensure this gets cancelled
setTimeout(function() {
this.finishingDrag = false;
}, 100);
};
AblePlayer.prototype.isCloseToCorner = function($window, mouseX, mouseY) {
// return true if mouse is close to bottom right corner (resize target)
var tolerance, position, top, left, width, height, bottom, right;
tolerance = 10; // number of pixels in both directions considered "close enough"
// first, get position of element
position = $window.offset();
top = position.top;
left = position.left;
width = $window.width();
height = $window.height();
bottom = top + height;
right = left + width;
if ((Math.abs(bottom-mouseY) <= tolerance) && (Math.abs(right-mouseX) <= tolerance)) {
return true;
}
return false;
};
AblePlayer.prototype.startResize = function(which, $element) {
var thisObj, $windowPopup, zIndex, startPos, newWidth, newHeight;
thisObj = this;
this.$activeWindow = $element;
this.resizing = true;
if (which === 'transcript') {
$windowPopup = this.$transcriptPopup;
}
else if (which === 'sign') {
$windowPopup = this.$signPopup;
}
// if window's popup menu is open, close it & place focus on button (???)
if ($windowPopup.is(':visible')) {
$windowPopup.hide().parent().focus();
}
// get starting width and height
startPos = this.$activeWindow.position();
this.dragKeyX = this.dragStartX;
this.dragKeyY = this.dragStartY;
this.dragStartWidth = this.$activeWindow.width();
this.dragStartHeight = this.$activeWindow.height();
// add event listeners
$(document).on('mousemove',function(e) {
if (thisObj.resizing) {
// calculate new width and height based on changes to mouse position
newWidth = thisObj.dragStartWidth + (e.pageX - thisObj.startMouseX);
newHeight = thisObj.dragStartHeight + (e.pageY - thisObj.startMouseY);
thisObj.resizeObject( which, newWidth, newHeight );
}
});
return false;
};
AblePlayer.prototype.endResize = function(which) {
var $window, $windowPopup, $windowButton;
if (which === 'transcript') {
$windowPopup = this.$transcriptPopup;
$windowButton = this.$transcriptPopupButton;
}
else if (which === 'sign') {
$windowPopup = this.$signPopup;
$windowButton = this.$signPopupButton;
}
$(document).off('mousemove mouseup');
this.$activeWindow.off('keydown');
$windowButton.show().focus();
this.resizing = false;
this.$activeWindow.removeClass('able-resize');
// save final width and height of dragged element
this.updateCookie(which);
// Booleans for preventing stray events
this.windowMenuClickRegistered = false;
this.finishingDrag = true;
// finishingDrag should e reset after window click event,
// which is triggered automatically after mouseup
// However, in case that's not reliable in some browsers
// need to ensure this gets cancelled
setTimeout(function() {
this.finishingDrag = false;
}, 100);
};
})(jQuery);