|
@@ -0,0 +1,347 @@
|
|
|
+'use strict';
|
|
|
+
|
|
|
+/**
|
|
|
+ * Copyright Marc J. Schmidt. See the LICENSE file at the top-level
|
|
|
+ * directory of this distribution and at
|
|
|
+ * https://github.com/marcj/css-element-queries/blob/master/LICENSE.
|
|
|
+ */
|
|
|
+(function (root, factory) {
|
|
|
+ if (typeof define === "function" && define.amd) {
|
|
|
+ define(factory);
|
|
|
+ } else if (typeof exports === "object") {
|
|
|
+ module.exports = factory();
|
|
|
+ } else {
|
|
|
+ root.ResizeSensor = factory();
|
|
|
+ }
|
|
|
+}(typeof window !== 'undefined' ? window : this, function () {
|
|
|
+
|
|
|
+ // Make sure it does not throw in a SSR (Server Side Rendering) situation
|
|
|
+ if (typeof window === "undefined") {
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ // https://github.com/Semantic-Org/Semantic-UI/issues/3855
|
|
|
+ // https://github.com/marcj/css-element-queries/issues/257
|
|
|
+ var globalWindow = typeof window != 'undefined' && window.Math == Math
|
|
|
+ ? window
|
|
|
+ : typeof self != 'undefined' && self.Math == Math
|
|
|
+ ? self
|
|
|
+ : Function('return this')();
|
|
|
+ // Only used for the dirty checking, so the event callback count is limited to max 1 call per fps per sensor.
|
|
|
+ // In combination with the event based resize sensor this saves cpu time, because the sensor is too fast and
|
|
|
+ // would generate too many unnecessary events.
|
|
|
+ var requestAnimationFrame = globalWindow.requestAnimationFrame ||
|
|
|
+ globalWindow.mozRequestAnimationFrame ||
|
|
|
+ globalWindow.webkitRequestAnimationFrame ||
|
|
|
+ function (fn) {
|
|
|
+ return globalWindow.setTimeout(fn, 20);
|
|
|
+ };
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Iterate over each of the provided element(s).
|
|
|
+ *
|
|
|
+ * @param {HTMLElement|HTMLElement[]} elements
|
|
|
+ * @param {Function} callback
|
|
|
+ */
|
|
|
+ function forEachElement(elements, callback){
|
|
|
+ var elementsType = Object.prototype.toString.call(elements);
|
|
|
+ var isCollectionTyped = ('[object Array]' === elementsType
|
|
|
+ || ('[object NodeList]' === elementsType)
|
|
|
+ || ('[object HTMLCollection]' === elementsType)
|
|
|
+ || ('[object Object]' === elementsType)
|
|
|
+ || ('undefined' !== typeof jQuery && elements instanceof jQuery) //jquery
|
|
|
+ || ('undefined' !== typeof Elements && elements instanceof Elements) //mootools
|
|
|
+ );
|
|
|
+ var i = 0, j = elements.length;
|
|
|
+ if (isCollectionTyped) {
|
|
|
+ for (; i < j; i++) {
|
|
|
+ callback(elements[i]);
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ callback(elements);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Get element size
|
|
|
+ * @param {HTMLElement} element
|
|
|
+ * @returns {Object} {width, height}
|
|
|
+ */
|
|
|
+ function getElementSize(element) {
|
|
|
+ if (!element.getBoundingClientRect) {
|
|
|
+ return {
|
|
|
+ width: element.offsetWidth,
|
|
|
+ height: element.offsetHeight
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ var rect = element.getBoundingClientRect();
|
|
|
+ return {
|
|
|
+ width: Math.round(rect.width),
|
|
|
+ height: Math.round(rect.height)
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Apply CSS styles to element.
|
|
|
+ *
|
|
|
+ * @param {HTMLElement} element
|
|
|
+ * @param {Object} style
|
|
|
+ */
|
|
|
+ function setStyle(element, style) {
|
|
|
+ Object.keys(style).forEach(function(key) {
|
|
|
+ element.style[key] = style[key];
|
|
|
+ });
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Class for dimension change detection.
|
|
|
+ *
|
|
|
+ * @param {Element|Element[]|Elements|jQuery} element
|
|
|
+ * @param {Function} callback
|
|
|
+ *
|
|
|
+ * @constructor
|
|
|
+ */
|
|
|
+ var ResizeSensor = function(element, callback) {
|
|
|
+ /**
|
|
|
+ *
|
|
|
+ * @constructor
|
|
|
+ */
|
|
|
+ function EventQueue() {
|
|
|
+ var q = [];
|
|
|
+ this.add = function(ev) {
|
|
|
+ q.push(ev);
|
|
|
+ };
|
|
|
+
|
|
|
+ var i, j;
|
|
|
+ this.call = function(sizeInfo) {
|
|
|
+ for (i = 0, j = q.length; i < j; i++) {
|
|
|
+ q[i].call(this, sizeInfo);
|
|
|
+ }
|
|
|
+ };
|
|
|
+
|
|
|
+ this.remove = function(ev) {
|
|
|
+ var newQueue = [];
|
|
|
+ for(i = 0, j = q.length; i < j; i++) {
|
|
|
+ if(q[i] !== ev) newQueue.push(q[i]);
|
|
|
+ }
|
|
|
+ q = newQueue;
|
|
|
+ };
|
|
|
+
|
|
|
+ this.length = function() {
|
|
|
+ return q.length;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ *
|
|
|
+ * @param {HTMLElement} element
|
|
|
+ * @param {Function} resized
|
|
|
+ */
|
|
|
+ function attachResizeEvent(element, resized) {
|
|
|
+ if (!element) return;
|
|
|
+ if (element.resizedAttached) {
|
|
|
+ element.resizedAttached.add(resized);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ element.resizedAttached = new EventQueue();
|
|
|
+ element.resizedAttached.add(resized);
|
|
|
+
|
|
|
+ element.resizeSensor = document.createElement('div');
|
|
|
+ element.resizeSensor.dir = 'ltr';
|
|
|
+ element.resizeSensor.className = 'resize-sensor';
|
|
|
+
|
|
|
+ var style = {
|
|
|
+ pointerEvents: 'none',
|
|
|
+ position: 'absolute',
|
|
|
+ left: '0px',
|
|
|
+ top: '0px',
|
|
|
+ right: '0px',
|
|
|
+ bottom: '0px',
|
|
|
+ overflow: 'hidden',
|
|
|
+ zIndex: '-1',
|
|
|
+ visibility: 'hidden',
|
|
|
+ maxWidth: '100%'
|
|
|
+ };
|
|
|
+ var styleChild = {
|
|
|
+ position: 'absolute',
|
|
|
+ left: '0px',
|
|
|
+ top: '0px',
|
|
|
+ transition: '0s',
|
|
|
+ };
|
|
|
+
|
|
|
+ setStyle(element.resizeSensor, style);
|
|
|
+
|
|
|
+ var expand = document.createElement('div');
|
|
|
+ expand.className = 'resize-sensor-expand';
|
|
|
+ setStyle(expand, style);
|
|
|
+
|
|
|
+ var expandChild = document.createElement('div');
|
|
|
+ setStyle(expandChild, styleChild);
|
|
|
+ expand.appendChild(expandChild);
|
|
|
+
|
|
|
+ var shrink = document.createElement('div');
|
|
|
+ shrink.className = 'resize-sensor-shrink';
|
|
|
+ setStyle(shrink, style);
|
|
|
+
|
|
|
+ var shrinkChild = document.createElement('div');
|
|
|
+ setStyle(shrinkChild, styleChild);
|
|
|
+ setStyle(shrinkChild, { width: '200%', height: '200%' });
|
|
|
+ shrink.appendChild(shrinkChild);
|
|
|
+
|
|
|
+ element.resizeSensor.appendChild(expand);
|
|
|
+ element.resizeSensor.appendChild(shrink);
|
|
|
+ element.appendChild(element.resizeSensor);
|
|
|
+
|
|
|
+ var computedStyle = window.getComputedStyle(element);
|
|
|
+ var position = computedStyle ? computedStyle.getPropertyValue('position') : null;
|
|
|
+ if ('absolute' !== position && 'relative' !== position && 'fixed' !== position) {
|
|
|
+ element.style.position = 'relative';
|
|
|
+ }
|
|
|
+
|
|
|
+ var dirty, rafId;
|
|
|
+ var size = getElementSize(element);
|
|
|
+ var lastWidth = 0;
|
|
|
+ var lastHeight = 0;
|
|
|
+ var initialHiddenCheck = true;
|
|
|
+ var lastAnimationFrame = 0;
|
|
|
+
|
|
|
+ var resetExpandShrink = function () {
|
|
|
+ var width = element.offsetWidth;
|
|
|
+ var height = element.offsetHeight;
|
|
|
+
|
|
|
+ expandChild.style.width = (width + 10) + 'px';
|
|
|
+ expandChild.style.height = (height + 10) + 'px';
|
|
|
+
|
|
|
+ expand.scrollLeft = width + 10;
|
|
|
+ expand.scrollTop = height + 10;
|
|
|
+
|
|
|
+ shrink.scrollLeft = width + 10;
|
|
|
+ shrink.scrollTop = height + 10;
|
|
|
+ };
|
|
|
+
|
|
|
+ var reset = function() {
|
|
|
+ // Check if element is hidden
|
|
|
+ if (initialHiddenCheck) {
|
|
|
+ var invisible = element.offsetWidth === 0 && element.offsetHeight === 0;
|
|
|
+ if (invisible) {
|
|
|
+ // Check in next frame
|
|
|
+ if (!lastAnimationFrame){
|
|
|
+ lastAnimationFrame = requestAnimationFrame(function(){
|
|
|
+ lastAnimationFrame = 0;
|
|
|
+
|
|
|
+ reset();
|
|
|
+ });
|
|
|
+ }
|
|
|
+
|
|
|
+ return;
|
|
|
+ } else {
|
|
|
+ // Stop checking
|
|
|
+ initialHiddenCheck = false;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ resetExpandShrink();
|
|
|
+ };
|
|
|
+ element.resizeSensor.resetSensor = reset;
|
|
|
+
|
|
|
+ var onResized = function() {
|
|
|
+ rafId = 0;
|
|
|
+
|
|
|
+ if (!dirty) return;
|
|
|
+
|
|
|
+ lastWidth = size.width;
|
|
|
+ lastHeight = size.height;
|
|
|
+
|
|
|
+ if (element.resizedAttached) {
|
|
|
+ element.resizedAttached.call(size);
|
|
|
+ }
|
|
|
+ };
|
|
|
+
|
|
|
+ var onScroll = function() {
|
|
|
+ size = getElementSize(element);
|
|
|
+ dirty = size.width !== lastWidth || size.height !== lastHeight;
|
|
|
+
|
|
|
+ if (dirty && !rafId) {
|
|
|
+ rafId = requestAnimationFrame(onResized);
|
|
|
+ }
|
|
|
+
|
|
|
+ reset();
|
|
|
+ };
|
|
|
+
|
|
|
+ var addEvent = function(el, name, cb) {
|
|
|
+ if (el.attachEvent) {
|
|
|
+ el.attachEvent('on' + name, cb);
|
|
|
+ } else {
|
|
|
+ el.addEventListener(name, cb);
|
|
|
+ }
|
|
|
+ };
|
|
|
+
|
|
|
+ addEvent(expand, 'scroll', onScroll);
|
|
|
+ addEvent(shrink, 'scroll', onScroll);
|
|
|
+
|
|
|
+ // Fix for custom Elements
|
|
|
+ requestAnimationFrame(reset);
|
|
|
+ }
|
|
|
+
|
|
|
+ forEachElement(element, function(elem){
|
|
|
+ attachResizeEvent(elem, callback);
|
|
|
+ });
|
|
|
+
|
|
|
+ this.detach = function(ev) {
|
|
|
+ ResizeSensor.detach(element, ev);
|
|
|
+ };
|
|
|
+
|
|
|
+ this.reset = function() {
|
|
|
+ element.resizeSensor.resetSensor();
|
|
|
+ };
|
|
|
+ };
|
|
|
+
|
|
|
+ ResizeSensor.reset = function(element) {
|
|
|
+ forEachElement(element, function(elem){
|
|
|
+ elem.resizeSensor.resetSensor();
|
|
|
+ });
|
|
|
+ };
|
|
|
+
|
|
|
+ ResizeSensor.detach = function(element, ev) {
|
|
|
+ forEachElement(element, function(elem){
|
|
|
+ if (!elem) return;
|
|
|
+ if(elem.resizedAttached && typeof ev === "function"){
|
|
|
+ elem.resizedAttached.remove(ev);
|
|
|
+ if(elem.resizedAttached.length()) return;
|
|
|
+ }
|
|
|
+ if (elem.resizeSensor) {
|
|
|
+ if (elem.contains(elem.resizeSensor)) {
|
|
|
+ elem.removeChild(elem.resizeSensor);
|
|
|
+ }
|
|
|
+ delete elem.resizeSensor;
|
|
|
+ delete elem.resizedAttached;
|
|
|
+ }
|
|
|
+ });
|
|
|
+ };
|
|
|
+
|
|
|
+ if (typeof MutationObserver !== "undefined") {
|
|
|
+ var observer = new MutationObserver(function (mutations) {
|
|
|
+ for (var i in mutations) {
|
|
|
+ if (mutations.hasOwnProperty(i)) {
|
|
|
+ var items = mutations[i].addedNodes;
|
|
|
+ for (var j = 0; j < items.length; j++) {
|
|
|
+ if (items[j].resizeSensor) {
|
|
|
+ ResizeSensor.reset(items[j]);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ });
|
|
|
+
|
|
|
+ document.addEventListener("DOMContentLoaded", function (event) {
|
|
|
+ observer.observe(document.body, {
|
|
|
+ childList: true,
|
|
|
+ subtree: true,
|
|
|
+ });
|
|
|
+ });
|
|
|
+ }
|
|
|
+
|
|
|
+ return ResizeSensor;
|
|
|
+
|
|
|
+}));
|