datalad-course/node_modules/fitty/dist/fitty.module.js

342 lines
No EOL
10 KiB
JavaScript

/*
* fitty v2.3.3 - Snugly resizes text to fit its parent container
* Copyright (c) 2020 Rik Schennink <rik@pqina.nl> (https://pqina.nl/)
*/
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; };
exports.default = function (w) {
// no window, early exit
if (!w) return;
// node list to array helper method
var toArray = function toArray(nl) {
return [].slice.call(nl);
};
// states
var DrawState = {
IDLE: 0,
DIRTY_CONTENT: 1,
DIRTY_LAYOUT: 2,
DIRTY: 3
};
// all active fitty elements
var fitties = [];
// group all redraw calls till next frame, we cancel each frame request when a new one comes in. If no support for request animation frame, this is an empty function and supports for fitty stops.
var redrawFrame = null;
var requestRedraw = 'requestAnimationFrame' in w ? function () {
w.cancelAnimationFrame(redrawFrame);
redrawFrame = w.requestAnimationFrame(function () {
return redraw(fitties.filter(function (f) {
return f.dirty && f.active;
}));
});
} : function () {};
// sets all fitties to dirty so they are redrawn on the next redraw loop, then calls redraw
var redrawAll = function redrawAll(type) {
return function () {
fitties.forEach(function (f) {
return f.dirty = type;
});
requestRedraw();
};
};
// redraws fitties so they nicely fit their parent container
var redraw = function redraw(fitties) {
// getting info from the DOM at this point should not trigger a reflow, let's gather as much intel as possible before triggering a reflow
// check if styles of all fitties have been computed
fitties.filter(function (f) {
return !f.styleComputed;
}).forEach(function (f) {
f.styleComputed = computeStyle(f);
});
// restyle elements that require pre-styling, this triggers a reflow, please try to prevent by adding CSS rules (see docs)
fitties.filter(shouldPreStyle).forEach(applyStyle);
// we now determine which fitties should be redrawn
var fittiesToRedraw = fitties.filter(shouldRedraw);
// we calculate final styles for these fitties
fittiesToRedraw.forEach(calculateStyles);
// now we apply the calculated styles from our previous loop
fittiesToRedraw.forEach(function (f) {
applyStyle(f);
markAsClean(f);
});
// now we dispatch events for all restyled fitties
fittiesToRedraw.forEach(dispatchFitEvent);
};
var markAsClean = function markAsClean(f) {
return f.dirty = DrawState.IDLE;
};
var calculateStyles = function calculateStyles(f) {
// get available width from parent node
f.availableWidth = f.element.parentNode.clientWidth;
// the space our target element uses
f.currentWidth = f.element.scrollWidth;
// remember current font size
f.previousFontSize = f.currentFontSize;
// let's calculate the new font size
f.currentFontSize = Math.min(Math.max(f.minSize, f.availableWidth / f.currentWidth * f.previousFontSize), f.maxSize);
// if allows wrapping, only wrap when at minimum font size (otherwise would break container)
f.whiteSpace = f.multiLine && f.currentFontSize === f.minSize ? 'normal' : 'nowrap';
};
// should always redraw if is not dirty layout, if is dirty layout, only redraw if size has changed
var shouldRedraw = function shouldRedraw(f) {
return f.dirty !== DrawState.DIRTY_LAYOUT || f.dirty === DrawState.DIRTY_LAYOUT && f.element.parentNode.clientWidth !== f.availableWidth;
};
// every fitty element is tested for invalid styles
var computeStyle = function computeStyle(f) {
// get style properties
var style = w.getComputedStyle(f.element, null);
// get current font size in pixels (if we already calculated it, use the calculated version)
f.currentFontSize = parseFloat(style.getPropertyValue('font-size'));
// get display type and wrap mode
f.display = style.getPropertyValue('display');
f.whiteSpace = style.getPropertyValue('white-space');
};
// determines if this fitty requires initial styling, can be prevented by applying correct styles through CSS
var shouldPreStyle = function shouldPreStyle(f) {
var preStyle = false;
// if we already tested for prestyling we don't have to do it again
if (f.preStyleTestCompleted) return false;
// should have an inline style, if not, apply
if (!/inline-/.test(f.display)) {
preStyle = true;
f.display = 'inline-block';
}
// to correctly calculate dimensions the element should have whiteSpace set to nowrap
if (f.whiteSpace !== 'nowrap') {
preStyle = true;
f.whiteSpace = 'nowrap';
}
// we don't have to do this twice
f.preStyleTestCompleted = true;
return preStyle;
};
// apply styles to single fitty
var applyStyle = function applyStyle(f) {
f.element.style.whiteSpace = f.whiteSpace;
f.element.style.display = f.display;
f.element.style.fontSize = f.currentFontSize + 'px';
};
// dispatch a fit event on a fitty
var dispatchFitEvent = function dispatchFitEvent(f) {
f.element.dispatchEvent(new CustomEvent('fit', {
detail: {
oldValue: f.previousFontSize,
newValue: f.currentFontSize,
scaleFactor: f.currentFontSize / f.previousFontSize
}
}));
};
// fit method, marks the fitty as dirty and requests a redraw (this will also redraw any other fitty marked as dirty)
var fit = function fit(f, type) {
return function () {
f.dirty = type;
if (!f.active) return;
requestRedraw();
};
};
var init = function init(f) {
// save some of the original CSS properties before we change them
f.originalStyle = {
whiteSpace: f.element.style.whiteSpace,
display: f.element.style.display,
fontSize: f.element.style.fontSize
};
// should we observe DOM mutations
observeMutations(f);
// this is a new fitty so we need to validate if it's styles are in order
f.newbie = true;
// because it's a new fitty it should also be dirty, we want it to redraw on the first loop
f.dirty = true;
// we want to be able to update this fitty
fitties.push(f);
};
var destroy = function destroy(f) {
return function () {
// remove from fitties array
fitties = fitties.filter(function (_) {
return _.element !== f.element;
});
// stop observing DOM
if (f.observeMutations) f.observer.disconnect();
// reset the CSS properties we changes
f.element.style.whiteSpace = f.originalStyle.whiteSpace;
f.element.style.display = f.originalStyle.display;
f.element.style.fontSize = f.originalStyle.fontSize;
};
};
// add a new fitty, does not redraw said fitty
var subscribe = function subscribe(f) {
return function () {
if (f.active) return;
f.active = true;
requestRedraw();
};
};
// remove an existing fitty
var unsubscribe = function unsubscribe(f) {
return function () {
return f.active = false;
};
};
var observeMutations = function observeMutations(f) {
// no observing?
if (!f.observeMutations) return;
// start observing mutations
f.observer = new MutationObserver(fit(f, DrawState.DIRTY_CONTENT));
// start observing
f.observer.observe(f.element, f.observeMutations);
};
// default mutation observer settings
var mutationObserverDefaultSetting = {
subtree: true,
childList: true,
characterData: true
};
// default fitty options
var defaultOptions = {
minSize: 16,
maxSize: 512,
multiLine: true,
observeMutations: 'MutationObserver' in w ? mutationObserverDefaultSetting : false
};
// array of elements in, fitty instances out
function fittyCreate(elements, options) {
// set options object
var fittyOptions = _extends({}, defaultOptions, options);
// create fitties
var publicFitties = elements.map(function (element) {
// create fitty instance
var f = _extends({}, fittyOptions, {
// internal options for this fitty
element: element,
active: true
});
// initialise this fitty
init(f);
// expose API
return {
element: element,
fit: fit(f, DrawState.DIRTY),
unfreeze: subscribe(f),
freeze: unsubscribe(f),
unsubscribe: destroy(f)
};
});
// call redraw on newly initiated fitties
requestRedraw();
// expose fitties
return publicFitties;
}
// fitty creation function
function fitty(target) {
var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
// if target is a string
return typeof target === 'string' ?
// treat it as a querySelector
fittyCreate(toArray(document.querySelectorAll(target)), options) :
// create single fitty
fittyCreate([target], options)[0];
}
// handles viewport changes, redraws all fitties, but only does so after a timeout
var resizeDebounce = null;
var onWindowResized = function onWindowResized() {
w.clearTimeout(resizeDebounce);
resizeDebounce = w.setTimeout(redrawAll(DrawState.DIRTY_LAYOUT), fitty.observeWindowDelay);
};
// define observe window property, so when we set it to true or false events are automatically added and removed
var events = ['resize', 'orientationchange'];
Object.defineProperty(fitty, 'observeWindow', {
set: function set(enabled) {
var method = (enabled ? 'add' : 'remove') + 'EventListener';
events.forEach(function (e) {
w[method](e, onWindowResized);
});
}
});
// fitty global properties (by setting observeWindow to true the events above get added)
fitty.observeWindow = true;
fitty.observeWindowDelay = 100;
// public fit all method, will force redraw no matter what
fitty.fitAll = redrawAll(DrawState.DIRTY);
// export our fitty function, we don't want to keep it to our selves
return fitty;
}(typeof window === 'undefined' ? null : window);