Skip to content

Commit

Permalink
Embed banners improvements
Browse files Browse the repository at this point in the history
- improved embed banners handling
- responsive breakpoints from external banners can be now manager by JS client after invoking the method `ExternalBanner.delegateResponsiveBehaviour()`
  • Loading branch information
tg666 committed Dec 12, 2023
1 parent a9549bb commit 7484794
Show file tree
Hide file tree
Showing 18 changed files with 512 additions and 239 deletions.
6 changes: 3 additions & 3 deletions demo/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
<!-- Custom styles -->
<style>
.container {
max-width: 1000px;
max-width: 1200px;
text-align: center;
margin: 0 auto;
}
Expand Down Expand Up @@ -58,7 +58,7 @@
url: 'http://localhost:8888',
channel: 'amp-demo',
resources: {
role: 'guest'
roles: 'guest',
},
metrics: {
receiver: 'debug',
Expand Down Expand Up @@ -225,7 +225,7 @@ <h2>Embed banner:</h2>
}

window.AMPClient.on('amp:banner:state-changed', function (banner) {
if ('RENDERED' !== banner.state || !banner.positionData.isMultiple()) {
if ('RENDERED' !== banner.state || !banner.positionData.isMultiple() || banner.isEmbed()) {
return;
}

Expand Down
71 changes: 34 additions & 37 deletions src/banner/banner-manager.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -4,27 +4,41 @@ import { EmbedBanner } from './embed/embed-banner.mjs';
import { Banner } from './banner.mjs';
import { State } from './state.mjs';
import { Fingerprint } from './fingerprint.mjs';
import { Resource } from '../request/resource.mjs';
import { SequenceGenerator } from '../utils/sequence-generator.mjs';
import { getHtmlElement } from '../utils/dom-helpers.mjs';

export class BannerManager {
#eventBus;
#dimensionsProvider;
#bannerRenderer;
#sequenceGenerator;
#banners = [];

constructor(eventBus) {
/**
* @param {EventBus} eventBus
* @param {DimensionsProvider} dimensionsProvider
* @param {BannerRenderer|null} bannerRenderer
*/
constructor(
eventBus,
dimensionsProvider,
bannerRenderer = null,
) {
this.#eventBus = eventBus;
this.#dimensionsProvider = dimensionsProvider;
this.#bannerRenderer = bannerRenderer;
this.#sequenceGenerator = new SequenceGenerator();

this.STATE = State;
}

addExternalBanner(element) {
element = this.#getElement(element);
element = getHtmlElement(element);

element.setAttribute('data-amp-attached', '');

const banner = new ExternalBanner(
this.#dimensionsProvider,
this.#eventBus,
this.#sequenceGenerator.getNextIdentifier(),
element,
Expand All @@ -36,22 +50,22 @@ export class BannerManager {
}

addManagedBanner(element, position, resources = {}, options = {}) {
const resourceArr = [];
let key;
element = this.#getElement(element);
if (null === this.#bannerRenderer) {
throw new Error(`Unable to add managed banner, renderer is not provided.`);
}

element.setAttribute('data-amp-attached', '');
element = getHtmlElement(element);

for (key in resources) {
resourceArr.push(new Resource(key, resources[key]));
}
element.setAttribute('data-amp-attached', '');

const banner = new ManagedBanner(
this.#dimensionsProvider,
this.#bannerRenderer,
this.#eventBus,
this.#sequenceGenerator.getNextIdentifier(),
element,
position,
resourceArr,
resources,
options,
);

Expand All @@ -61,7 +75,7 @@ export class BannerManager {
}

addEmbedBanner(iframe, position, options) {
iframe = this.#getElement(iframe);
iframe = getHtmlElement(iframe);

iframe.setAttribute('data-amp-attached', '');

Expand All @@ -78,6 +92,13 @@ export class BannerManager {
return banner;
}

removeBanner(banner) {
const length = this.#banners.length;
this.#banners = this.#banners.filter(b => b !== banner);

return length !== this.#banners.length;
}

getBannersByState({state, managed = true, external = true, embed = true}) {
return this.#banners.filter(banner => {
if (!(banner instanceof Banner) || banner.state !== state) {
Expand All @@ -92,7 +113,7 @@ export class BannerManager {
const fingerprintValue = fingerprint instanceof Fingerprint ? fingerprint.value : fingerprint;

for (let banner of this.#banners) {
if (banner in EmbedBanner) {
if (banner instanceof EmbedBanner) {
continue;
}

Expand All @@ -115,28 +136,4 @@ export class BannerManager {

return null;
}

#getElement(el) {
if (el instanceof HTMLElement) {
return el;
}

if (typeof el !== 'string') {
throw new TypeError('Element must be instance of HTMLElement or String');
}

let htmlEl;

if ('#' === el.charAt(0)) {
htmlEl = document.getElementById(el.slice(1));
} else {
htmlEl = document.querySelector(el);
}

if (!(htmlEl instanceof HTMLElement)) {
throw new TypeError('Selector ' + el + ' is invalid.');
}

return htmlEl;
}
}
6 changes: 6 additions & 0 deletions src/banner/banner.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,9 @@ export class Banner {
return this.#uid;
}

/**
* @returns {HTMLElement}
*/
get element() {
return this.#element;
}
Expand Down Expand Up @@ -92,6 +95,9 @@ export class Banner {
return null;
}

redrawIfNeeded() {
}

isManaged() {
return false;
}
Expand Down
72 changes: 57 additions & 15 deletions src/banner/external/external-banner.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,14 @@ import { Banner } from '../banner.mjs';
import { Fingerprint } from '../fingerprint.mjs';
import { PositionData } from '../position-data.mjs';
import { AttributesParser } from '../attributes-parser.mjs';
import { Contents } from '../responsive/contents.mjs';

export class ExternalBanner extends Banner {
#fingerprints = [];
#breakpointsByBannerId = {};
#contentsByBannerId = null;
#responsiveBehaviourDelegated = false;

constructor(eventBus, uid, element) {
constructor(dimensionsProvider, eventBus, uid, element) {
if (!('ampBannerExternal' in element.dataset)) {
throw new Error(`Unable to initialize ExternalBanner from element that does not have an attribute "data-amp-external".`);
}
Expand All @@ -25,7 +27,7 @@ export class ExternalBanner extends Banner {
super(eventBus, uid, element, positionData.code, options);

const fingerprints = [];
const breakpointsByBannerId = {};
const contentsByBannerId = {};

for (let banner of element.querySelectorAll('[data-amp-banner-fingerprint]')) {
const fingerprint = Fingerprint.createFromValue(banner.dataset.ampBannerFingerprint);
Expand All @@ -36,20 +38,17 @@ export class ExternalBanner extends Banner {
let breakpoint = content.dataset.ampContentBreakpoint;
breakpoint = 'default' === breakpoint ? null : parseInt(breakpoint);

if (!(fingerprint.bannerId in breakpointsByBannerId)) {
breakpointsByBannerId[fingerprint.bannerId] = [];
if (!(fingerprint.bannerId in contentsByBannerId)) {
contentsByBannerId[fingerprint.bannerId] = new Contents(dimensionsProvider, positionData.breakpointType);
}

breakpointsByBannerId[fingerprint.bannerId].push({
breakpoint: breakpoint,
element: content,
})
contentsByBannerId[fingerprint.bannerId].addContent(breakpoint, content);
}
}

this._positionData = positionData;
this.#fingerprints = fingerprints;
this.#breakpointsByBannerId = breakpointsByBannerId;
this.#contentsByBannerId = contentsByBannerId;

this.setState(state.value, state.info);
}
Expand All @@ -65,14 +64,22 @@ export class ExternalBanner extends Banner {
* @returns {number|null}
*/
getCurrenBreakpoint(bannerId) {
const breakpointsByBannerId = this.#breakpointsByBannerId;
const breakpoints = breakpointsByBannerId[bannerId] || [];
const contentsByBannerId = this.#contentsByBannerId;
const contents = contentsByBannerId[bannerId] || null;

for (let breakpoint of breakpoints) {
const style = getComputedStyle(breakpoint.element);
if (null === contents) {
return null;
}

if (this.#responsiveBehaviourDelegated) {
return contents.content ? contents.content.breakpoint : null;
}

for (let content of contents.contents) {
const style = getComputedStyle(content.data);

if ('none' !== style.display) {
return breakpoint.breakpoint;
return content.breakpoint;
}
}

Expand All @@ -82,4 +89,39 @@ export class ExternalBanner extends Banner {
isExternal() {
return true;
}

delegateResponsiveBehaviour() {
if (this.#responsiveBehaviourDelegated) {
return;
}

this.#responsiveBehaviourDelegated = true;
const styles = this.element.querySelectorAll('style');

for (let style of styles) {
style.remove();
}

this.redrawIfNeeded();
}

redrawIfNeeded() {
if (!this.#responsiveBehaviourDelegated) {
return;
}

for (let bannerId in this.#contentsByBannerId) {
const contents = this.#contentsByBannerId[bannerId];

if (!contents.needRedraw()) {
continue;
}

const currentContent = contents.content;

for (let content of contents.contents) {
content.data.style.display = content === currentContent ? 'block' : 'none';
}
}
}
}
Loading

0 comments on commit 7484794

Please sign in to comment.