diff --git a/packages/html/__tests__/HtmlImageLayer.test.ts b/packages/html/__tests__/HtmlImageLayer.test.ts
index 66f49ed7..cb013f60 100644
--- a/packages/html/__tests__/HtmlImageLayer.test.ts
+++ b/packages/html/__tests__/HtmlImageLayer.test.ts
@@ -127,4 +127,32 @@ describe('HtmlImageLayer tests', function () {
// test that the src which set by HtmlImageLayer contains last character "B" which is the character of placeholder plugin
expect(imgSetAttributeSpy.mock.calls[0][1]).toEqualAnalyticsToken('BAXAABABB');
});
+
+ it('should verfiy no responsive image request is fired with placeholder plugin', async function () {
+ const OriginalImage = Image;
+ // mocking Image constructor in order to simulate firing 'load' event
+ jest.spyOn(global, "Image").mockImplementation(() => {
+ const img = new OriginalImage();
+ setTimeout(() => {
+ img.dispatchEvent(new Event("load"));
+ }, 10)
+ return img;
+
+ })
+ const parentElement = document.createElement('div');
+ const img = document.createElement('img');
+ parentElement.append(img);
+ const imgSrcSpy = jest.spyOn(img, 'src', 'set');
+ const imgSetAttributeSpy = jest.spyOn(img, 'setAttribute');
+ new HtmlImageLayer(img, cldImage, [responsive({steps: 200}),placeholder()], sdkAnalyticsTokens);
+ await flushPromises();
+ expect(imgSrcSpy).toHaveBeenCalledTimes(1);
+ // test that the initial src is set to a token contains last character "B" which is the character of placeholder plugin
+ const imgSrcSpyAnalyticsToken = imgSrcSpy.mock.calls[0][0];
+ expect(imgSrcSpyAnalyticsToken).toEqualAnalyticsToken('BAXAABABB');
+ await flushPromises();
+ await flushPromises();
+ //TODO we want to check the second image that is loaded for the presence of a w_ paramter
+ expect(img.src).toBe("abc");
+ });
});
diff --git a/packages/html/src/plugins/accessibility.ts b/packages/html/src/plugins/accessibility.ts
index db6862f0..ea3ceb7a 100644
--- a/packages/html/src/plugins/accessibility.ts
+++ b/packages/html/src/plugins/accessibility.ts
@@ -22,7 +22,7 @@ export function accessibility({mode = 'darkmode'}: { mode?: string; }={}): Plugi
* @param pluginCloudinaryImage {CloudinaryImage}
* @param htmlPluginState {htmlPluginState} Holds cleanup callbacks and event subscriptions.
*/
-export function accessibilityPlugin(mode: AccessibilityMode, element: HTMLImageElement, pluginCloudinaryImage: CloudinaryImage, htmlPluginState: HtmlPluginState): Promise | boolean {
+export function accessibilityPlugin(mode: AccessibilityMode, element: HTMLImageElement, pluginCloudinaryImage: CloudinaryImage, htmlPluginState: HtmlPluginState, plugins?: Plugin[]): Promise | boolean {
if(isBrowser()){
if(!isImage(element)) return;
diff --git a/packages/html/src/plugins/lazyload.ts b/packages/html/src/plugins/lazyload.ts
index c3a5d1c8..a0ba9296 100644
--- a/packages/html/src/plugins/lazyload.ts
+++ b/packages/html/src/plugins/lazyload.ts
@@ -29,7 +29,7 @@ export function lazyload({rootMargin='0px', threshold=0.1}:{rootMargin?: string,
* @param cloudinaryImage {CloudinaryImage}
* @param htmlPluginState {HtmlPluginState} Holds cleanup callbacks and event subscriptions.
*/
-function lazyloadPlugin(rootMargin='0px', threshold=0.1 , element: HTMLImageElement | HTMLVideoElement, cloudinaryImage: CloudinaryImage, htmlPluginState: HtmlPluginState): Promise | boolean {
+function lazyloadPlugin(rootMargin='0px', threshold=0.1 , element: HTMLImageElement | HTMLVideoElement, cloudinaryImage: CloudinaryImage, htmlPluginState: HtmlPluginState, plugins?: Plugin[]): Promise | boolean {
// if SSR skip plugin
if(!isBrowser()) return false;
diff --git a/packages/html/src/plugins/placeholder.ts b/packages/html/src/plugins/placeholder.ts
index 3754e11c..d817b90d 100644
--- a/packages/html/src/plugins/placeholder.ts
+++ b/packages/html/src/plugins/placeholder.ts
@@ -28,7 +28,9 @@ export function placeholder({mode='vectorize'}:{mode?: string}={}): Plugin{
* @param htmlPluginState {htmlPluginState} Holds cleanup callbacks and event subscriptions.
* @param baseAnalyticsOptions {BaseAnalyticsOptions} analytics options for the url to be created
*/
-function placeholderPlugin(mode: PlaceholderMode, element: HTMLImageElement, pluginCloudinaryImage: CloudinaryImage, htmlPluginState: HtmlPluginState, baseAnalyticsOptions?: BaseAnalyticsOptions): Promise | boolean {
+// TODO: Optionally we might want to hold of with rendering
+// Maybe there is something in the responsive plugin already that should be moved here too?
+function placeholderPlugin(mode: PlaceholderMode, element: HTMLImageElement, pluginCloudinaryImage: CloudinaryImage, htmlPluginState: HtmlPluginState, baseAnalyticsOptions?: BaseAnalyticsOptions, plugins?: Plugin[]): Promise | boolean {
// @ts-ignore
// If we're using an invalid mode, we default to vectorize
if(!PLACEHOLDER_IMAGE_OPTIONS[mode]){
diff --git a/packages/html/src/plugins/responsive.ts b/packages/html/src/plugins/responsive.ts
index 24092aca..f6b55efe 100644
--- a/packages/html/src/plugins/responsive.ts
+++ b/packages/html/src/plugins/responsive.ts
@@ -29,7 +29,9 @@ export function responsive({steps}:{steps?: number | number[]}={}): Plugin{
* @param htmlPluginState {HtmlPluginState} holds cleanup callbacks and event subscriptions
* @param analyticsOptions {BaseAnalyticsOptions} analytics options for the url to be created
*/
-function responsivePlugin(steps?: number | number[], element?:HTMLImageElement, responsiveImage?: CloudinaryImage, htmlPluginState?: HtmlPluginState, baseAnalyticsOptions?: BaseAnalyticsOptions): Promise | boolean {
+function responsivePlugin(steps?: number | number[], element?:HTMLImageElement, responsiveImage?: CloudinaryImage, htmlPluginState?: HtmlPluginState, baseAnalyticsOptions?: BaseAnalyticsOptions, plugins?: Plugin[]): Promise | boolean {
+
+ console.debug(plugins);
if(!isBrowser()) return true;
@@ -46,7 +48,28 @@ function responsivePlugin(steps?: number | number[], element?:HTMLImageElement,
// Use a tagged generic action that can be later searched and replaced.
responsiveImage.addAction(new Action().setActionTag('responsive'));
// Immediately run the resize plugin, ensuring that first render gets a responsive image.
- onResize(steps, element, responsiveImage, analyticsOptions);
+ // If we disable initial run entirely the placeholder will load non-resposive image
+
+ const regex = /.*placeholder.*/gm;
+ let shouldRunImmediately = true // TODO: logic to test if there is a placeholder plugin
+
+ plugins.forEach((p)=>{
+ if (regex.exec(p.name) !== null){
+ console.debug("found the placeholder plugin!!!");
+ shouldRunImmediately = false;
+ }
+ });
+
+
+ console.debug("analyticsOptions: ",analyticsOptions);
+ console.debug("analyticsOptinos name: ",analyticsOptions.trackedAnalytics);
+
+ if(shouldRunImmediately) {
+ onResize(steps, element, responsiveImage, analyticsOptions);
+ } else {
+ // Probably this has to run on else, see comments in line 83
+ updateByContainerWidth(steps, element, responsiveImage);
+ }
let resizeRef: any;
htmlPluginState.pluginEventSubscription.push(()=>{
@@ -67,8 +90,20 @@ function responsivePlugin(steps?: number | number[], element?:HTMLImageElement,
* @param analyticsOptions {AnalyticsOptions} analytics options for the url to be created
*/
function onResize(steps?: number | number[], element?:HTMLImageElement, responsiveImage?: CloudinaryImage, analyticsOptions?: AnalyticsOptions){
+
updateByContainerWidth(steps, element, responsiveImage);
+ // TODO: 1. Responsive should not load the image if placeholder is running next
+ // It has to know, the placeholder is in the plugins
+ // A. Get plugins as a new fifth argument of the `responsivePlugin` function
+ // B. Loop over plugins and check if any of plugin.name is equal to "bound placeholderPlugin"
+
+ // If we disable each onResize then placeholder will render original image (!)
+ // So this cannot be conditional because we want run it always on window resize later
element.src = responsiveImage.toURL(analyticsOptions);
+
+ // So the magic to make sure placeholder loads large image with responsive
+ // Is done by the updateByContainerWidth function call
+ // ... and we might need to make sure it's called in line 53 if shouldRunImmediately is false
}
/**
diff --git a/packages/html/src/types.ts b/packages/html/src/types.ts
index 73314a18..c562be0c 100644
--- a/packages/html/src/types.ts
+++ b/packages/html/src/types.ts
@@ -4,7 +4,7 @@ import {
ITrackedPropertiesThroughAnalytics
} from "@cloudinary/url-gen/sdkAnalytics/interfaces/ITrackedPropertiesThroughAnalytics";
-export type Plugin = (element: HTMLImageElement|HTMLVideoElement, cloudinaryImage: CloudinaryImage, htmlPluginState?: HtmlPluginState, baseAnalyticsOptions?: BaseAnalyticsOptions) => Promise;
+export type Plugin = (element: HTMLImageElement|HTMLVideoElement, cloudinaryImage: CloudinaryImage, htmlPluginState?: HtmlPluginState, baseAnalyticsOptions?: BaseAnalyticsOptions,plugins?:Plugins) => Promise;
export type Plugins = Plugin[];
diff --git a/packages/html/src/utils/render.ts b/packages/html/src/utils/render.ts
index fc8825d5..246d8669 100644
--- a/packages/html/src/utils/render.ts
+++ b/packages/html/src/utils/render.ts
@@ -1,3 +1,4 @@
+import { lazyload } from 'plugins/lazyload';
import {Plugins, HtmlPluginState, BaseAnalyticsOptions, PluginResponse} from '../types'
import {CloudinaryVideo, CloudinaryImage} from "@cloudinary/url-gen";
@@ -14,10 +15,24 @@ export async function render(element: HTMLImageElement | HTMLVideoElement, plugi
if (plugins === undefined) return;
let response: PluginResponse;
for (let i = 0; i < plugins.length; i++) {
- response = await plugins[i](element, pluginCloudinaryAsset, pluginState, analyticsOptions);
+ // TODO: We have to pass all plugins to each plugin (so that each can determine what to do)
+ // 1. Pass plugins as a fifth parameter below (after analyticsOptions)
+ //
+ // 2. Inside each plugin we can use `name` property of the plugin functions to see what was added
+ //
+ // Example given the plugins look like below:
+ // const plugins = [lazyload(), responsive()]
+ // Then if you console log plugins[0].name you should get 'bound lazyloadPlugin'
+ // plugins[0].name === 'bound lazyloadPlugin'
+ const pluginResponse = await plugins[i](element, pluginCloudinaryAsset, pluginState, analyticsOptions, plugins);
if (response === 'canceled') {
break;
}
+ if(typeof pluginResponse === 'object') {
+ response = {...response, ...pluginResponse};
+ } else {
+ response = pluginResponse
+ }
}
if (response !== 'canceled') {
return response;
diff --git a/playground/html/index.html b/playground/html/index.html
index 0f3224ad..bb633a43 100644
--- a/playground/html/index.html
+++ b/playground/html/index.html
@@ -4,9 +4,15 @@
@Cloudinary/html Playground
+
-
+ Test Environment
+