UNPKG

@firstandthird/toc

Version:
792 lines (632 loc) 19.9 kB
var FirstandthirdToc = (function () { 'use strict'; function isWindow(obj) { return obj != null && obj === obj.window; } function find(selector, context) { if (context === void 0) { context = null; } if (selector instanceof HTMLElement || selector instanceof Node || isWindow(selector)) { return [selector]; } else if (selector instanceof NodeList) { return [].slice.call(selector); } else if (typeof selector === 'string') { var startElement = context ? find(context)[0] : document; return [].slice.call(startElement.querySelectorAll(selector)); } return []; } function addClass(selector, cls) { if (Array.isArray(selector)) { return selector.forEach(function (item) { return addClass(item, cls); }); } var els = find(selector); if (els.length) { var clsArray = [].concat(cls); els.forEach(function (el) { clsArray.forEach(function (item) { el.classList.add(item); }); }); return els; } } function on(selector, event, cb, capture) { if (capture === void 0) { capture = false; } if (Array.isArray(selector)) { selector.forEach(function (item) { return on(item, event, cb, capture); }); return; } var data = { cb: cb, capture: capture }; if (!window._domassistevents) { window._domassistevents = {}; } window._domassistevents["_" + event] = data; var el = find(selector); if (el.length) { el.forEach(function (item) { item.addEventListener(event, cb, capture); }); } } function findOne(selector, el) { var found = find(selector, el); if (found.length) { return found[0]; } return null; } // Check for the usage of native support for CustomEvents which is lacking // completely on IE. // function canIuseNativeCustom() { try { var p = new CustomEvent('t', { detail: { a: 'b' } }); return p.type === 't' && p.detail.a === 'b'; } catch (e) { return false; } } // Lousy polyfill for the Custom Event constructor for IE. var IECustomEvent = function CustomEvent(type, params) { var e = document.createEvent('CustomEvent'); if (params) { e.initCustomEvent(type, params.bubbles, params.cancelable, params.detail); } else { e.initCustomEvent(type, false, false, undefined); } return e; }; var DomassistCustomEvent = false; function fire(selector, type, params) { if (params === void 0) { params = {}; } if (Array.isArray(selector)) { return selector.forEach(function (item) { return fire(item, type, params); }); } if (!DomassistCustomEvent) { DomassistCustomEvent = canIuseNativeCustom() ? CustomEvent : IECustomEvent; } var els = find(selector); if (els.length) { if (params.bubbles !== false) { params.bubbles = true; } els.forEach(function (el) { var event = new DomassistCustomEvent(type, params); el.dispatchEvent(event); }); return els; } } function removeClass(selector, cls) { if (Array.isArray(selector)) { return selector.forEach(function (item) { return removeClass(item, cls); }); } var els = find(selector); if (els.length) { var clsArray = [].concat(cls); els.forEach(function (el) { clsArray.forEach(function (item) { el.classList.remove(item); }); }); return els; } } var setupReady = function setupReady(callbacks) { return function (callback) { callbacks.push(callback); function execute() { while (callbacks.length) { var fn = callbacks.shift(); if (typeof fn === 'function') { fn(); } } } function loaded() { document.removeEventListener('DOMContentLoaded', loaded); execute(); } setTimeout(function () { if (document.readyState !== 'loading') { return execute(); } }, 0); document.addEventListener('DOMContentLoaded', loaded); }; }; var ready = setupReady([]); function styles(selector, css) { if (Array.isArray(selector)) { selector.forEach(function (item) { return styles(item, css); }); } var els = find(selector); if (els.length) { els.forEach(function (el) { Object.keys(css).forEach(function (key) { el.style[key] = css[key]; }); }); } } /* global window */ function attrobj(key, el) { var values = {}; Object.keys(el.dataset).forEach(function (data) { if (data.match(new RegExp("^" + key)) && data !== key) { var optionName = data.replace(key, ''); var isGlobal = false; if (optionName.match(/^Global/)) { optionName = optionName.replace('Global', ''); isGlobal = true; } optionName = "" + optionName[0].toLowerCase() + optionName.slice(1); if (isGlobal) { values[optionName] = window[el.dataset[data]]; } else { values[optionName] = el.dataset[data]; } if (typeof values[optionName] === 'undefined' || values[optionName] === '') { values[optionName] = true; } } }); return values; } var debounce = function debounce(func, wait, immediate) { var timeout; return function () { var context = this; var args = arguments; // eslint-disable-line prefer-rest-params var later = function later() { timeout = null; if (!immediate) { func.apply(context, args); } }; var callNow = immediate && !timeout; clearTimeout(timeout); timeout = setTimeout(later, wait); if (callNow) { func.apply(context, args); } }; }; var Events = { In: 'scrolltriggers:inView', Out: 'scrolltriggers:outOfView', Pause: 'scrolltriggers:pause', Resume: 'scrolltriggers:resume', Bounds: 'scrolltriggers:bounds' }; var ScrollTrigger = /*#__PURE__*/ function () { function ScrollTrigger(el, options) { var _this = this; if (el.hasAttribute('data-scroll-init')) { return; } this.added = false; this.el = el; this.options = options; this.eventHandler = debounce(this.onScroll.bind(this), 10, true); this.dCalcBounds = debounce(this.calcBounds.bind(this), 10); this.paused = false; this.disabled = false; this.calcOffset(); // If images, once by default if (this.options.image || this.options.src || this.options.srcset) { this.options.once = true; } el.setAttribute('data-scroll-init', 'true'); this.calcBounds(); window.addEventListener('scroll', this.eventHandler); window.addEventListener('resize', this.dCalcBounds); on(this.el, Events.Pause, function () { _this.paused = true; }); on(this.el, Events.Resume, function () { _this.paused = false; }); /* Prevents a bug on Blink+Webkit in which scroll is always 0 until around 400 milliseconds due to anchor scrolling features. */ setTimeout(this.eventHandler, 400); } var _proto = ScrollTrigger.prototype; _proto.calcBounds = function calcBounds() { this.calcOffset(); // Element is hidden and not fixed var isAllowedToBeFixed = this.options.progress === true || typeof this.options.fixed !== 'undefined'; if (!this.el.offsetParent && !isAllowedToBeFixed || this.added && this.options.once) { // Don't even bother calculating this.disabled = true; return; } this.disabled = false; var position = this.options.position || 'bottom'; this.startEl = this.options.start ? findOne(this.options.start) : this.el; ScrollTrigger.checkElement(this.startEl, 'start', this.options.start); var rect = this.startEl.getBoundingClientRect(); var scrollY = ScrollTrigger.getScrollY(); var start = rect.top + scrollY + (this.options.offset || 0); this.start = ScrollTrigger.processPosition(position, start); if (this.options.end) { var endEl = findOne(this.options.end); var endRect = endEl.getBoundingClientRect(); var end = endRect.top + scrollY; var endPosition = this.options.positionEnd || 'bottom'; if (endPosition === 'auto') { endPosition = 'top'; } this.end = ScrollTrigger.processPosition(endPosition, end); if (this.options.positionEnd === 'auto') { this.end -= this.el.offsetHeight; } ScrollTrigger.checkElement(endEl, 'end', this.options.end); } this.fire(Events.Bounds); this.eventHandler(); }; _proto.calcOffset = function calcOffset() { this.options.offset = this.options.offset ? this.options.offset : this.el.dataset.scrollOffset; // Half a screen above loading if (this.options.image || this.options.srcset || this.options.offset === 'auto') { this.options.offset = Math.max((document.documentElement.clientHeight, window.innerHeight || 0) / 2) * -1; } else { this.options.offset = parseInt(this.options.offset || 0, 10); } }; _proto.inView = function inView() { var _this$options = this.options, className = _this$options.className, inView = _this$options.inView; if (className && this.el.classList) { addClass(this.el, className); } var image = this.options.image; var src = this.options.src; var srcset = this.options.srcset; if (image || src) { if (this.el.tagName === 'IMG') { this.el.setAttribute('src', image); } else if (this.el.tagName === 'IFRAME' || this.el.tagName === 'VIDEO') { this.el.setAttribute('src', src); } else { styles(this.el, { backgroundImage: "url(" + image + ")", backgroundRepeat: 'no-repeat' }); } } if (srcset) { this.el.setAttribute('srcset', srcset); } if (typeof inView === 'function') { inView(this.el, this.options); } this.fire(Events.In); if (this.options.once) { this.disabled = true; window.removeEventListener('scroll', this.eventHandler); window.removeEventListener('resize', this.dCalcBounds); } this.added = true; }; _proto.outOfView = function outOfView() { var _this$options2 = this.options, className = _this$options2.className, outOfView = _this$options2.outOfView; if (className && this.el.classList) { removeClass(this.el, className); } if (typeof outOfView === 'function') { outOfView(this.el, this.options); } this.fire(Events.Out); this.added = false; }; _proto.fire = function fire$1(event) { fire(this.el, event, { detail: { instance: this, options: this.options } }); }; _proto.onScroll = function onScroll() { var scroll = ScrollTrigger.getScrollY(); if (this.paused || this.disabled) { return; } if (this.options.progress) { var perc = scroll / (document.documentElement.scrollHeight - window.innerHeight); this.el.style.width = perc * 100 + "%"; } if (scroll < this.start || this.end && scroll > this.end) { if (this.added) { this.outOfView(); } return; } if (this.added) { return; } this.inView(); }; ScrollTrigger.checkElement = function checkElement(element, position, selector) { if (!element) { throw new Error(position + " element doesn't match any element with selector: \"" + selector + "\""); } }; ScrollTrigger.getScrollY = function getScrollY() { return window.pageYOffset || document.documentElement.scrollTop; }; ScrollTrigger.processPosition = function processPosition(position, currentValue) { if (position === 'top') { return currentValue; } if (position === 'middle') { currentValue -= window.innerHeight / 2; } else if (position === 'bottom') { currentValue -= window.innerHeight; } else { currentValue -= window.innerHeight * (parseInt(position, 10) / 100); } return currentValue; }; return ScrollTrigger; }(); var init = function init(items) { var instances = []; if (items && Array.isArray(items)) { items.forEach(function (item) { var els = find(item.el); if (els === null) { throw new Error('unknown element'); } els.forEach(function (el) { delete item.el; instances.push(new ScrollTrigger(el, item)); }); }); } else if (items) { throw new Error('please convert object to array'); } else { var els = find('[data-scroll]'); els.forEach(function (el) { var options = attrobj('scroll', el); if (options.progress !== null && typeof options.progress !== 'undefined') { options.progress = true; } options.className = options["class"]; if (options.offset) { options.offset = parseInt(options.offset, 10); } if (typeof options.once !== 'undefined') { options.once = true; } instances.push(new ScrollTrigger(el, options)); }); } return instances; }; if (document.readyState !== 'complete') { // Avoid image loading impacting on calculations document.addEventListener('readystatechange', function () { if (document.readyState === 'complete') { fire(window, 'resize'); } }); } ready(init); init.Events = Events; init.ScrollTrigger = ScrollTrigger; /* global window,document */ var duration = 1000; var ease = function ease(t, b, c, d) { if ((t /= d / 2) < 1) return c / 2 * t * t * t * t + b; //eslint-disable-line return -c / 2 * ((t -= 2) * t * t * t - 2) + b; }; var animate = function animate(startTime, start, end, callback) { if (callback === void 0) { callback = function callback() {}; } var time = new Date().getTime(); var difference = end - start; var goingUp = difference < 0; if (difference === 0) { return; } var to = Math.round(ease(time - startTime, start, difference, duration)); if (!goingUp && to > end) { to = end; } if (goingUp && to < end) { to = end; } window.scrollTo(0, to); if (to === end) { setTimeout(callback); return; } if (to < 0) { return; } window.requestAnimationFrame(function () { return animate(startTime, start, end, callback); }); }; var scroll = function scroll(target, hash, offset, silent) { if (offset === void 0) { offset = 0; } if (silent === void 0) { silent = false; } fire(target, 'smoothscroll:start', { bubbles: true }); var rect = target.getBoundingClientRect(); var scrollY = window.pageYOffset || document.documentElement.scrollTop; var adjustedOffset = Math.round(rect.top + scrollY) + offset; var startTime = new Date(); if (!target.hasAttribute('tabindex')) { target.tabIndex = '-1'; } if (!silent) { window.history.pushState(null, 'Scroll', hash); } animate(startTime.getTime(), scrollY, adjustedOffset, function () { fire(target, 'smoothscroll:end', { bubbles: true }); }); target.focus(); }; var listenEvent = function listenEvent(el, offset) { if (el.dataset.smoothActive) { return; } el.dataset.smoothActive = true; el.addEventListener('click', function (e) { var hash = el.getAttribute('href'); if (hash[0] !== '#') { return; } e.preventDefault(); scroll(document.querySelector(hash), hash, offset); }); }; var init$1 = function init(query, offset) { if (query === void 0) { query = '[data-smooth]'; } if (offset === void 0) { offset = 0; } if (!window.requestAnimationFrame) { return; } var els = query; if (typeof query === 'string') { els = document.querySelectorAll(query); } if (els instanceof Element) { els = [els]; } for (var i = 0, c = els.length; i < c; i++) { var el = els[i]; listenEvent(el, offset); } }; window.addEventListener('DOMContentLoaded', function () { init$1(); }); function init$2(el) { if (!el) { el = find('[data-toc]'); el.forEach(function (e) { return init$2(e); }); return; } if (!el) { return; } var container = el.dataset.tocContainer ? findOne(el.dataset.tocContainer) || document.body : document.body; var selectors = el.dataset.toc.split(',').map(function (s) { return s.trim(); }); var tocItems = []; var offset = el.dataset.tocOffset ? parseInt(el.dataset.tocOffset, 10) : 1; var i = 1; // Building dict selectors.forEach(function (selector) { var items = find(selector, container); items.forEach(function (item) { // Keep the id if already there var index = item.id || "toc-" + i++; var text = item.dataset.tocTitle ? item.dataset.tocTitle.trim() : item.textContent.trim(); var sanitizedClassName = selector.replace(/((:+[\w-\d]*)|[^A-z0-9-\s])/g, ' ').replace(/\s{2,}/g, ' ').trim(); var className = "toc-" + sanitizedClassName; // Set it if none if (item.id !== index) { item.id = index; } tocItems.push({ index: index, text: text, className: className }); }); }); var html = '<ul>'; var triggerOptions = []; // Building markup tocItems.forEach(function (item, j) { var nextEl = tocItems[j + 1]; var options = { el: ".toc-li-" + j, fixed: 'true', start: "#" + item.index, position: 'top', positionEnd: 'top', className: 'toc-visible' }; html += "\n<li class=\"toc-li-" + j + " " + item.className + "\"><a href=\"#" + item.index + "\">" + item.text + "</a></li>"; if (nextEl) { options.end = "#" + nextEl.index; } triggerOptions.push(options); }); html += '</ul>'; el.innerHTML = html; var tocs = find('li', el); var anchors = find('a', el); // Setting up scroll triggers and smooth scroll init(triggerOptions); init$1(anchors, offset); // Pause scroll triggers while smoothscrolling on(document.body, 'smoothscroll:start', function () { fire(tocs, 'scrolltriggers:pause'); }); on(document.body, 'smoothscroll:end', function () { fire(tocs, 'scrolltriggers:resume'); fire(window, 'scroll'); }); if (window.location.hash) { anchors.some(function (anchor) { var found = anchor.getAttribute('href') === window.location.hash; if (found) { setTimeout(function () { var element = findOne(window.location.hash); if (element) { // Silent scroll to element scroll(element, null, offset, true); } }); } return found; }); } } ready(init$2); return init$2; }()); //# sourceMappingURL=toc.bundle.js.map