mirror of
https://github.com/Decentralized-ID/decentralized-id.github.io.git
synced 2025-08-16 18:10:37 -04:00
introducing: minimal mistakes
This commit is contained in:
parent
5d7c36f4e3
commit
f4e2cfa1f6
766 changed files with 41906 additions and 1080 deletions
482
assets/js/plugins/gumshoe.js
Normal file
482
assets/js/plugins/gumshoe.js
Normal file
|
@ -0,0 +1,482 @@
|
|||
/*!
|
||||
* gumshoejs v5.1.0
|
||||
* A simple, framework-agnostic scrollspy script.
|
||||
* (c) 2019 Chris Ferdinandi
|
||||
* MIT License
|
||||
* http://github.com/cferdinandi/gumshoe
|
||||
*/
|
||||
|
||||
(function (root, factory) {
|
||||
if ( typeof define === 'function' && define.amd ) {
|
||||
define([], (function () {
|
||||
return factory(root);
|
||||
}));
|
||||
} else if ( typeof exports === 'object' ) {
|
||||
module.exports = factory(root);
|
||||
} else {
|
||||
root.Gumshoe = factory(root);
|
||||
}
|
||||
})(typeof global !== 'undefined' ? global : typeof window !== 'undefined' ? window : this, (function (window) {
|
||||
|
||||
'use strict';
|
||||
|
||||
//
|
||||
// Defaults
|
||||
//
|
||||
|
||||
var defaults = {
|
||||
|
||||
// Active classes
|
||||
navClass: 'active',
|
||||
contentClass: 'active',
|
||||
|
||||
// Nested navigation
|
||||
nested: false,
|
||||
nestedClass: 'active',
|
||||
|
||||
// Offset & reflow
|
||||
offset: 0,
|
||||
reflow: false,
|
||||
|
||||
// Event support
|
||||
events: true
|
||||
|
||||
};
|
||||
|
||||
|
||||
//
|
||||
// Methods
|
||||
//
|
||||
|
||||
/**
|
||||
* Merge two or more objects together.
|
||||
* @param {Object} objects The objects to merge together
|
||||
* @returns {Object} Merged values of defaults and options
|
||||
*/
|
||||
var extend = function () {
|
||||
var merged = {};
|
||||
Array.prototype.forEach.call(arguments, (function (obj) {
|
||||
for (var key in obj) {
|
||||
if (!obj.hasOwnProperty(key)) return;
|
||||
merged[key] = obj[key];
|
||||
}
|
||||
}));
|
||||
return merged;
|
||||
};
|
||||
|
||||
/**
|
||||
* Emit a custom event
|
||||
* @param {String} type The event type
|
||||
* @param {Node} elem The element to attach the event to
|
||||
* @param {Object} detail Any details to pass along with the event
|
||||
*/
|
||||
var emitEvent = function (type, elem, detail) {
|
||||
|
||||
// Make sure events are enabled
|
||||
if (!detail.settings.events) return;
|
||||
|
||||
// Create a new event
|
||||
var event = new CustomEvent(type, {
|
||||
bubbles: true,
|
||||
cancelable: true,
|
||||
detail: detail
|
||||
});
|
||||
|
||||
// Dispatch the event
|
||||
elem.dispatchEvent(event);
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* Get an element's distance from the top of the Document.
|
||||
* @param {Node} elem The element
|
||||
* @return {Number} Distance from the top in pixels
|
||||
*/
|
||||
var getOffsetTop = function (elem) {
|
||||
var location = 0;
|
||||
if (elem.offsetParent) {
|
||||
while (elem) {
|
||||
location += elem.offsetTop;
|
||||
elem = elem.offsetParent;
|
||||
}
|
||||
}
|
||||
return location >= 0 ? location : 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* Sort content from first to last in the DOM
|
||||
* @param {Array} contents The content areas
|
||||
*/
|
||||
var sortContents = function (contents) {
|
||||
contents.sort((function (item1, item2) {
|
||||
var offset1 = getOffsetTop(item1.content);
|
||||
var offset2 = getOffsetTop(item2.content);
|
||||
if (offset1 < offset2) return -1;
|
||||
return 1;
|
||||
}));
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the offset to use for calculating position
|
||||
* @param {Object} settings The settings for this instantiation
|
||||
* @return {Float} The number of pixels to offset the calculations
|
||||
*/
|
||||
var getOffset = function (settings) {
|
||||
|
||||
// if the offset is a function run it
|
||||
if (typeof settings.offset === 'function') {
|
||||
return parseFloat(settings.offset());
|
||||
}
|
||||
|
||||
// Otherwise, return it as-is
|
||||
return parseFloat(settings.offset);
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the document element's height
|
||||
* @private
|
||||
* @returns {Number}
|
||||
*/
|
||||
var getDocumentHeight = function () {
|
||||
return Math.max(
|
||||
document.body.scrollHeight, document.documentElement.scrollHeight,
|
||||
document.body.offsetHeight, document.documentElement.offsetHeight,
|
||||
document.body.clientHeight, document.documentElement.clientHeight
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Determine if an element is in view
|
||||
* @param {Node} elem The element
|
||||
* @param {Object} settings The settings for this instantiation
|
||||
* @param {Boolean} bottom If true, check if element is above bottom of viewport instead
|
||||
* @return {Boolean} Returns true if element is in the viewport
|
||||
*/
|
||||
var isInView = function (elem, settings, bottom) {
|
||||
var bounds = elem.getBoundingClientRect();
|
||||
var offset = getOffset(settings);
|
||||
if (bottom) {
|
||||
return parseInt(bounds.bottom, 10) < (window.innerHeight || document.documentElement.clientHeight);
|
||||
}
|
||||
return parseInt(bounds.top, 10) <= offset;
|
||||
};
|
||||
|
||||
/**
|
||||
* Check if at the bottom of the viewport
|
||||
* @return {Boolean} If true, page is at the bottom of the viewport
|
||||
*/
|
||||
var isAtBottom = function () {
|
||||
if (window.innerHeight + window.pageYOffset >= getDocumentHeight()) return true;
|
||||
return false;
|
||||
};
|
||||
|
||||
/**
|
||||
* Check if the last item should be used (even if not at the top of the page)
|
||||
* @param {Object} item The last item
|
||||
* @param {Object} settings The settings for this instantiation
|
||||
* @return {Boolean} If true, use the last item
|
||||
*/
|
||||
var useLastItem = function (item, settings) {
|
||||
if (isAtBottom() && isInView(item.content, settings, true)) return true;
|
||||
return false;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the active content
|
||||
* @param {Array} contents The content areas
|
||||
* @param {Object} settings The settings for this instantiation
|
||||
* @return {Object} The content area and matching navigation link
|
||||
*/
|
||||
var getActive = function (contents, settings) {
|
||||
var last = contents[contents.length-1];
|
||||
if (useLastItem(last, settings)) return last;
|
||||
for (var i = contents.length - 1; i >= 0; i--) {
|
||||
if (isInView(contents[i].content, settings)) return contents[i];
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Deactivate parent navs in a nested navigation
|
||||
* @param {Node} nav The starting navigation element
|
||||
* @param {Object} settings The settings for this instantiation
|
||||
*/
|
||||
var deactivateNested = function (nav, settings) {
|
||||
|
||||
// If nesting isn't activated, bail
|
||||
if (!settings.nested) return;
|
||||
|
||||
// Get the parent navigation
|
||||
var li = nav.parentNode.closest('li');
|
||||
if (!li) return;
|
||||
|
||||
// Remove the active class
|
||||
li.classList.remove(settings.nestedClass);
|
||||
|
||||
// Apply recursively to any parent navigation elements
|
||||
deactivateNested(li, settings);
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* Deactivate a nav and content area
|
||||
* @param {Object} items The nav item and content to deactivate
|
||||
* @param {Object} settings The settings for this instantiation
|
||||
*/
|
||||
var deactivate = function (items, settings) {
|
||||
|
||||
// Make sure their are items to deactivate
|
||||
if (!items) return;
|
||||
|
||||
// Get the parent list item
|
||||
var li = items.nav.closest('li');
|
||||
if (!li) return;
|
||||
|
||||
// Remove the active class from the nav and content
|
||||
li.classList.remove(settings.navClass);
|
||||
items.content.classList.remove(settings.contentClass);
|
||||
|
||||
// Deactivate any parent navs in a nested navigation
|
||||
deactivateNested(li, settings);
|
||||
|
||||
// Emit a custom event
|
||||
emitEvent('gumshoeDeactivate', li, {
|
||||
link: items.nav,
|
||||
content: items.content,
|
||||
settings: settings
|
||||
});
|
||||
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Activate parent navs in a nested navigation
|
||||
* @param {Node} nav The starting navigation element
|
||||
* @param {Object} settings The settings for this instantiation
|
||||
*/
|
||||
var activateNested = function (nav, settings) {
|
||||
|
||||
// If nesting isn't activated, bail
|
||||
if (!settings.nested) return;
|
||||
|
||||
// Get the parent navigation
|
||||
var li = nav.parentNode.closest('li');
|
||||
if (!li) return;
|
||||
|
||||
// Add the active class
|
||||
li.classList.add(settings.nestedClass);
|
||||
|
||||
// Apply recursively to any parent navigation elements
|
||||
activateNested(li, settings);
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* Activate a nav and content area
|
||||
* @param {Object} items The nav item and content to activate
|
||||
* @param {Object} settings The settings for this instantiation
|
||||
*/
|
||||
var activate = function (items, settings) {
|
||||
|
||||
// Make sure their are items to activate
|
||||
if (!items) return;
|
||||
|
||||
// Get the parent list item
|
||||
var li = items.nav.closest('li');
|
||||
if (!li) return;
|
||||
|
||||
// Add the active class to the nav and content
|
||||
li.classList.add(settings.navClass);
|
||||
items.content.classList.add(settings.contentClass);
|
||||
|
||||
// Activate any parent navs in a nested navigation
|
||||
activateNested(li, settings);
|
||||
|
||||
// Emit a custom event
|
||||
emitEvent('gumshoeActivate', li, {
|
||||
link: items.nav,
|
||||
content: items.content,
|
||||
settings: settings
|
||||
});
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* Create the Constructor object
|
||||
* @param {String} selector The selector to use for navigation items
|
||||
* @param {Object} options User options and settings
|
||||
*/
|
||||
var Constructor = function (selector, options) {
|
||||
|
||||
//
|
||||
// Variables
|
||||
//
|
||||
|
||||
var publicAPIs = {};
|
||||
var navItems, contents, current, timeout, settings;
|
||||
|
||||
|
||||
//
|
||||
// Methods
|
||||
//
|
||||
|
||||
/**
|
||||
* Set variables from DOM elements
|
||||
*/
|
||||
publicAPIs.setup = function () {
|
||||
|
||||
// Get all nav items
|
||||
navItems = document.querySelectorAll(selector);
|
||||
|
||||
// Create contents array
|
||||
contents = [];
|
||||
|
||||
// Loop through each item, get it's matching content, and push to the array
|
||||
Array.prototype.forEach.call(navItems, (function (item) {
|
||||
|
||||
// Get the content for the nav item
|
||||
var content = document.getElementById(decodeURIComponent(item.hash.substr(1)));
|
||||
if (!content) return;
|
||||
|
||||
// Push to the contents array
|
||||
contents.push({
|
||||
nav: item,
|
||||
content: content
|
||||
});
|
||||
|
||||
}));
|
||||
|
||||
// Sort contents by the order they appear in the DOM
|
||||
sortContents(contents);
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* Detect which content is currently active
|
||||
*/
|
||||
publicAPIs.detect = function () {
|
||||
|
||||
// Get the active content
|
||||
var active = getActive(contents, settings);
|
||||
|
||||
// if there's no active content, deactivate and bail
|
||||
if (!active) {
|
||||
if (current) {
|
||||
deactivate(current, settings);
|
||||
current = null;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// If the active content is the one currently active, do nothing
|
||||
if (current && active.content === current.content) return;
|
||||
|
||||
// Deactivate the current content and activate the new content
|
||||
deactivate(current, settings);
|
||||
activate(active, settings);
|
||||
|
||||
// Update the currently active content
|
||||
current = active;
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* Detect the active content on scroll
|
||||
* Debounced for performance
|
||||
*/
|
||||
var scrollHandler = function (event) {
|
||||
|
||||
// If there's a timer, cancel it
|
||||
if (timeout) {
|
||||
window.cancelAnimationFrame(timeout);
|
||||
}
|
||||
|
||||
// Setup debounce callback
|
||||
timeout = window.requestAnimationFrame(publicAPIs.detect);
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* Update content sorting on resize
|
||||
* Debounced for performance
|
||||
*/
|
||||
var resizeHandler = function (event) {
|
||||
|
||||
// If there's a timer, cancel it
|
||||
if (timeout) {
|
||||
window.cancelAnimationFrame(timeout);
|
||||
}
|
||||
|
||||
// Setup debounce callback
|
||||
timeout = window.requestAnimationFrame((function () {
|
||||
sortContents();
|
||||
publicAPIs.detect();
|
||||
}));
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* Destroy the current instantiation
|
||||
*/
|
||||
publicAPIs.destroy = function () {
|
||||
|
||||
// Undo DOM changes
|
||||
if (current) {
|
||||
deactivate(current);
|
||||
}
|
||||
|
||||
// Remove event listeners
|
||||
window.removeEventListener('scroll', scrollHandler, false);
|
||||
if (settings.reflow) {
|
||||
window.removeEventListener('resize', resizeHandler, false);
|
||||
}
|
||||
|
||||
// Reset variables
|
||||
contents = null;
|
||||
navItems = null;
|
||||
current = null;
|
||||
timeout = null;
|
||||
settings = null;
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* Initialize the current instantiation
|
||||
*/
|
||||
var init = function () {
|
||||
|
||||
// Merge user options into defaults
|
||||
settings = extend(defaults, options || {});
|
||||
|
||||
// Setup variables based on the current DOM
|
||||
publicAPIs.setup();
|
||||
|
||||
// Find the currently active content
|
||||
publicAPIs.detect();
|
||||
|
||||
// Setup event listeners
|
||||
window.addEventListener('scroll', scrollHandler, false);
|
||||
if (settings.reflow) {
|
||||
window.addEventListener('resize', resizeHandler, false);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
||||
//
|
||||
// Initialize and return the public APIs
|
||||
//
|
||||
|
||||
init();
|
||||
return publicAPIs;
|
||||
|
||||
};
|
||||
|
||||
|
||||
//
|
||||
// Return the Constructor
|
||||
//
|
||||
|
||||
return Constructor;
|
||||
|
||||
}));
|
Loading…
Add table
Add a link
Reference in a new issue