function normalized(text) { return String(text || "").trim().toLowerCase(); } function origin(text) { const match = String(text || "").match(/^https?:\/\/[^/]+/i); return match ? match[0].toLowerCase() : ""; } function filterItems(items, query, browserOrigin) { const needle = normalized(query); const observed = origin(browserOrigin); return (items || []).filter(item => { return needle.length === 0 || String(item.searchText || "").includes(needle); }).map((item, index) => { const exactOrigin = observed.length > 0 && (item.origins || []).some(candidate => { return origin(candidate) === observed; }); return { "item": item, "index": index, "originRank": needle.length === 0 && exactOrigin ? 0 : 1, "favoriteRank": item.favorite ? 0 : 1 }; }).sort((a, b) => { return (a.originRank - b.originRank) || (a.favoriteRank - b.favoriteRank) || a.item.title.localeCompare(b.item.title) || (a.index - b.index); }).map(entry => { return entry.item; }); } function groups(items) { const favorites = []; const all = []; for (let i = 0; i < (items || []).length; i++) { if (items[i].favorite) favorites.push(items[i]); else all.push(items[i]); } return [{ "label": "favorites", "items": favorites }, { "label": "all items", "items": all }].filter(group => { return group.items.length > 0; }); } function nextSelection(items, selectedId, delta) { if (!items || items.length === 0) return ""; let index = items.findIndex(item => { return item.id === selectedId; }); if (index < 0) index = delta > 0 ? -1 : 0; return items[(index + delta + items.length) % items.length].id; } if (typeof module !== "undefined") module.exports = { normalized, origin, filterItems, groups, nextSelection };