/**
* SWAP.PLUG
*
* Frontend Application JavaScript
*
* @package SWAP_PLUG
*/
(function () {
'use strict';
/**
* Main frontend controller.
*/
const SWAP_PLUG_Frontend = {
/**
* Initialize.
*/
init: function () {
this.bindModuleToggles();
this.bindCopyButtons();
this.bindDismissibleNotices();
this.bindExpandableSections();
this.bindModalControls();
this.bindKeyboardNavigation();
this.markReady();
},
/**
* Mark application as ready.
*/
markReady: function () {
document
.querySelectorAll('.swap-plug-app')
.forEach(function (app) {
app.classList.add('swap-plug-is-ready');
});
},
/**
* Module detail toggles.
*
* Supports:
* .swap-plug-module-toggle
*
* Expected target:
* data-target="#module-detail"
*/
bindModuleToggles: function () {
const toggles = document.querySelectorAll(
'.swap-plug-module-toggle'
);
if (!toggles.length) {
return;
}
toggles.forEach(function (toggle) {
toggle.addEventListener('click', function (event) {
const targetSelector = toggle.getAttribute(
'data-target'
);
if (!targetSelector) {
return;
}
const target = document.querySelector(
targetSelector
);
if (!target) {
return;
}
event.preventDefault();
const expanded =
toggle.getAttribute('aria-expanded') === 'true';
toggle.setAttribute(
'aria-expanded',
expanded ? 'false' : 'true'
);
target.hidden = expanded;
target.classList.toggle(
'swap-plug-is-open',
!expanded
);
});
});
},
/**
* Copy-to-clipboard buttons.
*
* Supports:
* .swap-plug-copy
*
* Expected source:
* data-copy="text"
*
* Or:
* data-copy-target="#element"
*/
bindCopyButtons: function () {
const buttons = document.querySelectorAll(
'.swap-plug-copy'
);
if (!buttons.length) {
return;
}
buttons.forEach(function (button) {
button.addEventListener('click', function (event) {
event.preventDefault();
const targetSelector =
button.getAttribute('data-copy-target');
let value =
button.getAttribute('data-copy') || '';
if (
!value &&
targetSelector
) {
const target = document.querySelector(
targetSelector
);
if (target) {
value =
'value' in target
? target.value
: target.textContent;
}
}
value = String(value).trim();
if (!value) {
return;
}
SWAP_PLUG_Frontend.copyText(
value,
button
);
});
});
},
/**
* Copy text using Clipboard API with fallback.
*
* @param {string} text
* @param {HTMLElement} button
*/
copyText: function (text, button) {
const controller = this;
if (
navigator.clipboard &&
window.isSecureContext
) {
navigator.clipboard
.writeText(text)
.then(function () {
controller.showCopyState(button);
})
.catch(function () {
controller.copyTextFallback(
text,
button
);
});
return;
}
this.copyTextFallback(text, button);
},
/**
* Clipboard fallback.
*
* @param {string} text
* @param {HTMLElement} button
*/
copyTextFallback: function (text, button) {
const textarea = document.createElement('textarea');
textarea.value = text;
textarea.setAttribute('readonly', '');
textarea.style.position = 'fixed';
textarea.style.opacity = '0';
textarea.style.pointerEvents = 'none';
document.body.appendChild(textarea);
textarea.select();
textarea.setSelectionRange(
0,
textarea.value.length
);
let copied = false;
try {
copied = document.execCommand('copy');
} catch (error) {
copied = false;
}
document.body.removeChild(textarea);
if (copied) {
this.showCopyState(button);
}
},
/**
* Show temporary copy state.
*
* @param {HTMLElement} button
*/
showCopyState: function (button) {
if (!button) {
return;
}
const originalText =
button.getAttribute(
'data-original-label'
) ||
button.textContent;
if (
!button.hasAttribute(
'data-original-label'
)
) {
button.setAttribute(
'data-original-label',
originalText
);
}
button.classList.add(
'swap-plug-copy-success'
);
button.setAttribute(
'aria-label',
'Copied'
);
button.textContent = 'Copied';
window.setTimeout(function () {
button.classList.remove(
'swap-plug-copy-success'
);
button.textContent =
button.getAttribute(
'data-original-label'
) || 'Copy';
button.removeAttribute(
'aria-label'
);
}, 1800);
},
/**
* Dismissible notices.
*
* Supports:
* .swap-plug-notice-dismiss
*
* Parent:
* .swap-plug-notice
*/
bindDismissibleNotices: function () {
const buttons = document.querySelectorAll(
'.swap-plug-notice-dismiss'
);
if (!buttons.length) {
return;
}
buttons.forEach(function (button) {
button.addEventListener('click', function () {
const notice = button.closest(
'.swap-plug-notice'
);
if (!notice) {
return;
}
notice.classList.add(
'swap-plug-notice-closing'
);
window.setTimeout(function () {
notice.remove();
}, 180);
});
});
},
/**
* Expandable sections.
*
* Supports:
* [data-swap-plug-expand]
*/
bindExpandableSections: function () {
const controls = document.querySelectorAll(
'[data-swap-plug-expand]'
);
if (!controls.length) {
return;
}
controls.forEach(function (control) {
const targetSelector =
control.getAttribute(
'data-swap-plug-expand'
);
if (!targetSelector) {
return;
}
const target = document.querySelector(
targetSelector
);
if (!target) {
return;
}
control.setAttribute(
'aria-expanded',
target.hidden ? 'false' : 'true'
);
control.addEventListener(
'click',
function (event) {
event.preventDefault();
const expanded =
control.getAttribute(
'aria-expanded'
) === 'true';
control.setAttribute(
'aria-expanded',
expanded
? 'false'
: 'true'
);
target.hidden = expanded;
target.classList.toggle(
'swap-plug-is-open',
!expanded
);
}
);
});
},
/**
* Modal controls.
*
* Supports:
* [data-swap-plug-modal-open]
* [data-swap-plug-modal-close]
*/
bindModalControls: function () {
const openers = document.querySelectorAll(
'[data-swap-plug-modal-open]'
);
const closers = document.querySelectorAll(
'[data-swap-plug-modal-close]'
);
openers.forEach(function (opener) {
opener.addEventListener(
'click',
function (event) {
event.preventDefault();
const selector =
opener.getAttribute(
'data-swap-plug-modal-open'
);
if (!selector) {
return;
}
const modal =
document.querySelector(
selector
);
if (!modal) {
return;
}
SWAP_PLUG_Frontend.openModal(
modal
);
}
);
});
closers.forEach(function (closer) {
closer.addEventListener(
'click',
function (event) {
event.preventDefault();
const modal =
closer.closest(
'.swap-plug-modal'
);
if (modal) {
SWAP_PLUG_Frontend.closeModal(
modal
);
}
}
);
});
document
.querySelectorAll(
'.swap-plug-modal'
)
.forEach(function (modal) {
modal.addEventListener(
'click',
function (event) {
if (
event.target === modal &&
modal.getAttribute(
'data-close-on-overlay'
) !== 'false'
) {
SWAP_PLUG_Frontend.closeModal(
modal
);
}
}
);
});
},
/**
* Open modal.
*
* @param {HTMLElement} modal
*/
openModal: function (modal) {
modal.hidden = false;
modal.classList.add(
'swap-plug-modal-open'
);
document.body.classList.add(
'swap-plug-modal-open'
);
const focusTarget =
modal.querySelector(
'button, [href], input, select, textarea'
);
if (focusTarget) {
window.setTimeout(function () {
focusTarget.focus();
}, 20);
}
},
/**
* Close modal.
*
* @param {HTMLElement} modal
*/
closeModal: function (modal) {
modal.classList.remove(
'swap-plug-modal-open'
);
modal.hidden = true;
document.body.classList.remove(
'swap-plug-modal-open'
);
},
/**
* Keyboard accessibility.
*/
bindKeyboardNavigation: function () {
document.addEventListener(
'keydown',
function (event) {
if (
event.key !== 'Escape'
) {
return;
}
const openModal =
document.querySelector(
'.swap-plug-modal.swap-plug-modal-open'
);
if (openModal) {
SWAP_PLUG_Frontend.closeModal(
openModal
);
}
}
);
}
};
/**
* Initialize after DOM is ready.
*/
if (
document.readyState === 'loading'
) {
document.addEventListener(
'DOMContentLoaded',
function () {
SWAP_PLUG_Frontend.init();
}
);
} else {
SWAP_PLUG_Frontend.init();
}
/**
* Public API.
*/
window.SWAP_PLUG_Frontend =
SWAP_PLUG_Frontend;
})();