js_snapshots += "]"
js_metrics += "]"
+ # Prepare also event structure if required
+ if widget_properties["displayEvents"]
+ events = {}
+ Event.find(:all, :conditions => {:resource_id => @resource.id}, :order => 'event_date').each() do |event|
+ if events[event.event_date]
+ events[event.event_date] << event
+ else
+ date_entry = [event]
+ events[event.event_date] = date_entry
+ end
+ end
+ js_events = "["
+ events.keys().sort.each() do |e_date|
+ e_details = events[e_date]
+ js_events += "{\"d\":d("
+ js_events += e_date.year.to_s
+ js_events += ","
+ # Need to decrease by 1 the month as the JS Date object start months at 0 (= January)
+ js_events += (e_date.month - 1).to_s
+ js_events += ","
+ js_events += e_date.day.to_s
+ js_events += "),\"l\":["
+ e_details.each() do |e|
+ js_events += "{\"n\":\""
+ js_events += e.name
+ js_events += "\",\"ld\":\""
+ js_events += human_short_date(e_date)
+ js_events += "\"},"
+ end
+ js_events += "]},"
+ end
+ js_events += "]"
+ end
+
# And prepare translations for labels
js_translations = "{"
js_translations += "\"date\":\"" + message("date") + "\""
js_translations += "}"
+ # Check if the widget height was specified
+ widgetHeight = widget_properties["widgetHeight"].to_i == 0 ? "null" : widget_properties["widgetHeight"]
%>
<% if widget_properties["chartTitle"] %>
var snapshots = <%= js_snapshots -%>;
var metrics = <%= js_metrics -%>;
var translations = <%= js_translations -%>;
+ var events = <%= js_events ? js_events : "null" -%>;
var timeline = new SonarWidgets.Timeline('timeline-chart-<%= widget.id -%>')
- .height(<%= widget_properties["widgetHeight"] ? widget_properties["widgetHeight"] : "null" -%>)
+ .height(<%= widgetHeight -%>)
.data(data)
.snapshots(snapshots)
.metrics(metrics)
- .translations(translations);
+ .translations(translations)
+ .events(events);
timeline.render();
</script>
this.wSnapshots;
this.wMetrics;
this.wTranslations;
+ this.wEvents;
this.height = function(height) {
this.wHeight = height;
return this;
this.wTranslations = translations;
return this;
}
+ this.events = function(events) {
+ this.wEvents = events;
+ return this;
+ }
}
SonarWidgets.Timeline.prototype.render = function() {
var metrics = this.wMetrics;
var snapshots = this.wSnapshots;
var translations = this.wTranslations;
+ var events = this.wEvents;
var widgetDiv = document.getElementById(this.wDivId);
+ var footerFont = "12px Arial,Helvetica,sans-serif";
+
/* Sizing and scales. */
- var footerHeight = 30 + this.wMetrics.size() * 12;
+ var footerHeight = 30 + (events ? 3 : this.wMetrics.size()) * 12;
var w = widgetDiv.parentNode.clientWidth - 60,
h = (this.wHeight == null ? 250 : this.wHeight) + footerHeight - 5,
S=2;
var x = pv.Scale.linear(pv.blend(pv.map(data, function(d) {return d;})), function(d) {return 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) {return d.y;}).range(10, h-10)
+ y[i]=pv.Scale.linear(data[i], function(d) {return d.y;}).range(20, h-10)
}
var interpolate = "linear"; /* cardinal or linear */
var idx = this.wData[0].size() - 1;
/* The mouseover dots and label in footer. */
line.add(pv.Dot)
- .visible(function() {return idx >= 0;})
.data(function(d) {return [d[idx]];})
.fillStyle(function() {return line.strokeStyle();})
.strokeStyle("#000")
.size(20)
.lineWidth(1)
.add(pv.Dot)
- .left(150)
+ .left(0)
.bottom(function() {return 0 - 30 - this.parent.index * 12;})
.anchor("right").add(pv.Label)
- .font("12px Arial,Helvetica,sans-serif")
+ .font(footerFont)
.text(function(d) {return metrics[this.parent.index] + ": " + d.y.toFixed(2);});
/* The date of the selected dot in footer. */
vis.add(pv.Label)
- .left(0)
+ .left(w/2)
.bottom(-36)
- .font("12px Arial,Helvetica,sans-serif")
- .text(function() {return translations.date + ": " + snapshots[idx].d});
+ .font(footerFont)
+ .text(function() {return translations.date + ": " + snapshots[idx].d;});
+
+ /* The event labels */
+ if (events) {
+ eventColor = "rgba(75,159,213,1)";
+ eventHoverColor = "rgba(202,227,242,1)";
+ vis.add(pv.Line)
+ .strokeStyle("rgba(0,0,0,.001)")
+ .data(events)
+ .left(function(e) {return x(e.d);})
+ .bottom(0)
+ .anchor("top")
+ .add(pv.Dot)
+ .bottom(6)
+ .shape("triangle")
+ .strokeStyle("grey")
+ .fillStyle(function(e) {return e.l[0].ld == snapshots[idx].d ? eventHoverColor : eventColor})
+ .add(pv.Dot)
+ .visible(function(e) { return e.l[0].ld == snapshots[idx].d;})
+ .left(w/2+8)
+ .bottom(-42)
+ .shape("triangle")
+ .fillStyle(function(e) {return e.l[0].ld == snapshots[idx].d ? eventHoverColor : eventColor})
+ .strokeStyle("grey")
+ .anchor("right")
+ .add(pv.Label)
+ .font(footerFont)
+ .text(function(e) {return e.l[0].n + ( e.l[1] ? " (... +" + (e.l.size()-1) + ")" : "");});
+ }
/* An invisible bar to capture events (without flickering). */
vis.add(pv.Bar)
.fillStyle("rgba(0,0,0,.001)")
+ .width(w+10)
.event("mouseout", function() {
i = -1;
return vis;
var mx = x.invert(vis.mouse().x);
idx = pv.search(data[0].map(function(d) {return d.x;}), mx);
idx = idx < 0 ? (-idx - 2) : idx;
+ idx = idx < 0 ? 0 : idx;
return vis;
});