Archive: Namaste PHP AMQP framework v1.0 (2017-2020)
952 days continuous production uptime, 40k+ tp/s single node. Original corpo Bitbucket history not included — clean archive commit.
This commit is contained in:
357
html/js/modules/dropdown.js
Executable file
357
html/js/modules/dropdown.js
Executable file
@@ -0,0 +1,357 @@
|
||||
'use strict';
|
||||
|
||||
(function ($) {
|
||||
|
||||
$.fn.scrollTo = function (elem) {
|
||||
|
||||
$(this).scrollTop($(this).scrollTop() - $(this).offset().top + $(elem).offset().top);
|
||||
return this;
|
||||
};
|
||||
|
||||
$.fn.dropdown = function (option) {
|
||||
|
||||
this.each(function () {
|
||||
|
||||
var origin = $(this);
|
||||
var options = $.extend({}, $.fn.dropdown.defaults, option);
|
||||
var isFocused = false;
|
||||
|
||||
// Dropdown menu
|
||||
var activates = $('#' + origin.attr('data-activates'));
|
||||
|
||||
function updateOptions() {
|
||||
|
||||
if (origin.data('induration') !== undefined) {
|
||||
options.inDuration = origin.data('inDuration');
|
||||
}
|
||||
if (origin.data('outduration') !== undefined) {
|
||||
options.outDuration = origin.data('outDuration');
|
||||
}
|
||||
if (origin.data('constrainwidth') !== undefined) {
|
||||
options.constrain_width = origin.data('constrainwidth');
|
||||
}
|
||||
if (origin.data('hover') !== undefined) {
|
||||
options.hover = origin.data('hover');
|
||||
}
|
||||
if (origin.data('gutter') !== undefined) {
|
||||
options.gutter = origin.data('gutter');
|
||||
}
|
||||
if (origin.data('beloworigin') !== undefined) {
|
||||
options.belowOrigin = origin.data('beloworigin');
|
||||
}
|
||||
if (origin.data('alignment') !== undefined) {
|
||||
options.alignment = origin.data('alignment');
|
||||
}
|
||||
}
|
||||
|
||||
updateOptions();
|
||||
|
||||
// Attach dropdown to its activator
|
||||
origin.after(activates);
|
||||
|
||||
/*
|
||||
Helper function to position and resize dropdown.
|
||||
Used in hover and click handler.
|
||||
*/
|
||||
function placeDropdown(eventType) {
|
||||
|
||||
// Check for simultaneous focus and click events.
|
||||
if (eventType === 'focus') {
|
||||
isFocused = true;
|
||||
}
|
||||
|
||||
// Check html data attributes
|
||||
updateOptions();
|
||||
|
||||
// Set Dropdown state
|
||||
activates.addClass('active');
|
||||
origin.addClass('active');
|
||||
|
||||
// Constrain width
|
||||
if (options.constrain_width === true) {
|
||||
|
||||
activates.css('width', origin.outerWidth());
|
||||
} else {
|
||||
|
||||
activates.css('white-space', 'nowrap');
|
||||
}
|
||||
|
||||
// Offscreen detection
|
||||
var windowHeight = window.innerHeight;
|
||||
var originHeight = origin.innerHeight();
|
||||
var offsetLeft = origin.offset().left;
|
||||
var offsetTop = origin.offset().top - $(window).scrollTop();
|
||||
var currAlignment = options.alignment;
|
||||
var gutterSpacing = 0;
|
||||
var leftPosition = 0;
|
||||
|
||||
// Below Origin
|
||||
var verticalOffset = 0;
|
||||
if (options.belowOrigin === true) {
|
||||
verticalOffset = originHeight;
|
||||
}
|
||||
|
||||
// Check for scrolling positioned container.
|
||||
var scrollOffset = 0;
|
||||
var wrapper = origin.parent();
|
||||
if (!wrapper.is('body') && wrapper[0].scrollHeight > wrapper[0].clientHeight) {
|
||||
|
||||
scrollOffset = wrapper[0].scrollTop;
|
||||
}
|
||||
|
||||
if (offsetLeft + activates.innerWidth() > $(window).width()) {
|
||||
|
||||
// Dropdown goes past screen on right, force right alignment
|
||||
currAlignment = 'right';
|
||||
} else if (offsetLeft - activates.innerWidth() + origin.innerWidth() < 0) {
|
||||
|
||||
// Dropdown goes past screen on left, force left alignment
|
||||
currAlignment = 'left';
|
||||
}
|
||||
// Vertical bottom offscreen detection
|
||||
if (offsetTop + activates.innerHeight() > windowHeight) {
|
||||
|
||||
// If going upwards still goes offscreen, just crop height of dropdown.
|
||||
if (offsetTop + originHeight - activates.innerHeight() < 0) {
|
||||
|
||||
var adjustedHeight = windowHeight - offsetTop - verticalOffset;
|
||||
activates.css('max-height', adjustedHeight);
|
||||
} else {
|
||||
|
||||
// Flow upwards.
|
||||
if (!verticalOffset) {
|
||||
verticalOffset += originHeight;
|
||||
}
|
||||
verticalOffset -= activates.innerHeight();
|
||||
}
|
||||
}
|
||||
|
||||
// Handle edge alignment
|
||||
if (currAlignment === 'left') {
|
||||
|
||||
gutterSpacing = options.gutter;
|
||||
leftPosition = origin.position().left + gutterSpacing;
|
||||
} else if (currAlignment === 'right') {
|
||||
|
||||
var offsetRight = origin.position().left + origin.outerWidth() - activates.outerWidth();
|
||||
gutterSpacing = -options.gutter;
|
||||
leftPosition = offsetRight + gutterSpacing;
|
||||
}
|
||||
|
||||
// Position dropdown
|
||||
activates.css({
|
||||
position: 'absolute',
|
||||
top: origin.position().top + verticalOffset + scrollOffset,
|
||||
left: leftPosition
|
||||
});
|
||||
|
||||
// Show dropdown
|
||||
activates.stop(true, true).css('opacity', 0).slideDown({
|
||||
queue: false,
|
||||
duration: options.inDuration,
|
||||
easing: 'easeOutCubic',
|
||||
complete: function complete() {
|
||||
$(this).css('height', '');
|
||||
}
|
||||
}).animate({
|
||||
opacity: 1,
|
||||
scrollTop: 0
|
||||
}, {
|
||||
queue: false,
|
||||
duration: options.inDuration,
|
||||
easing: 'easeOutSine'
|
||||
});
|
||||
}
|
||||
|
||||
function hideDropdown() {
|
||||
|
||||
// Check for simultaneous focus and click events.
|
||||
isFocused = false;
|
||||
activates.fadeOut(options.outDuration);
|
||||
activates.removeClass('active');
|
||||
origin.removeClass('active');
|
||||
setTimeout(function () {
|
||||
activates.css('max-height', '');
|
||||
}, options.outDuration);
|
||||
}
|
||||
|
||||
// Hover
|
||||
if (options.hover) {
|
||||
|
||||
var open = false;
|
||||
origin.unbind('click.' + origin.attr('id'));
|
||||
// Hover handler to show dropdown
|
||||
origin.on('mouseenter', function () {
|
||||
// Mouse over
|
||||
|
||||
if (open === false) {
|
||||
|
||||
placeDropdown();
|
||||
open = true;
|
||||
}
|
||||
});
|
||||
origin.on('mouseleave', function (e) {
|
||||
|
||||
// If hover on origin then to something other than dropdown content, then close
|
||||
var toEl = e.toElement || e.relatedTarget; // added browser compatibility for target element
|
||||
if (!$(toEl).closest('.dropdown-content').is(activates)) {
|
||||
|
||||
activates.stop(true, true);
|
||||
hideDropdown();
|
||||
open = false;
|
||||
}
|
||||
});
|
||||
|
||||
activates.on('mouseleave', function (e) {
|
||||
// Mouse out
|
||||
|
||||
var toEl = e.toElement || e.relatedTarget;
|
||||
if (!$(toEl).closest('.dropdown-button').is(origin)) {
|
||||
|
||||
activates.stop(true, true);
|
||||
hideDropdown();
|
||||
open = false;
|
||||
}
|
||||
});
|
||||
|
||||
// Click
|
||||
} else {
|
||||
|
||||
// Click handler to show dropdown
|
||||
origin.unbind('click.' + origin.attr('id'));
|
||||
origin.bind('click.' + origin.attr('id'), function (e) {
|
||||
|
||||
if (!isFocused) {
|
||||
|
||||
if (origin[0] === e.currentTarget && !origin.hasClass('active') && $(e.target).closest('.dropdown-content').length === 0) {
|
||||
|
||||
e.preventDefault(); // Prevents button click from moving window
|
||||
placeDropdown('click');
|
||||
} else if (origin.hasClass('active')) {
|
||||
// If origin is clicked and menu is open, close menu
|
||||
|
||||
hideDropdown();
|
||||
$(document).unbind('click.' + activates.attr('id') + ' touchstart.' + activates.attr('id'));
|
||||
}
|
||||
// If menu open, add click close handler to document
|
||||
if (activates.hasClass('active')) {
|
||||
|
||||
$(document).bind('click.' + activates.attr('id') + ' touchstart.' + activates.attr('id'), function (e) {
|
||||
|
||||
if (!activates.is(e.target) && !origin.is(e.target) && !origin.find(e.target).length) {
|
||||
|
||||
hideDropdown();
|
||||
$(document).unbind('click.' + activates.attr('id') + ' touchstart.' + activates.attr('id'));
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
origin.on('open', function (e, eventType) {
|
||||
|
||||
placeDropdown(eventType);
|
||||
});
|
||||
|
||||
origin.on('close', hideDropdown);
|
||||
});
|
||||
};
|
||||
|
||||
$.fn.dropdown.defaults = {
|
||||
inDuration: 300,
|
||||
outDuration: 225,
|
||||
constrain_width: true,
|
||||
hover: false,
|
||||
gutter: 0,
|
||||
belowOrigin: false,
|
||||
alignment: 'left'
|
||||
};
|
||||
|
||||
$('.dropdown-button').dropdown();
|
||||
})(jQuery);
|
||||
|
||||
var dropdownSelectors = $('.dropdown, .dropup');
|
||||
|
||||
// Custom function to read dropdown data
|
||||
function dropdownEffectData(target) {
|
||||
|
||||
// TODO - page level global?
|
||||
var effectInDefault = 'fadeIn';
|
||||
var effectOutDefault = 'fadeOut';
|
||||
var dropdown = $(target);
|
||||
var dropdownMenu = $('.dropdown-menu', target);
|
||||
var parentUl = dropdown.parents('ul.nav');
|
||||
|
||||
// If parent is ul.nav allow global effect settings
|
||||
if (parentUl.height > 0) {
|
||||
|
||||
effectInDefault = parentUl.data('dropdown-in') || null;
|
||||
effectOutDefault = parentUl.data('dropdown-out') || null;
|
||||
}
|
||||
|
||||
return {
|
||||
target: target,
|
||||
dropdown: dropdown,
|
||||
dropdownMenu: dropdownMenu,
|
||||
effectIn: dropdownMenu.data('dropdown-in') || effectInDefault,
|
||||
effectOut: dropdownMenu.data('dropdown-out') || effectOutDefault
|
||||
};
|
||||
}
|
||||
|
||||
// Custom function to start effect (in or out)
|
||||
function dropdownEffectStart(data, effectToStart) {
|
||||
|
||||
if (effectToStart) {
|
||||
|
||||
data.dropdown.addClass('dropdown-animating');
|
||||
data.dropdownMenu.addClass(['animated', effectToStart].join(' '));
|
||||
}
|
||||
}
|
||||
|
||||
// Custom function to read when animation is over
|
||||
function dropdownEffectEnd(data, callbackFunc) {
|
||||
|
||||
var animationEnd = 'webkitAnimationEnd mozAnimationEnd MSAnimationEnd oanimationend animationend';
|
||||
data.dropdown.one(animationEnd, function () {
|
||||
|
||||
data.dropdown.removeClass('dropdown-animating');
|
||||
data.dropdownMenu.removeClass(['animated', data.effectIn, data.effectOut].join(' '));
|
||||
|
||||
// Custom callback option, used to remove open class in out effect
|
||||
if (typeof callbackFunc === 'function') {
|
||||
|
||||
callbackFunc();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Bootstrap API hooks
|
||||
dropdownSelectors.on({
|
||||
'show.bs.dropdown': function showBsDropdown() {
|
||||
// On show, start in effect
|
||||
var dropdown = dropdownEffectData(this);
|
||||
dropdownEffectStart(dropdown, dropdown.effectIn);
|
||||
},
|
||||
'shown.bs.dropdown': function shownBsDropdown() {
|
||||
// On shown, remove in effect once complete
|
||||
var dropdown = dropdownEffectData(this);
|
||||
if (dropdown.effectIn && dropdown.effectOut) {
|
||||
dropdownEffectEnd(dropdown);
|
||||
}
|
||||
},
|
||||
'hide.bs.dropdown': function hideBsDropdown(e) {
|
||||
// On hide, start out effect
|
||||
var dropdown = dropdownEffectData(this);
|
||||
if (dropdown.effectOut) {
|
||||
|
||||
e.preventDefault();
|
||||
dropdownEffectStart(dropdown, dropdown.effectOut);
|
||||
dropdownEffectEnd(dropdown, function () {
|
||||
|
||||
dropdown.dropdown.removeClass('show');
|
||||
dropdown.dropdownMenu.removeClass('show');
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
Reference in New Issue
Block a user