import { queryOne } from '@ecl/dom-utils';
/**
* @param {HTMLElement} element DOM element for component instantiation and scope
* @param {Object} options
* @param {Boolean} options.attachClickListener Whether or not to bind click events
* @param (String) options.targetSelector The selector of the element where to attach the click listener
* @param (String) options.titleSelector The selector of the element containing the link
* @param (Integer) options.maxIterations Maximum number of ancestors to look for the element
*/
export class ContentBlock {
/**
* @static
* Shorthand for instance creation and initialisation.
*
* @param {HTMLElement} root DOM element for component instantiation and scope
*
* @return {ContentBlock} An instance of ContentBlock.
*/
static autoInit(root, { CONTENT_BLOCK: defaultOptions = {} } = {}) {
const contentBlock = new ContentBlock(root, defaultOptions);
contentBlock.init();
root.ECLContentBlock = contentBlock;
return contentBlock;
}
constructor(
element,
{
targetSelector = '[data-ecl-picture-link]',
titleSelector = '[data-ecl-title-link]',
attachClickListener = true,
maxIterations = 1,
withTitleAttr = false,
} = {},
) {
// Check element
if (!element || element.nodeType !== Node.ELEMENT_NODE) {
throw new TypeError(
'DOM element should be given to initialize this widget.',
);
}
this.element = element;
// Options
this.targetSelector = targetSelector;
this.titleSelector = titleSelector;
this.attachClickListener = attachClickListener;
this.maxIterations = maxIterations;
this.withTitleAttr = withTitleAttr;
// Bind `this` for use in callbacks
this.linkTo = this.linkTo.bind(this);
this.findElementInCommonAncestor =
this.findElementInCommonAncestor.bind(this);
}
/**
* Initialise component.
*/
init() {
if (!ECL) {
throw new TypeError('Called init but ECL is not present');
}
ECL.components = ECL.components || new Map();
this.picture = this.findElementInCommonAncestor(
this.element,
this.targetSelector,
this.maxIterations,
);
// Exit early if no picture is present.
if (!this.picture) {
return;
}
this.title = queryOne(this.titleSelector, this.element);
this.linkEl = this.title ? queryOne('a', this.title) : false;
if (this.linkEl) {
this.picture.style.cursor = 'pointer';
const img = queryOne('img', this.picture);
if (img && this.withTitleAttr) {
img.title = this.constructor.convertToFullURL(
this.linkEl.getAttribute('href'),
);
}
if (this.attachClickListener) {
this.picture.addEventListener('click', this.linkTo);
}
}
this.element.setAttribute('data-ecl-auto-initialized', true);
ECL.components.set(this.element, this);
}
/**
* Redirect the user to the desired url.
*/
linkTo() {
if (this.linkEl) {
// Click the linking element.
this.linkEl.click();
}
}
/**
* Find an element in a common ancestor.
*
* @param {HTMLElement} element
* @param {string} selector
*/
findElementInCommonAncestor(element, selector, maxIterations) {
const eureka = queryOne(selector, element);
if (eureka) {
return eureka;
}
if (element.classList.contains('ecl-card__body')) {
maxIterations += 1;
}
if (element === document.documentElement || maxIterations <= 0) {
return null;
}
return this.findElementInCommonAncestor(
element.parentElement,
selector,
maxIterations - 1,
);
}
/**
* Convert a path to a full url.
*
* @param {String} href
*/
static convertToFullURL(href) {
if (href.startsWith('http://') || href.startsWith('https://')) {
return href;
}
const baseUrl = new URL(window.location.href);
const fullUrl = new URL(href, baseUrl);
return fullUrl.href;
}
/**
* Destroy component.
*/
destroy() {
if (this.attachClickListener && this.picture) {
this.picture.removeEventListener('click', this.linkto);
}
if (this.element) {
this.element.removeAttribute('data-ecl-auto-initialized');
ECL.components.delete(this.element);
}
}
}
export default ContentBlock;