OLD | NEW |
| (Empty) |
1 /* | |
2 * Copyright (C) 2007, 2008 Apple Inc. All rights reserved. | |
3 * Copyright (C) 2008, 2009 Anthony Ricaud <rik@webkit.org> | |
4 * | |
5 * Redistribution and use in source and binary forms, with or without | |
6 * modification, are permitted provided that the following conditions | |
7 * are met: | |
8 * | |
9 * 1. Redistributions of source code must retain the above copyright | |
10 * notice, this list of conditions and the following disclaimer. | |
11 * 2. Redistributions in binary form must reproduce the above copyright | |
12 * notice, this list of conditions and the following disclaimer in the | |
13 * documentation and/or other materials provided with the distribution. | |
14 * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of | |
15 * its contributors may be used to endorse or promote products derived | |
16 * from this software without specific prior written permission. | |
17 * | |
18 * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY | |
19 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED | |
20 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | |
21 * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY | |
22 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES | |
23 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | |
24 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND | |
25 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF | |
27 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
28 */ | |
29 | |
30 WebInspector.SummaryBar = function(categories) | |
31 { | |
32 this.categories = categories; | |
33 | |
34 this.element = document.createElement("div"); | |
35 this.element.className = "summary-bar"; | |
36 | |
37 this.graphElement = document.createElement("canvas"); | |
38 this.graphElement.setAttribute("width", "450"); | |
39 this.graphElement.setAttribute("height", "38"); | |
40 this.graphElement.className = "summary-graph"; | |
41 this.element.appendChild(this.graphElement); | |
42 | |
43 this.legendElement = document.createElement("div"); | |
44 this.legendElement.className = "summary-graph-legend"; | |
45 this.element.appendChild(this.legendElement); | |
46 } | |
47 | |
48 WebInspector.SummaryBar.prototype = { | |
49 | |
50 get calculator() { | |
51 return this._calculator; | |
52 }, | |
53 | |
54 set calculator(x) { | |
55 this._calculator = x; | |
56 }, | |
57 | |
58 reset: function() | |
59 { | |
60 this.legendElement.removeChildren(); | |
61 this._drawSummaryGraph(); | |
62 }, | |
63 | |
64 update: function(data) | |
65 { | |
66 var graphInfo = this.calculator.computeSummaryValues(data); | |
67 | |
68 var fillSegments = []; | |
69 | |
70 this.legendElement.removeChildren(); | |
71 | |
72 for (var category in this.categories) { | |
73 var size = graphInfo.categoryValues[category]; | |
74 if (!size) | |
75 continue; | |
76 | |
77 var color = this.categories[category].color; | |
78 var colorString = "rgb(" + color.r + ", " + color.g + ", " + color.b
+ ")"; | |
79 | |
80 var fillSegment = {color: colorString, value: size}; | |
81 fillSegments.push(fillSegment); | |
82 | |
83 var legendLabel = this._makeLegendElement(this.categories[category].
title, this.calculator.formatValue(size), colorString); | |
84 this.legendElement.appendChild(legendLabel); | |
85 } | |
86 | |
87 if (graphInfo.total) { | |
88 var totalLegendLabel = this._makeLegendElement(WebInspector.UIString
("Total"), this.calculator.formatValue(graphInfo.total)); | |
89 totalLegendLabel.addStyleClass("total"); | |
90 this.legendElement.appendChild(totalLegendLabel); | |
91 } | |
92 | |
93 this._drawSummaryGraph(fillSegments); | |
94 }, | |
95 | |
96 _drawSwatch: function(canvas, color) | |
97 { | |
98 var ctx = canvas.getContext("2d"); | |
99 | |
100 function drawSwatchSquare() { | |
101 ctx.fillStyle = color; | |
102 ctx.fillRect(0, 0, 13, 13); | |
103 | |
104 var gradient = ctx.createLinearGradient(0, 0, 13, 13); | |
105 gradient.addColorStop(0.0, "rgba(255, 255, 255, 0.2)"); | |
106 gradient.addColorStop(1.0, "rgba(255, 255, 255, 0.0)"); | |
107 | |
108 ctx.fillStyle = gradient; | |
109 ctx.fillRect(0, 0, 13, 13); | |
110 | |
111 gradient = ctx.createLinearGradient(13, 13, 0, 0); | |
112 gradient.addColorStop(0.0, "rgba(0, 0, 0, 0.2)"); | |
113 gradient.addColorStop(1.0, "rgba(0, 0, 0, 0.0)"); | |
114 | |
115 ctx.fillStyle = gradient; | |
116 ctx.fillRect(0, 0, 13, 13); | |
117 | |
118 ctx.strokeStyle = "rgba(0, 0, 0, 0.6)"; | |
119 ctx.strokeRect(0.5, 0.5, 12, 12); | |
120 } | |
121 | |
122 ctx.clearRect(0, 0, 13, 24); | |
123 | |
124 drawSwatchSquare(); | |
125 | |
126 ctx.save(); | |
127 | |
128 ctx.translate(0, 25); | |
129 ctx.scale(1, -1); | |
130 | |
131 drawSwatchSquare(); | |
132 | |
133 ctx.restore(); | |
134 | |
135 this._fadeOutRect(ctx, 0, 13, 13, 13, 0.5, 0.0); | |
136 }, | |
137 | |
138 _drawSummaryGraph: function(segments) | |
139 { | |
140 if (!segments || !segments.length) { | |
141 segments = [{color: "white", value: 1}]; | |
142 this._showingEmptySummaryGraph = true; | |
143 } else | |
144 delete this._showingEmptySummaryGraph; | |
145 | |
146 // Calculate the total of all segments. | |
147 var total = 0; | |
148 for (var i = 0; i < segments.length; ++i) | |
149 total += segments[i].value; | |
150 | |
151 // Calculate the percentage of each segment, rounded to the nearest perc
ent. | |
152 var percents = segments.map(function(s) { return Math.max(Math.round(100
* s.value / total), 1) }); | |
153 | |
154 // Calculate the total percentage. | |
155 var percentTotal = 0; | |
156 for (var i = 0; i < percents.length; ++i) | |
157 percentTotal += percents[i]; | |
158 | |
159 // Make sure our percentage total is not greater-than 100, it can be gre
ater | |
160 // if we rounded up for a few segments. | |
161 while (percentTotal > 100) { | |
162 for (var i = 0; i < percents.length && percentTotal > 100; ++i) { | |
163 if (percents[i] > 1) { | |
164 --percents[i]; | |
165 --percentTotal; | |
166 } | |
167 } | |
168 } | |
169 | |
170 // Make sure our percentage total is not less-than 100, it can be less | |
171 // if we rounded down for a few segments. | |
172 while (percentTotal < 100) { | |
173 for (var i = 0; i < percents.length && percentTotal < 100; ++i) { | |
174 ++percents[i]; | |
175 ++percentTotal; | |
176 } | |
177 } | |
178 | |
179 var ctx = this.graphElement.getContext("2d"); | |
180 | |
181 var x = 0; | |
182 var y = 0; | |
183 var w = 450; | |
184 var h = 19; | |
185 var r = (h / 2); | |
186 | |
187 function drawPillShadow() | |
188 { | |
189 // This draws a line with a shadow that is offset away from the line
. The line is stroked | |
190 // twice with different X shadow offsets to give more feathered edge
s. Later we erase the | |
191 // line with destination-out 100% transparent black, leaving only th
e shadow. This only | |
192 // works if nothing has been drawn into the canvas yet. | |
193 | |
194 ctx.beginPath(); | |
195 ctx.moveTo(x + 4, y + h - 3 - 0.5); | |
196 ctx.lineTo(x + w - 4, y + h - 3 - 0.5); | |
197 ctx.closePath(); | |
198 | |
199 ctx.save(); | |
200 | |
201 ctx.shadowBlur = 2; | |
202 ctx.shadowColor = "rgba(0, 0, 0, 0.5)"; | |
203 ctx.shadowOffsetX = 3; | |
204 ctx.shadowOffsetY = 5; | |
205 | |
206 ctx.strokeStyle = "white"; | |
207 ctx.lineWidth = 1; | |
208 | |
209 ctx.stroke(); | |
210 | |
211 ctx.shadowOffsetX = -3; | |
212 | |
213 ctx.stroke(); | |
214 | |
215 ctx.restore(); | |
216 | |
217 ctx.save(); | |
218 | |
219 ctx.globalCompositeOperation = "destination-out"; | |
220 ctx.strokeStyle = "rgba(0, 0, 0, 1)"; | |
221 ctx.lineWidth = 1; | |
222 | |
223 ctx.stroke(); | |
224 | |
225 ctx.restore(); | |
226 } | |
227 | |
228 function drawPill() | |
229 { | |
230 // Make a rounded rect path. | |
231 ctx.beginPath(); | |
232 ctx.moveTo(x, y + r); | |
233 ctx.lineTo(x, y + h - r); | |
234 ctx.quadraticCurveTo(x, y + h, x + r, y + h); | |
235 ctx.lineTo(x + w - r, y + h); | |
236 ctx.quadraticCurveTo(x + w, y + h, x + w, y + h - r); | |
237 ctx.lineTo(x + w, y + r); | |
238 ctx.quadraticCurveTo(x + w, y, x + w - r, y); | |
239 ctx.lineTo(x + r, y); | |
240 ctx.quadraticCurveTo(x, y, x, y + r); | |
241 ctx.closePath(); | |
242 | |
243 // Clip to the rounded rect path. | |
244 ctx.save(); | |
245 ctx.clip(); | |
246 | |
247 // Fill the segments with the associated color. | |
248 var previousSegmentsWidth = 0; | |
249 for (var i = 0; i < segments.length; ++i) { | |
250 var segmentWidth = Math.round(w * percents[i] / 100); | |
251 ctx.fillStyle = segments[i].color; | |
252 ctx.fillRect(x + previousSegmentsWidth, y, segmentWidth, h); | |
253 previousSegmentsWidth += segmentWidth; | |
254 } | |
255 | |
256 // Draw the segment divider lines. | |
257 ctx.lineWidth = 1; | |
258 for (var i = 1; i < 20; ++i) { | |
259 ctx.beginPath(); | |
260 ctx.moveTo(x + (i * Math.round(w / 20)) + 0.5, y); | |
261 ctx.lineTo(x + (i * Math.round(w / 20)) + 0.5, y + h); | |
262 ctx.closePath(); | |
263 | |
264 ctx.strokeStyle = "rgba(0, 0, 0, 0.2)"; | |
265 ctx.stroke(); | |
266 | |
267 ctx.beginPath(); | |
268 ctx.moveTo(x + (i * Math.round(w / 20)) + 1.5, y); | |
269 ctx.lineTo(x + (i * Math.round(w / 20)) + 1.5, y + h); | |
270 ctx.closePath(); | |
271 | |
272 ctx.strokeStyle = "rgba(255, 255, 255, 0.2)"; | |
273 ctx.stroke(); | |
274 } | |
275 | |
276 // Draw the pill shading. | |
277 var lightGradient = ctx.createLinearGradient(x, y, x, y + (h / 1.5))
; | |
278 lightGradient.addColorStop(0.0, "rgba(220, 220, 220, 0.6)"); | |
279 lightGradient.addColorStop(0.4, "rgba(220, 220, 220, 0.2)"); | |
280 lightGradient.addColorStop(1.0, "rgba(255, 255, 255, 0.0)"); | |
281 | |
282 var darkGradient = ctx.createLinearGradient(x, y + (h / 3), x, y + h
); | |
283 darkGradient.addColorStop(0.0, "rgba(0, 0, 0, 0.0)"); | |
284 darkGradient.addColorStop(0.8, "rgba(0, 0, 0, 0.2)"); | |
285 darkGradient.addColorStop(1.0, "rgba(0, 0, 0, 0.5)"); | |
286 | |
287 ctx.fillStyle = darkGradient; | |
288 ctx.fillRect(x, y, w, h); | |
289 | |
290 ctx.fillStyle = lightGradient; | |
291 ctx.fillRect(x, y, w, h); | |
292 | |
293 ctx.restore(); | |
294 } | |
295 | |
296 ctx.clearRect(x, y, w, (h * 2)); | |
297 | |
298 drawPillShadow(); | |
299 drawPill(); | |
300 | |
301 ctx.save(); | |
302 | |
303 ctx.translate(0, (h * 2) + 1); | |
304 ctx.scale(1, -1); | |
305 | |
306 drawPill(); | |
307 | |
308 ctx.restore(); | |
309 | |
310 this._fadeOutRect(ctx, x, y + h + 1, w, h, 0.5, 0.0); | |
311 }, | |
312 | |
313 _fadeOutRect: function(ctx, x, y, w, h, a1, a2) | |
314 { | |
315 ctx.save(); | |
316 | |
317 var gradient = ctx.createLinearGradient(x, y, x, y + h); | |
318 gradient.addColorStop(0.0, "rgba(0, 0, 0, " + (1.0 - a1) + ")"); | |
319 gradient.addColorStop(0.8, "rgba(0, 0, 0, " + (1.0 - a2) + ")"); | |
320 gradient.addColorStop(1.0, "rgba(0, 0, 0, 1.0)"); | |
321 | |
322 ctx.globalCompositeOperation = "destination-out"; | |
323 | |
324 ctx.fillStyle = gradient; | |
325 ctx.fillRect(x, y, w, h); | |
326 | |
327 ctx.restore(); | |
328 }, | |
329 | |
330 _makeLegendElement: function(label, value, color) | |
331 { | |
332 var legendElement = document.createElement("label"); | |
333 legendElement.className = "summary-graph-legend-item"; | |
334 | |
335 if (color) { | |
336 var swatch = document.createElement("canvas"); | |
337 swatch.className = "summary-graph-legend-swatch"; | |
338 swatch.setAttribute("width", "13"); | |
339 swatch.setAttribute("height", "24"); | |
340 | |
341 legendElement.appendChild(swatch); | |
342 | |
343 this._drawSwatch(swatch, color); | |
344 } | |
345 | |
346 var labelElement = document.createElement("div"); | |
347 labelElement.className = "summary-graph-legend-label"; | |
348 legendElement.appendChild(labelElement); | |
349 | |
350 var headerElement = document.createElement("div"); | |
351 headerElement.className = "summary-graph-legend-header"; | |
352 headerElement.textContent = label; | |
353 labelElement.appendChild(headerElement); | |
354 | |
355 var valueElement = document.createElement("div"); | |
356 valueElement.className = "summary-graph-legend-value"; | |
357 valueElement.textContent = value; | |
358 labelElement.appendChild(valueElement); | |
359 | |
360 return legendElement; | |
361 } | |
362 } | |
363 | |
364 WebInspector.SummaryBar.prototype.__proto__ = WebInspector.Object.prototype; | |
OLD | NEW |