/* THIS FILE GETS MINIFIED! It is included as "ao3modal.min.js". USE A MINIFIER AND UPDATE THE .min.js FILE AFTER MAKING ANY CHANGES HERE! */ jQuery(document).ready(function() { window.ao3modal = (function($) { var _loading = false, _bgDiv = $('
', {'id': 'modal-bg'}).addClass('modal-closer'), _loadingDiv = $('
').addClass('loading'), _wrapDiv = $('
', {'id': 'modal-wrap'}).addClass('modal-closer'), _modalDiv = $('
', {'id': 'modal'}), _contentDiv = $('
').addClass('content userstuff'), _closeButton = $('').addClass('action modal-closer') .click(function(event) { event.preventDefault(); }) .attr('href', '#') .text('Close'), _title = $('').addClass('title'), _tallHeight, _mobile = (function(uastring) { // detectmobilebrowsers.com (added ipad to regex) return /(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(ad|hone|od)|iris|kindle|lge |maemo|midp|mmp|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows (ce|phone)|xda|xiino/i.test(uastring) || /1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\-(n|u)|c55\/|capi|ccwa|cdm\-|cell|chtm|cldc|cmd\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\-s|devi|dica|dmob|do(c|p)o|ds(12|\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\-|_)|g1 u|g560|gene|gf\-5|g\-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd\-(m|p|t)|hei\-|hi(pt|ta)|hp( i|ip)|hs\-c|ht(c(\-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\-(20|go|ma)|i230|iac( |\-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc\-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|\-[a-w])|libw|lynx|m1\-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m\-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\-2|po(ck|rt|se)|prox|psio|pt\-g|qa\-a|qc(07|12|21|32|60|\-[2-7]|i\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\-|oo|p\-)|sdk\/|se(c(\-|0|1)|47|mc|nd|ri)|sgh\-|shar|sie(\-|m)|sk\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\-|v\-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\-|tdg\-|tel(i|m)|tim\-|t\-mo|to(pl|sh)|ts(70|m\-|m3|m5)|tx\-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas\-|your|zeto|zte\-/i.test(uastring.substr(0,4)); })(navigator.userAgent||navigator.vendor||window.opera), _mobileScrollTop = -1, // for returning to previous page scroll position after closing modal _scrollbarWidth = _mobile ? 0 : (function() { // used as a margin when scrollbar is hidden var inner = $('

').css({'width': '100%', 'height': 200}), outer = $('

').css({ 'height': 150, 'left': 0, 'overflow': 'hidden', 'top': 0, 'visibility': 'hidden', 'width': 200 }).append(inner).appendTo($('body')), w1 = inner[0].offsetWidth, w2; outer.css('overflow', 'scroll'); w2 = inner[0].offsetWidth; if (w1 == w2) w2 = outer[0].clientWidth; outer.remove(); return (w1 - w2); })(), _keyboard; function _toggleScroll(on) { if (_mobile) { return; } $('body').css({ 'margin-right': on ? '' : _scrollbarWidth, 'overflow': on ? '' : 'hidden', 'height': on ? '' : _bgDiv.height() }); } function _calcSize() { var img, scaledHeight, maxHeight, hidden = !_modalDiv.is(':visible'); if (_mobile && _mobileScrollTop < 0) { _mobileScrollTop = $(window).scrollTop(); } if (hidden) { _modalDiv.css('opacity', 0).show(); } _modalDiv.removeClass('tall'); if (_modalDiv.hasClass('img')) { img = _contentDiv.children('img').first(); _modalDiv.toggleClass('tall', _modalDiv.height() >= 0.95*_bgDiv.height()); } else if (!_mobile) { scaledHeight = _bgDiv.height()*_tallHeight.scale; maxHeight = Math.min(scaledHeight, _tallHeight.max); _modalDiv.toggleClass('tall', (!maxHeight || _modalDiv[0].scrollHeight > maxHeight)); } if (hidden) { _modalDiv.hide().css('opacity', ''); } if (_mobile) { _wrapDiv.css('top', _mobileScrollTop); } else { _wrapDiv.css('top', $(window).scrollTop()); } } function _setContent(content, title) { _loading = false; _contentDiv.empty(); if (content instanceof $) { _contentDiv.append(content); } else { _contentDiv.html(content); } title = (title instanceof $) ? title : $('').addClass('title').text(title || ''); _title.replaceWith(title); _title = title; if (!_contentDiv.is(':empty')) { _calcSize(); if (!_contentDiv.is(':visible')) { _keyboard.toggleHandlers(true); _loadingDiv.hide(); _modalDiv.fadeIn(function() { _contentDiv.focus(); }); } } } function _show(href, title) { _modalDiv.hide(); _wrapDiv.show(); if ($.support.opacity) { _bgDiv.add(_loadingDiv).fadeIn(); } else { _bgDiv.add(_loadingDiv).show(); } _toggleScroll(false); _loading = true; if (href.indexOf('#') === 0) { _setContent($(href).html(), title || ''); } else if (href.indexOf('.jpg') == href.length-'.jpg'.length || href.indexOf('.gif') == href.length-'.gif'.length || href.indexOf('.bmp') == href.length-'.bmp'.length || href.indexOf('.png') == href.length-'.png'.length) { _modalDiv.addClass('img'); var img = $('').appendTo($('body')).attr('src', href), imgLoad = function() { var srcLink = $('').addClass('title') .attr({'href': href, 'title': 'View original', 'target': '_blank'}) .text(title || (href.indexOf('/') != -1 ? href.substring(href.lastIndexOf('/')+1) : href) ).css('text-decoration', 'underline'); img.remove(); if (!_loading) { return; } if (title) { img.attr('alt', title); } _setContent(img[0], srcLink); }; if (img[0].complete) { imgLoad(); } else { img.load(imgLoad); } } else { $.ajax({ url: href, success: function(data) { if (!_loading) { return; } _setContent(data, title); } }); } } function _hide() { _loading = false; _title.text(''); _keyboard.toggleHandlers(false); _wrapDiv.fadeOut(function() { _setContent(''); _toggleScroll(true); _modalDiv.css('width', '').removeClass('tall img'); if (_mobile && _mobileScrollTop >= 0) { $('html, body').animate({'scrollTop': _mobileScrollTop}, 'fast'); _mobileScrollTop = -1; } }); if ($.support.opacity) { _bgDiv.fadeOut(); } else { _bgDiv.hide(); } } function _addLink(elements) { elements.each(function() { var img = $(this).is('img') ? $(this).removeClass('modal') : false, a = !img ? $(this) : $('') .css('border', 'none') .attr({ 'title': (img.attr('title') || img.attr('alt')), 'href': img.attr('src') }).replaceAll(img) .append(img); a.addClass('modal modal-attached') .attr('aria-controls', 'modal') .filter(function() { return $(this).closest('.userstuff').length === 0; }).click(function(event){ _show($(this).attr('href'), $(this).attr('title')); event.preventDefault(); }); // if link refers to in-page modal content, find it and hide it if (a.attr('href').indexOf('#') === 0) { $(a.attr('href')).hide(); } }); } // add modal elements to page $('body').append( _bgDiv.append(_loadingDiv), _wrapDiv.append( _modalDiv.append( _contentDiv, $('
').addClass('footer') .append( _title, _closeButton ) ).trap() ) ); // find the height scale and max-height of modal windows whose content is taller than the viewport // values should come from the css ruleset for #modal.tall _tallHeight = _mobile ? null : (function() { var heights = {}, modalDiv = _modalDiv.clone(); modalDiv.css('margin-top', 9001).addClass('tall').appendTo(_bgDiv); heights.scale = !modalDiv.height() ? 0.8 : parseInt(modalDiv.css('height'))/100; heights.max = parseInt(modalDiv.css('max-height')); modalDiv.remove(); return heights; })(); // enable exit controls $('body').click(function(event) { if ($(event.target).hasClass('modal-closer') || (_modalDiv.hasClass('img') && $(event.target).hasClass('content'))) { _hide(); } }); // recalculate modal size on viewport resize $(window).resize(function() { if (_modalDiv.is(':visible')) { _calcSize(); } }); // set up keyboard handling _keyboard = (function() { var scrollValues = { 38: -20, // up arrow 40: 20, // down arrow 33: -200, // page up 34: 200 // page down }, tabbed = false, handlers = { 'keydown': function(event) { if (scrollValues[event.which]) { _contentDiv[0].scrollTop += scrollValues[event.which]; event.preventDefault(); } else if (_modalDiv.is(':visible')) { var target = event.target, tag = target.tagName.toLowerCase(), escKey = event.which == 27, enterKey = event.which == 13, targetIsInput = /^(input|textarea|a|button)$/.test(tag), // ignore enter keypresses on links & inputs targetInModal = !!$(target).closest('#modal')[0]; if (escKey || enterKey && (!targetInModal || !targetIsInput)) { _hide(); } // key events triggered from outside the modal should also die, // except for ctrl combinations like ctrl+c (or cmd+c on macOS) var keyShortcut = event.ctrlKey || event.metaKey; if (escKey || (!targetInModal && !keyShortcut) || enterKey && !targetIsInput) { event.preventDefault(); event.stopPropagation(); } } }, 'keypress': function(event) { if (scrollValues[event.which]) { event.preventDefault(); } }, 'keyup': function(event) { if (scrollValues[event.which]) { event.preventDefault(); } else if (event.which == 9 && !tabbed) { _closeButton.focus(); tabbed = true; event.preventDefault(); } } }; return { toggleHandlers: function(on) { tabbed = false; for (var eventType in handlers) { if (handlers.hasOwnProperty(eventType)) { if (on) { $('body').on(eventType, handlers[eventType]); } else { $('body').off(eventType, handlers[eventType]); } } } } }; })(); // set modal-classed links to open modal boxes _addLink($('a.modal, img.modal')); // ensure handlers are attached to dynamically added modal invokers $('body').on('click', 'a.modal, img.modal', function(event) { var $this = $(this); if ($this.is('.modal-attached')) { return; } _addLink($this); event.preventDefault(); if ($this.is('img')) { $this.parent().click(); } else { $this.click(); } }); return { show: _show, setContent: _setContent, hide: _hide, addLink: _addLink }; })(jQuery); });