Add: Shortcode Gallery for posts
parent
6409f2c6c6
commit
27bcae92c5
|
@ -1,12 +1,18 @@
|
|||
baseURL: 'https://oscarmlage.com'
|
||||
languageCode: 'en-us'
|
||||
title: 'oscarmlage'
|
||||
theme: 'oscar'
|
||||
theme:
|
||||
- 'oscar'
|
||||
- 'hugo-shortcode-gallery'
|
||||
pygmentsStyle: "monokai"
|
||||
canonifyurls: true
|
||||
paginate: 15
|
||||
summarylength: 30
|
||||
|
||||
timeout: 60000
|
||||
params:
|
||||
galleryLoadJQuery: true
|
||||
|
||||
defaultContentLanguage: "en"
|
||||
|
||||
markup:
|
||||
|
|
|
@ -0,0 +1,134 @@
|
|||
# hugo-shortcode-gallery
|
||||
|
||||
This is a theme component for hugo.
|
||||
|
||||
This component contains a shortcode to include a gallery in your .md files.
|
||||
|
||||
The gallery is rendered using autogenerated thumbnails arranged in a
|
||||
[grid](http://miromannino.github.io/Justified-Gallery/). With a click on the images
|
||||
a [lightbox](http://brutaldesign.github.io/swipebox/) is opened an all images can be
|
||||
viewed fullscreen.
|
||||
|
||||
# Demo
|
||||
|
||||
You can see this shortcode-gallery in action on [my website](https://matze.rocks/images/).
|
||||
|
||||
## Installation
|
||||
|
||||
Clone this git repository into your *themes* folder.
|
||||
|
||||
```
|
||||
git clone https://github.com/mfg92/hugo-shortcode-gallery.git
|
||||
```
|
||||
|
||||
Next edit your projects
|
||||
*config.toml* and add this theme component to your themes:
|
||||
|
||||
```
|
||||
theme = ["your-main-theme", "hugo-shortcode-gallery"]
|
||||
```
|
||||
|
||||
To read about hugo's theme components and how to use them have a look at
|
||||
https://gohugo.io/hugo-modules/theme-components/.
|
||||
|
||||
|
||||
## Usage Example
|
||||
|
||||
Here is an usage example:
|
||||
|
||||
```
|
||||
{{< gallery match="images/*" sortOrder="desc" rowHeight="150" margins="5" thumbnailResizeOptions="600x600 q90 Lanczos" showExif=true previewType="blur" embedPreview="true" >}}
|
||||
```
|
||||
|
||||
This shortcode will generate a gallery containing all images of the folder *images*.
|
||||
The folder must be next to the .md file where this gallery is used in. This uses [page bundles](https://gohugo.io/content-management/page-bundles/)
|
||||
so the directory layout should look like this:
|
||||
|
||||
```
|
||||
new-post-name/
|
||||
index.md
|
||||
images/
|
||||
DSC_0001.jpg
|
||||
DSC_0002.jpg
|
||||
```
|
||||
|
||||
The parameter `sortOrder` decides whether the images are sorted ascending ("asc") or descending ("desc").
|
||||
|
||||
The `rowHeight` parameter determines the height of the rows that are displayed while the
|
||||
`margin` parameter defines the gap between the images.
|
||||
|
||||
A thumbnail is generated using the `thumbnailResizeOptions` parameter, they are handed over
|
||||
to *Hugo's* [image processing](https://gohugo.io/content-management/image-processing/)
|
||||
function using the fit method. In the example above, the generated thumbnails have a width of max 600 pixel and
|
||||
a height of max 600, the actual width and height depend on the original aspect ratio. The JPEG image quality is 90% and the
|
||||
scaling uses the high quality *Lanczos* filter.
|
||||
|
||||
If `previewType` is set to "blur" (or "color"), a very low resolution image (or a single pixel image) will be loaded for every image in the gallery first.
|
||||
The hight resolution thumbnail images (see `thumbnailResizeOptions`) will only be loaded if they are on the currently visible part of the page (or close to it).
|
||||
This leads to a faster loading page. You can set `previewType` to "none" to disable this feature and all thumbnails will be directly loaded.
|
||||
|
||||
Enable `embedPreview` to let hugo embed the tiny preview image directly in the page HTML as a base64 strings. This reduces the amount of required network round trip times.
|
||||
|
||||
The setting `thumbnailHoverEffect` configures what should happen when the mouse hovers above a thumbnail in the gallery.
|
||||
It defaults to "none", but it can be set to "enlarge", in that case the image is scaled up (x1.1) in a short smooth animation.
|
||||
|
||||
The size of the image as shown in the gallery can be customised using the (optional) `imageResizeOptions` parameter. The syntax is the same as for `thumbnailResizeOptions`. If ommited, the image will be displayed in its original size.
|
||||
|
||||
The setting `lastRow` configures the justification of the last row of the grid. When set to "justify", the entire grid including the last row will be fully-justified, right and left. This parameter respects all of the `lastRow` options of Justified Gallery, including "nojustify" and "hide".
|
||||
|
||||
When the users clicks on an image, a lightbox shows up displaying the clicked image in large using the whole available space.
|
||||
If the image contains a title/description in the EXIF metadata field _ImageDescription_ or a title is defined in the image's sidecar file (see section below) there will be a top bar displaying that.
|
||||
If the `showExif` option is set to `true` (without quotes), some parts of the image's EXIF data will be shown on the bottom bar e.g.: "Canon EOS 80D + EF100-400mm f/4.5-5.6L IS II USM 400mm f/8 1/400sec ISO 2500".
|
||||
The EXIF display will only work if you add following lines to your *config.toml*:
|
||||
```TOML
|
||||
[imaging.exif]
|
||||
includeFields = ".*"
|
||||
```
|
||||
|
||||
An advanced setting is `filterOptions`: It allows the user to filter the displayed images by using buttons.
|
||||
The text of the buttons and the regex used to filter has to be specified in a JSON array of objects. Currently it is only supported to filter by EXIF tags, image description, start rating or color labels. In the future it will be possible to filter by image name or other EXIF fields (pull requests are welcome). In addition to the metadata of the EXIF embedded in the image, a metadata sidecar file (see section below) can be used to add metadata for filtering.
|
||||
|
||||
Additionally to the filter buttons, a button to activate full screen mode of the gallery is added.
|
||||
|
||||
An example of the `filterOptions` JSON:
|
||||
```
|
||||
filterOptions="[{label: 'All', tags: '.*'}, {label: 'Birds', tags: 'bird'}, {label: 'Macro', tags: 'macro'}, {label: 'Insects', tags: 'insect'}]"
|
||||
```
|
||||
|
||||
When `filterOptions` is used, the switch `storeSelectedFilterInUrl` can be set to `true`. This will instruct the gallery to append the name of the filter to the url displayed in the browser when a filter button is clicked. This has two purposes: The user can share this link and recipients will see the gallery with the same filter as the original user. Furthermore the selected filter is stored in the browsers history.
|
||||
|
||||
As many websites/themes already include *jQuery*, this theme component will use the available *jQuery* lib.
|
||||
If the page does not already use *jQuery* the parameter `loadJQuery=true` must be used to
|
||||
instruct the theme component to load the provided *jQuery* lib.
|
||||
|
||||
All settings can be done globally in the site's *config.toml*, for that the prefix `gallery` has to be used. E.g. `galleryLoadJQuery` instead of `loadJQuery`.
|
||||
|
||||
## Sidecar files
|
||||
|
||||
The metadata embedded in a image can be extended/overshadowed by a metadata sidecar file. The file must have the same name as the image plus ".meta" (e.g. "image.jpg.meta"). The content has to be a *JSON* like:
|
||||
|
||||
```JSON
|
||||
{
|
||||
"Tags": ["macro","insect"],
|
||||
"Title": "Maya the Bee",
|
||||
"ColorLabels": "RG",
|
||||
"Rating": 3
|
||||
}
|
||||
```
|
||||
|
||||
## Requirements
|
||||
|
||||
This component requires a hugo version >= 0.59.
|
||||
|
||||
## Dependencies
|
||||
|
||||
The component uses (and includes) [*Justified Gallery*](http://miromannino.github.io/Justified-Gallery/)
|
||||
to render the images between the text and [*Swipebox*](http://brutaldesign.github.io/swipebox/)
|
||||
to show them full screen. These dependencies are included in this repository.
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
When bigger galleries are processed it can be required to set hugo's timeout property in the *config.toml* to a higher value:
|
||||
```
|
||||
timeout = 60000 # This is required for larger galleries to be build (60 sec)
|
||||
```
|
|
@ -0,0 +1,51 @@
|
|||
/* Changes made here sadly only apply after restarting hugo server */
|
||||
|
||||
/* make 5px space between the button(filter options) in the filter bar
|
||||
.justified-gallery-filterbar
|
||||
display: flex
|
||||
justify-content: flex-start
|
||||
flex-wrap: wrap
|
||||
gap: 5px
|
||||
|
||||
button
|
||||
padding: 6px
|
||||
border: 1px solid #fff
|
||||
border-radius: 5px
|
||||
background-color: transparent
|
||||
font-weight: bold
|
||||
color: #fff
|
||||
line-height: 1em
|
||||
|
||||
&:hover
|
||||
text-decoration: underline
|
||||
|
||||
&.selected
|
||||
text-decoration: underline
|
||||
background-color: #fff3
|
||||
|
||||
svg
|
||||
width: 1em
|
||||
height: 1em
|
||||
transform: rotate(90deg)
|
||||
transition: transform .2s linear
|
||||
|
||||
&:hover svg
|
||||
transform: rotate(90deg) scale(1.3)
|
||||
|
||||
|
||||
.fulltab
|
||||
position: absolute
|
||||
top: 0
|
||||
left: 0
|
||||
z-index: 100
|
||||
min-height: 100%
|
||||
background-color: #222
|
||||
|
||||
&.justified-gallery-filterbar
|
||||
position: sticky
|
||||
top: 0
|
||||
left: 0
|
||||
z-index: 101
|
||||
background-color: #222
|
||||
padding: 5px
|
||||
margin: 0px
|
|
@ -0,0 +1 @@
|
|||
<svg aria-hidden="true" focusable="false" data-prefix="fas" data-icon="compress-alt" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><path fill="currentColor" d="M4.686 427.314L104 328l-32.922-31.029C55.958 281.851 66.666 256 88.048 256h112C213.303 256 224 266.745 224 280v112c0 21.382-25.803 32.09-40.922 16.971L152 376l-99.314 99.314c-6.248 6.248-16.379 6.248-22.627 0L4.686 449.941c-6.248-6.248-6.248-16.379 0-22.627zM443.314 84.686L344 184l32.922 31.029c15.12 15.12 4.412 40.971-16.97 40.971h-112C234.697 256 224 245.255 224 232V120c0-21.382 25.803-32.09 40.922-16.971L296 136l99.314-99.314c6.248-6.248 16.379-6.248 22.627 0l25.373 25.373c6.248 6.248 6.248 16.379 0 22.627z"></path></svg>
|
After Width: | Height: | Size: 712 B |
|
@ -0,0 +1 @@
|
|||
<svg aria-hidden="true" focusable="false" data-prefix="fas" data-icon="expand-alt" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><path fill="currentColor" d="M212.686 315.314L120 408l32.922 31.029c15.12 15.12 4.412 40.971-16.97 40.971h-112C10.697 480 0 469.255 0 456V344c0-21.382 25.803-32.09 40.922-16.971L72 360l92.686-92.686c6.248-6.248 16.379-6.248 22.627 0l25.373 25.373c6.249 6.248 6.249 16.378 0 22.627zm22.628-118.628L328 104l-32.922-31.029C279.958 57.851 290.666 32 312.048 32h112C437.303 32 448 42.745 448 56v112c0 21.382-25.803 32.09-40.922 16.971L376 152l-92.686 92.686c-6.248 6.248-16.379 6.248-22.627 0l-25.373-25.373c-6.249-6.248-6.249-16.378 0-22.627z"></path></svg>
|
After Width: | Height: | Size: 704 B |
|
@ -0,0 +1 @@
|
|||
https://fontawesome.com/license/free
|
|
@ -0,0 +1,3 @@
|
|||
# this allows to use resources.Get to get resources of the folder "assets"
|
||||
# see https://github.com/gohugoio/hugo/commit/dea71670c059ab4d5a42bd22503f18c087dd22d4
|
||||
assetDir = "assets"
|
|
@ -0,0 +1,412 @@
|
|||
{{ $currentPage := . }}
|
||||
|
||||
{{ $images := (.Page.Resources.ByType "image") }}
|
||||
{{ if .Get "match"}}
|
||||
{{ $images = (.Page.Resources.Match (.Get "match")) }}
|
||||
{{ end }}
|
||||
|
||||
{{ $filterOptions := .Get "filterOptions" | default (.Site.Params.galleryFilterOptions | default "[]") }}
|
||||
{{ if not $filterOptions }}
|
||||
{{ $filterOptions = "[]" }}
|
||||
{{ end }}
|
||||
|
||||
{{ $storeSelectedFilterInUrl := .Get "storeSelectedFilterInUrl" | default (.Site.Params.storeSelectedFilterInUrl | default false) }}
|
||||
|
||||
{{ $sortOrder := .Get "sortOrder" | default (.Site.Params.gallerySortOrder | default "asc") }}
|
||||
|
||||
{{ $rowHeight := .Get "rowHeight" | default (.Site.Params.galleryRowHeight | default 150) }}
|
||||
|
||||
{{ $margins := .Get "margins" | default (.Site.Params.galleryRowMargins | default 5) }}
|
||||
|
||||
{{ $thumbnailResizeOptions := .Get "thumbnailResizeOptions" | default (.Site.Params.galleryThumbnailResizeOptions | default "300x150 q85 Lanczos") }}
|
||||
|
||||
{{ $imageResizeOptions := .Get "imageResizeOptions" | default .Site.Params.galleryImageResizeOptions }}
|
||||
|
||||
{{ $loadJQuery := .Get "loadJQuery" | default (.Site.Params.galleryLoadJQuery | default false) }}
|
||||
|
||||
{{ $showExif := .Get "showExif" | default (.Site.Params.galleryShowExif | default false) }}
|
||||
|
||||
{{ $justifiedGalleryParameters := .Get "justifiedGalleryParameters" | default (.Site.Params.galleryJustifiedGalleryParameters | default "") }}
|
||||
|
||||
{{ $previewType := .Get "previewType" | default (.Site.Params.galleryPreviewType | default "blur") }}
|
||||
|
||||
{{ $embedPreview := .Get "embedPreview" | default (.Site.Params.galleryEmbedPreview | default true) }}
|
||||
|
||||
{{ $thumbnailHoverEffect := .Get "thumbnailHoverEffect" | default (.Site.Params.galleryThumbnailHoverEffect | default "none") }}
|
||||
|
||||
<!-- hugos image processing saves images at resources/_gen/images, if the property resourceDir
|
||||
is changed in hugos config.toml file the images are save <resourceDir>/_gen/images.
|
||||
Because it is not possible to access the value of resourceDir, users who change resourceDir also have to change
|
||||
[params] resourceDir. -->
|
||||
{{ $thumbnailResourceDir := printf "%s%s" (.Site.Params.resourceDir | default "resources") "/_gen/images/" }}
|
||||
|
||||
<!-- Load jquery, jquery-lazy, swipebox and justified_gallery only once per page -->
|
||||
{{ if not (.Page.Scratch.Get "galleryLoaded") }}
|
||||
{{ .Page.Scratch.Set "galleryLoaded" true }}
|
||||
<!-- Use relURL to support hugo projects that run in a subdirectory (bug #18) -->
|
||||
|
||||
{{ if $loadJQuery }}
|
||||
<script src="{{ "/shortcode-gallery/jquery-3.6.0.min.js" | relURL }}"></script>
|
||||
{{ end }}
|
||||
|
||||
{{ if not (eq $previewType "none") }}
|
||||
<script src="{{ "/shortcode-gallery/lazy/jquery.lazy.min.js" | relURL }}"></script>
|
||||
{{ end }}
|
||||
|
||||
<script src="{{ "/shortcode-gallery/swipebox/js/jquery.swipebox.min.js" | relURL }}"></script>
|
||||
<link rel="stylesheet" href="{{ "/shortcode-gallery/swipebox/css/swipebox.min.css"| relURL }}">
|
||||
|
||||
<script src="{{ "/shortcode-gallery/justified_gallery/jquery.justifiedGallery.min.js"| relURL }}"></script>
|
||||
<link rel="stylesheet" href="{{ "/shortcode-gallery/justified_gallery/justifiedGallery.min.css"| relURL }}"/>
|
||||
{{ end }}
|
||||
|
||||
<style>
|
||||
{{ if (eq $thumbnailHoverEffect "enlarge") }}
|
||||
.jg-entry img {
|
||||
transition: transform .25s ease-in-out !important;
|
||||
}
|
||||
|
||||
.jg-entry img:hover {
|
||||
transform: scale(1.1);
|
||||
}
|
||||
{{ end }}
|
||||
|
||||
{{ if not (eq $filterOptions "[]") }}
|
||||
/* inline css from assets folder */
|
||||
{{ (resources.Get "shortcode-gallery/filterbar.sass" | toCSS).Content | safeCSS }}
|
||||
{{ end }}
|
||||
</style>
|
||||
|
||||
<!--
|
||||
Ordinal increases every time this shortcode is used in a document
|
||||
Ordinal: {{ .Ordinal}}
|
||||
-->
|
||||
{{ $galleryId := (printf "gallery-%v-%v" .Page.File.UniqueID .Ordinal)}}
|
||||
{{ $galleryWrapperId := (printf "gallery-%v-%v-wrapper" .Page.File.UniqueID .Ordinal)}}
|
||||
|
||||
<div id="{{ $galleryWrapperId }}" class="gallery-wrapper">
|
||||
<div id="{{ $galleryId }}" class="justified-gallery">
|
||||
{{ range $original := sort $images "Name" $sortOrder}}
|
||||
{{ if eq $original.ResourceType "image" }}
|
||||
|
||||
{{/* Get metadata from sidecar file, if present. Else an empty dictionary is used. */}}
|
||||
{{ $metaFileName := print $original.Name ".meta"}}
|
||||
{{ $metadata := $currentPage.Page.Resources.GetMatch ($metaFileName) }}
|
||||
{{ if $metadata }}
|
||||
{{ $metadata = $metadata.Content }}
|
||||
{{ $metadata = $metadata | unmarshal }}
|
||||
{{ else }}
|
||||
{{ $metadata = dict }}
|
||||
{{ end }}
|
||||
|
||||
{{/* If the image has exif informations, those are merged together with the metadata from the file */}}
|
||||
{{ if in "jpg jpeg tiff" $original.MediaType.SubType }}
|
||||
{{ with $original.Exif }}
|
||||
{{ $metadata = merge .Tags $metadata }}
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
|
||||
|
||||
{{/* Compute rotation to correct orientation of thumbnail (and downscaled gallery images) in case the exif contains a value for orientation. See issue #22. */}}
|
||||
{{ $rotation := "" }}
|
||||
{{ with $metadata.Orientation }}
|
||||
{{/* Exif orientation is explained here: https://www.impulseadventure.com/photo/exif-orientation.html */}}
|
||||
{{/* Example images can be found here: https://github.com/recurser/exif-orientation-examples*/}}
|
||||
{{/* We can fix orientation 3, 6 and 8 by rotating. */}}
|
||||
{{/* To fix orientation 2, 4, 5, 6 we would need to flip the image, sadly hugo can not do that. */}}
|
||||
{{/* So we can only fix them a "bit" by rotating them but they will be mirrored. */}}
|
||||
{{/* An orientation of 2 means that the image only needs to be flipped so we do nothing in that case. */}}
|
||||
{{/* An orientation of 1 means that the image has the correct rotation and is not mirrored. */}}
|
||||
{{ if or (eq . 8) (eq . 7) }}
|
||||
{{ $rotation = " r90" }}
|
||||
{{ else if or (eq . 3) (eq . 4) }}
|
||||
{{ $rotation = " r180" }}
|
||||
{{ else if or (eq . 6) (eq . 5) }}
|
||||
{{ $rotation = " r270" }}
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
|
||||
{{/* Create thumbnail, rotate it if needed */}}
|
||||
{{ $thumbnail := ($original.Fit (printf "%s %s" $thumbnailResizeOptions $rotation)) }}
|
||||
|
||||
|
||||
<div>
|
||||
{{ $full := "" }}
|
||||
{{ if $imageResizeOptions }}
|
||||
{{/* Downscale gallery image, rotate it if needed */}}
|
||||
{{ $full = ($original.Fit (printf "%s %s" $imageResizeOptions $rotation)) }}
|
||||
{{ else }}
|
||||
{{ $full = $original }}
|
||||
{{ end }}
|
||||
<a href="{{ $full.RelPermalink }}"
|
||||
class="galleryImg"
|
||||
{{ with $metadata }}
|
||||
{{ if .Title }}
|
||||
title="{{ .Title }}"
|
||||
{{ else if .ImageDescription }}
|
||||
title="{{ .ImageDescription }}"
|
||||
{{ end }}
|
||||
|
||||
{{ if $showExif }}
|
||||
data-description="{{ .Model }} + {{ .LensModel }}<br/>{{ .FocalLength }}mm f/{{ .FNumber }} {{ .ExposureTime }}sec ISO {{ .ISOSpeedRatings }}"
|
||||
{{ end }}
|
||||
|
||||
{{ if not (eq $filterOptions "[]") }}
|
||||
{{/* only include fields that are currently supported by the filter mechanism (in JS at the end of the file) */}}
|
||||
data-meta="{{ (dict "Tags" $metadata.Tags "Rating" $metadata.Rating "ColorLabels" $metadata.ColorLabels "ImageDescription" $metadata.ImageDescription) | jsonify }}"
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
>
|
||||
<img
|
||||
width="{{ $thumbnail.Width }}" height="{{ $thumbnail.Height }}"
|
||||
|
||||
{{ if (eq $previewType "blur") }}
|
||||
{{ $preview_b := ($thumbnail.Fit ("32x32 q70 box jpg")) }}
|
||||
style="filter: blur(25px);"
|
||||
{{ if $embedPreview }}
|
||||
src="data:image/jpeg;base64,{{ $preview_b.Content | base64Encode }}"
|
||||
{{ else }}
|
||||
src="{{ $preview_b.RelPermalink }}"
|
||||
{{ end }}
|
||||
class="lazy"
|
||||
data-src="{{ $thumbnail.RelPermalink }}"
|
||||
{{ else if (eq $previewType "color") }}
|
||||
{{ $preview_1p := ($thumbnail.Resize ("1x1 box png")) }}
|
||||
{{ if $embedPreview }}
|
||||
src="data:image/png;base64,{{ $preview_1p.Content | base64Encode }}"
|
||||
{{ else }}
|
||||
src="{{ $preview_1p.RelPermalink }}"
|
||||
{{ end }}
|
||||
class="lazy"
|
||||
data-src="{{ $thumbnail.RelPermalink }}"
|
||||
{{ else }}
|
||||
src="{{ $thumbnail.RelPermalink }}"
|
||||
{{ end }}
|
||||
|
||||
{{ with $metadata }}
|
||||
{{ if .ImageDescription }}
|
||||
alt="{{ .ImageDescription }}"
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
>
|
||||
</a>
|
||||
</div>
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
if (!jQuery) {
|
||||
alert("jquery is not loaded");
|
||||
}
|
||||
|
||||
$( document ).ready(() => {
|
||||
const gallery = $("#{{ $galleryId }}");
|
||||
{{ $lastRowJustification := .Get "lastRow" | default (.Site.Params.galleryLastRow | default "justify") }}
|
||||
|
||||
// the instance of swipebox, it will be set once justifiedGallery is initialized
|
||||
let swipeboxInstance = null;
|
||||
|
||||
// before the gallery initialization the listener has to be added
|
||||
// else we can get a race condition and the listener is never called
|
||||
gallery.on('jg.complete', () => {
|
||||
{{ if or (eq $previewType "blur") (eq $previewType "color") }}
|
||||
// if there is already some low resolution image data loaded, then we will wait for loading´
|
||||
// the hi-res until the justified gallery has done the layout
|
||||
$(() => {
|
||||
$('.lazy').Lazy({
|
||||
visibleOnly: true,
|
||||
afterLoad: element => element.css({filter: "none", transition: "filter 1.0s ease-in-out"})
|
||||
});
|
||||
});
|
||||
{{ end }}
|
||||
|
||||
swipeboxInstance = $('.galleryImg').swipebox(
|
||||
jQuery.extend({},
|
||||
{ {{ $justifiedGalleryParameters | safeJS }} }
|
||||
)
|
||||
);
|
||||
});
|
||||
|
||||
// initialize the justified gallery
|
||||
gallery.justifiedGallery({
|
||||
rowHeight : {{ $rowHeight }},
|
||||
margins : {{ $margins }},
|
||||
border : 0,
|
||||
waitThumbnailsLoad : false,
|
||||
lastRow : {{ $lastRowJustification }},
|
||||
captions : false,
|
||||
// if there is at least one filter option
|
||||
{{ if not (eq $filterOptions "[]") }}
|
||||
// we first show no images at all
|
||||
// till the code way below selects a filter and applies it
|
||||
// this prevent creating the layout twice
|
||||
filter : () => false
|
||||
{{ end }}
|
||||
});
|
||||
|
||||
// only include JS code for filter options if there at least one filter option
|
||||
{{ if not (eq $filterOptions "[]") }}
|
||||
|
||||
// this function can be used to create a function that can be used by justifiedGallery
|
||||
// for filtering images by their metadata
|
||||
function createMetadataFilter(filterFunction) {
|
||||
return (entry, index, array) => {
|
||||
let meta = $(entry).find("a").attr("data-meta");
|
||||
meta = meta ? JSON.parse(meta) : {};
|
||||
|
||||
let include = filterFunction(meta);
|
||||
|
||||
// only those images visible in justified gallery should be displayed
|
||||
// in swipebox (only <a> with class galleryImg are displayed in swipebox)
|
||||
$(entry).find("a").toggleClass("galleryImg", include);
|
||||
|
||||
return include;
|
||||
}
|
||||
}
|
||||
|
||||
// this function returns a function that can be used by justifiedGallery
|
||||
// for filtering images by their tags
|
||||
function createTagFilter(tagsRegexString) {
|
||||
const tagsRegex = RegExp(tagsRegexString);
|
||||
return createMetadataFilter(meta => {
|
||||
let tags = meta.Tags;
|
||||
tags = tags ? tags : [];
|
||||
return tags.some(tag => tagsRegex.test(tag));
|
||||
});
|
||||
};
|
||||
|
||||
// this function returns a function that can be used by justifiedGallery
|
||||
// for filtering images by their description
|
||||
function createImageDescriptionFilter(descriptionRegexString) {
|
||||
const descriptionRegex = RegExp(descriptionRegexString);
|
||||
return createMetadataFilter(meta => {
|
||||
let imageDescription = meta.ImageDescription;
|
||||
return imageDescription !== null && descriptionRegex.test(imageDescription);
|
||||
});
|
||||
};
|
||||
|
||||
// this function returns a function that can be used by justifiedGallery
|
||||
// for filtering images by their star rating
|
||||
function createRatingFilter(min, max) {
|
||||
return createMetadataFilter(meta => {
|
||||
let rating = meta.Rating;
|
||||
if(rating === null){
|
||||
rating = -1;
|
||||
}
|
||||
return rating >= min && rating <= max;
|
||||
});
|
||||
};
|
||||
|
||||
// this function returns a function that can be used by justifiedGallery
|
||||
// for filtering images by their color labels
|
||||
function createColorLabelFilter(color) {
|
||||
color = color.charAt(0).toLowerCase()
|
||||
return createMetadataFilter(meta => {
|
||||
let colors = meta.ColorLabels;
|
||||
return colors && colors.includes(color);
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
const filterOptions = {{ $filterOptions | safeJS }};
|
||||
|
||||
// insert a div for inserting filter buttons before the gallery
|
||||
const filterBar = $("<div class='justified-gallery-filterbar'/>");
|
||||
gallery.before(filterBar);
|
||||
|
||||
var wrapper = document.getElementById("{{ $galleryWrapperId }}");
|
||||
|
||||
// inline svg icons
|
||||
const expandIcon = '{{ (resources.Get "shortcode-gallery/font-awesome/expand-alt-solid.svg").Content | safeHTML }}';
|
||||
const compressIcon = '{{ (resources.Get "shortcode-gallery/font-awesome/compress-alt-solid.svg").Content | safeHTML }}';
|
||||
|
||||
function setFulltab(activate) {
|
||||
if(activate == wrapper.classList.contains("fulltab")){
|
||||
return; // nothing to do, we are already in the right state
|
||||
}
|
||||
|
||||
wrapper.classList.toggle("fulltab");
|
||||
gallery.justifiedGallery({
|
||||
rowHeight : {{ $rowHeight }} * (activate ? 1.5 : 1.0),
|
||||
lastRow: (activate ? "nojustify": {{ $lastRowJustification }}),
|
||||
// force justifiedGallery to refresh
|
||||
refreshTime: 0,
|
||||
});
|
||||
// force justifiedGallery to refresh
|
||||
gallery.data('jg.controller').startImgAnalyzer();
|
||||
fullTabButton.html(wrapper.classList.contains("fulltab") ? compressIcon : expandIcon)
|
||||
};
|
||||
|
||||
const fullTabButton = $("<button/>");
|
||||
fullTabButton.html(expandIcon);
|
||||
fullTabButton.click(() => setFulltab(!wrapper.classList.contains("fulltab")));
|
||||
filterBar.append(fullTabButton);
|
||||
$(document).keyup(e => {
|
||||
// when ESC is pressed
|
||||
if (e.keyCode === 27){
|
||||
setFulltab(false);
|
||||
}
|
||||
});
|
||||
|
||||
function activateFilterButton(filterButton) {
|
||||
// activate associated filter
|
||||
gallery.justifiedGallery({filter : filterButton.filter});
|
||||
// remove select class from all other selected buttons
|
||||
filterBar.find('.selected').removeClass('selected');
|
||||
filterButton.addClass("selected");
|
||||
};
|
||||
|
||||
// check if the url contains an instruction to apply a specific filter
|
||||
// eg. example.com/images/#gallery-filter=Birds
|
||||
const params = new URLSearchParams(location.hash.replace(/^\#/,""));
|
||||
let activeFilter = params.get('gallery-filter');
|
||||
if (!activeFilter) {
|
||||
// default to first filter
|
||||
activeFilter = filterOptions[0].label;
|
||||
}
|
||||
|
||||
// create a button for each filter entry
|
||||
filterOptions.forEach(filterConfig => {
|
||||
let filter; // create a filter function based on the available attributes of filterConfig
|
||||
if(filterConfig.tags) {
|
||||
filter = createTagFilter(filterConfig.tags);
|
||||
} else if(filterConfig.rating){
|
||||
if(filterConfig.rating.includes("-")){
|
||||
minMax = filterConfig.rating.split("-"); // e.g. "3-5"
|
||||
} else {
|
||||
minMax = [filterConfig.rating, filterConfig.rating]; // e.g. "4"
|
||||
}
|
||||
filter = createRatingFilter(parseInt(minMax[0]), parseInt(minMax[1]));
|
||||
} else if(filterConfig.color_label){
|
||||
filter = createColorLabelFilter(filterConfig.color_label);
|
||||
} else if(filterConfig.description){
|
||||
filter = createImageDescriptionFilter(filterConfig.description);
|
||||
} else {
|
||||
// default to always true filter
|
||||
filter = createMetadataFilter(meta => true);
|
||||
}
|
||||
|
||||
const filterButton = $("<button/>");
|
||||
filterButton.text(filterConfig.label);
|
||||
filterButton.filter = filter;
|
||||
filterButton.click(() => {
|
||||
activateFilterButton(filterButton);
|
||||
|
||||
{{ if $storeSelectedFilterInUrl }}
|
||||
// save applied filter in browser url
|
||||
const params = new URLSearchParams(location.hash.replace(/^\#/,""));
|
||||
params.set('gallery-filter', filterConfig.label);+
|
||||
window.history.replaceState("", "", location.pathname + location.search + "#" + params.toString());
|
||||
{{ end }}
|
||||
});
|
||||
filterBar.append(filterButton);
|
||||
|
||||
if(filterConfig.label.toLowerCase() === activeFilter.toLowerCase()) {
|
||||
activateFilterButton(filterButton);
|
||||
}
|
||||
});
|
||||
{{ end }}
|
||||
});
|
||||
</script>
|
2
src/themes/hugo-shortcode-gallery/static/shortcode-gallery/jquery-3.6.0.min.js
vendored
Normal file
2
src/themes/hugo-shortcode-gallery/static/shortcode-gallery/jquery-3.6.0.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
|
@ -0,0 +1,22 @@
|
|||
Copyright (c) 2018 Miro Mannino
|
||||
|
||||
Permission is hereby granted, free of charge, to any person
|
||||
obtaining a copy of this software and associated documentation
|
||||
files (the "Software"), to deal in the Software without
|
||||
restriction, including without limitation the rights to use,
|
||||
copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the
|
||||
Software is furnished to do so, subject to the following
|
||||
conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
||||
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
OTHER DEALINGS IN THE SOFTWARE.
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
|
@ -0,0 +1,96 @@
|
|||
/*!
|
||||
* justifiedGallery - v3.7.0
|
||||
* http://miromannino.github.io/Justified-Gallery/
|
||||
* Copyright (c) 2018 Miro Mannino
|
||||
* Licensed under the MIT license.
|
||||
*/
|
||||
.justified-gallery {
|
||||
width: 100%;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
.justified-gallery > a,
|
||||
.justified-gallery > div,
|
||||
.justified-gallery > figure {
|
||||
position: absolute;
|
||||
display: inline-block;
|
||||
overflow: hidden;
|
||||
/* background: #888888; To have gray placeholders while the gallery is loading with waitThumbnailsLoad = false */
|
||||
filter: "alpha(opacity=10)";
|
||||
opacity: 0.1;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
.justified-gallery > a > img,
|
||||
.justified-gallery > div > img,
|
||||
.justified-gallery > figure > img,
|
||||
.justified-gallery > a > a > img,
|
||||
.justified-gallery > div > a > img,
|
||||
.justified-gallery > figure > a > img {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
border: none;
|
||||
filter: "alpha(opacity=0)";
|
||||
opacity: 0;
|
||||
}
|
||||
.justified-gallery > a > .caption,
|
||||
.justified-gallery > div > .caption,
|
||||
.justified-gallery > figure > .caption {
|
||||
display: none;
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
padding: 5px;
|
||||
background-color: #000000;
|
||||
left: 0;
|
||||
right: 0;
|
||||
margin: 0;
|
||||
color: white;
|
||||
font-size: 12px;
|
||||
font-weight: 300;
|
||||
font-family: sans-serif;
|
||||
}
|
||||
.justified-gallery > a > .caption.caption-visible,
|
||||
.justified-gallery > div > .caption.caption-visible,
|
||||
.justified-gallery > figure > .caption.caption-visible {
|
||||
display: initial;
|
||||
filter: "alpha(opacity=70)";
|
||||
opacity: 0.7;
|
||||
transition: opacity 500ms ease-in;
|
||||
}
|
||||
.justified-gallery > .entry-visible {
|
||||
filter: "alpha(opacity=100)";
|
||||
opacity: 1;
|
||||
background: none;
|
||||
}
|
||||
.justified-gallery > .entry-visible > img,
|
||||
.justified-gallery > .entry-visible > a > img {
|
||||
filter: "alpha(opacity=100)";
|
||||
opacity: 1;
|
||||
transition: opacity 500ms ease-in;
|
||||
}
|
||||
.justified-gallery > .jg-filtered {
|
||||
display: none;
|
||||
}
|
||||
.justified-gallery > .spinner {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
margin-left: -24px;
|
||||
padding: 10px 0 10px 0;
|
||||
left: 50%;
|
||||
filter: "alpha(opacity=100)";
|
||||
opacity: 1;
|
||||
overflow: initial;
|
||||
}
|
||||
.justified-gallery > .spinner > span {
|
||||
display: inline-block;
|
||||
filter: "alpha(opacity=0)";
|
||||
opacity: 0;
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
margin: 0 4px 0 4px;
|
||||
background-color: #000;
|
||||
border-radius: 6px;
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
/*!
|
||||
* justifiedGallery - v3.7.0
|
||||
* http://miromannino.github.io/Justified-Gallery/
|
||||
* Copyright (c) 2018 Miro Mannino
|
||||
* Licensed under the MIT license.
|
||||
*/.justified-gallery{width:100%;position:relative;overflow:hidden}.justified-gallery>a,.justified-gallery>div,.justified-gallery>figure{position:absolute;display:inline-block;overflow:hidden;filter:"alpha(opacity=10)";opacity:.1;margin:0;padding:0}.justified-gallery>a>a>img,.justified-gallery>a>img,.justified-gallery>div>a>img,.justified-gallery>div>img,.justified-gallery>figure>a>img,.justified-gallery>figure>img{position:absolute;top:50%;left:50%;margin:0;padding:0;border:none;filter:"alpha(opacity=0)";opacity:0}.justified-gallery>a>.caption,.justified-gallery>div>.caption,.justified-gallery>figure>.caption{display:none;position:absolute;bottom:0;padding:5px;background-color:#000;left:0;right:0;margin:0;color:#fff;font-size:12px;font-weight:300;font-family:sans-serif}.justified-gallery>a>.caption.caption-visible,.justified-gallery>div>.caption.caption-visible,.justified-gallery>figure>.caption.caption-visible{display:initial;filter:"alpha(opacity=70)";opacity:.7;transition:opacity .5s ease-in}.justified-gallery>.entry-visible{filter:"alpha(opacity=100)";opacity:1;background:0 0}.justified-gallery>.entry-visible>a>img,.justified-gallery>.entry-visible>img{filter:"alpha(opacity=100)";opacity:1;transition:opacity .5s ease-in}.justified-gallery>.jg-filtered{display:none}.justified-gallery>.spinner{position:absolute;bottom:0;margin-left:-24px;padding:10px 0;left:50%;filter:"alpha(opacity=100)";opacity:1;overflow:initial}.justified-gallery>.spinner>span{display:inline-block;filter:"alpha(opacity=0)";opacity:0;width:8px;height:8px;margin:0 4px;background-color:#000;border-radius:6px}
|
|
@ -0,0 +1,872 @@
|
|||
/*!
|
||||
* jQuery & Zepto Lazy - v1.7.10
|
||||
* http://jquery.eisbehr.de/lazy/
|
||||
*
|
||||
* Copyright 2012 - 2018, Daniel 'Eisbehr' Kern
|
||||
*
|
||||
* Dual licensed under the MIT and GPL-2.0 licenses:
|
||||
* http://www.opensource.org/licenses/mit-license.php
|
||||
* http://www.gnu.org/licenses/gpl-2.0.html
|
||||
*
|
||||
* $("img.lazy").lazy();
|
||||
*/
|
||||
|
||||
;(function(window, undefined) {
|
||||
"use strict";
|
||||
|
||||
// noinspection JSUnresolvedVariable
|
||||
/**
|
||||
* library instance - here and not in construct to be shorter in minimization
|
||||
* @return void
|
||||
*/
|
||||
var $ = window.jQuery || window.Zepto,
|
||||
|
||||
/**
|
||||
* unique plugin instance id counter
|
||||
* @type {number}
|
||||
*/
|
||||
lazyInstanceId = 0,
|
||||
|
||||
/**
|
||||
* helper to register window load for jQuery 3
|
||||
* @type {boolean}
|
||||
*/
|
||||
windowLoaded = false;
|
||||
|
||||
/**
|
||||
* make lazy available to jquery - and make it a bit more case-insensitive :)
|
||||
* @access public
|
||||
* @type {function}
|
||||
* @param {object} settings
|
||||
* @return {LazyPlugin}
|
||||
*/
|
||||
$.fn.Lazy = $.fn.lazy = function(settings) {
|
||||
return new LazyPlugin(this, settings);
|
||||
};
|
||||
|
||||
/**
|
||||
* helper to add plugins to lazy prototype configuration
|
||||
* @access public
|
||||
* @type {function}
|
||||
* @param {string|Array} names
|
||||
* @param {string|Array|function} [elements]
|
||||
* @param {function} loader
|
||||
* @return void
|
||||
*/
|
||||
$.Lazy = $.lazy = function(names, elements, loader) {
|
||||
// make second parameter optional
|
||||
if ($.isFunction(elements)) {
|
||||
loader = elements;
|
||||
elements = [];
|
||||
}
|
||||
|
||||
// exit here if parameter is not a callable function
|
||||
if (!$.isFunction(loader)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// make parameters an array of names to be sure
|
||||
names = $.isArray(names) ? names : [names];
|
||||
elements = $.isArray(elements) ? elements : [elements];
|
||||
|
||||
var config = LazyPlugin.prototype.config,
|
||||
forced = config._f || (config._f = {});
|
||||
|
||||
// add the loader plugin for every name
|
||||
for (var i = 0, l = names.length; i < l; i++) {
|
||||
if (config[names[i]] === undefined || $.isFunction(config[names[i]])) {
|
||||
config[names[i]] = loader;
|
||||
}
|
||||
}
|
||||
|
||||
// add forced elements loader
|
||||
for (var c = 0, a = elements.length; c < a; c++) {
|
||||
forced[elements[c]] = names[0];
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* contains all logic and the whole element handling
|
||||
* is packed in a private function outside class to reduce memory usage, because it will not be created on every plugin instance
|
||||
* @access private
|
||||
* @type {function}
|
||||
* @param {LazyPlugin} instance
|
||||
* @param {object} config
|
||||
* @param {object|Array} items
|
||||
* @param {object} events
|
||||
* @param {string} namespace
|
||||
* @return void
|
||||
*/
|
||||
function _executeLazy(instance, config, items, events, namespace) {
|
||||
/**
|
||||
* a helper to trigger the 'onFinishedAll' callback after all other events
|
||||
* @access private
|
||||
* @type {number}
|
||||
*/
|
||||
var _awaitingAfterLoad = 0,
|
||||
|
||||
/**
|
||||
* visible content width
|
||||
* @access private
|
||||
* @type {number}
|
||||
*/
|
||||
_actualWidth = -1,
|
||||
|
||||
/**
|
||||
* visible content height
|
||||
* @access private
|
||||
* @type {number}
|
||||
*/
|
||||
_actualHeight = -1,
|
||||
|
||||
/**
|
||||
* determine possibly detected high pixel density
|
||||
* @access private
|
||||
* @type {boolean}
|
||||
*/
|
||||
_isRetinaDisplay = false,
|
||||
|
||||
/**
|
||||
* dictionary entry for better minimization
|
||||
* @access private
|
||||
* @type {string}
|
||||
*/
|
||||
_afterLoad = 'afterLoad',
|
||||
|
||||
/**
|
||||
* dictionary entry for better minimization
|
||||
* @access private
|
||||
* @type {string}
|
||||
*/
|
||||
_load = 'load',
|
||||
|
||||
/**
|
||||
* dictionary entry for better minimization
|
||||
* @access private
|
||||
* @type {string}
|
||||
*/
|
||||
_error = 'error',
|
||||
|
||||
/**
|
||||
* dictionary entry for better minimization
|
||||
* @access private
|
||||
* @type {string}
|
||||
*/
|
||||
_img = 'img',
|
||||
|
||||
/**
|
||||
* dictionary entry for better minimization
|
||||
* @access private
|
||||
* @type {string}
|
||||
*/
|
||||
_src = 'src',
|
||||
|
||||
/**
|
||||
* dictionary entry for better minimization
|
||||
* @access private
|
||||
* @type {string}
|
||||
*/
|
||||
_srcset = 'srcset',
|
||||
|
||||
/**
|
||||
* dictionary entry for better minimization
|
||||
* @access private
|
||||
* @type {string}
|
||||
*/
|
||||
_sizes = 'sizes',
|
||||
|
||||
/**
|
||||
* dictionary entry for better minimization
|
||||
* @access private
|
||||
* @type {string}
|
||||
*/
|
||||
_backgroundImage = 'background-image';
|
||||
|
||||
/**
|
||||
* initialize plugin
|
||||
* bind loading to events or set delay time to load all items at once
|
||||
* @access private
|
||||
* @return void
|
||||
*/
|
||||
function _initialize() {
|
||||
// detect actual device pixel ratio
|
||||
// noinspection JSUnresolvedVariable
|
||||
_isRetinaDisplay = window.devicePixelRatio > 1;
|
||||
|
||||
// prepare all initial items
|
||||
items = _prepareItems(items);
|
||||
|
||||
// if delay time is set load all items at once after delay time
|
||||
if (config.delay >= 0) {
|
||||
setTimeout(function() {
|
||||
_lazyLoadItems(true);
|
||||
}, config.delay);
|
||||
}
|
||||
|
||||
// if no delay is set or combine usage is active bind events
|
||||
if (config.delay < 0 || config.combined) {
|
||||
// create unique event function
|
||||
events.e = _throttle(config.throttle, function(event) {
|
||||
// reset detected window size on resize event
|
||||
if (event.type === 'resize') {
|
||||
_actualWidth = _actualHeight = -1;
|
||||
}
|
||||
|
||||
// execute 'lazy magic'
|
||||
_lazyLoadItems(event.all);
|
||||
});
|
||||
|
||||
// create function to add new items to instance
|
||||
events.a = function(additionalItems) {
|
||||
additionalItems = _prepareItems(additionalItems);
|
||||
items.push.apply(items, additionalItems);
|
||||
};
|
||||
|
||||
// create function to get all instance items left
|
||||
events.g = function() {
|
||||
// filter loaded items before return in case internal filter was not running until now
|
||||
return (items = $(items).filter(function() {
|
||||
return !$(this).data(config.loadedName);
|
||||
}));
|
||||
};
|
||||
|
||||
// create function to force loading elements
|
||||
events.f = function(forcedItems) {
|
||||
for (var i = 0; i < forcedItems.length; i++) {
|
||||
// only handle item if available in current instance
|
||||
// use a compare function, because Zepto can't handle object parameter for filter
|
||||
// var item = items.filter(forcedItems[i]);
|
||||
/* jshint loopfunc: true */
|
||||
var item = items.filter(function() {
|
||||
return this === forcedItems[i];
|
||||
});
|
||||
|
||||
if (item.length) {
|
||||
_lazyLoadItems(false, item);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// load initial items
|
||||
_lazyLoadItems();
|
||||
|
||||
// bind lazy load functions to scroll and resize event
|
||||
// noinspection JSUnresolvedVariable
|
||||
$(config.appendScroll).on('scroll.' + namespace + ' resize.' + namespace, events.e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* prepare items before handle them
|
||||
* @access private
|
||||
* @param {Array|object|jQuery} items
|
||||
* @return {Array|object|jQuery}
|
||||
*/
|
||||
function _prepareItems(items) {
|
||||
// fetch used configurations before loops
|
||||
var defaultImage = config.defaultImage,
|
||||
placeholder = config.placeholder,
|
||||
imageBase = config.imageBase,
|
||||
srcsetAttribute = config.srcsetAttribute,
|
||||
loaderAttribute = config.loaderAttribute,
|
||||
forcedTags = config._f || {};
|
||||
|
||||
// filter items and only add those who not handled yet and got needed attributes available
|
||||
items = $(items).filter(function() {
|
||||
var element = $(this),
|
||||
tag = _getElementTagName(this);
|
||||
|
||||
return !element.data(config.handledName) &&
|
||||
(element.attr(config.attribute) || element.attr(srcsetAttribute) || element.attr(loaderAttribute) || forcedTags[tag] !== undefined);
|
||||
})
|
||||
|
||||
// append plugin instance to all elements
|
||||
.data('plugin_' + config.name, instance);
|
||||
|
||||
for (var i = 0, l = items.length; i < l; i++) {
|
||||
var element = $(items[i]),
|
||||
tag = _getElementTagName(items[i]),
|
||||
elementImageBase = element.attr(config.imageBaseAttribute) || imageBase;
|
||||
|
||||
// generate and update source set if an image base is set
|
||||
if (tag === _img && elementImageBase && element.attr(srcsetAttribute)) {
|
||||
element.attr(srcsetAttribute, _getCorrectedSrcSet(element.attr(srcsetAttribute), elementImageBase));
|
||||
}
|
||||
|
||||
// add loader to forced element types
|
||||
if (forcedTags[tag] !== undefined && !element.attr(loaderAttribute)) {
|
||||
element.attr(loaderAttribute, forcedTags[tag]);
|
||||
}
|
||||
|
||||
// set default image on every element without source
|
||||
if (tag === _img && defaultImage && !element.attr(_src)) {
|
||||
element.attr(_src, defaultImage);
|
||||
}
|
||||
|
||||
// set placeholder on every element without background image
|
||||
else if (tag !== _img && placeholder && (!element.css(_backgroundImage) || element.css(_backgroundImage) === 'none')) {
|
||||
element.css(_backgroundImage, "url('" + placeholder + "')");
|
||||
}
|
||||
}
|
||||
|
||||
return items;
|
||||
}
|
||||
|
||||
/**
|
||||
* the 'lazy magic' - check all items
|
||||
* @access private
|
||||
* @param {boolean} [allItems]
|
||||
* @param {object} [forced]
|
||||
* @return void
|
||||
*/
|
||||
function _lazyLoadItems(allItems, forced) {
|
||||
// skip if no items where left
|
||||
if (!items.length) {
|
||||
// destroy instance if option is enabled
|
||||
if (config.autoDestroy) {
|
||||
// noinspection JSUnresolvedFunction
|
||||
instance.destroy();
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
var elements = forced || items,
|
||||
loadTriggered = false,
|
||||
imageBase = config.imageBase || '',
|
||||
srcsetAttribute = config.srcsetAttribute,
|
||||
handledName = config.handledName;
|
||||
|
||||
// loop all available items
|
||||
for (var i = 0; i < elements.length; i++) {
|
||||
// item is at least in loadable area
|
||||
if (allItems || forced || _isInLoadableArea(elements[i])) {
|
||||
var element = $(elements[i]),
|
||||
tag = _getElementTagName(elements[i]),
|
||||
attribute = element.attr(config.attribute),
|
||||
elementImageBase = element.attr(config.imageBaseAttribute) || imageBase,
|
||||
customLoader = element.attr(config.loaderAttribute);
|
||||
|
||||
// is not already handled
|
||||
if (!element.data(handledName) &&
|
||||
// and is visible or visibility doesn't matter
|
||||
(!config.visibleOnly || element.is(':visible')) && (
|
||||
// and image source or source set attribute is available
|
||||
(attribute || element.attr(srcsetAttribute)) && (
|
||||
// and is image tag where attribute is not equal source or source set
|
||||
(tag === _img && (elementImageBase + attribute !== element.attr(_src) || element.attr(srcsetAttribute) !== element.attr(_srcset))) ||
|
||||
// or is non image tag where attribute is not equal background
|
||||
(tag !== _img && elementImageBase + attribute !== element.css(_backgroundImage))
|
||||
) ||
|
||||
// or custom loader is available
|
||||
customLoader))
|
||||
{
|
||||
// mark element always as handled as this point to prevent double handling
|
||||
loadTriggered = true;
|
||||
element.data(handledName, true);
|
||||
|
||||
// load item
|
||||
_handleItem(element, tag, elementImageBase, customLoader);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// when something was loaded remove them from remaining items
|
||||
if (loadTriggered) {
|
||||
items = $(items).filter(function() {
|
||||
return !$(this).data(handledName);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* load the given element the lazy way
|
||||
* @access private
|
||||
* @param {object} element
|
||||
* @param {string} tag
|
||||
* @param {string} imageBase
|
||||
* @param {function} [customLoader]
|
||||
* @return void
|
||||
*/
|
||||
function _handleItem(element, tag, imageBase, customLoader) {
|
||||
// increment count of items waiting for after load
|
||||
++_awaitingAfterLoad;
|
||||
|
||||
// extended error callback for correct 'onFinishedAll' handling
|
||||
var errorCallback = function() {
|
||||
_triggerCallback('onError', element);
|
||||
_reduceAwaiting();
|
||||
|
||||
// prevent further callback calls
|
||||
errorCallback = $.noop;
|
||||
};
|
||||
|
||||
// trigger function before loading image
|
||||
_triggerCallback('beforeLoad', element);
|
||||
|
||||
// fetch all double used data here for better code minimization
|
||||
var srcAttribute = config.attribute,
|
||||
srcsetAttribute = config.srcsetAttribute,
|
||||
sizesAttribute = config.sizesAttribute,
|
||||
retinaAttribute = config.retinaAttribute,
|
||||
removeAttribute = config.removeAttribute,
|
||||
loadedName = config.loadedName,
|
||||
elementRetina = element.attr(retinaAttribute);
|
||||
|
||||
// handle custom loader
|
||||
if (customLoader) {
|
||||
// on load callback
|
||||
var loadCallback = function() {
|
||||
// remove attribute from element
|
||||
if (removeAttribute) {
|
||||
element.removeAttr(config.loaderAttribute);
|
||||
}
|
||||
|
||||
// mark element as loaded
|
||||
element.data(loadedName, true);
|
||||
|
||||
// call after load event
|
||||
_triggerCallback(_afterLoad, element);
|
||||
|
||||
// remove item from waiting queue and possibly trigger finished event
|
||||
// it's needed to be asynchronous to run after filter was in _lazyLoadItems
|
||||
setTimeout(_reduceAwaiting, 1);
|
||||
|
||||
// prevent further callback calls
|
||||
loadCallback = $.noop;
|
||||
};
|
||||
|
||||
// bind error event to trigger callback and reduce waiting amount
|
||||
element.off(_error).one(_error, errorCallback)
|
||||
|
||||
// bind after load callback to element
|
||||
.one(_load, loadCallback);
|
||||
|
||||
// trigger custom loader and handle response
|
||||
if (!_triggerCallback(customLoader, element, function(response) {
|
||||
if(response) {
|
||||
element.off(_load);
|
||||
loadCallback();
|
||||
}
|
||||
else {
|
||||
element.off(_error);
|
||||
errorCallback();
|
||||
}
|
||||
})) {
|
||||
element.trigger(_error);
|
||||
}
|
||||
}
|
||||
|
||||
// handle images
|
||||
else {
|
||||
// create image object
|
||||
var imageObj = $(new Image());
|
||||
|
||||
// bind error event to trigger callback and reduce waiting amount
|
||||
imageObj.one(_error, errorCallback)
|
||||
|
||||
// bind after load callback to image
|
||||
.one(_load, function() {
|
||||
// remove element from view
|
||||
element.hide();
|
||||
|
||||
// set image back to element
|
||||
// do it as single 'attr' calls, to be sure 'src' is set after 'srcset'
|
||||
if (tag === _img) {
|
||||
element.attr(_sizes, imageObj.attr(_sizes))
|
||||
.attr(_srcset, imageObj.attr(_srcset))
|
||||
.attr(_src, imageObj.attr(_src));
|
||||
}
|
||||
else {
|
||||
element.css(_backgroundImage, "url('" + imageObj.attr(_src) + "')");
|
||||
}
|
||||
|
||||
// bring it back with some effect!
|
||||
element[config.effect](config.effectTime);
|
||||
|
||||
// remove attribute from element
|
||||
if (removeAttribute) {
|
||||
element.removeAttr(srcAttribute + ' ' + srcsetAttribute + ' ' + retinaAttribute + ' ' + config.imageBaseAttribute);
|
||||
|
||||
// only remove 'sizes' attribute, if it was a custom one
|
||||
if (sizesAttribute !== _sizes) {
|
||||
element.removeAttr(sizesAttribute);
|
||||
}
|
||||
}
|
||||
|
||||
// mark element as loaded
|
||||
element.data(loadedName, true);
|
||||
|
||||
// call after load event
|
||||
_triggerCallback(_afterLoad, element);
|
||||
|
||||
// cleanup image object
|
||||
imageObj.remove();
|
||||
|
||||
// remove item from waiting queue and possibly trigger finished event
|
||||
_reduceAwaiting();
|
||||
});
|
||||
|
||||
// set sources
|
||||
// do it as single 'attr' calls, to be sure 'src' is set after 'srcset'
|
||||
var imageSrc = (_isRetinaDisplay && elementRetina ? elementRetina : element.attr(srcAttribute)) || '';
|
||||
imageObj.attr(_sizes, element.attr(sizesAttribute))
|
||||
.attr(_srcset, element.attr(srcsetAttribute))
|
||||
.attr(_src, imageSrc ? imageBase + imageSrc : null);
|
||||
|
||||
// call after load even on cached image
|
||||
imageObj.complete && imageObj.trigger(_load); // jshint ignore : line
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* check if the given element is inside the current viewport or threshold
|
||||
* @access private
|
||||
* @param {object} element
|
||||
* @return {boolean}
|
||||
*/
|
||||
function _isInLoadableArea(element) {
|
||||
var elementBound = element.getBoundingClientRect(),
|
||||
direction = config.scrollDirection,
|
||||
threshold = config.threshold,
|
||||
vertical = // check if element is in loadable area from top
|
||||
((_getActualHeight() + threshold) > elementBound.top) &&
|
||||
// check if element is even in loadable are from bottom
|
||||
(-threshold < elementBound.bottom),
|
||||
horizontal = // check if element is in loadable area from left
|
||||
((_getActualWidth() + threshold) > elementBound.left) &&
|
||||
// check if element is even in loadable area from right
|
||||
(-threshold < elementBound.right);
|
||||
|
||||
if (direction === 'vertical') {
|
||||
return vertical;
|
||||
}
|
||||
else if (direction === 'horizontal') {
|
||||
return horizontal;
|
||||
}
|
||||
|
||||
return vertical && horizontal;
|
||||
}
|
||||
|
||||
/**
|
||||
* receive the current viewed width of the browser
|
||||
* @access private
|
||||
* @return {number}
|
||||
*/
|
||||
function _getActualWidth() {
|
||||
return _actualWidth >= 0 ? _actualWidth : (_actualWidth = $(window).width());
|
||||
}
|
||||
|
||||
/**
|
||||
* receive the current viewed height of the browser
|
||||
* @access private
|
||||
* @return {number}
|
||||
*/
|
||||
function _getActualHeight() {
|
||||
return _actualHeight >= 0 ? _actualHeight : (_actualHeight = $(window).height());
|
||||
}
|
||||
|
||||
/**
|
||||
* get lowercase tag name of an element
|
||||
* @access private
|
||||
* @param {object} element
|
||||
* @returns {string}
|
||||
*/
|
||||
function _getElementTagName(element) {
|
||||
return element.tagName.toLowerCase();
|
||||
}
|
||||
|
||||
/**
|
||||
* prepend image base to all srcset entries
|
||||
* @access private
|
||||
* @param {string} srcset
|
||||
* @param {string} imageBase
|
||||
* @returns {string}
|
||||
*/
|
||||
function _getCorrectedSrcSet(srcset, imageBase) {
|
||||
if (imageBase) {
|
||||
// trim, remove unnecessary spaces and split entries
|
||||
var entries = srcset.split(',');
|
||||
srcset = '';
|
||||
|
||||
for (var i = 0, l = entries.length; i < l; i++) {
|
||||
srcset += imageBase + entries[i].trim() + (i !== l - 1 ? ',' : '');
|
||||
}
|
||||
}
|
||||
|
||||
return srcset;
|
||||
}
|
||||
|
||||
/**
|
||||
* helper function to throttle down event triggering
|
||||
* @access private
|
||||
* @param {number} delay
|
||||
* @param {function} callback
|
||||
* @return {function}
|
||||
*/
|
||||
function _throttle(delay, callback) {
|
||||
var timeout,
|
||||
lastExecute = 0;
|
||||
|
||||
return function(event, ignoreThrottle) {
|
||||
var elapsed = +new Date() - lastExecute;
|
||||
|
||||
function run() {
|
||||
lastExecute = +new Date();
|
||||
// noinspection JSUnresolvedFunction
|
||||
callback.call(instance, event);
|
||||
}
|
||||
|
||||
timeout && clearTimeout(timeout); // jshint ignore : line
|
||||
|
||||
if (elapsed > delay || !config.enableThrottle || ignoreThrottle) {
|
||||
run();
|
||||
}
|
||||
else {
|
||||
timeout = setTimeout(run, delay - elapsed);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* reduce count of awaiting elements to 'afterLoad' event and fire 'onFinishedAll' if reached zero
|
||||
* @access private
|
||||
* @return void
|
||||
*/
|
||||
function _reduceAwaiting() {
|
||||
--_awaitingAfterLoad;
|
||||
|
||||
// if no items were left trigger finished event
|
||||
if (!items.length && !_awaitingAfterLoad) {
|
||||
_triggerCallback('onFinishedAll');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* single implementation to handle callbacks, pass element and set 'this' to current instance
|
||||
* @access private
|
||||
* @param {string|function} callback
|
||||
* @param {object} [element]
|
||||
* @param {*} [args]
|
||||
* @return {boolean}
|
||||
*/
|
||||
function _triggerCallback(callback, element, args) {
|
||||
if ((callback = config[callback])) {
|
||||
// jQuery's internal '$(arguments).slice(1)' are causing problems at least on old iPads
|
||||
// below is shorthand of 'Array.prototype.slice.call(arguments, 1)'
|
||||
callback.apply(instance, [].slice.call(arguments, 1));
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// if event driven or window is already loaded don't wait for page loading
|
||||
if (config.bind === 'event' || windowLoaded) {
|
||||
_initialize();
|
||||
}
|
||||
|
||||
// otherwise load initial items and start lazy after page load
|
||||
else {
|
||||
// noinspection JSUnresolvedVariable
|
||||
$(window).on(_load + '.' + namespace, _initialize);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* lazy plugin class constructor
|
||||
* @constructor
|
||||
* @access private
|
||||
* @param {object} elements
|
||||
* @param {object} settings
|
||||
* @return {object|LazyPlugin}
|
||||
*/
|
||||
function LazyPlugin(elements, settings) {
|
||||
/**
|
||||
* this lazy plugin instance
|
||||
* @access private
|
||||
* @type {object|LazyPlugin|LazyPlugin.prototype}
|
||||
*/
|
||||
var _instance = this,
|
||||
|
||||
/**
|
||||
* this lazy plugin instance configuration
|
||||
* @access private
|
||||
* @type {object}
|
||||
*/
|
||||
_config = $.extend({}, _instance.config, settings),
|
||||
|
||||
/**
|
||||
* instance generated event executed on container scroll or resize
|
||||
* packed in an object to be referenceable and short named because properties will not be minified
|
||||
* @access private
|
||||
* @type {object}
|
||||
*/
|
||||
_events = {},
|
||||
|
||||
/**
|
||||
* unique namespace for instance related events
|
||||
* @access private
|
||||
* @type {string}
|
||||
*/
|
||||
_namespace = _config.name + '-' + (++lazyInstanceId);
|
||||
|
||||
// noinspection JSUndefinedPropertyAssignment
|
||||
/**
|
||||
* wrapper to get or set an entry from plugin instance configuration
|
||||
* much smaller on minify as direct access
|
||||
* @access public
|
||||
* @type {function}
|
||||
* @param {string} entryName
|
||||
* @param {*} [value]
|
||||
* @return {LazyPlugin|*}
|
||||
*/
|
||||
_instance.config = function(entryName, value) {
|
||||
if (value === undefined) {
|
||||
return _config[entryName];
|
||||
}
|
||||
|
||||
_config[entryName] = value;
|
||||
return _instance;
|
||||
};
|
||||
|
||||
// noinspection JSUndefinedPropertyAssignment
|
||||
/**
|
||||
* add additional items to current instance
|
||||
* @access public
|
||||
* @param {Array|object|string} items
|
||||
* @return {LazyPlugin}
|
||||
*/
|
||||
_instance.addItems = function(items) {
|
||||
_events.a && _events.a($.type(items) === 'string' ? $(items) : items); // jshint ignore : line
|
||||
return _instance;
|
||||
};
|
||||
|
||||
// noinspection JSUndefinedPropertyAssignment
|
||||
/**
|
||||
* get all left items of this instance
|
||||
* @access public
|
||||
* @returns {object}
|
||||
*/
|
||||
_instance.getItems = function() {
|
||||
return _events.g ? _events.g() : {};
|
||||
};
|
||||
|
||||
// noinspection JSUndefinedPropertyAssignment
|
||||
/**
|
||||
* force lazy to load all items in loadable area right now
|
||||
* by default without throttle
|
||||
* @access public
|
||||
* @type {function}
|
||||
* @param {boolean} [useThrottle]
|
||||
* @return {LazyPlugin}
|
||||
*/
|
||||
_instance.update = function(useThrottle) {
|
||||
_events.e && _events.e({}, !useThrottle); // jshint ignore : line
|
||||
return _instance;
|
||||
};
|
||||
|
||||
// noinspection JSUndefinedPropertyAssignment
|
||||
/**
|
||||
* force element(s) to load directly, ignoring the viewport
|
||||
* @access public
|
||||
* @param {Array|object|string} items
|
||||
* @return {LazyPlugin}
|
||||
*/
|
||||
_instance.force = function(items) {
|
||||
_events.f && _events.f($.type(items) === 'string' ? $(items) : items); // jshint ignore : line
|
||||
return _instance;
|
||||
};
|
||||
|
||||
// noinspection JSUndefinedPropertyAssignment
|
||||
/**
|
||||
* force lazy to load all available items right now
|
||||
* this call ignores throttling
|
||||
* @access public
|
||||
* @type {function}
|
||||
* @return {LazyPlugin}
|
||||
*/
|
||||
_instance.loadAll = function() {
|
||||
_events.e && _events.e({all: true}, true); // jshint ignore : line
|
||||
return _instance;
|
||||
};
|
||||
|
||||
// noinspection JSUndefinedPropertyAssignment
|
||||
/**
|
||||
* destroy this plugin instance
|
||||
* @access public
|
||||
* @type {function}
|
||||
* @return undefined
|
||||
*/
|
||||
_instance.destroy = function() {
|
||||
// unbind instance generated events
|
||||
// noinspection JSUnresolvedFunction, JSUnresolvedVariable
|
||||
$(_config.appendScroll).off('.' + _namespace, _events.e);
|
||||
// noinspection JSUnresolvedVariable
|
||||
$(window).off('.' + _namespace);
|
||||
|
||||
// clear events
|
||||
_events = {};
|
||||
|
||||
return undefined;
|
||||
};
|
||||
|
||||
// start using lazy and return all elements to be chainable or instance for further use
|
||||
// noinspection JSUnresolvedVariable
|
||||
_executeLazy(_instance, _config, elements, _events, _namespace);
|
||||
return _config.chainable ? elements : _instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* settings and configuration data
|
||||
* @access public
|
||||
* @type {object|*}
|
||||
*/
|
||||
LazyPlugin.prototype.config = {
|
||||
// general
|
||||
name : 'lazy',
|
||||
chainable : true,
|
||||
autoDestroy : true,
|
||||
bind : 'load',
|
||||
threshold : 500,
|
||||
visibleOnly : false,
|
||||
appendScroll : window,
|
||||
scrollDirection : 'both',
|
||||
imageBase : null,
|
||||
defaultImage : 'data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACH5BAEAAAAALAAAAAABAAEAAAICRAEAOw==',
|
||||
placeholder : null,
|
||||
delay : -1,
|
||||
combined : false,
|
||||
|
||||
// attributes
|
||||
attribute : 'data-src',
|
||||
srcsetAttribute : 'data-srcset',
|
||||
sizesAttribute : 'data-sizes',
|
||||
retinaAttribute : 'data-retina',
|
||||
loaderAttribute : 'data-loader',
|
||||
imageBaseAttribute : 'data-imagebase',
|
||||
removeAttribute : true,
|
||||
handledName : 'handled',
|
||||
loadedName : 'loaded',
|
||||
|
||||
// effect
|
||||
effect : 'show',
|
||||
effectTime : 0,
|
||||
|
||||
// throttle
|
||||
enableThrottle : true,
|
||||
throttle : 250,
|
||||
|
||||
// callbacks
|
||||
beforeLoad : undefined,
|
||||
afterLoad : undefined,
|
||||
onError : undefined,
|
||||
onFinishedAll : undefined
|
||||
};
|
||||
|
||||
// register window load event globally to prevent not loading elements
|
||||
// since jQuery 3.X ready state is fully async and may be executed after 'load'
|
||||
$(window).on('load', function() {
|
||||
windowLoaded = true;
|
||||
});
|
||||
})(window);
|
1
src/themes/hugo-shortcode-gallery/static/shortcode-gallery/lazy/jquery.lazy.min.js
vendored
Normal file
1
src/themes/hugo-shortcode-gallery/static/shortcode-gallery/lazy/jquery.lazy.min.js
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
(function(t,e){"use strict";function r(r,a,i,u,l){function f(){L=t.devicePixelRatio>1,i=c(i),a.delay>=0&&setTimeout(function(){s(!0)},a.delay),(a.delay<0||a.combined)&&(u.e=v(a.throttle,function(t){"resize"===t.type&&(w=B=-1),s(t.all)}),u.a=function(t){t=c(t),i.push.apply(i,t)},u.g=function(){return i=n(i).filter(function(){return!n(this).data(a.loadedName)})},u.f=function(t){for(var e=0;e<t.length;e++){var r=i.filter(function(){return this===t[e]});r.length&&s(!1,r)}},s(),n(a.appendScroll).on("scroll."+l+" resize."+l,u.e))}function c(t){var i=a.defaultImage,o=a.placeholder,u=a.imageBase,l=a.srcsetAttribute,f=a.loaderAttribute,c=a._f||{};t=n(t).filter(function(){var t=n(this),r=m(this);return!t.data(a.handledName)&&(t.attr(a.attribute)||t.attr(l)||t.attr(f)||c[r]!==e)}).data("plugin_"+a.name,r);for(var s=0,d=t.length;s<d;s++){var A=n(t[s]),g=m(t[s]),h=A.attr(a.imageBaseAttribute)||u;g===N&&h&&A.attr(l)&&A.attr(l,b(A.attr(l),h)),c[g]===e||A.attr(f)||A.attr(f,c[g]),g===N&&i&&!A.attr(E)?A.attr(E,i):g===N||!o||A.css(O)&&"none"!==A.css(O)||A.css(O,"url('"+o+"')")}return t}function s(t,e){if(!i.length)return void(a.autoDestroy&&r.destroy());for(var o=e||i,u=!1,l=a.imageBase||"",f=a.srcsetAttribute,c=a.handledName,s=0;s<o.length;s++)if(t||e||A(o[s])){var g=n(o[s]),h=m(o[s]),b=g.attr(a.attribute),v=g.attr(a.imageBaseAttribute)||l,p=g.attr(a.loaderAttribute);g.data(c)||a.visibleOnly&&!g.is(":visible")||!((b||g.attr(f))&&(h===N&&(v+b!==g.attr(E)||g.attr(f)!==g.attr(F))||h!==N&&v+b!==g.css(O))||p)||(u=!0,g.data(c,!0),d(g,h,v,p))}u&&(i=n(i).filter(function(){return!n(this).data(c)}))}function d(t,e,r,i){++z;var o=function(){y("onError",t),p(),o=n.noop};y("beforeLoad",t);var u=a.attribute,l=a.srcsetAttribute,f=a.sizesAttribute,c=a.retinaAttribute,s=a.removeAttribute,d=a.loadedName,A=t.attr(c);if(i){var g=function(){s&&t.removeAttr(a.loaderAttribute),t.data(d,!0),y(T,t),setTimeout(p,1),g=n.noop};t.off(I).one(I,o).one(D,g),y(i,t,function(e){e?(t.off(D),g()):(t.off(I),o())})||t.trigger(I)}else{var h=n(new Image);h.one(I,o).one(D,function(){t.hide(),e===N?t.attr(C,h.attr(C)).attr(F,h.attr(F)).attr(E,h.attr(E)):t.css(O,"url('"+h.attr(E)+"')"),t[a.effect](a.effectTime),s&&(t.removeAttr(u+" "+l+" "+c+" "+a.imageBaseAttribute),f!==C&&t.removeAttr(f)),t.data(d,!0),y(T,t),h.remove(),p()});var m=(L&&A?A:t.attr(u))||"";h.attr(C,t.attr(f)).attr(F,t.attr(l)).attr(E,m?r+m:null),h.complete&&h.trigger(D)}}function A(t){var e=t.getBoundingClientRect(),r=a.scrollDirection,n=a.threshold,i=h()+n>e.top&&-n<e.bottom,o=g()+n>e.left&&-n<e.right;return"vertical"===r?i:"horizontal"===r?o:i&&o}function g(){return w>=0?w:w=n(t).width()}function h(){return B>=0?B:B=n(t).height()}function m(t){return t.tagName.toLowerCase()}function b(t,e){if(e){var r=t.split(",");t="";for(var a=0,n=r.length;a<n;a++)t+=e+r[a].trim()+(a!==n-1?",":"")}return t}function v(t,e){var n,i=0;return function(o,u){function l(){i=+new Date,e.call(r,o)}var f=+new Date-i;n&&clearTimeout(n),f>t||!a.enableThrottle||u?l():n=setTimeout(l,t-f)}}function p(){--z,i.length||z||y("onFinishedAll")}function y(t,e,n){return!!(t=a[t])&&(t.apply(r,[].slice.call(arguments,1)),!0)}var z=0,w=-1,B=-1,L=!1,T="afterLoad",D="load",I="error",N="img",E="src",F="srcset",C="sizes",O="background-image";"event"===a.bind||o?f():n(t).on(D+"."+l,f)}function a(a,o){var u=this,l=n.extend({},u.config,o),f={},c=l.name+"-"+ ++i;return u.config=function(t,r){return r===e?l[t]:(l[t]=r,u)},u.addItems=function(t){return f.a&&f.a("string"===n.type(t)?n(t):t),u},u.getItems=function(){return f.g?f.g():{}},u.update=function(t){return f.e&&f.e({},!t),u},u.force=function(t){return f.f&&f.f("string"===n.type(t)?n(t):t),u},u.loadAll=function(){return f.e&&f.e({all:!0},!0),u},u.destroy=function(){return n(l.appendScroll).off("."+c,f.e),n(t).off("."+c),f={},e},r(u,l,a,f,c),l.chainable?a:u}var n=t.jQuery||t.Zepto,i=0,o=!1;n.fn.Lazy=n.fn.lazy=function(t){return new a(this,t)},n.Lazy=n.lazy=function(t,r,i){if(n.isFunction(r)&&(i=r,r=[]),n.isFunction(i)){t=n.isArray(t)?t:[t],r=n.isArray(r)?r:[r];for(var o=a.prototype.config,u=o._f||(o._f={}),l=0,f=t.length;l<f;l++)(o[t[l]]===e||n.isFunction(o[t[l]]))&&(o[t[l]]=i);for(var c=0,s=r.length;c<s;c++)u[r[c]]=t[0]}},a.prototype.config={name:"lazy",chainable:!0,autoDestroy:!0,bind:"load",threshold:500,visibleOnly:!1,appendScroll:t,scrollDirection:"both",imageBase:null,defaultImage:"data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACH5BAEAAAAALAAAAAABAAEAAAICRAEAOw==",placeholder:null,delay:-1,combined:!1,attribute:"data-src",srcsetAttribute:"data-srcset",sizesAttribute:"data-sizes",retinaAttribute:"data-retina",loaderAttribute:"data-loader",imageBaseAttribute:"data-imagebase",removeAttribute:!0,handledName:"handled",loadedName:"loaded",effect:"show",effectTime:0,enableThrottle:!0,throttle:250,beforeLoad:e,afterLoad:e,onError:e,onFinishedAll:e},n(t).on("load",function(){o=!0})})(window);
|
|
@ -0,0 +1,332 @@
|
|||
/*! Swipebox v1.3.0 | Constantin Saguin csag.co | MIT License | github.com/brutaldesign/swipebox */
|
||||
|
||||
html.swipebox-html.swipebox-touch {
|
||||
overflow: hidden !important;
|
||||
}
|
||||
|
||||
#swipebox-overlay img {
|
||||
border: none !important;
|
||||
}
|
||||
|
||||
#swipebox-overlay {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
z-index: 99999 !important;
|
||||
overflow: hidden;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
#swipebox-container {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
#swipebox-slider {
|
||||
transition: transform 0.4s ease;
|
||||
height: 100%;
|
||||
left: 0;
|
||||
top: 0;
|
||||
width: 100%;
|
||||
white-space: nowrap;
|
||||
position: absolute;
|
||||
display: none;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
#swipebox-slider .slide {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
line-height: 1px;
|
||||
text-align: center;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
#swipebox-slider .slide:before {
|
||||
content: "";
|
||||
display: inline-block;
|
||||
height: 50%;
|
||||
width: 1px;
|
||||
margin-right: -1px;
|
||||
}
|
||||
|
||||
#swipebox-slider .slide img,
|
||||
#swipebox-slider .slide .swipebox-video-container,
|
||||
#swipebox-slider .slide .swipebox-inline-container {
|
||||
display: inline-block;
|
||||
max-height: 100%;
|
||||
max-width: 100%;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
width: auto;
|
||||
height: auto;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
#swipebox-slider .slide .swipebox-video-container {
|
||||
background: none;
|
||||
max-width: 1140px;
|
||||
max-height: 100%;
|
||||
width: 100%;
|
||||
padding: 5%;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
#swipebox-slider .slide .swipebox-video-container .swipebox-video {
|
||||
width: 100%;
|
||||
height: 0;
|
||||
padding-bottom: 56.25%;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
#swipebox-slider .slide .swipebox-video-container .swipebox-video iframe {
|
||||
width: 100% !important;
|
||||
height: 100% !important;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Non-css loading animation using an animated GIF
|
||||
*/
|
||||
#swipebox-overlay:not(.useCssLoadingAnimation) #swipebox-slider .slide-loading {
|
||||
background: url(../img/loader.gif) no-repeat center center;
|
||||
}
|
||||
|
||||
/*
|
||||
* Styling for loading-animation-wrapper and fade in animation when the corrsepsong slide get visible (= is current slide)
|
||||
*/
|
||||
#swipebox-slider .loading-animation-wrapper {
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
#swipebox-slider .slide.current .loading-animation-wrapper {
|
||||
animation: loader-fadein ease-in 2s;
|
||||
}
|
||||
|
||||
@keyframes loader-fadein {
|
||||
from { opacity: 0; }
|
||||
to { opacity: 1; }
|
||||
}
|
||||
|
||||
/*
|
||||
* The loading animation.
|
||||
* This was extracted from here https://github.com/maxbeier/text-spinners/blob/master/spinners.css
|
||||
* and afterwards modified.
|
||||
*/
|
||||
.loading-animation-wrapper .loading {
|
||||
display: inline-block;
|
||||
overflow: hidden;
|
||||
height: 1.3em;
|
||||
margin-top: -0.3em;
|
||||
line-height: 1.5em;
|
||||
vertical-align: text-bottom;
|
||||
font-size: 40px;
|
||||
}
|
||||
|
||||
.loading-animation-wrapper .loading::after {
|
||||
display: inline-table;
|
||||
white-space: pre;
|
||||
text-align: left;
|
||||
content: "◇◇◇\A◈◇◇\A◇◈◇\A◇◇◈";
|
||||
animation: loader-spin4 1s steps(4) infinite;
|
||||
}
|
||||
|
||||
@keyframes loader-spin4 {to { transform: translateY( -6.0em); } }
|
||||
|
||||
|
||||
|
||||
#swipebox-bottom-bar,
|
||||
#swipebox-top-bar {
|
||||
transition: 0.5s;
|
||||
position: absolute;
|
||||
left: 0;
|
||||
z-index: 999;
|
||||
height: 50px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
#swipebox-bottom-bar {
|
||||
bottom: -50px;
|
||||
}
|
||||
|
||||
#swipebox-bottom-bar.visible-bars {
|
||||
transform: translate3d(0, -50px, 0);
|
||||
}
|
||||
|
||||
#swipebox-top-bar {
|
||||
top: -50px;
|
||||
}
|
||||
|
||||
#swipebox-top-bar.visible-bars {
|
||||
transform: translate3d(0, 50px, 0);
|
||||
}
|
||||
|
||||
#swipebox-title {
|
||||
display: block;
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
#swipebox-description {
|
||||
color: white !important;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
height: 100%;
|
||||
text-align: center;
|
||||
font-size: 0.8em;
|
||||
line-height: 1.5em;
|
||||
}
|
||||
|
||||
#swipebox-prev,
|
||||
#swipebox-next,
|
||||
#swipebox-close {
|
||||
background-image: url(../img/icons.png);
|
||||
background-repeat: no-repeat;
|
||||
border: none !important;
|
||||
text-decoration: none !important;
|
||||
cursor: pointer;
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
top: 0;
|
||||
}
|
||||
|
||||
.useSvg #swipebox-prev,
|
||||
.useSvg #swipebox-next,
|
||||
.useSvg #swipebox-close{
|
||||
background-image: url(../img/icons.svg);
|
||||
}
|
||||
|
||||
#swipebox-arrows {
|
||||
display: block;
|
||||
margin: 0 auto;
|
||||
width: 100%;
|
||||
height: 50px;
|
||||
}
|
||||
|
||||
#swipebox-prev {
|
||||
background-position: -32px 13px;
|
||||
float: left;
|
||||
}
|
||||
|
||||
#swipebox-next {
|
||||
background-position: -78px 13px;
|
||||
float: right;
|
||||
}
|
||||
|
||||
#swipebox-close {
|
||||
top: 0;
|
||||
right: 0;
|
||||
position: absolute;
|
||||
z-index: 9999;
|
||||
background-position: 15px 12px;
|
||||
}
|
||||
|
||||
.swipebox-no-close-button #swipebox-close {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#swipebox-prev.disabled,
|
||||
#swipebox-next.disabled {
|
||||
opacity: 0.3;
|
||||
}
|
||||
|
||||
.swipebox-no-touch #swipebox-overlay.rightSpring #swipebox-slider {
|
||||
animation: rightSpring 0.3s;
|
||||
}
|
||||
|
||||
.swipebox-no-touch #swipebox-overlay.leftSpring #swipebox-slider {
|
||||
animation: leftSpring 0.3s;
|
||||
}
|
||||
|
||||
.swipebox-touch #swipebox-container:before,
|
||||
.swipebox-touch #swipebox-container:after {
|
||||
backface-visibility: hidden;
|
||||
transition: all .3s ease;
|
||||
content: ' ';
|
||||
position: absolute;
|
||||
z-index: 999;
|
||||
top: 0;
|
||||
height: 100%;
|
||||
width: 20px;
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.swipebox-touch #swipebox-container:before {
|
||||
left: 0;
|
||||
box-shadow: inset 10px 0px 10px -8px #656565;
|
||||
}
|
||||
|
||||
.swipebox-touch #swipebox-container:after {
|
||||
right: 0;
|
||||
box-shadow: inset -10px 0px 10px -8px #656565;
|
||||
}
|
||||
|
||||
.swipebox-touch #swipebox-overlay.leftSpringTouch #swipebox-container:before {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.swipebox-touch #swipebox-overlay.rightSpringTouch #swipebox-container:after {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
@keyframes rightSpring {
|
||||
0% {
|
||||
left: 0;
|
||||
}
|
||||
50% {
|
||||
left: -30px;
|
||||
}
|
||||
100% {
|
||||
left: 0;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes leftSpring {
|
||||
0% {
|
||||
left: 0;
|
||||
}
|
||||
50% {
|
||||
left: 30px;
|
||||
}
|
||||
100% {
|
||||
left: 0;
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (min-width: 800px) {
|
||||
#swipebox-close {
|
||||
right: 10px;
|
||||
}
|
||||
#swipebox-arrows {
|
||||
width: 92%;
|
||||
max-width: 800px;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Skin
|
||||
--------------------------*/
|
||||
|
||||
#swipebox-overlay {
|
||||
background: #0d0d0d;
|
||||
}
|
||||
|
||||
#swipebox-bottom-bar,
|
||||
#swipebox-top-bar {
|
||||
text-shadow: 1px 1px 1px black;
|
||||
background: #000;
|
||||
opacity: 0.95;
|
||||
}
|
||||
|
||||
#swipebox-top-bar {
|
||||
color: white !important;
|
||||
line-height: 43px;
|
||||
}
|
1
src/themes/hugo-shortcode-gallery/static/shortcode-gallery/swipebox/css/swipebox.min.css
vendored
Normal file
1
src/themes/hugo-shortcode-gallery/static/shortcode-gallery/swipebox/css/swipebox.min.css
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
/*! Swipebox v1.3.0 | Constantin Saguin csag.co | MIT License | github.com/brutaldesign/swipebox */html.swipebox-html.swipebox-touch{overflow:hidden!important}#swipebox-overlay img{border:none!important}#swipebox-overlay{width:100%;height:100%;position:fixed;top:0;left:0;z-index:99999!important;overflow:hidden;user-select:none}#swipebox-container{position:relative;width:100%;height:100%}#swipebox-slider{transition:transform .4s ease;height:100%;left:0;top:0;width:100%;white-space:nowrap;position:absolute;display:none;cursor:pointer}#swipebox-slider .slide{height:100%;width:100%;line-height:1px;text-align:center;display:inline-block}#swipebox-slider .slide:before{content:"";display:inline-block;height:50%;width:1px;margin-right:-1px}#swipebox-slider .slide .swipebox-inline-container,#swipebox-slider .slide .swipebox-video-container,#swipebox-slider .slide img{display:inline-block;max-height:100%;max-width:100%;margin:0;padding:0;width:auto;height:auto;vertical-align:middle}#swipebox-slider .slide .swipebox-video-container{background:0 0;max-width:1140px;max-height:100%;width:100%;padding:5%;box-sizing:border-box}#swipebox-slider .slide .swipebox-video-container .swipebox-video{width:100%;height:0;padding-bottom:56.25%;overflow:hidden;position:relative}#swipebox-slider .slide .swipebox-video-container .swipebox-video iframe{width:100%!important;height:100%!important;position:absolute;top:0;left:0}#swipebox-overlay:not(.useCssLoadingAnimation) #swipebox-slider .slide-loading{background:url(../img/loader.gif) no-repeat center center}#swipebox-slider .loading-animation-wrapper{position:absolute;width:100%}#swipebox-slider .slide.current .loading-animation-wrapper{animation:loader-fadein ease-in 2s}@keyframes loader-fadein{from{opacity:0}to{opacity:1}}.loading-animation-wrapper .loading{display:inline-block;overflow:hidden;height:1.3em;margin-top:-.3em;line-height:1.5em;vertical-align:text-bottom;font-size:40px}.loading-animation-wrapper .loading::after{display:inline-table;white-space:pre;text-align:left;content:"◇◇◇\A◈◇◇\A◇◈◇\A◇◇◈";animation:loader-spin4 1s steps(4) infinite}@keyframes loader-spin4{to{transform:translateY(-6em)}}#swipebox-bottom-bar,#swipebox-top-bar{transition:.5s;position:absolute;left:0;z-index:999;height:50px;width:100%}#swipebox-bottom-bar{bottom:-50px}#swipebox-bottom-bar.visible-bars{transform:translate3d(0,-50px,0)}#swipebox-top-bar{top:-50px}#swipebox-top-bar.visible-bars{transform:translate3d(0,50px,0)}#swipebox-title{display:block;width:100%;text-align:center}#swipebox-description{color:#fff!important;display:flex;align-items:center;justify-content:center;height:100%;text-align:center;font-size:.8em;line-height:1.5em}#swipebox-close,#swipebox-next,#swipebox-prev{background-image:url(../img/icons.png);background-repeat:no-repeat;border:none!important;text-decoration:none!important;cursor:pointer;width:50px;height:50px;top:0}.useSvg #swipebox-close,.useSvg #swipebox-next,.useSvg #swipebox-prev{background-image:url(../img/icons.svg)}#swipebox-arrows{display:block;margin:0 auto;width:100%;height:50px}#swipebox-prev{background-position:-32px 13px;float:left}#swipebox-next{background-position:-78px 13px;float:right}#swipebox-close{top:0;right:0;position:absolute;z-index:9999;background-position:15px 12px}.swipebox-no-close-button #swipebox-close{display:none}#swipebox-next.disabled,#swipebox-prev.disabled{opacity:.3}.swipebox-no-touch #swipebox-overlay.rightSpring #swipebox-slider{animation:rightSpring .3s}.swipebox-no-touch #swipebox-overlay.leftSpring #swipebox-slider{animation:leftSpring .3s}.swipebox-touch #swipebox-container:after,.swipebox-touch #swipebox-container:before{backface-visibility:hidden;transition:all .3s ease;content:' ';position:absolute;z-index:999;top:0;height:100%;width:20px;opacity:0}.swipebox-touch #swipebox-container:before{left:0;box-shadow:inset 10px 0 10px -8px #656565}.swipebox-touch #swipebox-container:after{right:0;box-shadow:inset -10px 0 10px -8px #656565}.swipebox-touch #swipebox-overlay.leftSpringTouch #swipebox-container:before{opacity:1}.swipebox-touch #swipebox-overlay.rightSpringTouch #swipebox-container:after{opacity:1}@keyframes rightSpring{0%{left:0}50%{left:-30px}100%{left:0}}@keyframes leftSpring{0%{left:0}50%{left:30px}100%{left:0}}@media screen and (min-width:800px){#swipebox-close{right:10px}#swipebox-arrows{width:92%;max-width:800px}}#swipebox-overlay{background:#0d0d0d}#swipebox-bottom-bar,#swipebox-top-bar{text-shadow:1px 1px 1px #000;background:#000;opacity:.95}#swipebox-top-bar{color:#fff!important;line-height:43px}
|
Binary file not shown.
After Width: | Height: | Size: 729 B |
|
@ -0,0 +1 @@
|
|||
<?xml version="1.0" encoding="utf-8"?> <!-- Generator: IcoMoon.io --> <!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"> <svg width="120" height="24" viewBox="0 0 120 24" fill="#ffffff" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><path d="M 17.384,17.705q0.00,0.536 -0.375,0.911l-1.821,1.821q-0.375,0.375 -0.911,0.375t-0.911-0.375l-3.938-3.938l-3.938,3.938q-0.375,0.375 -0.911,0.375t-0.911-0.375l-1.821-1.821q-0.375-0.375 -0.375-0.911t 0.375-0.911l 3.938-3.938l-3.938-3.938q-0.375-0.375 -0.375-0.911t 0.375-0.911l 1.821-1.821q 0.375-0.375 0.911-0.375t 0.911,0.375l 3.938,3.938l 3.938-3.938q 0.375-0.375 0.911-0.375t 0.911,0.375l 1.821,1.821q 0.375,0.375 0.375,0.911 t-0.375,0.911l-3.938,3.938l 3.938,3.938q 0.375,0.375 0.375,0.911zM 57.938,21.067l-8.732-8.719q-0.496-0.496 -0.496-1.212t 0.496-1.212l 8.732-8.719q 0.496-0.496 1.212-0.496t 1.212,0.496l 1.004,1.004q 0.496,0.496 0.496,1.212t-0.496,1.212l-6.509,6.509l 6.509,6.496q 0.496,0.509 0.496,1.219t-0.496,1.205l-1.004,1.004q-0.496,0.496 -1.212,0.496t-1.212-0.496zM 110.719,11.143q0.00,0.696 -0.496,1.219l-8.732,8.719q-0.496,0.496 -1.205,0.496t-1.205-0.496l-1.018-1.004q-0.496-0.522 -0.496-1.219q0.00-0.71 0.496-1.205l 6.509-6.509l-6.509-6.496q-0.496-0.522 -0.496-1.219q0.00-0.71 0.496-1.205l 1.018-1.004q 0.482-0.509 1.205-0.509t 1.205,0.509l 8.732,8.719q 0.496,0.496 0.496,1.205z"></path></svg>
|
After Width: | Height: | Size: 1.4 KiB |
Binary file not shown.
After Width: | Height: | Size: 2.5 KiB |
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
Loading…
Reference in New Issue