')
.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);
});