diff --git a/src/components/img/img-loader.ts b/src/components/img/img-loader.ts index e7d98a0efa..402ea14d66 100644 --- a/src/components/img/img-loader.ts +++ b/src/components/img/img-loader.ts @@ -20,7 +20,9 @@ export class ImgLoader { // looks like there's already an active http request going on // for this same source, so let's just add another listener img.xhr.addEventListener('load', (xhrEvent) => { - onXhrLoad(callback, xhrEvent, useCache, img, this.imgs); + const target: any = xhrEvent.target; + const contentType = target.getResponseHeader('Content-Type'); + onXhrLoad(callback, target.status, contentType, target.response, useCache, img, this.imgs); }); img.xhr.addEventListener('error', (xhrErrorEvent) => { onXhrError(callback, img, xhrErrorEvent); @@ -30,7 +32,7 @@ export class ImgLoader { if (!img) { // no image data yet, so let's create it - img = { src: src }; + img = { src: src, len: 0 }; this.imgs.push(img); } @@ -41,7 +43,9 @@ export class ImgLoader { // add the listeners if it loaded or errored img.xhr.addEventListener('load', (xhrEvent) => { - onXhrLoad(callback, xhrEvent, useCache, img, this.imgs); + const target: any = xhrEvent.target; + const contentType = target.getResponseHeader('Content-Type'); + onXhrLoad(callback, target.status, contentType, target.response, useCache, img, this.imgs); }); img.xhr.addEventListener('error', (xhrErrorEvent) => { onXhrError(callback, img, xhrErrorEvent); @@ -64,20 +68,19 @@ export class ImgLoader { } -function onXhrLoad(callback: ImgLoadCallback, ev: any, useCache: boolean, img: ImgData, imgs: ImgData[]) { +export function onXhrLoad(callback: ImgLoadCallback, status: number, contentType: string, responseData: ArrayBuffer, useCache: boolean, img: ImgData, imgs: ImgData[]) { if (!callback) { - return; + return null; } // the http request has been loaded // create a rsp object to send back to the main thread - const status: number = ev.target.status; let datauri: string = null; if (status === 200) { // success!! // now let's convert the response arraybuffer data into a datauri - datauri = getDataUri(ev.target.getResponseHeader('Content-Type'), ev.target.response); + datauri = getDataUri(contentType, responseData); if (useCache) { // if the image was successfully downloaded @@ -86,17 +89,7 @@ function onXhrLoad(callback: ImgLoadCallback, ev: any, useCache: boolean, img: I img.datauri = datauri; img.len = datauri.length; - // let's loop through all our cached data and if we go - // over our limit then let's clean it out a bit - // oldest data should go first - var cacheSize = 0; - for (var i = imgs.length - 1; i >= 0; i--) { - cacheSize += imgs[i].len; - if (cacheSize > CACHE_LIMIT) { - console.debug(`img-loader, clear: ${imgs[i].src}, len: ${imgs[i].len}`); - imgs.splice(i, 1); - } - } + cleanCache(imgs, CACHE_LIMIT); } } @@ -105,6 +98,22 @@ function onXhrLoad(callback: ImgLoadCallback, ev: any, useCache: boolean, img: I } +export function cleanCache(imgs: ImgData[], cacheLimit: number) { + // let's loop through all our cached data and if we go + // over our limit then let's clean it out a bit + // oldest data should go first + let cacheSize = 0; + for (var i = imgs.length - 1; i >= 0; i--) { + cacheSize += imgs[i].len; + if (cacheSize > cacheLimit) { + console.debug(`img-loader, clear cache`); + imgs.splice(0, i + 1); + break; + } + } +} + + function onXhrError(callback: ImgLoadCallback, imgData: ImgData, err: ErrorEvent) { // darn, we got an error! callback && callback(0, (err.message || ''), null); diff --git a/src/components/img/test/img.spec.ts b/src/components/img/test/img.spec.ts index 2223fe6d82..01eeca0d8e 100644 --- a/src/components/img/test/img.spec.ts +++ b/src/components/img/test/img.spec.ts @@ -1,13 +1,55 @@ import { ElementRef, Renderer } from '@angular/core'; import { Content } from '../../content/content'; import { Img } from '../img'; -import { ImgLoader } from '../img-loader'; +import { ImgLoader, ImgData, ImgLoadCallback, cleanCache, onXhrLoad } from '../img-loader'; import { mockContent, MockDomController, mockElementRef, mockPlatform, mockRenderer, mockZone } from '../../../util/mock-providers'; import { Platform } from '../../../platform/platform'; describe('Img', () => { + describe('cleanCache', () => { + + it('should clean out oldest img data when passing cache limit', () => { + const imgs: ImgData[] = [ + { src: 'img1.jpg', len: 100 }, + { src: 'img2.jpg', len: 0 }, + { src: 'img3.jpg', len: 100 }, + { src: 'img4.jpg', len: 100 }, + ]; + cleanCache(imgs, 100); + expect(imgs.length).toEqual(1); + expect(imgs[0].src).toEqual('img4.jpg'); + }); + + }); + + describe('onXhrLoad', () => { + + it('should cache img response', () => { + const callback: ImgLoadCallback = () => {}; + const status = 200; + const contentType = 'image/jpeg'; + const responseData = new ArrayBuffer(0); + const useCache = true; + const imgData: ImgData = { + src: 'image.jpg' + }; + const imgs: ImgData[] = []; + + onXhrLoad(callback, status, contentType, responseData, useCache, imgData, imgs); + + expect(imgData.datauri).toEqual('data:image/jpeg;base64,'); + expect(imgData.len).toEqual(imgData.datauri.length); + }); + + it('should do nothing when theres no callback', () => { + const r = onXhrLoad(null, 0, 'image/jpeg', new ArrayBuffer(0), true, null, null); + expect(r).toEqual(null); + }); + + }); + describe('reset', () => { it('should clear rendering src', () => {