Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

UHF-9739 Updating news #757

Merged
merged 5 commits into from
May 20, 2024
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
289 changes: 167 additions & 122 deletions modules/helfi_toc/assets/js/tableOfContents.js
Original file line number Diff line number Diff line change
@@ -1,64 +1,71 @@
'use strict';

(function (Drupal, once, drupalSettings) {
Drupal.behaviors.table_of_contents = {
attach: function attach() {

function findAvailableId(name, reserved, anchors, count) {
let newName = name;
if (count > 0) { // Only when headings are not unique on page we want to add counter
newName += `-${count}`;
}
if (reserved.includes(newName)) {
return findAvailableId(name, reserved, anchors, count + 1);
}

if (anchors.includes(newName)) {
if (count === 0) {
count += 1; // When reserved heading is visible on page, lets start counting from 2 instead of 1
}
return findAvailableId(name, reserved, anchors, count + 1);
}
return newName;
}

const anchors = [];
const tableOfContents = document.getElementById('helfi-toc-table-of-contents');
const tableOfContentsList = document.querySelector('#helfi-toc-table-of-contents-list > ul');
const mainContent = document.querySelector('main.layout-main-wrapper');
const reservedElems = document.querySelectorAll('[id]');
const reserved = []; // Let's list current id's here to avoid creating duplicates
reservedElems.forEach(function (elem) {
reserved.push(elem.id);
});
((Drupal, once, drupalSettings) => {

// Global table of contents object.
Drupal.tableOfContents = {

// List of reserved ids.
reservedIds: [],

// List of anchors.
anchors: [],

// Exclude elements from TOC that are not content:
// e.g. TOC, sidebar, cookie compliance banner etc.
exclusions: () => {
return '' +
':not(.layout-sidebar-first *)' +
':not(.layout-sidebar-second *)' +
':not(.tools__container *)' +
':not(.breadcrumb__container *)' +
':not(#helfi-toc-table-of-contents *)' +
':not(.embedded-content-cookie-compliance *)' +
':not(.react-and-share-cookie-compliance *)'
},

// Exclude elements from TOC that are not content:
// e.g. TOC, sidebar, cookie compliency-banner etc.
const exclusions =
'' +
':not(.layout-sidebar-first *)' +
':not(.layout-sidebar-second *)' +
':not(.tools__container *)' +
':not(.breadcrumb__container *)' +
':not(#helfi-toc-table-of-contents *)' +
':not(.embedded-content-cookie-compliance *)' +
':not(.react-and-share-cookie-compliance *)';

const titleComponents = [
// List of heading tags with exclusions.
titleComponents: (exclusions = Drupal.tableOfContents.exclusions()) => {
return [
`h2${exclusions}`,
`h3${exclusions}`,
`h4${exclusions}`,
`h5${exclusions}`,
`h6${exclusions}`,
];
},

const mainLanguages = [
'en',
'fi',
'sv',
];
// Find available ID for the anchor link.
findAvailableId: (name, count) => {
let newName = name;

// Add postfix to the name if heading is not unique.
if (count > 0) {
newName += `-${count}`;
}

if (Drupal.tableOfContents.reservedIds.includes(newName)) {
return Drupal.tableOfContents.findAvailableId(name, count + 1);
}

const swaps = {
if (Drupal.tableOfContents.anchors.includes(newName)) {
// When reserved heading is visible on page, lets start counting from 2 instead of 1
if (count === 0) {
count += 1;
}
return Drupal.tableOfContents.findAvailableId(name,count + 1);
}
return newName;
},

// Main languages.
mainLanguages: () => {
return ['en', 'fi', 'sv'];
},

// Locale conversions.
localeConversions: () => {
return {
'0': '[°₀۰0]',
'1': '[¹₁۱1]',
'2': '[²₂۲2]',
Expand All @@ -70,101 +77,148 @@
'8': '[⁸₈۸8]',
'9': '[⁹₉۹9]',
'a': '[àáảãạăắằẳẵặâấầẩẫậāąåαάἀἁἂἃἄἅἆἇᾀᾁᾂᾃᾄᾅᾆᾇὰᾰᾱᾲᾳᾴᾶᾷаأအာါǻǎªაअاaä]',
'aa': '[عआآ]',
'ae': '[æǽ]',
'ai': '[ऐ]',
'b': '[бβبဗბbब]',
'c': '[çćčĉċc©]',
'ch': '[чჩჭچ]',
'd': '[ďðđƌȡɖɗᵭᶁᶑдδدضဍဒდdᴅᴆ]',
'dj': '[ђđ]',
'dz': '[џძ]',
'e': '[éèẻẽẹêếềểễệëēęěĕėεέἐἑἒἓἔἕὲеёэєəဧေဲეएإئe]',
'ei': '[ऍ]',
'f': '[фφفƒფf]',
'g': '[ĝğġģгґγဂგگg]',
'gh': '[غღ]',
'gx': '[ĝ]',
'h': '[ĥħηήحهဟှჰh]',
'hx': '[ĥ]',
'i': '[íìỉĩịîïīĭįıιίϊΐἰἱἲἳἴἵἶἷὶῐῑῒῖῗіїиဣိီည်ǐიइیii̇ϒ]',
'ii': '[ई]',
'ij': '[ij]',
'j': '[ĵјჯجj]',
'jx': '[ĵ]',
'k': '[ķĸкκقكကკქکk]',
'kh': '[хخხ]',
'l': '[łľĺļŀлλلလლlल]',
'lj': '[љ]',
'm': '[мμمမმm]',
'n': '[ñńňņʼnŋνнنနნn]',
'o': '[óòỏõọôốồổỗộơớờởỡợøōőŏοὀὁὂὃὄὅὸόоوθိုǒǿºოओoöө]',
'p': '[пπပპپp]',
'q': '[ყq]',
'r': '[ŕřŗрρرრr]',
's': '[śšşсσșςسصစſსsŝ]',
't': '[ťţтτțتطဋတŧთტt]',
'u': '[úùủũụưứừửữựûūůűŭųµуဉုူǔǖǘǚǜუउuўü]',
'v': '[вვϐv]',
'w': '[ŵωώဝွw]',
'x': '[χξx]',
'y': '[ýỳỷỹỵÿŷйыυϋύΰيယyῠῡὺ]',
'z': '[źžżзζزဇზz]',
'aa': '[عआآ]',
'ae': '[æǽ]',
'ai': '[ऐ]',
'ch': '[чჩჭچ]',
'dj': '[ђđ]',
'dz': '[џძ]',
'ei': '[ऍ]',
'gh': '[غღ]',
'ii': '[ई]',
'ij': '[ij]',
'kh': '[хخხ]',
'lj': '[љ]',
'nj': '[њ]',
'o': '[óòỏõọôốồổỗộơớờởỡợøōőŏοὀὁὂὃὄὅὸόоوθိုǒǿºოओoöө]',
'oe': '[öœؤ]',
'oi': '[ऑ]',
'oii': '[ऒ]',
'p': '[пπပპپp]',
'ps': '[ψ]',
'q': '[ყq]',
'r': '[ŕřŗрρرრr]',
's': '[śšşсσșςسصစſსsŝ]',
'sh': '[шშش]',
'shch': '[щ]',
'ss': '[ß]',
'sx': '[ŝ]',
't': '[ťţтτțتطဋတŧთტt]',
'th': '[þϑثذظ]',
'ts': '[цცწ]',
'u': '[úùủũụưứừửữựûūůűŭųµуဉုူǔǖǘǚǜუउuўü]',
'ue': '[ü]',
'uu': '[ऊ]',
'v': '[вვϐv]',
'w': '[ŵωώဝွw]',
'x': '[χξx]',
'y': '[ýỳỷỹỵÿŷйыυϋύΰيယyῠῡὺ]',
'ya': '[я]',
'yu': '[ю]',
'z': '[źžżзζزဇზz]',
'zh': '[жჟژ]',
'gx': '[ĝ]',
'hx': '[ĥ]',
'jx': '[ĵ]',
};
},

// Craft table of contents.
once('table-of-contents', titleComponents.join(','), mainContent)
.forEach(function (content) {
let name = content.textContent
.toLowerCase()
.trim();

// To ensure backwards compatibility, this is done only to "other" languages.
if (!mainLanguages.includes(drupalSettings.path.currentLanguage)) {
Object.keys(swaps).forEach((swap) => {
name = name.replace(new RegExp(swaps[swap], 'g'), swap);
});
}
else {
name = name
.replace(/ä/gi, 'a')
.replace(/ö/gi, 'o')
.replace(/å/gi, 'a');
}
// A function to create table of content elements.
createTableOfContentElements: (content) => {
// Remove loading text and noscript element.
let name = content.textContent
.toLowerCase()
.trim();

// To ensure backwards compatibility, this is done only to "other" languages.
if (!Drupal.tableOfContents.mainLanguages().includes(drupalSettings.path.currentLanguage)) {
Object.keys(Drupal.tableOfContents.localeConversions()).forEach((swap) => {
name = name.replace(new RegExp(Drupal.tableOfContents.localeConversions()[swap], 'g'), swap);
});
}
else {
name = name
.replace(/ä/gi, 'a')
.replace(/ö/gi, 'o')
.replace(/å/gi, 'a');
}

name = name
// Replace any remaining non-word character including whitespace with '-'.
// This leaves only characters matching [A-Za-z0-9-_] to the name.
.replace(/\W/g, '-')
// Use underscore at the end of the string: 'example-1' -> 'example_1'.
.replace(/-(\d+)$/g, '_$1');
name = name
// Replace any remaining non-word character including whitespace with '-'.
// This leaves only characters matching [A-Za-z0-9-_] to the name.
.replace(/\W/g, '-')
// Use underscore at the end of the string: 'example-1' -> 'example_1'.
.replace(/-(\d+)$/g, '_$1');

let nodeName = content.nodeName.toLowerCase();
if (nodeName === 'button') {
nodeName = content.parentElement.nodeName.toLowerCase();
}
let nodeName = content.nodeName.toLowerCase();
if (nodeName === 'button') {
nodeName = content.parentElement.nodeName.toLowerCase();
}

const anchorName = content.id
? content.id
: Drupal.tableOfContents.findAvailableId(name, 0);

const anchorName = content.id
? content.id
: findAvailableId(name, reserved, anchors, 0);
Drupal.tableOfContents.anchors.push(anchorName);

anchors.push(anchorName);
// Create anchor links.
content.setAttribute('id', anchorName);
content.setAttribute('tabindex', '-1'); // Set tabindex to -1 to avoid issues with screen readers.

return {
nodeName: nodeName,
anchorName: anchorName,
}
},

// A function to reveal table of contents.
updateTOC: (tocElement) => {
// Remove loading text and noscript element.
const removeElements = tocElement.parentElement.querySelectorAll('.js-remove');
removeElements.forEach(function (element) {
element.remove();
});

// Update toc visible.
tocElement.setAttribute('data-js', 'true');
},
}

// Attach table of contents.
Drupal.behaviors.tableOfContents = {
attach: function attach() {
const tableOfContents = document.getElementById('helfi-toc-table-of-contents');

// Bail if table of contents is not enabled.
if (!tableOfContents) {
return;
}

const tableOfContentsList = document.querySelector('#helfi-toc-table-of-contents-list > ul');
const mainContent = document.querySelector('main.layout-main-wrapper');
const reservedElems = document.querySelectorAll('[id]');
reservedElems.forEach(function (elem) {
Drupal.tableOfContents.reservedIds.push(elem.id);
});

// Craft table of contents.
once('table-of-contents', Drupal.tableOfContents.titleComponents().join(','), mainContent)
.forEach((content) => {

const { nodeName, anchorName} = Drupal.tableOfContents.createTableOfContentElements(content, []);

// Create table of contents if component is enabled.
if (tableOfContentsList && nodeName === 'h2') {
Expand All @@ -179,20 +233,11 @@
listItem.appendChild(link);
tableOfContentsList.appendChild(listItem);
}
// Create anchor links.
content.setAttribute('id', anchorName);
content.setAttribute('tabindex', '-1'); // Set tabindex to -1 to avoid issues with screen readers.
});
}
);

if (tableOfContents) {
// Remove loading text and noscript element.
const removeElements = tableOfContents.parentElement.querySelectorAll('.js-remove');
removeElements.forEach(function (element) {
element.remove();
});

// Update toc visible.
tableOfContents.setAttribute('data-js', 'true');
Drupal.tableOfContents.updateTOC(tableOfContents);
}
},
};
Expand Down