} The scripture data or null if not found.
+ */
+ static async get(reference, translation) {
+ return this.#get(reference, translation);
+ }
+
+ /**
+ * Internal method to check local storage for scripture data.
+ *
+ * @param {string} reference - The scripture reference.
+ * @param {string} translation - The translation.
+ * @returns {Object|null} The stored data or null if not found.
+ * @throws {Error} If parsing or retrieval from local storage fails.
+ * @private
+ */
+ static #get(reference, translation) {
+ const key = this.#key(reference, translation);
+ try {
+ const storedItem = localStorage.getItem(key);
+ if (storedItem) {
+ const { data, timestamp } = JSON.parse(storedItem);
+ if (timestamp > Date.now() - Memory.ONE_MONTH_IN_MILLISECONDS) {
+ return data;
+ }
+ }
+ return null;
+ } catch (error) {
+ console.error('Error parsing or retrieving data from local storage:', error);
+ throw error;
+ }
+ }
+
+ /**
+ * Generates a key for scripture data storage.
+ *
+ * @param {string} reference - The scripture reference.
+ * @param {string} translation - The translation.
+ * @returns {string} A unique key for local storage.
+ * @private
+ */
+ static #key(reference, translation) {
+ return `getBible-${translation}-${reference}`;
+ }
+}
diff --git a/src/js/elements/Element.js b/src/js/elements/Element.js
new file mode 100644
index 0000000..a06472a
--- /dev/null
+++ b/src/js/elements/Element.js
@@ -0,0 +1,43 @@
+import { ModalElement } from './ModalElement.js';
+import { InlineElement } from './InlineElement.js';
+import { TooltipElement } from './TooltipElement.js';
+
+/**
+ * Element class responsible for creating and managing different types of elements
+ * based on the specified format.
+ */
+export class Element {
+ /**
+ * Constructs an Element instance based on the given format.
+ *
+ * @param {HTMLElement} triggerElement - The trigger element.
+ * @param {string} format - The format type.
+ */
+ constructor(triggerElement, format = 'tooltip') {
+ if (!(triggerElement instanceof HTMLElement)) {
+ throw new Error("triggerElement must be an instance of HTMLElement.");
+ }
+
+ const elementTypes = {
+ 'modal': ModalElement,
+ 'inline': InlineElement,
+ 'tooltip': TooltipElement
+ };
+
+ const ElementType = elementTypes[format] || TooltipElement;
+ this.element = new ElementType(triggerElement);
+
+ if (process.env.DEBUG) {
+ console.log(`${format} element selected`);
+ }
+ }
+
+ /**
+ * Load the content into the element.
+ *
+ * @param {string} content - The content to load.
+ */
+ load(content) {
+ this.element.load(content);
+ }
+}
diff --git a/src/js/elements/InlineElement.js b/src/js/elements/InlineElement.js
new file mode 100644
index 0000000..472f1eb
--- /dev/null
+++ b/src/js/elements/InlineElement.js
@@ -0,0 +1,28 @@
+/**
+ * InlineElement class responsible for adding inline elements.
+ */
+export class InlineElement {
+ /**
+ * Creates an instance of InlineElement.
+ *
+ * @param {HTMLElement} triggerElement - The element that triggers the inline display.
+ */
+ constructor(triggerElement) {
+ if (!(triggerElement instanceof HTMLElement)) {
+ throw new Error("triggerElement must be an instance of HTMLElement.");
+ }
+ this.triggerElement = triggerElement;
+ // Clear initial content
+ this.triggerElement.innerHTML = '';
+ }
+
+ /**
+ * Loads content into the trigger element. Appends new content if existing content is present.
+ *
+ * @param {string} content - The content to load into the trigger element.
+ */
+ load(content) {
+ const existingContent = this.triggerElement.innerHTML;
+ this.triggerElement.innerHTML = existingContent ? `${existingContent}\n ${content}` : content;
+ }
+}
diff --git a/src/js/elements/ModalElement.js b/src/js/elements/ModalElement.js
new file mode 100644
index 0000000..a696de7
--- /dev/null
+++ b/src/js/elements/ModalElement.js
@@ -0,0 +1,59 @@
+import { BaseModal } from './modals/BaseModal.js';
+import { UikitModal } from './modals/UikitModal.js';
+import { BootstrapModal } from './modals/BootstrapModal.js';
+import { FoundationModal } from './modals/FoundationModal.js';
+import { TailwindModal } from './modals/TailwindModal.js';
+
+/**
+ * ModalElement class responsible for creating and managing modal elements.
+ * It dynamically selects the appropriate modal style based on the available UI framework.
+ */
+export class ModalElement {
+ /**
+ * Constructs an instance of ModalElement with the appropriate modal type
+ * based on the detected UI framework.
+ *
+ * @param {HTMLElement} triggerElement - The element that triggers the modal.
+ */
+ constructor(triggerElement) {
+ this.modal = ModalElement.framework(triggerElement);
+ }
+
+ /**
+ * Loads content into the modal.
+ *
+ * @param {string} content - The content to load into the modal.
+ */
+ load(content) {
+ this.modal.load(content);
+ }
+
+ /**
+ * Determines the appropriate modal implementation based on the available UI framework.
+ *
+ * @param {HTMLElement} triggerElement - The element triggering the modal.
+ * @returns {BaseModal|BootstrapModal|UikitModal|FoundationModal|TailwindModal} The modal instance.
+ */
+ static framework(triggerElement) {
+ const frameworks = {
+ 'UIkit': UikitModal,
+ 'bootstrap': BootstrapModal,
+ 'Foundation': FoundationModal,
+ 'tailwind': TailwindModal
+ };
+
+ for (const [key, ModalType] of Object.entries(frameworks)) {
+ if (typeof window[key] !== 'undefined' || (key === 'tailwind' && document.querySelector('.tailwind-class') !== null)) {
+ if (process.env.DEBUG) {
+ console.log(`${key} modal selected`);
+ }
+ return new ModalType(triggerElement);
+ }
+ }
+
+ if (process.env.DEBUG) {
+ console.log(`base modal selected`);
+ }
+ return new BaseModal(triggerElement);
+ }
+}
diff --git a/src/js/elements/TooltipElement.js b/src/js/elements/TooltipElement.js
new file mode 100644
index 0000000..a112e1b
--- /dev/null
+++ b/src/js/elements/TooltipElement.js
@@ -0,0 +1,60 @@
+import { BaseTooltip } from './tooltip/BaseTooltip.js';
+import { BootstrapTooltip } from './tooltip/BootstrapTooltip.js';
+import { UikitTooltip } from './tooltip/UikitTooltip.js';
+import { FoundationTooltip } from './tooltip/FoundationTooltip.js';
+import { TailwindTooltip } from './tooltip/TailwindTooltip.js';
+
+/**
+ * TooltipElement class responsible for creating and managing tooltip elements.
+ * It dynamically selects the appropriate tooltip style based on the available UI framework.
+ */
+export class TooltipElement {
+ /**
+ * Constructs an instance of TooltipElement with the appropriate tooltip type
+ * based on the detected UI framework.
+ *
+ * @param {HTMLElement} triggerElement - The element that triggers the tooltip.
+ */
+ constructor(triggerElement) {
+ this.tooltip = TooltipElement.framework(triggerElement);
+ }
+
+ /**
+ * Loads content into the tooltip.
+ *
+ * @param {string} content - The content to load into the tooltip.
+ */
+ load(content) {
+ this.tooltip.load(content);
+ }
+
+ /**
+ * Determines the appropriate tooltip implementation based on the available UI framework.
+ *
+ * @param {HTMLElement} triggerElement - The element triggering the tooltip.
+ * @returns {BaseTooltip|BootstrapTooltip|UikitTooltip|FoundationTooltip|TailwindTooltip} The tooltip instance.
+ * @param debug
+ */
+ static framework(triggerElement) {
+ const frameworks = {
+ 'UIkit': UikitTooltip,
+ 'bootstrap': BootstrapTooltip,
+ 'Foundation': FoundationTooltip,
+ 'tailwind': TailwindTooltip
+ };
+
+ for (const [key, TooltipType] of Object.entries(frameworks)) {
+ if (typeof window[key] !== 'undefined' || (key === 'tailwind' && document.querySelector('.tailwind-class') !== null)) {
+ if (process.env.DEBUG) {
+ console.log(`${key} tooltip selected`);
+ }
+ return new TooltipType(triggerElement);
+ }
+ }
+
+ if (process.env.DEBUG) {
+ console.log(`base tooltip selected`);
+ }
+ return new BaseTooltip(triggerElement);
+ }
+}
diff --git a/src/js/elements/modals/BaseModal.js b/src/js/elements/modals/BaseModal.js
new file mode 100644
index 0000000..623ddb3
--- /dev/null
+++ b/src/js/elements/modals/BaseModal.js
@@ -0,0 +1,77 @@
+export class BaseModal {
+ /**
+ * Creates a new BaseModal instance.
+ *
+ * @param {HTMLElement} triggerElement - The elements that triggers the modal.
+ */
+ constructor(triggerElement) {
+ this.modalId = `modal-${Math.random().toString(36).slice(2, 11)}`;
+ this.triggerElement = triggerElement;
+ this.triggerElement.style.cursor = 'pointer';
+ this.initializeTrigger();
+ }
+
+ /**
+ * Loads content into the modal.
+ *
+ * @param {string} content - The content to load into the modal.
+ */
+ load(content) {
+ const existingModal = document.getElementById(this.modalId);
+ // Check if modal already exists
+ if (existingModal) {
+ // Update the content of the existing modal
+ const contentDiv = document.getElementById(`${this.modalId}-content`);
+ if (contentDiv) {
+ contentDiv.innerHTML += content;
+ }
+ } else {
+ // If modal doesn't exist, create it with the new content
+ this.create(content);
+ }
+ }
+
+ /**
+ * Insert HTML into the dom.
+ *
+ * @param {string} html - The html to insert.
+ */
+ insertIntoDOM(html) {
+ document.body.insertAdjacentHTML('beforeend', html);
+ }
+
+ /**
+ * Creates the modal.
+ *
+ * @param {string} content - The initial content of the modal.
+ */
+ create(content) {
+ const modalHtml = `
+ `;
+ this.insertIntoDOM(modalHtml);
+
+ const modalElement = document.getElementById(this.modalId);
+ modalElement.addEventListener('click', (event) => {
+ if (event.target === modalElement) {
+ modalElement.style.display = 'none';
+ }
+ });
+ }
+
+ /**
+ * Initializes the modal trigger.
+ *
+ */
+ initializeTrigger() {
+ this.triggerElement.addEventListener('click', () => {
+ document.getElementById(this.modalId).style.display = 'flex';
+ });
+ }
+}
diff --git a/src/js/elements/modals/BootstrapModal.js b/src/js/elements/modals/BootstrapModal.js
new file mode 100644
index 0000000..535ee95
--- /dev/null
+++ b/src/js/elements/modals/BootstrapModal.js
@@ -0,0 +1,41 @@
+import { BaseModal } from './BaseModal.js';
+
+export class BootstrapModal extends BaseModal {
+ constructor(triggerElement) {
+ super(triggerElement);
+ }
+
+ show() {
+ const modal = new bootstrap.Modal(document.getElementById(this.modalId));
+ modal.show();
+ }
+
+ hide() {
+ const modal = bootstrap.Modal.getInstance(document.getElementById(this.modalId));
+ if (modal) {
+ modal.hide();
+ }
+ }
+
+ create(content) {
+ const modalHtml = `
+ `;
+ this.insertIntoDOM(modalHtml);
+ }
+
+ initializeTrigger() {
+ this.triggerElement.setAttribute('data-bs-toggle', 'modal');
+ this.triggerElement.setAttribute('data-bs-target', `#${this.modalId}`);
+ }
+}
diff --git a/src/js/elements/modals/FoundationModal.js b/src/js/elements/modals/FoundationModal.js
new file mode 100644
index 0000000..e6890b0
--- /dev/null
+++ b/src/js/elements/modals/FoundationModal.js
@@ -0,0 +1,38 @@
+import { BaseModal } from './BaseModal.js';
+
+export class FoundationModal extends BaseModal {
+ constructor(triggerElement) {
+ super(triggerElement);
+ this.modalElement = null;
+ }
+
+ show() {
+ if (this.modalElement) {
+ this.modalElement.open();
+ }
+ }
+
+ hide() {
+ if (this.modalElement) {
+ this.modalElement.close();
+ }
+ }
+
+ create(content) {
+ const modalHtml = `
+
+
+ ${content}
+
+
+ ×
+
+
`;
+ this.insertIntoDOM(modalHtml);
+ this.modalElement = new Foundation.Reveal(document.getElementById(this.modalId));
+ }
+
+ initializeTrigger() {
+ this.triggerElement.setAttribute('data-open', this.modalId);
+ }
+}
diff --git a/src/js/elements/modals/TailwindModal.js b/src/js/elements/modals/TailwindModal.js
new file mode 100644
index 0000000..f67f80c
--- /dev/null
+++ b/src/js/elements/modals/TailwindModal.js
@@ -0,0 +1,35 @@
+import { BaseModal } from './BaseModal.js';
+
+export class TailwindModal extends BaseModal {
+ constructor(triggerElement) {
+ super(triggerElement);
+ }
+
+ show() {
+ document.getElementById(this.modalId).classList.remove('hidden');
+ }
+
+ hide() {
+ document.getElementById(this.modalId).classList.add('hidden');
+ }
+
+ create(content) {
+ const modalHtml = `
+
+
+
+ ${content}
+
+
Close
+
+
`;
+ this.insertIntoDOM(modalHtml);
+ }
+
+ initializeTrigger(triggerElement) {
+ this.triggerElement.addEventListener('click', () => {
+ document.getElementById(this.modalId).classList.remove('hidden');
+ });
+ }
+}
+
diff --git a/src/js/elements/modals/UikitModal.js b/src/js/elements/modals/UikitModal.js
new file mode 100644
index 0000000..2e08caa
--- /dev/null
+++ b/src/js/elements/modals/UikitModal.js
@@ -0,0 +1,33 @@
+import { BaseModal } from './BaseModal.js';
+
+export class UikitModal extends BaseModal {
+ constructor(triggerElement) {
+ super(triggerElement);
+ }
+
+ show() {
+ UIkit.modal(`#${this.modalId}`).show();
+ }
+
+ hide() {
+ UIkit.modal(`#${this.modalId}`).hide();
+ }
+
+ create(content) {
+ const modalHtml = `
+ `;
+ this.insertIntoDOM(modalHtml);
+ }
+
+ initializeTrigger() {
+ this.triggerElement.setAttribute('uk-toggle', `target: #${this.modalId}`);
+ }
+}
+
diff --git a/src/js/elements/tooltip/BaseTooltip.js b/src/js/elements/tooltip/BaseTooltip.js
new file mode 100644
index 0000000..bcd37ae
--- /dev/null
+++ b/src/js/elements/tooltip/BaseTooltip.js
@@ -0,0 +1,24 @@
+export class BaseTooltip {
+ /**
+ * Creates a new BaseTooltip instance.
+ *
+ * @param {HTMLElement} triggerElement - The elements that triggers the tooltip.
+ */
+ constructor(triggerElement) {
+ this.triggerElement = triggerElement;
+ this.triggerElement.style.cursor = 'help';
+ }
+
+ /**
+ * Loads content into the tooltip. If the trigger elements already has a title,
+ * the new content is appended to it.
+ *
+ * @param {string} content - The content to load into the tooltip.
+ * @throws {Error} Throws an error if the trigger elements is not valid.
+ */
+ load(content) {
+ const existingTitle = this.triggerElement.getAttribute('title');
+ const newTitle = existingTitle ? existingTitle + "\n" + content : content;
+ this.triggerElement.setAttribute('title', newTitle);
+ }
+}
diff --git a/src/js/elements/tooltip/BootstrapTooltip.js b/src/js/elements/tooltip/BootstrapTooltip.js
new file mode 100644
index 0000000..a105c85
--- /dev/null
+++ b/src/js/elements/tooltip/BootstrapTooltip.js
@@ -0,0 +1,19 @@
+import { BaseTooltip } from './BaseTooltip.js';
+
+export class BootstrapTooltip extends BaseTooltip {
+ constructor(triggerElement) {
+ super(triggerElement);
+ }
+
+ load(content) {
+ try {
+ super.load(content);
+ const tooltipTriggerList = [].slice.call(document.querySelectorAll('[data-bs-toggle="tooltip"]'));
+ const tooltipList = tooltipTriggerList.map(function (tooltipTriggerEl) {
+ return new bootstrap.Tooltip(tooltipTriggerEl);
+ });
+ } catch (error) {
+ console.error('Error loading BootstrapTooltip:', error);
+ }
+ }
+}
diff --git a/src/js/elements/tooltip/FoundationTooltip.js b/src/js/elements/tooltip/FoundationTooltip.js
new file mode 100644
index 0000000..4ca803e
--- /dev/null
+++ b/src/js/elements/tooltip/FoundationTooltip.js
@@ -0,0 +1,33 @@
+import { BaseTooltip } from './BaseTooltip.js';
+
+export class FoundationTooltip extends BaseTooltip {
+ constructor(triggerElement) {
+ super(triggerElement);
+ }
+
+ load(content) {
+ try {
+ this.triggerElement.setAttribute('data-tooltip', '');
+ super.load(content);
+ this.triggerElement.classList.add('has-tip');
+
+ new Foundation.Tooltip(this.triggerElement, {
+ // Default options
+ disableHover: false, // Allows tooltip to be hoverable
+ fadeOutDuration: 150, // Duration of fade out animation in milliseconds
+ fadeInDuration: 150, // Duration of fade in animation in milliseconds
+ showOn: 'all', // Can be 'all', 'large', 'medium', 'small'
+ templateClasses: '', // Custom class(es) to be added to the tooltip template
+ tipText: () => this.triggerElement.getAttribute('title'), // Function to define tooltip text
+ triggerClass: 'has-tip', // Class to be added on the trigger elements
+ touchCloseText: 'tap to close', // Text for close button on touch devices
+ positionClass: 'top', // Position of tooltip, can be 'top', 'bottom', 'left', 'right', etc.
+ vOffset: 10, // Vertical offset
+ hOffset: 12, // Horizontal offset
+ allowHtml: false // Allow HTML in tooltip content
+ });
+ } catch (error) {
+ console.error('Error loading FoundationTooltip:', error);
+ }
+ }
+}
diff --git a/src/js/elements/tooltip/TailwindTooltip.js b/src/js/elements/tooltip/TailwindTooltip.js
new file mode 100644
index 0000000..c1ad0d3
--- /dev/null
+++ b/src/js/elements/tooltip/TailwindTooltip.js
@@ -0,0 +1,44 @@
+import { BaseTooltip } from './BaseTooltip.js';
+
+export class TailwindTooltip extends BaseTooltip {
+ constructor(triggerElement) {
+ super(triggerElement);
+ }
+
+ load(content) {
+ try {
+ super.load(content);
+ this._createTooltipElement();
+ this._initializeEvents();
+ } catch (error) {
+ console.error('Error loading TailwindTooltip:', error);
+ }
+ }
+
+ _createTooltipElement() {
+ this.tooltipElement = document.createElement('div');
+ this.tooltipElement.id = this.tooltipId;
+ this.tooltipElement.className = 'absolute invisible bg-gray-800 text-white text-xs px-2 py-1 rounded-md';
+ this.tooltipElement.style.transition = 'visibility 0.3s linear, opacity 0.3s linear';
+ this.tooltipElement.textContent = this.triggerElement.getAttribute('title');
+ document.body.appendChild(this.tooltipElement);
+ }
+
+ _initializeEvents() {
+ this.triggerElement.addEventListener('mouseenter', () => {
+ const rect = this.triggerElement.getBoundingClientRect();
+ this._title = this.triggerElement.getAttribute('title');
+ this.tooltipElement.style.left = `${rect.left + window.scrollX}px`;
+ this.tooltipElement.style.top = `${rect.bottom + 5 + window.scrollY}px`;
+ this.tooltipElement.classList.remove('invisible');
+ this.tooltipElement.classList.add('opacity-100');
+ this.triggerElement.setAttribute('title', '');
+ });
+
+ this.triggerElement.addEventListener('mouseleave', () => {
+ this.tooltipElement.classList.add('invisible');
+ this.tooltipElement.classList.remove('opacity-100');
+ this.triggerElement.setAttribute('title', this._title);
+ });
+ }
+}
diff --git a/src/js/elements/tooltip/UikitTooltip.js b/src/js/elements/tooltip/UikitTooltip.js
new file mode 100644
index 0000000..741b107
--- /dev/null
+++ b/src/js/elements/tooltip/UikitTooltip.js
@@ -0,0 +1,16 @@
+import { BaseTooltip } from './BaseTooltip.js';
+
+export class UikitTooltip extends BaseTooltip {
+ constructor(triggerElement) {
+ super(triggerElement);
+ }
+
+ load(content) {
+ try {
+ super.load(content);
+ UIkit.tooltip(this.triggerElement);
+ } catch (error) {
+ console.error('Error loading UikitTooltip:', error);
+ }
+ }
+}
diff --git a/src/js/formats/BaseFormat.js b/src/js/formats/BaseFormat.js
new file mode 100644
index 0000000..5aeece4
--- /dev/null
+++ b/src/js/formats/BaseFormat.js
@@ -0,0 +1,16 @@
+export class BaseFormat {
+ /**
+ * Get formats the verses.
+ *
+ * @param {Object} data - The data containing verses and their details.
+ * @param {boolean} showBook - Whether to show book names.
+ * @param {boolean} showTrans - Whether to show translations.
+ * @param {boolean} showAbbr - Whether to show abbreviations.
+ * @param {boolean} showLang - Whether to show languages.
+ * @returns {string} The formatted verses.
+ * @abstract
+ */
+ get(data, showBook, showTrans, showAbbr, showLang) {
+ throw new Error("The 'get' method must be implemented in BaseFormat subclass.");
+ }
+}
diff --git a/src/js/formats/BlockFormat.js b/src/js/formats/BlockFormat.js
new file mode 100644
index 0000000..ed66551
--- /dev/null
+++ b/src/js/formats/BlockFormat.js
@@ -0,0 +1,56 @@
+import { BaseFormat } from './BaseFormat.js';
+
+export class BlockFormat extends BaseFormat {
+ /**
+ * Formats the verses for HTML block elements.
+ *
+ * @param {Object} data - The data containing verses and their details.
+ * @param {boolean} showBook - Whether to show book names.
+ * @param {boolean} showTrans - Whether to show translations.
+ * @param {boolean} showAbbr - Whether to show abbreviations.
+ * @param {boolean} showLang - Whether to show languages.
+ * @returns {string} The formatted HTML string.
+ */
+ get(data, showBook, showTrans, showAbbr, showLang) {
+ let formattedHtml = '';
+ let setBookName = new Set();
+ let setTranslation = new Set();
+ let setAbbreviation = new Set();
+ let setLanguage = new Set();
+
+ for (const key in data) {
+ if (!data.hasOwnProperty(key)) continue;
+
+ let headerParts = [];
+ if (showTrans && !setTranslation.has(key)) {
+ headerParts.push(`${data[key].translation} `);
+ setTranslation.add(key);
+ }
+ if (showAbbr && !setAbbreviation.has(key)) {
+ headerParts.push(`${data[key].abbreviation} `);
+ setAbbreviation.add(key);
+ }
+ if (showBook && !setBookName.has(key)) {
+ headerParts.push(`${data[key].name} `);
+ setBookName.add(key);
+ }
+ if (showLang && !setLanguage.has(key)) {
+ headerParts.push(`${data[key].language} `);
+ setLanguage.add(key);
+ }
+
+ // Construct the header
+ if (headerParts.length > 0) {
+ formattedHtml += `\n`;
+ }
+
+ // Add verses
+ const verses = data[key].verses
+ .map(verse => `${verse.verse}. ${verse.text} `)
+ .join(" ");
+ formattedHtml += `${verses}
`;
+ }
+
+ return `${formattedHtml}
`;
+ }
+}
diff --git a/src/js/formats/Format.js b/src/js/formats/Format.js
new file mode 100644
index 0000000..fb28b5a
--- /dev/null
+++ b/src/js/formats/Format.js
@@ -0,0 +1,39 @@
+import { BlockFormat } from './BlockFormat.js';
+import { InlineFormat } from './InlineFormat.js';
+import { PlainFormat } from './PlainFormat.js';
+
+/**
+ * Format class responsible for creating and managing different types of formats
+ * based on the specified type.
+ */
+export class Format {
+ /**
+ * Constructs a Format instance based on the given type.
+ *
+ * @param {string} formatType - The format type.
+ */
+ constructor(formatType = 'tooltip') {
+ const formatTypes = {
+ 'modal': BlockFormat,
+ 'inline': InlineFormat,
+ 'tooltip': PlainFormat
+ };
+
+ const FormatType = formatTypes[formatType] || PlainFormat;
+ this.format = new FormatType();
+ }
+
+ /**
+ * Get the formatted verses.
+ *
+ * @param {Object} data - The data containing verses and their details.
+ * @param {boolean} showBook - Whether to show book names.
+ * @param {boolean} showTrans - Whether to show translations.
+ * @param {boolean} showAbbr - Whether to show abbreviations.
+ * @param {boolean} showLang - Whether to show languages.
+ * @returns {string} The formatted verses.
+ */
+ get(data, showBook, showTrans, showAbbr, showLang) {
+ return this.format.get(data, showBook, showTrans, showAbbr, showLang);
+ }
+}
diff --git a/src/js/formats/InlineFormat.js b/src/js/formats/InlineFormat.js
new file mode 100644
index 0000000..4c584ff
--- /dev/null
+++ b/src/js/formats/InlineFormat.js
@@ -0,0 +1,56 @@
+import { BaseFormat } from './BaseFormat.js';
+
+export class InlineFormat extends BaseFormat {
+ /**
+ * Formats the verses for HTML inline elements.
+ *
+ * @param {Object} data - The data containing verses and their details.
+ * @param {boolean} showBook - Whether to show book names.
+ * @param {boolean} showTrans - Whether to show translations.
+ * @param {boolean} showAbbr - Whether to show abbreviations.
+ * @param {boolean} showLang - Whether to show languages.
+ * @returns {string} The formatted HTML string.
+ */
+ get(data, showBook, showTrans, showAbbr, showLang) {
+ let formattedHtml = '';
+ let setBookName = new Set();
+ let setTranslation = new Set();
+ let setAbbreviation = new Set();
+ let setLanguage = new Set();
+
+ for (const key in data) {
+ if (!data.hasOwnProperty(key)) continue;
+
+ let footerParts = [];
+ if (showTrans && !setTranslation.has(key)) {
+ footerParts.push(`${data[key].translation} `);
+ setTranslation.add(key);
+ }
+ if (showAbbr && !setAbbreviation.has(key)) {
+ footerParts.push(`${data[key].abbreviation} `);
+ setAbbreviation.add(key);
+ }
+ if (showBook && !setBookName.has(key)) {
+ footerParts.push(`${data[key].name} `);
+ setBookName.add(key);
+ }
+ if (showLang && !setLanguage.has(key)) {
+ footerParts.push(`${data[key].language} `);
+ setLanguage.add(key);
+ }
+
+ // Add verses
+ const verses = data[key].verses
+ .map(verse => `${verse.verse}. ${verse.text} `)
+ .join("\n");
+ formattedHtml += `${verses} \n`;
+
+ // Construct the footer
+ if (footerParts.length > 0) {
+ formattedHtml += `\n`;
+ }
+ }
+
+ return `${formattedHtml} `;
+ }
+}
diff --git a/src/js/formats/PlainFormat.js b/src/js/formats/PlainFormat.js
new file mode 100644
index 0000000..c5a6364
--- /dev/null
+++ b/src/js/formats/PlainFormat.js
@@ -0,0 +1,54 @@
+import { BaseFormat } from './BaseFormat.js';
+
+export class PlainFormat extends BaseFormat {
+ /**
+ * Formats the verses for plain text display.
+ *
+ * @param {Object} data - The data containing verses and their details.
+ * @param {boolean} showBook - Whether to show book names.
+ * @param {boolean} showTrans - Whether to show translations.
+ * @param {boolean} showAbbr - Whether to show abbreviations.
+ * @param {boolean} showLang - Whether to show languages.
+ * @returns {string} The formatted text.
+ */
+ get(data, showBook, showTrans, showAbbr, showLang) {
+ let formattedText = '';
+ let setBookName = new Set();
+ let setTranslation = new Set();
+ let setAbbreviation = new Set();
+ let setLanguage = new Set();
+
+ for (const key in data) {
+ if (!data.hasOwnProperty(key)) continue; // Ensure processing only own properties
+
+ let headerParts = [];
+ if (showTrans && !setTranslation.has(key)) {
+ headerParts.push(data[key].translation);
+ setTranslation.add(key);
+ }
+ if (showAbbr && !setAbbreviation.has(key)) {
+ headerParts.push(data[key].abbreviation);
+ setAbbreviation.add(key);
+ }
+ if (showBook && !setBookName.has(key)) {
+ headerParts.push(data[key].name);
+ setBookName.add(key);
+ }
+ if (showLang && !setLanguage.has(key)) {
+ headerParts.push(data[key].language);
+ setLanguage.add(key);
+ }
+
+ // Construct the header
+ if (headerParts.length > 0) {
+ formattedText += '[' + headerParts.join(' - ') + "]\n";
+ }
+
+ // Add verses
+ const verses = data[key].verses.map(verse => `${verse.verse}. ${verse.text}`).join("\n");
+ formattedText += verses + "\n\n"; // Add extra newline for separation
+ }
+
+ return formattedText.trim();
+ }
+}
diff --git a/src/js/getBible.js b/src/js/getBible.js
new file mode 100644
index 0000000..4e8f6d2
--- /dev/null
+++ b/src/js/getBible.js
@@ -0,0 +1,14 @@
+import { Loader } from "./core/Loader.js";
+
+/**
+ * Entry point to load Bible references.
+ * Attaches event listener to DOMContentLoaded to find elements with class 'getBible'
+ * and initializes Loader for each element.
+ */
+document.addEventListener('DOMContentLoaded', (event) => {
+ const elements = document.querySelectorAll('.getBible');
+ elements.forEach(element => {
+ const loader = new Loader();
+ loader.load(element);
+ });
+});
diff --git a/tests/basic.html b/tests/basic.html
new file mode 100644
index 0000000..501111f
--- /dev/null
+++ b/tests/basic.html
@@ -0,0 +1,29 @@
+
+
+
+
+
+ Basic GetBible Test
+
+
+
+
+
+
+
+Scripture References
+
+Hover over the references to see the scripture text:
+
+
+ John 3:16,19
+
+ John 3:16-17; 1 John 3:16-19,22
+ Genesis 1:1
+ Psalms 23:1-4
+ Romans 8:28,31-39
+
+
+
+
diff --git a/tests/bootstrap.html b/tests/bootstrap.html
new file mode 100644
index 0000000..9a46e16
--- /dev/null
+++ b/tests/bootstrap.html
@@ -0,0 +1,38 @@
+
+
+
+
+
+ Bootstrap Tooltips Test | GetBible
+
+
+
+
+
+
+
+
+
+
+
+Scripture References
+
+Hover over the references to see the scripture text:
+
+
+ John 3:16,19
+
+ John 3:16-17; 1 John 3:16-19,22
+ Genesis 1:1
+ Psalms 23:1-4
+ Romans 8:28,31-39
+
+
+
+
diff --git a/tests/foundation.html b/tests/foundation.html
new file mode 100644
index 0000000..197934a
--- /dev/null
+++ b/tests/foundation.html
@@ -0,0 +1,42 @@
+
+
+
+
+
+
+ Foundation Tooltips Test | GetBible
+
+
+
+
+
+
+
+
+
+Scripture References
+
+Hover over the references to see the scripture text:
+
+
+ John 3:16,19
+
+ John 3:16-17; 1 John 3:16-19,22
+ Genesis 1:1
+ Psalms 23:1-4
+ Romans 8:28,31-39
+
+
+
+
+
+
+
+Please help us!!! We are not getting this to work, because of jQuery, or so it seems.
+
+
diff --git a/tests/tailwind.html b/tests/tailwind.html
new file mode 100644
index 0000000..549bf21
--- /dev/null
+++ b/tests/tailwind.html
@@ -0,0 +1,35 @@
+
+
+
+
+
+ Tailwind Tooltips Test | GetBible
+
+
+
+
+
+
+
+
+
+
+
+
Scripture References
+
+
+
+Hover over the references to see the scripture text:
+
+
+ John 3:16,19
+
+ John 3:16-17; 1 John 3:16-19,22
+ Genesis 1:1
+ Psalms 23:1-4
+ Romans 8:28,31-39
+
+
+
+
diff --git a/tests/uikit.html b/tests/uikit.html
new file mode 100644
index 0000000..b2bb165
--- /dev/null
+++ b/tests/uikit.html
@@ -0,0 +1,36 @@
+
+
+
+
+
+ Uikit Tooltips Test | GetBible
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Scripture References
+
+Hover over the references to see the scripture text:
+
+
+ John 3:16,19
+
+ John 3:16-17; 1 John 3:16-19,22
+ Genesis 1:1
+ Psalms 23:1-4
+ Romans 8:28,31-39
+
+
+
+