editoria11y-1.0.0-alpha8/js/editoria11y-drupal.js
js/editoria11y-drupal.js
/* globals Drupal, drupalSettings, Ed11y, console, ed11yLang, ed11yLangDrupal, editoria11yOptions, drupalTranslations */
/**
* Drupal initializer.
* Launch as behavior and pull variables from config.
*/
// Prevent multiple inits.
let ed11yOnce;
let ed11yInitialized;
let ed11yWaiting = false;
const ed11yInitializer = function () {
"use strict";
if (ed11yInitialized === 'disabled' || ed11yInitialized === 'pending') {
return;
}
ed11yInitialized = 'pending';
const dS = drupalSettings.editoria11y;
const urlParams = new URLSearchParams(window.location.search);
let lang = dS.lang ? dS.lang : 'en';
// @todo 3.x restore language functionality.
/*if (lang !== 'en') {
lang = 'dynamic';
ed11yLang.dynamic = ed11yLangDrupal;
options.langSanitizes = true; // Use Drupal string sanitizer.
}*/
let options = {
/* Alpha-only; will be moved into CSF */
contrastPlugin: true,
readabilityPlugin: true,
developerPlugin: true,
embeddedContentPlugin: true,
linksAdvancedPlugin: true,
checks: {
// Heading checks
HEADING_SKIPPED_LEVEL: {
type: 'warning',
},
HEADING_EMPTY_WITH_IMAGE: true,
HEADING_EMPTY: true,
HEADING_FIRST: true,
HEADING_LONG: {
maxLength: 170,
},
HEADING_MISSING_ONE: true,
// Image checks
MISSING_ALT_LINK: true,
MISSING_ALT_LINK_HAS_TEXT: true,
MISSING_ALT: true,
IMAGE_DECORATIVE_CAROUSEL: {
sources: '.carousel',
},
LINK_IMAGE_NO_ALT_TEXT: true,
LINK_IMAGE_TEXT: true,
IMAGE_FIGURE_DECORATIVE: true,
IMAGE_DECORATIVE: true,
LINK_ALT_FILE_EXT: true,
ALT_FILE_EXT: true,
LINK_PLACEHOLDER_ALT: true,
ALT_PLACEHOLDER: true,
LINK_SUS_ALT: true,
SUS_ALT: true,
LINK_IMAGE_LONG_ALT: {
maxLength: 250,
},
IMAGE_ALT_TOO_LONG: {
maxLength: 250,
},
LINK_IMAGE_ALT: false,
// {dismissAll: true,} default.
LINK_IMAGE_ALT_AND_TEXT: true,
IMAGE_FIGURE_DUPLICATE_ALT: true,
IMAGE_PASS: {
dismissAll: true,
},
ALT_UNPRONOUNCEABLE: true,
LINK_ALT_UNPRONOUNCEABLE: true,
ALT_MAYBE_BAD: {
minLength: 15,
},
LINK_ALT_MAYBE_BAD: {
minLength: 15,
},
// Link checks
DUPLICATE_TITLE: {
dismissAll: true,
},
LINK_EMPTY_LABELLEDBY: true,
LINK_EMPTY_NO_LABEL: true,
LINK_STOPWORD: {
type: 'warning',
},
LINK_STOPWORD_ARIA: true,
LINK_SYMBOLS: true,
LINK_CLICK_HERE: false,
LINK_DOI: {
dismissAll: true,
},
LINK_URL: {
maxLength: 40,
},
LINK_LABEL: {
dismissAll: true,
},
LINK_EMPTY: true,
LINK_IDENTICAL_NAME: {
dismissAll: true,
},
LINK_NEW_TAB: {
dismissAll: true,
},
LINK_FILE_EXT: true,
// Form labels checks
LABELS_MISSING_IMAGE_INPUT: true,
LABELS_INPUT_RESET: true,
LABELS_MISSING_LABEL: true,
LABELS_ARIA_LABEL_INPUT: true,
LABELS_NO_FOR_ATTRIBUTE: true,
LABELS_PLACEHOLDER: false, // @todo change default in config
// Embedded content checks
EMBED_AUDIO: {
sources: '',
},
EMBED_VIDEO: {
sources: '',
},
EMBED_DATA_VIZ: {
sources: '',
},
EMBED_UNFOCUSABLE: true,
EMBED_MISSING_TITLE: true,
EMBED_GENERAL: true,
// Quality assurance checks
QA_BAD_LINK: {
sources: '',
},
QA_STRONG_ITALICS: true,
QA_IN_PAGE_LINK: true,
QA_DOCUMENT: {
sources: '',
dismissAll: true,
},
QA_PDF: {
dismissAll: true,
},
QA_BLOCKQUOTE: true,
TABLES_MISSING_HEADINGS: true,
TABLES_SEMANTIC_HEADING: true,
TABLES_EMPTY_HEADING: true,
QA_FAKE_HEADING: true,
QA_FAKE_LIST: true,
QA_UPPERCASE: true,
QA_UNDERLINE: true,
QA_SUBSCRIPT: true,
QA_NESTED_COMPONENTS: {
sources: '',
},
QA_JUSTIFY: true,
QA_SMALL_TEXT: true,
// Meta checks
META_LANG: true,
META_SCALABLE: true,
META_MAX: true,
META_REFRESH: true,
// Developer checks
DUPLICATE_ID: true,
META_TITLE: true,
UNCONTAINED_LI: true,
TABINDEX_ATTR: true,
HIDDEN_FOCUSABLE: true,
LABEL_IN_NAME: true,
BTN_EMPTY: true,
BTN_EMPTY_LABELLEDBY: true,
BTN_ROLE_IN_NAME: true,
// Contrast checks
CONTRAST_WARNING: {
dismissAll: true,
},
CONTRAST_INPUT: true,
CONTRAST_ERROR: true,
CONTRAST_PLACEHOLDER: true,
CONTRAST_PLACEHOLDER_UNSUPPORTED: true,
CONTRAST_ERROR_GRAPHIC: true,
CONTRAST_WARNING_GRAPHIC: false,
CONTRAST_UNSUPPORTED: {
dismissAll: true,
},
},
/* End alpha-only check configuration */
checkRoot: dS.content_root ? dS.content_root : false,
containerIgnore: !!dS.ignore_elements ?
`#toolbar-administration *, .tabledrag, ${dS.ignore_elements}` :
'#toolbar-administration *, .tabledrag',
panelNoCover: !!dS.panel_no_cover ?
dS.panel_no_cover :
'#klaro-cookie-notice, #klaro_toggle_dialog, .same-page-preview-dialog.ui-dialog-position-side, #gin_sidebar, #admin-toolbar',
panelPinTo: dS.panel_pin === 'left' ? 'left' : 'right',
ignoreAllIfAbsent: !!dS.ignore_all_if_absent ? dS.ignore_all_if_absent : false,
// 100 under contextuals, 491 Gin tools, 1000 CKEditor tools, 1260 modals.
// Ed11y adds 9000 to tips, 99999 to modal tips. Was 491 until May 2025.
buttonZIndex: 100,
autoDetectShadowComponents: !!dS.detect_shadow,
shadowComponents: dS.shadow_components ? dS.shadow_components : false,
linkIgnore:
`[aria-hidden][tabindex="-1"], [id$="-local-tasks"] a,
.contextual-links a,
.block-local-tasks-block a, .filter-help > a, .contextual-region > nav a
${drupalSettings.path.currentPathIsAdmin ? ', a[target="_blank"]' : ''}`,
headerIgnore:
'.filter-guidelines-item *, nav *, [id$="-local-tasks"] *, ' +
'.block-local-tasks-block *, .tabledrag h4',
imageIgnore:
`[aria-hidden], [aria-hidden] img, [role="presentation"],
a[href][aria-label] img, button[aria-label] img,
a[href][aria-labelledby] img, button[aria-labelledby] img`,
lang: lang, // Todo 3.x needed? enUS?
currentPage: dS.page_path,
allowHide: !!dS.allow_hide,
allowOK: !!dS.allow_ok,
syncedDismissals: dS.dismissals,
showDismissed: urlParams.has('ed1ref'),
linkStringsNewWindows: !!dS.link_strings_new_windows ?
new RegExp (dS.link_strings_new_windows, 'gi')
: !!dS.ignore_link_strings ?
new RegExp(dS.ignore_link_strings, 'gi')
: new RegExp ('(' + Drupal.t('download') + ')|(\\s' + Drupal.t('tab') + ')|(' + Drupal.t('window') + ')', 'gi'),
linkIgnoreStrings: !!dS.ignore_link_strings ? dS.ignore_link_strings.split(',')
: [Drupal.t('link is external'), Drupal.t('link sendS email')],
linkIgnoreSelector: !!dS.link_ignore_selector ? dS.link_ignore_selector : false,
hiddenHandlers: !!dS.hidden_handlers ? dS.hidden_handlers : '',
constrainButtons: !!dS.element_hides_overflow ? dS.element_hides_overflow : '',
theme: !!dS.theme ? dS.theme : 'sleekTheme',
embeddedContent: !!dS.embedded_content_warning ? dS.embedded_content_warning : false,
documentLinks: !!dS.download_links ? dS.download_links : `a[href$='.pdf'], a[href*='.pdf?']`,
customTests: dS.custom_tests,
cssUrls: !!dS.css_url ? [dS.css_url + '/library/dist/editoria11y.min.css'] : false,
ignoreTests: dS.ignore_tests ? dS.ignore_tests : false, // @todo merge needs rewrite.
reportsURL: !!dS.view_reports ? dS.dashboard_url : false,
};
// todo postpone: store dismissalKeys for PDFs in page results, and check dismissals table for page level matches on load.
let editors = (Drupal.editors && (Object.hasOwn(Drupal.editors, 'ckeditor5') || Object.hasOwn(Drupal.editors, 'gutenberg')));
// As of 2.2.10, ignore front-end editors (rich text comment fields).
if (editors) {
options.inlineAlerts = false;
const editRoutes = /(node|term|user)\/\d+\/edit/;
// @todo: does this need to be a parameter?
if (!drupalSettings.path.currentPathIsAdmin &&
!drupalSettings.path.currentPath.match(editRoutes) ) {
editors = false;
}
}
if (editors) {
options.watchForChanges = true;
if (Object.hasOwn(Drupal.editors, 'gutenberg')) {
options.buttonZIndex = 1000;
} else {
// CKEditor injects a label that messes up the "text + alt" link test.
options.ignoreAriaOnElements = '[data-drupal-media-preview], [data-drupal-entity-preview]';
}
} else {
options.watchForChanges = dS.watch_for_changes === 'checkRoots' ?
'checkRoots' :
dS.watch_for_changes !== 'false';
}
let delay = drupalSettings.path.currentPathIsAdmin ? 250 : 0;
// Way too many race conditions on admin side.
if (document.URL.indexOf('mode=same_page_preview') > -1 || (
drupalSettings.path.currentPathIsAdmin &&
dS.disable_live === true
)) {
ed11yOnce = true;
ed11yInitialized = 'disabled';
return;
} else if (drupalSettings.path.currentPathIsAdmin && !editors) {
// Ed11y will init later if a behavior brings in something editable.
ed11yInitialized = false;
return;
}
if (document.querySelector('.layout-builder-form')) {
// Layout builder is not compatible.
ed11yOnce = true;
ed11yInitialized = 'disabled';
return;
} else if (editors) {
// Editable content is present, optimize for speed.
options.autoDetectShadowComponents = false;
options.ignoreContentOutsideRoots = true; // @todo 3.x is there also config for this?
if (Object.hasOwn(Drupal.editors, 'gutenberg')) {
options.ignoreAriaOnElements = 'h1,h2,h3,h4,h5,h6';
delay = 1000;
window.setTimeout(function () {
if (Ed11y.results.length === 0) {
// Ed11y fails to initialize if Gutenberg is really late.
Ed11y.checkAll();
}
}, 6000);
}
options.checkRoot = '.gutenberg__editor .is-root-container, [contenteditable="true"]:not(.gutenberg__editor [contenteditable], [contenteditable="true"] [contenteditable])';
options.ignoreElements += ', [hidden], [style*="display: none"], [style*="display:none"], [hidden] *, [style*="display: none"] *, [style*="display:none"] *, [data-drupal-message-type]';
// todo merge
options.ignoreAllIfAbsent = options.ignoreAllIfAbsent ?
options.ignoreAllIfAbsent + ', [contenteditable="true"], .gutenberg__editor .is-root-container':
'[contenteditable="true"], .gutenberg__editor .is-root-container';
options.initialHeadingLevel = [];
if (dS.live_h2) {
options.initialHeadingLevel.push(
{
selector: dS.live_h2,
previousHeading: 1,
}
);
}
if (dS.live_h3) {
options.initialHeadingLevel.push(
{
selector: dS.live_h3,
previousHeading: 2,
}
);
}
if (dS.live_h4) {
options.initialHeadingLevel.push(
{
selector: dS.live_h4,
previousHeading: 3,
}
);
}
if (dS.live_h_inherit) {
options.initialHeadingLevel.push(
{
selector: dS.live_h_inherit,
previousHeading: 'inherit',
}
);
}
options.initialHeadingLevel.push({
selector: '*',
previousHeading: 0, // Ignores first heading for level skip detection.
});
}
options.alertMode = dS.assertiveness ? dS.assertiveness : 'assertive';
// If assertiveness is "smart" we set it to assertive if the doc was recently changed.
const now = new Date();
if (drupalSettings.path.currentPathIsAdmin && (Drupal.editors && (Object.hasOwn(Drupal.editors, 'ckeditor5') || Object.hasOwn(Drupal.editors, 'gutenberg'))) && (options.alertMode === 'smart' || options.alertMode === 'assertive')) {
options.alertMode = 'active';
}
else if (
urlParams.has('ed1ref') ||
(options.alertMode === 'smart' &&
((now / 1000) - dS.changed < 60)
)
) {
options.alertMode = 'assertive';
}
// todo postpone: ignoreAllIfPresent
options.preventCheckingIfPresent = !!dS.no_load ?
dS.no_load + ', .layout-builder-form' :
'.layout-builder-form';
if (!!(parent?.drupalSettings?.canvas) && !parent.document.body.querySelector('[class^=_PagePreviewIframe]')) {
// Only run when Drupal Canvas is running if it is in Preview mode.
options.preventCheckingIfPresent = 'body';
}
// todo postpone: preventCheckingIfAbsent
const editSelector = (selector, action) => {
return `[id$="-local-tasks"] a[href*="/${selector}/"][href$="/${action}"],
.block-local-tasks-block a[href*="/${selector}/"][href$="/${action}"]`;
};
const editLink = document.querySelector(editSelector('node', 'edit'));
const layoutLink = document.querySelector(editSelector('node', 'layout'));
const userLink = document.querySelector(editSelector('user', 'edit'));
const termLink = document.querySelector(editSelector('taxonomy/term', 'edit'));
if (editLink || layoutLink || userLink || termLink) {
const editIcon = document.createElement('span');
editIcon.classList.add('ed11y-custom-edit-icon');
editIcon.innerHTML = '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!--!Font Awesome Free 6.7.2 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2025 Fonticons, Inc.--><path fill="currentColor" d="M441 59L453 71c9 9 9 25 0 34L424 134 378 88 407 59c9-9 25-9 34 0zM210 256L344 122 390 168 256 302c-3 3-7 5-10 6l-59 17 17-59c1-4 3-8 6-10zM373 25L176 222c-9 9-15 19-18 31l-29 100c-2 8-.1 17 6 24s15 9 24 6l100-27c12-3 23-10 31-18L487 139c28-28 28-74 0-102L475 25C447-3 401-3 373 25zM88 64C39 64 0 103 0 152L0 424c0 49 39 88 88 88l272 0c49 0 88-39 88-88l0-112c0-13-11-24-24-24s-24 11-24 24l0 112c0 22-18 40-40 40L88 464c-22 0-40-18-40-40l0-272c0-22 18-40 40-40l112 0c13 0 24-11 24-24s-11-24-24-24L88 64z"/></svg>';
const reLink = function(link, text) {
const linkButton = document.createElement('a');
linkButton.href = link.href;
linkButton.textContent = text;
linkButton.prepend(editIcon.cloneNode(true));
return linkButton;
};
const editLinks = document.createElement('div');
if (editLink) {
editLinks.appendChild(reLink(editLink, Drupal.t('Page editor')));
}
if (layoutLink) {
editLinks.appendChild(reLink(layoutLink, Drupal.t('Layout editor')));
}
if (userLink) {
editLinks.appendChild(reLink(userLink, Drupal.t('Edit user')));
}
if (termLink) {
editLinks.appendChild(reLink(termLink, Drupal.t('Edit term')));
}
options.editLinks = editLinks;
// Set listener to hide links on view.
if (!!dS.hide_edit_links) {
document.addEventListener('ed11yPop', e => {
if (e.detail.result.element.closest(drupalSettings.editoria11y.hide_edit_links)) {
e.detail.tip.shadowRoot.querySelector('.ed11y-custom-edit-links')?.setAttribute('hidden', '');
}
});
}
}
if (typeof editoria11yOptionsOverride !== 'undefined' && typeof editoria11yOptions === 'function') {
options = editoria11yOptions(options); // @todo run like custom tests?
}
ed11yWaiting = true;
window.setTimeout(function() {
ed11yInitialized = true;
// Increase zIndex on tips drawn inside Drupal's modal dialog.
document.addEventListener('ed11yResultsPainted', function () {
if (Ed11y.State.inlineAlerts) {
// Inline alerts inherit z-index.
return;
}
Ed11y.Results?.forEach(result => {
const inDialog = result?.element?.closest('dialog, [role="dialog"]');
if (inDialog) {
result?.toggle?.style.setProperty('--ed11y-buttonZIndex', '99999');
}
});
});
Ed11y.Lang.addI18n(Sa11yLangEnUS.strings); // @todo language match.
const ed11y = new Ed11y.Ed11y(options);
ed11yWaiting = false;
// When Drupal dialog opens, constrain checks inside dialog.
let rootsCache;
let dialogRoots = '';
document.addEventListener('dialog:aftercreate', function () {
// @todo merge convert from fixedRoots while running.
if (!rootsCache) {
rootsCache = Ed11y.Options.checkRoot;
const rootsParse = Ed11y.Options.checkRoot.split(',');
rootsParse.forEach((root, i) => {
rootsParse[i] = `#drupal-modal ${root}`;
});
dialogRoots = rootsParse.join(', ');
}
Ed11y.State.checkRoot = dialogRoots;
Ed11y.State.forceFullCheck = true;
Ed11y.incrementalCheck(); // todo merge
// Todo: if Editoria11y disables, drop its zIndex behind the modal?
});
document.addEventListener('dialog:afterclose', function () {
// todo check if there are ANY dialogs still open.
if (rootsCache) {
Ed11y.State.checkRoot = Ed11y.Options.checkRoot;
Ed11y.forceFullCheck = true;
Ed11y.incrementalCheck();
}
});
window.setTimeout(function() {
if (Ed11y.State.disabled) {
reportSyncDone();
// Tell crawler to move on.
}
// Append ?ed1string to URLs to check translations
if (!urlParams.has('ed1strings')) {
return;
}
if (typeof(drupalTranslations) === 'undefined') {
console.warn('Editoria11y: No translations present to debug.');
return;
}
const target = document.querySelector('main');
const wrap = document.createElement('div');
target.prepend(wrap);
const missingTranslations = document.createElement('strong');
missingTranslations.textContent = Drupal.t("Translation needed: ");
missingTranslations.style.setProperty('border', '1px solid');
missingTranslations.style.setProperty('filter', 'invert(1)');
for (const [key, value] of Object.entries(Ed11y.Lang)) { // @todo merge
if (!ed11yLangDrupal[key]) {
console.warn(key);
}
let checkTranslation = true;
if (!(drupalTranslations && drupalTranslations.strings && drupalTranslations.problems)) {
checkTranslation = false;
}
let item = document.createElement('div');
if (value.title && typeof value.tip()) {
item.textContent = value.tip('example');
if (checkTranslation && !drupalTranslations.strings[""][value.tip('')]) {
item.prepend(missingTranslations.cloneNode(true));
}
let title = document.createElement('strong');
title.style.setProperty('display', 'block');
title.textContent = value.title;
if (checkTranslation && !drupalTranslations.strings[""][value.title]) {
title.prepend(missingTranslations.cloneNode(true));
}
item.prepend(title);
} else {
item.innerHTML = value;
if (checkTranslation && !(drupalTranslations.strings[""][value] || drupalTranslations.strings.problems[value])) {
item.prepend(missingTranslations.cloneNode(true));
}
}
const itemKey = document.createElement('em');
itemKey.textContent = key + ': ';
item.prepend(itemKey);
wrap.append(item);
const br = document.createElement('br');
wrap.append(br);
}
},100);
}, delay);
/**
* Initiate sync
*
* */
const reportSyncDone = function() {
if (parent && parent.ed11ySynced) {
parent.ed11ySynced--;
}
};
let csrfToken = false;
function getCsrfToken(action, data)
{
{
fetch(`${dS.session_url}`, {
method: "GET"
})
.then(res => res.text())
.then(token => {
csrfToken = token;
postData(action, data).catch(err => console.error(err));
})
.catch(err => console.error(err));
}
}
let postData = async function (action, data) {
if (!csrfToken) {
getCsrfToken(action, data);
} else {
let apiRoot = dS.api_url.replace('results/report','');
let url = `${apiRoot}${action}`;
fetch(url, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-CSRF-Token': csrfToken,
},
body: JSON.stringify(data),
}).then(() => reportSyncDone())
.catch((error) => console.error('Error:', error));
}
};
// Purge changed aliases & deleted pages.
const ed1Ref = urlParams.has('ed1ref') ? decodeURIComponent(urlParams.get('ed1ref')) : false;
if (ed1Ref && ed1Ref !== dS.page_path) {
let data = {
page_path: ed1Ref,
};
window.setTimeout(function() {
postData('purge/page', data);
},100,data);
}
let results = {};
let oks = {};
let hides = {};
let total = 0;
let extractResults = function () {
results = {};
oks = [];
hides = [];
total = 0;
Ed11y.Results.forEach(result => {
const testKey = result.test;
const testName = Ed11y.Lang.langStrings[`${testKey}_TEST_NAME`];
if (!testName) {
// @todo 3.x confirm all test names are now in place.
console.warn('3.x debug test without name: ', result);
}
else if (result.dismissalStatus !== "ok") { // @todo merge test
// log all items not marked as OK
if (results[testKey]) {
results[testKey] = {
count: parseInt(results[testKey].count) + 1,
result_name: testName,
};
total++;
} else {
results[testKey] = {
count: 1,
result_name: testName,
};
total++;
}
}
else {
// todo 2.3: use dismissalKey instead with a separate row for each dismissalKey
oks.push({
resultKey: result.test,
dismissalKey: result.dismiss,
resultName: testName,
});
}
});
};
let sendResults = function () {
window.setTimeout(function () {
total = 0;
extractResults();
let data = {
page_title: dS.page_title,
page_path: dS.page_path,
entity_id: dS.entity_id,
page_count: total,
language: dS.lang,
entity_type: dS.entity_type, // node or false
route_name: dS.route_name, // e.g., entity.node.canonical or view.frontpage.page_1
results: results,
hides: hides,
oks: oks,
};
postData('results/report', data);
// Short timeout to let execution queue clear.
}, 100);
};
let firstRun = true;
if (dS.dismissals && dS.sync !== 'dismissals' && dS.sync !== 'disable') {
document.addEventListener('ed11yResults', function () {
if (firstRun) {
if ((Ed11y.Results.length > 0 || dS.pid)) {
sendResults();
} else {
reportSyncDone();
}
firstRun = false;
}
});
}
let dismissalsCache = {};
let dismissalsData = {
dismissals: [],
};
const debounce = (callback, wait) => {
let timeoutId = null;
return (...args) => {
window.clearTimeout(timeoutId);
timeoutId = window.setTimeout(() => {
callback.apply(null, args);
}, wait);
};
};
const sendDismissals = debounce(()=> {
// Get dynamic title from edit pages.
// todo: Canvas title selector?
const editableTitleField = document.querySelector('#edit-title-wrapper input, #edit-name-wrapper input, #edit-name input');
dismissalsData.page_title = drupalSettings.path.currentPathIsAdmin &&
editableTitleField && editableTitleField.value ?
editableTitleField.value :
dS.page_title;
dismissalsData.page_path = dS.page_path;
dismissalsData.entity_id = dS.entity_id;
dismissalsData.language = dS.lang;
dismissalsData.entity_type = dS.entity_type; // node or false
dismissalsData.route_name = dS.route_name; // e.g., entity.node.canonical or view.frontpage.page_1
postData('dismiss', dismissalsData).then(() => {
dismissalsData.dismissals = [];
dismissalsCache = {};
});
}, 250);
const resendResults = debounce(()=> {
sendResults();
}, 250);
const prepareDismissal = function (detail) {
if (!!detail) {
if (detail.dismissAction === 'reset') {
dismissalsCache.dismissals = [];
dismissalsData = {
dismissals: [
{
dismissal_status: 'reset', // ok, ignore or reset
result_key: detail.dismissTest, // which test is sending a result
element_id: detail.dismissKey, // some recognizable attribute of the item marked
},
],
};
if (dS.sync !== 'dismissals') {
window.setTimeout(function() {
resendResults();
},500);
}
} else if (detail.dismissTest in dismissalsCache && dismissalsCache[detail.dismissTest].includes(detail.dismissKey)) {
return false;
} else {
// Send if we have not already sent the same key.
if (!(detail.dismissTest in dismissalsCache)) {
dismissalsCache[detail.dismissTest] = [detail.dismissKey];
} else {
dismissalsCache[detail.dismissTest].push(detail.dismissKey);
}
dismissalsData.dismissals.push(
{
// @todo merge rewrite
result_name: Ed11y.Lang.langStrings[detail.dismissTest + '_TEST_NAME'], // which test is sending a result
result_key: detail.dismissTest, // which test is sending a result
element_id: detail.dismissKey, // some recognizable attribute of the item marked
dismissal_status: detail.dismissAction, // ok, ignore or reset
}
);
if (detail.dismissAction === 'ok' && dS.sync !== 'dismissals') {
window.setTimeout(function() {
resendResults();
},500);
}
}
window.setTimeout(function() {sendDismissals();}, 0);
}
};
if (dS.dismissals && dS.sync !== 'disable') {
document.addEventListener('ed11yDismissalUpdate', function (e) {
prepareDismissal(e.detail);
}, false);
}
ed11yOnce = true;
};
Drupal.behaviors.editoria11y = {
attach: function (context) {
"use strict";
if (ed11yInitialized === true && ed11yOnce) {
// Recheck page about a second after every behavior.
// Todo: global mutation watch instead or in addition?
window.setTimeout(function () {
Ed11y.forceFullCheck = true;
if (drupalSettings.editor || typeof(DrupalGutenberg) === 'object') {
Ed11y.State.inlineAlerts = false;
}
if (Ed11y.bodyStyle) { // @todo merge rewrite
// todo: shouldn't forceFull make this not necessary?
Ed11y.incrementalCheck();
} else {
Ed11y.checkAll();
}
}, 1000);
} else if (ed11yOnce &&
(!ed11yInitialized ||
ed11yInitialized !== 'pending'
) &&
!drupalSettings.editoria11y.disable_live &&
Drupal.editors &&
(Object.hasOwn(Drupal.editors, 'ckeditor5') ||
Object.hasOwn(Drupal.editors, 'gutenberg'))) {
window.setTimeout(function () {
if (ed11yInitialized !== true) {
ed11yInitializer();
}
}, 1000);
}
if (context === document && !ed11yOnce && CSS.supports('selector(:is(body))')) {
ed11yOnce = true;
// Timeout necessary to prevent Paragraphs needing 2 clicks to open.
window.setTimeout(()=> {
ed11yInitializer();
}, 100);
}
}
};
