summaryrefslogtreecommitdiff
path: root/templates/assets/tether/src/js/utils.js
diff options
context:
space:
mode:
authorschneefux <schneefux+commit@schneefux.xyz>2017-03-22 19:19:26 +0100
committerschneefux <schneefux+commit@schneefux.xyz>2017-03-22 19:19:26 +0100
commitd42c8b528eb595101954f478e0024e3f079f6fbf (patch)
tree61a528cf94f1f6941f4683ae8736a4a0135c035e /templates/assets/tether/src/js/utils.js
downloadminionlivesmatter-d42c8b528eb595101954f478e0024e3f079f6fbf.tar.gz
minionlivesmatter-d42c8b528eb595101954f478e0024e3f079f6fbf.zip
first implementation of fun stats site
Diffstat (limited to 'templates/assets/tether/src/js/utils.js')
-rw-r--r--templates/assets/tether/src/js/utils.js370
1 files changed, 370 insertions, 0 deletions
diff --git a/templates/assets/tether/src/js/utils.js b/templates/assets/tether/src/js/utils.js
new file mode 100644
index 0000000..19ba7fe
--- /dev/null
+++ b/templates/assets/tether/src/js/utils.js
@@ -0,0 +1,370 @@
+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 (var k in boundingRect) {
+ rect[k] = boundingRect[k];
+ }
+
+ if (node.ownerDocument !== document) {
+ let frameElement = node.ownerDocument.defaultView.frameElement;
+ if (frameElement) {
+ let frameRect = getActualBoundingClientRect(frameElement);
+ rect.top += frameRect.top;
+ rect.bottom += frameRect.top;
+ rect.left += frameRect.left;
+ rect.right += frameRect.left;
+ }
+ }
+
+ 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
+ const computedStyle = getComputedStyle(el) || {};
+ const position = computedStyle.position;
+ let parents = [];
+
+ if (position === 'fixed') {
+ return [el];
+ }
+
+ let parent = el;
+ while ((parent = parent.parentNode) && parent && parent.nodeType === 1) {
+ let style;
+ try {
+ style = getComputedStyle(parent);
+ } catch (err) {}
+
+ if (typeof style === 'undefined' || style === null) {
+ parents.push(parent);
+ return parents;
+ }
+
+ const {overflow, overflowX, overflowY} = style;
+ if (/(auto|scroll)/.test(overflow + overflowY + overflowX)) {
+ if (position !== 'absolute' || ['relative', 'absolute', 'fixed'].indexOf(style.position) >= 0) {
+ parents.push(parent)
+ }
+ }
+ }
+
+ parents.push(el.ownerDocument.body);
+
+ // If the node is within a frame, account for the parent window scroll
+ if (el.ownerDocument !== document) {
+ parents.push(el.ownerDocument.defaultView);
+ }
+
+ 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;
+}
+
+let _scrollBarSize = null;
+function getScrollBarSize() {
+ if (_scrollBarSize) {
+ return _scrollBarSize;
+ }
+ const inner = document.createElement('div');
+ inner.style.width = '100%';
+ inner.style.height = '200px';
+
+ const outer = document.createElement('div');
+ extend(outer.style, {
+ position: 'absolute',
+ top: 0,
+ left: 0,
+ pointerEvents: 'none',
+ visibility: 'hidden',
+ width: '200px',
+ height: '150px',
+ overflow: 'hidden'
+ });
+
+ outer.appendChild(inner);
+
+ document.body.appendChild(outer);
+
+ const widthContained = inner.offsetWidth;
+ outer.style.overflow = 'scroll';
+ let widthScroll = inner.offsetWidth;
+
+ if (widthContained === widthScroll) {
+ widthScroll = outer.clientWidth;
+ }
+
+ document.body.removeChild(outer);
+
+ const width = widthContained - widthScroll;
+
+ _scrollBarSize = {width, height: width};
+ 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;
+}
+
+function removeClass(el, name) {
+ if (typeof el.classList !== 'undefined') {
+ name.split(' ').forEach(cls => {
+ if (cls.trim()) {
+ el.classList.remove(cls);
+ }
+ });
+ } else {
+ const regex = new RegExp(`(^| )${ name.split(' ').join('|') }( |$)`, 'gi');
+ const className = getClassName(el).replace(regex, ' ');
+ setClassName(el, className);
+ }
+}
+
+function addClass(el, name) {
+ if (typeof el.classList !== 'undefined') {
+ name.split(' ').forEach(cls => {
+ if (cls.trim()) {
+ el.classList.add(cls);
+ }
+ });
+ } else {
+ removeClass(el, name);
+ const cls = getClassName(el) + ` ${name}`;
+ setClassName(el, cls);
+ }
+}
+
+function hasClass(el, name) {
+ if (typeof el.classList !== 'undefined') {
+ return el.classList.contains(name);
+ }
+ const className = getClassName(el);
+ return new RegExp(`(^| )${ name }( |$)`, 'gi').test(className);
+}
+
+function getClassName(el) {
+ // Can't use just SVGAnimatedString here since nodes within a Frame in IE have
+ // completely separately SVGAnimatedString base classes
+ if (el.className instanceof el.ownerDocument.defaultView.SVGAnimatedString) {
+ return el.className.baseVal;
+ }
+ return el.className;
+}
+
+function setClassName(el, className) {
+ el.setAttribute('class', className);
+}
+
+
+function updateClasses(el, add, all) {
+ // Of the set of 'all' classes, we need the 'add' classes, and only the
+ // 'add' classes to be set.
+ all.forEach(cls => {
+ if (add.indexOf(cls) === -1 && hasClass(el, cls)) {
+ removeClass(el, cls);
+ }
+ });
+
+ add.forEach(cls => {
+ if (!hasClass(el, cls)) {
+ addClass(el, cls);
+ }
+ });
+}
+
+const deferred = [];
+
+const defer = (fn) => {
+ deferred.push(fn);
+};
+
+const flush = () => {
+ let fn;
+ while(fn = deferred.pop()) {
+ fn();
+ }
+};
+
+class Evented {
+ on(event, handler, ctx, once=false) {
+ if (typeof this.bindings === 'undefined') {
+ this.bindings = {};
+ }
+ if (typeof this.bindings[event] === 'undefined') {
+ this.bindings[event] = [];
+ }
+ this.bindings[event].push({handler, ctx, once});
+ }
+
+ once(event, handler, ctx) {
+ this.on(event, handler, ctx, true);
+ }
+
+ off(event, handler) {
+ if (typeof this.bindings === 'undefined' ||
+ typeof this.bindings[event] === 'undefined') {
+ return;
+ }
+
+ if (typeof handler === 'undefined') {
+ delete this.bindings[event];
+ } else {
+ let i = 0;
+ while (i < this.bindings[event].length) {
+ if (this.bindings[event][i].handler === handler) {
+ this.bindings[event].splice(i, 1);
+ } else {
+ ++i;
+ }
+ }
+ }
+ }
+
+ trigger(event, ...args) {
+ if (typeof this.bindings !== 'undefined' && this.bindings[event]) {
+ let i = 0;
+ while (i < this.bindings[event].length) {
+ const {handler, ctx, once} = this.bindings[event][i];
+
+ let context = ctx;
+ if (typeof context === 'undefined') {
+ context = this;
+ }
+
+ handler.apply(context, args);
+
+ if (once) {
+ this.bindings[event].splice(i, 1);
+ } else {
+ ++i;
+ }
+ }
+ }
+ }
+}
+
+TetherBase.Utils = {
+ getActualBoundingClientRect,
+ getScrollParents,
+ getBounds,
+ getOffsetParent,
+ extend,
+ addClass,
+ removeClass,
+ hasClass,
+ updateClasses,
+ defer,
+ flush,
+ uniqueId,
+ Evented,
+ getScrollBarSize,
+ removeUtilElements
+};