| /* |
| * Copyright (C) 2007, 2008 Apple Inc. All rights reserved. |
| * Copyright (C) 2008, 2009 Anthony Ricaud <rik@webkit.org> |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions |
| * are met: |
| * |
| * 1. Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * 2. Redistributions in binary form must reproduce the above copyright |
| * notice, this list of conditions and the following disclaimer in the |
| * documentation and/or other materials provided with the distribution. |
| * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of |
| * its contributors may be used to endorse or promote products derived |
| * from this software without specific prior written permission. |
| * |
| * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY |
| * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED |
| * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE |
| * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY |
| * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES |
| * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; |
| * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND |
| * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF |
| * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| */ |
| |
| WebInspector.SummaryBar = function(categories) |
| { |
| this.categories = categories; |
| |
| this.element = document.createElement("div"); |
| this.element.className = "summary-bar"; |
| |
| this.graphElement = document.createElement("canvas"); |
| this.graphElement.setAttribute("width", "450"); |
| this.graphElement.setAttribute("height", "38"); |
| this.graphElement.className = "summary-graph"; |
| this.element.appendChild(this.graphElement); |
| |
| this.legendElement = document.createElement("div"); |
| this.legendElement.className = "summary-graph-legend"; |
| this.element.appendChild(this.legendElement); |
| } |
| |
| WebInspector.SummaryBar.prototype = { |
| |
| get calculator() { |
| return this._calculator; |
| }, |
| |
| set calculator(x) { |
| this._calculator = x; |
| }, |
| |
| reset: function() |
| { |
| this.legendElement.removeChildren(); |
| this._drawSummaryGraph(); |
| }, |
| |
| update: function(data) |
| { |
| var graphInfo = this.calculator.computeSummaryValues(data); |
| |
| var fillSegments = []; |
| |
| this.legendElement.removeChildren(); |
| |
| for (var category in this.categories) { |
| var size = graphInfo.categoryValues[category]; |
| if (!size) |
| continue; |
| |
| var colorString = this.categories[category].color; |
| |
| var fillSegment = {color: colorString, value: size}; |
| fillSegments.push(fillSegment); |
| |
| var legendLabel = this._makeLegendElement(this.categories[category].title, this.calculator.formatValue(size), colorString); |
| this.legendElement.appendChild(legendLabel); |
| } |
| |
| if (graphInfo.total) { |
| var totalLegendLabel = this._makeLegendElement(WebInspector.UIString("Total"), this.calculator.formatValue(graphInfo.total)); |
| totalLegendLabel.addStyleClass("total"); |
| this.legendElement.appendChild(totalLegendLabel); |
| } |
| |
| this._drawSummaryGraph(fillSegments); |
| }, |
| |
| _drawSwatch: function(canvas, color) |
| { |
| var ctx = canvas.getContext("2d"); |
| |
| function drawSwatchSquare() { |
| ctx.fillStyle = color; |
| ctx.fillRect(0, 0, 13, 13); |
| |
| var gradient = ctx.createLinearGradient(0, 0, 13, 13); |
| gradient.addColorStop(0.0, "rgba(255, 255, 255, 0.2)"); |
| gradient.addColorStop(1.0, "rgba(255, 255, 255, 0.0)"); |
| |
| ctx.fillStyle = gradient; |
| ctx.fillRect(0, 0, 13, 13); |
| |
| gradient = ctx.createLinearGradient(13, 13, 0, 0); |
| gradient.addColorStop(0.0, "rgba(0, 0, 0, 0.2)"); |
| gradient.addColorStop(1.0, "rgba(0, 0, 0, 0.0)"); |
| |
| ctx.fillStyle = gradient; |
| ctx.fillRect(0, 0, 13, 13); |
| |
| ctx.strokeStyle = "rgba(0, 0, 0, 0.6)"; |
| ctx.strokeRect(0.5, 0.5, 12, 12); |
| } |
| |
| ctx.clearRect(0, 0, 13, 24); |
| |
| drawSwatchSquare(); |
| |
| ctx.save(); |
| |
| ctx.translate(0, 25); |
| ctx.scale(1, -1); |
| |
| drawSwatchSquare(); |
| |
| ctx.restore(); |
| |
| this._fadeOutRect(ctx, 0, 13, 13, 13, 0.5, 0.0); |
| }, |
| |
| _drawSummaryGraph: function(segments) |
| { |
| if (!segments || !segments.length) { |
| segments = [{color: "white", value: 1}]; |
| this._showingEmptySummaryGraph = true; |
| } else |
| delete this._showingEmptySummaryGraph; |
| |
| // Calculate the total of all segments. |
| var total = 0; |
| for (var i = 0; i < segments.length; ++i) |
| total += segments[i].value; |
| |
| // Calculate the percentage of each segment, rounded to the nearest percent. |
| var percents = segments.map(function(s) { return Math.max(Math.round(100 * s.value / total), 1) }); |
| |
| // Calculate the total percentage. |
| var percentTotal = 0; |
| for (var i = 0; i < percents.length; ++i) |
| percentTotal += percents[i]; |
| |
| // Make sure our percentage total is not greater-than 100, it can be greater |
| // if we rounded up for a few segments. |
| while (percentTotal > 100) { |
| for (var i = 0; i < percents.length && percentTotal > 100; ++i) { |
| if (percents[i] > 1) { |
| --percents[i]; |
| --percentTotal; |
| } |
| } |
| } |
| |
| // Make sure our percentage total is not less-than 100, it can be less |
| // if we rounded down for a few segments. |
| while (percentTotal < 100) { |
| for (var i = 0; i < percents.length && percentTotal < 100; ++i) { |
| ++percents[i]; |
| ++percentTotal; |
| } |
| } |
| |
| var ctx = this.graphElement.getContext("2d"); |
| |
| var x = 0; |
| var y = 0; |
| var w = 450; |
| var h = 19; |
| var r = (h / 2); |
| |
| function drawPillShadow() |
| { |
| // This draws a line with a shadow that is offset away from the line. The line is stroked |
| // twice with different X shadow offsets to give more feathered edges. Later we erase the |
| // line with destination-out 100% transparent black, leaving only the shadow. This only |
| // works if nothing has been drawn into the canvas yet. |
| |
| ctx.beginPath(); |
| ctx.moveTo(x + 4, y + h - 3 - 0.5); |
| ctx.lineTo(x + w - 4, y + h - 3 - 0.5); |
| ctx.closePath(); |
| |
| ctx.save(); |
| |
| ctx.shadowBlur = 2; |
| ctx.shadowColor = "rgba(0, 0, 0, 0.5)"; |
| ctx.shadowOffsetX = 3; |
| ctx.shadowOffsetY = 5; |
| |
| ctx.strokeStyle = "white"; |
| ctx.lineWidth = 1; |
| |
| ctx.stroke(); |
| |
| ctx.shadowOffsetX = -3; |
| |
| ctx.stroke(); |
| |
| ctx.restore(); |
| |
| ctx.save(); |
| |
| ctx.globalCompositeOperation = "destination-out"; |
| ctx.strokeStyle = "rgba(0, 0, 0, 1)"; |
| ctx.lineWidth = 1; |
| |
| ctx.stroke(); |
| |
| ctx.restore(); |
| } |
| |
| function drawPill() |
| { |
| // Make a rounded rect path. |
| ctx.beginPath(); |
| ctx.moveTo(x, y + r); |
| ctx.lineTo(x, y + h - r); |
| ctx.arc(x + r, y + h - r, r, Math.PI, Math.PI / 2, true); |
| ctx.lineTo(x + w - r, y + h); |
| ctx.arc(x + w - r, y + h - r, r, Math.PI / 2, 0, true); |
| ctx.lineTo(x + w, y + r); |
| ctx.arc(x + w - r, y + r, r, 0, 3 * Math.PI / 2, true); |
| ctx.lineTo(x + r, y); |
| ctx.arc(x + r, y + r, r, Math.PI / 2, Math.PI, true); |
| ctx.closePath(); |
| |
| // Clip to the rounded rect path. |
| ctx.save(); |
| ctx.clip(); |
| |
| // Fill the segments with the associated color. |
| var previousSegmentsWidth = 0; |
| for (var i = 0; i < segments.length; ++i) { |
| var segmentWidth = Math.round(w * percents[i] / 100); |
| ctx.fillStyle = segments[i].color; |
| ctx.fillRect(x + previousSegmentsWidth, y, segmentWidth, h); |
| previousSegmentsWidth += segmentWidth; |
| } |
| |
| // Draw the segment divider lines. |
| ctx.lineWidth = 1; |
| for (var i = 1; i < 20; ++i) { |
| ctx.beginPath(); |
| ctx.moveTo(x + (i * Math.round(w / 20)) + 0.5, y); |
| ctx.lineTo(x + (i * Math.round(w / 20)) + 0.5, y + h); |
| ctx.closePath(); |
| |
| ctx.strokeStyle = "rgba(0, 0, 0, 0.2)"; |
| ctx.stroke(); |
| |
| ctx.beginPath(); |
| ctx.moveTo(x + (i * Math.round(w / 20)) + 1.5, y); |
| ctx.lineTo(x + (i * Math.round(w / 20)) + 1.5, y + h); |
| ctx.closePath(); |
| |
| ctx.strokeStyle = "rgba(255, 255, 255, 0.2)"; |
| ctx.stroke(); |
| } |
| |
| // Draw the pill shading. |
| var lightGradient = ctx.createLinearGradient(x, y, x, y + (h / 1.5)); |
| lightGradient.addColorStop(0.0, "rgba(220, 220, 220, 0.6)"); |
| lightGradient.addColorStop(0.4, "rgba(220, 220, 220, 0.2)"); |
| lightGradient.addColorStop(1.0, "rgba(255, 255, 255, 0.0)"); |
| |
| var darkGradient = ctx.createLinearGradient(x, y + (h / 3), x, y + h); |
| darkGradient.addColorStop(0.0, "rgba(0, 0, 0, 0.0)"); |
| darkGradient.addColorStop(0.8, "rgba(0, 0, 0, 0.2)"); |
| darkGradient.addColorStop(1.0, "rgba(0, 0, 0, 0.5)"); |
| |
| ctx.fillStyle = darkGradient; |
| ctx.fillRect(x, y, w, h); |
| |
| ctx.fillStyle = lightGradient; |
| ctx.fillRect(x, y, w, h); |
| |
| ctx.restore(); |
| } |
| |
| ctx.clearRect(x, y, w, (h * 2)); |
| |
| drawPillShadow(); |
| drawPill(); |
| |
| ctx.save(); |
| |
| ctx.translate(0, (h * 2) + 1); |
| ctx.scale(1, -1); |
| |
| drawPill(); |
| |
| ctx.restore(); |
| |
| this._fadeOutRect(ctx, x, y + h + 1, w, h, 0.5, 0.0); |
| }, |
| |
| _fadeOutRect: function(ctx, x, y, w, h, a1, a2) |
| { |
| ctx.save(); |
| |
| var gradient = ctx.createLinearGradient(x, y, x, y + h); |
| gradient.addColorStop(0.0, "rgba(0, 0, 0, " + (1.0 - a1) + ")"); |
| gradient.addColorStop(0.8, "rgba(0, 0, 0, " + (1.0 - a2) + ")"); |
| gradient.addColorStop(1.0, "rgba(0, 0, 0, 1.0)"); |
| |
| ctx.globalCompositeOperation = "destination-out"; |
| |
| ctx.fillStyle = gradient; |
| ctx.fillRect(x, y, w, h); |
| |
| ctx.restore(); |
| }, |
| |
| _makeLegendElement: function(label, value, color) |
| { |
| var legendElement = document.createElement("label"); |
| legendElement.className = "summary-graph-legend-item"; |
| |
| if (color) { |
| var swatch = document.createElement("canvas"); |
| swatch.className = "summary-graph-legend-swatch"; |
| swatch.setAttribute("width", "13"); |
| swatch.setAttribute("height", "24"); |
| |
| legendElement.appendChild(swatch); |
| |
| this._drawSwatch(swatch, color); |
| } |
| |
| var labelElement = document.createElement("div"); |
| labelElement.className = "summary-graph-legend-label"; |
| legendElement.appendChild(labelElement); |
| |
| var headerElement = document.createElement("div"); |
| headerElement.className = "summary-graph-legend-header"; |
| headerElement.textContent = label; |
| labelElement.appendChild(headerElement); |
| |
| var valueElement = document.createElement("div"); |
| valueElement.className = "summary-graph-legend-value"; |
| valueElement.textContent = value; |
| labelElement.appendChild(valueElement); |
| |
| return legendElement; |
| } |
| } |
| |
| WebInspector.SummaryBar.prototype.__proto__ = WebInspector.Object.prototype; |