diff --git a/app/assets/javascripts/listEntry.js b/app/assets/javascripts/listEntry.js
index 6a6087efa..9161b22d2 100644
--- a/app/assets/javascripts/listEntry.js
+++ b/app/assets/javascripts/listEntry.js
@@ -1,22 +1,28 @@
(function (window) {
'use strict';
+ // This module creates dynamic add/remove input lists. It lets users add multiple entries
+ // (like email addresses or domains) with "Add another" and "Remove" buttons. It handles
+ // min/max entry limits, focus management, and preserves input attributes.
+ // We're keeping this for now but it's not currently being used in the application.
+
var Modules = window.NotifyModules;
var lists = [],
listEntry,
ListEntry;
ListEntry = function (elm) {
- var $elm = $(elm),
- idPattern = $elm.prop('id');
+ var idPattern = elm.id;
- if (!idPattern) { return false; }
+ if (!idPattern) {
+ return false;
+ }
this.idPattern = idPattern;
this.elementSelector = '.list-entry, .input-list__button--remove, .input-list__button--add';
this.entries = [];
- this.$wrapper = $elm;
+ this.wrapper = elm;
this.minEntries = 2;
- this.listItemName = this.$wrapper.data('listItemName');
+ this.listItemName = this.wrapper.dataset.listItemName;
this.getSharedAttributes();
this.getOriginalClasses();
@@ -26,33 +32,43 @@
this.render();
this.bindEvents();
};
- ListEntry.optionalAttributes = ['aria-describedby'];
+ ListEntry.escapeHtml = function(unsafe) {
+ if (!unsafe) return '';
+ return String(unsafe)
+ .replace(/&/g, "&")
+ .replace(//g, ">")
+ .replace(/"/g, """)
+ .replace(/'/g, "'");
+ };
ListEntry.prototype.renderEntry = function(data) {
+ var escapeHtml = ListEntry.escapeHtml;
return `
-
`;
};
ListEntry.prototype.renderAddButton = function(data) {
- return ``;
+ var escapeHtml = ListEntry.escapeHtml;
+ return ``;
};
ListEntry.prototype.getSharedAttributes = function () {
- var $inputs = this.$wrapper.find('input'),
+ var inputs = this.wrapper.querySelectorAll('input'),
generatedAttributes = ['id', 'name', 'value', 'class'],
attributes = [],
attrIdx,
@@ -64,15 +80,16 @@
elmIdx = attrsByElm.length,
existingAttributes = [],
elmAttrs,
- attrIdx;
+ attrIdx,
+ escapeHtml = ListEntry.escapeHtml;
while (elmIdx--) {
elmAttrs = attrsByElm[elmIdx];
attrIdx = elmAttrs.length;
while (attrIdx--) {
// prevent duplicates
- if ($.inArray(elmAttrs[attrIdx].name, existingAttributes) === -1) {
- attrStr += ` ${elmAttrs[attrIdx].name}="${elmAttrs[attrIdx].value}"`;
+ if (existingAttributes.indexOf(elmAttrs[attrIdx].name) === -1) {
+ attrStr += ` ${escapeHtml(elmAttrs[attrIdx].name)}="${escapeHtml(elmAttrs[attrIdx].value)}"`;
existingAttributes.push(elmAttrs[attrIdx].name);
}
}
@@ -80,11 +97,11 @@
return attrStr;
};
- $inputs.each(function (idx, elm) {
+ inputs.forEach(function (elm) {
attrIdx = elm.attributes.length;
elmAttributes = [];
while(attrIdx--) {
- if ($.inArray(elm.attributes[attrIdx].name, generatedAttributes) === -1) {
+ if (generatedAttributes.indexOf(elm.attributes[attrIdx].name) === -1) {
elmAttributes.push({
'name': elm.attributes[attrIdx].name,
'value': elm.attributes[attrIdx].value
@@ -99,9 +116,9 @@
this.sharedAttributes = (attributes.length) ? getAttributesHTML(attributes) : '';
};
ListEntry.prototype.getOriginalClasses = function () {
- var $firstInput = this.$wrapper.find('input').first();
- if ($firstInput.length) {
- var classList = $firstInput.attr('class');
+ var firstInput = this.wrapper.querySelector('input');
+ if (firstInput) {
+ var classList = firstInput.getAttribute('class');
if (classList) {
// Preserve any additional classes from the original input
this.additionalClasses = classList;
@@ -114,8 +131,8 @@
};
ListEntry.prototype.getValues = function () {
this.entries = [];
- this.$wrapper.find('input').each(function (idx, elm) {
- var val = $(elm).val();
+ this.wrapper.querySelectorAll('input').forEach(function (elm) {
+ var val = elm.value;
this.entries.push(val);
}.bind(this));
@@ -144,11 +161,12 @@
}
};
ListEntry.prototype.bindEvents = function () {
- this.$wrapper.on('click', '.input-list__button--remove', function (e) {
- this.removeEntry($(e.target));
- }.bind(this));
- this.$wrapper.on('click', '.input-list__button--add', function (e) {
- this.addEntry();
+ this.wrapper.addEventListener('click', function (e) {
+ if (e.target.closest('.input-list__button--remove')) {
+ this.removeEntry(e.target);
+ } else if (e.target.closest('.input-list__button--add')) {
+ this.addEntry();
+ }
}.bind(this));
};
ListEntry.prototype.shiftFocus = function (opts) {
@@ -159,7 +177,13 @@
} else { // opts.action === 'add'
numberTargeted = opts.entryNumberFocused + 1;
}
- this.$wrapper.find('.list-entry').eq(numberTargeted - 1).find('input').focus();
+ var entries = this.wrapper.querySelectorAll('.list-entry');
+ if (entries[numberTargeted - 1]) {
+ var input = entries[numberTargeted - 1].querySelector('input');
+ if (input) {
+ input.focus();
+ }
+ }
};
ListEntry.prototype.removeEntryFromEntries = function (entryNumber) {
var idx,
@@ -173,7 +197,7 @@
}
this.entries = newEntries;
};
- ListEntry.prototype.addEntry = function ($removeButton) {
+ ListEntry.prototype.addEntry = function () {
var currentLastEntryNumber = this.entries.length;
this.getValues();
@@ -181,8 +205,23 @@
this.render();
this.shiftFocus({ 'action' : 'add', 'entryNumberFocused' : currentLastEntryNumber });
};
- ListEntry.prototype.removeEntry = function ($removeButton) {
- var entryNumber = parseInt($removeButton.find('span').text().match(/\d+/)[0], 10);
+ ListEntry.prototype.removeEntry = function (removeButton) {
+ var button = removeButton.closest('.input-list__button--remove');
+ if (!button) {
+ console.error('ListEntry: Remove button not found');
+ return;
+ }
+ var span = button.querySelector('span');
+ if (!span) {
+ console.error('ListEntry: Entry number span not found in remove button');
+ return;
+ }
+ var match = span.textContent.match(/\d+/);
+ if (!match) {
+ console.error('ListEntry: Could not find entry number in remove button');
+ return;
+ }
+ var entryNumber = parseInt(match[0], 10);
this.getValues();
this.removeEntryFromEntries(entryNumber);
@@ -190,8 +229,10 @@
this.shiftFocus({ 'action' : 'remove', 'entryNumberFocused' : entryNumber });
};
ListEntry.prototype.render = function () {
- this.$wrapper.find(this.elementSelector).remove();
- $.each(this.entries, function (idx, entry) {
+ this.wrapper.querySelectorAll(this.elementSelector).forEach(function(el) {
+ el.remove();
+ });
+ this.entries.forEach(function (entry, idx) {
var entryNumber = idx + 1,
dataObj = {
'id' : this.getId(entryNumber),
@@ -207,10 +248,10 @@
if (entryNumber > 1) {
dataObj.button = true;
}
- this.$wrapper.append(this.renderEntry(dataObj));
+ this.wrapper.insertAdjacentHTML('beforeend', this.renderEntry(dataObj));
}.bind(this));
if (this.entries.length < this.maxEntries) {
- this.$wrapper.append(this.renderAddButton({
+ this.wrapper.insertAdjacentHTML('beforeend', this.renderAddButton({
'listItemName' : this.listItemName,
'entriesLeft' : (this.maxEntries - this.entries.length)
}));
@@ -219,7 +260,7 @@
Modules['list-entry'] = function () {
- this.start = component => lists.push(new ListEntry($(component)));
+ this.start = component => lists.push(new ListEntry(component));
};