-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathindex.js
175 lines (155 loc) · 5.49 KB
/
index.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
const flakes = require('simpleflakes')
const { ALREADY_VOTED, UNAVAILABLE_CHOICE } = require('./util/errors')
/**
* A basic poll object that allows for easier tracking of surveys and polls.
*
* @class
* @prop {string} label - Indicates the label/question to display.
* @prop {string} startTime - Indicates when the poll should start.
* @prop {string} endTime - Indicates when the poll should end.
* @prop {Map<string, number>} values - The poll choices and their current vote count.
* @prop {Map<string, boolean>} voters - The poll voters id and if they've voted or not.
* @prop {boolean} allowMultiple - Indicates if a voter can vote more than once.
* @prop {boolean} anyInput - Indicates if values that have not been previously defined can be used.
* @prop {string} id - The unique id for this given poll, if none is provided, it is generated using `simpleflakes`.
*/
class Poll {
/**
*
* @param {object} options
* @param {string} options.label The label/descrption to display.
* @param {string} options.startTime When the poll should start.
* @param {string} options.endTime When the poll should end.
* @param {string[]} [options.values] An array of acceptable choices.
* @param {boolean} [options.allowMultiple] Whether or not a voter can vote more than once.
* @param {boolean} [options.anyInput] Whether or not any value is acceptable.
* @param {string} [id] The unique id for this given poll, randomly generated by default.
*/
constructor (options, id) {
// Set required parameters
this.label = options.label
this.startTime = options.startTime
this.endTime = options.endTime
// Set optional parameters
if (options.values === undefined) this.values = new Map()
else options.values.forEach(val => { this.values[val] = 0 })
if (options.allowMultiple === undefined) this.allowMultiple = false
else this.allowMultiple = options.allowMultiple
if (options.anyInput === undefined) this.anyInput = false
else this.anyInput = options.anyInput
if (id === undefined) {
this.id = flakes.simpleflake(Date.now(), 23).toString()
} else this.id = id
this.voters = new Map()
}
/**
* If any input is allowed, the given choice is incremented by 1 or set to 1 if it
* is the first time to be voted on. If not, then the given choice has its vote
* count increased and resulting value returned.
*
* @param {string} choice A string representing the choice you wish to vote for.
* @param {string} voterId A unique string representing a voter.
* @param {boolean} [rescind] An optional flag to remove a vote in the event a user changes it.
*
* @returns {Promise<number>}
*/
async vote (choice, voterId, rescind = false) {
// Verify if more than one vote is allowed
if (this.allowMultiple) {
return this.handleChoice(choice, rescind)
} else {
// Check if the user has already voted
if (this.voters.get(voterId) === true) return Promise.reject(ALREADY_VOTED)
else {
// Prevent the user from voting again
this.voters.set(voterId, true)
return this.handleChoice(choice, rescind)
}
}
}
/**
* Formats the current voting results into a prettier string with line feeds and everything.
*/
results () {
let output = 'Current voting results:\n'
this.values.forEach((val, key, map) => {
output += `\n${key} has ${val} vote(s).`
})
return output
}
/**
* Not intended to be called by users of this module unless they know what they're doing.
*
* @param {string} choice
* @param {boolean} rescind
*
* @returns {Promise<number>} Resulting vote count
*
* @private
*/
handleChoice (choice, rescind) {
return new Promise((resolve, reject) => {
if (rescind) resolve(this.removeVote(choice))
else resolve(this.addVote(choice))
})
}
/**
* Used to increase the vote count for a specified voting choice. Not intended to be called by
* users of the module.
*
* @param {string} choice The choice you wish to add a vote to.
*
* @returns {Promise<number>} Resulting vote count
*
* @private
*/
addVote (choice) {
return new Promise((resolve, reject) => {
if (this.anyInput) {
let currVal = this.values.get(choice)
if (currVal === undefined) {
this.values.set(choice, 1)
} else {
this.values.set(choice, currVal + 1)
}
resolve(this.values.get(choice))
} else {
let currVal = this.values.get(choice)
if (currVal === undefined) reject(UNAVAILABLE_CHOICE)
else {
this.values.set(choice, currVal + 1)
resolve(this.values.get(choice))
}
}
})
}
/**
* Used to decrease the vote count for a specified voting choice. Not intended to be called by
* users of the module.
*
* @param {string} choice The choice you wish to add a vote to.
*
* @returns {Promise<number>} Resulting vote count
*
* @private
*/
removeVote (choice) {
return new Promise((resolve, reject) => {
if (this.anyInput) {
let currVal = this.values.get(choice)
if (currVal !== undefined) {
this.values.set(choice, currVal - 1)
}
resolve(this.values.get(choice))
} else {
let currVal = this.values.get(choice)
if (currVal === undefined) reject(UNAVAILABLE_CHOICE)
else {
this.values.set(choice, currVal - 1)
resolve(this.values.get(choice))
}
}
})
}
}
module.exports = Poll