@ngx-gallery/core
Version:
Angular gallery directive that hooks the lightbox with the images automatically.
1,459 lines (1,435 loc) • 73.2 kB
JavaScript
import { animate, style, transition, trigger } from '@angular/animations';
import { HttpClientModule } from '@angular/common/http';
import { DomSanitizer } from '@angular/platform-browser';
import { isPlatformBrowser, DOCUMENT, CommonModule } from '@angular/common';
import { InjectionToken, Inject, Injectable, Optional, Component, Input, Output, HostBinding, ChangeDetectionStrategy, EventEmitter, NgZone, ElementRef, PLATFORM_ID, Directive, NgModule, ViewChild, defineInjectable, inject } from '@angular/core';
import { BehaviorSubject, Subject, of, EMPTY, Subscription, fromEvent, zip } from 'rxjs';
import { delay, filter, switchMap, tap, map, debounceTime } from 'rxjs/operators';
/**
* @fileoverview added by tsickle
* @suppress {checkTypes,extraRequire,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc
*/
/** @enum {string} */
const GalleryAction = {
INITIALIZED: 'initialized',
ITEMS_CHANGED: 'itemsChanged',
INDEX_CHANGED: 'indexChanged',
PLAY: 'play',
STOP: 'stop',
};
/** @enum {string} */
const ImageSize = {
Cover: 'cover',
Contain: 'contain',
};
/** @enum {string} */
const LoadingStrategy = {
Preload: 'preload',
Lazy: 'lazy',
Default: 'default',
};
/** @enum {string} */
const ThumbnailsPosition = {
Top: 'top',
Left: 'left',
Right: 'right',
Bottom: 'bottom',
};
/** @enum {string} */
const ImageLoaderMode = {
Determinate: 'determinate',
Indeterminate: 'indeterminate',
};
/** @enum {string} */
const DotsPosition = {
Top: 'top',
Bottom: 'bottom',
};
/** @enum {string} */
const CounterPosition = {
Top: 'top',
Bottom: 'bottom',
};
/** @enum {string} */
const ThumbnailsMode = {
Free: 'free',
Strict: 'strict',
};
/** @enum {string} */
const SlidingDirection = {
Horizontal: 'horizontal',
Vertical: 'vertical',
};
/** @enum {string} */
const GalleryItemType = {
Image: 'image',
Video: 'video',
Youtube: 'youtube',
Iframe: 'iframe',
};
/**
* @fileoverview added by tsickle
* @suppress {checkTypes,extraRequire,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc
*/
/**
* Initial state
* @type {?}
*/
const defaultState = {
action: GalleryAction.INITIALIZED,
isPlaying: false,
hasNext: false,
hasPrev: false,
currIndex: 0,
items: []
};
/** @type {?} */
const defaultConfig = {
nav: true,
loop: true,
zoomOut: 0,
dots: false,
thumb: true,
dotsSize: 30,
counter: true,
gestures: true,
autoPlay: false,
thumbWidth: 120,
thumbHeight: 90,
panSensitivity: 25,
disableThumb: false,
playerInterval: 3000,
imageSize: ImageSize.Contain,
thumbMode: ThumbnailsMode.Strict,
dotsPosition: DotsPosition.Bottom,
counterPosition: CounterPosition.Top,
thumbPosition: ThumbnailsPosition.Bottom,
loadingStrategy: LoadingStrategy.Default,
slidingDirection: SlidingDirection.Horizontal,
navIcon: `<?xml version="1.0" encoding="UTF-8"?><svg width="512px" height="512px" enable-background="new 0 0 240.823 240.823" version="1.1" viewBox="0 0 240.823 240.823" xml:space="preserve" xmlns="http://www.w3.org/2000/svg"><path d="m183.19 111.82l-108.3-108.26c-4.752-4.74-12.451-4.74-17.215 0-4.752 4.74-4.752 12.439 0 17.179l99.707 99.671-99.695 99.671c-4.752 4.74-4.752 12.439 0 17.191 4.752 4.74 12.463 4.74 17.215 0l108.3-108.26c4.68-4.691 4.68-12.511-0.012-17.19z" fill="#fff"/></svg>`,
loadingIcon: `<?xml version="1.0" encoding="UTF-8"?><svg stroke="#fff" viewBox="0 0 44 44" xmlns="http://www.w3.org/2000/svg"><g fill="none" fill-rule="evenodd" stroke-width="2"><circle cx="22" cy="22" r="1"><animate attributeName="r" begin="0s" calcMode="spline" dur="1.8s" keySplines="0.165, 0.84, 0.44, 1" keyTimes="0; 1" repeatCount="indefinite" values="1; 20"/><animate attributeName="stroke-opacity" begin="0s" calcMode="spline" dur="1.8s" keySplines="0.3, 0.61, 0.355, 1" keyTimes="0; 1" repeatCount="indefinite" values="1; 0"/></circle><circle cx="22" cy="22" r="1"><animate attributeName="r" begin="-0.9s" calcMode="spline" dur="1.8s" keySplines="0.165, 0.84, 0.44, 1" keyTimes="0; 1" repeatCount="indefinite" values="1; 20"/><animate attributeName="stroke-opacity" begin="-0.9s" calcMode="spline" dur="1.8s" keySplines="0.3, 0.61, 0.355, 1" keyTimes="0; 1" repeatCount="indefinite" values="1; 0"/></circle></g></svg>`
};
/**
* @fileoverview added by tsickle
* @suppress {checkTypes,extraRequire,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc
*/
class ImageItem {
/**
* @param {?} data
*/
constructor(data) {
this.data = data;
this.type = GalleryItemType.Image;
}
}
class VideoItem {
/**
* @param {?} data
*/
constructor(data) {
this.data = data;
this.type = GalleryItemType.Video;
}
}
class IframeItem {
/**
* @param {?} data
*/
constructor(data) {
this.data = data;
this.type = GalleryItemType.Iframe;
}
}
class YoutubeItem {
/**
* @param {?} data
*/
constructor(data) {
this.data = {
src: `//youtube.com/embed/${data.src}?wmode=transparent`,
thumb: data.thumb ? data.thumb : `//img.youtube.com/vi/${data.src}/default.jpg`
};
this.type = GalleryItemType.Youtube;
}
}
/**
* @fileoverview added by tsickle
* @suppress {checkTypes,extraRequire,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc
*/
/** @type {?} */
const filterActions = (actions) => {
return filter((state) => actions.indexOf(state.action) > -1);
};
class GalleryRef {
/**
* @param {?} config
* @param {?} deleteInstance
*/
constructor(config, deleteInstance) {
this.deleteInstance = deleteInstance;
/**
* Stream that emits on item click
*/
this.itemClick = new Subject();
/**
* Stream that emits on thumbnail click
*/
this.thumbClick = new Subject();
/**
* Stream that emits on an error occurs
*/
this.error = new Subject();
this._state = new BehaviorSubject(defaultState);
this._config = new BehaviorSubject(config);
this.state = this._state.asObservable();
this.config = this._config.asObservable();
}
/**
* Stream that emits when gallery is initialized/reset
* @return {?}
*/
get initialized() {
return this.state.pipe(filterActions([GalleryAction.INITIALIZED]));
}
/**
* Stream that emits when items is changed (items loaded, item added, item removed)
* @return {?}
*/
get itemsChanged() {
return this.state.pipe(filterActions([GalleryAction.ITEMS_CHANGED]));
}
/**
* Stream that emits when current item is changed
* @return {?}
*/
get indexChanged() {
return this.state.pipe(filterActions([GalleryAction.INDEX_CHANGED]));
}
/**
* Stream that emits when the player should start or stop
* @return {?}
*/
get playingChanged() {
return this.state.pipe(filterActions([GalleryAction.PLAY, GalleryAction.STOP]));
}
/**
* Stream that emits when the player should start or stop
* @private
* @return {?}
*/
get playerActions() {
return this.state.pipe(filterActions([GalleryAction.PLAY, GalleryAction.STOP, GalleryAction.INDEX_CHANGED]));
}
/**
* Activate player actions listener
* @return {?}
*/
activatePlayer() {
return this.playerActions.pipe(switchMap((e) => e.isPlaying ? of({}).pipe(delay(this._config.value.playerInterval), tap(() => this.next())) : EMPTY));
}
/**
* Set gallery state
* @private
* @param {?} state
* @return {?}
*/
setState(state) {
this._state.next(Object.assign({}, this._state.value, state));
}
/**
* Set gallery config
* @param {?} config
* @return {?}
*/
setConfig(config) {
this._config.next(Object.assign({}, this._config.value, config));
}
/**
* Add gallery item
* @param {?} item - Gallery item object
* @param {?=} active - Set the new item as current slide
* @return {?}
*/
add(item, active) {
/** @type {?} */
const items = [...this._state.value.items, item];
this.setState({
action: GalleryAction.ITEMS_CHANGED,
items: items,
hasNext: items.length > 1,
currIndex: active ? items.length - 1 : this._state.value.currIndex
});
}
/**
* Add image item
* @param {?} data
* @param {?=} active
* @return {?}
*/
addImage(data, active) {
this.add(new ImageItem(data), active);
}
/**
* Add video item
* @param {?} data
* @param {?=} active
* @return {?}
*/
addVideo(data, active) {
this.add(new VideoItem(data), active);
}
/**
* Add iframe item
* @param {?} data
* @param {?=} active
* @return {?}
*/
addIframe(data, active) {
this.add(new IframeItem(data), active);
}
/**
* Add youtube item
* @param {?} data
* @param {?=} active
* @return {?}
*/
addYoutube(data, active) {
this.add(new YoutubeItem(data), active);
}
/**
* Remove gallery item
* @param {?} i - Item index
* @return {?}
*/
remove(i) {
/** @type {?} */
const items = [
...this._state.value.items.slice(0, i),
...this._state.value.items.slice(i + 1, this._state.value.items.length)
];
this.setState({
action: GalleryAction.ITEMS_CHANGED,
items: items,
hasNext: items.length > 1,
hasPrev: i > 0
});
}
/**
* Load items and reset the state
* @param {?} items - Gallery images data
* @return {?}
*/
load(items) {
if (items) {
this.setState({
action: GalleryAction.ITEMS_CHANGED,
items: items,
hasNext: items.length > 1,
hasPrev: false
});
}
}
/**
* Set active item
* @param {?} i - Active Index
* @return {?}
*/
set(i) {
if (i !== this._state.value.currIndex) {
this.setState({
action: GalleryAction.INDEX_CHANGED,
currIndex: i,
hasNext: i < this._state.value.items.length - 1,
hasPrev: i > 0
});
}
}
/**
* Next item
* @return {?}
*/
next() {
if (this._state.value.hasNext) {
this.set(this._state.value.currIndex + 1);
}
else if (this._config.value.loop) {
this.set(0);
}
}
/**
* Prev item
* @return {?}
*/
prev() {
if (this._state.value.hasPrev) {
this.set(this._state.value.currIndex - 1);
}
else if (this._config.value.loop) {
this.set(this._state.value.items.length - 1);
}
}
/**
* Start gallery player
* @param {?=} interval
* @return {?}
*/
play(interval) {
if (interval) {
this.setConfig({ playerInterval: interval });
}
this.setState({ action: GalleryAction.PLAY, isPlaying: true });
}
/**
* Stop gallery player
* @return {?}
*/
stop() {
this.setState({ action: GalleryAction.STOP, isPlaying: false });
}
/**
* Reset gallery to initial state
* @return {?}
*/
reset() {
this.setState(defaultState);
}
/**
* Destroy gallery
* @return {?}
*/
destroy() {
this._state.complete();
this._config.complete();
this.itemClick.complete();
this.thumbClick.complete();
this.deleteInstance();
}
}
/**
* @fileoverview added by tsickle
* @suppress {checkTypes,extraRequire,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc
*/
/** @type {?} */
const GALLERY_CONFIG = new InjectionToken('galleryConfig');
/**
* @fileoverview added by tsickle
* @suppress {checkTypes,extraRequire,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc
*/
class Gallery {
/**
* @param {?} config
*/
constructor(config) {
/**
* Store gallery instances
*/
this._instances = new Map();
this.config = config ? Object.assign({}, defaultConfig, config) : defaultConfig;
}
/**
* Get or create gallery by ID
* @param {?=} id
* @param {?=} config
* @return {?}
*/
ref(id = 'root', config) {
if (this._instances.has(id)) {
/** @type {?} */
const galleryRef = this._instances.get(id);
if (config) {
galleryRef.setConfig(Object.assign({}, this.config, config));
}
return galleryRef;
}
else {
return this._instances.set(id, new GalleryRef(Object.assign({}, this.config, config), this.deleteInstance(id))).get(id);
}
}
/**
* Destroy all gallery instances
* @return {?}
*/
destroyAll() {
this._instances.forEach((ref) => ref.destroy());
}
/**
* Reset all gallery instances
* @return {?}
*/
resetAll() {
this._instances.forEach((ref) => ref.reset());
}
/**
* A destroyer function for each gallery instance
* @private
* @param {?} id
* @return {?}
*/
deleteInstance(id) {
return () => {
if (this._instances.has(id)) {
this._instances.delete(id);
}
};
}
}
Gallery.decorators = [
{ type: Injectable, args: [{
providedIn: 'root'
},] }
];
/** @nocollapse */
Gallery.ctorParameters = () => [
{ type: undefined, decorators: [{ type: Optional }, { type: Inject, args: [GALLERY_CONFIG,] }] }
];
/** @nocollapse */ Gallery.ngInjectableDef = defineInjectable({ factory: function Gallery_Factory() { return new Gallery(inject(GALLERY_CONFIG, 8)); }, token: Gallery, providedIn: "root" });
/**
* @fileoverview added by tsickle
* @suppress {checkTypes,extraRequire,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc
*/
class GalleryComponent {
/**
* @param {?} _gallery
*/
constructor(_gallery) {
this._gallery = _gallery;
this.nav = this._gallery.config.nav;
this.dots = this._gallery.config.dots;
this.loop = this._gallery.config.loop;
this.thumb = this._gallery.config.thumb;
this.zoomOut = this._gallery.config.zoomOut;
this.counter = this._gallery.config.counter;
this.dotsSize = this._gallery.config.dotsSize;
this.autoPlay = this._gallery.config.autoPlay;
this.gestures = this._gallery.config.gestures;
this.thumbWidth = this._gallery.config.thumbWidth;
this.thumbHeight = this._gallery.config.thumbHeight;
this.disableThumb = this._gallery.config.disableThumb;
this.panSensitivity = this._gallery.config.panSensitivity;
this.playerInterval = this._gallery.config.playerInterval;
this.itemTemplate = this._gallery.config.itemTemplate;
this.thumbTemplate = this._gallery.config.thumbTemplate;
this.thumbMode = this._gallery.config.thumbMode;
this.imageSize = this._gallery.config.imageSize;
this.dotsPosition = this._gallery.config.dotsPosition;
this.counterPosition = this._gallery.config.counterPosition;
this.slidingDirection = this._gallery.config.slidingDirection;
this.loadingStrategy = this._gallery.config.loadingStrategy;
this.thumbPosition = this._gallery.config.thumbPosition;
// Inputs used by the lightbox
/**
* Destroy gallery ref on component destroy event
*/
this.destroyRef = true;
/**
* Skip initializing the config with components inputs (Lightbox mode)
*/
this.skipInitConfig = false;
this.itemClick = new EventEmitter();
this.thumbClick = new EventEmitter();
this.playingChange = new EventEmitter();
this.indexChange = new EventEmitter();
this.itemsChange = new EventEmitter();
this.error = new EventEmitter();
this._itemClick$ = Subscription.EMPTY;
this._thumbClick$ = Subscription.EMPTY;
this._itemChange$ = Subscription.EMPTY;
this._indexChange$ = Subscription.EMPTY;
this._playingChange$ = Subscription.EMPTY;
this._playerListener$ = Subscription.EMPTY;
}
/**
* @private
* @return {?}
*/
getConfig() {
return {
nav: this.nav,
dots: this.dots,
loop: this.loop,
thumb: this.thumb,
zoomOut: this.zoomOut,
counter: this.counter,
autoPlay: this.autoPlay,
gestures: this.gestures,
dotsSize: this.dotsSize,
imageSize: this.imageSize,
thumbMode: this.thumbMode,
thumbWidth: this.thumbWidth,
thumbHeight: this.thumbHeight,
disableThumb: this.disableThumb,
dotsPosition: this.dotsPosition,
itemTemplate: this.itemTemplate,
thumbTemplate: this.thumbTemplate,
thumbPosition: this.thumbPosition,
panSensitivity: this.panSensitivity,
playerInterval: this.playerInterval,
counterPosition: this.counterPosition,
loadingStrategy: this.loadingStrategy,
slidingDirection: this.slidingDirection
};
}
/**
* @param {?} i
* @return {?}
*/
onAction(i) {
switch (i) {
case 'next':
this.galleryRef.next();
break;
case 'prev':
this.galleryRef.prev();
break;
default:
this.galleryRef.set((/** @type {?} */ (i)));
}
}
/**
* @param {?} changes
* @return {?}
*/
ngOnChanges(changes) {
if (this.galleryRef) {
this.galleryRef.setConfig(this.getConfig());
if (changes.items && changes.items.currentValue !== changes.items.previousValue) {
this.load(this.items);
}
}
}
/**
* @return {?}
*/
ngOnInit() {
// Get gallery instance by id
if (this.skipInitConfig) {
this.galleryRef = this._gallery.ref(this.id);
}
else {
this.galleryRef = this._gallery.ref(this.id, this.getConfig());
}
// Load gallery items
this.load(this.items);
// Activate player listener
this._playerListener$ = this.galleryRef.activatePlayer().subscribe();
// Subscribes to events on demand
if (this.indexChange.observers.length) {
this._indexChange$ = this.galleryRef.indexChanged.subscribe((state) => this.indexChange.emit(state));
}
if (this.itemsChange.observers.length) {
this._itemChange$ = this.galleryRef.itemsChanged.subscribe((state) => this.itemsChange.emit(state));
}
if (this.playingChange.observers.length) {
this._playingChange$ = this.galleryRef.playingChanged.subscribe((state) => this.playingChange.emit(state));
}
// Start playing if auto-play is set to true
if (this.autoPlay) {
this.play();
}
}
/**
* @return {?}
*/
ngOnDestroy() {
this._itemClick$.unsubscribe();
this._thumbClick$.unsubscribe();
this._itemChange$.unsubscribe();
this._indexChange$.unsubscribe();
this._playingChange$.unsubscribe();
this._playerListener$.unsubscribe();
if (this.destroyRef) {
this.galleryRef.destroy();
}
}
/**
* @param {?} i
* @return {?}
*/
onItemClick(i) {
this.itemClick.emit(i);
this.galleryRef.itemClick.next(i);
}
/**
* @param {?} i
* @return {?}
*/
onThumbClick(i) {
this.galleryRef.set(i);
this.thumbClick.emit(i);
this.galleryRef.thumbClick.next(i);
}
/**
* @param {?} err
* @return {?}
*/
onError(err) {
this.error.emit(err);
this.galleryRef.error.next(err);
}
/**
* @param {?} items
* @return {?}
*/
load(items) {
this.galleryRef.load(items);
}
/**
* @param {?} item
* @param {?=} active
* @return {?}
*/
add(item, active) {
this.galleryRef.add(item, active);
}
/**
* @param {?} data
* @param {?=} active
* @return {?}
*/
addImage(data, active) {
this.add(new ImageItem(data), active);
}
/**
* @param {?} data
* @param {?=} active
* @return {?}
*/
addVideo(data, active) {
this.add(new VideoItem(data), active);
}
/**
* @param {?} data
* @param {?=} active
* @return {?}
*/
addIframe(data, active) {
this.add(new IframeItem(data), active);
}
/**
* @param {?} data
* @param {?=} active
* @return {?}
*/
addYoutube(data, active) {
this.add(new YoutubeItem(data), active);
}
/**
* @param {?} i
* @return {?}
*/
remove(i) {
this.galleryRef.remove(i);
}
/**
* @return {?}
*/
next() {
this.galleryRef.next();
}
/**
* @return {?}
*/
prev() {
this.galleryRef.prev();
}
/**
* @param {?} i
* @return {?}
*/
set(i) {
this.galleryRef.set(i);
}
/**
* @return {?}
*/
reset() {
this.galleryRef.reset();
}
/**
* @param {?=} interval
* @return {?}
*/
play(interval) {
this.galleryRef.play(interval);
}
/**
* @return {?}
*/
stop() {
this.galleryRef.stop();
}
}
GalleryComponent.decorators = [
{ type: Component, args: [{
selector: 'gallery',
changeDetection: ChangeDetectionStrategy.OnPush,
template: `
<gallery-core [state]="galleryRef.state | async"
[config]="galleryRef.config | async"
(action)="onAction($event)"
(itemClick)="onItemClick($event)"
(thumbClick)="onThumbClick($event)"
(error)="onError($event)"></gallery-core>
<ng-content></ng-content>
`,
styles: ["::ng-deep gallery-core[dotsPosition=top] gallery-dots{top:0}::ng-deep gallery-core[dotsPosition=bottom] gallery-dots{bottom:0}::ng-deep gallery-dots{margin:7px;position:absolute;left:50%;-webkit-transform:translateX(-50%);transform:translateX(-50%)}::ng-deep .g-dot{cursor:pointer;z-index:20}::ng-deep .g-dot:hover .g-dot-inner{opacity:1}::ng-deep .g-dot-active .g-dot-inner{opacity:1;-webkit-transform:scale(1.5)!important;transform:scale(1.5)!important}::ng-deep .g-dot-inner{background-color:#fff;opacity:.6;width:30%;height:30%;border-radius:50%;box-shadow:0 0 1px #000;transition:.2s}::ng-deep .g-dot,::ng-deep .g-dot-inner,::ng-deep gallery-dots{display:flex;justify-content:center;align-items:center}::ng-deep .g-nav-next,::ng-deep .g-nav-prev{position:absolute;top:50%;width:30px;height:40px;cursor:pointer;z-index:999}::ng-deep .g-nav-next{right:.5em;-webkit-transform:translateY(-50%) perspective(1px);transform:translateY(-50%) perspective(1px)}::ng-deep .g-nav-prev{left:.5em;-webkit-transform:translateY(-50%) perspective(1px) scale(-1,-1);transform:translateY(-50%) perspective(1px) scale(-1,-1)}@media only screen and (max-width:480px){::ng-deep .g-nav-next{right:.2em}::ng-deep .g-nav-prev{left:.2em}}::ng-deep .g-items-container{height:100%}::ng-deep .g-slider{position:absolute;transition:transform .4s cubic-bezier(.5,0,.5,1);transition:transform .4s cubic-bezier(.5,0,.5,1),-webkit-transform .4s cubic-bezier(.5,0,.5,1)}::ng-deep gallery-core[slidingDirection=horizontal] .g-slider{flex-direction:row}::ng-deep gallery-core[slidingDirection=vertical] .g-slider{flex-direction:column}::ng-deep gallery-thumbs{display:block;z-index:1;overflow:unset}::ng-deep .g-thumbs-container{position:relative;z-index:206;width:100%;height:100%;left:0;top:0;display:flex;overflow:unset}::ng-deep gallery-core[disableThumb=true] gallery-thumb{cursor:default}::ng-deep gallery-core[thumbPosition=bottom] gallery-thumbs .g-slider,::ng-deep gallery-core[thumbPosition=top] gallery-thumbs .g-slider{flex-direction:row;top:0;left:50%}::ng-deep gallery-core[thumbPosition=bottom] gallery-thumb,::ng-deep gallery-core[thumbPosition=top] gallery-thumb{padding:1px 0 1px 1px}::ng-deep gallery-core[thumbPosition=left] gallery-thumbs .g-slider,::ng-deep gallery-core[thumbPosition=right] gallery-thumbs .g-slider{flex-direction:column;top:50%;left:0}::ng-deep gallery-core[thumbPosition=left] gallery-thumb,::ng-deep gallery-core[thumbPosition=right] gallery-thumb{padding:0 1px 1px}::ng-deep gallery-core[thumbPosition=top]{flex-direction:column}::ng-deep gallery-core[thumbPosition=left]{flex-direction:row}::ng-deep gallery-core[thumbPosition=right]{flex-direction:row-reverse}::ng-deep gallery-core[thumbPosition=bottom]{flex-direction:column-reverse}::ng-deep gallery-thumb.g-active-thumb .g-thumb-loading{background-color:#464646}::ng-deep .g-thumb-loading{position:relative;overflow:hidden;height:100%;background-color:#262626}::ng-deep .g-thumb-loading::before{content:\"\";position:absolute;top:0;right:0;bottom:0;left:50%;z-index:1;width:500%;margin-left:-250%;-webkit-animation:.8s linear infinite phAnimation;animation:.8s linear infinite phAnimation;background:linear-gradient(to right,rgba(255,255,255,0) 46%,rgba(255,255,255,.35) 50%,rgba(255,255,255,0) 54%) 50% 50%}@-webkit-keyframes phAnimation{0%{-webkit-transform:translate3d(-30%,0,0);transform:translate3d(-30%,0,0)}100%{-webkit-transform:translate3d(30%,0,0);transform:translate3d(30%,0,0)}}@keyframes phAnimation{0%{-webkit-transform:translate3d(-30%,0,0);transform:translate3d(-30%,0,0)}100%{-webkit-transform:translate3d(30%,0,0);transform:translate3d(30%,0,0)}}::ng-deep gallery-core[counterPosition=top] .g-counter{top:0;border-bottom-left-radius:4px;border-bottom-right-radius:4px}::ng-deep gallery-core[counterPosition=bottom] .g-counter{bottom:0;border-top-left-radius:4px;border-top-right-radius:4px}::ng-deep .g-counter{z-index:50;position:absolute;left:50%;-webkit-transform:translateX(-50%) perspective(1px);transform:translateX(-50%) perspective(1px);font-size:12px;padding:4px 10px;color:#fff;background-color:rgba(0,0,0,.5)}::ng-deep gallery[gallerize] gallery-item{cursor:pointer}::ng-deep gallery-item,::ng-deep gallery-thumb{position:relative;height:100%;width:100%;display:block;overflow:hidden}::ng-deep gallery-item h2,::ng-deep gallery-item h4,::ng-deep gallery-thumb h2,::ng-deep gallery-thumb h4{color:coral;margin:0}::ng-deep gallery-item h2,::ng-deep gallery-thumb h2{font-size:3.5em;margin-bottom:.3em}::ng-deep gallery-item h4,::ng-deep gallery-thumb h4{font-size:1.6em}::ng-deep gallery-item{z-index:10}::ng-deep gallery-item iframe,::ng-deep gallery-item video{position:absolute;width:100%;height:100%}::ng-deep gallery-thumb{opacity:.5;cursor:pointer;transition:opacity .3s cubic-bezier(.5,0,.5,1)}::ng-deep gallery-thumb.g-active-thumb{opacity:1}::ng-deep .g-image-item{background-position:center center;background-repeat:no-repeat;background-size:cover;width:100%;height:100%}::ng-deep .g-image-error-message,::ng-deep .g-template{position:absolute;z-index:10;left:0;top:0;right:0;bottom:0;color:#fff;display:flex;align-items:center;justify-content:center;flex-direction:column}::ng-deep .g-loading{position:absolute;-webkit-transform:translate3d(-50%,-50%,0);transform:translate3d(-50%,-50%,0);left:50%;top:50%;width:80px;height:80px}::ng-deep gallery-core[imageSize=contain] gallery-slider .g-image-item{background-size:contain}::ng-deep gallery-image{display:flex;justify-content:center;align-items:center;height:100%}::ng-deep gallery{position:relative;z-index:1;overflow:hidden;display:block;height:500px;background-color:#000}::ng-deep gallery *{box-sizing:border-box}::ng-deep gallery,::ng-deep gallery-core{position:relative;overflow:hidden}::ng-deep .g-box,::ng-deep .g-slider,::ng-deep gallery-core{display:flex;height:100%;width:100%}::ng-deep gallery[fluid]{-webkit-transform:translateX(-50vw);transform:translateX(-50vw);width:100vw;left:50%}::ng-deep gallery[fluid][fluid=false]{-webkit-transform:none;transform:none;width:initial;left:initial}::ng-deep .g-no-transition{transition:unset!important}::ng-deep .g-box,::ng-deep gallery-slider{overflow:hidden;position:relative;display:flex;flex-direction:column;flex:1;order:1;height:100%}::ng-deep .g-btn-close svg,::ng-deep gallery-nav svg{width:100%;height:100%;-webkit-filter:drop-shadow(0 0 1px #000);filter:drop-shadow(0 0 1px #000);transition:opacity .2s linear;opacity:.6}::ng-deep .g-btn-close svg:hover,::ng-deep gallery-nav svg:hover{opacity:1}"]
}] }
];
/** @nocollapse */
GalleryComponent.ctorParameters = () => [
{ type: Gallery }
];
GalleryComponent.propDecorators = {
id: [{ type: Input }],
items: [{ type: Input }],
nav: [{ type: Input }],
dots: [{ type: Input }],
loop: [{ type: Input }],
thumb: [{ type: Input }],
zoomOut: [{ type: Input }],
counter: [{ type: Input }],
dotsSize: [{ type: Input }],
autoPlay: [{ type: Input }],
gestures: [{ type: Input }],
thumbWidth: [{ type: Input }],
thumbHeight: [{ type: Input }],
disableThumb: [{ type: Input }],
panSensitivity: [{ type: Input }],
playerInterval: [{ type: Input }],
itemTemplate: [{ type: Input }],
thumbTemplate: [{ type: Input }],
thumbMode: [{ type: Input }],
imageSize: [{ type: Input }],
dotsPosition: [{ type: Input }],
counterPosition: [{ type: Input }],
slidingDirection: [{ type: Input }],
loadingStrategy: [{ type: Input }],
thumbPosition: [{ type: Input }],
destroyRef: [{ type: Input }],
skipInitConfig: [{ type: Input }],
itemClick: [{ type: Output }],
thumbClick: [{ type: Output }],
playingChange: [{ type: Output }],
indexChange: [{ type: Output }],
itemsChange: [{ type: Output }],
error: [{ type: Output }]
};
/**
* @fileoverview added by tsickle
* @suppress {checkTypes,extraRequire,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc
*/
class GalleryIframeComponent {
/**
* @param {?} _sanitizer
*/
constructor(_sanitizer) {
this._sanitizer = _sanitizer;
}
/**
* @param {?} shouldPause
* @return {?}
*/
set pauseVideo(shouldPause) {
/** @type {?} */
const iframe = this.iframe.nativeElement;
if (shouldPause) {
/** @type {?} */
const src = iframe.src;
iframe.src = src;
}
}
/**
* @return {?}
*/
ngOnInit() {
this.iframeSrc = this._sanitizer.bypassSecurityTrustResourceUrl(this.src);
}
}
GalleryIframeComponent.decorators = [
{ type: Component, args: [{
selector: 'gallery-iframe',
changeDetection: ChangeDetectionStrategy.OnPush,
template: `
<iframe #iframe
frameborder="0"
allowfullscreen
[src]="iframeSrc">
</iframe>
`
}] }
];
/** @nocollapse */
GalleryIframeComponent.ctorParameters = () => [
{ type: DomSanitizer }
];
GalleryIframeComponent.propDecorators = {
src: [{ type: Input }],
pauseVideo: [{ type: Input, args: ['pause',] }],
iframe: [{ type: ViewChild, args: ['iframe',] }]
};
/**
* @fileoverview added by tsickle
* @suppress {checkTypes,extraRequire,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc
*/
class GalleryImageComponent {
/**
* @param {?} _sanitizer
*/
constructor(_sanitizer) {
this._sanitizer = _sanitizer;
/**
* Stream that emits the state
*/
this._state = new BehaviorSubject('loading');
this.state = this._state.asObservable();
/**
* Progress value
*/
this.progress = 0;
/**
* Stream that emits when an error occurs
*/
this.error = new EventEmitter();
}
/**
* @return {?}
*/
get imageLoadSuccess() {
return !!this.imageUrl;
}
/**
* @return {?}
*/
get imageLoadFailed() {
return !!this.loadError;
}
/**
* @return {?}
*/
ngOnInit() {
if (this.loadingIcon) {
this.loaderTemplate = this._sanitizer.bypassSecurityTrustHtml(this.loadingIcon);
}
if (this.loadingError) {
this.errorTemplate = this._sanitizer.bypassSecurityTrustHtml(this.loadingError);
}
}
/**
* @return {?}
*/
ngOnDestroy() {
this._state.complete();
}
/**
* @param {?} __0
* @return {?}
*/
onProgress({ loaded, total }) {
this.progress = loaded * 100 / total;
}
/**
* @param {?} blobUrl
* @return {?}
*/
onLoaded(blobUrl) {
this.imageUrl = this._sanitizer.bypassSecurityTrustStyle(`url(${blobUrl})`);
this._state.next('success');
}
/**
* @param {?} err
* @return {?}
*/
onError(err) {
this.loadError = err;
this._state.next('failed');
this.error.emit(err);
}
}
GalleryImageComponent.decorators = [
{ type: Component, args: [{
selector: 'gallery-image',
changeDetection: ChangeDetectionStrategy.OnPush,
animations: [
trigger('fadeIn', [
transition(':enter', [
style({ opacity: 0 }),
animate('300ms ease-in', style({ opacity: 1 }))
])
])
],
template: `
<ng-container [lazyImage]="src"
(progress)="onProgress($event)"
(loaded)="onLoaded($event)"
(error)="onError($event)"
[ngSwitch]="state | async">
<div *ngSwitchCase="'success'"
@fadeIn
class="g-image-item"
[style.backgroundImage]="imageUrl">
</div>
<div *ngSwitchCase="'failed'"
class="g-image-error-message">
<div *ngIf="errorTemplate; else defaultError"
[innerHTML]="errorTemplate"></div>
<ng-template #defaultError>
<ng-container *ngIf="isThumbnail; else isLarge">
<h4>⚠</h4>
</ng-container>
<ng-template #isLarge>
<h2>⚠</h2>
<p>Unable to load the image!</p>
</ng-template>
</ng-template>
</div>
<ng-container *ngSwitchCase="'loading'">
<div *ngIf="loaderTemplate; else defaultLoader"
class="g-loading"
[innerHTML]="loaderTemplate">
</div>
<ng-template #defaultLoader>
<div *ngIf="isThumbnail" class="g-thumb-loading"></div>
</ng-template>
</ng-container>
</ng-container>
`
}] }
];
/** @nocollapse */
GalleryImageComponent.ctorParameters = () => [
{ type: DomSanitizer }
];
GalleryImageComponent.propDecorators = {
isThumbnail: [{ type: Input }],
src: [{ type: Input }],
loadingIcon: [{ type: Input }],
loadingError: [{ type: Input }],
error: [{ type: Output }],
imageLoadSuccess: [{ type: HostBinding, args: ['class.g-image-loaded',] }],
imageLoadFailed: [{ type: HostBinding, args: ['class.g-image-error',] }]
};
/**
* @fileoverview added by tsickle
* @suppress {checkTypes,extraRequire,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc
*/
class GalleryVideoComponent {
constructor() {
/**
* Stream that emits when an error occurs
*/
this.error = new EventEmitter();
}
/**
* @param {?} shouldPause
* @return {?}
*/
set pauseVideo(shouldPause) {
/** @type {?} */
const video = this.video.nativeElement;
if (shouldPause && !video.paused) {
video.pause();
}
}
/**
* @return {?}
*/
ngOnInit() {
if (this.src instanceof Array) {
// If video has multiple sources
this.videoSources = [...this.src];
}
else {
this.videoSources = [{ url: this.src }];
}
}
}
GalleryVideoComponent.decorators = [
{ type: Component, args: [{
selector: 'gallery-video',
changeDetection: ChangeDetectionStrategy.OnPush,
template: `
<video #video controls poster="{{poster}}" (error)="error.emit($event)">
<source *ngFor="let src of videoSources" src="{{src?.url}}" type="{{src?.type}}"/>
</video>
`
}] }
];
GalleryVideoComponent.propDecorators = {
src: [{ type: Input }],
poster: [{ type: Input }],
pauseVideo: [{ type: Input, args: ['pause',] }],
error: [{ type: Output }],
video: [{ type: ViewChild, args: ['video',] }]
};
/**
* @fileoverview added by tsickle
* @suppress {checkTypes,extraRequire,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc
*/
/**
* @fileoverview added by tsickle
* @suppress {checkTypes,extraRequire,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc
*/
class GalleryNavComponent {
/**
* @param {?} _sanitizer
*/
constructor(_sanitizer) {
this._sanitizer = _sanitizer;
this.action = new EventEmitter();
}
/**
* @return {?}
*/
ngOnInit() {
this.navIcon = this._sanitizer.bypassSecurityTrustHtml(this.config.navIcon);
}
}
GalleryNavComponent.decorators = [
{ type: Component, args: [{
selector: 'gallery-nav',
changeDetection: ChangeDetectionStrategy.OnPush,
template: `
<i *ngIf="config.loop || state.hasPrev"
class="g-nav-prev"
aria-label="Previous"
(tapClick)="action.emit('prev')"
[innerHtml]="navIcon"></i>
<i *ngIf="config.loop || state.hasNext"
class="g-nav-next"
aria-label="Next"
(tapClick)="action.emit('next')"
[innerHtml]="navIcon"></i>
`
}] }
];
/** @nocollapse */
GalleryNavComponent.ctorParameters = () => [
{ type: DomSanitizer }
];
GalleryNavComponent.propDecorators = {
state: [{ type: Input }],
config: [{ type: Input }],
action: [{ type: Output }]
};
/**
* @fileoverview added by tsickle
* @suppress {checkTypes,extraRequire,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc
*/
class GalleryCoreComponent {
constructor() {
this.action = new EventEmitter();
this.itemClick = new EventEmitter();
this.thumbClick = new EventEmitter();
this.error = new EventEmitter();
}
/**
* Set thumbnails position
* @return {?}
*/
get thumbPosition() {
return this.config.thumbPosition;
}
/**
* Set sliding direction
* @return {?}
*/
get slidingDirection() {
return this.config.slidingDirection;
}
/**
* Disable thumbnails clicks
* @return {?}
*/
get disableThumb() {
return this.config.disableThumb;
}
/**
* Set gallery image size
* @return {?}
*/
get imageSize() {
return this.config.imageSize;
}
/**
* Set gallery dots position
* @return {?}
*/
get dotsPosition() {
return this.config.dotsPosition;
}
/**
* Set gallery counter position
* @return {?}
*/
get counterPosition() {
return this.config.counterPosition;
}
}
GalleryCoreComponent.decorators = [
{ type: Component, args: [{
selector: 'gallery-core',
changeDetection: ChangeDetectionStrategy.OnPush,
template: `
<gallery-thumbs *ngIf="config.thumb"
[state]="state"
[config]="config"
(action)="action.emit($event)"
(thumbClick)="thumbClick.emit($event)">
</gallery-thumbs>
<div class="g-box">
<gallery-slider [state]="state"
[config]="config"
(action)="action.emit($event)"
(itemClick)="itemClick.emit($event)"
(error)="error.emit($event)">
<gallery-nav *ngIf="config.nav && state.items.length > 1"
[state]="state"
[config]="config"
(action)="action.emit($event)">
</gallery-nav>
</gallery-slider>
<gallery-dots *ngIf="config.dots"
[state]="state"
[config]="config"
(action)="action.emit($event)">
</gallery-dots>
<gallery-counter *ngIf="config.counter"
[state]="state">
</gallery-counter>
</div>
`
}] }
];
GalleryCoreComponent.propDecorators = {
state: [{ type: Input }],
config: [{ type: Input }],
action: [{ type: Output }],
itemClick: [{ type: Output }],
thumbClick: [{ type: Output }],
error: [{ type: Output }],
thumbPosition: [{ type: HostBinding, args: ['attr.thumbPosition',] }],
slidingDirection: [{ type: HostBinding, args: ['attr.slidingDirection',] }],
disableThumb: [{ type: HostBinding, args: ['attr.disableThumb',] }],
imageSize: [{ type: HostBinding, args: ['attr.imageSize',] }],
dotsPosition: [{ type: HostBinding, args: ['attr.dotsPosition',] }],
counterPosition: [{ type: HostBinding, args: ['attr.counterPosition',] }]
};
/**
* @fileoverview added by tsickle
* @suppress {checkTypes,extraRequire,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc
*/
class GalleryDotsComponent {
constructor() {
this.action = new EventEmitter();
}
}
GalleryDotsComponent.decorators = [
{ type: Component, args: [{
selector: 'gallery-dots',
changeDetection: ChangeDetectionStrategy.OnPush,
template: `
<div class="g-dot"
*ngFor="let item of state.items; let i = index"
[class.g-dot-active]="i === state.currIndex"
[style.width.px]="config?.dotsSize"
[style.height.px]="config?.dotsSize"
(tapClick)="action.emit(i)">
<div class="g-dot-inner"></div>
</div>
`
}] }
];
GalleryDotsComponent.propDecorators = {
state: [{ type: Input }],
config: [{ type: Input }],
action: [{ type: Output }]
};
/**
* @fileoverview added by tsickle
* @suppress {checkTypes,extraRequire,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc
*/
class GalleryThumbsComponent {
/**
* @param {?} _el
* @param {?} _zone
*/
constructor(_el, _zone) {
this._el = _el;
this._zone = _zone;
/**
* Sliding worker
*/
this._slidingWorker$ = new BehaviorSubject({ value: 0, active: false });
/**
* Current slider position in free sliding mode
*/
this._freeModeCurrentOffset = 0;
/**
* Stream that emits when the active item should change
*/
this.action = new EventEmitter();
/**
* Stream that emits when thumb is clicked
*/
this.thumbClick = new EventEmitter();
/**
* Stream that emits when an error occurs
*/
this.error = new EventEmitter();
// Activate sliding worker
this.sliderState$ = this._slidingWorker$.pipe(map((state) => ({
style: this.getSliderStyles(state),
active: state.active
})));
}
/**
* @return {?}
*/
ngOnChanges() {
// Refresh the slider
this.updateSlider({ value: 0, active: false });
this._freeModeCurrentOffset = 0;
}
/**
* @return {?}
*/
ngOnInit() {
if (this.config.gestures && !this.config.disableThumb && typeof Hammer !== 'undefined') {
/** @type {?} */
let direction;
switch (this.config.thumbPosition) {
case ThumbnailsPosition.Right:
case ThumbnailsPosition.Left:
direction = Hammer.DIRECTION_VERTICAL;
break;
case ThumbnailsPosition.Top:
case ThumbnailsPosition.Bottom:
direction = Hammer.DIRECTION_HORIZONTAL;
break;
}
// Activate gestures
this._hammer = new Hammer(this._el.nativeElement);
this._hammer.get('pan').set({ direction });
this._zone.runOutsideAngular(() => {
// Move the slider
switch (this.config.thumbMode) {
case ThumbnailsMode.Strict:
this._hammer.on('pan', (e) => this.strictMode(e));
break;
case ThumbnailsMode.Free:
this._hammer.on('pan', (e) => this.freeMode(e));
}
});
}
}
/**
* @return {?}
*/
ngOnDestroy() {
if (this._hammer) {
this._hammer.destroy();
}
}
/**
* Sliding strict mode
* @private
* @param {?} e
* @return {?}
*/
strictMode(e) {
switch (this.config.thumbPosition) {
case ThumbnailsPosition.Right:
case ThumbnailsPosition.Left:
this.updateSlider({ value: e.deltaY, active: true });
if (e.isFinal) {
this.updateSlider({ value: 0, active: false });
this.verticalPan(e);
}
break;
case ThumbnailsPosition.Top:
case ThumbnailsPosition.Bottom:
this.updateSlider({ value: e.deltaX, active: true });
if (e.isFinal) {
this.updateSlider({ value: 0, active: false });
this.horizontalPan(e);
}
}
}
/**
* Sliding free mode
* @private
* @param {?} e
* @return {?}
*/
freeMode(e) {
switch (this.config.thumbPosition) {
case ThumbnailsPosition.Right:
case ThumbnailsPosition.Left:
this.updateSlider({ value: this._freeModeCurrentOffset + e.deltaY, active: true });
if (e.isFinal) {
if (this.minFreeScrollExceeded(e.deltaY, this.config.thumbWidth, this.config.thumbHeight)) {
this._freeModeCurrentOffset = -(this.state.items.length - 1 - this.state.currIndex) * this.config.thumbHeight;
}
else if (this.maxFreeScrollExceeded(e.deltaY, this.config.thumbHeight, this.config.thumbWidth)) {
this._freeModeCurrentOffset = this.state.currIndex * this.config.thumbHeight;
}
else {
this._freeModeCurrentOffset += e.deltaY;
}
this.updateSlider({ value: this._freeModeCurrentOffset, active: false });
}
break;
case ThumbnailsPosition.Top:
case ThumbnailsPosition.Bottom:
this.updateSlider({ value: this._freeModeCurrentOffset + e.deltaX, active: true });
if (e.isFinal) {
if (this.minFreeScrollExceeded(e.deltaX, this.config.thumbHeight, this.config.thumbWidth)) {
this._freeModeCurrentOffset = -(this.state.items.length - 1 - this.state.currIndex) * this.config.thumbWidth;
}
else if (this.maxFreeScrollExceeded(e.deltaX, this.config.thumbWidth, this.config.thumbHeight)) {
this._freeModeCurrentOffset = this.state.currIndex * this.config.thumbWidth;
}
else {
this._freeModeCurrentOffset += e.deltaX;
}
this.updateSlider({ value: this._freeModeCurrentOffset, active: false });
}
}
}
/**
* Check if the minimum free scroll is exceeded (used in Bottom, Left directions)
* @private
* @param {?} delta
* @param {?} width
* @param {?} height
* @return {?}
*/
minFreeScrollExceeded(delta, width, height) {
return -(this._freeModeCurrentOffset + delta - width / 2) > (this.state.items.length - this.state.currIndex) * height;
}
/**
* Check if the maximum free scroll is exceeded (used in Top, Right directions)
* @private
* @param {?} delta
* @param {?} width
* @param {?} height
* @return {?}
*/
maxFreeScrollExceeded(delta, width, height) {
return this._freeModeCurrentOffset + delta > (this.state.currIndex * width) + (height / 2);
}
/**
* Convert sliding state to styles
* @private
* @param {?} state
* @return {?}
*/
getSliderStyles(state) {
/** @type {?} */
let value;
switch (this.config.thumbPosition) {
case ThumbnailsPosition.Top:
case ThumbnailsPosition.Bottom:
this.width = '100%';
this.height = this.config.thumbHeight + 'px';
value = -(this.state.currIndex * this.config.thumbWidth) - (this.config.thumbWidth / 2 - state.value);