mirror of
https://github.com/netbox-community/netbox.git
synced 2024-05-10 07:54:54 +00:00
258 lines
6.8 KiB
JavaScript
258 lines
6.8 KiB
JavaScript
define([
|
|
'jquery',
|
|
'../utils'
|
|
], function ($, Utils) {
|
|
function AttachBody (decorated, $element, options) {
|
|
this.$dropdownParent = $(options.get('dropdownParent') || document.body);
|
|
|
|
decorated.call(this, $element, options);
|
|
}
|
|
|
|
AttachBody.prototype.bind = function (decorated, container, $container) {
|
|
var self = this;
|
|
|
|
decorated.call(this, container, $container);
|
|
|
|
container.on('open', function () {
|
|
self._showDropdown();
|
|
self._attachPositioningHandler(container);
|
|
|
|
// Must bind after the results handlers to ensure correct sizing
|
|
self._bindContainerResultHandlers(container);
|
|
});
|
|
|
|
container.on('close', function () {
|
|
self._hideDropdown();
|
|
self._detachPositioningHandler(container);
|
|
});
|
|
|
|
this.$dropdownContainer.on('mousedown', function (evt) {
|
|
evt.stopPropagation();
|
|
});
|
|
};
|
|
|
|
AttachBody.prototype.destroy = function (decorated) {
|
|
decorated.call(this);
|
|
|
|
this.$dropdownContainer.remove();
|
|
};
|
|
|
|
AttachBody.prototype.position = function (decorated, $dropdown, $container) {
|
|
// Clone all of the container classes
|
|
$dropdown.attr('class', $container.attr('class'));
|
|
|
|
$dropdown.removeClass('select2');
|
|
$dropdown.addClass('select2-container--open');
|
|
|
|
$dropdown.css({
|
|
position: 'absolute',
|
|
top: -999999
|
|
});
|
|
|
|
this.$container = $container;
|
|
};
|
|
|
|
AttachBody.prototype.render = function (decorated) {
|
|
var $container = $('<span></span>');
|
|
|
|
var $dropdown = decorated.call(this);
|
|
$container.append($dropdown);
|
|
|
|
this.$dropdownContainer = $container;
|
|
|
|
return $container;
|
|
};
|
|
|
|
AttachBody.prototype._hideDropdown = function (decorated) {
|
|
this.$dropdownContainer.detach();
|
|
};
|
|
|
|
AttachBody.prototype._bindContainerResultHandlers =
|
|
function (decorated, container) {
|
|
|
|
// These should only be bound once
|
|
if (this._containerResultsHandlersBound) {
|
|
return;
|
|
}
|
|
|
|
var self = this;
|
|
|
|
container.on('results:all', function () {
|
|
self._positionDropdown();
|
|
self._resizeDropdown();
|
|
});
|
|
|
|
container.on('results:append', function () {
|
|
self._positionDropdown();
|
|
self._resizeDropdown();
|
|
});
|
|
|
|
container.on('results:message', function () {
|
|
self._positionDropdown();
|
|
self._resizeDropdown();
|
|
});
|
|
|
|
container.on('select', function () {
|
|
self._positionDropdown();
|
|
self._resizeDropdown();
|
|
});
|
|
|
|
container.on('unselect', function () {
|
|
self._positionDropdown();
|
|
self._resizeDropdown();
|
|
});
|
|
|
|
this._containerResultsHandlersBound = true;
|
|
};
|
|
|
|
AttachBody.prototype._attachPositioningHandler =
|
|
function (decorated, container) {
|
|
var self = this;
|
|
|
|
var scrollEvent = 'scroll.select2.' + container.id;
|
|
var resizeEvent = 'resize.select2.' + container.id;
|
|
var orientationEvent = 'orientationchange.select2.' + container.id;
|
|
|
|
var $watchers = this.$container.parents().filter(Utils.hasScroll);
|
|
$watchers.each(function () {
|
|
Utils.StoreData(this, 'select2-scroll-position', {
|
|
x: $(this).scrollLeft(),
|
|
y: $(this).scrollTop()
|
|
});
|
|
});
|
|
|
|
$watchers.on(scrollEvent, function (ev) {
|
|
var position = Utils.GetData(this, 'select2-scroll-position');
|
|
$(this).scrollTop(position.y);
|
|
});
|
|
|
|
$(window).on(scrollEvent + ' ' + resizeEvent + ' ' + orientationEvent,
|
|
function (e) {
|
|
self._positionDropdown();
|
|
self._resizeDropdown();
|
|
});
|
|
};
|
|
|
|
AttachBody.prototype._detachPositioningHandler =
|
|
function (decorated, container) {
|
|
var scrollEvent = 'scroll.select2.' + container.id;
|
|
var resizeEvent = 'resize.select2.' + container.id;
|
|
var orientationEvent = 'orientationchange.select2.' + container.id;
|
|
|
|
var $watchers = this.$container.parents().filter(Utils.hasScroll);
|
|
$watchers.off(scrollEvent);
|
|
|
|
$(window).off(scrollEvent + ' ' + resizeEvent + ' ' + orientationEvent);
|
|
};
|
|
|
|
AttachBody.prototype._positionDropdown = function () {
|
|
var $window = $(window);
|
|
|
|
var isCurrentlyAbove = this.$dropdown.hasClass('select2-dropdown--above');
|
|
var isCurrentlyBelow = this.$dropdown.hasClass('select2-dropdown--below');
|
|
|
|
var newDirection = null;
|
|
|
|
var offset = this.$container.offset();
|
|
|
|
offset.bottom = offset.top + this.$container.outerHeight(false);
|
|
|
|
var container = {
|
|
height: this.$container.outerHeight(false)
|
|
};
|
|
|
|
container.top = offset.top;
|
|
container.bottom = offset.top + container.height;
|
|
|
|
var dropdown = {
|
|
height: this.$dropdown.outerHeight(false)
|
|
};
|
|
|
|
var viewport = {
|
|
top: $window.scrollTop(),
|
|
bottom: $window.scrollTop() + $window.height()
|
|
};
|
|
|
|
var enoughRoomAbove = viewport.top < (offset.top - dropdown.height);
|
|
var enoughRoomBelow = viewport.bottom > (offset.bottom + dropdown.height);
|
|
|
|
var css = {
|
|
left: offset.left,
|
|
top: container.bottom
|
|
};
|
|
|
|
// Determine what the parent element is to use for calculating the offset
|
|
var $offsetParent = this.$dropdownParent;
|
|
|
|
// For statically positioned elements, we need to get the element
|
|
// that is determining the offset
|
|
if ($offsetParent.css('position') === 'static') {
|
|
$offsetParent = $offsetParent.offsetParent();
|
|
}
|
|
|
|
var parentOffset = {
|
|
top: 0,
|
|
left: 0
|
|
};
|
|
|
|
if (
|
|
$.contains(document.body, $offsetParent[0]) ||
|
|
$offsetParent[0].isConnected
|
|
) {
|
|
parentOffset = $offsetParent.offset();
|
|
}
|
|
|
|
css.top -= parentOffset.top;
|
|
css.left -= parentOffset.left;
|
|
|
|
if (!isCurrentlyAbove && !isCurrentlyBelow) {
|
|
newDirection = 'below';
|
|
}
|
|
|
|
if (!enoughRoomBelow && enoughRoomAbove && !isCurrentlyAbove) {
|
|
newDirection = 'above';
|
|
} else if (!enoughRoomAbove && enoughRoomBelow && isCurrentlyAbove) {
|
|
newDirection = 'below';
|
|
}
|
|
|
|
if (newDirection == 'above' ||
|
|
(isCurrentlyAbove && newDirection !== 'below')) {
|
|
css.top = container.top - parentOffset.top - dropdown.height;
|
|
}
|
|
|
|
if (newDirection != null) {
|
|
this.$dropdown
|
|
.removeClass('select2-dropdown--below select2-dropdown--above')
|
|
.addClass('select2-dropdown--' + newDirection);
|
|
this.$container
|
|
.removeClass('select2-container--below select2-container--above')
|
|
.addClass('select2-container--' + newDirection);
|
|
}
|
|
|
|
this.$dropdownContainer.css(css);
|
|
};
|
|
|
|
AttachBody.prototype._resizeDropdown = function () {
|
|
var css = {
|
|
width: this.$container.outerWidth(false) + 'px'
|
|
};
|
|
|
|
if (this.options.get('dropdownAutoWidth')) {
|
|
css.minWidth = css.width;
|
|
css.position = 'relative';
|
|
css.width = 'auto';
|
|
}
|
|
|
|
this.$dropdown.css(css);
|
|
};
|
|
|
|
AttachBody.prototype._showDropdown = function (decorated) {
|
|
this.$dropdownContainer.appendTo(this.$dropdownParent);
|
|
|
|
this._positionDropdown();
|
|
this._resizeDropdown();
|
|
};
|
|
|
|
return AttachBody;
|
|
});
|