initial commit

This commit is contained in:
2024-11-13 22:26:45 +01:00
commit fd466fc511
156 changed files with 13223 additions and 0 deletions

View File

@@ -0,0 +1,43 @@
:root {
color-scheme: light dark;
--surface-1-light: #fff;
--surface-2-light: #e5e5e5;
--text-1-light: #0a0a0a;
--text-2-light: #737373;
--surface-1-dark: #171717;
--surface-2-dark: #404040;
--text-1-dark: #fafafa;
--text-2-dark: #a3a3a3;
--surface-1: var(--surface-1-light);
--surface-2: var(--surface-2-light);
--text-1: var(--text-1-light);
--text-2: var(--text-2-light);
}
@media (prefers-color-scheme: dark) {
:root {
--surface-1: var(--surface-1-dark);
--surface-2: var(--surface-2-dark);
--text-1: var(--text-1-dark);
--text-2: var(--text-2-dark);
}
}
html.light {
color-scheme: light;
--surface-1: var(--surface-1-light);
--surface-2: var(--surface-2-light);
--text-1: var(--text-1-light);
--text-2: var(--text-2-light);
}
html.dark {
color-scheme: dark;
--surface-1: var(--surface-1-dark);
--surface-2: var(--surface-2-dark);
--text-1: var(--text-1-dark);
--text-2: var(--text-2-dark);
}

View File

@@ -0,0 +1,99 @@
*,
::before,
::after {
box-sizing: border-box;
border-width: 0;
}
html {
line-height: 1.5;
font-family: ui-sans-serif, system-ui, sans-serif;
-webkit-text-size-adjust: 100%;
-moz-tab-size: 4;
tab-size: 4;
}
body {
margin: 0;
}
h1,
h2,
h3,
h4,
h5,
h6 {
font-weight: inherit;
font-size: inherit;
}
a {
color: inherit;
text-decoration: inherit;
}
b,
strong {
font-weight: bolder;
}
button,
input {
font-family: inherit;
font-feature-settings: inherit;
font-variation-settings: inherit;
font-size: 100%;
font-weight: inherit;
line-height: inherit;
color: inherit;
margin: 0;
padding: 0;
}
button,
select {
text-transform: none;
}
button {
-webkit-appearance: button;
background-image: none;
background-color: transparent;
cursor: pointer;
}
:disabled {
cursor: default;
}
:-moz-focusring {
outline: 1px dotted ButtonText;
}
h1,
h2,
h3,
figure,
p {
margin: 0;
}
ul,
menu {
margin: 0;
padding: 0;
list-style: none;
}
img,
svg,
video {
display: block;
vertical-align: middle;
}
img,
video {
max-width: 100%;
height: auto;
}

View File

@@ -0,0 +1,428 @@
html {
height: 100%;
}
body {
display: flex;
flex-direction: column;
width: 100%;
min-height: 100%;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
background-color: var(--surface-1);
color: var(--text-1);
}
body > header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 0.5rem;
width: 100%;
min-height: 4rem;
ul {
display: inline-flex;
}
.btn {
display: inline-flex;
justify-content: center;
align-items: center;
border-radius: 0.5rem;
padding-right: 1rem;
padding-left: 1rem;
height: 3rem;
font-weight: 600;
font-size: 1.25rem;
user-select: none;
}
.btn-square {
padding: 0;
width: 3rem;
}
}
body > menu {
margin: 3rem auto 4rem;
padding-right: 1.5rem;
padding-left: 1.5rem;
width: 100%;
max-width: 768px;
color: var(--text-2);
font-weight: 600;
font-size: 1.125rem;
line-height: 1.75rem;
user-select: none;
text-align: center;
a {
display: block;
padding-top: 0.5rem;
padding-bottom: 0.5rem;
&:hover,
&[aria-current="true"] {
color: var(--text-1);
}
}
}
body > main {
flex-grow: 1;
width: 100%;
height: 100%;
}
main > section {
margin: 3rem auto 4rem;
padding-right: 1.5rem;
padding-left: 1.5rem;
}
body > footer {
margin-right: auto;
margin-left: auto;
color: var(--text-2);
font-size: 0.875rem;
line-height: 1.25rem;
section:last-of-type {
display: flex;
flex-flow: row wrap;
justify-content: center;
gap: 1rem;
padding: 2.5rem;
a:hover {
text-decoration-line: underline;
}
}
}
hgroup {
display: flex;
flex-direction: column;
align-items: center;
gap: 1.5rem;
margin: 3rem auto 4rem;
padding-right: 1.5rem;
padding-left: 1.5rem;
max-width: 1024px;
text-align: center;
h1 {
font-weight: 700;
font-size: 1.875rem;
line-height: 2.25rem;
}
h2 {
font-weight: 700;
font-size: 1.5rem;
line-height: 2rem;
}
p {
color: var(--text-2);
text-wrap: balance;
}
@media (min-width: 640px) {
width: 83.3333%;
}
@media (min-width: 768px) {
h1 {
font-size: 2.25rem;
line-height: 2.5rem;
}
h2 {
font-size: 1.875rem;
line-height: 2.25rem;
}
p {
font-size: 1.125rem;
line-height: 1.75rem;
}
}
}
section.galleries {
display: grid;
grid-template-columns: repeat(1, minmax(0, 1fr));
gap: 2rem 1.5rem;
max-width: 1280px;
@media (min-width: 640px) {
grid-template-columns: repeat(2, minmax(0, 1fr));
row-gap: 3rem;
}
@media (min-width: 1024px) {
grid-template-columns: repeat(3, minmax(0, 1fr));
}
}
section.gallery {
padding-right: 0;
padding-left: 0;
max-width: 1536px;
@media (min-width: 640px) {
padding-right: 1.5rem /* 24px */;
padding-left: 1.5rem /* 24px */;
}
}
.prose {
max-width: 768px;
color: var(--text-1);
font-size: 1rem;
line-height: 1.75;
a {
color: var(--text-1);
font-weight: 500;
text-decoration: underline;
}
p {
margin-top: 1.25em;
margin-bottom: 1.25em;
}
img {
margin-top: 2em;
margin-bottom: 2em;
}
h3 {
margin-top: 1.6em;
margin-bottom: 0.6em;
color: var(--text-1);
font-weight: 600;
font-size: 1.25em;
line-height: 1.6;
}
ul {
margin-top: 1.25em;
margin-bottom: 1.25em;
padding-left: 1.625em;
list-style-type: disc;
& > li {
margin-top: 0.5em;
margin-bottom: 0.5em;
padding-left: 0.375em;
}
li::marker {
color: var(--text-2);
font-variant-numeric: tabular-nums;
unicode-bidi: isolate;
text-align: start !important;
text-align-last: start !important;
text-indent: 0 !important;
text-transform: none;
}
}
h3 + * {
margin-top: 0;
}
figure {
margin-top: 2em;
margin-bottom: 2em;
}
figure > * {
margin-top: 0;
margin-bottom: 0;
}
> :first-child {
margin-top: 0;
}
> :last-child {
margin-bottom: 0;
}
}
.card {
display: flex;
flex-direction: column;
border-radius: 1rem;
& > figure {
aspect-ratio: 3/2;
width: 100%;
border-radius: 1rem;
}
& > img, & figure > img {
transition-duration: 150ms;
transition-property: all;
transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
box-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.05);
border-radius: 1rem;
aspect-ratio: 3/2;
width: 100%;
overflow: hidden;
object-fit: cover;
&:hover {
box-shadow:
0 10px 15px -3px rgba(0, 0, 0, 0.1),
0 4px 6px -4px rgba(0, 0, 0, 0.1);
}
}
& > div {
display: flex;
flex: 1 1 auto;
flex-direction: column;
gap: 0.75rem;
padding: 1rem;
& > h2 {
font-weight: 600;
font-size: 1.125rem;
line-height: 1.375;
}
& > p {
color: var(--text-2);
font-size: 0.875rem;
line-height: 1.25rem;
}
}
}
.gallery-item {
cursor: zoom-in;
}
.hidden {
display: none;
}
.group[aria-expanded="true"] {
.group-aria-expanded\:block {
display: block;
}
.group-aria-expanded\:hidden {
display: none;
}
}
section.social-icons {
display: flex;
flex-wrap: wrap;
gap: 1.5rem;
justify-content: center;
margin-top: 2rem;
padding-right: 1.5rem;
padding-left: 1.5rem;
}
section.featured {
margin: 3rem auto 4rem;
padding-right: 1.5rem;
padding-left: 1.5rem;
max-width: 1280px;
color: var(--text-1-dark);
}
.featured-card {
display: flex;
box-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.05);
border-radius: 1rem;
background-position: center;
background-size: cover;
aspect-ratio: 1 / 1;
width: 100%;
overflow: hidden;
transition-duration: 150ms;
transition-property: all;
transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
@media (min-width: 640px) {
aspect-ratio: 16/9;
}
& > div {
display: flex;
flex-direction: column;
justify-content: flex-end;
gap: 1rem;
background-image: linear-gradient(to top, RGB(0 0 0 / 0.8) 10%, transparent 50%);
padding: 1.5rem;
width: 100%;
height: 100%;
& > h2 {
font-weight: 700;
font-size: 1.5rem;
line-height: 1.25;
@media (min-width: 768px) {
font-size: 1.875rem;
line-height: 2.25rem;
}
}
& > p {
font-size: 0.875rem;
line-height: 1.25rem;
}
}
&:hover {
box-shadow:
0 10px 15px -3px rgba(0, 0, 0, 0.1),
0 4px 6px -4px rgba(0, 0, 0, 0.1);
}
}
nav.categories {
padding-right: 1.5rem;
padding-left: 1.5rem;
margin-top: 2rem;
& > ul {
display: flex;
flex-direction: row;
justify-content: center;
gap: 0.75rem;
flex-wrap: wrap;
li {
max-width: 100%;
& > a {
display: block;
padding: 0.5rem 0.75rem;
border-radius: 9999px;
border: 1px solid var(--text-2);
font-size: 0.875rem;
line-height: 1.25rem;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
@media (min-width: 640px) {
padding: 0.75rem 1rem;
font-size: 1rem;
}
}
}
}
}

View File

@@ -0,0 +1 @@
/* custom.css */

View File

@@ -0,0 +1,20 @@
@import "normalize";
@import "colors";
@import "styles";
@import "photoswipe/photoswipe";
@import "photoswipe/photoswipe-dynamic-caption-plugin";
@import "custom";
.lazyload,
.lazyloading {
opacity: 0;
}
.lazyloaded {
opacity: 1;
transition: opacity 300ms;
}
img.lazyload:not([src]) {
visibility: hidden;
}

View File

@@ -0,0 +1,52 @@
.pswp__dynamic-caption {
color: #fff;
position: absolute;
width: 100%;
left: 0;
top: 0;
transition: opacity 120ms linear !important; /* override default */
}
.pswp-caption-content {
display: none;
}
.pswp__dynamic-caption a {
color: #fff;
}
.pswp__dynamic-caption--faded {
opacity: 0 !important;
}
.pswp__dynamic-caption--aside {
width: auto;
max-width: 300px;
padding: 20px 15px 20px 20px;
margin-top: 70px;
}
.pswp__dynamic-caption--below {
width: auto;
max-width: 700px;
padding: 15px 0 0;
}
.pswp__dynamic-caption--on-hor-edge {
padding-left: 15px;
padding-right: 15px;
}
.pswp__dynamic-caption--mobile {
width: 100%;
background: rgba(0,0,0,0.5);
padding: 10px 15px;
right: 0;
bottom: 0;
/* override styles that were set via JS.
as they interfere with size measurement */
top: auto !important;
left: 0 !important;
}

View File

@@ -0,0 +1,420 @@
/*! PhotoSwipe main CSS by Dmytro Semenov | photoswipe.com */
.pswp {
--pswp-bg: #000;
--pswp-placeholder-bg: #222;
--pswp-root-z-index: 100000;
--pswp-preloader-color: rgba(79, 79, 79, 0.4);
--pswp-preloader-color-secondary: rgba(255, 255, 255, 0.9);
/* defined via js:
--pswp-transition-duration: 333ms; */
--pswp-icon-color: #fff;
--pswp-icon-color-secondary: #4f4f4f;
--pswp-icon-stroke-color: #4f4f4f;
--pswp-icon-stroke-width: 2px;
--pswp-error-text-color: var(--pswp-icon-color);
}
/*
Styles for basic PhotoSwipe (pswp) functionality (sliding area, open/close transitions)
*/
.pswp {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: var(--pswp-root-z-index);
display: none;
touch-action: none;
outline: 0;
opacity: 0.003;
contain: layout style size;
-webkit-tap-highlight-color: rgba(0, 0, 0, 0);
}
/* Prevents focus outline on the root element,
(it may be focused initially) */
.pswp:focus {
outline: 0;
}
.pswp * {
box-sizing: border-box;
}
.pswp img {
max-width: none;
}
.pswp--open {
display: block;
}
.pswp,
.pswp__bg {
transform: translateZ(0);
will-change: opacity;
}
.pswp__bg {
opacity: 0.005;
background: var(--pswp-bg);
}
.pswp,
.pswp__scroll-wrap {
overflow: hidden;
}
.pswp__scroll-wrap,
.pswp__bg,
.pswp__container,
.pswp__item,
.pswp__content,
.pswp__img,
.pswp__zoom-wrap {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
}
.pswp__img,
.pswp__zoom-wrap {
width: auto;
height: auto;
}
.pswp--click-to-zoom.pswp--zoom-allowed .pswp__img {
cursor: -webkit-zoom-in;
cursor: -moz-zoom-in;
cursor: zoom-in;
}
.pswp--click-to-zoom.pswp--zoomed-in .pswp__img {
cursor: move;
cursor: -webkit-grab;
cursor: -moz-grab;
cursor: grab;
}
.pswp--click-to-zoom.pswp--zoomed-in .pswp__img:active {
cursor: -webkit-grabbing;
cursor: -moz-grabbing;
cursor: grabbing;
}
/* :active to override grabbing cursor */
.pswp--no-mouse-drag.pswp--zoomed-in .pswp__img,
.pswp--no-mouse-drag.pswp--zoomed-in .pswp__img:active,
.pswp__img {
cursor: -webkit-zoom-out;
cursor: -moz-zoom-out;
cursor: zoom-out;
}
/* Prevent selection and tap highlights */
.pswp__container,
.pswp__img,
.pswp__button,
.pswp__counter {
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
}
.pswp__item {
/* z-index for fade transition */
z-index: 1;
overflow: hidden;
}
.pswp__hidden {
display: none !important;
}
/* Allow to click through pswp__content element, but not its children */
.pswp__content {
pointer-events: none;
}
.pswp__content > * {
pointer-events: auto;
}
/*
PhotoSwipe UI
*/
/*
Error message appears when image is not loaded
(JS option errorMsg controls markup)
*/
.pswp__error-msg-container {
display: grid;
}
.pswp__error-msg {
margin: auto;
font-size: 1em;
line-height: 1;
color: var(--pswp-error-text-color);
}
/*
class pswp__hide-on-close is applied to elements that
should hide (for example fade out) when PhotoSwipe is closed
and show (for example fade in) when PhotoSwipe is opened
*/
.pswp .pswp__hide-on-close {
opacity: 0.005;
will-change: opacity;
transition: opacity var(--pswp-transition-duration) cubic-bezier(0.4, 0, 0.22, 1);
z-index: 10; /* always overlap slide content */
pointer-events: none; /* hidden elements should not be clickable */
}
/* class pswp--ui-visible is added when opening or closing transition starts */
.pswp--ui-visible .pswp__hide-on-close {
opacity: 1;
pointer-events: auto;
}
/* <button> styles, including css reset */
.pswp__button {
position: relative;
display: block;
width: 50px;
height: 60px;
padding: 0;
margin: 0;
overflow: hidden;
cursor: pointer;
background: none;
border: 0;
box-shadow: none;
opacity: 0.85;
-webkit-appearance: none;
-webkit-touch-callout: none;
}
.pswp__button:hover,
.pswp__button:active,
.pswp__button:focus {
transition: none;
padding: 0;
background: none;
border: 0;
box-shadow: none;
opacity: 1;
}
.pswp__button:disabled {
opacity: 0.3;
cursor: auto;
}
.pswp__icn {
fill: var(--pswp-icon-color);
color: var(--pswp-icon-color-secondary);
}
.pswp__icn {
position: absolute;
top: 14px;
left: 9px;
width: 32px;
height: 32px;
overflow: hidden;
pointer-events: none;
}
.pswp__icn-shadow {
stroke: var(--pswp-icon-stroke-color);
stroke-width: var(--pswp-icon-stroke-width);
fill: none;
}
.pswp__icn:focus {
outline: 0;
}
/*
div element that matches size of large image,
large image loads on top of it,
used when msrc is not provided
*/
div.pswp__img--placeholder,
.pswp__img--with-bg {
background: var(--pswp-placeholder-bg);
}
.pswp__top-bar {
position: absolute;
left: 0;
top: 0;
width: 100%;
height: 60px;
display: flex;
flex-direction: row;
justify-content: flex-end;
z-index: 10;
/* allow events to pass through top bar itself */
pointer-events: none !important;
}
.pswp__top-bar > * {
pointer-events: auto;
/* this makes transition significantly more smooth,
even though inner elements are not animated */
will-change: opacity;
}
/*
Close button
*/
.pswp__button--close {
margin-right: 6px;
}
/*
Arrow buttons
*/
.pswp__button--arrow {
position: absolute;
top: 0;
width: 75px;
height: 100px;
top: 50%;
margin-top: -50px;
}
.pswp__button--arrow:disabled {
display: none;
cursor: default;
}
.pswp__button--arrow .pswp__icn {
top: 50%;
margin-top: -30px;
width: 60px;
height: 60px;
background: none;
border-radius: 0;
}
.pswp--one-slide .pswp__button--arrow {
display: none;
}
/* hide arrows on touch screens */
.pswp--touch .pswp__button--arrow {
visibility: hidden;
}
/* show arrows only after mouse was used */
.pswp--has_mouse .pswp__button--arrow {
visibility: visible;
}
.pswp__button--arrow--prev {
right: auto;
left: 0px;
}
.pswp__button--arrow--next {
right: 0px;
}
.pswp__button--arrow--next .pswp__icn {
left: auto;
right: 14px;
/* flip horizontally */
transform: scale(-1, 1);
}
/*
Zoom button
*/
.pswp__button--zoom {
display: none;
}
.pswp--zoom-allowed .pswp__button--zoom {
display: block;
}
/* "+" => "-" */
.pswp--zoomed-in .pswp__zoom-icn-bar-v {
display: none;
}
/*
Loading indicator
*/
.pswp__preloader {
position: relative;
overflow: hidden;
width: 50px;
height: 60px;
margin-right: auto;
}
.pswp__preloader .pswp__icn {
opacity: 0;
transition: opacity 0.2s linear;
animation: pswp-clockwise 600ms linear infinite;
}
.pswp__preloader--active .pswp__icn {
opacity: 0.85;
}
@keyframes pswp-clockwise {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
/*
"1 of 10" counter
*/
.pswp__counter {
height: 30px;
margin-top: 15px;
margin-inline-start: 20px;
font-size: 14px;
line-height: 30px;
color: var(--pswp-icon-color);
text-shadow: 1px 1px 3px var(--pswp-icon-color-secondary);
opacity: 0.85;
}
.pswp--one-slide .pswp__counter {
display: none;
}

View File

@@ -0,0 +1 @@
/* custom.js */

View File

@@ -0,0 +1,52 @@
import justifiedLayout from "./justified-layout/index.js";
import * as params from "@params";
const gallery = document.getElementById("gallery");
if (gallery) {
let containerWidth = 0;
const items = gallery.querySelectorAll(".gallery-item");
const input = Array.from(items).map((item) => {
const img = item.querySelector("img");
img.style.width = "100%";
img.style.height = "auto";
return {
width: parseFloat(img.getAttribute("width")),
height: parseFloat(img.getAttribute("height")),
};
});
function updateGallery() {
if (containerWidth === gallery.getBoundingClientRect().width) return;
containerWidth = gallery.getBoundingClientRect().width;
const geometry = justifiedLayout(input, {
containerWidth,
containerPadding: 0,
boxSpacing: Number.isInteger(params.boxSpacing) ? params.boxSpacing : 10,
targetRowHeight: params.targetRowHeight || 288,
targetRowHeightTolerance: Number.isInteger(params.targetRowHeightTolerance) ? params.targetRowHeightTolerance : 0.25,
});
items.forEach((item, i) => {
const { width, height, top, left } = geometry.boxes[i];
item.style.position = "absolute";
item.style.width = width + "px";
item.style.height = height + "px";
item.style.top = top + "px";
item.style.left = left + "px";
item.style.overflow = "hidden";
});
gallery.style.position = "relative";
gallery.style.height = geometry.containerHeight + "px";
gallery.style.visibility = "";
}
window.addEventListener("resize", updateGallery);
window.addEventListener("orientationchange", updateGallery);
updateGallery();
updateGallery();
}

View File

@@ -0,0 +1,248 @@
/*!
* Copyright 2019 SmugMug, Inc.
* Licensed under the terms of the MIT license. Please see LICENSE file in the project root for terms.
* @license
*/
'use strict';
var Row = require('./row');
/**
* Create a new, empty row.
*
* @method createNewRow
* @param layoutConfig {Object} The layout configuration
* @param layoutData {Object} The current state of the layout
* @return A new, empty row of the type specified by this layout.
*/
function createNewRow(layoutConfig, layoutData) {
var isBreakoutRow;
// Work out if this is a full width breakout row
if (layoutConfig.fullWidthBreakoutRowCadence !== false) {
if (((layoutData._rows.length + 1) % layoutConfig.fullWidthBreakoutRowCadence) === 0) {
isBreakoutRow = true;
}
}
return new Row({
top: layoutData._containerHeight,
left: layoutConfig.containerPadding.left,
width: layoutConfig.containerWidth - layoutConfig.containerPadding.left - layoutConfig.containerPadding.right,
spacing: layoutConfig.boxSpacing.horizontal,
targetRowHeight: layoutConfig.targetRowHeight,
targetRowHeightTolerance: layoutConfig.targetRowHeightTolerance,
edgeCaseMinRowHeight: 0.5 * layoutConfig.targetRowHeight,
edgeCaseMaxRowHeight: 2 * layoutConfig.targetRowHeight,
rightToLeft: false,
isBreakoutRow: isBreakoutRow,
widowLayoutStyle: layoutConfig.widowLayoutStyle
});
}
/**
* Add a completed row to the layout.
* Note: the row must have already been completed.
*
* @method addRow
* @param layoutConfig {Object} The layout configuration
* @param layoutData {Object} The current state of the layout
* @param row {Row} The row to add.
* @return {Array} Each item added to the row.
*/
function addRow(layoutConfig, layoutData, row) {
layoutData._rows.push(row);
layoutData._layoutItems = layoutData._layoutItems.concat(row.getItems());
// Increment the container height
layoutData._containerHeight += row.height + layoutConfig.boxSpacing.vertical;
return row.items;
}
/**
* Calculate the current layout for all items in the list that require layout.
* "Layout" means geometry: position within container and size
*
* @method computeLayout
* @param layoutConfig {Object} The layout configuration
* @param layoutData {Object} The current state of the layout
* @param itemLayoutData {Array} Array of items to lay out, with data required to lay out each item
* @return {Object} The newly-calculated layout, containing the new container height, and lists of layout items
*/
function computeLayout(layoutConfig, layoutData, itemLayoutData) {
var laidOutItems = [],
itemAdded,
currentRow,
nextToLastRowHeight;
// Apply forced aspect ratio if specified, and set a flag.
if (layoutConfig.forceAspectRatio) {
itemLayoutData.forEach(function (itemData) {
itemData.forcedAspectRatio = true;
itemData.aspectRatio = layoutConfig.forceAspectRatio;
});
}
// Loop through the items
itemLayoutData.some(function (itemData, i) {
if (isNaN(itemData.aspectRatio)) {
throw new Error("Item " + i + " has an invalid aspect ratio");
}
// If not currently building up a row, make a new one.
if (!currentRow) {
currentRow = createNewRow(layoutConfig, layoutData);
}
// Attempt to add item to the current row.
itemAdded = currentRow.addItem(itemData);
if (currentRow.isLayoutComplete()) {
// Row is filled; add it and start a new one
laidOutItems = laidOutItems.concat(addRow(layoutConfig, layoutData, currentRow));
if (layoutData._rows.length >= layoutConfig.maxNumRows) {
currentRow = null;
return true;
}
currentRow = createNewRow(layoutConfig, layoutData);
// Item was rejected; add it to its own row
if (!itemAdded) {
itemAdded = currentRow.addItem(itemData);
if (currentRow.isLayoutComplete()) {
// If the rejected item fills a row on its own, add the row and start another new one
laidOutItems = laidOutItems.concat(addRow(layoutConfig, layoutData, currentRow));
if (layoutData._rows.length >= layoutConfig.maxNumRows) {
currentRow = null;
return true;
}
currentRow = createNewRow(layoutConfig, layoutData);
}
}
}
});
// Handle any leftover content (orphans) depending on where they lie
// in this layout update, and in the total content set.
if (currentRow && currentRow.getItems().length && layoutConfig.showWidows) {
// Last page of all content or orphan suppression is suppressed; lay out orphans.
if (layoutData._rows.length) {
// Only Match previous row's height if it exists and it isn't a breakout row
if (layoutData._rows[layoutData._rows.length - 1].isBreakoutRow) {
nextToLastRowHeight = layoutData._rows[layoutData._rows.length - 1].targetRowHeight;
} else {
nextToLastRowHeight = layoutData._rows[layoutData._rows.length - 1].height;
}
currentRow.forceComplete(false, nextToLastRowHeight);
} else {
// ...else use target height if there is no other row height to reference.
currentRow.forceComplete(false);
}
laidOutItems = laidOutItems.concat(addRow(layoutConfig, layoutData, currentRow));
layoutConfig._widowCount = currentRow.getItems().length;
}
// We need to clean up the bottom container padding
// First remove the height added for box spacing
layoutData._containerHeight = layoutData._containerHeight - layoutConfig.boxSpacing.vertical;
// Then add our bottom container padding
layoutData._containerHeight = layoutData._containerHeight + layoutConfig.containerPadding.bottom;
return {
containerHeight: layoutData._containerHeight,
widowCount: layoutConfig._widowCount,
boxes: layoutData._layoutItems
};
}
/**
* Takes in a bunch of box data and config. Returns
* geometry to lay them out in a justified view.
*
* @method covertSizesToAspectRatios
* @param sizes {Array} Array of objects with widths and heights
* @return {Array} A list of aspect ratios
*/
module.exports = function (input, config) {
var layoutConfig = {};
var layoutData = {};
// Defaults
var defaults = {
containerWidth: 1060,
containerPadding: 10,
boxSpacing: 10,
targetRowHeight: 320,
targetRowHeightTolerance: 0.25,
maxNumRows: Number.POSITIVE_INFINITY,
forceAspectRatio: false,
showWidows: true,
fullWidthBreakoutRowCadence: false,
widowLayoutStyle: 'left'
};
var containerPadding = {};
var boxSpacing = {};
config = config || {};
// Merge defaults and config passed in
layoutConfig = Object.assign(defaults, config);
// Sort out padding and spacing values
containerPadding.top = (!isNaN(parseFloat(layoutConfig.containerPadding.top))) ? layoutConfig.containerPadding.top : layoutConfig.containerPadding;
containerPadding.right = (!isNaN(parseFloat(layoutConfig.containerPadding.right))) ? layoutConfig.containerPadding.right : layoutConfig.containerPadding;
containerPadding.bottom = (!isNaN(parseFloat(layoutConfig.containerPadding.bottom))) ? layoutConfig.containerPadding.bottom : layoutConfig.containerPadding;
containerPadding.left = (!isNaN(parseFloat(layoutConfig.containerPadding.left))) ? layoutConfig.containerPadding.left : layoutConfig.containerPadding;
boxSpacing.horizontal = (!isNaN(parseFloat(layoutConfig.boxSpacing.horizontal))) ? layoutConfig.boxSpacing.horizontal : layoutConfig.boxSpacing;
boxSpacing.vertical = (!isNaN(parseFloat(layoutConfig.boxSpacing.vertical))) ? layoutConfig.boxSpacing.vertical : layoutConfig.boxSpacing;
layoutConfig.containerPadding = containerPadding;
layoutConfig.boxSpacing = boxSpacing;
// Local
layoutData._layoutItems = [];
layoutData._awakeItems = [];
layoutData._inViewportItems = [];
layoutData._leadingOrphans = [];
layoutData._trailingOrphans = [];
layoutData._containerHeight = layoutConfig.containerPadding.top;
layoutData._rows = [];
layoutData._orphans = [];
layoutConfig._widowCount = 0;
// Convert widths and heights to aspect ratios if we need to
return computeLayout(layoutConfig, layoutData, input.map(function (item) {
if (item.width && item.height) {
return { aspectRatio: item.width / item.height };
} else {
return { aspectRatio: item };
}
}));
};

View File

@@ -0,0 +1,333 @@
/*!
* Copyright 2019 SmugMug, Inc.
* Licensed under the terms of the MIT license. Please see LICENSE file in the project root for terms.
* @license
*/
/**
* Row
* Wrapper for each row in a justified layout.
* Stores relevant values and provides methods for calculating layout of individual rows.
*
* @param {Object} layoutConfig - The same as that passed
* @param {Object} Initialization parameters. The following are all required:
* @param params.top {Number} Top of row, relative to container
* @param params.left {Number} Left side of row relative to container (equal to container left padding)
* @param params.width {Number} Width of row, not including container padding
* @param params.spacing {Number} Horizontal spacing between items
* @param params.targetRowHeight {Number} Layout algorithm will aim for this row height
* @param params.targetRowHeightTolerance {Number} Row heights may vary +/- (`targetRowHeight` x `targetRowHeightTolerance`)
* @param params.edgeCaseMinRowHeight {Number} Absolute minimum row height for edge cases that cannot be resolved within tolerance.
* @param params.edgeCaseMaxRowHeight {Number} Absolute maximum row height for edge cases that cannot be resolved within tolerance.
* @param params.isBreakoutRow {Boolean} Is this row in particular one of those breakout rows? Always false if it's not that kind of photo list
* @param params.widowLayoutStyle {String} If widows are visible, how should they be laid out?
* @constructor
*/
var Row = module.exports = function (params) {
// Top of row, relative to container
this.top = params.top;
// Left side of row relative to container (equal to container left padding)
this.left = params.left;
// Width of row, not including container padding
this.width = params.width;
// Horizontal spacing between items
this.spacing = params.spacing;
// Row height calculation values
this.targetRowHeight = params.targetRowHeight;
this.targetRowHeightTolerance = params.targetRowHeightTolerance;
this.minAspectRatio = this.width / params.targetRowHeight * (1 - params.targetRowHeightTolerance);
this.maxAspectRatio = this.width / params.targetRowHeight * (1 + params.targetRowHeightTolerance);
// Edge case row height minimum/maximum
this.edgeCaseMinRowHeight = params.edgeCaseMinRowHeight;
this.edgeCaseMaxRowHeight = params.edgeCaseMaxRowHeight;
// Widow layout direction
this.widowLayoutStyle = params.widowLayoutStyle;
// Full width breakout rows
this.isBreakoutRow = params.isBreakoutRow;
// Store layout data for each item in row
this.items = [];
// Height remains at 0 until it's been calculated
this.height = 0;
};
Row.prototype = {
/**
* Attempt to add a single item to the row.
* This is the heart of the justified algorithm.
* This method is direction-agnostic; it deals only with sizes, not positions.
*
* If the item fits in the row, without pushing row height beyond min/max tolerance,
* the item is added and the method returns true.
*
* If the item leaves row height too high, there may be room to scale it down and add another item.
* In this case, the item is added and the method returns true, but the row is incomplete.
*
* If the item leaves row height too short, there are too many items to fit within tolerance.
* The method will either accept or reject the new item, favoring the resulting row height closest to within tolerance.
* If the item is rejected, left/right padding will be required to fit the row height within tolerance;
* if the item is accepted, top/bottom cropping will be required to fit the row height within tolerance.
*
* @method addItem
* @param itemData {Object} Item layout data, containing item aspect ratio.
* @return {Boolean} True if successfully added; false if rejected.
*/
addItem: function (itemData) {
var newItems = this.items.concat(itemData),
// Calculate aspect ratios for items only; exclude spacing
rowWidthWithoutSpacing = this.width - (newItems.length - 1) * this.spacing,
newAspectRatio = newItems.reduce(function (sum, item) {
return sum + item.aspectRatio;
}, 0),
targetAspectRatio = rowWidthWithoutSpacing / this.targetRowHeight,
previousRowWidthWithoutSpacing,
previousAspectRatio,
previousTargetAspectRatio;
// Handle big full-width breakout photos if we're doing them
if (this.isBreakoutRow) {
// Only do it if there's no other items in this row
if (this.items.length === 0) {
// Only go full width if this photo is a square or landscape
if (itemData.aspectRatio >= 1) {
// Close out the row with a full width photo
this.items.push(itemData);
this.completeLayout(rowWidthWithoutSpacing / itemData.aspectRatio, 'justify');
return true;
}
}
}
if (newAspectRatio < this.minAspectRatio) {
// New aspect ratio is too narrow / scaled row height is too tall.
// Accept this item and leave row open for more items.
this.items.push(Object.assign({}, itemData));
return true;
} else if (newAspectRatio > this.maxAspectRatio) {
// New aspect ratio is too wide / scaled row height will be too short.
// Accept item if the resulting aspect ratio is closer to target than it would be without the item.
// NOTE: Any row that falls into this block will require cropping/padding on individual items.
if (this.items.length === 0) {
// When there are no existing items, force acceptance of the new item and complete the layout.
// This is the pano special case.
this.items.push(Object.assign({}, itemData));
this.completeLayout(rowWidthWithoutSpacing / newAspectRatio, 'justify');
return true;
}
// Calculate width/aspect ratio for row before adding new item
previousRowWidthWithoutSpacing = this.width - (this.items.length - 1) * this.spacing;
previousAspectRatio = this.items.reduce(function (sum, item) {
return sum + item.aspectRatio;
}, 0);
previousTargetAspectRatio = previousRowWidthWithoutSpacing / this.targetRowHeight;
if (Math.abs(newAspectRatio - targetAspectRatio) > Math.abs(previousAspectRatio - previousTargetAspectRatio)) {
// Row with new item is us farther away from target than row without; complete layout and reject item.
this.completeLayout(previousRowWidthWithoutSpacing / previousAspectRatio, 'justify');
return false;
} else {
// Row with new item is us closer to target than row without;
// accept the new item and complete the row layout.
this.items.push(Object.assign({}, itemData));
this.completeLayout(rowWidthWithoutSpacing / newAspectRatio, 'justify');
return true;
}
} else {
// New aspect ratio / scaled row height is within tolerance;
// accept the new item and complete the row layout.
this.items.push(Object.assign({}, itemData));
this.completeLayout(rowWidthWithoutSpacing / newAspectRatio, 'justify');
return true;
}
},
/**
* Check if a row has completed its layout.
*
* @method isLayoutComplete
* @return {Boolean} True if complete; false if not.
*/
isLayoutComplete: function () {
return this.height > 0;
},
/**
* Set row height and compute item geometry from that height.
* Will justify items within the row unless instructed not to.
*
* @method completeLayout
* @param newHeight {Number} Set row height to this value.
* @param widowLayoutStyle {String} How should widows display? Supported: left | justify | center
*/
completeLayout: function (newHeight, widowLayoutStyle) {
var itemWidthSum = this.left,
rowWidthWithoutSpacing = this.width - (this.items.length - 1) * this.spacing,
clampedToNativeRatio,
clampedHeight,
errorWidthPerItem,
roundedCumulativeErrors,
singleItemGeometry,
centerOffset;
// Justify unless explicitly specified otherwise.
if (typeof widowLayoutStyle === 'undefined' || ['justify', 'center', 'left'].indexOf(widowLayoutStyle) < 0) {
widowLayoutStyle = 'left';
}
// Clamp row height to edge case minimum/maximum.
clampedHeight = Math.max(this.edgeCaseMinRowHeight, Math.min(newHeight, this.edgeCaseMaxRowHeight));
if (newHeight !== clampedHeight) {
// If row height was clamped, the resulting row/item aspect ratio will be off,
// so force it to fit the width (recalculate aspectRatio to match clamped height).
// NOTE: this will result in cropping/padding commensurate to the amount of clamping.
this.height = clampedHeight;
clampedToNativeRatio = (rowWidthWithoutSpacing / clampedHeight) / (rowWidthWithoutSpacing / newHeight);
} else {
// If not clamped, leave ratio at 1.0.
this.height = newHeight;
clampedToNativeRatio = 1.0;
}
// Compute item geometry based on newHeight.
this.items.forEach(function (item) {
item.top = this.top;
item.width = item.aspectRatio * this.height * clampedToNativeRatio;
item.height = this.height;
// Left-to-right.
// TODO right to left
// item.left = this.width - itemWidthSum - item.width;
item.left = itemWidthSum;
// Increment width.
itemWidthSum += item.width + this.spacing;
}, this);
// If specified, ensure items fill row and distribute error
// caused by rounding width and height across all items.
if (widowLayoutStyle === 'justify') {
itemWidthSum -= (this.spacing + this.left);
errorWidthPerItem = (itemWidthSum - this.width) / this.items.length;
roundedCumulativeErrors = this.items.map(function (item, i) {
return Math.round((i + 1) * errorWidthPerItem);
});
if (this.items.length === 1) {
// For rows with only one item, adjust item width to fill row.
singleItemGeometry = this.items[0];
singleItemGeometry.width -= Math.round(errorWidthPerItem);
} else {
// For rows with multiple items, adjust item width and shift items to fill the row,
// while maintaining equal spacing between items in the row.
this.items.forEach(function (item, i) {
if (i > 0) {
item.left -= roundedCumulativeErrors[i - 1];
item.width -= (roundedCumulativeErrors[i] - roundedCumulativeErrors[i - 1]);
} else {
item.width -= roundedCumulativeErrors[i];
}
});
}
} else if (widowLayoutStyle === 'center') {
// Center widows
centerOffset = (this.width - itemWidthSum) / 2;
this.items.forEach(function (item) {
item.left += centerOffset + this.spacing;
}, this);
}
},
/**
* Force completion of row layout with current items.
*
* @method forceComplete
* @param fitToWidth {Boolean} Stretch current items to fill the row width.
* This will likely result in padding.
* @param fitToWidth {Number}
*/
forceComplete: function (fitToWidth, rowHeight) {
// TODO Handle fitting to width
// var rowWidthWithoutSpacing = this.width - (this.items.length - 1) * this.spacing,
// currentAspectRatio = this.items.reduce(function (sum, item) {
// return sum + item.aspectRatio;
// }, 0);
if (typeof rowHeight === 'number') {
this.completeLayout(rowHeight, this.widowLayoutStyle);
} else {
// Complete using target row height.
this.completeLayout(this.targetRowHeight, this.widowLayoutStyle);
}
},
/**
* Return layout data for items within row.
* Note: returns actual list, not a copy.
*
* @method getItems
* @return Layout data for items within row.
*/
getItems: function () {
return this.items;
}
};

View File

@@ -0,0 +1,805 @@
(function (window, factory) {
var lazySizes = factory(window, window.document, Date);
window.lazySizes = lazySizes;
if (typeof module == "object" && module.exports) {
module.exports = lazySizes;
}
})(
typeof window != "undefined" ? window : {},
/**
* import("./types/global")
* @typedef { import("./types/lazysizes-config").LazySizesConfigPartial } LazySizesConfigPartial
*/
function l(window, document, Date) {
// Pass in the window Date function also for SSR because the Date class can be lost
"use strict";
/*jshint eqnull:true */
var lazysizes,
/**
* @type { LazySizesConfigPartial }
*/
lazySizesCfg;
(function () {
var prop;
var lazySizesDefaults = {
lazyClass: "lazyload",
loadedClass: "lazyloaded",
loadingClass: "lazyloading",
preloadClass: "lazypreload",
errorClass: "lazyerror",
//strictClass: 'lazystrict',
autosizesClass: "lazyautosizes",
fastLoadedClass: "ls-is-cached",
iframeLoadMode: 0,
srcAttr: "data-src",
srcsetAttr: "data-srcset",
sizesAttr: "data-sizes",
//preloadAfterLoad: false,
minSize: 40,
customMedia: {},
init: true,
expFactor: 1.5,
hFac: 0.8,
loadMode: 2,
loadHidden: true,
ricTimeout: 0,
throttleDelay: 125,
};
lazySizesCfg = window.lazySizesConfig || window.lazysizesConfig || {};
for (prop in lazySizesDefaults) {
if (!(prop in lazySizesCfg)) {
lazySizesCfg[prop] = lazySizesDefaults[prop];
}
}
})();
if (!document || !document.getElementsByClassName) {
return {
init: function () {},
/**
* @type { LazySizesConfigPartial }
*/
cfg: lazySizesCfg,
/**
* @type { true }
*/
noSupport: true,
};
}
var docElem = document.documentElement;
var supportPicture = window.HTMLPictureElement;
var _addEventListener = "addEventListener";
var _getAttribute = "getAttribute";
/**
* Update to bind to window because 'this' becomes null during SSR
* builds.
*/
var addEventListener = window[_addEventListener].bind(window);
var setTimeout = window.setTimeout;
var requestAnimationFrame = window.requestAnimationFrame || setTimeout;
var requestIdleCallback = window.requestIdleCallback;
var regPicture = /^picture$/i;
var loadEvents = ["load", "error", "lazyincluded", "_lazyloaded"];
var regClassCache = {};
var forEach = Array.prototype.forEach;
/**
* @param ele {Element}
* @param cls {string}
*/
var hasClass = function (ele, cls) {
if (!regClassCache[cls]) {
regClassCache[cls] = new RegExp("(\\s|^)" + cls + "(\\s|$)");
}
return regClassCache[cls].test(ele[_getAttribute]("class") || "") && regClassCache[cls];
};
/**
* @param ele {Element}
* @param cls {string}
*/
var addClass = function (ele, cls) {
if (!hasClass(ele, cls)) {
ele.setAttribute("class", (ele[_getAttribute]("class") || "").trim() + " " + cls);
}
};
/**
* @param ele {Element}
* @param cls {string}
*/
var removeClass = function (ele, cls) {
var reg;
if ((reg = hasClass(ele, cls))) {
ele.setAttribute("class", (ele[_getAttribute]("class") || "").replace(reg, " "));
}
};
var addRemoveLoadEvents = function (dom, fn, add) {
var action = add ? _addEventListener : "removeEventListener";
if (add) {
addRemoveLoadEvents(dom, fn);
}
loadEvents.forEach(function (evt) {
dom[action](evt, fn);
});
};
/**
* @param elem { Element }
* @param name { string }
* @param detail { any }
* @param noBubbles { boolean }
* @param noCancelable { boolean }
* @returns { CustomEvent }
*/
var triggerEvent = function (elem, name, detail, noBubbles, noCancelable) {
var event = document.createEvent("Event");
if (!detail) {
detail = {};
}
detail.instance = lazysizes;
event.initEvent(name, !noBubbles, !noCancelable);
event.detail = detail;
elem.dispatchEvent(event);
return event;
};
var updatePolyfill = function (el, full) {
var polyfill;
if (!supportPicture && (polyfill = window.picturefill || lazySizesCfg.pf)) {
if (full && full.src && !el[_getAttribute]("srcset")) {
el.setAttribute("srcset", full.src);
}
polyfill({ reevaluate: true, elements: [el] });
} else if (full && full.src) {
el.src = full.src;
}
};
var getCSS = function (elem, style) {
return (getComputedStyle(elem, null) || {})[style];
};
/**
*
* @param elem { Element }
* @param parent { Element }
* @param [width] {number}
* @returns {number}
*/
var getWidth = function (elem, parent, width) {
width = width || elem.offsetWidth;
while (width < lazySizesCfg.minSize && parent && !elem._lazysizesWidth) {
width = parent.offsetWidth;
parent = parent.parentNode;
}
return width;
};
var rAF = (function () {
var running, waiting;
var firstFns = [];
var secondFns = [];
var fns = firstFns;
var run = function () {
var runFns = fns;
fns = firstFns.length ? secondFns : firstFns;
running = true;
waiting = false;
while (runFns.length) {
runFns.shift()();
}
running = false;
};
var rafBatch = function (fn, queue) {
if (running && !queue) {
fn.apply(this, arguments);
} else {
fns.push(fn);
if (!waiting) {
waiting = true;
(document.hidden ? setTimeout : requestAnimationFrame)(run);
}
}
};
rafBatch._lsFlush = run;
return rafBatch;
})();
var rAFIt = function (fn, simple) {
return simple
? function () {
rAF(fn);
}
: function () {
var that = this;
var args = arguments;
rAF(function () {
fn.apply(that, args);
});
};
};
var throttle = function (fn) {
var running;
var lastTime = 0;
var gDelay = lazySizesCfg.throttleDelay;
var rICTimeout = lazySizesCfg.ricTimeout;
var run = function () {
running = false;
lastTime = Date.now();
fn();
};
var idleCallback =
requestIdleCallback && rICTimeout > 49
? function () {
requestIdleCallback(run, { timeout: rICTimeout });
if (rICTimeout !== lazySizesCfg.ricTimeout) {
rICTimeout = lazySizesCfg.ricTimeout;
}
}
: rAFIt(function () {
setTimeout(run);
}, true);
return function (isPriority) {
var delay;
if ((isPriority = isPriority === true)) {
rICTimeout = 33;
}
if (running) {
return;
}
running = true;
delay = gDelay - (Date.now() - lastTime);
if (delay < 0) {
delay = 0;
}
if (isPriority || delay < 9) {
idleCallback();
} else {
setTimeout(idleCallback, delay);
}
};
};
//based on http://modernjavascript.blogspot.de/2013/08/building-better-debounce.html
var debounce = function (func) {
var timeout, timestamp;
var wait = 99;
var run = function () {
timeout = null;
func();
};
var later = function () {
var last = Date.now() - timestamp;
if (last < wait) {
setTimeout(later, wait - last);
} else {
(requestIdleCallback || run)(run);
}
};
return function () {
timestamp = Date.now();
if (!timeout) {
timeout = setTimeout(later, wait);
}
};
};
var loader = (function () {
var preloadElems, isCompleted, resetPreloadingTimer, loadMode, started;
var eLvW, elvH, eLtop, eLleft, eLright, eLbottom, isBodyHidden;
var regImg = /^img$/i;
var regIframe = /^iframe$/i;
var supportScroll = "onscroll" in window && !/(gle|ing)bot/.test(navigator.userAgent);
var shrinkExpand = 0;
var currentExpand = 0;
var isLoading = 0;
var lowRuns = -1;
var resetPreloading = function (e) {
isLoading--;
if (!e || isLoading < 0 || !e.target) {
isLoading = 0;
}
};
var isVisible = function (elem) {
if (isBodyHidden == null) {
isBodyHidden = getCSS(document.body, "visibility") == "hidden";
}
return isBodyHidden || !(getCSS(elem.parentNode, "visibility") == "hidden" && getCSS(elem, "visibility") == "hidden");
};
var isNestedVisible = function (elem, elemExpand) {
var outerRect;
var parent = elem;
var visible = isVisible(elem);
eLtop -= elemExpand;
eLbottom += elemExpand;
eLleft -= elemExpand;
eLright += elemExpand;
while (visible && (parent = parent.offsetParent) && parent != document.body && parent != docElem) {
visible = (getCSS(parent, "opacity") || 1) > 0;
if (visible && getCSS(parent, "overflow") != "visible") {
outerRect = parent.getBoundingClientRect();
visible = eLright > outerRect.left && eLleft < outerRect.right && eLbottom > outerRect.top - 1 && eLtop < outerRect.bottom + 1;
}
}
return visible;
};
var checkElements = function () {
var eLlen, i, rect, autoLoadElem, loadedSomething, elemExpand, elemNegativeExpand, elemExpandVal, beforeExpandVal, defaultExpand, preloadExpand, hFac;
var lazyloadElems = lazysizes.elements;
if ((loadMode = lazySizesCfg.loadMode) && isLoading < 8 && (eLlen = lazyloadElems.length)) {
i = 0;
lowRuns++;
for (; i < eLlen; i++) {
if (!lazyloadElems[i] || lazyloadElems[i]._lazyRace) {
continue;
}
if (!supportScroll || (lazysizes.prematureUnveil && lazysizes.prematureUnveil(lazyloadElems[i]))) {
unveilElement(lazyloadElems[i]);
continue;
}
if (!(elemExpandVal = lazyloadElems[i][_getAttribute]("data-expand")) || !(elemExpand = elemExpandVal * 1)) {
elemExpand = currentExpand;
}
if (!defaultExpand) {
defaultExpand = !lazySizesCfg.expand || lazySizesCfg.expand < 1 ? (docElem.clientHeight > 500 && docElem.clientWidth > 500 ? 500 : 370) : lazySizesCfg.expand;
lazysizes._defEx = defaultExpand;
preloadExpand = defaultExpand * lazySizesCfg.expFactor;
hFac = lazySizesCfg.hFac;
isBodyHidden = null;
if (currentExpand < preloadExpand && isLoading < 1 && lowRuns > 2 && loadMode > 2 && !document.hidden) {
currentExpand = preloadExpand;
lowRuns = 0;
} else if (loadMode > 1 && lowRuns > 1 && isLoading < 6) {
currentExpand = defaultExpand;
} else {
currentExpand = shrinkExpand;
}
}
if (beforeExpandVal !== elemExpand) {
eLvW = innerWidth + elemExpand * hFac;
elvH = innerHeight + elemExpand;
elemNegativeExpand = elemExpand * -1;
beforeExpandVal = elemExpand;
}
rect = lazyloadElems[i].getBoundingClientRect();
if ((eLbottom = rect.bottom) >= elemNegativeExpand && (eLtop = rect.top) <= elvH && (eLright = rect.right) >= elemNegativeExpand * hFac && (eLleft = rect.left) <= eLvW && (eLbottom || eLright || eLleft || eLtop) && (lazySizesCfg.loadHidden || isVisible(lazyloadElems[i])) && ((isCompleted && isLoading < 3 && !elemExpandVal && (loadMode < 3 || lowRuns < 4)) || isNestedVisible(lazyloadElems[i], elemExpand))) {
unveilElement(lazyloadElems[i]);
loadedSomething = true;
if (isLoading > 9) {
break;
}
} else if (!loadedSomething && isCompleted && !autoLoadElem && isLoading < 4 && lowRuns < 4 && loadMode > 2 && (preloadElems[0] || lazySizesCfg.preloadAfterLoad) && (preloadElems[0] || (!elemExpandVal && (eLbottom || eLright || eLleft || eLtop || lazyloadElems[i][_getAttribute](lazySizesCfg.sizesAttr) != "auto")))) {
autoLoadElem = preloadElems[0] || lazyloadElems[i];
}
}
if (autoLoadElem && !loadedSomething) {
unveilElement(autoLoadElem);
}
}
};
var throttledCheckElements = throttle(checkElements);
var switchLoadingClass = function (e) {
var elem = e.target;
if (elem._lazyCache) {
delete elem._lazyCache;
return;
}
resetPreloading(e);
addClass(elem, lazySizesCfg.loadedClass);
removeClass(elem, lazySizesCfg.loadingClass);
addRemoveLoadEvents(elem, rafSwitchLoadingClass);
triggerEvent(elem, "lazyloaded");
};
var rafedSwitchLoadingClass = rAFIt(switchLoadingClass);
var rafSwitchLoadingClass = function (e) {
rafedSwitchLoadingClass({ target: e.target });
};
var changeIframeSrc = function (elem, src) {
var loadMode = elem.getAttribute("data-load-mode") || lazySizesCfg.iframeLoadMode;
// loadMode can be also a string!
if (loadMode == 0) {
elem.contentWindow.location.replace(src);
} else if (loadMode == 1) {
elem.src = src;
}
};
var handleSources = function (source) {
var customMedia;
var sourceSrcset = source[_getAttribute](lazySizesCfg.srcsetAttr);
if ((customMedia = lazySizesCfg.customMedia[source[_getAttribute]("data-media") || source[_getAttribute]("media")])) {
source.setAttribute("media", customMedia);
}
if (sourceSrcset) {
source.setAttribute("srcset", sourceSrcset);
}
};
var lazyUnveil = rAFIt(function (elem, detail, isAuto, sizes, isImg) {
var src, srcset, parent, isPicture, event, firesLoad;
if (!(event = triggerEvent(elem, "lazybeforeunveil", detail)).defaultPrevented) {
if (sizes) {
if (isAuto) {
addClass(elem, lazySizesCfg.autosizesClass);
} else {
elem.setAttribute("sizes", sizes);
}
}
srcset = elem[_getAttribute](lazySizesCfg.srcsetAttr);
src = elem[_getAttribute](lazySizesCfg.srcAttr);
if (isImg) {
parent = elem.parentNode;
isPicture = parent && regPicture.test(parent.nodeName || "");
}
firesLoad = detail.firesLoad || ("src" in elem && (srcset || src || isPicture));
event = { target: elem };
addClass(elem, lazySizesCfg.loadingClass);
if (firesLoad) {
clearTimeout(resetPreloadingTimer);
resetPreloadingTimer = setTimeout(resetPreloading, 2500);
addRemoveLoadEvents(elem, rafSwitchLoadingClass, true);
}
if (isPicture) {
forEach.call(parent.getElementsByTagName("source"), handleSources);
}
if (srcset) {
elem.setAttribute("srcset", srcset);
} else if (src && !isPicture) {
if (regIframe.test(elem.nodeName)) {
changeIframeSrc(elem, src);
} else {
elem.src = src;
}
}
if (isImg && (srcset || isPicture)) {
updatePolyfill(elem, { src: src });
}
}
if (elem._lazyRace) {
delete elem._lazyRace;
}
removeClass(elem, lazySizesCfg.lazyClass);
rAF(function () {
// Part of this can be removed as soon as this fix is older: https://bugs.chromium.org/p/chromium/issues/detail?id=7731 (2015)
var isLoaded = elem.complete && elem.naturalWidth > 1;
if (!firesLoad || isLoaded) {
if (isLoaded) {
addClass(elem, lazySizesCfg.fastLoadedClass);
}
switchLoadingClass(event);
elem._lazyCache = true;
setTimeout(function () {
if ("_lazyCache" in elem) {
delete elem._lazyCache;
}
}, 9);
}
if (elem.loading == "lazy") {
isLoading--;
}
}, true);
});
/**
*
* @param elem { Element }
*/
var unveilElement = function (elem) {
if (elem._lazyRace) {
return;
}
var detail;
var isImg = regImg.test(elem.nodeName);
//allow using sizes="auto", but don't use. it's invalid. Use data-sizes="auto" or a valid value for sizes instead (i.e.: sizes="80vw")
var sizes = isImg && (elem[_getAttribute](lazySizesCfg.sizesAttr) || elem[_getAttribute]("sizes"));
var isAuto = sizes == "auto";
if ((isAuto || !isCompleted) && isImg && (elem[_getAttribute]("src") || elem.srcset) && !elem.complete && !hasClass(elem, lazySizesCfg.errorClass) && hasClass(elem, lazySizesCfg.lazyClass)) {
return;
}
detail = triggerEvent(elem, "lazyunveilread").detail;
if (isAuto) {
autoSizer.updateElem(elem, true, elem.offsetWidth);
}
elem._lazyRace = true;
isLoading++;
lazyUnveil(elem, detail, isAuto, sizes, isImg);
};
var afterScroll = debounce(function () {
lazySizesCfg.loadMode = 3;
throttledCheckElements();
});
var altLoadmodeScrollListner = function () {
if (lazySizesCfg.loadMode == 3) {
lazySizesCfg.loadMode = 2;
}
afterScroll();
};
var onload = function () {
if (isCompleted) {
return;
}
if (Date.now() - started < 999) {
setTimeout(onload, 999);
return;
}
isCompleted = true;
lazySizesCfg.loadMode = 3;
throttledCheckElements();
addEventListener("scroll", altLoadmodeScrollListner, true);
};
return {
_: function () {
started = Date.now();
lazysizes.elements = document.getElementsByClassName(lazySizesCfg.lazyClass);
preloadElems = document.getElementsByClassName(lazySizesCfg.lazyClass + " " + lazySizesCfg.preloadClass);
addEventListener("scroll", throttledCheckElements, true);
addEventListener("resize", throttledCheckElements, true);
addEventListener("pageshow", function (e) {
if (e.persisted) {
var loadingElements = document.querySelectorAll("." + lazySizesCfg.loadingClass);
if (loadingElements.length && loadingElements.forEach) {
requestAnimationFrame(function () {
loadingElements.forEach(function (img) {
if (img.complete) {
unveilElement(img);
}
});
});
}
}
});
if (window.MutationObserver) {
new MutationObserver(throttledCheckElements).observe(docElem, { childList: true, subtree: true, attributes: true });
} else {
docElem[_addEventListener]("DOMNodeInserted", throttledCheckElements, true);
docElem[_addEventListener]("DOMAttrModified", throttledCheckElements, true);
setInterval(throttledCheckElements, 999);
}
addEventListener("hashchange", throttledCheckElements, true);
//, 'fullscreenchange'
["focus", "mouseover", "click", "load", "transitionend", "animationend"].forEach(function (name) {
document[_addEventListener](name, throttledCheckElements, true);
});
if (/d$|^c/.test(document.readyState)) {
onload();
} else {
addEventListener("load", onload);
document[_addEventListener]("DOMContentLoaded", throttledCheckElements);
setTimeout(onload, 20000);
}
if (lazysizes.elements.length) {
checkElements();
rAF._lsFlush();
} else {
throttledCheckElements();
}
},
checkElems: throttledCheckElements,
unveil: unveilElement,
_aLSL: altLoadmodeScrollListner,
};
})();
var autoSizer = (function () {
var autosizesElems;
var sizeElement = rAFIt(function (elem, parent, event, width) {
var sources, i, len;
elem._lazysizesWidth = width;
width += "px";
elem.setAttribute("sizes", width);
if (regPicture.test(parent.nodeName || "")) {
sources = parent.getElementsByTagName("source");
for (i = 0, len = sources.length; i < len; i++) {
sources[i].setAttribute("sizes", width);
}
}
if (!event.detail.dataAttr) {
updatePolyfill(elem, event.detail);
}
});
/**
*
* @param elem {Element}
* @param dataAttr
* @param [width] { number }
*/
var getSizeElement = function (elem, dataAttr, width) {
var event;
var parent = elem.parentNode;
if (parent) {
width = getWidth(elem, parent, width);
event = triggerEvent(elem, "lazybeforesizes", { width: width, dataAttr: !!dataAttr });
if (!event.defaultPrevented) {
width = event.detail.width;
if (width && width !== elem._lazysizesWidth) {
sizeElement(elem, parent, event, width);
}
}
}
};
var updateElementsSizes = function () {
var i;
var len = autosizesElems.length;
if (len) {
i = 0;
for (; i < len; i++) {
getSizeElement(autosizesElems[i]);
}
}
};
var debouncedUpdateElementsSizes = debounce(updateElementsSizes);
return {
_: function () {
autosizesElems = document.getElementsByClassName(lazySizesCfg.autosizesClass);
addEventListener("resize", debouncedUpdateElementsSizes);
},
checkElems: debouncedUpdateElementsSizes,
updateElem: getSizeElement,
};
})();
var init = function () {
if (!init.i && document.getElementsByClassName) {
init.i = true;
autoSizer._();
loader._();
}
};
setTimeout(function () {
if (lazySizesCfg.init) {
init();
}
});
lazysizes = {
/**
* @type { LazySizesConfigPartial }
*/
cfg: lazySizesCfg,
autoSizer: autoSizer,
loader: loader,
init: init,
uP: updatePolyfill,
aC: addClass,
rC: removeClass,
hC: hasClass,
fire: triggerEvent,
gW: getWidth,
rAF: rAF,
};
return lazysizes;
},
);

View File

@@ -0,0 +1,83 @@
import PhotoSwipeLightbox from "./photoswipe/photoswipe-lightbox.esm.js";
import PhotoSwipe from "./photoswipe/photoswipe.esm.js";
import PhotoSwipeDynamicCaption from "./photoswipe/photoswipe-dynamic-caption-plugin.esm.min.js";
import * as params from "@params";
const gallery = document.getElementById("gallery");
if (gallery) {
const lightbox = new PhotoSwipeLightbox({
gallery,
children: ".gallery-item",
showHideAnimationType: "zoom",
bgOpacity: 1,
pswpModule: PhotoSwipe,
imageClickAction: "close",
paddingFn: (viewportSize) => {
return viewportSize.x < 700
? {
top: 0,
bottom: 0,
left: 0,
right: 0,
}
: {
top: 30,
bottom: 30,
left: 0,
right: 0,
};
},
closeTitle: params.closeTitle,
zoomTitle: params.zoomTitle,
arrowPrevTitle: params.arrowPrevTitle,
arrowNextTitle: params.arrowNextTitle,
errorMsg: params.errorMsg,
});
lightbox.on("uiRegister", () => {
lightbox.pswp.ui.registerElement({
name: "download-button",
order: 8,
isButton: true,
tagName: "a",
html: {
isCustomSVG: true,
inner: '<path d="M20.5 14.3 17.1 18V10h-2.2v7.9l-3.4-3.6L10 16l6 6.1 6-6.1ZM23 23H9v2h14Z" id="pswp__icn-download"/>',
outlineID: "pswp__icn-download",
},
onInit: (el, pswp) => {
el.setAttribute("download", "");
el.setAttribute("target", "_blank");
el.setAttribute("rel", "noopener");
el.setAttribute("title", params.downloadTitle || "Download");
pswp.on("change", () => {
el.href = pswp.currSlide.data.element.href;
});
},
});
});
lightbox.on("change", () => {
history.replaceState("", document.title, "#" + lightbox.pswp.currSlide.index);
});
lightbox.on("close", () => {
history.replaceState("", document.title, window.location.pathname);
});
new PhotoSwipeDynamicCaption(lightbox, {
mobileLayoutBreakpoint: 700,
type: "auto",
mobileCaptionOverlapRatio: 1,
});
lightbox.init();
if (window.location.hash.substring(1).length > 0) {
const index = parseInt(window.location.hash.substring(1), 10);
if (!Number.isNaN(index) && index >= 0 && index < gallery.querySelectorAll("a").length) {
lightbox.loadAndOpen(index, { gallery });
}
}
}

View File

@@ -0,0 +1,5 @@
import "./menu.js";
import "./gallery.js";
import "./lazysizes.js";
import "./lightbox.js";
import "./custom.js";

View File

@@ -0,0 +1,9 @@
const el = document.getElementById("menu-toggle");
if (el) {
el.addEventListener("click", (event) => {
event.preventDefault();
const target = document.getElementById("menu");
el.ariaExpanded = target.classList.contains("hidden");
target.classList.toggle("hidden");
});
}

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,10 @@
{
"compilerOptions": {
"baseUrl": ".",
"paths": {
"*": [
"*"
]
}
}
}