/*! * jQuery Popup Overlay * * @version 1.7.4 * @requires jQuery v1.7.1+ * @link http://vast-engineering.github.com/jquery-popup-overlay/ */ ;(function ($) { var $window = $(window); var options = {}; var zindexvalues = []; var lastclicked = []; var scrollbarwidth; var bodymarginright = null; var opensuffix = '_open'; var closesuffix = '_close'; var stack = []; var transitionsupport = null; var opentimer; var iOS = /(iPad|iPhone|iPod)/g.test(navigator.userAgent); var methods = { _init: function (el) { var $el = $(el); var options = $el.data('popupoptions'); lastclicked[el.id] = false; zindexvalues[el.id] = 0; if (!$el.data('popup-initialized')) { $el.attr('data-popup-initialized', 'true'); methods._initonce(el); } if (options.autoopen) { setTimeout(function() { methods.show(el, 0); }, 0); } }, _initonce: function (el) { var $el = $(el); var $body = $('body'); var $wrapper; var options = $el.data('popupoptions'); var css; bodymarginright = parseInt($body.css('margin-right'), 10); transitionsupport = document.body.style.webkitTransition !== undefined || document.body.style.MozTransition !== undefined || document.body.style.msTransition !== undefined || document.body.style.OTransition !== undefined || document.body.style.transition !== undefined; if (options.type == 'tooltip') { options.background = false; options.scrolllock = false; } if (options.backgroundactive) { options.background = false; options.blur = false; options.scrolllock = false; } if (options.scrolllock) { // Calculate the browser's scrollbar width dynamically var parent; var child; if (typeof scrollbarwidth === 'undefined') { parent = $('<div style="width:50px;height:50px;overflow:auto"><div/></div>').appendTo('body'); child = parent.children(); scrollbarwidth = child.innerWidth() - child.height(99).innerWidth(); parent.remove(); } } if (!$el.attr('id')) { $el.attr('id', 'j-popup-' + parseInt((Math.random() * 100000000), 10)); } $el.addClass('popup_content'); $body.prepend(el); $el.wrap('<div id="' + el.id + '_wrapper" class="popup_wrapper" />'); $wrapper = $('#' + el.id + '_wrapper'); $wrapper.css({ opacity: 0, visibility: 'hidden', position: 'absolute' }); // Make div clickable in iOS if (iOS) { $wrapper.css('cursor', 'pointer'); } if (options.type == 'overlay') { $wrapper.css('overflow','auto'); } $el.css({ opacity: 0, visibility: 'hidden', display: 'inline-block' }); if (options.setzindex && !options.autozindex) { $wrapper.css('z-index', '100001'); } if (!options.outline) { $el.css('outline', 'none'); } if (options.transition) { $el.css('transition', options.transition); $wrapper.css('transition', options.transition); } // Hide popup content from screen readers initially $el.attr('aria-hidden', true); if ((options.background) && (!$('#' + el.id + '_background').length)) { $body.prepend('<div id="' + el.id + '_background" class="popup_background"></div>'); var $background = $('#' + el.id + '_background'); $background.css({ opacity: 0, visibility: 'hidden', backgroundColor: options.color, position: 'fixed', top: 0, right: 0, bottom: 0, left: 0 }); if (options.setzindex && !options.autozindex) { $background.css('z-index', '100000'); } if (options.transition) { $background.css('transition', options.transition); } } if (options.type == 'overlay') { $el.css({ textAlign: 'left', position: 'relative', verticalAlign: 'middle' }); css = { position: 'fixed', width: '100%', height: '100%', top: 0, left: 0, textAlign: 'center' }; if(options.backgroundactive){ css.position = 'relative'; css.height = '0'; css.overflow = 'visible'; } $wrapper.css(css); // CSS vertical align helper $wrapper.append('<div class="popup_align" />'); $('.popup_align').css({ display: 'inline-block', verticalAlign: 'middle', height: '100%' }); } // Add WAI ARIA role to announce dialog to screen readers $el.attr('role', 'dialog'); var openelement = (options.openelement) ? options.openelement : ('.' + el.id + opensuffix); $(openelement).each(function (i, item) { $(item).attr('data-popup-ordinal', i); if (!item.id) { $(item).attr('id', 'open_' + parseInt((Math.random() * 100000000), 10)); } }); // Set aria-labelledby (if aria-label or aria-labelledby is not set in html) if (!($el.attr('aria-labelledby') || $el.attr('aria-label'))) { $el.attr('aria-labelledby', $(openelement).attr('id')); } // Show and hide tooltips on hover if(options.action == 'hover'){ options.keepfocus = false; // Handler: mouseenter, focusin $(openelement).on('mouseenter', function (event) { methods.show(el, $(this).data('popup-ordinal')); }); // Handler: mouseleave, focusout $(openelement).on('mouseleave', function (event) { methods.hide(el); }); } else { // Handler: Show popup when clicked on `open` element $(document).on('click', openelement, function (event) { event.preventDefault(); var ord = $(this).data('popup-ordinal'); setTimeout(function() { // setTimeout is to allow `close` method to finish (for issues with multiple tooltips) methods.show(el, ord); }, 0); }); } if (options.detach) { $el.hide().detach(); } else { $wrapper.hide(); } }, /** * Show method * * @param {object} el - popup instance DOM node * @param {number} ordinal - order number of an `open` element */ show: function (el, ordinal) { var $el = $(el); if ($el.data('popup-visible')) return; // Initialize if not initialized. Required for: $('#popup').popup('show') if (!$el.data('popup-initialized')) { methods._init(el); } $el.attr('data-popup-initialized', 'true'); var $body = $('body'); var options = $el.data('popupoptions'); var $wrapper = $('#' + el.id + '_wrapper'); var $background = $('#' + el.id + '_background'); // `beforeopen` callback event callback(el, ordinal, options.beforeopen); // Remember last clicked place lastclicked[el.id] = ordinal; // Add popup id to popup stack setTimeout(function() { stack.push(el.id); }, 0); // Calculating maximum z-index if (options.autozindex) { var elements = document.getElementsByTagName('*'); var len = elements.length; var maxzindex = 0; for(var i=0; i<len; i++){ var elementzindex = $(elements[i]).css('z-index'); if(elementzindex !== 'auto'){ elementzindex = parseInt(elementzindex, 10); if(maxzindex < elementzindex){ maxzindex = elementzindex; } } } zindexvalues[el.id] = maxzindex; // Add z-index to the background if (options.background) { if (zindexvalues[el.id] > 0) { $('#' + el.id + '_background').css({ zIndex: (zindexvalues[el.id] + 1) }); } } // Add z-index to the wrapper if (zindexvalues[el.id] > 0) { $wrapper.css({ zIndex: (zindexvalues[el.id] + 2) }); } } if (options.detach) { $wrapper.prepend(el); $el.show(); } else { $wrapper.show(); } opentimer = setTimeout(function() { $wrapper.css({ visibility: 'visible', opacity: 1 }); $('html').addClass('popup_visible').addClass('popup_visible_' + el.id); $el.addClass('popup_content_visible'); }, 20); // 20ms required for opening animation to occur in FF // Disable background layer scrolling when popup is opened if (options.scrolllock) { $body.css('overflow', 'hidden'); if ($body.height() > $window.height()) { $body.css('margin-right', bodymarginright + scrollbarwidth); } } if(options.backgroundactive){ //calculates the vertical align $el.css({ top:( $window.height() - ( $el.get(0).offsetHeight + parseInt($el.css('margin-top'), 10) + parseInt($el.css('margin-bottom'), 10) ) )/2 +'px' }); } $el.css({ 'visibility': 'visible', 'opacity': 1 }); // Show background if (options.background) { $background.css({ 'visibility': 'visible', 'opacity': options.opacity }); // Fix IE8 issue with background not appearing setTimeout(function() { $background.css({ 'opacity': options.opacity }); }, 0); } $el.data('popup-visible', true); // Position popup methods.reposition(el, ordinal); // Remember which element had focus before opening a popup $el.data('focusedelementbeforepopup', document.activeElement); // Handler: Keep focus inside dialog box if (options.keepfocus) { // Make holder div focusable $el.attr('tabindex', -1); // Focus popup or user specified element. // Initial timeout of 50ms is set to give some time to popup to show after clicking on // `open` element, and after animation is complete to prevent background scrolling. setTimeout(function() { if (options.focuselement === 'closebutton') { $('#' + el.id + ' .' + el.id + closesuffix + ':first').focus(); } else if (options.focuselement) { $(options.focuselement).focus(); } else { $el.focus(); } }, options.focusdelay); } // Hide main content from screen readers $(options.pagecontainer).attr('aria-hidden', true); // Reveal popup content to screen readers $el.attr('aria-hidden', false); callback(el, ordinal, options.onopen); if (transitionsupport) { $wrapper.one('transitionend', function() { callback(el, ordinal, options.opentransitionend); }); } else { callback(el, ordinal, options.opentransitionend); } }, /** * Hide method * * @param {object} el - popup instance DOM node */ hide: function (el) { if(opentimer) clearTimeout(opentimer); var $body = $('body'); var $el = $(el); var options = $el.data('popupoptions'); var $wrapper = $('#' + el.id + '_wrapper'); var $background = $('#' + el.id + '_background'); $el.data('popup-visible', false); if (stack.length === 1) { $('html').removeClass('popup_visible').removeClass('popup_visible_' + el.id); } else { if($('html').hasClass('popup_visible_' + el.id)) { $('html').removeClass('popup_visible_' + el.id); } } // Remove last opened popup from the stack stack.pop(); if($('html').hasClass('popup_content_visible')) { $el.removeClass('popup_content_visible'); } if (options.keepfocus) { // Focus back on saved element setTimeout(function() { if ($($el.data('focusedelementbeforepopup')).is(':visible')) { $el.data('focusedelementbeforepopup').focus(); } }, 0); } // Hide popup $wrapper.css({ 'visibility': 'hidden', 'opacity': 0 }); $el.css({ 'visibility': 'hidden', 'opacity': 0 }); // Hide background if (options.background) { $background.css({ 'visibility': 'hidden', 'opacity': 0 }); } // Reveal main content to screen readers $(options.pagecontainer).attr('aria-hidden', false); // Hide popup content from screen readers $el.attr('aria-hidden', true); // `onclose` callback event callback(el, lastclicked[el.id], options.onclose); if (transitionsupport && $el.css('transition-duration') !== '0s') { $el.one('transitionend', function(e) { if (!($el.data('popup-visible'))) { if (options.detach) { $el.hide().detach(); } else { $wrapper.hide(); } } // Re-enable scrolling of background layer if (options.scrolllock) { setTimeout(function() { $body.css({ overflow: 'visible', 'margin-right': bodymarginright }); }, 10); // 10ms added for CSS transition in Firefox which doesn't like overflow:auto } callback(el, lastclicked[el.id], options.closetransitionend); }); } else { if (options.detach) { $el.hide().detach(); } else { $wrapper.hide(); } // Re-enable scrolling of background layer if (options.scrolllock) { setTimeout(function() { $body.css({ overflow: 'visible', 'margin-right': bodymarginright }); }, 10); // 10ms added for CSS transition in Firefox which doesn't like overflow:auto } callback(el, lastclicked[el.id], options.closetransitionend); } }, /** * Toggle method * * @param {object} el - popup instance DOM node * @param {number} ordinal - order number of an `open` element */ toggle: function (el, ordinal) { if ($(el).data('popup-visible')) { methods.hide(el); } else { setTimeout(function() { methods.show(el, ordinal); }, 0); } }, /** * Reposition method * * @param {object} el - popup instance DOM node * @param {number} ordinal - order number of an `open` element */ reposition: function (el, ordinal) { var $el = $(el); var options = $el.data('popupoptions'); var $wrapper = $('#' + el.id + '_wrapper'); var $background = $('#' + el.id + '_background'); ordinal = ordinal || 0; // Tooltip type if (options.type == 'tooltip') { $wrapper.css({ 'position': 'absolute' }); var $tooltipanchor; if (options.tooltipanchor) { $tooltipanchor = $(options.tooltipanchor); } else if (options.openelement) { $tooltipanchor = $(options.openelement).filter('[data-popup-ordinal="' + ordinal + '"]'); } else { $tooltipanchor = $('.' + el.id + opensuffix + '[data-popup-ordinal="' + ordinal + '"]'); } var linkOffset = $tooltipanchor.offset(); // Horizontal position for tooltip if (options.horizontal == 'right') { $wrapper.css('left', linkOffset.left + $tooltipanchor.outerWidth() + options.offsetleft); } else if (options.horizontal == 'leftedge') { $wrapper.css('left', linkOffset.left + $tooltipanchor.outerWidth() - $tooltipanchor.outerWidth() + options.offsetleft); } else if (options.horizontal == 'left') { $wrapper.css('right', $window.width() - linkOffset.left - options.offsetleft); } else if (options.horizontal == 'rightedge') { $wrapper.css('right', $window.width() - linkOffset.left - $tooltipanchor.outerWidth() - options.offsetleft); } else { $wrapper.css('left', linkOffset.left + ($tooltipanchor.outerWidth() / 2) - ($el.outerWidth() / 2) - parseFloat($el.css('marginLeft')) + options.offsetleft); } // Vertical position for tooltip if (options.vertical == 'bottom') { $wrapper.css('top', linkOffset.top + $tooltipanchor.outerHeight() + options.offsettop); } else if (options.vertical == 'bottomedge') { $wrapper.css('top', linkOffset.top + $tooltipanchor.outerHeight() - $el.outerHeight() + options.offsettop); } else if (options.vertical == 'top') { $wrapper.css('bottom', $window.height() - linkOffset.top - options.offsettop); } else if (options.vertical == 'topedge') { $wrapper.css('bottom', $window.height() - linkOffset.top - $el.outerHeight() - options.offsettop); } else { $wrapper.css('top', linkOffset.top + ($tooltipanchor.outerHeight() / 2) - ($el.outerHeight() / 2) - parseFloat($el.css('marginTop')) + options.offsettop); } // Overlay type } else if (options.type == 'overlay') { // Horizontal position for overlay if (options.horizontal) { $wrapper.css('text-align', options.horizontal); } else { $wrapper.css('text-align', 'center'); } // Vertical position for overlay if (options.vertical) { $el.css('vertical-align', options.vertical); } else { $el.css('vertical-align', 'middle'); } } } }; /** * Callback event calls * * @param {object} el - popup instance DOM node * @param {number} ordinal - order number of an `open` element * @param {function} func - callback function */ var callback = function (el, ordinal, func) { var options = $(el).data('popupoptions'); var openelement = (options.openelement) ? options.openelement : ('.' + el.id + opensuffix); var elementclicked = $(openelement + '[data-popup-ordinal="' + ordinal + '"]'); if (typeof func == 'function') { func.call($(el), el, elementclicked); } }; // Hide popup if ESC key is pressed $(document).on('keydown', function (event) { if(stack.length) { var elementId = stack[stack.length - 1]; var el = document.getElementById(elementId); if ($(el).data('popupoptions').escape && event.keyCode == 27) { methods.hide(el); } } }); // Hide popup on click $(document).on('click', function (event) { if(stack.length) { var elementId = stack[stack.length - 1]; var el = document.getElementById(elementId); var closeButton = ($(el).data('popupoptions').closeelement) ? $(el).data('popupoptions').closeelement : ('.' + el.id + closesuffix); // Click on Close button if ($(event.target).closest(closeButton).length) { event.preventDefault(); methods.hide(el); } // Click outside of popup if ($(el).data('popupoptions').blur && !$(event.target).closest('#' + elementId).length && event.which !== 2) { methods.hide(el); if ($(el).data('popupoptions').type === 'overlay') { event.preventDefault(); // iOS will trigger click on the links below the overlay when clicked on the overlay if we don't prevent default action } } } }); // Keep keyboard focus inside of popup $(document).on('focusin', function(event) { if(stack.length) { var elementId = stack[stack.length - 1]; var el = document.getElementById(elementId); if ($(el).data('popupoptions').keepfocus) { if (!el.contains(event.target)) { event.stopPropagation(); el.focus(); } } } }); /** * Plugin API */ $.fn.popup = function (customoptions) { return this.each(function () { $el = $(this); if (typeof customoptions === 'object') { // e.g. $('#popup').popup({'color':'blue'}) var opt = $.extend({}, $.fn.popup.defaults, customoptions); $el.data('popupoptions', opt); options = $el.data('popupoptions'); methods._init(this); } else if (typeof customoptions === 'string') { // e.g. $('#popup').popup('hide') if (!($el.data('popupoptions'))) { $el.data('popupoptions', $.fn.popup.defaults); options = $el.data('popupoptions'); } methods[customoptions].call(this, this); } else { // e.g. $('#popup').popup() if (!($el.data('popupoptions'))) { $el.data('popupoptions', $.fn.popup.defaults); options = $el.data('popupoptions'); } methods._init(this); } }); }; $.fn.popup.defaults = { type: 'overlay', autoopen: false, background: true, backgroundactive: false, color: 'black', opacity: '0.5', horizontal: 'center', vertical: 'middle', offsettop: 0, offsetleft: 0, escape: true, blur: true, setzindex: true, autozindex: false, scrolllock: false, keepfocus: true, focuselement: null, focusdelay: 50, outline: false, pagecontainer: null, detach: false, openelement: null, closeelement: null, transition: null, tooltipanchor: null, beforeopen: null, onclose: null, onopen: null, opentransitionend: null, closetransitionend: null }; })(jQuery);