'bottom': HORIZONTAL_ALIGNMENTS
}
+function nextItem(item, array) {
+ var currentIdx = array.indexOf(item);
+ if(currentIdx === array.length - 1) {
+ return array[0];
+ } else {
+ return array[currentIdx + 1];
+ }
+}
+
class Dropdown extends Plugin {
/**
* Creates a new instance of a dropdown.
}
_setupPositionAndAlignment() {
- if(this.options.position === 'left' || this.options.position === 'right') {
- this.isHorizontallyPositioned = true;
- }
- if(this.options.position === 'top' || this.options.position === 'bottom') {
- this.isVerticallyPositioned = true;
- }
-
this.position = this.options.position === 'auto' ? this._getDefaultPosition() : this.options.position;
this.alignment = this.options.alignment === 'auto' ? this._getDefaultAlignment() : this.options.alignment;
}
* @private
*/
_reposition() {
+ if(this._alignmentsExhausted(this.position)) {
+ this.position = nextItem(this.position, POSITIONS);
+ this.alignment = ALIGNMENTS[this.position][0];
+ console.log('alignments exhausted, repositioned to ', this.position, this.alignment);
+ } else {
+ this._realign();
+ }
}
*/
_realign() {
this._addTriedPosition(this.position, this.alignment)
- var alignments = ALIGNMENTS[this.position]
- var currentIdx = alignments.indexOf(this.alignment);
- if(currentIdx === alignments.length - 1) {
- this.alignment = alignments[0];
- } else {
- this.alignment = alignments[currentIdx + 1];
- }
+ this.alignment = nextItem(this.alignment, ALIGNMENTS[this.position])
+ console.log('realigning to', this.position, this.alignment);
}
_addTriedPosition(position, alignment) {
}
_positionsExhausted() {
- if(this.options.position === 'auto') {
- } else {
- return this.triedPositions[this.position] && this.triedPositions[this.position].length == ALIGNMENTS[this.position].length;
+ var isExhausted = true;
+ for(var i = 0; i < POSITIONS.length; i++) {
+ isExhausted = isExhausted && this._alignmentsExhausted(POSITIONS[i]);
}
+ return isExhausted;
+ }
+
+ _alignmentsExhausted(position) {
+ return this.triedPositions[position] && this.triedPositions[position].length == ALIGNMENTS[position].length;
}
/**
this.$element.offset(Box.GetExplicitOffsets(this.$element, this.$anchor, this.position, this.alignment, this.options.vOffset, this.options.hOffset));
if(!this.options.allowOverlap) {
- while(!Box.ImNotTouchingYou(this.$element, this.$parent, this.isVerticallyPositioned, this.isHorizontallyPositioned) && !this._positionsExhausted()){
- if(this.options.position === 'auto') {
- this._reposition();
- } else {
- console.log('realigning');
- this._realign();
+ var overlaps = {};
+ var minOverlap = 100000000;
+ // default coordinates to how we start, in case we can't figure out better
+ var minCoordinates = {position: this.position, alignment: this.alignment};
+ while(!this._positionsExhausted()) {
+ let overlap = Box.OverlapArea(this.$element, this.$parent);
+ if(overlap === 0) {
+ return;
}
- this._setPosition();
+
+ if(overlap < minOverlap) {
+ minOverlap = overlap;
+ minCoordinates = {position: this.position, alignment: this.alignment};
+ }
+
+ this._reposition();
+
+ this.$element.offset(Box.GetExplicitOffsets(this.$element, this.$anchor, this.position, this.alignment, this.options.vOffset, this.options.hOffset));
}
+ // If we get through the entire loop, there was no non-overlapping
+ // position available. Pick the version with least overlap.
+ this.position = minCoordinates.position;
+ this.alignment = minCoordinates.alignment;
+ this.$element.offset(Box.GetExplicitOffsets(this.$element, this.$anchor, this.position, this.alignment, this.options.vOffset, this.options.hOffset));
}
}
var Box = {
ImNotTouchingYou: ImNotTouchingYou,
+ OverlapArea: OverlapArea,
GetDimensions: GetDimensions,
GetOffsets: GetOffsets,
GetExplicitOffsets: GetExplicitOffsets
* @returns {Boolean} - true if collision free, false if a collision in any direction.
*/
function ImNotTouchingYou(element, parent, lrOnly, tbOnly) {
- var eleDims = GetDimensions(element),
- top, bottom, left, right;
+ return OverlapArea(element, parent, lrOnly, tbOnly) === 0;
+};
+function OverlapArea(element, parent, lrOnly, tbOnly) {
+ var eleDims = GetDimensions(element),
+ topOver, bottomOver, leftOver, rightOver;
if (parent) {
var parDims = GetDimensions(parent);
- bottom = (eleDims.offset.top + eleDims.height <= parDims.height + parDims.offset.top);
- top = (eleDims.offset.top >= parDims.offset.top);
- left = (eleDims.offset.left >= parDims.offset.left);
- right = (eleDims.offset.left + eleDims.width <= parDims.width + parDims.offset.left);
+ bottomOver = (parDims.height + parDims.offset.top) - (eleDims.offset.top + eleDims.height);
+ topOver = eleDims.offset.top - parDims.offset.top;
+ leftOver = eleDims.offset.left - parDims.offset.left;
+ rightOver = (parDims.width + parDims.offset.left) - (eleDims.offset.left + eleDims.width);
}
else {
- bottom = (eleDims.offset.top + eleDims.height <= eleDims.windowDims.height + eleDims.windowDims.offset.top);
- top = (eleDims.offset.top >= eleDims.windowDims.offset.top);
- left = (eleDims.offset.left >= eleDims.windowDims.offset.left);
- right = (eleDims.offset.left + eleDims.width <= eleDims.windowDims.width);
+ bottomOver = (eleDims.windowDims.height + eleDims.windowDims.offset.top) - (eleDims.offset.top + eleDims.height);
+ topOver = eleDims.offset.top - eleDims.windowDims.offset.top;
+ leftOver = eleDims.offset.left - eleDims.windowDims.offset.left;
+ rightOver = eleDims.windowDims.width - (eleDims.offset.left + eleDims.width);
}
- var allDirs = [bottom, top, left, right];
+ bottomOver = Math.min(bottomOver, 0);
+ topOver = Math.min(topOver, 0);
+ leftOver = Math.min(leftOver, 0);
+ rightOver = Math.min(rightOver, 0);
if (lrOnly) {
- return left === right === true;
+ return leftOver + rightOver;
}
-
if (tbOnly) {
- return top === bottom === true;
+ return topOver + bottomOver;
}
- return allDirs.indexOf(false) === -1;
-};
+ // use sum of squares b/c we care about overlap area.
+ return Math.sqrt((topOver * topOver) + (bottomOver * bottomOver) + (leftOver * leftOver) + (rightOver * rightOver));
+}
/**
* Uses native methods to return an object of dimension values.