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
JavaScript
(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