module.exports = class { onCreate(input) { this.state = { characters: [], channels: [], eventNames: [], filters: [], search: "", activeMap: { events: {}, characters: {}, channels: {}, }, } } onInput(input) { this.update(input) } onMount(input) { this.getEl("search").addEventListener("keydown", ev => { if (this.waiting) { return } if (this.timeout != null) { clearTimeout(this.timeout) } if (ev.key === "Enter") { this.emit("add", "search", ev.target.value) this.getEl("search").value = "" } this.timeout = setTimeout(() => { this.timeout = null this.state.search = ev.target.value this.update(this.input) }, 200) }) } update(input) { if (input.filter) { this.updateActive(input) } const search = this.state.search.toLocaleLowerCase() this.state.characters = input.characters.filter(c => !this.state.activeMap.characters[c.id]).filter(c => this.matches(c.name, search)).sort((a,b) => compareFilters(a.name, b.name, search)) this.state.channels = input.channels.filter(c => !this.state.activeMap.channels[c.name]).filter(c => this.matches(c.name, search)).sort((a,b) => compareFilters(a.name, b.name, search)) this.state.eventNames = input.eventNames.filter(e => !this.state.activeMap.events[e]).filter(e => this.matches(e, search)).sort((a,b) => compareFilters(a, b, search)) } updateActive({filter, characters, channels, eventNames}) { const filters = [] const activeMap = { events: {}, characters: {}, channels: {}, } if (filter.search != null) { filters.push({ type: "search", key: "search", icon: "T", color: "color-text", text: filter.search, }) } for (const character of (filter.characters || [])) { activeMap.characters[character] = true filters.push({ type: "characters", key: character, icon: "C", color: "color-tag-character", id: character, text: (characters.find(c => c.id === character) || {name: "Unknown ("+character+")"}).name, }) } for (const channel of (filter.channels || [])) { activeMap.channels[channel] = true filters.push({ type: "channels", key: channel.replace(/'/g, "__"), icon: "#", color: "color-tag-location", id: channel, text: channel, }) } for (const event of (filter.events || [])) { activeMap.events[event] = true filters.push({ type: "events", key: event.replace(/['\s\.]/g, "__"), icon: "E", color: "color-tag-event", id: event, text: event, }) } this.state.filters = filters this.state.activeMap = activeMap } matches(str, search) { if (search == "") { return true } if (search.length > str.length) { return false } return str.toLowerCase().indexOf(search) !== -1 } } /** * * @param {string} a * @param {string} b * @param {string} search */ function compareFilters(a, b, search) { if (search != "") { if (search.charAt(0) != "#" && a.charAt(0) == "#" && b.charAt(0) == "#") { search = '#' + search } const aStarts = a.toLocaleLowerCase().startsWith(search) const bStarts = b.toLocaleLowerCase().startsWith(search) if (aStarts && !bStarts) { return -1 } else if (bStarts && !aStarts) { return 1 } } return a.localeCompare(b) }