@fancyapps/ui
Version:
JavaScript UI Component Library
253 lines (204 loc) • 6.18 kB
JavaScript
import { canUseDOM } from "../../../shared/utils/canUseDOM.js";
export class Hash {
constructor(fancybox) {
this.fancybox = fancybox;
for (const methodName of ["onChange", "onClosing"]) {
this[methodName] = this[methodName].bind(this);
}
this.events = {
initCarousel: this.onChange,
"Carousel.change": this.onChange,
closing: this.onClosing,
};
this.hasCreatedHistory = false;
this.origHash = "";
this.timer = null;
}
/**
* Process `Carousel.ready` and `Carousel.change` events to update URL hash
* @param {Object} fancybox
* @param {Object} carousel
*/
onChange(fancybox) {
const carousel = fancybox.Carousel;
if (this.timer) {
clearTimeout(this.timer);
}
const firstRun = carousel.prevPage === null;
const currentSlide = fancybox.getSlide();
const currentHash = new URL(document.URL).hash;
let newHash = false;
if (currentSlide.slug) {
newHash = "#" + currentSlide.slug;
} else {
const dataset = currentSlide.$trigger && currentSlide.$trigger.dataset;
const slug = fancybox.option("slug") || (dataset && dataset.fancybox);
if (slug && slug.length && slug !== "true") {
newHash = "#" + slug + (carousel.slides.length > 1 ? "-" + (currentSlide.index + 1) : "");
}
}
if (firstRun) {
this.origHash = currentHash !== newHash ? currentHash : "";
}
if (newHash && currentHash !== newHash) {
this.timer = setTimeout(() => {
try {
window.history[firstRun ? "pushState" : "replaceState"](
{},
document.title,
window.location.pathname + window.location.search + newHash
);
if (firstRun) {
this.hasCreatedHistory = true;
}
} catch (e) {}
}, 300);
}
}
/**
* Process `closing` event to clean up
*/
onClosing() {
if (this.timer) {
clearTimeout(this.timer);
}
// Skip if closing is triggered by pressing browser back button or by changing hash manually
if (this.hasSilentClose === true) {
return;
}
// Restore original url
try {
window.history.replaceState(
{},
document.title,
window.location.pathname + window.location.search + (this.origHash || "")
);
return;
} catch (e) {}
}
attach(fancybox) {
fancybox.on(this.events);
}
detach(fancybox) {
fancybox.off(this.events);
}
/**
* Start fancybox from current URL hash,
* this will be called on page load OR/AND after changing URL hash
* @param {Class} Fancybox
*/
static startFromUrl() {
const Fancybox = Hash.Fancybox;
if (!Fancybox || Fancybox.getInstance() || Fancybox.defaults.Hash === false) {
return;
}
const { hash, slug, index } = Hash.getParsedURL();
if (!slug) {
return;
}
// Support custom slug
// ===
let selectedElem = document.querySelector(`[data-slug="${hash}"]`);
if (selectedElem) {
selectedElem.dispatchEvent(new CustomEvent("click", { bubbles: true, cancelable: true }));
}
if (Fancybox.getInstance()) {
return;
}
// If elements are not found by custom slug, use URL hash value as group name
// ===
const groupElems = document.querySelectorAll(`[data-fancybox="${slug}"]`);
if (!groupElems.length) {
return;
}
if (index === null && groupElems.length === 1) {
selectedElem = groupElems[0];
} else if (index) {
selectedElem = groupElems[index - 1];
}
if (selectedElem) {
selectedElem.dispatchEvent(new CustomEvent("click", { bubbles: true, cancelable: true }));
}
}
/**
* Handle `hash` change, change gallery item to current index or start/close current instance
*/
static onHashChange() {
const { slug, index } = Hash.getParsedURL();
const Fancybox = Hash.Fancybox;
const instance = Fancybox && Fancybox.getInstance();
if (instance && instance.plugins.Hash) {
// Check if hash matches currently active gallery
if (slug) {
const carousel = instance.Carousel;
/**
* Support manually opened gallery
*/
if (slug === instance.option("slug")) {
return carousel.slideTo(index - 1);
}
/**
* Check if URL hash matches `data-slug` value of active element
*/
for (let slide of carousel.slides) {
if (slide.slug && slide.slug === slug) {
return carousel.slideTo(slide.index);
}
}
/**
* Check if URL hash matches `data-fancybox` value of active element
*/
const slide = instance.getSlide();
const dataset = slide.$trigger && slide.$trigger.dataset;
if (dataset && dataset.fancybox === slug) {
return carousel.slideTo(index - 1);
}
}
/**
* Close if no matching element found
*/
instance.plugins.Hash.hasSilentClose = true;
instance.close();
}
/**
* Attempt to start
*/
Hash.startFromUrl();
}
/**
* Add event bindings that will start new Fancybox instance based in the current URL
*/
static create(Fancybox) {
Hash.Fancybox = Fancybox;
function proceed() {
window.addEventListener("hashchange", Hash.onHashChange, false);
Hash.startFromUrl();
}
if (canUseDOM) {
window.requestAnimationFrame(() => {
if (/complete|interactive|loaded/.test(document.readyState)) {
proceed();
} else {
document.addEventListener("DOMContentLoaded", proceed);
}
});
}
}
static destroy() {
window.removeEventListener("hashchange", Hash.onHashChange, false);
}
/**
* Helper method to split URL hash into useful pieces
*/
static getParsedURL() {
const hash = window.location.hash.substr(1),
tmp = hash.split("-"),
index = tmp.length > 1 && /^\+?\d+$/.test(tmp[tmp.length - 1]) ? parseInt(tmp.pop(-1), 10) || null : null,
slug = tmp.join("-");
return {
hash,
slug,
index,
};
}
}