###* Pop-up overlays =============== Instead of linking to another page fragment, you can also choose to "roll up" any target CSS selector in a popup overlay. Popup overlays close themselves if the user clicks somewhere outside the popup area. For modal dialogs see [up.modal](/up.modal) instead. \#\#\# Incomplete documentation! We need to work on this page: - Show the HTML structure of the popup elements, and how to style them via CSS - Explain how to position popup using `up-position` - Explain how dialogs auto-close themselves when a fragment changes behind the popup layer - Document method parameters @class up.popup ### up.popup = (-> u = up.util currentSource = undefined config = openAnimation: 'fade-in' closeAnimation: 'fade-out' position: 'bottom-right' ###* @method up.popup.defaults @param {String} options.animation @param {String} options.position ### defaults = (options) -> u.extend(config, options) setPosition = ($link, $popup, position) -> linkBox = u.measure($link, full: true) css = switch position when "bottom-right" right: linkBox.right top: linkBox.top + linkBox.height when "bottom-left" left: linkBox.left top: linkBox.bottom + linkBox.height when "top-right" right: linkBox.right bottom: linkBox.top when "top-left" left: linkBox.left bottom: linkBox.top else u.error("Unknown position %o", position) $popup.attr('up-position', position) $popup.css(css) ensureInViewport($popup) ensureInViewport = ($popup) -> box = u.measure($popup, full: true) errorX = null errorY = null if box.right < 0 errorX = -box.right # errorX is positive if box.bottom < 0 errorY = -box.bottom # errorY is positive if box.left < 0 errorX = box.left # errorX is negative if box.top < 0 errorY = box.top # errorY is negative if errorX # We use parseInt to: # 1) convert "50px" to 50 # 2) convert "auto" to NaN if left = parseInt($popup.css('left')) $popup.css('left', left - errorX) else if right = parseInt($popup.css('right')) $popup.css('right', right + errorX) if errorY if top = parseInt($popup.css('top')) $popup.css('top', top - errorY) else if bottom = parseInt($popup.css('bottom')) $popup.css('bottom', bottom + errorY) rememberHistory = -> $popup = $('.up-popup') $popup.attr('up-previous-url', up.browser.url()) $popup.attr('up-previous-title', document.title) discardHistory = -> $popup = $('.up-popup') $popup.removeAttr('up-previous-url') $popup.removeAttr('up-previous-title') createHiddenPopup = ($link, selector, sticky) -> $popup = u.$createElementFromSelector('.up-popup') $popup.attr('up-sticky', '') if sticky $placeholder = u.$createElementFromSelector(selector) $placeholder.appendTo($popup) $popup.appendTo(document.body) rememberHistory() $popup.hide() $popup updated = ($link, $popup, position, animation, animateOptions) -> $popup.show() setPosition($link, $popup, position) up.animate($popup, animation, animateOptions) ###* Opens a popup overlay. @method up.popup.open @param {Element|jQuery|String} elementOrSelector @param {String} [options.url] @param {String} [options.position='bottom-right'] @param {String} [options.animation] The animation to use when opening the popup. @param {Number} [options.duration] The duration of the animation. See [`up.animate`](/up.motion#up.animate). @param {Number} [options.delay] The delay before the animation starts. See [`up.animate`](/up.motion#up.animate). @param {String} [options.easing] The timing function that controls the animation's acceleration. [`up.animate`](/up.motion#up.animate). @param {Boolean} [options.sticky=false] If set to `true`, the popup remains open even if the page changes in the background. @param {Object} [options.history=false] ### open = (linkOrSelector, options) -> $link = $(linkOrSelector) options = u.options(options) url = u.option(options.url, $link.attr('href')) selector = u.option(options.target, $link.attr('up-popup'), 'body') position = u.option(options.position, $link.attr('up-position'), config.position) animation = u.option(options.animation, $link.attr('up-animation'), config.openAnimation) sticky = u.option(options.sticky, $link.is('[up-sticky]')) history = if up.browser.canPushState() then u.option(options.history, $link.attr('up-history'), false) else false animateOptions = up.motion.animateOptions(options, $link) close() $popup = createHiddenPopup($link, selector, sticky) up.replace(selector, url, history: history insert: -> updated($link, $popup, position, animation, animateOptions) ) ###* Returns the source URL for the fragment displayed in the current popup overlay, or `undefined` if no popup is open. @method up.popup.source @return {String} the source URL ### source = -> currentSource ###* Closes a currently opened popup overlay. Does nothing if no popup is currently open. @method up.popup.close @param {Object} options See options for [`up.animate`](/up.motion#up.animate). ### close = (options) -> $popup = $('.up-popup') if $popup.length options = u.options(options, animation: config.closeAnimation, url: $popup.attr('up-previous-url'), title: $popup.attr('up-previous-title') ) currentSource = undefined up.destroy($popup, options) autoclose = -> unless $('.up-popup').is('[up-sticky]') discardHistory() close() ###* Returns whether the given element or selector is contained within the current popup. @methods up.popup.contains @param {String} elementOrSelector @protected ### contains = (elementOrSelector) -> $element = $(elementOrSelector) $element.closest('.up-popup').length > 0 ###* Opens the target of this link in a popup overlay: Switch deck If the `up-sticky` attribute is set, the dialog does not auto-close if a page fragment below the popup overlay updates: Switch deck Settings @method a[up-popup] @ujs @param [up-sticky] @param [up-position] ### up.on('click', 'a[up-popup]', (event, $link) -> event.preventDefault() if $link.is('.up-current') close() else open($link) ) # Close the popup when someone clicks outside the popup # (but not on a popup opener). up.on('click', 'body', (event, $body) -> $target = $(event.target) unless $target.closest('.up-popup').length || $target.closest('[up-popup]').length close() ) up.bus.on('fragment:ready', ($fragment) -> if contains($fragment) if newSource = $fragment.attr('up-source') currentSource = newSource else autoclose() ) # Close the pop-up overlay when the user presses ESC. up.magic.onEscape(-> close()) ###* When an element with this attribute is clicked, a currently open popup is closed. @method [up-close] @ujs ### up.on('click', '[up-close]', (event, $element) -> if $element.closest('.up-popup') close() ) # The framework is reset between tests, so also close # a currently open popup. up.bus.on 'framework:reset', close open: open close: close source: source defaults: defaults contains: contains )()