extensions.add(SizeWidget.class);
extensions.add(EventsWidget.class);
extensions.add(CustomMeasuresWidget.class);
+ extensions.add(TimelineWidget.class);
// chart
extensions.add(XradarChart.class);
--- /dev/null
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2008-2011 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * Sonar is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * Sonar is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with Sonar; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02
+ */
+package org.sonar.plugins.core.widgets;
+
+import org.sonar.api.web.*;
+
+@WidgetProperties(
+ {
+ @WidgetProperty(key = "metric1", type = WidgetPropertyType.METRIC),
+ @WidgetProperty(key = "metric2", type = WidgetPropertyType.METRIC),
+ @WidgetProperty(key = "metric3", type = WidgetPropertyType.METRIC),
+ @WidgetProperty(key = "displayEvents", type = WidgetPropertyType.BOOLEAN)
+ }
+)
+public class TimelineWidget extends AbstractRubyTemplate implements RubyRailsWidget {
+ public String getId() {
+ return "timeline";
+ }
+
+ public String getTitle() {
+ return "Timeline";
+ }
+
+ @Override
+ protected String getTemplatePath() {
+ return "/org/sonar/plugins/core/widgets/timeline.html.erb";
+ //return "/Users/fbellingard/Documents/Sonar/repos/sonar/plugins/sonar-core-plugin/src/main/resources/org/sonar/plugins/core/widgets/timeline.html.erb";
+ }
+}
\ No newline at end of file
--- /dev/null
+<%= javascript_include_tag 'protovis-sonar' %>
+
+<div id="timeline-chart"></div>
+
+<script type="text/javascript+protovis">
+
+<%
+ metric_data_map = {}
+ (1..3).each do |index|
+ metric=widget_properties["metric#{index}"]
+ if metric
+ metric_data_map[metric.id] = []
+ end
+ end
+
+ trends = TrendsChart.time_machine_measures(@resource, metric_data_map.keys, {}).sort { |t1, t2| t1["created_at"] <=> t2["created_at"] }
+ trends.each() do |trend_item|
+ metric_data_map[trend_item["metric_id"].to_i] << {:date => trend_item["created_at"], :value => trend_item["value"]}
+ end
+
+ js_data = "["
+ metric_data_map.keys.each() do |metric_id|
+ js_data += "["
+ metric_data_map[metric_id].each() do |metric_data|
+ m_date = Time.parse(metric_data[:date])
+ js_data += "{\"x\":new Date("
+ js_data += m_date.strftime("%Y,%m,%d")
+ js_data += "),\"y\":\""
+ js_data += metric_data[:value]
+ js_data += "\"},"
+ end
+ js_data += "],"
+ end
+ js_data += "]"
+
+%>
+ var data = <%= js_data -%>;
+
+ displayTrendChart('timeline-chart', data);
+
+</script>
\ No newline at end of file
widget.size.accessors.suffix=\ accessors
widget.size.paragraphs.suffix=\ paragraphs
+widget.timeline.name=Timeline
+widget.timeline.description=Displays up to 3 metrics on a history chart.
+
widget.ckjm.name=Chidamber & Kemerer
widget.ckjm.description=Reports on LCOM4 and RFC average and distribution.
widget.ckjm.lcom4=LCOM4
--- /dev/null
+function displayTrendChart(divId, data) {
+
+ /* Sizing and scales. */
+ var w = 400 - 40,
+ h = 300 - 25,
+ S=2;
+
+
+ var x = pv.Scale.linear(pv.blend(pv.map(data, function(d) d)), function(d) d.x).range(0, w);
+ var y = new Array(data.length);
+ for(var i = 0; i < data.length; i++){
+ y[i]=pv.Scale.linear(data[i], function(d) d.y).range(0, h)
+ }
+ var interpolate = "linear"; /* cardinal or linear */
+ var idx = -1;
+
+ /* The root panel. */
+ var vis = new pv.Panel()
+ .canvas(document.getElementById(divId))
+ .width(w)
+ .height(h)
+ .left(30)
+ .right(10)
+ .bottom(20)
+ .top(5)
+ .strokeStyle("#CCC");
+
+ /* X-axis */
+ vis.add(pv.Rule)
+ .data(x.ticks())
+ .left(x)
+ .bottom(-5)
+ .height(5)
+ .anchor("bottom")
+ .add(pv.Label)
+ .text(x.tickFormat);
+
+ /* Y-axis and ticks. */
+ var show_y_axis = (data.length==1)
+ if (show_y_axis) {
+ vis.add(pv.Rule)
+ .data(y[0].ticks(5))
+ .bottom(y[0])
+ .strokeStyle(function(d) d ? "#eee" : "#000")
+ .anchor("left")
+ .add(pv.Label)
+ .text(y[0].tickFormat);
+ }
+
+ /* A panel for each data series. */
+ var panel = vis.add(pv.Panel)
+ .data(data);
+
+ /* The line. */
+ var line = panel.add(pv.Line)
+ .data(function(array) array)
+ .left(function(d) x(d.x))
+ .bottom(function(d) y[this.parent.index](d.y))
+ .interpolate(function() interpolate)
+ .lineWidth(2);
+
+ /* The mouseover dots and label. */
+ line.add(pv.Dot)
+ .visible(function() idx >= 0)
+ .data(function(d) [d[idx]])
+ .fillStyle(function() line.strokeStyle())
+ .strokeStyle("#000")
+ .size(20)
+ .lineWidth(1)
+ .add(pv.Dot)
+ .left(10)
+ .bottom(function() this.parent.index * 12 + 10)
+ .anchor("right").add(pv.Label)
+ .text(function(d) d.y.toFixed(2));
+
+
+ /* An invisible bar to capture events (without flickering). */
+ vis.add(pv.Bar)
+ .fillStyle("rgba(0,0,0,.001)")
+ .event("mouseout", function() {
+ i = -1;
+ return vis;
+ })
+ .event("mousemove", function() {
+ var mx = x.invert(vis.mouse().x);
+ idx = pv.search(data[0].map(function(d) d.x), mx);
+ idx = idx < 0 ? (-idx - 2) : idx;
+ return vis;
+ });
+ vis.render();
+
+}
\ No newline at end of file