UNPKG

ng-lazyload-image-angular-universal

Version:

This is an extension from ng-lazyload-image, made available for Angular Universal.

791 lines (770 loc) 27.3 kB
(function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('@angular/common'), require('@angular/core'), require('rxjs'), require('rxjs/operators')) : typeof define === 'function' && define.amd ? define('ng-lazyload-image-angular-universal', ['exports', '@angular/common', '@angular/core', 'rxjs', 'rxjs/operators'], factory) : (factory((global['ng-lazyload-image-angular-universal'] = {}),global.ng.common,global.ng.core,global.rxjs,global.rxjs.operators)); }(this, (function (exports,common,core,rxjs,operators) { 'use strict'; /** * @fileoverview added by tsickle * @suppress {checkTypes,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc */ /** @type {?} */ var cssClassNames = { loaded: 'ng-lazyloaded', loading: 'ng-lazyloading', failed: 'ng-failed-lazyloaded', }; /** * @param {?} element * @param {?} cssClassName * @return {?} */ function removeCssClassName(element, cssClassName) { element.className = element.className.replace(cssClassName, ''); } /** * @param {?} element * @param {?} cssClassName * @return {?} */ function addCssClassName(element, cssClassName) { if (!element.className.includes(cssClassName)) { element.className += " " + cssClassName; } } /** * @param {?} element * @param {?} cssClassName * @return {?} */ function hasCssClassName(element, cssClassName) { return element.className && element.className.includes(cssClassName); } /** * @fileoverview added by tsickle * @suppress {checkTypes,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc */ /** * @return {?} */ function isWindowDefined() { return typeof window !== 'undefined'; } /** * @param {?} element * @return {?} */ function isChildOfPicture(element) { return Boolean(element.parentElement && element.parentElement.nodeName.toLowerCase() === 'picture'); } /** * @param {?} element * @return {?} */ function isImageElement(element) { return element.nodeName.toLowerCase() === 'img'; } /** * @param {?} element * @param {?} imagePath * @param {?} useSrcset * @return {?} */ function setImage(element, imagePath, useSrcset) { if (isImageElement(element)) { if (useSrcset) { element.srcset = imagePath; } else { element.src = imagePath; } } else { element.style.backgroundImage = "url('" + imagePath + "')"; } return element; } /** * @param {?} attrName * @return {?} */ function setSources(attrName) { return ( /** * @param {?} image * @return {?} */function (image) { /** @type {?} */ var sources = image.parentElement.getElementsByTagName('source'); for (var i = 0; i < sources.length; i++) { /** @type {?} */ var attrValue = sources[i].getAttribute(attrName); if (attrValue) { sources[i].srcset = attrValue; } } }); } /** @type {?} */ var setSourcesToDefault = setSources('defaultImage'); /** @type {?} */ var setSourcesToLazy = setSources('lazyLoad'); /** @type {?} */ var setSourcesToError = setSources('errorImage'); /** * @param {?} setSourcesFn * @return {?} */ function setImageAndSources(setSourcesFn) { return ( /** * @param {?} element * @param {?} imagePath * @param {?} useSrcset * @return {?} */function (element, imagePath, useSrcset) { if (isImageElement(element) && isChildOfPicture(element)) { setSourcesFn(element); } if (imagePath) { setImage(element, imagePath, useSrcset); } }); } /** @type {?} */ var setImageAndSourcesToDefault = setImageAndSources(setSourcesToDefault); /** @type {?} */ var setImageAndSourcesToLazy = setImageAndSources(setSourcesToLazy); /** @type {?} */ var setImageAndSourcesToError = setImageAndSources(setSourcesToError); /** * @fileoverview added by tsickle * @suppress {checkTypes,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc */ /** * @fileoverview added by tsickle * @suppress {checkTypes,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc */ /** @type {?} */ var end = ( /** * @param {?} __0 * @return {?} */function (_a) { var element = _a.element; return addCssClassName(element, cssClassNames.loaded); }); /** @type {?} */ var loadImage = ( /** * @param {?} __0 * @return {?} */function (_a) { var element = _a.element, useSrcset = _a.useSrcset, imagePath = _a.imagePath; /** @type {?} */ var img; if (isImageElement(element) && isChildOfPicture(element)) { /** @type {?} */ var parentClone = ( /** @type {?} */(element.parentNode.cloneNode(true))); img = parentClone.getElementsByTagName('img')[0]; setSourcesToLazy(img); setImage(img, imagePath, useSrcset); } else { img = new Image(); if (isImageElement(element) && element.sizes) { img.sizes = element.sizes; } if (useSrcset) { img.srcset = imagePath; } else { img.src = imagePath; } } return rxjs.Observable.create(( /** * @param {?} observer * @return {?} */function (observer) { img.onload = ( /** * @return {?} */function () { observer.next(imagePath); observer.complete(); }); img.onerror = ( /** * @param {?} err * @return {?} */function (err) { observer.error(null); }); })); }); /** @type {?} */ var setErrorImage = ( /** * @param {?} __0 * @return {?} */function (_a) { var element = _a.element, errorImagePath = _a.errorImagePath, useSrcset = _a.useSrcset; setImageAndSourcesToError(element, errorImagePath, useSrcset); addCssClassName(element, cssClassNames.failed); }); /** @type {?} */ var setLoadedImage = ( /** * @param {?} __0 * @return {?} */function (_a) { var element = _a.element, imagePath = _a.imagePath, useSrcset = _a.useSrcset; setImageAndSourcesToLazy(element, imagePath, useSrcset); }); /** @type {?} */ var setup = ( /** * @param {?} __0 * @return {?} */function (_a) { var element = _a.element, defaultImagePath = _a.defaultImagePath, useSrcset = _a.useSrcset; setImageAndSourcesToDefault(element, defaultImagePath, useSrcset); if (hasCssClassName(element, cssClassNames.loaded)) { removeCssClassName(element, cssClassNames.loaded); } }); /** @type {?} */ var sharedPreset = { finally: end, loadImage: loadImage, setErrorImage: setErrorImage, setLoadedImage: setLoadedImage, setup: setup }; /** * @fileoverview added by tsickle * @suppress {checkTypes,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc */ var Rect = /** @class */ (function () { function Rect(left, top, right, bottom) { this.left = left; this.top = top; this.right = right; this.bottom = bottom; } /** * @param {?} element * @return {?} */ Rect.fromElement = /** * @param {?} element * @return {?} */ function (element) { var _a = element.getBoundingClientRect(), left = _a.left, top = _a.top, right = _a.right, bottom = _a.bottom; if (left === 0 && top === 0 && right === 0 && bottom === 0) { return Rect.empty; } else { return new Rect(left, top, right, bottom); } }; /** * @param {?} _window * @return {?} */ Rect.fromWindow = /** * @param {?} _window * @return {?} */ function (_window) { return new Rect(0, 0, _window.innerWidth, _window.innerHeight); }; /** * @param {?} inflateBy * @return {?} */ Rect.prototype.inflate = /** * @param {?} inflateBy * @return {?} */ function (inflateBy) { this.left -= inflateBy; this.top -= inflateBy; this.right += inflateBy; this.bottom += inflateBy; }; /** * @param {?} rect * @return {?} */ Rect.prototype.intersectsWith = /** * @param {?} rect * @return {?} */ function (rect) { return rect.left < this.right && this.left < rect.right && rect.top < this.bottom && this.top < rect.bottom; }; /** * @param {?} rect * @return {?} */ Rect.prototype.getIntersectionWith = /** * @param {?} rect * @return {?} */ function (rect) { /** @type {?} */ var left = Math.max(this.left, rect.left); /** @type {?} */ var top = Math.max(this.top, rect.top); /** @type {?} */ var right = Math.min(this.right, rect.right); /** @type {?} */ var bottom = Math.min(this.bottom, rect.bottom); if (right >= left && bottom >= top) { return new Rect(left, top, right, bottom); } else { return Rect.empty; } }; Rect.empty = new Rect(0, 0, 0, 0); return Rect; }()); /** * @fileoverview added by tsickle * @suppress {checkTypes,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc */ /** @type {?} */ var scrollListeners = new WeakMap(); /** * @param {?} obs * @param {?=} scheduler * @return {?} */ function sampleObservable(obs, scheduler) { return obs.pipe(operators.sampleTime(100, scheduler), operators.share(), operators.startWith('')); } // Only create one scroll listener per target and share the observable. // Typical, there will only be one observable per application /** @type {?} */ var getScrollListener = ( /** * @param {?} scrollTarget * @return {?} */function (scrollTarget) { if (!scrollTarget || typeof scrollTarget.addEventListener !== 'function') { if (isWindowDefined()) { console.warn('`addEventListener` on ' + scrollTarget + ' (scrollTarget) is not a function. Skipping this target'); } return rxjs.empty(); } if (scrollListeners.has(scrollTarget)) { return scrollListeners.get(scrollTarget); } /** @type {?} */ var srollEvent = rxjs.Observable.create(( /** * @param {?} observer * @return {?} */function (observer) { /** @type {?} */ var eventName = 'scroll'; /** @type {?} */ var handler = ( /** * @param {?} event * @return {?} */function (event) { return observer.next(event); }); /** @type {?} */ var options = { passive: true, capture: false }; scrollTarget.addEventListener(eventName, handler, options); return ( /** * @return {?} */function () { return scrollTarget.removeEventListener(eventName, handler, options); }); })); /** @type {?} */ var listener = sampleObservable(srollEvent); scrollListeners.set(scrollTarget, listener); return listener; }); /** * @fileoverview added by tsickle * @suppress {checkTypes,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc */ /** @type {?} */ var isVisible = ( /** * @param {?} __0 * @param {?=} getWindow * @return {?} */function (_a, getWindow) { var element = _a.element, offset = _a.offset, scrollContainer = _a.scrollContainer; if (getWindow === void 0) { getWindow = ( /** * @return {?} */function () { return window; }); } /** @type {?} */ var elementBounds = Rect.fromElement(element); if (elementBounds === Rect.empty) { return false; } /** @type {?} */ var windowBounds = Rect.fromWindow(getWindow()); elementBounds.inflate(offset); if (scrollContainer) { /** @type {?} */ var scrollContainerBounds = Rect.fromElement(scrollContainer); /** @type {?} */ var intersection = scrollContainerBounds.getIntersectionWith(windowBounds); return elementBounds.intersectsWith(intersection); } else { return elementBounds.intersectsWith(windowBounds); } }); /** @type {?} */ var getObservable = ( /** * @param {?} attributes * @return {?} */function (attributes) { if (attributes.scrollObservable) { return attributes.scrollObservable.pipe(operators.startWith('')); } if (attributes.scrollContainer) { return getScrollListener(attributes.scrollContainer); } return getScrollListener(isWindowDefined() ? window : undefined); }); /** @type {?} */ var scrollPreset = Object.assign({}, sharedPreset, { isVisible: isVisible, getObservable: getObservable }); /** * @fileoverview added by tsickle * @suppress {checkTypes,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc */ /** * @fileoverview added by tsickle * @suppress {checkTypes,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc */ /** * @template E * @param {?=} options * @return {?} */ function cretateHooks(options) { if (!options) { return scrollPreset; } /** @type {?} */ var hooks = {}; if (options.preset) { Object.assign(hooks, options.preset); } else { Object.assign(hooks, scrollPreset); } Object.keys(options) .filter(( /** * @param {?} key * @return {?} */function (key) { return key !== 'preset'; })) .forEach(( /** * @param {?} key * @return {?} */function (key) { hooks[key] = options[key]; })); return ( /** @type {?} */(hooks)); } /** * @fileoverview added by tsickle * @suppress {checkTypes,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc */ /** * @template E * @param {?} hookSet * @param {?} attributes * @return {?} */ function lazyLoadImage(hookSet, attributes) { return ( /** * @param {?} scrollObservable * @return {?} */function (scrollObservable) { return scrollObservable.pipe(operators.filter(( /** * @param {?} event * @return {?} */function (event) { return hookSet.isVisible({ element: attributes.element, event: event, offset: attributes.offset, scrollContainer: attributes.scrollContainer }); })), operators.take(1), operators.mergeMap(( /** * @return {?} */function () { return hookSet.loadImage(attributes); })), operators.tap(( /** * @param {?} imagePath * @return {?} */function (imagePath) { return hookSet.setLoadedImage({ element: attributes.element, imagePath: imagePath, useSrcset: attributes.useSrcset }); })), operators.map(( /** * @return {?} */function () { return true; })), operators.catchError(( /** * @return {?} */function () { hookSet.setErrorImage(attributes); return rxjs.of(false); })), operators.tap(( /** * @return {?} */function () { return hookSet.finally(attributes); }))); }); } /** * @fileoverview added by tsickle * @suppress {checkTypes,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc */ var LazyLoadImageDirective = /** @class */ (function () { function LazyLoadImageDirective(el, ngZone, platformId, options) { this.platformId = platformId; // Whether srcset attribute should be used instead of src this.onLoad = new core.EventEmitter(); // Callback when an image is loaded this.elementRef = el; this.ngZone = ngZone; this.propertyChanges$ = new rxjs.ReplaySubject(); this.hooks = cretateHooks(options); } /** * @return {?} */ LazyLoadImageDirective.prototype.ngOnChanges = /** * @return {?} */ function () { this.propertyChanges$.next({ element: this.elementRef.nativeElement, imagePath: this.lazyImage, defaultImagePath: this.defaultImage, errorImagePath: this.errorImage, useSrcset: this.useSrcset, offset: this.offset | 0, scrollContainer: this.scrollTarget, scrollObservable: this.scrollObservable }); }; /** * @return {?} */ LazyLoadImageDirective.prototype.ngAfterContentInit = /** * @return {?} */ function () { var _this = this; // Disable lazy load image in server side if (common.isPlatformServer(this.platformId)) { this.scrollSubscription = this.propertyChanges$ .pipe(operators.tap(( /** * @param {?} attributes * @return {?} */function (attributes) { return _this.hooks.setup(attributes); })), operators.first()) .subscribe(); return null; } this.ngZone.runOutsideAngular(( /** * @return {?} */function () { _this.scrollSubscription = _this.propertyChanges$ .pipe(operators.tap(( /** * @param {?} attributes * @return {?} */function (attributes) { return _this.hooks.setup(attributes); })), operators.switchMap(( /** * @param {?} attributes * @return {?} */function (attributes) { return _this.hooks .getObservable(attributes) .pipe(lazyLoadImage(_this.hooks, attributes)); }))) .subscribe(( /** * @param {?} success * @return {?} */function (success) { return _this.onLoad.emit(success); })); })); }; /** * @return {?} */ LazyLoadImageDirective.prototype.ngOnDestroy = /** * @return {?} */ function () { [this.scrollSubscription] .filter(( /** * @param {?} subscription * @return {?} */function (subscription) { return subscription && !subscription.isUnsubscribed; })) .forEach(( /** * @param {?} subscription * @return {?} */function (subscription) { return subscription.unsubscribe(); })); }; LazyLoadImageDirective.decorators = [ { type: core.Directive, args: [{ selector: '[lazyLoad]' },] } ]; /** @nocollapse */ LazyLoadImageDirective.ctorParameters = function () { return [ { type: core.ElementRef }, { type: core.NgZone }, { type: Object, decorators: [{ type: core.Inject, args: [core.PLATFORM_ID,] }] }, { type: undefined, decorators: [{ type: core.Optional }, { type: core.Inject, args: ['options',] }] } ]; }; LazyLoadImageDirective.propDecorators = { lazyImage: [{ type: core.Input, args: ['lazyLoad',] }], defaultImage: [{ type: core.Input }], errorImage: [{ type: core.Input }], scrollTarget: [{ type: core.Input }], scrollObservable: [{ type: core.Input }], offset: [{ type: core.Input }], useSrcset: [{ type: core.Input }], onLoad: [{ type: core.Output }] }; return LazyLoadImageDirective; }()); /** * @fileoverview added by tsickle * @suppress {checkTypes,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc */ var LazyLoadImageModule = /** @class */ (function () { function LazyLoadImageModule() { } /** * @param {?} options * @return {?} */ LazyLoadImageModule.forRoot = /** * @param {?} options * @return {?} */ function (options) { return { ngModule: LazyLoadImageModule, providers: [{ provide: 'options', useValue: options }] }; }; LazyLoadImageModule.decorators = [ { type: core.NgModule, args: [{ declarations: [LazyLoadImageDirective], exports: [LazyLoadImageDirective] },] } ]; return LazyLoadImageModule; }()); /** * @fileoverview added by tsickle * @suppress {checkTypes,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc */ /** @type {?} */ var observers = new WeakMap(); /** @type {?} */ var intersectionSubject = new rxjs.Subject(); /** * @param {?} entrys * @return {?} */ function loadingCallback(entrys) { entrys.forEach(( /** * @param {?} entry * @return {?} */function (entry) { return intersectionSubject.next(entry); })); } /** @type {?} */ var getIntersectionObserver = ( /** * @param {?} attributes * @return {?} */function (attributes) { if (!attributes.scrollContainer && !isWindowDefined()) { return rxjs.empty(); } /** @type {?} */ var options = { root: attributes.scrollContainer }; if (attributes.offset) { options.rootMargin = attributes.offset + "px"; } /** @type {?} */ var scrollContainer = attributes.scrollContainer || window; /** @type {?} */ var observer = observers.get(scrollContainer); if (!observer) { observer = new IntersectionObserver(loadingCallback, options); observers.set(scrollContainer, observer); } observer.observe(attributes.element); return rxjs.Observable.create(( /** * @param {?} obs * @return {?} */function (obs) { /** @type {?} */ var subscription = intersectionSubject.pipe(operators.filter(( /** * @param {?} entry * @return {?} */function (entry) { return entry.target === attributes.element; }))).subscribe(obs); return ( /** * @return {?} */function () { subscription.unsubscribe(); observer.unobserve(attributes.element); }); })); }); /** * @fileoverview added by tsickle * @suppress {checkTypes,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc */ /** @type {?} */ var isVisible$1 = ( /** * @param {?} __0 * @return {?} */function (_a) { var event = _a.event; return event.isIntersecting; }); /** @type {?} */ var getObservable$1 = ( /** * @param {?} attributes * @param {?=} _getInterObserver * @return {?} */function (attributes, _getInterObserver) { if (_getInterObserver === void 0) { _getInterObserver = getIntersectionObserver; } if (attributes.scrollObservable) { return attributes.scrollObservable; } return _getInterObserver(attributes); }); /** @type {?} */ var intersectionObserverPreset = Object.assign({}, sharedPreset, { isVisible: isVisible$1, getObservable: getObservable$1 }); /** * @fileoverview added by tsickle * @suppress {checkTypes,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc */ /** * @fileoverview added by tsickle * @suppress {checkTypes,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc */ /** * @fileoverview added by tsickle * @suppress {checkTypes,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc */ exports.LazyLoadImageDirective = LazyLoadImageDirective; exports.LazyLoadImageModule = LazyLoadImageModule; exports.intersectionObserverPreset = intersectionObserverPreset; exports.scrollPreset = scrollPreset; exports.default = LazyLoadImageModule; Object.defineProperty(exports, '__esModule', { value: true }); }))); //# sourceMappingURL=ng-lazyload-image-angular-universal.umd.js.map