'use strict'; import { rtl as Rtl } from "./foundation.core.utils"; var Box = { ImNotTouchingYou: ImNotTouchingYou, OverlapArea: OverlapArea, GetDimensions: GetDimensions, GetOffsets: GetOffsets, GetExplicitOffsets: GetExplicitOffsets } /** * Compares the dimensions of an element to a container and determines collision events with container. * @function * @param {jQuery} element - jQuery object to test for collisions. * @param {jQuery} parent - jQuery object to use as bounding container. * @param {Boolean} lrOnly - set to true to check left and right values only. * @param {Boolean} tbOnly - set to true to check top and bottom values only. * @default if no parent object passed, detects collisions with `window`. * @returns {Boolean} - true if collision free, false if a collision in any direction. */ function ImNotTouchingYou(element, parent, lrOnly, tbOnly, ignoreBottom) { return OverlapArea(element, parent, lrOnly, tbOnly, ignoreBottom) === 0; }; function OverlapArea(element, parent, lrOnly, tbOnly, ignoreBottom) { var eleDims = GetDimensions(element), topOver, bottomOver, leftOver, rightOver; if (parent) { var parDims = GetDimensions(parent); 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 { 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); } bottomOver = ignoreBottom ? 0 : Math.min(bottomOver, 0); topOver = Math.min(topOver, 0); leftOver = Math.min(leftOver, 0); rightOver = Math.min(rightOver, 0); if (lrOnly) { return leftOver + rightOver; } if (tbOnly) { return topOver + bottomOver; } // 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. * @function * @param {jQuery || HTML} element - jQuery object or DOM element for which to get the dimensions. Can be any element other that document or window. * @returns {Object} - nested object of integer pixel values * TODO - if element is window, return only those values. */ function GetDimensions(elem){ elem = elem.length ? elem[0] : elem; if (elem === window || elem === document) { throw new Error("I'm sorry, Dave. I'm afraid I can't do that."); } var rect = elem.getBoundingClientRect(), parRect = elem.parentNode.getBoundingClientRect(), winRect = document.body.getBoundingClientRect(), winY = window.pageYOffset, winX = window.pageXOffset; return { width: rect.width, height: rect.height, offset: { top: rect.top + winY, left: rect.left + winX }, parentDims: { width: parRect.width, height: parRect.height, offset: { top: parRect.top + winY, left: parRect.left + winX } }, windowDims: { width: winRect.width, height: winRect.height, offset: { top: winY, left: winX } } } } /** * Returns an object of top and left integer pixel values for dynamically rendered elements, * such as: Tooltip, Reveal, and Dropdown. Maintained for backwards compatibility, and where * you don't know alignment, but generally from * 6.4 forward you should use GetExplicitOffsets, as GetOffsets conflates position and alignment. * @function * @param {jQuery} element - jQuery object for the element being positioned. * @param {jQuery} anchor - jQuery object for the element's anchor point. * @param {String} position - a string relating to the desired position of the element, relative to it's anchor * @param {Number} vOffset - integer pixel value of desired vertical separation between anchor and element. * @param {Number} hOffset - integer pixel value of desired horizontal separation between anchor and element. * @param {Boolean} isOverflow - if a collision event is detected, sets to true to default the element to full width - any desired offset. * TODO alter/rewrite to work with `em` values as well/instead of pixels */ function GetOffsets(element, anchor, position, vOffset, hOffset, isOverflow) { console.log("NOTE: GetOffsets is deprecated in favor of GetExplicitOffsets and will be removed in 6.5"); switch (position) { case 'top': return Rtl() ? GetExplicitOffsets(element, anchor, 'top', 'left', vOffset, hOffset, isOverflow) : GetExplicitOffsets(element, anchor, 'top', 'right', vOffset, hOffset, isOverflow); case 'bottom': return Rtl() ? GetExplicitOffsets(element, anchor, 'bottom', 'left', vOffset, hOffset, isOverflow) : GetExplicitOffsets(element, anchor, 'bottom', 'right', vOffset, hOffset, isOverflow); case 'center top': return GetExplicitOffsets(element, anchor, 'top', 'center', vOffset, hOffset, isOverflow); case 'center bottom': return GetExplicitOffsets(element, anchor, 'bottom', 'center', vOffset, hOffset, isOverflow); case 'center left': return GetExplicitOffsets(element, anchor, 'left', 'center', vOffset, hOffset, isOverflow); case 'center right': return GetExplicitOffsets(element, anchor, 'right', 'center', vOffset, hOffset, isOverflow); case 'left bottom': return GetExplicitOffsets(element, anchor, 'bottom', 'left', vOffset, hOffset, isOverflow); case 'right bottom': return GetExplicitOffsets(element, anchor, 'bottom', 'right', vOffset, hOffset, isOverflow); // Backwards compatibility... this along with the reveal and reveal full // classes are the only ones that didn't reference anchor case 'center': return { left: ($eleDims.windowDims.offset.left + ($eleDims.windowDims.width / 2)) - ($eleDims.width / 2) + hOffset, top: ($eleDims.windowDims.offset.top + ($eleDims.windowDims.height / 2)) - ($eleDims.height / 2 + vOffset) } case 'reveal': return { left: ($eleDims.windowDims.width - $eleDims.width) / 2 + hOffset, top: $eleDims.windowDims.offset.top + vOffset } case 'reveal full': return { left: $eleDims.windowDims.offset.left, top: $eleDims.windowDims.offset.top } break; default: return { left: (Rtl() ? $anchorDims.offset.left - $eleDims.width + $anchorDims.width - hOffset: $anchorDims.offset.left + hOffset), top: $anchorDims.offset.top + $anchorDims.height + vOffset } } } function GetExplicitOffsets(element, anchor, position, alignment, vOffset, hOffset, isOverflow) { var $eleDims = GetDimensions(element), $anchorDims = anchor ? GetDimensions(anchor) : null; var topVal, leftVal; // set position related attribute switch (position) { case 'top': topVal = $anchorDims.offset.top - ($eleDims.height + vOffset); break; case 'bottom': topVal = $anchorDims.offset.top + $anchorDims.height + vOffset; break; case 'left': leftVal = $anchorDims.offset.left - ($eleDims.width + hOffset); break; case 'right': leftVal = $anchorDims.offset.left + $anchorDims.width + hOffset; break; } // set alignment related attribute switch (position) { case 'top': case 'bottom': switch (alignment) { case 'left': leftVal = $anchorDims.offset.left + hOffset; break; case 'right': leftVal = $anchorDims.offset.left - $eleDims.width + $anchorDims.width - hOffset; break; case 'center': leftVal = isOverflow ? hOffset : (($anchorDims.offset.left + ($anchorDims.width / 2)) - ($eleDims.width / 2)) + hOffset; break; } break; case 'right': case 'left': switch (alignment) { case 'bottom': topVal = $anchorDims.offset.top - vOffset + $anchorDims.height - $eleDims.height; break; case 'top': topVal = $anchorDims.offset.top + vOffset break; case 'center': topVal = ($anchorDims.offset.top + vOffset + ($anchorDims.height / 2)) - ($eleDims.height / 2) break; } break; } return {top: topVal, left: leftVal}; } export {Box};