initial commit
This commit is contained in:
43
themes/gallery/assets/css/_colors.scss
Normal file
43
themes/gallery/assets/css/_colors.scss
Normal 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);
|
||||
}
|
||||
99
themes/gallery/assets/css/_normalize.scss
Normal file
99
themes/gallery/assets/css/_normalize.scss
Normal 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;
|
||||
}
|
||||
428
themes/gallery/assets/css/_styles.scss
Normal file
428
themes/gallery/assets/css/_styles.scss
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
1
themes/gallery/assets/css/custom.css
Normal file
1
themes/gallery/assets/css/custom.css
Normal file
@@ -0,0 +1 @@
|
||||
/* custom.css */
|
||||
20
themes/gallery/assets/css/main.scss
Normal file
20
themes/gallery/assets/css/main.scss
Normal 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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
420
themes/gallery/assets/css/photoswipe/photoswipe.css
Normal file
420
themes/gallery/assets/css/photoswipe/photoswipe.css
Normal 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;
|
||||
}
|
||||
1
themes/gallery/assets/js/custom.js
Normal file
1
themes/gallery/assets/js/custom.js
Normal file
@@ -0,0 +1 @@
|
||||
/* custom.js */
|
||||
52
themes/gallery/assets/js/gallery.js
Normal file
52
themes/gallery/assets/js/gallery.js
Normal 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();
|
||||
}
|
||||
248
themes/gallery/assets/js/justified-layout/index.js
Normal file
248
themes/gallery/assets/js/justified-layout/index.js
Normal 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 };
|
||||
}
|
||||
}));
|
||||
};
|
||||
333
themes/gallery/assets/js/justified-layout/row.js
Normal file
333
themes/gallery/assets/js/justified-layout/row.js
Normal 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;
|
||||
}
|
||||
|
||||
};
|
||||
805
themes/gallery/assets/js/lazysizes.js
Normal file
805
themes/gallery/assets/js/lazysizes.js
Normal 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;
|
||||
},
|
||||
);
|
||||
83
themes/gallery/assets/js/lightbox.js
Normal file
83
themes/gallery/assets/js/lightbox.js
Normal 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 });
|
||||
}
|
||||
}
|
||||
}
|
||||
5
themes/gallery/assets/js/main.js
Normal file
5
themes/gallery/assets/js/main.js
Normal file
@@ -0,0 +1,5 @@
|
||||
import "./menu.js";
|
||||
import "./gallery.js";
|
||||
import "./lazysizes.js";
|
||||
import "./lightbox.js";
|
||||
import "./custom.js";
|
||||
9
themes/gallery/assets/js/menu.js
Normal file
9
themes/gallery/assets/js/menu.js
Normal 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");
|
||||
});
|
||||
}
|
||||
5
themes/gallery/assets/js/photoswipe/photoswipe-dynamic-caption-plugin.esm.min.js
vendored
Normal file
5
themes/gallery/assets/js/photoswipe/photoswipe-dynamic-caption-plugin.esm.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
1960
themes/gallery/assets/js/photoswipe/photoswipe-lightbox.esm.js
Normal file
1960
themes/gallery/assets/js/photoswipe/photoswipe-lightbox.esm.js
Normal file
File diff suppressed because it is too large
Load Diff
7081
themes/gallery/assets/js/photoswipe/photoswipe.esm.js
Normal file
7081
themes/gallery/assets/js/photoswipe/photoswipe.esm.js
Normal file
File diff suppressed because it is too large
Load Diff
10
themes/gallery/assets/jsconfig.json
Normal file
10
themes/gallery/assets/jsconfig.json
Normal file
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"baseUrl": ".",
|
||||
"paths": {
|
||||
"*": [
|
||||
"*"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user