Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Split out some utils #325

Merged
merged 1 commit into from
Sep 26, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions src/js/abutment.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
import TetherBase from './utils';
import { updateClasses } from './utils/classes';
import { defer } from './utils/deferred';

const { getBounds } = TetherBase.Utils;
import { getBounds } from './utils/bounds';

TetherBase.modules.push({
position({ top, left }) {
Expand Down
7 changes: 2 additions & 5 deletions src/js/constraint.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,8 @@
import TetherBase from './utils';
import { updateClasses } from './utils/classes';
import { defer } from './utils/deferred';

const {
getBounds,
extend
} = TetherBase.Utils;
import { extend } from './utils/general';
import { getBounds } from './utils/bounds';

const BOUNDS_FORMAT = ['left', 'top', 'right', 'bottom'];

Expand Down
7 changes: 3 additions & 4 deletions src/js/tether.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,18 +8,17 @@ import { Evented } from './evented';
import TetherBase from './utils';
import { addClass, removeClass, updateClasses } from './utils/classes';
import { defer, flush } from './utils/deferred';
import { extend } from './utils/general';
import { addOffset, attachmentToOffset, autoToFixedAttachment, offsetToPx, parseTopLeft } from './utils/offset';
import { getBounds, removeUtilElements } from './utils/bounds';
import './constraint';
import './abutment';
import './shift';

const {
getScrollParents,
getBounds,
getOffsetParent,
extend,
getScrollBarSize,
removeUtilElements
getScrollBarSize
} = TetherBase.Utils;

function isFullscreenElement(e) {
Expand Down
137 changes: 2 additions & 135 deletions src/js/utils.js
Original file line number Diff line number Diff line change
@@ -1,42 +1,10 @@
import { defer } from './utils/deferred';
import { extend } from './utils/general';

let TetherBase;
if (typeof TetherBase === 'undefined') {
TetherBase = { modules: [] };
}

let zeroElement = null;

// Same as native getBoundingClientRect, except it takes into account parent <frame> offsets
// if the element lies within a nested document (<frame> or <iframe>-like).
function getActualBoundingClientRect(node) {
let boundingRect = node.getBoundingClientRect();

// The original object returned by getBoundingClientRect is immutable, so we clone it
// We can't use extend because the properties are not considered part of the object by hasOwnProperty in IE9
let rect = {};
for (let k in boundingRect) {
rect[k] = boundingRect[k];
}

try {
if (node.ownerDocument !== document) {
let { frameElement } = node.ownerDocument.defaultView;
if (frameElement) {
let frameRect = getActualBoundingClientRect(frameElement);
rect.top += frameRect.top;
rect.bottom += frameRect.top;
rect.left += frameRect.left;
rect.right += frameRect.left;
}
}
} catch(err) {
// Ignore "Access is denied" in IE11/Edge
}

return rect;
}

function getScrollParents(el) {
// In firefox if the el is inside an iframe with display: none; window.getComputedStyle() will return null;
// https://bugzilla.mozilla.org/show_bug.cgi?id=548397
Expand Down Expand Up @@ -80,85 +48,6 @@ function getScrollParents(el) {
return parents;
}

const uniqueId = (() => {
let id = 0;
return () => ++id;
})();

const zeroPosCache = {};
const getOrigin = () => {
// getBoundingClientRect is unfortunately too accurate. It introduces a pixel or two of
// jitter as the user scrolls that messes with our ability to detect if two positions
// are equivilant or not. We place an element at the top left of the page that will
// get the same jitter, so we can cancel the two out.
let node = zeroElement;
if (!node || !document.body.contains(node)) {
node = document.createElement('div');
node.setAttribute('data-tether-id', uniqueId());
extend(node.style, {
top: 0,
left: 0,
position: 'absolute'
});

document.body.appendChild(node);

zeroElement = node;
}

const id = node.getAttribute('data-tether-id');
if (typeof zeroPosCache[id] === 'undefined') {
zeroPosCache[id] = getActualBoundingClientRect(node);

// Clear the cache when this position call is done
defer(() => {
delete zeroPosCache[id];
});
}

return zeroPosCache[id];
};

function removeUtilElements() {
if (zeroElement) {
document.body.removeChild(zeroElement);
}
zeroElement = null;
}

function getBounds(el) {
let doc;
if (el === document) {
doc = document;
el = document.documentElement;
} else {
doc = el.ownerDocument;
}

const docEl = doc.documentElement;

const box = getActualBoundingClientRect(el);

const origin = getOrigin();

box.top -= origin.top;
box.left -= origin.left;

if (typeof box.width === 'undefined') {
box.width = document.body.scrollWidth - box.left - box.right;
}
if (typeof box.height === 'undefined') {
box.height = document.body.scrollHeight - box.top - box.bottom;
}

box.top = box.top - docEl.clientTop;
box.left = box.left - docEl.clientLeft;
box.right = doc.body.clientWidth - box.width - box.left;
box.bottom = doc.body.clientHeight - box.height - box.top;

return box;
}

function getOffsetParent(el) {
return el.offsetParent || document.documentElement;
}
Expand Down Expand Up @@ -205,32 +94,10 @@ function getScrollBarSize() {
return _scrollBarSize;
}

function extend(out = {}) {
const args = [];

Array.prototype.push.apply(args, arguments);

args.slice(1).forEach((obj) => {
if (obj) {
for (let key in obj) {
if ({}.hasOwnProperty.call(obj, key)) {
out[key] = obj[key];
}
}
}
});

return out;
}

TetherBase.Utils = {
getScrollParents,
getBounds,
getOffsetParent,
extend,
uniqueId,
getScrollBarSize,
removeUtilElements
getScrollBarSize
};

export default TetherBase;
111 changes: 111 additions & 0 deletions src/js/utils/bounds.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
import { defer } from './deferred';
import { extend, uniqueId } from './general';

const zeroPosCache = {};
let zeroElement = null;

export function getBounds(el) {
let doc;
if (el === document) {
doc = document;
el = document.documentElement;
} else {
doc = el.ownerDocument;
}

const docEl = doc.documentElement;

const box = _getActualBoundingClientRect(el);

const origin = _getOrigin();

box.top -= origin.top;
box.left -= origin.left;

if (typeof box.width === 'undefined') {
box.width = document.body.scrollWidth - box.left - box.right;
}
if (typeof box.height === 'undefined') {
box.height = document.body.scrollHeight - box.top - box.bottom;
}

box.top = box.top - docEl.clientTop;
box.left = box.left - docEl.clientLeft;
box.right = doc.body.clientWidth - box.width - box.left;
box.bottom = doc.body.clientHeight - box.height - box.top;

return box;
}

export function removeUtilElements() {
if (zeroElement) {
document.body.removeChild(zeroElement);
}
zeroElement = null;
}

/**
* Same as native getBoundingClientRect, except it takes into account parent <frame> offsets
* if the element lies within a nested document (<frame> or <iframe>-like).
* @param node
*/
function _getActualBoundingClientRect(node) {
let boundingRect = node.getBoundingClientRect();

// The original object returned by getBoundingClientRect is immutable, so we clone it
// We can't use extend because the properties are not considered part of the object by hasOwnProperty in IE9
let rect = {};
for (let k in boundingRect) {
rect[k] = boundingRect[k];
}

try {
if (node.ownerDocument !== document) {
let { frameElement } = node.ownerDocument.defaultView;
if (frameElement) {
let frameRect = _getActualBoundingClientRect(frameElement);
rect.top += frameRect.top;
rect.bottom += frameRect.top;
rect.left += frameRect.left;
rect.right += frameRect.left;
}
}
} catch(err) {
// Ignore "Access is denied" in IE11/Edge
}

return rect;
}

function _getOrigin() {
// getBoundingClientRect is unfortunately too accurate. It introduces a pixel or two of
// jitter as the user scrolls that messes with our ability to detect if two positions
// are equivilant or not. We place an element at the top left of the page that will
// get the same jitter, so we can cancel the two out.
let node = zeroElement;
if (!node || !document.body.contains(node)) {
node = document.createElement('div');
node.setAttribute('data-tether-id', uniqueId());
extend(node.style, {
top: 0,
left: 0,
position: 'absolute'
});

document.body.appendChild(node);

zeroElement = node;
}

const id = node.getAttribute('data-tether-id');
if (typeof zeroPosCache[id] === 'undefined') {
zeroPosCache[id] = _getActualBoundingClientRect(node);

// Clear the cache when this position call is done
defer(() => {
delete zeroPosCache[id];
});
}

return zeroPosCache[id];
}
22 changes: 22 additions & 0 deletions src/js/utils/general.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
export function extend(out = {}) {
const args = [];

Array.prototype.push.apply(args, arguments);

args.slice(1).forEach((obj) => {
if (obj) {
for (let key in obj) {
if ({}.hasOwnProperty.call(obj, key)) {
out[key] = obj[key];
}
}
}
});

return out;
}

export const uniqueId = (() => {
let id = 0;
return () => ++id;
})();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why call it export?