// Eryn Wells class Carousel { carousel = null; #items = null; #caption = null; constructor(element) { this.carousel = element; this.#caption = element.querySelector("figcaption"); } get items() { if (this.#items === null) { const boundingRect = this.carousel.getBoundingClientRect(); this.#items = Array.from(this.carousel.querySelectorAll("ul > li")).map(item => { let itemRect = item.getClientRects()[0]; let relativeX = itemRect.x - boundingRect.x; return new CarouselItem(item, relativeX); }); } return this.#items; } get scrollContainer() { return this.carousel.querySelector("ul"); } setCaption(caption) { if (!this.#caption) { return; } this.#caption.innerHTML = caption; } scrollTo(index) { this.scrollContainer.scrollTo(this.items[index].relativeX, 0); } setUpScrollEventListener(photoParametersTable) { let previousScrollLeft = null; let isScrollingLeft = true; this.scrollContainer.addEventListener("scroll", event => { const target = event.target; const scrollLeft = target.scrollLeft; const carouselRect = target.getBoundingClientRect(); const carouselRectHorizontalCenter = carouselRect.width / 2.0; if (previousScrollLeft !== null) { isScrollingLeft = scrollLeft > previousScrollLeft; } previousScrollLeft = scrollLeft; this.items.forEach(item => { const itemRelativeXCoordinate = isScrollingLeft ? item.relativeX - scrollLeft : item.relativeMaxX - scrollLeft; const itemWasLeftOfContainerCenter = item.isLeftOfContainerCenter; const itemIsLeftOfContainerCenter = itemRelativeXCoordinate < carouselRectHorizontalCenter; item.isLeftOfContainerCenter = itemIsLeftOfContainerCenter; if ( (isScrollingLeft && (!itemWasLeftOfContainerCenter && itemIsLeftOfContainerCenter)) || (!isScrollingLeft && (itemWasLeftOfContainerCenter && !itemIsLeftOfContainerCenter))) { this.setCaption(item.title); photoParametersTable.setMakeModel(item.make, item.model); photoParametersTable.setLocation(item.latitude, item.longitude); photoParametersTable.setMegapixels(item.megapixels); photoParametersTable.setSize(item.width, item.height); photoParametersTable.setISO(item.iso); photoParametersTable.setFocalLength(item.focalLength); photoParametersTable.setFNumber(item.fNumber); photoParametersTable.setExposureTime(item.exposureTime); } }); }); } } class CarouselItem { element = null; relativeX = 0; isLeftOfContainerCenter = false; constructor(element, relativeX) { this.element = element; this.relativeX = relativeX } get relativeMaxX() { const rect = this.element.getBoundingClientRect(); return this.relativeX + rect.width; } get title() { return this.element.dataset.title; } get latitude() { return this.element.dataset.latitude; } get longitude() { return this.element.dataset.longitude; } get megapixels() { return this.element.dataset.megapixels; } get width() { return this.element.dataset.width; } get height() { return this.element.dataset.height; } get make() { return this.element.dataset.make; } get model() { return this.element.dataset.model; } get iso() { return this.element.dataset.iso; } get focalLength() { return this.element.dataset.focalLength; } get fNumber() { return this.element.dataset.fNumber; } get exposureTime() { return this.element.dataset.exposureTime; } } class PhotoParametersTable { tableElement; #makeModelElement; #latitudeElement; #longitudeElement; #megapixelsElement; #widthElement; #heightElement; #isoElement; #focalLengthElement; #fNumberElement; #exposureTimeElement; constructor(element) { this.tableElement = element; } setMakeModel(make, model) { if (!this.#makeModelElement) { this.#makeModelElement = this.#getElementWithQuerySelector("td.make-model"); } if (!this.#makeModelElement) { return; } let makeModel = ""; if (make && model) { return `${make} ${model}`; } else if (make) { return make; } else if (model) { return model; } else { return ""; } this.#makeModelElement.innerHTML = makeModel; } setLocation(latitude, longitude) { let latitudeElement = this.#latitudeElement; if (!latitudeElement) { latitudeElement = this.#getElementWithQuerySelector("td.location > data.latitude"); this.#latitudeElement = latitudeElement; } let longitudeElement = this.#longitudeElement; if (!longitudeElement) { longitudeElement = this.#getElementWithQuerySelector("td.location > data.longitude"); this.#longitudeElement = longitudeElement; } if (!latitudeElement || !longitudeElement) { return; } latitudeElement.innerHTML = latitude; longitudeElement.innerHTML = longitude; } setMegapixels(megapixels) { let megapixelsElement = this.#megapixelsElement; if (!megapixelsElement) { megapixelsElement = this.#getElementWithQuerySelector("td.size > data.megapixels"); this.#megapixelsElement = megapixelsElement; } if (!megapixelsElement) { return; } megapixelsElement.innerHTML = megapixels; } setSize(width, height) { let widthElement = this.#widthElement; if (!widthElement) { widthElement = this.#getElementWithQuerySelector("td.size > data.width"); this.#widthElement = widthElement; } let heightElement = this.#heightElement; if (heightElement) { heightElement = this.#getElementWithQuerySelector("td.size > data.height"); this.#heightElement = heightElement; } if (!widthElement || !heightElement) { return; } widthElement.innerHTML = width; heightElement.innerHTML = height; } setISO(iso) { let isoElement = this.#isoElement; if (!isoElement) { isoElement = this.#getElementWithQuerySelector("td.iso"); this.#isoElement = isoElement; } if (!isoElement) { return; } isoElement.innerHTML = `ISO ${iso}`; } setFocalLength(focalLength) { let focalLengthElement = this.#focalLengthElement; if (!focalLengthElement) { focalLengthElement = this.#getElementWithQuerySelector("td.focal-length"); this.#focalLengthElement = focalLengthElement; } if (!focalLengthElement) { return; } focalLengthElement.innerHTML = `${focalLength} mm`; } setFNumber(f) { let fNumberElement = this.#fNumberElement; if (!fNumberElement) { fNumberElement = this.#getElementWithQuerySelector("td.f-number"); this.#fNumberElement = fNumberElement; } if (!fNumberElement) { return; } fNumberElement.innerHTML = `ƒ${f}`; } setExposureTime(exposureTime) { let exposureTimeElement = this.#exposureTimeElement; if (!exposureTimeElement) { exposureTimeElement = this.#getElementWithQuerySelector("td.exposure-time"); this.#exposureTimeElement = exposureTimeElement; } if (!exposureTimeElement) { return; } exposureTimeElement.innerHTML = `${exposureTime} s`; } #getElementWithQuerySelector(query) { const element = this.tableElement.querySelector(query); if (!element) { return null; } return element; } } document.addEventListener("DOMContentLoaded", (event) => { const carouselElements = document.querySelectorAll("figure.carousel"); if (carouselElements.length === 0) { return; } const allCarousels = Array.from(carouselElements).sort((a, b) => { const aRect = a.getBoundingClientRect(); const bRect = a.getBoundingClientRect(); return aRect.top - bRect.top; }).map(elem => new Carousel(elem)); let photoParametersTable = null; const photoParamsTableElement = document.querySelector(".photo-params table"); if (photoParamsTableElement) { photoParametersTable = new PhotoParametersTable(photoParamsTableElement); } allCarousels.forEach(carousel => { carousel.setUpScrollEventListener(photoParametersTable); }); const url = new URL(window.location); let photoGetParameter = url.searchParams.get("photo"); if (photoGetParameter) { try { photoGetParameter = parseInt(photoGetParameter); allCarousels[0].scrollTo(photoGetParameter); } catch (e) { console.error("Unable to parse 'photo' GET parameter as integer:", e); } } });