diff --git a/src/definitions/modules/popup.js b/src/definitions/modules/popup.js index 5024cde80..f8f7da46b 100644 --- a/src/definitions/modules/popup.js +++ b/src/definitions/modules/popup.js @@ -59,6 +59,7 @@ $.fn.popup = function(parameters) { searchDepth = 0, triedPositions = false, + openedWithTouch = false, element = this, instance = $module.data(moduleNamespace), @@ -166,10 +167,24 @@ $.fn.popup = function(parameters) { clearTimeout(module.showTimer); module.hideTimer = setTimeout(module.hide, delay); }, + touchstart: function(event) { + openedWithTouch = true; + module.event.start(); + }, resize: function() { if( module.is.visible() ) { module.set.position(); } + }, + hideGracefully: function(event) { + // don't close on clicks inside popup + if(event && $(event.target).closest(selector.popup).length === 0) { + module.debug('Click occurred outside popup hiding popup'); + module.hide(); + } + else { + module.debug('Click was inside popup, keeping popup open'); + } } }, @@ -312,18 +327,6 @@ $.fn.popup = function(parameters) { }) ; }, - - hideGracefully: function(event) { - // don't close on clicks inside popup - if(event && $(event.target).closest(selector.popup).length === 0) { - module.debug('Click occurred outside popup hiding popup'); - module.hide(); - } - else { - module.debug('Click was inside popup, keeping popup open'); - } - }, - exists: function() { if(!$popup) { return false; @@ -438,15 +441,51 @@ $.fn.popup = function(parameters) { $module.removeData(metadata.variation); return $module.data(metadata.variation) || settings.variation; }, + sizes: function() { + var + targetElement = $target[0], + sizes = {} + ; + // grab position + sizes.target = (settings.inline || settings.popup) + ? $target.position() + : $target.offset() + ; + sizes = { + target: { + element : $target[0], + width : $target.outerWidth(), + height : $target.outerHeight(), + margin : {} + }, + popup: { + width: $popup.outerWidth(), + height: $popup.outerHeight() + }, + parent: { + width: $offsetParent.outerWidth(), + height: $offsetParent.outerHeight() + } + }; + + sizes.target.margin.top = (settings.inline) + ? parseInt( window.getComputedStyle(targetElement).getPropertyValue('margin-top'), 10) + : 0 + ; + sizes.target.margin.left = (settings.inline) + ? module.is.rtl() + ? parseInt( window.getComputedStyle(targetElement).getPropertyValue('margin-right'), 10) + : parseInt( window.getComputedStyle(targetElement).getPropertyValue('margin-left') , 10) + : 0 + ; + return sizes; + }, id: function() { return id; }, startEvent: function() { if(settings.on == 'hover') { - return (hasTouch && settings.addTouchEvents) - ? 'touchstart mouseenter' - : 'mouseenter' - ; + return 'mouseenter'; } else if(settings.on == 'focus') { return 'focus'; @@ -454,10 +493,7 @@ $.fn.popup = function(parameters) { return false; }, scrollEvent: function() { - return (hasTouch && settings.addTouchEvents) - ? 'touchmove scroll' - : 'scroll' - ; + return 'scroll'; }, endEvent: function() { if(settings.on == 'hover') { @@ -510,8 +546,8 @@ $.fn.popup = function(parameters) { right : screen.left + screen.width }, popup = { - width : $popup.width(), - height : $popup.height(), + width : $popup.outerWidth(), + height : $popup.outerHeight(), offset : $popup.offset() }, offstage = {}, @@ -610,7 +646,7 @@ $.fn.popup = function(parameters) { }, set: { - position: function(position, arrowOffset) { + position: function(position, sizes) { // exit conditions if($target.length === 0 || $popup.length === 0) { @@ -618,140 +654,125 @@ $.fn.popup = function(parameters) { return; } var - windowWidth = $(window).width(), - windowHeight = $(window).height(), - - targetWidth = $target.outerWidth(), - targetHeight = $target.outerHeight(), - - popupWidth = $popup.outerWidth(), - popupHeight = $popup.outerHeight(), - - parentWidth = $offsetParent.outerWidth(), - parentHeight = $offsetParent.outerHeight(), - - distanceAway = settings.distanceAway, - - targetElement = $target[0], - - marginTop = (settings.inline) - ? parseInt( window.getComputedStyle(targetElement).getPropertyValue('margin-top'), 10) - : 0, - marginLeft = (settings.inline) - ? module.is.rtl() - ? parseInt( window.getComputedStyle(targetElement).getPropertyValue('margin-right'), 10) - : parseInt( window.getComputedStyle(targetElement).getPropertyValue('margin-left') , 10) - : 0, - - target = (settings.inline || settings.popup) - ? $target.position() - : $target.offset(), - + offset, + distanceAway, + target, + popup, + parent, computedPosition, positioning, offstagePosition ; - position = position || $module.data(metadata.position) || settings.position; - arrowOffset = arrowOffset || $module.data(metadata.offset) || settings.offset; + sizes = sizes || module.get.sizes(); + position = position || $module.data(metadata.position) || settings.position; + + offset = $module.data(metadata.offset) || settings.offset; + distanceAway = settings.distanceAway; + + // shorthand + target = sizes.target; + popup = sizes.popup; + parent = sizes.parent; if(target.top === 0 && target.left === 0) { module.debug('Popup target is hidden, no action taken'); return false; } - if(searchDepth == settings.maxSearchDepth && settings.lastResort) { - module.debug('Using last resort position to display', settings.lastResort); - position = settings.lastResort; - } - if(settings.inline) { module.debug('Adding targets margin to calculation'); if(position == 'left center' || position == 'right center') { - arrowOffset += marginTop; - distanceAway += -marginLeft; + offset += target.margin.top; + distanceAway += -target.margin.left; } else if (position == 'top left' || position == 'top center' || position == 'top right') { - arrowOffset += marginLeft; - distanceAway -= marginTop; + offset += target.margin.left; + distanceAway -= target.margin.top; } else { - arrowOffset += marginLeft; - distanceAway += marginTop; + offset += target.margin.left; + distanceAway += target.margin.top; } } + module.debug('Calculating popup positioning', position); - computedPosition = position; if (module.is.rtl()) { - computedPosition = computedPosition.replace(/left|right/g, function (match) { + position = position.replace(/left|right/g, function (match) { return (match == 'left') ? 'right' : 'left' ; }); - module.debug('RTL: Popup positioning updated', computedPosition); + module.debug('RTL: Popup positioning updated', position); + } + + if(searchDepth == settings.maxSearchDepth && settings.lastResort) { + module.debug('Using last resort position to display', settings.lastResort); + position = settings.lastResort; } - switch (computedPosition) { + + switch (position) { case 'top left': positioning = { top : 'auto', - bottom : parentHeight - target.top + distanceAway, - left : target.left + arrowOffset, + bottom : parent.height - target.top + distanceAway, + left : target.left + offset, right : 'auto' }; break; case 'top center': positioning = { - bottom : parentHeight - target.top + distanceAway, - left : target.left + (targetWidth / 2) - (popupWidth / 2) + arrowOffset, + bottom : parent.height - target.top + distanceAway, + left : target.left + (target.width / 2) - (popup.width / 2) + offset, top : 'auto', right : 'auto' }; break; case 'top right': positioning = { - bottom : parentHeight - target.top + distanceAway, - right : parentWidth - target.left - targetWidth - arrowOffset, + bottom : parent.height - target.top + distanceAway, + right : parent.width - target.left - target.width - offset, top : 'auto', left : 'auto' }; break; case 'left center': positioning = { - top : target.top + (targetHeight / 2) - (popupHeight / 2) + arrowOffset, - right : parentWidth - target.left + distanceAway, + top : target.top + (target.height / 2) - (popup.height / 2) + offset, + right : parent.width - target.left + distanceAway, left : 'auto', bottom : 'auto' }; break; case 'right center': positioning = { - top : target.top + (targetHeight / 2) - (popupHeight / 2) + arrowOffset, - left : target.left + targetWidth + distanceAway, + top : target.top + (target.height / 2) - (popup.height / 2) + offset, + left : target.left + target.width + distanceAway, bottom : 'auto', right : 'auto' }; break; case 'bottom left': positioning = { - top : target.top + targetHeight + distanceAway, - left : target.left + arrowOffset, + top : target.top + target.height + distanceAway, + left : target.left + offset, bottom : 'auto', right : 'auto' }; break; case 'bottom center': positioning = { - top : target.top + targetHeight + distanceAway, - left : target.left + (targetWidth / 2) - (popupWidth / 2) + arrowOffset, + top : target.top + target.height + distanceAway, + left : target.left + (target.width / 2) - (popup.width / 2) + offset, bottom : 'auto', right : 'auto' }; break; case 'bottom right': positioning = { - top : target.top + targetHeight + distanceAway, - right : parentWidth - target.left - targetWidth - arrowOffset, + top : target.top + target.height + distanceAway, + right : parent.width - target.left - target.width - offset, left : 'auto', bottom : 'auto' }; @@ -781,7 +802,7 @@ $.fn.popup = function(parameters) { position = module.get.nextPosition(position); module.debug('Trying new position', position); return ($popup) - ? module.set.position(position) + ? module.set.position(position, sizes) : false ; } @@ -835,6 +856,11 @@ $.fn.popup = function(parameters) { .on('click' + eventNamespace, module.toggle) ; } + if(settings.on == 'hover' && hasTouch) { + $module + .on('touchstart' + eventNamespace, module.event.touchstart) + ; + } else if( module.get.startEvent() ) { $module .on(module.get.startEvent() + eventNamespace, module.event.start) @@ -855,21 +881,30 @@ $.fn.popup = function(parameters) { ; } }, - close:function() { - if(settings.hideOnScroll === true || settings.hideOnScroll == 'auto' && settings.on != 'click') { + close: function() { + if(settings.hideOnScroll === true || (settings.hideOnScroll == 'auto' && settings.on != 'click')) { $document - .one(module.get.scrollEvent() + elementNamespace, module.hideGracefully) + .one(module.get.scrollEvent() + elementNamespace, module.event.hideGracefully) ; $context - .one(module.get.scrollEvent() + elementNamespace, module.hideGracefully) + .one(module.get.scrollEvent() + elementNamespace, module.event.hideGracefully) + ; + } + if(settings.on == 'hover' && openedWithTouch) { + module.verbose('Binding popup close event to document'); + $document + .on('touchstart' + elementNamespace, function(event) { + module.verbose('Touched away from popup'); + module.event.hideGracefully.call(element, event); + }) ; } if(settings.on == 'click' && settings.closable) { module.verbose('Binding popup close event to document'); $document .on('click' + elementNamespace, function(event) { - module.verbose('Pop-up clickaway intent detected'); - module.hideGracefully.call(element, event); + module.verbose('Clicked away from popup'); + module.event.hideGracefully.call(element, event); }) ; } @@ -878,7 +913,7 @@ $.fn.popup = function(parameters) { unbind: { close: function() { - if(settings.hideOnScroll === true || settings.hideOnScroll == 'auto' && settings.on != 'click') { + if(settings.hideOnScroll === true || (settings.hideOnScroll == 'auto' && settings.on != 'click')) { $document .off('scroll' + elementNamespace, module.hide) ; @@ -886,6 +921,12 @@ $.fn.popup = function(parameters) { .off('scroll' + elementNamespace, module.hide) ; } + if(settings.on == 'hover' && openedWithTouch) { + $document + .off('touchstart' + elementNamespace) + ; + openedWithTouch = false; + } if(settings.on == 'click' && settings.closable) { module.verbose('Removing close event from document'); $document