/
whiteboard_poll.coffee
executable file
·284 lines (255 loc) · 11.6 KB
/
whiteboard_poll.coffee
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
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
# A poll in the whiteboard
class @WhiteboardPollModel extends WhiteboardToolModel
constructor: (@paper) ->
super @paper
# the defintion of this shape, kept so we can redraw the shape whenever needed
# format: x1, y1, x2, y2, stroke color, thickness, fill
@definition = [0, 0, 0, 0, "#333333", "2px", "#ffffff"]
@paper
# Creates a polling in the paper
# @param {number} x1 the x value of the top left corner
# @param {number} y1 the y value of the top left corner
# @param {number} x2 the x value of the bottom right corner
# @param {number} y2 the y value of the bottom right corner
# @param {number} thickness the thickness of the object's line(s)
# @param {string} backgroundColor the background color of the base poll rectangle
# @param {number} calcFontSize the default font-size of the text objects
make: (startingData) =>
#data needed to create the first base rectangle filled with white color
x1 = startingData.points[0]
y1 = startingData.points[1]
x2 = startingData.points[2] + startingData.points[0] - 0.001
y2 = startingData.points[3] + startingData.points[1] - 0.001
thickness = 2
backgroundColor = "#ffffff"
verticalPadding = 0
horizontalPadding = 0
calcFontSize = 30
votesTotal = 0
maxNumVotes = 0
textArray = []
#creating an array of text objects for the labels, percentages and number inside line bars
if startingData.result?
#counting the total number of votes and finding the biggest number of votes
for i in [0..startingData.result.length-1]
votesTotal += startingData.result[i].num_votes
if maxNumVotes < startingData.result[i].num_votes
maxNumVotes = startingData.result[i].num_votes
textArray[i] = []
#filling the array with proper text objects to display
for i in [0..startingData.result.length-1]
textArray[i].push(startingData.result[i].key, startingData.result[i].num_votes+"")
if votesTotal is 0
textArray[i].push("0%")
else
textArray[i].push(startingData.result[i].num_votes/votesTotal*100+"%")
#if coordinates are reversed - change them back
if x2 < x1
[x1, x2] = [x2, x1]
if y2 < y1
[y1, y2] = [y2, y1]
#Params:
#x - the actual calculated x value of the top left corner of the polling area
#y - the actual calculated y value of the top left corner of the polling area
#width - the width of the polling area
#height - the height of the polling area
x = x1 * @gw + @xOffset
y = y1 * @gh + @yOffset
width = (x2 * @gw + @xOffset) - x
height = (y2 * @gh + @yOffset) - y
#creating a base rectangle
@obj = @paper.rect(x, y, width, height, 0)
@obj.attr "stroke", "#333333"
@obj.attr "fill", backgroundColor
@obj.attr "stroke-width", zoomStroke(formatThickness(thickness))
@definition =
shape: "poll_result"
data: [x1, y1, x2, y2, @obj.attrs["stroke"], @obj.attrs["stroke-width"], @obj.attrs["fill"]]
#Calculating a proper font-size, and the maximum widht and height of the objects
calculatedData = calculateFontAndWidth(textArray, calcFontSize, width, height, x, y)
calcFontSize = calculatedData[0]
maxLeftWidth = calculatedData[1]
maxRightWidth = calculatedData[2]
maxLineHeight = calculatedData[3]
maxDigitWidth = calculatedData[4]
maxBarWidth = width*0.9-maxLeftWidth-maxRightWidth
barHeight = height*0.75/textArray.length
svgNSi = "http://www.w3.org/2000/svg"
#Initializing a text element for further calculations and for the left column of keys
@obj2 = @paper.text(x, y, "")
@obj2.attr
"fill": "#333333"
"font-family": "Arial"
"font-size": calcFontSize
@obj2.node.style["text-anchor"] = "start" # force left align
@obj2.node.style["textAnchor"] = "start" # for firefox, 'cause they like to be different
leftCell = @obj2.node
while leftCell? and leftCell.hasChildNodes()
leftCell.removeChild(leftCell.firstChild)
#Initializing a text element for the right column of percentages
@obj3 = @paper.text(x, y, "")
@obj3.attr
"fill": "#333333"
"font-family": "Arial"
"font-size": calcFontSize
@obj3.node.style["text-anchor"] = "end" # force right align
@obj3.node.style["textAnchor"] = "end" # for firefox, 'cause they like to be different
rightCell = @obj3.node
while rightCell? and rightCell.hasChildNodes()
rightCell.removeChild(rightCell.firstChild)
#setting a font size for the text elements on the left and on the right
leftCell.style['font-size'] = calcFontSize
rightCell.style['font-size'] = calcFontSize
#Horizontal padding
horizontalPadding = width*0.1/4
#Vertical padding
verticalPadding = height*0.25/(textArray.length+1)
#*****************************************************************************************************
#******************************************MAGIC NUMBER***********************************************
#There is no automatic vertical centering in SVG.
#To center the text element we have to move it down by the half of its height.
#But every text element has its own padding by default.
#The height we receive by calling getBBox() includes padding, but the anchor point doesn't consider it.
#This way the text element is moved down a little bit too much and we have to move it up a bit.
#Number 3.5 seems to work fine.
# Oleksandr Zhurbenko. August 19, 2015
magicNumber = 3.5
#*****************************************************************************************************
#Initial coordinates of the key column
yLeft = y+verticalPadding+barHeight/2 - magicNumber
xLeft = x + horizontalPadding + 1
#Initial coordinates of the line bar column
xBar = x+maxLeftWidth+horizontalPadding*2
yBar = y + verticalPadding
#Initial coordinates of the percentage column
yRight = y+verticalPadding+barHeight/2 - magicNumber
xRight = x + horizontalPadding*3 + maxLeftWidth + maxRightWidth + maxBarWidth + 1
objects = [@obj, @obj2, @obj3]
for i in [0..textArray.length-1]
#Adding an element to the left column
tempSpanEl = document.createElementNS(svgNSi, "tspan")
tempSpanEl.setAttributeNS null, "x", xLeft
tempSpanEl.setAttributeNS null, "y", yLeft
tempSpanEl.setAttributeNS null, "dy", maxLineHeight/2
tempTextNode = document.createTextNode(textArray[i][0])
tempSpanEl.appendChild tempTextNode
leftCell.appendChild tempSpanEl
#drawing a black graph bar
if maxNumVotes is 0 or startingData.result[i].num_votes is 0
barWidth = 2
else
barWidth = startingData.result[i].num_votes / maxNumVotes * maxBarWidth
@obj4 = @paper.rect(xBar, yBar, barWidth, barHeight, 0)
@obj4.attr "stroke", "#333333"
@obj4.attr "fill", "#333333"
@obj4.attr "stroke-width", zoomStroke(formatThickness(0))
objects.push @obj4
#Adding an element to the right column
tempSpanEl = document.createElementNS(svgNSi, "tspan")
tempSpanEl.setAttributeNS null, "x", xRight
tempSpanEl.setAttributeNS null, "y", yRight
tempSpanEl.setAttributeNS null, "dy", maxLineHeight/2
tempTextNode = document.createTextNode(textArray[i][2])
tempSpanEl.appendChild tempTextNode
rightCell.appendChild tempSpanEl
#changing the Y coordinate for all the objects
yBar = yBar + barHeight + verticalPadding
yLeft = yLeft + barHeight + verticalPadding
yRight = yRight + barHeight + verticalPadding
#Initializing a text element for the number of votes text field inside the line bar
@obj5 = @paper.text(x, y, "")
@obj5.attr
"fill": "#333333"
"font-family": "Arial"
"font-size": calcFontSize
centerCell = @obj5.node
while centerCell? and centerCell.hasChildNodes()
centerCell.removeChild(centerCell.firstChild)
#Initial coordinates of the text inside the bar column
xNumVotesDefault = x+maxLeftWidth+horizontalPadding*2
xNumVotesMovedRight = xNumVotesDefault + barWidth/2 + horizontalPadding + maxDigitWidth/2
yNumVotes = y + verticalPadding - magicNumber
color = "white"
#Drawing the text element with the number of votes inside of the black line bars
#Or outside if a line bar is too small
for i in [0..textArray.length-1]
if maxNumVotes is 0 or startingData.result[i].num_votes is 0
barWidth = 2
else
barWidth = startingData.result[i].num_votes / maxNumVotes * maxBarWidth
if barWidth < maxDigitWidth + 8
xNumVotes = xNumVotesMovedRight
color = "#333333"
else
xNumVotes = xNumVotesDefault
color = "white"
tempSpanEl = document.createElementNS(svgNSi, "tspan")
tempSpanEl.setAttributeNS null, "x", xNumVotes + barWidth/2
tempSpanEl.setAttributeNS null, "y", yNumVotes + barHeight/2
tempSpanEl.setAttributeNS null, "dy", maxLineHeight/2
tempSpanEl.setAttributeNS null, "fill", color
tempTextNode = document.createTextNode(startingData.result[i].num_votes)
tempSpanEl.appendChild tempTextNode
centerCell.appendChild tempSpanEl
yNumVotes = yNumVotes + barHeight + verticalPadding
objects.push @obj5
objects
# Update the poll dimensions. Does nothing.
update: (startingData) ->
calculateFontAndWidth = (textArray, calcFontSize, width, height, x, y) ->
calculatedData = []
#maximum line width can be either 1/3 of the line or 40px
#maximum line height is 75% of the initial size of the box divided by the number of lines
maxLineWidth = width/3
maxLineHeight = height*0.75/textArray?.length
#calculating a proper font-size
flag = true
while flag
flag = false
for i in [0..textArray.length-1]
for j in [0..textArray[i].length-1]
test = getRenderedTextSize(textArray[i][j], calcFontSize)
spanWidth = test[0]
spanHeight = test[1]
if spanWidth > maxLineWidth or spanHeight > maxLineHeight
calcFontSize -= 1
flag = true
calculatedData.push calcFontSize
#looking for a maximum width and height of the left and right text elements
maxLeftWidth = 0
maxRightWidth = 0
maxLineHeight = 0
for line in textArray
test = getRenderedTextSize(line[0], calcFontSize)
spanWidth = test[0]
spanHeight = test[1]
if spanWidth > maxLeftWidth
maxLeftWidth = spanWidth
if spanHeight > maxLineHeight
maxLineHeight = spanHeight
test = getRenderedTextSize(line[2], calcFontSize)
spanWidth = test[0]
spanHeight = test[1]
if spanWidth > maxRightWidth
maxRightWidth = spanWidth
if spanHeight > maxLineHeight
maxLineHeight = spanHeight
test = getRenderedTextSize("0", calcFontSize)
spanWidth = test[0]
spanHeight = test[1]
maxDigitWidth = spanWidth
calculatedData.push maxLeftWidth, maxRightWidth, maxLineHeight, maxDigitWidth
calculatedData
getRenderedTextSize = (string, fontSize) ->
paper = Raphael(0, 0, 0, 0)
paper.canvas.style.visibility = 'hidden'
el = paper.text(0, 0, string)
el.attr "font-family", "Arial"
el.attr "font-size", fontSize
bBox = el.getBBox()
paper.remove()
arrayTest = []
arrayTest.push bBox.width
arrayTest.push bBox.height
paper.remove()
arrayTest