const link = /(\w+:\/\/(?:[^:/[\]\s]+|\[[0-9a-f:]+\])(?::\d+)?(?:\/[^/\s]*)*)/ig; const XSS = require('./xss'); function sanitizedWithLinksReplaced(text) { return XSS.sanitizeText(text) .replace(link, '$1'); } class Poll { static create(createdBy, title, choices, options = { hideVotes: false }) { let poll = new Poll(); poll.createdAt = new Date(); poll.createdBy = createdBy; poll.title = sanitizedWithLinksReplaced(title); poll.choices = choices.map(choice => sanitizedWithLinksReplaced(choice)); poll.hideVotes = options.hideVotes; poll.votes = new Map(); return poll; } static fromChannelData({ initiator, title, options, _counts, votes, timestamp, obscured }) { let poll = new Poll(); if (timestamp === undefined) // Very old polls still in the database lack timestamps timestamp = Date.now(); poll.createdAt = new Date(timestamp); poll.createdBy = initiator; poll.title = title; poll.choices = options; poll.votes = new Map(); Object.keys(votes).forEach(key => { if (votes[key] !== null) poll.votes.set(key, votes[key]); }); poll.hideVotes = obscured; return poll; } toChannelData() { let counts = new Array(this.choices.length); counts.fill(0); // TODO: it would be desirable one day to move away from using an Object here. // This is just for backwards-compatibility with the existing format. let votes = {}; this.votes.forEach((index, key) => { votes[key] = index; counts[index]++; }); return { title: this.title, initiator: this.createdBy, options: this.choices, counts, votes, obscured: this.hideVotes, timestamp: this.createdAt.getTime() }; } countVote(key, choiceId) { if (choiceId < 0 || choiceId >= this.choices.length) return false; let changed = !this.votes.has(key) || this.votes.get(key) !== choiceId; this.votes.set(key, choiceId); return changed; } uncountVote(key) { let changed = this.votes.has(key); this.votes.delete(key); return changed; } toUpdateFrame(showHiddenVotes) { let counts = new Array(this.choices.length); counts.fill(0); this.votes.forEach(index => counts[index]++); if (this.hideVotes) { counts = counts.map(c => { if (showHiddenVotes) return `${c}?`; else return '?'; }); } return { title: this.title, options: this.choices, counts: counts, initiator: this.createdBy, timestamp: this.createdAt.getTime() }; } } exports.Poll = Poll;