web/wp-content/themes/mozilla-builders/static/js/plugins/hang-punctuation.js (46 lines of code) (raw):
/** @type {Record<string, [string, string]>} */
const PUNCTUATION_MARKS = {
'\u201c': ['hang-punc-medium', 'hang-punc-header-medium'], // “ - ldquo - left smart double quote
'\u2018': ['hang-punc-small', 'hang-punc-header-small'], // ‘ - lsquo - left smart single quote
'\u0022': ['hang-punc-medium', 'hang-punc-header-medium'], // " - ldquo - left dumb double quote
'\u0027': ['hang-punc-small', 'hang-punc-header-small'], // ' - lsquo - left dumb single quote
'\u00AB': ['hang-punc-large', 'hang-punc-header-large'], // « - laquo - left double angle quote
'\u2039': ['hang-punc-medium', 'hang-punc-header-medium'], // ‹ - lsaquo - left single angle quote
'\u201E': ['hang-punc-medium', 'hang-punc-header-medium'], // „ - bdquo - left smart double low quote
'\u201A': ['hang-punc-small', 'hang-punc-header-small'], // ‚ - sbquo - left smart single low quote
};
/** @type {import('alpinejs').PluginCallback} */
export function hangPunctuation(Alpine) {
Alpine.directive('hang-punc', registerRoot);
}
/**
* Hangs the punctuation of the given element if eligible.
*
* @param {HTMLElement} el
*/
function hangIfEligible(el) {
const text = el.innerText || el.textContent;
const marks = Object.keys(PUNCTUATION_MARKS);
marks.forEach(mark => {
if (text.indexOf(mark) === 0) {
const isHeader =
el.tagName === 'H1' ||
el.tagName === 'H2' ||
el.tagName === 'H3' ||
el.tagName === 'H4' ||
el.tagName === 'H5';
const [bodyClass, headerClass] = PUNCTUATION_MARKS[mark];
if (isHeader) {
el.classList.add(headerClass);
} else {
el.classList.add(bodyClass);
}
}
});
}
/**
* Registers the root element.
*
* @param {HTMLElement} el
*/
function registerRoot(el) {
const children = el.childNodes;
// Loop over all direct descendants of the $container
// If it's a blockquote, loop over its direct descendants
for (let i = 0; i < children.length; i += 1) {
const child = children[i];
if (child.tagName === 'blockquote') {
for (let k = 0; k < child.childNodes.length; k += 1) {
hangIfEligible(child.childNodes[k]);
}
} else {
hangIfEligible(child);
}
}
}