Implement a photo carousel
The layout of this is all CSS with scroll-snap. (Neat!) Also implement a JavaScript scroll event listener that detects when photos scroll and updates the caption and photo data. Refactor a few parts of the photo_exif_table into partials so they can be reused for the carousel items.
This commit is contained in:
parent
c256179803
commit
81a5507e8f
7 changed files with 393 additions and 21 deletions
264
assets/scripts/photo_carousel.js
Normal file
264
assets/scripts/photo_carousel.js
Normal file
|
@ -0,0 +1,264 @@
|
||||||
|
// Eryn Wells <eryn@erynwells.me>
|
||||||
|
|
||||||
|
class Carousel {
|
||||||
|
carousel = null;
|
||||||
|
|
||||||
|
constructor(element) {
|
||||||
|
this.carousel = element;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class PhotoParametersTable {
|
||||||
|
tableElement;
|
||||||
|
|
||||||
|
#makeModelElement;
|
||||||
|
#latitudeElement;
|
||||||
|
#longitudeElement;
|
||||||
|
#megapixelsElement;
|
||||||
|
#widthElement;
|
||||||
|
#heightElement;
|
||||||
|
#isoElement;
|
||||||
|
#focalLengthElement;
|
||||||
|
#fNumberElement;
|
||||||
|
#exposureTimeElement;
|
||||||
|
|
||||||
|
constructor(element) {
|
||||||
|
this.tableElement = element;
|
||||||
|
}
|
||||||
|
|
||||||
|
setMakeModel(make, model) {
|
||||||
|
if (!this.#makeModelElement) {
|
||||||
|
this.#makeModelElement = this.#getElementWithQuerySelector("td.make-model");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!this.#makeModelElement) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let makeModel = "";
|
||||||
|
if (make && model) {
|
||||||
|
return `${make} ${model}`;
|
||||||
|
} else if (make) {
|
||||||
|
return make;
|
||||||
|
} else if (model) {
|
||||||
|
return model;
|
||||||
|
} else {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
this.#makeModelElement.innerHTML = makeModel;
|
||||||
|
}
|
||||||
|
|
||||||
|
setLocation(latitude, longitude) {
|
||||||
|
let latitudeElement = this.#latitudeElement;
|
||||||
|
if (!latitudeElement) {
|
||||||
|
latitudeElement = this.#getElementWithQuerySelector("td.location > data.latitude");
|
||||||
|
this.#latitudeElement = latitudeElement;
|
||||||
|
}
|
||||||
|
|
||||||
|
let longitudeElement = this.#longitudeElement;
|
||||||
|
if (!longitudeElement) {
|
||||||
|
longitudeElement = this.#getElementWithQuerySelector("td.location > data.longitude");
|
||||||
|
this.#longitudeElement = longitudeElement;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!latitudeElement || !longitudeElement) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
latitudeElement.innerHTML = latitude;
|
||||||
|
longitudeElement.innerHTML = longitude;
|
||||||
|
}
|
||||||
|
|
||||||
|
setMegapixels(megapixels) {
|
||||||
|
let megapixelsElement = this.#megapixelsElement;
|
||||||
|
if (!megapixelsElement) {
|
||||||
|
megapixelsElement = this.#getElementWithQuerySelector("td.size > data.megapixels");
|
||||||
|
this.#megapixelsElement = megapixelsElement;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!megapixelsElement) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
megapixelsElement.innerHTML = megapixels;
|
||||||
|
}
|
||||||
|
|
||||||
|
setSize(width, height) {
|
||||||
|
let widthElement = this.#widthElement;
|
||||||
|
if (!widthElement) {
|
||||||
|
widthElement = this.#getElementWithQuerySelector("td.size > data.width");
|
||||||
|
this.#widthElement = widthElement;
|
||||||
|
}
|
||||||
|
|
||||||
|
let heightElement = this.#heightElement;
|
||||||
|
if (heightElement) {
|
||||||
|
heightElement = this.#getElementWithQuerySelector("td.size > data.height");
|
||||||
|
this.#heightElement = heightElement;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!widthElement || !heightElement) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
widthElement.innerHTML = width;
|
||||||
|
heightElement.innerHTML = height;
|
||||||
|
}
|
||||||
|
|
||||||
|
setISO(iso) {
|
||||||
|
let isoElement = this.#isoElement;
|
||||||
|
if (!isoElement) {
|
||||||
|
isoElement = this.#getElementWithQuerySelector("td.iso");
|
||||||
|
this.#isoElement = isoElement;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isoElement) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
isoElement.innerHTML = `ISO ${iso}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
setFocalLength(focalLength) {
|
||||||
|
let focalLengthElement = this.#focalLengthElement;
|
||||||
|
if (!focalLengthElement) {
|
||||||
|
focalLengthElement = this.#getElementWithQuerySelector("td.focal-length");
|
||||||
|
this.#focalLengthElement = focalLengthElement;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!focalLengthElement) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
focalLengthElement.innerHTML = `${focalLength} mm`;
|
||||||
|
}
|
||||||
|
|
||||||
|
setFNumber(f) {
|
||||||
|
let fNumberElement = this.#fNumberElement;
|
||||||
|
if (!fNumberElement) {
|
||||||
|
fNumberElement = this.#getElementWithQuerySelector("td.f-number");
|
||||||
|
this.#fNumberElement = fNumberElement;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!fNumberElement) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
fNumberElement.innerHTML = `ƒ${f}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
setExposureTime(exposureTime) {
|
||||||
|
let exposureTimeElement = this.#exposureTimeElement;
|
||||||
|
if (!exposureTimeElement) {
|
||||||
|
exposureTimeElement = this.#getElementWithQuerySelector("td.exposure-time");
|
||||||
|
this.#exposureTimeElement = exposureTimeElement;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!exposureTimeElement) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
exposureTimeElement.innerHTML = `${exposureTime} s`;
|
||||||
|
}
|
||||||
|
|
||||||
|
#getElementWithQuerySelector(query) {
|
||||||
|
const element = this.tableElement.querySelector(query);
|
||||||
|
if (!element) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return element;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
document.addEventListener("DOMContentLoaded", (event) => {
|
||||||
|
let photoParams = null;
|
||||||
|
|
||||||
|
const photoParamsTableElement = document.querySelector(".photo-params table");
|
||||||
|
if (photoParamsTableElement) {
|
||||||
|
photoParams = new PhotoParametersTable(photoParamsTableElement);
|
||||||
|
}
|
||||||
|
|
||||||
|
document.querySelectorAll("figure.carousel").forEach(carouselElement => {
|
||||||
|
class CarouselItem {
|
||||||
|
element = null;
|
||||||
|
relativeX = 0;
|
||||||
|
isLeftOfContainerCenter = false;
|
||||||
|
|
||||||
|
constructor(element, relativeX) {
|
||||||
|
this.element = element;
|
||||||
|
this.relativeX = relativeX
|
||||||
|
}
|
||||||
|
|
||||||
|
get relativeMaxX() {
|
||||||
|
const rect = this.element.getBoundingClientRect();
|
||||||
|
return this.relativeX + rect.width;
|
||||||
|
}
|
||||||
|
|
||||||
|
get title() { return this.element.dataset.title; }
|
||||||
|
get latitude() { return this.element.dataset.latitude; }
|
||||||
|
get longitude() { return this.element.dataset.longitude; }
|
||||||
|
get megapixels() { return this.element.dataset.megapixels; }
|
||||||
|
get width() { return this.element.dataset.width; }
|
||||||
|
get height() { return this.element.dataset.height; }
|
||||||
|
get make() { return this.element.dataset.make; }
|
||||||
|
get model() { return this.element.dataset.model; }
|
||||||
|
get iso() { return this.element.dataset.iso; }
|
||||||
|
get focalLength() { return this.element.dataset.focalLength; }
|
||||||
|
get fNumber() { return this.element.dataset.fNumber; }
|
||||||
|
get exposureTime() { return this.element.dataset.exposureTime; }
|
||||||
|
}
|
||||||
|
|
||||||
|
let captionElement = carouselElement.querySelector("figcaption");
|
||||||
|
|
||||||
|
let carouselRect = carouselElement.getBoundingClientRect();
|
||||||
|
let carouselRectHorizontalCenter = carouselRect.width / 2.0;
|
||||||
|
|
||||||
|
let carouselItems = Array.from(carouselElement.querySelectorAll("ul > li")).map(item => {
|
||||||
|
let itemRect = item.getClientRects()[0];
|
||||||
|
let relativeX = itemRect.x - carouselRect.x;
|
||||||
|
return new CarouselItem(item, relativeX);
|
||||||
|
});
|
||||||
|
|
||||||
|
let previousScrollLeft = null;
|
||||||
|
let isScrollingLeft = true;
|
||||||
|
|
||||||
|
carouselElement.querySelector("ul").addEventListener("scroll", event => {
|
||||||
|
const target = event.target;
|
||||||
|
const scrollLeft = target.scrollLeft;
|
||||||
|
|
||||||
|
if (previousScrollLeft === null) {
|
||||||
|
carouselRect = target.getBoundingClientRect();
|
||||||
|
carouselRectHorizontalCenter = carouselRect.width / 2.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (previousScrollLeft !== null) {
|
||||||
|
isScrollingLeft = scrollLeft > previousScrollLeft;
|
||||||
|
}
|
||||||
|
previousScrollLeft = scrollLeft;
|
||||||
|
|
||||||
|
carouselItems.forEach(item => {
|
||||||
|
const itemRelativeXCoordinate = isScrollingLeft ? item.relativeX - scrollLeft : item.relativeMaxX - scrollLeft;
|
||||||
|
|
||||||
|
const itemWasLeftOfContainerCenter = item.isLeftOfContainerCenter;
|
||||||
|
const itemIsLeftOfContainerCenter = itemRelativeXCoordinate < carouselRectHorizontalCenter;
|
||||||
|
item.isLeftOfContainerCenter = itemIsLeftOfContainerCenter;
|
||||||
|
|
||||||
|
if ( (isScrollingLeft && (!itemWasLeftOfContainerCenter && itemIsLeftOfContainerCenter))
|
||||||
|
|| (!isScrollingLeft && (itemWasLeftOfContainerCenter && !itemIsLeftOfContainerCenter))) {
|
||||||
|
if (captionElement) {
|
||||||
|
captionElement.innerHTML = item.title;
|
||||||
|
}
|
||||||
|
|
||||||
|
photoParams.setMakeModel(item.make, item.model);
|
||||||
|
photoParams.setLocation(item.latitude, item.longitude);
|
||||||
|
photoParams.setMegapixels(item.megapixels);
|
||||||
|
photoParams.setSize(item.width, item.height);
|
||||||
|
photoParams.setISO(item.iso);
|
||||||
|
photoParams.setFocalLength(item.focalLength);
|
||||||
|
photoParams.setFNumber(item.fNumber);
|
||||||
|
photoParams.setExposureTime(item.exposureTime);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
|
@ -5,6 +5,8 @@
|
||||||
--photo-params-container-background-color: rgb(var(--super-lt-gray));
|
--photo-params-container-background-color: rgb(var(--super-lt-gray));
|
||||||
--photo-params-color: rgb(var(--sub-dk-gray));
|
--photo-params-color: rgb(var(--sub-dk-gray));
|
||||||
--photo-params-border-color: rgb(var(--super-lt-gray));
|
--photo-params-border-color: rgb(var(--super-lt-gray));
|
||||||
|
|
||||||
|
--carousel-gradient-full-opaque: var(--background);
|
||||||
}
|
}
|
||||||
@media (prefers-color-scheme: dark) {
|
@media (prefers-color-scheme: dark) {
|
||||||
:root {
|
:root {
|
||||||
|
@ -90,6 +92,62 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.photos.page > figure.carousel {
|
||||||
|
position: relative;
|
||||||
|
max-width: 100%;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.photos.page > figure.carousel > ul {
|
||||||
|
align-items: stretch;
|
||||||
|
display: flex;
|
||||||
|
flex-flow: row nowrap;
|
||||||
|
gap: 0 calc(0.5 * var(--body-item-spacing));
|
||||||
|
margin: 0;
|
||||||
|
overflow-x: scroll;
|
||||||
|
scroll-snap-type: x mandatory;
|
||||||
|
padding: 0;
|
||||||
|
padding-inline: var(--body-item-spacing);
|
||||||
|
}
|
||||||
|
|
||||||
|
.photos.page > figure.carousel > ul::-webkit-scrollbar {
|
||||||
|
background: transparent;
|
||||||
|
width: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.photos.page > figure.carousel > .shadow {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
height: 100%;
|
||||||
|
width: var(--body-item-spacing);
|
||||||
|
z-index: 10;
|
||||||
|
}
|
||||||
|
|
||||||
|
.photos.page > figure.carousel > .shadow.left {
|
||||||
|
background: linear-gradient(
|
||||||
|
to right,
|
||||||
|
rgba(var(--carousel-gradient-full-opaque), 1),
|
||||||
|
rgba(var(--carousel-gradient-full-opaque), 0));
|
||||||
|
left: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.photos.page > figure.carousel > .shadow.right {
|
||||||
|
background: linear-gradient(
|
||||||
|
to left,
|
||||||
|
rgba(var(--carousel-gradient-full-opaque), 1),
|
||||||
|
rgba(var(--carousel-gradient-full-opaque), 0));
|
||||||
|
right: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.photos.page > figure.carousel > ul > li {
|
||||||
|
scroll-snap-align: center;
|
||||||
|
margin: 0;
|
||||||
|
width: var(--content-width);
|
||||||
|
flex-shrink: 0;
|
||||||
|
list-style: none;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
.photo-params {
|
.photo-params {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,10 +9,8 @@
|
||||||
<tr>
|
<tr>
|
||||||
{{ if and .Lat .Long }}
|
{{ if and .Lat .Long }}
|
||||||
<td colspan="2" class="location">
|
<td colspan="2" class="location">
|
||||||
{{ $lat := float .Lat }}{{ $latDir := cond (eq $lat 0) "" (cond (gt $lat 0) "N" "S") }}
|
<data class="latitude">{{ partial "photos/latitude.html" . }}</data>,
|
||||||
<data class="latitude" value="{{ $lat }}">{{ .Lat | lang.FormatNumber (cond (ne $lat 0) 3 0) }}º{{ $latDir }}</data>,
|
<data class="longitude">{{ partial "photos/longitude.html" . }}</data>
|
||||||
{{ $long := float .Long }}{{ $longDir := cond (eq $long 0) "" (cond (gt $long 0) "E" "W") }}
|
|
||||||
<data class="longitude" value="{{ $long }}">{{ .Long | lang.FormatNumber (cond (ne $long 0) 3 0) }}º{{ $longDir }}</data>
|
|
||||||
</td>
|
</td>
|
||||||
{{ end }}
|
{{ end }}
|
||||||
{{ if and .Tags.PixelXDimension .Tags.PixelYDimension }}
|
{{ if and .Tags.PixelXDimension .Tags.PixelYDimension }}
|
||||||
|
@ -20,8 +18,8 @@
|
||||||
{{ $widthpx := .Tags.PixelXDimension }}
|
{{ $widthpx := .Tags.PixelXDimension }}
|
||||||
{{ $heightpx := .Tags.PixelYDimension }}
|
{{ $heightpx := .Tags.PixelYDimension }}
|
||||||
{{ if and (gt $widthpx 0) (gt $heightpx 0) }}
|
{{ if and (gt $widthpx 0) (gt $heightpx 0) }}
|
||||||
{{ $megapixels := div (mul $widthpx $heightpx) 1e6 }}
|
<data class="megapixels">{{ partial "photos/megapixels.html" .Tags }}</data> • <data class="width">{{
|
||||||
<data value="{{ $megapixels }}">{{ $megapixels | lang.FormatNumber 0 }} MP</data> • {{ $widthpx }} × {{ $heightpx }}
|
$widthpx }}</data> × <data class="height">{{ $heightpx }}</data>
|
||||||
{{ end }}
|
{{ end }}
|
||||||
</td>
|
</td>
|
||||||
{{ end }}
|
{{ end }}
|
||||||
|
|
4
layouts/partials/photos/latitude.html
Normal file
4
layouts/partials/photos/latitude.html
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
{{ $lat := float .Lat }}
|
||||||
|
{{ $latDir := cond (eq $lat 0) "" (cond (gt $lat 0) "N" "S") }}
|
||||||
|
{{ $formattedLat := printf "%sº%s" (.Lat | lang.FormatNumber (cond (ne $lat 0) 3 0)) $latDir }}
|
||||||
|
{{ return $formattedLat }}
|
4
layouts/partials/photos/longitude.html
Normal file
4
layouts/partials/photos/longitude.html
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
{{ $long := float .Long }}
|
||||||
|
{{ $longDir := cond (eq $long 0) "" (cond (gt $long 0) "E" "W") }}
|
||||||
|
{{ $formattedLong := printf "%sº%s" (.Long | lang.FormatNumber (cond (ne $long 0) 3 0)) $longDir }}
|
||||||
|
{{ return $formattedLong }}
|
8
layouts/partials/photos/megapixels.html
Normal file
8
layouts/partials/photos/megapixels.html
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
{{ $widthpx := .PixelXDimension }}
|
||||||
|
{{ $heightpx := .PixelYDimension }}
|
||||||
|
{{ $megapixels := 0 }}
|
||||||
|
{{ if and (gt $widthpx 0) (gt $heightpx 0) }}
|
||||||
|
{{ $megapixels = div (mul $widthpx $heightpx) 1e6 }}
|
||||||
|
{{ end }}
|
||||||
|
{{ $formattedMegapixels := printf "%.0f MP" $megapixels }}
|
||||||
|
{{ return $formattedMegapixels }}
|
|
@ -12,29 +12,56 @@
|
||||||
{{ errorf "Missing photo from photos page %q" .Path }}
|
{{ errorf "Missing photo from photos page %q" .Path }}
|
||||||
{{ end }}
|
{{ end }}
|
||||||
|
|
||||||
|
{{- $firstImage := index $photos 0 -}}
|
||||||
{{ if eq (len $photos) 1 }}
|
{{ if eq (len $photos) 1 }}
|
||||||
{{- $img := index $photos 0 -}}
|
|
||||||
<figure><img src="{{ $img.RelPermalink }}"{{ with $img.Params.alt }} alt="{{ . }}"{{ end }}></figure>
|
|
||||||
|
|
||||||
{{ .Content }}
|
|
||||||
|
|
||||||
{{- if .Params.photo_details | default true -}}
|
|
||||||
{{- partial "photo_exif_table.html" $img.Exif -}}
|
|
||||||
|
|
||||||
{{- if in ($.Site.BaseURL | string) "localhost" -}}
|
|
||||||
{{- partial "development/photo_exif_table.html" $img.Exif -}}
|
|
||||||
{{- end -}}
|
|
||||||
{{- end -}}
|
|
||||||
{{ else }}
|
|
||||||
<figure>
|
<figure>
|
||||||
<ul class="carousel">
|
<img src="{{ $firstImage.RelPermalink }}"{{ with $firstImage.Params.alt }} alt="{{ . }}"{{ end }}>
|
||||||
|
</figure>
|
||||||
|
{{ else }}
|
||||||
|
<figure class="carousel">
|
||||||
|
<div class="shadow left"></div>
|
||||||
|
<div class="shadow right"></div>
|
||||||
|
<ul>
|
||||||
{{- range $photos -}}
|
{{- range $photos -}}
|
||||||
<li>{{ . }}</li>
|
<li data-title="{{ .Title }}"
|
||||||
|
{{- with .Exif -}}
|
||||||
|
data-latitude="{{ partial "photos/latitude.html" . }}"
|
||||||
|
data-longitude="{{ partial "photos/longitude.html" . }}"
|
||||||
|
{{- with .Tags -}}
|
||||||
|
data-make="{{ .Make }}"
|
||||||
|
data-model="{{ .Model }}"
|
||||||
|
data-iso="{{ .ISOSpeedRatings }}"
|
||||||
|
data-focal-length="{{ .FocalLengthIn35mmFilm }}"
|
||||||
|
data-f-number="{{ .FNumber }}"
|
||||||
|
data-exposure-time="{{ .ExposureTime }}"
|
||||||
|
data-megapixels="{{ partial "photos/megapixels.html" . }}"
|
||||||
|
data-width="{{ .PixelXDimension }}"
|
||||||
|
data-height="{{ .PixelYDimension }}"
|
||||||
|
{{- end -}}
|
||||||
|
{{- end -}}
|
||||||
|
>
|
||||||
|
<img src="{{ .RelPermalink }}"{{ with .Params.alt }} alt="{{ . }}"{{ end }}>
|
||||||
|
</li>
|
||||||
{{- end -}}
|
{{- end -}}
|
||||||
</ul>
|
</ul>
|
||||||
|
<figcaption>
|
||||||
|
{{ $firstImage.Title }}
|
||||||
|
</figcaption>
|
||||||
</figure>
|
</figure>
|
||||||
{{ end }}
|
{{ end }}
|
||||||
|
|
||||||
|
<section id="content">
|
||||||
|
{{ .Content }}
|
||||||
|
</section>
|
||||||
|
|
||||||
|
{{- if .Params.photo_details | default true -}}
|
||||||
|
{{- partial "photo_exif_table.html" $firstImage.Exif -}}
|
||||||
|
|
||||||
|
{{- if in ($.Site.BaseURL | string) "localhost" -}}
|
||||||
|
{{- partial "development/photo_exif_table.html" $firstImage.Exif -}}
|
||||||
|
{{- end -}}
|
||||||
|
{{- end -}}
|
||||||
|
|
||||||
<footer>
|
<footer>
|
||||||
{{ partial "footer_tags.html" . }}
|
{{ partial "footer_tags.html" . }}
|
||||||
</footer>
|
</footer>
|
||||||
|
@ -43,3 +70,12 @@
|
||||||
{{ define "footer" }}
|
{{ define "footer" }}
|
||||||
{{ partial "footer.html" . }}
|
{{ partial "footer.html" . }}
|
||||||
{{ end }}
|
{{ end }}
|
||||||
|
|
||||||
|
{{ define "scripts" }}
|
||||||
|
{{- $photos := partial "photos/list.html" . -}}
|
||||||
|
{{ if gt (len $photos) 1 }}
|
||||||
|
{{ with resources.Get "scripts/photo_carousel.js" | fingerprint "md5" }}
|
||||||
|
<script src="{{ .RelPermalink }}"></script>
|
||||||
|
{{ end }}
|
||||||
|
{{ end }}
|
||||||
|
{{ end }}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue