first commit

This commit is contained in:
2023-09-08 01:45:46 -07:00
commit 8cbf53172b
108 changed files with 29005 additions and 0 deletions

936
js/ResizeObserver.js Normal file
View 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

File diff suppressed because one or more lines are too long

269
js/alpha-color-picker.js Normal file
View 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
View 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
View 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
View 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

File diff suppressed because one or more lines are too long

707
js/sticky-sidebar.js Normal file
View 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

File diff suppressed because one or more lines are too long