first commit
This commit is contained in:
936
js/ResizeObserver.js
Normal file
936
js/ResizeObserver.js
Normal file
@ -0,0 +1,936 @@
|
||||
(function (global, factory) {
|
||||
typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
|
||||
typeof define === 'function' && define.amd ? define(factory) :
|
||||
(global.ResizeObserver = factory());
|
||||
}(this, (function () { 'use strict';
|
||||
|
||||
/**
|
||||
* A collection of shims that provide minimal functionality of the ES6 collections.
|
||||
*
|
||||
* These implementations are not meant to be used outside of the ResizeObserver
|
||||
* modules as they cover only a limited range of use cases.
|
||||
*/
|
||||
/* eslint-disable require-jsdoc, valid-jsdoc */
|
||||
var MapShim = (function () {
|
||||
if (typeof Map !== 'undefined') {
|
||||
return Map;
|
||||
}
|
||||
/**
|
||||
* Returns index in provided array that matches the specified key.
|
||||
*
|
||||
* @param {Array<Array>} arr
|
||||
* @param {*} key
|
||||
* @returns {number}
|
||||
*/
|
||||
function getIndex(arr, key) {
|
||||
var result = -1;
|
||||
arr.some(function (entry, index) {
|
||||
if (entry[0] === key) {
|
||||
result = index;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
return result;
|
||||
}
|
||||
return /** @class */ (function () {
|
||||
function class_1() {
|
||||
this.__entries__ = [];
|
||||
}
|
||||
Object.defineProperty(class_1.prototype, "size", {
|
||||
/**
|
||||
* @returns {boolean}
|
||||
*/
|
||||
get: function () {
|
||||
return this.__entries__.length;
|
||||
},
|
||||
enumerable: true,
|
||||
configurable: true
|
||||
});
|
||||
/**
|
||||
* @param {*} key
|
||||
* @returns {*}
|
||||
*/
|
||||
class_1.prototype.get = function (key) {
|
||||
var index = getIndex(this.__entries__, key);
|
||||
var entry = this.__entries__[index];
|
||||
return entry && entry[1];
|
||||
};
|
||||
/**
|
||||
* @param {*} key
|
||||
* @param {*} value
|
||||
* @returns {void}
|
||||
*/
|
||||
class_1.prototype.set = function (key, value) {
|
||||
var index = getIndex(this.__entries__, key);
|
||||
if (~index) {
|
||||
this.__entries__[index][1] = value;
|
||||
}
|
||||
else {
|
||||
this.__entries__.push([key, value]);
|
||||
}
|
||||
};
|
||||
/**
|
||||
* @param {*} key
|
||||
* @returns {void}
|
||||
*/
|
||||
class_1.prototype.delete = function (key) {
|
||||
var entries = this.__entries__;
|
||||
var index = getIndex(entries, key);
|
||||
if (~index) {
|
||||
entries.splice(index, 1);
|
||||
}
|
||||
};
|
||||
/**
|
||||
* @param {*} key
|
||||
* @returns {void}
|
||||
*/
|
||||
class_1.prototype.has = function (key) {
|
||||
return !!~getIndex(this.__entries__, key);
|
||||
};
|
||||
/**
|
||||
* @returns {void}
|
||||
*/
|
||||
class_1.prototype.clear = function () {
|
||||
this.__entries__.splice(0);
|
||||
};
|
||||
/**
|
||||
* @param {Function} callback
|
||||
* @param {*} [ctx=null]
|
||||
* @returns {void}
|
||||
*/
|
||||
class_1.prototype.forEach = function (callback, ctx) {
|
||||
if (ctx === void 0) { ctx = null; }
|
||||
for (var _i = 0, _a = this.__entries__; _i < _a.length; _i++) {
|
||||
var entry = _a[_i];
|
||||
callback.call(ctx, entry[1], entry[0]);
|
||||
}
|
||||
};
|
||||
return class_1;
|
||||
}());
|
||||
})();
|
||||
|
||||
/**
|
||||
* Detects whether window and document objects are available in current environment.
|
||||
*/
|
||||
var isBrowser = typeof window !== 'undefined' && typeof document !== 'undefined' && window.document === document;
|
||||
|
||||
// Returns global object of a current environment.
|
||||
var global$1 = (function () {
|
||||
if (typeof global !== 'undefined' && global.Math === Math) {
|
||||
return global;
|
||||
}
|
||||
if (typeof self !== 'undefined' && self.Math === Math) {
|
||||
return self;
|
||||
}
|
||||
if (typeof window !== 'undefined' && window.Math === Math) {
|
||||
return window;
|
||||
}
|
||||
// eslint-disable-next-line no-new-func
|
||||
return Function('return this')();
|
||||
})();
|
||||
|
||||
/**
|
||||
* A shim for the requestAnimationFrame which falls back to the setTimeout if
|
||||
* first one is not supported.
|
||||
*
|
||||
* @returns {number} Requests' identifier.
|
||||
*/
|
||||
var requestAnimationFrame$1 = (function () {
|
||||
if (typeof requestAnimationFrame === 'function') {
|
||||
// It's required to use a bounded function because IE sometimes throws
|
||||
// an "Invalid calling object" error if rAF is invoked without the global
|
||||
// object on the left hand side.
|
||||
return requestAnimationFrame.bind(global$1);
|
||||
}
|
||||
return function (callback) { return setTimeout(function () { return callback(Date.now()); }, 1000 / 60); };
|
||||
})();
|
||||
|
||||
// Defines minimum timeout before adding a trailing call.
|
||||
var trailingTimeout = 2;
|
||||
/**
|
||||
* Creates a wrapper function which ensures that provided callback will be
|
||||
* invoked only once during the specified delay period.
|
||||
*
|
||||
* @param {Function} callback - Function to be invoked after the delay period.
|
||||
* @param {number} delay - Delay after which to invoke callback.
|
||||
* @returns {Function}
|
||||
*/
|
||||
function throttle (callback, delay) {
|
||||
var leadingCall = false, trailingCall = false, lastCallTime = 0;
|
||||
/**
|
||||
* Invokes the original callback function and schedules new invocation if
|
||||
* the "proxy" was called during current request.
|
||||
*
|
||||
* @returns {void}
|
||||
*/
|
||||
function resolvePending() {
|
||||
if (leadingCall) {
|
||||
leadingCall = false;
|
||||
callback();
|
||||
}
|
||||
if (trailingCall) {
|
||||
proxy();
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Callback invoked after the specified delay. It will further postpone
|
||||
* invocation of the original function delegating it to the
|
||||
* requestAnimationFrame.
|
||||
*
|
||||
* @returns {void}
|
||||
*/
|
||||
function timeoutCallback() {
|
||||
requestAnimationFrame$1(resolvePending);
|
||||
}
|
||||
/**
|
||||
* Schedules invocation of the original function.
|
||||
*
|
||||
* @returns {void}
|
||||
*/
|
||||
function proxy() {
|
||||
var timeStamp = Date.now();
|
||||
if (leadingCall) {
|
||||
// Reject immediately following calls.
|
||||
if (timeStamp - lastCallTime < trailingTimeout) {
|
||||
return;
|
||||
}
|
||||
// Schedule new call to be in invoked when the pending one is resolved.
|
||||
// This is important for "transitions" which never actually start
|
||||
// immediately so there is a chance that we might miss one if change
|
||||
// happens amids the pending invocation.
|
||||
trailingCall = true;
|
||||
}
|
||||
else {
|
||||
leadingCall = true;
|
||||
trailingCall = false;
|
||||
setTimeout(timeoutCallback, delay);
|
||||
}
|
||||
lastCallTime = timeStamp;
|
||||
}
|
||||
return proxy;
|
||||
}
|
||||
|
||||
// Minimum delay before invoking the update of observers.
|
||||
var REFRESH_DELAY = 20;
|
||||
// A list of substrings of CSS properties used to find transition events that
|
||||
// might affect dimensions of observed elements.
|
||||
var transitionKeys = ['top', 'right', 'bottom', 'left', 'width', 'height', 'size', 'weight'];
|
||||
// Check if MutationObserver is available.
|
||||
var mutationObserverSupported = typeof MutationObserver !== 'undefined';
|
||||
/**
|
||||
* Singleton controller class which handles updates of ResizeObserver instances.
|
||||
*/
|
||||
var ResizeObserverController = /** @class */ (function () {
|
||||
/**
|
||||
* Creates a new instance of ResizeObserverController.
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
function ResizeObserverController() {
|
||||
/**
|
||||
* Indicates whether DOM listeners have been added.
|
||||
*
|
||||
* @private {boolean}
|
||||
*/
|
||||
this.connected_ = false;
|
||||
/**
|
||||
* Tells that controller has subscribed for Mutation Events.
|
||||
*
|
||||
* @private {boolean}
|
||||
*/
|
||||
this.mutationEventsAdded_ = false;
|
||||
/**
|
||||
* Keeps reference to the instance of MutationObserver.
|
||||
*
|
||||
* @private {MutationObserver}
|
||||
*/
|
||||
this.mutationsObserver_ = null;
|
||||
/**
|
||||
* A list of connected observers.
|
||||
*
|
||||
* @private {Array<ResizeObserverSPI>}
|
||||
*/
|
||||
this.observers_ = [];
|
||||
this.onTransitionEnd_ = this.onTransitionEnd_.bind(this);
|
||||
this.refresh = throttle(this.refresh.bind(this), REFRESH_DELAY);
|
||||
}
|
||||
/**
|
||||
* Adds observer to observers list.
|
||||
*
|
||||
* @param {ResizeObserverSPI} observer - Observer to be added.
|
||||
* @returns {void}
|
||||
*/
|
||||
ResizeObserverController.prototype.addObserver = function (observer) {
|
||||
if (!~this.observers_.indexOf(observer)) {
|
||||
this.observers_.push(observer);
|
||||
}
|
||||
// Add listeners if they haven't been added yet.
|
||||
if (!this.connected_) {
|
||||
this.connect_();
|
||||
}
|
||||
};
|
||||
/**
|
||||
* Removes observer from observers list.
|
||||
*
|
||||
* @param {ResizeObserverSPI} observer - Observer to be removed.
|
||||
* @returns {void}
|
||||
*/
|
||||
ResizeObserverController.prototype.removeObserver = function (observer) {
|
||||
var observers = this.observers_;
|
||||
var index = observers.indexOf(observer);
|
||||
// Remove observer if it's present in registry.
|
||||
if (~index) {
|
||||
observers.splice(index, 1);
|
||||
}
|
||||
// Remove listeners if controller has no connected observers.
|
||||
if (!observers.length && this.connected_) {
|
||||
this.disconnect_();
|
||||
}
|
||||
};
|
||||
/**
|
||||
* Invokes the update of observers. It will continue running updates insofar
|
||||
* it detects changes.
|
||||
*
|
||||
* @returns {void}
|
||||
*/
|
||||
ResizeObserverController.prototype.refresh = function () {
|
||||
var changesDetected = this.updateObservers_();
|
||||
// Continue running updates if changes have been detected as there might
|
||||
// be future ones caused by CSS transitions.
|
||||
if (changesDetected) {
|
||||
this.refresh();
|
||||
}
|
||||
};
|
||||
/**
|
||||
* Updates every observer from observers list and notifies them of queued
|
||||
* entries.
|
||||
*
|
||||
* @private
|
||||
* @returns {boolean} Returns "true" if any observer has detected changes in
|
||||
* dimensions of it's elements.
|
||||
*/
|
||||
ResizeObserverController.prototype.updateObservers_ = function () {
|
||||
// Collect observers that have active observations.
|
||||
var activeObservers = this.observers_.filter(function (observer) {
|
||||
return observer.gatherActive(), observer.hasActive();
|
||||
});
|
||||
// Deliver notifications in a separate cycle in order to avoid any
|
||||
// collisions between observers, e.g. when multiple instances of
|
||||
// ResizeObserver are tracking the same element and the callback of one
|
||||
// of them changes content dimensions of the observed target. Sometimes
|
||||
// this may result in notifications being blocked for the rest of observers.
|
||||
activeObservers.forEach(function (observer) { return observer.broadcastActive(); });
|
||||
return activeObservers.length > 0;
|
||||
};
|
||||
/**
|
||||
* Initializes DOM listeners.
|
||||
*
|
||||
* @private
|
||||
* @returns {void}
|
||||
*/
|
||||
ResizeObserverController.prototype.connect_ = function () {
|
||||
// Do nothing if running in a non-browser environment or if listeners
|
||||
// have been already added.
|
||||
if (!isBrowser || this.connected_) {
|
||||
return;
|
||||
}
|
||||
// Subscription to the "Transitionend" event is used as a workaround for
|
||||
// delayed transitions. This way it's possible to capture at least the
|
||||
// final state of an element.
|
||||
document.addEventListener('transitionend', this.onTransitionEnd_);
|
||||
window.addEventListener('resize', this.refresh);
|
||||
if (mutationObserverSupported) {
|
||||
this.mutationsObserver_ = new MutationObserver(this.refresh);
|
||||
this.mutationsObserver_.observe(document, {
|
||||
attributes: true,
|
||||
childList: true,
|
||||
characterData: true,
|
||||
subtree: true
|
||||
});
|
||||
}
|
||||
else {
|
||||
document.addEventListener('DOMSubtreeModified', this.refresh);
|
||||
this.mutationEventsAdded_ = true;
|
||||
}
|
||||
this.connected_ = true;
|
||||
};
|
||||
/**
|
||||
* Removes DOM listeners.
|
||||
*
|
||||
* @private
|
||||
* @returns {void}
|
||||
*/
|
||||
ResizeObserverController.prototype.disconnect_ = function () {
|
||||
// Do nothing if running in a non-browser environment or if listeners
|
||||
// have been already removed.
|
||||
if (!isBrowser || !this.connected_) {
|
||||
return;
|
||||
}
|
||||
document.removeEventListener('transitionend', this.onTransitionEnd_);
|
||||
window.removeEventListener('resize', this.refresh);
|
||||
if (this.mutationsObserver_) {
|
||||
this.mutationsObserver_.disconnect();
|
||||
}
|
||||
if (this.mutationEventsAdded_) {
|
||||
document.removeEventListener('DOMSubtreeModified', this.refresh);
|
||||
}
|
||||
this.mutationsObserver_ = null;
|
||||
this.mutationEventsAdded_ = false;
|
||||
this.connected_ = false;
|
||||
};
|
||||
/**
|
||||
* "Transitionend" event handler.
|
||||
*
|
||||
* @private
|
||||
* @param {TransitionEvent} event
|
||||
* @returns {void}
|
||||
*/
|
||||
ResizeObserverController.prototype.onTransitionEnd_ = function (_a) {
|
||||
var _b = _a.propertyName, propertyName = _b === void 0 ? '' : _b;
|
||||
// Detect whether transition may affect dimensions of an element.
|
||||
var isReflowProperty = transitionKeys.some(function (key) {
|
||||
return !!~propertyName.indexOf(key);
|
||||
});
|
||||
if (isReflowProperty) {
|
||||
this.refresh();
|
||||
}
|
||||
};
|
||||
/**
|
||||
* Returns instance of the ResizeObserverController.
|
||||
*
|
||||
* @returns {ResizeObserverController}
|
||||
*/
|
||||
ResizeObserverController.getInstance = function () {
|
||||
if (!this.instance_) {
|
||||
this.instance_ = new ResizeObserverController();
|
||||
}
|
||||
return this.instance_;
|
||||
};
|
||||
/**
|
||||
* Holds reference to the controller's instance.
|
||||
*
|
||||
* @private {ResizeObserverController}
|
||||
*/
|
||||
ResizeObserverController.instance_ = null;
|
||||
return ResizeObserverController;
|
||||
}());
|
||||
|
||||
/**
|
||||
* Defines non-writable/enumerable properties of the provided target object.
|
||||
*
|
||||
* @param {Object} target - Object for which to define properties.
|
||||
* @param {Object} props - Properties to be defined.
|
||||
* @returns {Object} Target object.
|
||||
*/
|
||||
var defineConfigurable = (function (target, props) {
|
||||
for (var _i = 0, _a = Object.keys(props); _i < _a.length; _i++) {
|
||||
var key = _a[_i];
|
||||
Object.defineProperty(target, key, {
|
||||
value: props[key],
|
||||
enumerable: false,
|
||||
writable: false,
|
||||
configurable: true
|
||||
});
|
||||
}
|
||||
return target;
|
||||
});
|
||||
|
||||
/**
|
||||
* Returns the global object associated with provided element.
|
||||
*
|
||||
* @param {Object} target
|
||||
* @returns {Object}
|
||||
*/
|
||||
var getWindowOf = (function (target) {
|
||||
// Assume that the element is an instance of Node, which means that it
|
||||
// has the "ownerDocument" property from which we can retrieve a
|
||||
// corresponding global object.
|
||||
var ownerGlobal = target && target.ownerDocument && target.ownerDocument.defaultView;
|
||||
// Return the local global object if it's not possible extract one from
|
||||
// provided element.
|
||||
return ownerGlobal || global$1;
|
||||
});
|
||||
|
||||
// Placeholder of an empty content rectangle.
|
||||
var emptyRect = createRectInit(0, 0, 0, 0);
|
||||
/**
|
||||
* Converts provided string to a number.
|
||||
*
|
||||
* @param {number|string} value
|
||||
* @returns {number}
|
||||
*/
|
||||
function toFloat(value) {
|
||||
return parseFloat(value) || 0;
|
||||
}
|
||||
/**
|
||||
* Extracts borders size from provided styles.
|
||||
*
|
||||
* @param {CSSStyleDeclaration} styles
|
||||
* @param {...string} positions - Borders positions (top, right, ...)
|
||||
* @returns {number}
|
||||
*/
|
||||
function getBordersSize(styles) {
|
||||
var positions = [];
|
||||
for (var _i = 1; _i < arguments.length; _i++) {
|
||||
positions[_i - 1] = arguments[_i];
|
||||
}
|
||||
return positions.reduce(function (size, position) {
|
||||
var value = styles['border-' + position + '-width'];
|
||||
return size + toFloat(value);
|
||||
}, 0);
|
||||
}
|
||||
/**
|
||||
* Extracts paddings sizes from provided styles.
|
||||
*
|
||||
* @param {CSSStyleDeclaration} styles
|
||||
* @returns {Object} Paddings box.
|
||||
*/
|
||||
function getPaddings(styles) {
|
||||
var positions = ['top', 'right', 'bottom', 'left'];
|
||||
var paddings = {};
|
||||
for (var _i = 0, positions_1 = positions; _i < positions_1.length; _i++) {
|
||||
var position = positions_1[_i];
|
||||
var value = styles['padding-' + position];
|
||||
paddings[position] = toFloat(value);
|
||||
}
|
||||
return paddings;
|
||||
}
|
||||
/**
|
||||
* Calculates content rectangle of provided SVG element.
|
||||
*
|
||||
* @param {SVGGraphicsElement} target - Element content rectangle of which needs
|
||||
* to be calculated.
|
||||
* @returns {DOMRectInit}
|
||||
*/
|
||||
function getSVGContentRect(target) {
|
||||
var bbox = target.getBBox();
|
||||
return createRectInit(0, 0, bbox.width, bbox.height);
|
||||
}
|
||||
/**
|
||||
* Calculates content rectangle of provided HTMLElement.
|
||||
*
|
||||
* @param {HTMLElement} target - Element for which to calculate the content rectangle.
|
||||
* @returns {DOMRectInit}
|
||||
*/
|
||||
function getHTMLElementContentRect(target) {
|
||||
// Client width & height properties can't be
|
||||
// used exclusively as they provide rounded values.
|
||||
var clientWidth = target.clientWidth, clientHeight = target.clientHeight;
|
||||
// By this condition we can catch all non-replaced inline, hidden and
|
||||
// detached elements. Though elements with width & height properties less
|
||||
// than 0.5 will be discarded as well.
|
||||
//
|
||||
// Without it we would need to implement separate methods for each of
|
||||
// those cases and it's not possible to perform a precise and performance
|
||||
// effective test for hidden elements. E.g. even jQuery's ':visible' filter
|
||||
// gives wrong results for elements with width & height less than 0.5.
|
||||
if (!clientWidth && !clientHeight) {
|
||||
return emptyRect;
|
||||
}
|
||||
var styles = getWindowOf(target).getComputedStyle(target);
|
||||
var paddings = getPaddings(styles);
|
||||
var horizPad = paddings.left + paddings.right;
|
||||
var vertPad = paddings.top + paddings.bottom;
|
||||
// Computed styles of width & height are being used because they are the
|
||||
// only dimensions available to JS that contain non-rounded values. It could
|
||||
// be possible to utilize the getBoundingClientRect if only it's data wasn't
|
||||
// affected by CSS transformations let alone paddings, borders and scroll bars.
|
||||
var width = toFloat(styles.width), height = toFloat(styles.height);
|
||||
// Width & height include paddings and borders when the 'border-box' box
|
||||
// model is applied (except for IE).
|
||||
if (styles.boxSizing === 'border-box') {
|
||||
// Following conditions are required to handle Internet Explorer which
|
||||
// doesn't include paddings and borders to computed CSS dimensions.
|
||||
//
|
||||
// We can say that if CSS dimensions + paddings are equal to the "client"
|
||||
// properties then it's either IE, and thus we don't need to subtract
|
||||
// anything, or an element merely doesn't have paddings/borders styles.
|
||||
if (Math.round(width + horizPad) !== clientWidth) {
|
||||
width -= getBordersSize(styles, 'left', 'right') + horizPad;
|
||||
}
|
||||
if (Math.round(height + vertPad) !== clientHeight) {
|
||||
height -= getBordersSize(styles, 'top', 'bottom') + vertPad;
|
||||
}
|
||||
}
|
||||
// Following steps can't be applied to the document's root element as its
|
||||
// client[Width/Height] properties represent viewport area of the window.
|
||||
// Besides, it's as well not necessary as the <html> itself neither has
|
||||
// rendered scroll bars nor it can be clipped.
|
||||
if (!isDocumentElement(target)) {
|
||||
// In some browsers (only in Firefox, actually) CSS width & height
|
||||
// include scroll bars size which can be removed at this step as scroll
|
||||
// bars are the only difference between rounded dimensions + paddings
|
||||
// and "client" properties, though that is not always true in Chrome.
|
||||
var vertScrollbar = Math.round(width + horizPad) - clientWidth;
|
||||
var horizScrollbar = Math.round(height + vertPad) - clientHeight;
|
||||
// Chrome has a rather weird rounding of "client" properties.
|
||||
// E.g. for an element with content width of 314.2px it sometimes gives
|
||||
// the client width of 315px and for the width of 314.7px it may give
|
||||
// 314px. And it doesn't happen all the time. So just ignore this delta
|
||||
// as a non-relevant.
|
||||
if (Math.abs(vertScrollbar) !== 1) {
|
||||
width -= vertScrollbar;
|
||||
}
|
||||
if (Math.abs(horizScrollbar) !== 1) {
|
||||
height -= horizScrollbar;
|
||||
}
|
||||
}
|
||||
return createRectInit(paddings.left, paddings.top, width, height);
|
||||
}
|
||||
/**
|
||||
* Checks whether provided element is an instance of the SVGGraphicsElement.
|
||||
*
|
||||
* @param {Element} target - Element to be checked.
|
||||
* @returns {boolean}
|
||||
*/
|
||||
var isSVGGraphicsElement = (function () {
|
||||
// Some browsers, namely IE and Edge, don't have the SVGGraphicsElement
|
||||
// interface.
|
||||
if (typeof SVGGraphicsElement !== 'undefined') {
|
||||
return function (target) { return target instanceof getWindowOf(target).SVGGraphicsElement; };
|
||||
}
|
||||
// If it's so, then check that element is at least an instance of the
|
||||
// SVGElement and that it has the "getBBox" method.
|
||||
// eslint-disable-next-line no-extra-parens
|
||||
return function (target) { return (target instanceof getWindowOf(target).SVGElement &&
|
||||
typeof target.getBBox === 'function'); };
|
||||
})();
|
||||
/**
|
||||
* Checks whether provided element is a document element (<html>).
|
||||
*
|
||||
* @param {Element} target - Element to be checked.
|
||||
* @returns {boolean}
|
||||
*/
|
||||
function isDocumentElement(target) {
|
||||
return target === getWindowOf(target).document.documentElement;
|
||||
}
|
||||
/**
|
||||
* Calculates an appropriate content rectangle for provided html or svg element.
|
||||
*
|
||||
* @param {Element} target - Element content rectangle of which needs to be calculated.
|
||||
* @returns {DOMRectInit}
|
||||
*/
|
||||
function getContentRect(target) {
|
||||
if (!isBrowser) {
|
||||
return emptyRect;
|
||||
}
|
||||
if (isSVGGraphicsElement(target)) {
|
||||
return getSVGContentRect(target);
|
||||
}
|
||||
return getHTMLElementContentRect(target);
|
||||
}
|
||||
/**
|
||||
* Creates rectangle with an interface of the DOMRectReadOnly.
|
||||
* Spec: https://drafts.fxtf.org/geometry/#domrectreadonly
|
||||
*
|
||||
* @param {DOMRectInit} rectInit - Object with rectangle's x/y coordinates and dimensions.
|
||||
* @returns {DOMRectReadOnly}
|
||||
*/
|
||||
function createReadOnlyRect(_a) {
|
||||
var x = _a.x, y = _a.y, width = _a.width, height = _a.height;
|
||||
// If DOMRectReadOnly is available use it as a prototype for the rectangle.
|
||||
var Constr = typeof DOMRectReadOnly !== 'undefined' ? DOMRectReadOnly : Object;
|
||||
var rect = Object.create(Constr.prototype);
|
||||
// Rectangle's properties are not writable and non-enumerable.
|
||||
defineConfigurable(rect, {
|
||||
x: x, y: y, width: width, height: height,
|
||||
top: y,
|
||||
right: x + width,
|
||||
bottom: height + y,
|
||||
left: x
|
||||
});
|
||||
return rect;
|
||||
}
|
||||
/**
|
||||
* Creates DOMRectInit object based on the provided dimensions and the x/y coordinates.
|
||||
* Spec: https://drafts.fxtf.org/geometry/#dictdef-domrectinit
|
||||
*
|
||||
* @param {number} x - X coordinate.
|
||||
* @param {number} y - Y coordinate.
|
||||
* @param {number} width - Rectangle's width.
|
||||
* @param {number} height - Rectangle's height.
|
||||
* @returns {DOMRectInit}
|
||||
*/
|
||||
function createRectInit(x, y, width, height) {
|
||||
return { x: x, y: y, width: width, height: height };
|
||||
}
|
||||
|
||||
/**
|
||||
* Class that is responsible for computations of the content rectangle of
|
||||
* provided DOM element and for keeping track of it's changes.
|
||||
*/
|
||||
var ResizeObservation = /** @class */ (function () {
|
||||
/**
|
||||
* Creates an instance of ResizeObservation.
|
||||
*
|
||||
* @param {Element} target - Element to be observed.
|
||||
*/
|
||||
function ResizeObservation(target) {
|
||||
/**
|
||||
* Broadcasted width of content rectangle.
|
||||
*
|
||||
* @type {number}
|
||||
*/
|
||||
this.broadcastWidth = 0;
|
||||
/**
|
||||
* Broadcasted height of content rectangle.
|
||||
*
|
||||
* @type {number}
|
||||
*/
|
||||
this.broadcastHeight = 0;
|
||||
/**
|
||||
* Reference to the last observed content rectangle.
|
||||
*
|
||||
* @private {DOMRectInit}
|
||||
*/
|
||||
this.contentRect_ = createRectInit(0, 0, 0, 0);
|
||||
this.target = target;
|
||||
}
|
||||
/**
|
||||
* Updates content rectangle and tells whether it's width or height properties
|
||||
* have changed since the last broadcast.
|
||||
*
|
||||
* @returns {boolean}
|
||||
*/
|
||||
ResizeObservation.prototype.isActive = function () {
|
||||
var rect = getContentRect(this.target);
|
||||
this.contentRect_ = rect;
|
||||
return (rect.width !== this.broadcastWidth ||
|
||||
rect.height !== this.broadcastHeight);
|
||||
};
|
||||
/**
|
||||
* Updates 'broadcastWidth' and 'broadcastHeight' properties with a data
|
||||
* from the corresponding properties of the last observed content rectangle.
|
||||
*
|
||||
* @returns {DOMRectInit} Last observed content rectangle.
|
||||
*/
|
||||
ResizeObservation.prototype.broadcastRect = function () {
|
||||
var rect = this.contentRect_;
|
||||
this.broadcastWidth = rect.width;
|
||||
this.broadcastHeight = rect.height;
|
||||
return rect;
|
||||
};
|
||||
return ResizeObservation;
|
||||
}());
|
||||
|
||||
var ResizeObserverEntry = /** @class */ (function () {
|
||||
/**
|
||||
* Creates an instance of ResizeObserverEntry.
|
||||
*
|
||||
* @param {Element} target - Element that is being observed.
|
||||
* @param {DOMRectInit} rectInit - Data of the element's content rectangle.
|
||||
*/
|
||||
function ResizeObserverEntry(target, rectInit) {
|
||||
var contentRect = createReadOnlyRect(rectInit);
|
||||
// According to the specification following properties are not writable
|
||||
// and are also not enumerable in the native implementation.
|
||||
//
|
||||
// Property accessors are not being used as they'd require to define a
|
||||
// private WeakMap storage which may cause memory leaks in browsers that
|
||||
// don't support this type of collections.
|
||||
defineConfigurable(this, { target: target, contentRect: contentRect });
|
||||
}
|
||||
return ResizeObserverEntry;
|
||||
}());
|
||||
|
||||
var ResizeObserverSPI = /** @class */ (function () {
|
||||
/**
|
||||
* Creates a new instance of ResizeObserver.
|
||||
*
|
||||
* @param {ResizeObserverCallback} callback - Callback function that is invoked
|
||||
* when one of the observed elements changes it's content dimensions.
|
||||
* @param {ResizeObserverController} controller - Controller instance which
|
||||
* is responsible for the updates of observer.
|
||||
* @param {ResizeObserver} callbackCtx - Reference to the public
|
||||
* ResizeObserver instance which will be passed to callback function.
|
||||
*/
|
||||
function ResizeObserverSPI(callback, controller, callbackCtx) {
|
||||
/**
|
||||
* Collection of resize observations that have detected changes in dimensions
|
||||
* of elements.
|
||||
*
|
||||
* @private {Array<ResizeObservation>}
|
||||
*/
|
||||
this.activeObservations_ = [];
|
||||
/**
|
||||
* Registry of the ResizeObservation instances.
|
||||
*
|
||||
* @private {Map<Element, ResizeObservation>}
|
||||
*/
|
||||
this.observations_ = new MapShim();
|
||||
if (typeof callback !== 'function') {
|
||||
throw new TypeError('The callback provided as parameter 1 is not a function.');
|
||||
}
|
||||
this.callback_ = callback;
|
||||
this.controller_ = controller;
|
||||
this.callbackCtx_ = callbackCtx;
|
||||
}
|
||||
/**
|
||||
* Starts observing provided element.
|
||||
*
|
||||
* @param {Element} target - Element to be observed.
|
||||
* @returns {void}
|
||||
*/
|
||||
ResizeObserverSPI.prototype.observe = function (target) {
|
||||
if (!arguments.length) {
|
||||
throw new TypeError('1 argument required, but only 0 present.');
|
||||
}
|
||||
// Do nothing if current environment doesn't have the Element interface.
|
||||
if (typeof Element === 'undefined' || !(Element instanceof Object)) {
|
||||
return;
|
||||
}
|
||||
if (!(target instanceof getWindowOf(target).Element)) {
|
||||
throw new TypeError('parameter 1 is not of type "Element".');
|
||||
}
|
||||
var observations = this.observations_;
|
||||
// Do nothing if element is already being observed.
|
||||
if (observations.has(target)) {
|
||||
return;
|
||||
}
|
||||
observations.set(target, new ResizeObservation(target));
|
||||
this.controller_.addObserver(this);
|
||||
// Force the update of observations.
|
||||
this.controller_.refresh();
|
||||
};
|
||||
/**
|
||||
* Stops observing provided element.
|
||||
*
|
||||
* @param {Element} target - Element to stop observing.
|
||||
* @returns {void}
|
||||
*/
|
||||
ResizeObserverSPI.prototype.unobserve = function (target) {
|
||||
if (!arguments.length) {
|
||||
throw new TypeError('1 argument required, but only 0 present.');
|
||||
}
|
||||
// Do nothing if current environment doesn't have the Element interface.
|
||||
if (typeof Element === 'undefined' || !(Element instanceof Object)) {
|
||||
return;
|
||||
}
|
||||
if (!(target instanceof getWindowOf(target).Element)) {
|
||||
throw new TypeError('parameter 1 is not of type "Element".');
|
||||
}
|
||||
var observations = this.observations_;
|
||||
// Do nothing if element is not being observed.
|
||||
if (!observations.has(target)) {
|
||||
return;
|
||||
}
|
||||
observations.delete(target);
|
||||
if (!observations.size) {
|
||||
this.controller_.removeObserver(this);
|
||||
}
|
||||
};
|
||||
/**
|
||||
* Stops observing all elements.
|
||||
*
|
||||
* @returns {void}
|
||||
*/
|
||||
ResizeObserverSPI.prototype.disconnect = function () {
|
||||
this.clearActive();
|
||||
this.observations_.clear();
|
||||
this.controller_.removeObserver(this);
|
||||
};
|
||||
/**
|
||||
* Collects observation instances the associated element of which has changed
|
||||
* it's content rectangle.
|
||||
*
|
||||
* @returns {void}
|
||||
*/
|
||||
ResizeObserverSPI.prototype.gatherActive = function () {
|
||||
var _this = this;
|
||||
this.clearActive();
|
||||
this.observations_.forEach(function (observation) {
|
||||
if (observation.isActive()) {
|
||||
_this.activeObservations_.push(observation);
|
||||
}
|
||||
});
|
||||
};
|
||||
/**
|
||||
* Invokes initial callback function with a list of ResizeObserverEntry
|
||||
* instances collected from active resize observations.
|
||||
*
|
||||
* @returns {void}
|
||||
*/
|
||||
ResizeObserverSPI.prototype.broadcastActive = function () {
|
||||
// Do nothing if observer doesn't have active observations.
|
||||
if (!this.hasActive()) {
|
||||
return;
|
||||
}
|
||||
var ctx = this.callbackCtx_;
|
||||
// Create ResizeObserverEntry instance for every active observation.
|
||||
var entries = this.activeObservations_.map(function (observation) {
|
||||
return new ResizeObserverEntry(observation.target, observation.broadcastRect());
|
||||
});
|
||||
this.callback_.call(ctx, entries, ctx);
|
||||
this.clearActive();
|
||||
};
|
||||
/**
|
||||
* Clears the collection of active observations.
|
||||
*
|
||||
* @returns {void}
|
||||
*/
|
||||
ResizeObserverSPI.prototype.clearActive = function () {
|
||||
this.activeObservations_.splice(0);
|
||||
};
|
||||
/**
|
||||
* Tells whether observer has active observations.
|
||||
*
|
||||
* @returns {boolean}
|
||||
*/
|
||||
ResizeObserverSPI.prototype.hasActive = function () {
|
||||
return this.activeObservations_.length > 0;
|
||||
};
|
||||
return ResizeObserverSPI;
|
||||
}());
|
||||
|
||||
// Registry of internal observers. If WeakMap is not available use current shim
|
||||
// for the Map collection as it has all required methods and because WeakMap
|
||||
// can't be fully polyfilled anyway.
|
||||
var observers = typeof WeakMap !== 'undefined' ? new WeakMap() : new MapShim();
|
||||
/**
|
||||
* ResizeObserver API. Encapsulates the ResizeObserver SPI implementation
|
||||
* exposing only those methods and properties that are defined in the spec.
|
||||
*/
|
||||
var ResizeObserver = /** @class */ (function () {
|
||||
/**
|
||||
* Creates a new instance of ResizeObserver.
|
||||
*
|
||||
* @param {ResizeObserverCallback} callback - Callback that is invoked when
|
||||
* dimensions of the observed elements change.
|
||||
*/
|
||||
function ResizeObserver(callback) {
|
||||
if (!(this instanceof ResizeObserver)) {
|
||||
throw new TypeError('Cannot call a class as a function.');
|
||||
}
|
||||
if (!arguments.length) {
|
||||
throw new TypeError('1 argument required, but only 0 present.');
|
||||
}
|
||||
var controller = ResizeObserverController.getInstance();
|
||||
var observer = new ResizeObserverSPI(callback, controller, this);
|
||||
observers.set(this, observer);
|
||||
}
|
||||
return ResizeObserver;
|
||||
}());
|
||||
// Expose public methods of ResizeObserver.
|
||||
[
|
||||
'observe',
|
||||
'unobserve',
|
||||
'disconnect'
|
||||
].forEach(function (method) {
|
||||
ResizeObserver.prototype[method] = function () {
|
||||
var _a;
|
||||
return (_a = observers.get(this))[method].apply(_a, arguments);
|
||||
};
|
||||
});
|
||||
|
||||
var index = (function () {
|
||||
// Export existing implementation if available.
|
||||
if (typeof global$1.ResizeObserver !== 'undefined') {
|
||||
return global$1.ResizeObserver;
|
||||
}
|
||||
return ResizeObserver;
|
||||
})();
|
||||
|
||||
return index;
|
||||
|
||||
})));
|
1
js/ResizeObserver.min.js
vendored
Normal file
1
js/ResizeObserver.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
269
js/alpha-color-picker.js
Normal file
269
js/alpha-color-picker.js
Normal file
@ -0,0 +1,269 @@
|
||||
/**
|
||||
* Alpha Color Picker JS
|
||||
*
|
||||
* This file includes several helper functions and the core control JS.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Override the stock color.js toString() method to add support for
|
||||
* outputting RGBa or Hex.
|
||||
*/
|
||||
Color.prototype.toString = function ( flag ) {
|
||||
|
||||
// If our no-alpha flag has been passed in, output RGBa value with 100% opacity.
|
||||
// This is used to set the background color on the opacity slider during color changes.
|
||||
if ( 'no-alpha' == flag ) {
|
||||
return this.toCSS( 'rgba', '1' ).replace( /\s+/g, '' );
|
||||
}
|
||||
|
||||
// If we have a proper opacity value, output RGBa.
|
||||
if ( 1 > this._alpha ) {
|
||||
return this.toCSS( 'rgba', this._alpha ).replace( /\s+/g, '' );
|
||||
}
|
||||
|
||||
// Proceed with stock color.js hex output.
|
||||
var hex = parseInt( this._color, 10 ).toString( 16 );
|
||||
if ( this.error ) {
|
||||
return '';
|
||||
}
|
||||
if ( hex.length < 6 ) {
|
||||
for ( var i = 6 - hex.length - 1; i >= 0; i-- ) {
|
||||
hex = '0' + hex;
|
||||
}
|
||||
}
|
||||
|
||||
return '#' + hex;
|
||||
};
|
||||
|
||||
/**
|
||||
* Given an RGBa, RGB, or hex color value, return the alpha channel value.
|
||||
*/
|
||||
function acp_get_alpha_value_from_color( value ) {
|
||||
var alphaVal;
|
||||
|
||||
// Remove all spaces from the passed in value to help our RGBa regex.
|
||||
value = value.replace( / /g, '' );
|
||||
|
||||
if ( value.match( /rgba\(\d+\,\d+\,\d+\,([^\)]+)\)/ ) ) {
|
||||
alphaVal = parseFloat( value.match( /rgba\(\d+\,\d+\,\d+\,([^\)]+)\)/)[1] ).toFixed( 2 ) * 100;
|
||||
alphaVal = parseInt( alphaVal );
|
||||
} else {
|
||||
alphaVal = 100;
|
||||
}
|
||||
|
||||
return alphaVal;
|
||||
}
|
||||
|
||||
/**
|
||||
* Force update the alpha value of the color picker object and maybe the alpha slider.
|
||||
*/
|
||||
function acp_update_alpha_value_on_color_control( alpha, $control, $alphaSlider, update_slider ) {
|
||||
var iris, colorPicker, color;
|
||||
|
||||
iris = $control.data( 'a8cIris' );
|
||||
colorPicker = $control.data( 'wpWpColorPicker' );
|
||||
|
||||
// Set the alpha value on the Iris object.
|
||||
iris._color._alpha = alpha;
|
||||
|
||||
// Store the new color value.
|
||||
color = iris._color.toString();
|
||||
|
||||
// Set the value of the input.
|
||||
$control.val( color );
|
||||
|
||||
// Update the background color of the color picker.
|
||||
colorPicker.toggler.css( {
|
||||
'background-color': color
|
||||
} );
|
||||
|
||||
// Maybe update the alpha slider itself.
|
||||
if ( update_slider ) {
|
||||
acp_update_alpha_value_on_alpha_slider( alpha, $alphaSlider );
|
||||
}
|
||||
|
||||
// Update the color value of the color picker object.
|
||||
$control.wpColorPicker( 'color', color );
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the slider handle position and label.
|
||||
*/
|
||||
function acp_update_alpha_value_on_alpha_slider( alpha, $alphaSlider ) {
|
||||
$alphaSlider.slider( 'value', alpha );
|
||||
$alphaSlider.find( '.ui-slider-handle' ).text( alpha.toString() );
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialization trigger.
|
||||
*/
|
||||
jQuery( document ).ready( function ( $ ) {
|
||||
|
||||
// Loop over each control and transform it into our color picker.
|
||||
$( '.alpha-color-control' ).each( function () {
|
||||
|
||||
// Scope the vars.
|
||||
var $control, startingColor, paletteInput, showOpacity, defaultColor, palette,
|
||||
colorPickerOptions, $container, $alphaSlider, alphaVal, sliderOptions;
|
||||
|
||||
// Store the control instance.
|
||||
$control = $( this );
|
||||
|
||||
// Get a clean starting value for the option.
|
||||
startingColor = $control.val().replace( /\s+/g, '' );
|
||||
|
||||
// Get some data off the control.
|
||||
paletteInput = $control.attr( 'data-palette' );
|
||||
showOpacity = $control.attr( 'data-show-opacity' );
|
||||
defaultColor = $control.attr( 'data-default-color' );
|
||||
|
||||
// Process the palette.
|
||||
if ( -1 !== paletteInput.indexOf( '|' ) ) {
|
||||
palette = paletteInput.split( '|' );
|
||||
} else if ( 'false' == paletteInput ) {
|
||||
palette = false;
|
||||
} else {
|
||||
palette = true;
|
||||
}
|
||||
|
||||
// Set up the options that we'll pass to wpColorPicker().
|
||||
colorPickerOptions = {
|
||||
change: function ( event, ui ) {
|
||||
var key, value, alpha, $transparency;
|
||||
|
||||
key = $control.attr( 'data-customize-setting-link' );
|
||||
value = $control.wpColorPicker( 'color' );
|
||||
|
||||
// Set the opacity value on the slider handle when the default color button is clicked.
|
||||
if ( defaultColor == value ) {
|
||||
alpha = acp_get_alpha_value_from_color( value );
|
||||
$alphaSlider.find( '.ui-slider-handle' ).text( alpha );
|
||||
}
|
||||
|
||||
// Send ajax request to wp.customize to trigger the Save action.
|
||||
wp.customize(key, function ( obj ) {
|
||||
obj.set( value );
|
||||
});
|
||||
|
||||
$transparency = $container.find( '.transparency' );
|
||||
|
||||
// Always show the background color of the opacity slider at 100% opacity.
|
||||
$transparency.css( 'background-color', ui.color.toString( 'no-alpha' ) );
|
||||
},
|
||||
palettes: palette // Use the passed in palette.
|
||||
};
|
||||
|
||||
// Create the colorpicker.
|
||||
$control.wpColorPicker( colorPickerOptions );
|
||||
|
||||
$container = $control.parents( '.wp-picker-container:first' );
|
||||
|
||||
// Insert our opacity slider.
|
||||
$('<div class="alpha-color-picker-container">' +
|
||||
'<div class="min-click-zone click-zone"></div>' +
|
||||
'<div class="max-click-zone click-zone"></div>' +
|
||||
'<div class="alpha-slider"></div>' +
|
||||
'<div class="transparency"></div>' +
|
||||
'</div>').appendTo($container.find( '.wp-picker-holder' ) );
|
||||
|
||||
$alphaSlider = $container.find( '.alpha-slider' );
|
||||
|
||||
// If starting value is in format RGBa, grab the alpha channel.
|
||||
alphaVal = acp_get_alpha_value_from_color( startingColor );
|
||||
|
||||
// Set up jQuery UI slider() options.
|
||||
sliderOptions = {
|
||||
create: function ( event, ui ) {
|
||||
var value = $(this).slider( 'value' );
|
||||
|
||||
// Set up initial values.
|
||||
$( this ).find( '.ui-slider-handle' ).text( value );
|
||||
$( this ).siblings( '.transparency' ).css( 'background-color', startingColor );
|
||||
},
|
||||
value: alphaVal,
|
||||
range: 'max',
|
||||
step: 1,
|
||||
min: 0,
|
||||
max: 100,
|
||||
animate: 300
|
||||
};
|
||||
|
||||
// Initialize jQuery UI slider with our options.
|
||||
$alphaSlider.slider( sliderOptions );
|
||||
|
||||
// Maybe show the opacity on the handle.
|
||||
if ( 'true' === showOpacity ) {
|
||||
$alphaSlider.find( '.ui-slider-handle' ).addClass( 'show-opacity' );
|
||||
}
|
||||
|
||||
// Bind event handlers for the click zones.
|
||||
$container.find( '.min-click-zone' ).on( 'click', function () {
|
||||
acp_update_alpha_value_on_color_control( 0, $control, $alphaSlider, true );
|
||||
} );
|
||||
$container.find( '.max-click-zone').on( 'click', function () {
|
||||
acp_update_alpha_value_on_color_control( 100, $control, $alphaSlider, true );
|
||||
} );
|
||||
|
||||
// Bind event handler for clicking on a palette color.
|
||||
$container.find( '.iris-palette' ).on( 'click', function () {
|
||||
var color, alpha;
|
||||
|
||||
color = $( this ).css( 'background-color' );
|
||||
alpha = acp_get_alpha_value_from_color( color );
|
||||
|
||||
acp_update_alpha_value_on_alpha_slider( alpha, $alphaSlider );
|
||||
|
||||
// Sometimes Iris doesn't set a perfect background-color on the palette,
|
||||
// for example rgba(20, 80, 100, 0.3) becomes rgba(20, 80, 100, 0.298039).
|
||||
// To compensante for this we round the opacity value on RGBa colors here
|
||||
// and save it a second time to the color picker object.
|
||||
if ( alpha != 100 ) {
|
||||
color = color.replace( /[^,]+(?=\))/, ( alpha / 100 ).toFixed( 2 ) );
|
||||
}
|
||||
|
||||
$control.wpColorPicker( 'color', color );
|
||||
});
|
||||
|
||||
// Bind event handler for clicking on the 'Clear' button.
|
||||
$container.find( '.button.wp-picker-clear' ).on( 'click', function () {
|
||||
var key = $control.attr( 'data-customize-setting-link' );
|
||||
|
||||
// The #fff color is delibrate here. This sets the color picker to white instead of the
|
||||
// defult black, which puts the color picker in a better place to visually represent empty.
|
||||
$control.wpColorPicker('color', '#ffffff');
|
||||
|
||||
// Set the actual option value to empty string.
|
||||
wp.customize( key, function ( obj ) {
|
||||
obj.set( '' );
|
||||
} );
|
||||
|
||||
acp_update_alpha_value_on_alpha_slider( 100, $alphaSlider );
|
||||
} );
|
||||
|
||||
// Bind event handler for clicking on the 'Default' button.
|
||||
$container.find( '.button.wp-picker-default' ).on( 'click', function () {
|
||||
var alpha = acp_get_alpha_value_from_color( defaultColor );
|
||||
|
||||
acp_update_alpha_value_on_alpha_slider( alpha, $alphaSlider );
|
||||
} );
|
||||
|
||||
// Bind event handler for typing or pasting into the input.
|
||||
$control.on( 'input', function () {
|
||||
var value = $( this ).val();
|
||||
var alpha = acp_get_alpha_value_from_color( value );
|
||||
|
||||
acp_update_alpha_value_on_alpha_slider( alpha, $alphaSlider );
|
||||
} );
|
||||
|
||||
// Update all the things when the slider is interacted with.
|
||||
$alphaSlider.slider().on( 'slide', function ( event, ui ) {
|
||||
var alpha = parseFloat( ui.value ) / 100.0;
|
||||
|
||||
acp_update_alpha_value_on_color_control( alpha, $control, $alphaSlider, false );
|
||||
|
||||
// Change value shown on slider handle.
|
||||
$( this ).find( '.ui-slider-handle').text( ui.value );
|
||||
});
|
||||
|
||||
} );
|
||||
} );
|
7
js/customize-controls.js
Normal file
7
js/customize-controls.js
Normal file
@ -0,0 +1,7 @@
|
||||
wp.customize.sectionConstructor['pressbook-button'] = wp.customize.Section.extend( {
|
||||
// No events for this type of section.
|
||||
attachEvents: function() {},
|
||||
|
||||
// Always make the section active.
|
||||
isContextuallyActive: function() { return true; }
|
||||
} );
|
77
js/customizer.js
Normal file
77
js/customizer.js
Normal file
@ -0,0 +1,77 @@
|
||||
/* global wp, jQuery */
|
||||
/**
|
||||
* File customizer.js.
|
||||
*
|
||||
* Theme Customizer enhancements for a better user experience.
|
||||
*
|
||||
* Contains handlers to make Theme Customizer preview reload changes asynchronously.
|
||||
*/
|
||||
|
||||
( function( $ ) {
|
||||
// Site title.
|
||||
wp.customize( 'blogname', function( value ) {
|
||||
value.bind( function( to ) {
|
||||
$( '.site-title > a' ).text( to );
|
||||
} );
|
||||
} );
|
||||
|
||||
// Site tagline.
|
||||
wp.customize( 'blogdescription', function( value ) {
|
||||
value.bind( function( to ) {
|
||||
$( '.site-tagline' ).text( to );
|
||||
} );
|
||||
} );
|
||||
|
||||
// Theme CSS output.
|
||||
var pressbook_css_output, pressbook_prop_value;
|
||||
|
||||
$.each( pressbook.styles, function( key, rules ) {
|
||||
|
||||
wp.customize( 'set_styles[' + key + ']', function( value ) {
|
||||
|
||||
value.bind( function( to ) {
|
||||
if ( ! $( 'style#pressbook-styles-' + key ).length ) {
|
||||
$( '<style id="pressbook-styles-' + key + '"></style>' ).insertAfter( '#' + pressbook.handle_id );
|
||||
}
|
||||
|
||||
pressbook_css_output = '';
|
||||
|
||||
$.each( rules, function( selector, values ) {
|
||||
|
||||
$.each( values, function( prop_key, prop_value ) {
|
||||
if ( prop_value.remove && prop_value.remove.length ) {
|
||||
$.each( prop_value.remove, function( remove_style_key, remove_style_id ) {
|
||||
$( 'style#pressbook-styles-' + remove_style_id ).remove();
|
||||
} );
|
||||
}
|
||||
|
||||
if ( 'header_bg_position' === key ) {
|
||||
to = to.replaceAll( '-', ' ' );
|
||||
}
|
||||
|
||||
pressbook_prop_value = prop_value.place.replaceAll( '_PLACE', to );
|
||||
|
||||
if ( prop_value.extra && ! $.isEmptyObject( prop_value.extra ) ) {
|
||||
$.each( prop_value.extra, function( extra_place_key, extra_place ) {
|
||||
pressbook_prop_value = pressbook_prop_value.replaceAll( extra_place, wp.customize( 'set_styles[' + extra_place_key + ']' ).get() );
|
||||
} );
|
||||
}
|
||||
|
||||
pressbook_css_output += ( selector + '{' );
|
||||
|
||||
pressbook_css_output += ( prop_key + ':' + pressbook_prop_value + ';' );
|
||||
|
||||
pressbook_css_output += '}';
|
||||
} );
|
||||
|
||||
} );
|
||||
|
||||
$( 'style#pressbook-styles-' + key ).html( pressbook_css_output );
|
||||
|
||||
} );
|
||||
|
||||
} );
|
||||
|
||||
} );
|
||||
|
||||
}( jQuery ) );
|
528
js/script.js
Normal file
528
js/script.js
Normal file
@ -0,0 +1,528 @@
|
||||
// Namespace.
|
||||
var pressbook = pressbook || {};
|
||||
|
||||
/**
|
||||
* Is the DOM ready?
|
||||
*
|
||||
* This implementation is coming from https://gomakethings.com/a-native-javascript-equivalent-of-jquerys-ready-method/
|
||||
*
|
||||
* @param {Function} fn Callback function to run.
|
||||
*/
|
||||
function pressbookDomReady( fn ) {
|
||||
if ( typeof fn !== 'function' ) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ( 'interactive' === document.readyState || 'complete' === document.readyState ) {
|
||||
return fn();
|
||||
}
|
||||
|
||||
document.addEventListener( 'DOMContentLoaded', fn, false );
|
||||
}
|
||||
|
||||
// Animation curves.
|
||||
Math.easeInOutQuad = function ( t, b, c, d ) {
|
||||
t /= d / 2;
|
||||
if ( t < 1 ) return c / 2 * t * t + b;
|
||||
t--;
|
||||
return - c / 2 * ( t* ( t-2 ) - 1 ) + b;
|
||||
};
|
||||
|
||||
// Setup main menu.
|
||||
pressbook.setupMainMenu = {
|
||||
|
||||
init: function() {
|
||||
const mainNav = document.getElementById( 'site-navigation' );
|
||||
if ( mainNav ) {
|
||||
|
||||
const menu = mainNav.getElementsByTagName( 'ul' )[ 0 ]
|
||||
const toggle = mainNav.querySelector( '.primary-menu-toggle' );
|
||||
|
||||
// Hide menu toggle button if menu is empty and return early.
|
||||
if ( 'undefined' === typeof menu ) {
|
||||
if ( toggle ) {
|
||||
toggle.style.display = 'none';
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Add class 'nav-menu' to the menu.
|
||||
menu.classList.add( 'nav-menu' );
|
||||
|
||||
const arrows = mainNav.querySelectorAll( '.main-navigation-arrow-btn' );
|
||||
const links = mainNav.querySelectorAll( 'li > a' );
|
||||
const linksWithoutChildren = mainNav.querySelectorAll( 'li:not(.menu-item-has-children) > a' );
|
||||
const lastLinksWithoutChildren = mainNav.querySelectorAll( 'li:last-child:not(.menu-item-has-children) > a' );
|
||||
const searchMenu = mainNav.querySelector( '.primary-menu-search' );
|
||||
const searchToggle = mainNav.querySelector( '.primary-menu-search-toggle' );
|
||||
|
||||
// Add class if touch screen device.
|
||||
this.toggleTouchClass( mainNav );
|
||||
|
||||
// Toggle navigation when the user clicks the menu toggle button.
|
||||
this.toggleNavigation( toggle, mainNav );
|
||||
|
||||
// Collapse menu when the user clicks outside the navigation.
|
||||
this.collapseIfClickOutside( toggle, mainNav );
|
||||
|
||||
// Collapse menu when the user presses the escape key.
|
||||
this.collapseIfEscapeKeyPress( toggle, mainNav );
|
||||
|
||||
// Collapse menu when the user resizes the window.
|
||||
this.collapseOnResize( toggle, mainNav );
|
||||
|
||||
// Toggle sub-menu.
|
||||
this.toggleSubmenu( arrows, links, linksWithoutChildren, lastLinksWithoutChildren );
|
||||
|
||||
// Trap focus in modal.
|
||||
this.trapFocusInModal( mainNav );
|
||||
|
||||
// Toggle search form.
|
||||
this.toggleSearch( searchToggle, searchMenu );
|
||||
|
||||
// Trap focus in search.
|
||||
this.trapFocusInSearch( searchMenu );
|
||||
}
|
||||
},
|
||||
|
||||
toggleTouchClass: function( nav ) {
|
||||
const touchClass = 'main-navigation--touch';
|
||||
if ( isTouchDevice() ) {
|
||||
nav.classList.add( touchClass );
|
||||
}
|
||||
|
||||
window.addEventListener( 'resize', function() {
|
||||
if ( isTouchDevice() ) {
|
||||
nav.classList.add( touchClass );
|
||||
} else {
|
||||
nav.classList.remove( touchClass );
|
||||
}
|
||||
} );
|
||||
|
||||
function isTouchDevice() {
|
||||
return ( ( 'ontouchstart' in window ) || ( navigator.maxTouchPoints > 0 ) || ( navigator.msMaxTouchPoints > 0 ) );
|
||||
}
|
||||
},
|
||||
|
||||
toggleNavigation: function( button, nav ) {
|
||||
if ( ! button ) {
|
||||
return;
|
||||
}
|
||||
|
||||
button.addEventListener( 'click', function( event ) {
|
||||
event.preventDefault();
|
||||
|
||||
nav.classList.toggle( 'toggled' );
|
||||
|
||||
if ( button ) {
|
||||
if ( 'true' === button.getAttribute( 'aria-expanded' ) ) {
|
||||
button.setAttribute( 'aria-expanded', 'false' );
|
||||
} else {
|
||||
button.setAttribute( 'aria-expanded', 'true' );
|
||||
}
|
||||
}
|
||||
} );
|
||||
},
|
||||
|
||||
collapseIfClickOutside: function( button, nav ) {
|
||||
const that = this;
|
||||
document.addEventListener( 'click', function( event ) {
|
||||
const isClickInside = nav.contains( event.target );
|
||||
|
||||
if ( ! isClickInside ) {
|
||||
nav.classList.remove( 'toggled' );
|
||||
|
||||
if ( button ) {
|
||||
button.setAttribute( 'aria-expanded', 'false' );
|
||||
}
|
||||
|
||||
// Remove all the focus classes in the ul.
|
||||
[].forEach.call( nav.querySelectorAll( '.focus' ), function( li ) {
|
||||
li.classList.remove( 'focus' );
|
||||
} );
|
||||
|
||||
// Set aria-expanded to false.
|
||||
[].forEach.call( nav.querySelectorAll( '.main-navigation-arrow-btn' ), function( button ) {
|
||||
button.setAttribute( 'aria-expanded', 'false' );
|
||||
} );
|
||||
|
||||
// Collapse search form.
|
||||
that.collapseSearchForm( nav );
|
||||
}
|
||||
} );
|
||||
},
|
||||
|
||||
collapseIfEscapeKeyPress: function( button, nav ) {
|
||||
const that = this;
|
||||
document.addEventListener( 'keyup', function( event ) {
|
||||
if ( 'Escape' === event.key ) {
|
||||
nav.classList.remove( 'toggled' );
|
||||
|
||||
if ( button ) {
|
||||
button.setAttribute( 'aria-expanded', 'false' );
|
||||
}
|
||||
|
||||
// Collapse search form.
|
||||
that.collapseSearchForm( nav );
|
||||
}
|
||||
} );
|
||||
},
|
||||
|
||||
collapseOnResize: function( button, nav ) {
|
||||
window.addEventListener( 'resize', function() {
|
||||
if ( window.matchMedia( 'screen and (min-width: 768px)' ).matches ) {
|
||||
nav.classList.remove( 'toggled' );
|
||||
|
||||
if ( button ) {
|
||||
button.setAttribute( 'aria-expanded', 'false' );
|
||||
}
|
||||
}
|
||||
} );
|
||||
},
|
||||
|
||||
toggleSubmenu: function( arrows, links, linksWithoutChildren, lastLinksWithoutChildren ) {
|
||||
[].forEach.call( arrows, function( arrow ) {
|
||||
arrow.addEventListener( 'click', toggleFocus );
|
||||
arrow.addEventListener( 'keydown', toggleSubmenuWithArrow );
|
||||
} );
|
||||
|
||||
[].forEach.call( links, function( link ) {
|
||||
link.addEventListener( 'focus', closeSubmenuWithLink );
|
||||
} );
|
||||
|
||||
[].forEach.call( linksWithoutChildren, function( link ) {
|
||||
link.addEventListener( 'focus', openSubmenuWithLink );
|
||||
} );
|
||||
|
||||
[].forEach.call( lastLinksWithoutChildren, function( link ) {
|
||||
link.addEventListener( 'keydown', closePreviousWithLink );
|
||||
} );
|
||||
|
||||
function toggleFocus() {
|
||||
var self = this,
|
||||
addFocusEl = false; // Add focus class to this element.
|
||||
|
||||
// Move up through the ancestors of the current arrow button until we hit ul.
|
||||
while ( 'ul' !== self.tagName.toLowerCase() ) {
|
||||
|
||||
// If we hit the first li, then save it in addFocusEl if it does not contain the focus class.
|
||||
if ( 'li' === self.tagName.toLowerCase() && ! addFocusEl ) {
|
||||
if ( ! self.classList.contains( 'focus' ) ) {
|
||||
addFocusEl = self;
|
||||
}
|
||||
}
|
||||
|
||||
self = self.parentElement;
|
||||
}
|
||||
|
||||
// Remove all the focus classes in the ul.
|
||||
[].forEach.call( self.querySelectorAll( '.focus' ), function( li ) {
|
||||
li.classList.remove( 'focus' );
|
||||
} );
|
||||
|
||||
// Set aria-expanded to false.
|
||||
[].forEach.call( self.querySelectorAll( '.main-navigation-arrow-btn' ), function( button ) {
|
||||
button.setAttribute( 'aria-expanded', 'false' );
|
||||
} );
|
||||
|
||||
if ( addFocusEl ) {
|
||||
// Add focus class to addFocusEl.
|
||||
addFocusEl.classList.add( 'focus' );
|
||||
|
||||
// Set aria-expanded to true.
|
||||
this.setAttribute( 'aria-expanded', 'true' );
|
||||
}
|
||||
}
|
||||
|
||||
function toggleSubmenuWithArrow( event ) {
|
||||
const parentEl = this.parentElement,
|
||||
tabKey = ( 'Tab' === event.key ),
|
||||
shiftKey = event.shiftKey;
|
||||
|
||||
if ( tabKey && shiftKey && parentEl.classList.contains( 'focus' ) ) {
|
||||
parentEl.classList.remove( 'focus' );
|
||||
this.setAttribute( 'aria-expanded', 'false' );
|
||||
} else if ( tabKey && ! shiftKey && ! parentEl.classList.contains( 'focus' ) ) {
|
||||
parentEl.classList.add( 'focus' );
|
||||
this.setAttribute( 'aria-expanded', 'true' );
|
||||
}
|
||||
}
|
||||
|
||||
function closeSubmenuWithLink() {
|
||||
var self = this,
|
||||
previousClosed = false;
|
||||
|
||||
// Move up through the ancestors of the current link until we hit .nav-menu.
|
||||
while ( ! self.classList.contains( 'nav-menu' ) ) {
|
||||
|
||||
// Close previous sub-menus before opening next.
|
||||
if ( ! previousClosed && ( 'ul' === self.tagName.toLowerCase() ) ) {
|
||||
// Remove all the focus classes in the ul.
|
||||
[].forEach.call( self.querySelectorAll( '.focus' ), function( li ) {
|
||||
li.classList.remove( 'focus' );
|
||||
} );
|
||||
|
||||
// Set aria-expanded to false.
|
||||
[].forEach.call( self.querySelectorAll( '.main-navigation-arrow-btn' ), function( button ) {
|
||||
button.setAttribute( 'aria-expanded', 'false' );
|
||||
} );
|
||||
|
||||
previousClosed = true;
|
||||
}
|
||||
|
||||
self = self.parentElement;
|
||||
}
|
||||
}
|
||||
|
||||
function openSubmenuWithLink() {
|
||||
var self = this,
|
||||
arrow;
|
||||
|
||||
// Move up through the ancestors of the current link until we hit .nav-menu.
|
||||
while ( ! self.classList.contains( 'nav-menu' ) ) {
|
||||
|
||||
// On li elements add the class .focus and set aria-expanded to true.
|
||||
if ( 'li' === self.tagName.toLowerCase() && ! self.classList.contains( 'focus' ) ) {
|
||||
self.classList.add( 'focus' );
|
||||
|
||||
arrow = self.querySelector( '.main-navigation-arrow-btn' );
|
||||
if ( arrow ) {
|
||||
this.setAttribute( 'aria-expanded', 'true' );
|
||||
}
|
||||
}
|
||||
|
||||
self = self.parentElement;
|
||||
}
|
||||
}
|
||||
|
||||
function closePreviousWithLink( event ) {
|
||||
var self = this,
|
||||
nextEl = false,
|
||||
tabKey = ( 'Tab' === event.key ),
|
||||
shiftKey = event.shiftKey;
|
||||
|
||||
if ( tabKey && ! shiftKey ) {
|
||||
// Move up through the ancestors of the current link until there is sibling li.
|
||||
do {
|
||||
nextEl = ( function( element ) {
|
||||
do {
|
||||
element = element.nextSibling;
|
||||
} while ( element && ( 1 !== element.nodeType ) );
|
||||
|
||||
return element;
|
||||
} ) ( self );
|
||||
|
||||
self = self.parentElement;
|
||||
} while ( ! nextEl );
|
||||
|
||||
// Remove all the focus classes in the li.
|
||||
[].forEach.call( self.querySelectorAll( '.focus' ), function( li ) {
|
||||
li.classList.remove( 'focus' );
|
||||
} );
|
||||
|
||||
// Set aria-expanded to false.
|
||||
[].forEach.call( self.querySelectorAll( '.main-navigation-arrow-btn' ), function( button ) {
|
||||
button.setAttribute( 'aria-expanded', 'false' );
|
||||
} );
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
trapFocusInModal: function( nav ) {
|
||||
document.addEventListener( 'keydown', function( event ) {
|
||||
if ( ! nav.classList.contains( 'toggled' ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
const elements = nav.querySelectorAll( 'input, a, button' );
|
||||
|
||||
if ( elements.length < 1 ) {
|
||||
return;
|
||||
}
|
||||
|
||||
const firstEl = elements[ 0 ],
|
||||
lastEl = elements[ elements.length - 1 ],
|
||||
activeEl = document.activeElement,
|
||||
tabKey = ( 'Tab' === event.key ),
|
||||
shiftKey = event.shiftKey;
|
||||
|
||||
if ( tabKey && ! shiftKey && lastEl === activeEl ) {
|
||||
event.preventDefault();
|
||||
firstEl.focus();
|
||||
}
|
||||
|
||||
if ( tabKey && shiftKey && firstEl === activeEl ) {
|
||||
event.preventDefault();
|
||||
lastEl.focus();
|
||||
}
|
||||
} );
|
||||
},
|
||||
|
||||
toggleSearch: function( toggle, search ) {
|
||||
if ( ! toggle || ! search ) {
|
||||
return;
|
||||
}
|
||||
|
||||
toggle.addEventListener( 'click', function( event ) {
|
||||
event.preventDefault();
|
||||
|
||||
search.classList.toggle( 'toggled' );
|
||||
|
||||
if ( 'true' === toggle.getAttribute( 'aria-expanded' ) ) {
|
||||
toggle.setAttribute( 'aria-expanded', 'false' );
|
||||
} else {
|
||||
toggle.setAttribute( 'aria-expanded', 'true' );
|
||||
}
|
||||
} );
|
||||
},
|
||||
|
||||
collapseSearchForm: function( nav ) {
|
||||
const search = nav.querySelector( '.primary-menu-search' );
|
||||
const toggle = nav.querySelector( '.primary-menu-search-toggle' );
|
||||
|
||||
if ( search ) {
|
||||
search.classList.remove( 'toggled' );
|
||||
}
|
||||
|
||||
if ( toggle ) {
|
||||
toggle.setAttribute( 'aria-expanded', 'false' );
|
||||
}
|
||||
},
|
||||
|
||||
trapFocusInSearch: function( search ) {
|
||||
document.addEventListener( 'keydown', function( event ) {
|
||||
if ( ! search || ! search.classList.contains( 'toggled' ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
const toggle = search.querySelector( '.primary-menu-search-toggle' );
|
||||
|
||||
if ( 'none' === window.getComputedStyle( toggle, null ).display ) {
|
||||
return;
|
||||
}
|
||||
|
||||
const elements = search.querySelectorAll( 'input, a, button' );
|
||||
|
||||
if ( elements.length < 1 ) {
|
||||
return;
|
||||
}
|
||||
|
||||
const firstEl = elements[ 0 ],
|
||||
lastEl = elements[ elements.length - 1 ],
|
||||
activeEl = document.activeElement,
|
||||
tabKey = ( 'Tab' === event.key ),
|
||||
shiftKey = event.shiftKey;
|
||||
|
||||
if ( tabKey && ! shiftKey && lastEl === activeEl ) {
|
||||
event.preventDefault();
|
||||
firstEl.focus();
|
||||
}
|
||||
|
||||
if ( tabKey && shiftKey && firstEl === activeEl ) {
|
||||
event.preventDefault();
|
||||
lastEl.focus();
|
||||
}
|
||||
} );
|
||||
},
|
||||
|
||||
}; // pressbook.setupMainMenu
|
||||
|
||||
// Go to top.
|
||||
pressbook.goToTop = {
|
||||
|
||||
offset: 300,
|
||||
offsetOpacity: 1200,
|
||||
scrollDuration: 700,
|
||||
|
||||
init: function() {
|
||||
const goToTop = document.querySelector( '.go-to-top' );
|
||||
if ( goToTop ) {
|
||||
this.handleScroll( goToTop );
|
||||
this.handleClick( goToTop );
|
||||
}
|
||||
},
|
||||
|
||||
handleScroll: function( goToTop ) {
|
||||
var offset = this.offset,
|
||||
offsetOpacity = this.offsetOpacity,
|
||||
scrolling = false;
|
||||
|
||||
window.addEventListener( 'scroll', function() {
|
||||
if ( ! scrolling ) {
|
||||
scrolling = true;
|
||||
if ( ! window.requestAnimationFrame ) {
|
||||
setTimeout( toggleOnChangeOffset, 250 );
|
||||
} else {
|
||||
window.requestAnimationFrame( toggleOnChangeOffset );
|
||||
}
|
||||
}
|
||||
} );
|
||||
|
||||
function toggleOnChangeOffset() {
|
||||
var windowTop = window.scrollY || document.documentElement.scrollTop;
|
||||
|
||||
if ( windowTop > offset ) {
|
||||
goToTop.classList.add( 'go-to-top--show' )
|
||||
} else {
|
||||
goToTop.classList.remove( 'go-to-top--show' );
|
||||
goToTop.classList.remove( 'go-to-top--fade-out' );
|
||||
}
|
||||
|
||||
if ( windowTop > offsetOpacity ) {
|
||||
goToTop.classList.add( 'go-to-top--fade-out' );
|
||||
}
|
||||
|
||||
scrolling = false;
|
||||
};
|
||||
},
|
||||
|
||||
handleClick: function( goToTop ) {
|
||||
goToTop.addEventListener( 'click', function( event ) {
|
||||
event.preventDefault();
|
||||
|
||||
if ( ! window.requestAnimationFrame ) {
|
||||
window.scrollTo( 0, 0 )
|
||||
} else {
|
||||
scrollTo( 0, this.scrollDuration );
|
||||
}
|
||||
|
||||
goToTop.blur();
|
||||
}.bind( this ) );
|
||||
|
||||
// Smooth scroll.
|
||||
function scrollTo( final, duration, cb ) {
|
||||
var start = window.scrollY || document.documentElement.scrollTop,
|
||||
currentTime = null;
|
||||
|
||||
var animateScroll = function( timestamp ) {
|
||||
if ( ! currentTime ) {
|
||||
currentTime = timestamp;
|
||||
}
|
||||
|
||||
var progress = timestamp - currentTime;
|
||||
|
||||
if ( progress > duration ) {
|
||||
progress = duration;
|
||||
}
|
||||
|
||||
var val = Math.easeInOutQuad( progress, start, final - start, duration );
|
||||
|
||||
window.scrollTo( 0, val );
|
||||
if ( progress < duration ) {
|
||||
window.requestAnimationFrame( animateScroll );
|
||||
} else {
|
||||
cb && cb();
|
||||
}
|
||||
};
|
||||
|
||||
window.requestAnimationFrame( animateScroll );
|
||||
};
|
||||
}
|
||||
|
||||
}; // pressbook.goToTop
|
||||
|
||||
pressbookDomReady( function() {
|
||||
pressbook.setupMainMenu.init(); // Setup main menu.
|
||||
pressbook.goToTop.init(); // Setup go to top.
|
||||
} );
|
1
js/script.min.js
vendored
Normal file
1
js/script.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
707
js/sticky-sidebar.js
Normal file
707
js/sticky-sidebar.js
Normal file
@ -0,0 +1,707 @@
|
||||
(function (global, factory) {
|
||||
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
|
||||
typeof define === 'function' && define.amd ? define(['exports'], factory) :
|
||||
(factory((global.StickySidebar = {})));
|
||||
}(this, (function (exports) { 'use strict';
|
||||
|
||||
var commonjsGlobal = typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : {};
|
||||
|
||||
|
||||
|
||||
function unwrapExports (x) {
|
||||
return x && x.__esModule && Object.prototype.hasOwnProperty.call(x, 'default') ? x['default'] : x;
|
||||
}
|
||||
|
||||
function createCommonjsModule(fn, module) {
|
||||
return module = { exports: {} }, fn(module, module.exports), module.exports;
|
||||
}
|
||||
|
||||
var stickySidebar = createCommonjsModule(function (module, exports) {
|
||||
(function (global, factory) {
|
||||
if (typeof undefined === "function" && undefined.amd) {
|
||||
undefined(['exports'], factory);
|
||||
} else {
|
||||
factory(exports);
|
||||
}
|
||||
})(commonjsGlobal, function (exports) {
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
|
||||
function _classCallCheck(instance, Constructor) {
|
||||
if (!(instance instanceof Constructor)) {
|
||||
throw new TypeError("Cannot call a class as a function");
|
||||
}
|
||||
}
|
||||
|
||||
var _createClass = function () {
|
||||
function defineProperties(target, props) {
|
||||
for (var i = 0; i < props.length; i++) {
|
||||
var descriptor = props[i];
|
||||
descriptor.enumerable = descriptor.enumerable || false;
|
||||
descriptor.configurable = true;
|
||||
if ("value" in descriptor) descriptor.writable = true;
|
||||
Object.defineProperty(target, descriptor.key, descriptor);
|
||||
}
|
||||
}
|
||||
|
||||
return function (Constructor, protoProps, staticProps) {
|
||||
if (protoProps) defineProperties(Constructor.prototype, protoProps);
|
||||
if (staticProps) defineProperties(Constructor, staticProps);
|
||||
return Constructor;
|
||||
};
|
||||
}();
|
||||
|
||||
/**
|
||||
* Sticky Sidebar v2 JavaScript Plugin.
|
||||
* @version 1.0.1
|
||||
* @author Øystein Blixhavn <oystein@blixhavn.no>
|
||||
* @license The MIT License (MIT)
|
||||
*/
|
||||
var StickySidebar = function () {
|
||||
|
||||
// ---------------------------------
|
||||
// # Define Constants
|
||||
// ---------------------------------
|
||||
//
|
||||
var EVENT_KEY = '.stickySidebar';
|
||||
var DEFAULTS = {
|
||||
/**
|
||||
* Additional top spacing of the element when it becomes sticky.
|
||||
* @type {Numeric|Function}
|
||||
*/
|
||||
topSpacing: 0,
|
||||
|
||||
/**
|
||||
* Additional bottom spacing of the element when it becomes sticky.
|
||||
* @type {Numeric|Function}
|
||||
*/
|
||||
bottomSpacing: 0,
|
||||
|
||||
/**
|
||||
* Container sidebar selector to know what the beginning and end of sticky element.
|
||||
* @type {String|False}
|
||||
*/
|
||||
containerSelector: false,
|
||||
|
||||
/**
|
||||
* Parent element where the scrolling happens.
|
||||
*/
|
||||
scrollContainer: false,
|
||||
|
||||
/**
|
||||
* Inner wrapper selector.
|
||||
* @type {String}
|
||||
*/
|
||||
innerWrapperSelector: '.inner-wrapper-sticky',
|
||||
|
||||
/**
|
||||
* The name of CSS class to apply to elements when they have become stuck.
|
||||
* @type {String|False}
|
||||
*/
|
||||
stickyClass: 'is-affixed',
|
||||
|
||||
/**
|
||||
* Detect when sidebar and its container change height so re-calculate their dimensions.
|
||||
* @type {Boolean}
|
||||
*/
|
||||
resizeSensor: true,
|
||||
|
||||
/**
|
||||
* The sidebar returns to its normal position if its width below this value.
|
||||
* @type {Numeric}
|
||||
*/
|
||||
minWidth: false
|
||||
};
|
||||
|
||||
// ---------------------------------
|
||||
// # Class Definition
|
||||
// ---------------------------------
|
||||
//
|
||||
/**
|
||||
* Sticky Sidebar Class.
|
||||
* @public
|
||||
*/
|
||||
|
||||
var StickySidebar = function () {
|
||||
|
||||
/**
|
||||
* Sticky Sidebar Constructor.
|
||||
* @constructor
|
||||
* @param {HTMLElement|String} sidebar - The sidebar element or sidebar selector.
|
||||
* @param {Object} options - The options of sticky sidebar.
|
||||
*/
|
||||
function StickySidebar(sidebar) {
|
||||
var _this = this;
|
||||
|
||||
var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
|
||||
|
||||
_classCallCheck(this, StickySidebar);
|
||||
|
||||
this.options = StickySidebar.extend(DEFAULTS, options);
|
||||
|
||||
// Sidebar element query if there's no one, throw error.
|
||||
this.sidebar = 'string' === typeof sidebar ? document.querySelector(sidebar) : sidebar;
|
||||
if ('undefined' === typeof this.sidebar) throw new Error("There is no specific sidebar element.");
|
||||
|
||||
this.sidebarInner = false;
|
||||
this.container = this.sidebar.parentElement;
|
||||
|
||||
// Current Affix Type of sidebar element.
|
||||
this.affixedType = 'STATIC';
|
||||
this.direction = 'down';
|
||||
this.support = {
|
||||
transform: false,
|
||||
transform3d: false
|
||||
};
|
||||
|
||||
this._initialized = false;
|
||||
this._reStyle = false;
|
||||
this._breakpoint = false;
|
||||
|
||||
// Dimensions of sidebar, container and screen viewport.
|
||||
this.dimensions = {
|
||||
translateY: 0,
|
||||
maxTranslateY: 0,
|
||||
topSpacing: 0,
|
||||
lastTopSpacing: 0,
|
||||
bottomSpacing: 0,
|
||||
lastBottomSpacing: 0,
|
||||
sidebarHeight: 0,
|
||||
sidebarWidth: 0,
|
||||
containerTop: 0,
|
||||
containerHeight: 0,
|
||||
viewportHeight: 0,
|
||||
viewportTop: 0,
|
||||
lastViewportTop: 0
|
||||
};
|
||||
|
||||
// Bind event handlers for referencability.
|
||||
['handleEvent'].forEach(function (method) {
|
||||
_this[method] = _this[method].bind(_this);
|
||||
});
|
||||
|
||||
// Initialize sticky sidebar for first time.
|
||||
this.initialize();
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the sticky sidebar by adding inner wrapper, define its container,
|
||||
* min-width breakpoint, calculating dimensions, adding helper classes and inline style.
|
||||
* @private
|
||||
*/
|
||||
|
||||
|
||||
_createClass(StickySidebar, [{
|
||||
key: 'initialize',
|
||||
value: function initialize() {
|
||||
var _this2 = this;
|
||||
|
||||
this._setSupportFeatures();
|
||||
|
||||
// Get sticky sidebar inner wrapper, if not found, will create one.
|
||||
if (this.options.innerWrapperSelector) {
|
||||
this.sidebarInner = this.sidebar.querySelector(this.options.innerWrapperSelector);
|
||||
|
||||
if (null === this.sidebarInner) this.sidebarInner = false;
|
||||
}
|
||||
|
||||
if (!this.sidebarInner) {
|
||||
var wrapper = document.createElement('div');
|
||||
wrapper.setAttribute('class', 'inner-wrapper-sticky');
|
||||
this.sidebar.appendChild(wrapper);
|
||||
|
||||
while (this.sidebar.firstChild != wrapper) {
|
||||
wrapper.appendChild(this.sidebar.firstChild);
|
||||
}this.sidebarInner = this.sidebar.querySelector('.inner-wrapper-sticky');
|
||||
}
|
||||
|
||||
// Container wrapper of the sidebar.
|
||||
if (this.options.containerSelector) {
|
||||
var containers = document.querySelectorAll(this.options.containerSelector);
|
||||
containers = Array.prototype.slice.call(containers);
|
||||
|
||||
containers.forEach(function (container, item) {
|
||||
if (!container.contains(_this2.sidebar)) return;
|
||||
_this2.container = container;
|
||||
});
|
||||
|
||||
if (!containers.length) throw new Error("The container does not contains on the sidebar.");
|
||||
}
|
||||
|
||||
// Get scroll container, if provided
|
||||
this.scrollContainer = this.options.scrollContainer ? document.querySelector(this.options.scrollContainer) : undefined;
|
||||
|
||||
// If top/bottom spacing is not function parse value to integer.
|
||||
if ('function' !== typeof this.options.topSpacing) this.options.topSpacing = parseInt(this.options.topSpacing) || 0;
|
||||
|
||||
if ('function' !== typeof this.options.bottomSpacing) this.options.bottomSpacing = parseInt(this.options.bottomSpacing) || 0;
|
||||
|
||||
// Breakdown sticky sidebar if screen width below `options.minWidth`.
|
||||
this._widthBreakpoint();
|
||||
|
||||
// Calculate dimensions of sidebar, container and viewport.
|
||||
this.calcDimensions();
|
||||
|
||||
// Affix sidebar in proper position.
|
||||
this.stickyPosition();
|
||||
|
||||
// Bind all events.
|
||||
this.bindEvents();
|
||||
|
||||
// Inform other properties the sticky sidebar is initialized.
|
||||
this._initialized = true;
|
||||
}
|
||||
}, {
|
||||
key: 'bindEvents',
|
||||
value: function bindEvents() {
|
||||
this.eventTarget = this.scrollContainer ? this.scrollContainer : window;
|
||||
|
||||
this.eventTarget.addEventListener('resize', this, { passive: true, capture: false });
|
||||
this.eventTarget.addEventListener('scroll', this, { passive: true, capture: false });
|
||||
|
||||
this.sidebar.addEventListener('update' + EVENT_KEY, this);
|
||||
|
||||
if (this.options.resizeSensor && 'undefined' !== typeof ResizeSensor) {
|
||||
new ResizeSensor(this.sidebarInner, this.handleEvent);
|
||||
new ResizeSensor(this.container, this.handleEvent);
|
||||
}
|
||||
}
|
||||
}, {
|
||||
key: 'handleEvent',
|
||||
value: function handleEvent(event) {
|
||||
this.updateSticky(event);
|
||||
}
|
||||
}, {
|
||||
key: 'calcDimensions',
|
||||
value: function calcDimensions() {
|
||||
if (this._breakpoint) return;
|
||||
var dims = this.dimensions;
|
||||
|
||||
// Container of sticky sidebar dimensions.
|
||||
dims.containerTop = StickySidebar.offsetRelative(this.container).top;
|
||||
dims.containerHeight = this.container.clientHeight;
|
||||
dims.containerBottom = dims.containerTop + dims.containerHeight;
|
||||
|
||||
// Sidebar dimensions.
|
||||
dims.sidebarHeight = this.sidebarInner.offsetHeight;
|
||||
dims.sidebarWidth = this.sidebarInner.offsetWidth;
|
||||
|
||||
// Screen viewport dimensions.
|
||||
dims.viewportHeight = window.innerHeight;
|
||||
|
||||
// Maximum sidebar translate Y.
|
||||
dims.maxTranslateY = dims.containerHeight - dims.sidebarHeight;
|
||||
|
||||
this._calcDimensionsWithScroll();
|
||||
}
|
||||
}, {
|
||||
key: '_calcDimensionsWithScroll',
|
||||
value: function _calcDimensionsWithScroll() {
|
||||
var dims = this.dimensions;
|
||||
|
||||
dims.sidebarLeft = StickySidebar.offsetRelative(this.sidebar).left;
|
||||
|
||||
if (this.scrollContainer) {
|
||||
dims.viewportTop = this.scrollContainer.scrollTop;
|
||||
dims.viewportLeft = this.scrollContainer.scrollLeft;
|
||||
} else {
|
||||
dims.viewportTop = document.documentElement.scrollTop || document.body.scrollTop;
|
||||
dims.viewportLeft = document.documentElement.scrollLeft || document.body.scrollLeft;
|
||||
}
|
||||
dims.viewportBottom = dims.viewportTop + dims.viewportHeight;
|
||||
|
||||
dims.topSpacing = this.options.topSpacing;
|
||||
dims.bottomSpacing = this.options.bottomSpacing;
|
||||
|
||||
if ('function' === typeof dims.topSpacing) dims.topSpacing = parseInt(dims.topSpacing(this.sidebar)) || 0;
|
||||
|
||||
if ('function' === typeof dims.bottomSpacing) dims.bottomSpacing = parseInt(dims.bottomSpacing(this.sidebar)) || 0;
|
||||
|
||||
if ('VIEWPORT-TOP' === this.affixedType) {
|
||||
// Adjust translate Y in the case decrease top spacing value.
|
||||
if (dims.topSpacing < dims.lastTopSpacing) {
|
||||
dims.translateY += dims.lastTopSpacing - dims.topSpacing;
|
||||
this._reStyle = true;
|
||||
}
|
||||
} else if ('VIEWPORT-BOTTOM' === this.affixedType) {
|
||||
// Adjust translate Y in the case decrease bottom spacing value.
|
||||
if (dims.bottomSpacing < dims.lastBottomSpacing) {
|
||||
dims.translateY += dims.lastBottomSpacing - dims.bottomSpacing;
|
||||
this._reStyle = true;
|
||||
}
|
||||
}
|
||||
|
||||
dims.lastTopSpacing = dims.topSpacing;
|
||||
dims.lastBottomSpacing = dims.bottomSpacing;
|
||||
}
|
||||
}, {
|
||||
key: 'isSidebarFitsViewport',
|
||||
value: function isSidebarFitsViewport() {
|
||||
var dims = this.dimensions;
|
||||
var offset = this.scrollDirection === 'down' ? dims.lastBottomSpacing : dims.lastTopSpacing;
|
||||
return this.dimensions.sidebarHeight + offset < this.dimensions.viewportHeight;
|
||||
}
|
||||
}, {
|
||||
key: 'observeScrollDir',
|
||||
value: function observeScrollDir() {
|
||||
var dims = this.dimensions;
|
||||
if (dims.lastViewportTop === dims.viewportTop) return;
|
||||
|
||||
var furthest = 'down' === this.direction ? Math.min : Math.max;
|
||||
|
||||
// If the browser is scrolling not in the same direction.
|
||||
if (dims.viewportTop === furthest(dims.viewportTop, dims.lastViewportTop)) this.direction = 'down' === this.direction ? 'up' : 'down';
|
||||
}
|
||||
}, {
|
||||
key: 'getAffixType',
|
||||
value: function getAffixType() {
|
||||
this._calcDimensionsWithScroll();
|
||||
var dims = this.dimensions;
|
||||
var colliderTop = dims.viewportTop + dims.topSpacing;
|
||||
var affixType = this.affixedType;
|
||||
|
||||
if (colliderTop <= dims.containerTop || dims.containerHeight <= dims.sidebarHeight) {
|
||||
dims.translateY = 0;
|
||||
affixType = 'STATIC';
|
||||
} else {
|
||||
affixType = 'up' === this.direction ? this._getAffixTypeScrollingUp() : this._getAffixTypeScrollingDown();
|
||||
}
|
||||
|
||||
// Make sure the translate Y is not bigger than container height.
|
||||
dims.translateY = Math.max(0, dims.translateY);
|
||||
dims.translateY = Math.min(dims.containerHeight, dims.translateY);
|
||||
dims.translateY = Math.round(dims.translateY);
|
||||
|
||||
dims.lastViewportTop = dims.viewportTop;
|
||||
return affixType;
|
||||
}
|
||||
}, {
|
||||
key: '_getAffixTypeScrollingDown',
|
||||
value: function _getAffixTypeScrollingDown() {
|
||||
var dims = this.dimensions;
|
||||
var sidebarBottom = dims.sidebarHeight + dims.containerTop;
|
||||
var colliderTop = dims.viewportTop + dims.topSpacing;
|
||||
var colliderBottom = dims.viewportBottom - dims.bottomSpacing;
|
||||
var affixType = this.affixedType;
|
||||
|
||||
if (this.isSidebarFitsViewport()) {
|
||||
if (dims.sidebarHeight + colliderTop >= dims.containerBottom) {
|
||||
dims.translateY = dims.containerBottom - sidebarBottom;
|
||||
affixType = 'CONTAINER-BOTTOM';
|
||||
} else if (colliderTop >= dims.containerTop) {
|
||||
dims.translateY = colliderTop - dims.containerTop;
|
||||
affixType = 'VIEWPORT-TOP';
|
||||
}
|
||||
} else {
|
||||
if (dims.containerBottom <= colliderBottom) {
|
||||
dims.translateY = dims.containerBottom - sidebarBottom;
|
||||
affixType = 'CONTAINER-BOTTOM';
|
||||
} else if (sidebarBottom + dims.translateY <= colliderBottom) {
|
||||
dims.translateY = colliderBottom - sidebarBottom;
|
||||
affixType = 'VIEWPORT-BOTTOM';
|
||||
} else if (dims.containerTop + dims.translateY <= colliderTop && 0 !== dims.translateY && dims.maxTranslateY !== dims.translateY) {
|
||||
affixType = 'VIEWPORT-UNBOTTOM';
|
||||
}
|
||||
}
|
||||
|
||||
return affixType;
|
||||
}
|
||||
}, {
|
||||
key: '_getAffixTypeScrollingUp',
|
||||
value: function _getAffixTypeScrollingUp() {
|
||||
var dims = this.dimensions;
|
||||
var sidebarBottom = dims.sidebarHeight + dims.containerTop;
|
||||
var colliderTop = dims.viewportTop + dims.topSpacing;
|
||||
var colliderBottom = dims.viewportBottom - dims.bottomSpacing;
|
||||
var affixType = this.affixedType;
|
||||
|
||||
if (colliderTop <= dims.translateY + dims.containerTop) {
|
||||
dims.translateY = colliderTop - dims.containerTop;
|
||||
affixType = 'VIEWPORT-TOP';
|
||||
} else if (dims.containerBottom <= colliderBottom) {
|
||||
dims.translateY = dims.containerBottom - sidebarBottom;
|
||||
affixType = 'CONTAINER-BOTTOM';
|
||||
} else if (!this.isSidebarFitsViewport()) {
|
||||
|
||||
if (dims.containerTop <= colliderTop && 0 !== dims.translateY && dims.maxTranslateY !== dims.translateY) {
|
||||
affixType = 'VIEWPORT-UNBOTTOM';
|
||||
}
|
||||
}
|
||||
|
||||
return affixType;
|
||||
}
|
||||
}, {
|
||||
key: '_getStyle',
|
||||
value: function _getStyle(affixType) {
|
||||
if ('undefined' === typeof affixType) return;
|
||||
|
||||
var style = { inner: {}, outer: {} };
|
||||
var dims = this.dimensions;
|
||||
|
||||
switch (affixType) {
|
||||
case 'VIEWPORT-TOP':
|
||||
style.inner = { position: 'fixed', top: dims.topSpacing,
|
||||
left: dims.sidebarLeft - dims.viewportLeft, width: dims.sidebarWidth };
|
||||
break;
|
||||
case 'VIEWPORT-BOTTOM':
|
||||
style.inner = { position: 'fixed', top: 'auto', left: dims.sidebarLeft,
|
||||
bottom: dims.bottomSpacing, width: dims.sidebarWidth };
|
||||
break;
|
||||
case 'CONTAINER-BOTTOM':
|
||||
case 'VIEWPORT-UNBOTTOM':
|
||||
var translate = this._getTranslate(0, dims.translateY + 'px');
|
||||
|
||||
if (translate) style.inner = { transform: translate };else style.inner = { position: 'absolute', top: dims.translateY, width: dims.sidebarWidth };
|
||||
break;
|
||||
}
|
||||
|
||||
switch (affixType) {
|
||||
case 'VIEWPORT-TOP':
|
||||
case 'VIEWPORT-BOTTOM':
|
||||
case 'VIEWPORT-UNBOTTOM':
|
||||
case 'CONTAINER-BOTTOM':
|
||||
style.outer = { height: dims.sidebarHeight, position: 'relative' };
|
||||
break;
|
||||
}
|
||||
|
||||
style.outer = StickySidebar.extend({ height: '', position: '' }, style.outer);
|
||||
style.inner = StickySidebar.extend({ position: 'relative', top: '', left: '',
|
||||
bottom: '', width: '', transform: '' }, style.inner);
|
||||
|
||||
return style;
|
||||
}
|
||||
}, {
|
||||
key: 'stickyPosition',
|
||||
value: function stickyPosition(force) {
|
||||
if (this._breakpoint) return;
|
||||
|
||||
force = this._reStyle || force || false;
|
||||
|
||||
var offsetTop = this.options.topSpacing;
|
||||
var offsetBottom = this.options.bottomSpacing;
|
||||
|
||||
var affixType = this.getAffixType();
|
||||
var style = this._getStyle(affixType);
|
||||
|
||||
if ((this.affixedType != affixType || force) && affixType) {
|
||||
var affixEvent = 'affix.' + affixType.toLowerCase().replace('viewport-', '') + EVENT_KEY;
|
||||
StickySidebar.eventTrigger(this.sidebar, affixEvent);
|
||||
|
||||
if ('STATIC' === affixType) StickySidebar.removeClass(this.sidebar, this.options.stickyClass);else StickySidebar.addClass(this.sidebar, this.options.stickyClass);
|
||||
|
||||
for (var key in style.outer) {
|
||||
var unit = 'number' === typeof style.outer[key] ? 'px' : '';
|
||||
this.sidebar.style[key] = style.outer[key] + unit;
|
||||
}
|
||||
|
||||
for (var _key in style.inner) {
|
||||
var _unit = 'number' === typeof style.inner[_key] ? 'px' : '';
|
||||
this.sidebarInner.style[_key] = style.inner[_key] + _unit;
|
||||
}
|
||||
|
||||
var affixedEvent = 'affixed.' + affixType.toLowerCase().replace('viewport-', '') + EVENT_KEY;
|
||||
StickySidebar.eventTrigger(this.sidebar, affixedEvent);
|
||||
} else {
|
||||
if (this._initialized) this.sidebarInner.style.left = style.inner.left;
|
||||
}
|
||||
|
||||
this.affixedType = affixType;
|
||||
}
|
||||
}, {
|
||||
key: '_widthBreakpoint',
|
||||
value: function _widthBreakpoint() {
|
||||
|
||||
if (window.innerWidth <= this.options.minWidth) {
|
||||
this._breakpoint = true;
|
||||
this.affixedType = 'STATIC';
|
||||
|
||||
this.sidebar.removeAttribute('style');
|
||||
StickySidebar.removeClass(this.sidebar, this.options.stickyClass);
|
||||
this.sidebarInner.removeAttribute('style');
|
||||
} else {
|
||||
this._breakpoint = false;
|
||||
}
|
||||
}
|
||||
}, {
|
||||
key: 'updateSticky',
|
||||
value: function updateSticky() {
|
||||
var _this3 = this;
|
||||
|
||||
var event = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
|
||||
|
||||
if (this._running) return;
|
||||
this._running = true;
|
||||
|
||||
(function (eventType) {
|
||||
requestAnimationFrame(function () {
|
||||
switch (eventType) {
|
||||
// When browser is scrolling and re-calculate just dimensions
|
||||
// within scroll.
|
||||
case 'scroll':
|
||||
_this3._calcDimensionsWithScroll();
|
||||
_this3.observeScrollDir();
|
||||
_this3.stickyPosition();
|
||||
break;
|
||||
|
||||
// When browser is resizing or there's no event, observe width
|
||||
// breakpoint and re-calculate dimensions.
|
||||
case 'resize':
|
||||
default:
|
||||
_this3._widthBreakpoint();
|
||||
_this3.calcDimensions();
|
||||
_this3.stickyPosition(true);
|
||||
break;
|
||||
}
|
||||
_this3._running = false;
|
||||
});
|
||||
})(event.type);
|
||||
}
|
||||
}, {
|
||||
key: '_setSupportFeatures',
|
||||
value: function _setSupportFeatures() {
|
||||
var support = this.support;
|
||||
|
||||
support.transform = StickySidebar.supportTransform();
|
||||
support.transform3d = StickySidebar.supportTransform(true);
|
||||
}
|
||||
}, {
|
||||
key: '_getTranslate',
|
||||
value: function _getTranslate() {
|
||||
var y = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 0;
|
||||
var x = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0;
|
||||
var z = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 0;
|
||||
|
||||
if (this.support.transform3d) return 'translate3d(' + y + ', ' + x + ', ' + z + ')';else if (this.support.translate) return 'translate(' + y + ', ' + x + ')';else return false;
|
||||
}
|
||||
}, {
|
||||
key: 'destroy',
|
||||
value: function destroy() {
|
||||
window.removeEventListener('resize', this, { capture: false });
|
||||
window.removeEventListener('scroll', this, { capture: false });
|
||||
|
||||
this.sidebar.classList.remove(this.options.stickyClass);
|
||||
this.sidebar.style.minHeight = '';
|
||||
|
||||
this.sidebar.removeEventListener('update' + EVENT_KEY, this);
|
||||
|
||||
var styleReset = { inner: {}, outer: {} };
|
||||
|
||||
styleReset.inner = { position: '', top: '', left: '', bottom: '', width: '', transform: '' };
|
||||
styleReset.outer = { height: '', position: '' };
|
||||
|
||||
for (var key in styleReset.outer) {
|
||||
this.sidebar.style[key] = styleReset.outer[key];
|
||||
}for (var _key2 in styleReset.inner) {
|
||||
this.sidebarInner.style[_key2] = styleReset.inner[_key2];
|
||||
}if (this.options.resizeSensor && 'undefined' !== typeof ResizeSensor) {
|
||||
ResizeSensor.detach(this.sidebarInner, this.handleEvent);
|
||||
ResizeSensor.detach(this.container, this.handleEvent);
|
||||
}
|
||||
}
|
||||
}], [{
|
||||
key: 'supportTransform',
|
||||
value: function supportTransform(transform3d) {
|
||||
var result = false,
|
||||
property = transform3d ? 'perspective' : 'transform',
|
||||
upper = property.charAt(0).toUpperCase() + property.slice(1),
|
||||
prefixes = ['Webkit', 'Moz', 'O', 'ms'],
|
||||
support = document.createElement('support'),
|
||||
style = support.style;
|
||||
|
||||
(property + ' ' + prefixes.join(upper + ' ') + upper).split(' ').forEach(function (property, i) {
|
||||
if (style[property] !== undefined) {
|
||||
result = property;
|
||||
return false;
|
||||
}
|
||||
});
|
||||
return result;
|
||||
}
|
||||
}, {
|
||||
key: 'eventTrigger',
|
||||
value: function eventTrigger(element, eventName, data) {
|
||||
try {
|
||||
var event = new CustomEvent(eventName, { detail: data });
|
||||
} catch (e) {
|
||||
var event = document.createEvent('CustomEvent');
|
||||
event.initCustomEvent(eventName, true, true, data);
|
||||
}
|
||||
element.dispatchEvent(event);
|
||||
}
|
||||
}, {
|
||||
key: 'extend',
|
||||
value: function extend(defaults, options) {
|
||||
var results = {};
|
||||
for (var key in defaults) {
|
||||
if ('undefined' !== typeof options[key]) results[key] = options[key];else results[key] = defaults[key];
|
||||
}
|
||||
return results;
|
||||
}
|
||||
}, {
|
||||
key: 'offsetRelative',
|
||||
value: function offsetRelative(element) {
|
||||
var result = { left: 0, top: 0 };
|
||||
|
||||
do {
|
||||
var offsetTop = element.offsetTop;
|
||||
var offsetLeft = element.offsetLeft;
|
||||
|
||||
if (!isNaN(offsetTop)) result.top += offsetTop;
|
||||
|
||||
if (!isNaN(offsetLeft)) result.left += offsetLeft;
|
||||
|
||||
element = 'BODY' === element.tagName ? element.parentElement : element.offsetParent;
|
||||
} while (element);
|
||||
return result;
|
||||
}
|
||||
}, {
|
||||
key: 'addClass',
|
||||
value: function addClass(element, className) {
|
||||
if (!StickySidebar.hasClass(element, className)) {
|
||||
if (element.classList) element.classList.add(className);else element.className += ' ' + className;
|
||||
}
|
||||
}
|
||||
}, {
|
||||
key: 'removeClass',
|
||||
value: function removeClass(element, className) {
|
||||
if (StickySidebar.hasClass(element, className)) {
|
||||
if (element.classList) element.classList.remove(className);else element.className = element.className.replace(new RegExp('(^|\\b)' + className.split(' ').join('|') + '(\\b|$)', 'gi'), ' ');
|
||||
}
|
||||
}
|
||||
}, {
|
||||
key: 'hasClass',
|
||||
value: function hasClass(element, className) {
|
||||
if (element.classList) return element.classList.contains(className);else return new RegExp('(^| )' + className + '( |$)', 'gi').test(element.className);
|
||||
}
|
||||
}, {
|
||||
key: 'defaults',
|
||||
get: function () {
|
||||
return DEFAULTS;
|
||||
}
|
||||
}]);
|
||||
|
||||
return StickySidebar;
|
||||
}();
|
||||
|
||||
return StickySidebar;
|
||||
}();
|
||||
|
||||
exports.default = StickySidebar;
|
||||
|
||||
|
||||
// Global
|
||||
// -------------------------
|
||||
window.StickySidebar = StickySidebar;
|
||||
});
|
||||
});
|
||||
|
||||
var stickySidebar$1 = unwrapExports(stickySidebar);
|
||||
|
||||
exports['default'] = stickySidebar$1;
|
||||
exports.__moduleExports = stickySidebar;
|
||||
|
||||
Object.defineProperty(exports, '__esModule', { value: true });
|
||||
|
||||
})));
|
||||
|
||||
//# sourceMappingURL=sticky-sidebar.js.map
|
1
js/sticky-sidebar.min.js
vendored
Normal file
1
js/sticky-sidebar.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user