blob: a565617bc504a84fa26f1ab01217285c0e055db1 [file] [log] [blame]
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
var App = require('app');
App.ChartView = Em.View.extend({
dateFormat:'dd/mm/yy',
timeFormat:'h:m',
w:900,
p:30, // axis padding
shift:30,
ticksCount:10,
pointsLimit:300,
areaHeight:30, // px
axis:false,
x:false,
y:false,
init:function () {
this._super();
var renderer = this;
this.x = d3.time.scale().domain([renderer.getMinDate({}), renderer.getMaxDate({})]).range([0, this.get('w')]);
this.y = d3.scale.linear().domain([0, 50]).range([this.get('h'), 0]);
this.axis = d3.svg.axis().orient("top").scale(this.x).ticks(this.get('ticksCount'));
},
h: function(){
return this.get('p') + this.get('nodeAttributes').length * this.get('areaHeight'); //default: 160
}.property('nodeAttributes', 'p'),
activeH: function(){
return this.get('nodeAttributes').length * this.get('areaHeight'); // default 160;
}.property('h'),
ruleHeight: function(){
return this.get('nodeAttributes').length * this.get('areaHeight');
}.property('nodeAttributes'),
updateY: function(){
this.y = d3.scale.linear().domain([0, 50]).range([this.get('h'), 0]);
}.observes('h'),
getMinDate:function (data) {
if (data.length)
return new Date(Date.parse(data[0]['date']));
return new Date();
},
getMaxDate:function (data) {
if (data.length)
return new Date(Date.parse(data[data.length - 1]['date']));
return new Date();
},
area:function () {
var renderer = this;
var area = d3.svg.area().x(function (d) {
return renderer.x(renderer.getDate(d));
});
area.y1(function (d) {
return renderer.get('h') - (renderer.get('h') - renderer.y(d[$(this).attr("getter")])) / renderer.get('koef');
});
area.y0(function (d) {
return renderer.get('h');
});
return area;
},
line:function () {
var renderer = this;
var area = d3.svg.line().x(function (d) {
return renderer.x(renderer.getDate(d));
})
.interpolate("basis");
area.y(function (d) {
return renderer.get('h');
});
return area;
},
/**
* @todo: calculate this
* coefficient of compression
* @param shift
* @return {Number}
*/
koef:function () {
// max value divide on area height;
return 2 * (this.get('nodeAttributes').length + 1);
}.property('h'),
getDate:function (d) {
return new Date(Date.parse(d.date));
},
dateTimeToDateObject:function (string) {
var ren = this;
return new Date($.datepicker.parseDateTime(ren.dateFormat, ren.timeFormat, string));
},
getDefaultShift:function () {
return -1 * this.get('areaHeight') * (this.get('nodeAttributes').length - 1);
},
percentScaleXDefaultTranslate:function () {
return this.w + 3
},
clearPlot: function(){
d3.select(this.get('chartContainerSelector')).selectAll("*").remove();
},
drawPlot:function () {
this.clearPlot();
var renderer = this;
this.x.domain([renderer.getMinDate({}), renderer.getMaxDate({})]);
var rule = $('<div></div>').addClass("rule").css('height', renderer.get('ruleHeight')).mouseenter(function () { $(this).hide(); });
$(this.get('chartContainerSelector')).prepend(rule);
var vis = d3.select(this.get('chartContainerSelector'))
.append("svg:svg")
.attr("width", renderer.get('w') + 5)
.attr("height", renderer.get('h'))
.attr("rendererId", this.get('elementId'))
.on("mousemove", function () {
var area = d3.select(this).select("path.line");
var d = area.data()[0];
var x = d3.mouse(this)[0];
var renderer = Em.View.views[d3.select(this).attr('rendererId')];
var container = $(this).parent();
var scale = renderer.x;
// first move rule
var rule = $(container).children("div.rule");
rule.css("left", (168 + x) + "px"); // 168 - left container margin
rule.show();
x = x + 5; // some correction
var selectedDate = scale.invert(x);
// search date between this coordinates
var prevVal = false;
var nextVal = d[0];
$.each(d, function (i, point) {
if (renderer.getDate(point).getTime() <= selectedDate.getTime()) {
prevVal = nextVal;
nextVal = point;
} else {
return;
}
});
var len1 = Math.abs(x - scale(renderer.getDate(prevVal)));
var len2 = Math.abs(x - scale(renderer.getDate(nextVal)));
var clearing = 5;
var pointToShow = false;
// if the first point if closer
if ((len1 < len2) && (len1 <= clearing)) {
pointToShow = prevVal;
} else if (len2 <= clearing) { // the second point is closer
pointToShow = nextVal;
}
$.each(renderer.get('nodeAttributes'), function (i, v) {
var value = !pointToShow ? "" : pointToShow[v] + "%";
$(rule).children("div." + v).html(value);
});
});
vis.append("svg:g")
.attr("class", "axis")
.attr("transform", "translate(0," + this.get('p') + ")")
.call(renderer.axis)
$.each(this.get('nodeAttributes'), function (i, v) {
var element = $('<div></div>').addClass(v).addClass("stateValue").html("");
rule.append(element);
});
var shift = this.getDefaultShift();
vis.append("svg:path")
.attr("class", "horizontal-line")
.data([
{}
])
.attr("transform", "translate(0," + (shift - this.get('areaHeight')) + ")")
.attr("d", renderer.line())
.style("stroke", "#000");
$.each(this.get('nodeAttributes'), function (i, v) {
vis.append("svg:path").data([
{}
])
.attr("class", "line")
.attr("getter", v)
.attr("transform", "translate(0, " + shift + ")")
.attr("d", renderer.area())
.style("fill", function () {
return "#31a354";
});
vis.append("svg:path")
.attr("class", "horizontal-line")
.data([
{}
])
.attr("transform", "translate(0," + shift + ")")
.attr("d", renderer.line())
.style("stroke", "#000");
shift += renderer.get('areaHeight');
});
},
getData:function (containerId) {
return (d3.select(containerId + " path.line").data())[0];
},
drawChart:function () {
var containerSel = this.get('chartContainerSelector');
var data = this.get('data');
while (data.length > this.get('pointsLimit')) {
data.shift();
}
var renderer = this;
var minDate = this.getMinDate(data);
var maxDate = this.getMaxDate(data);
this.x.domain([minDate, maxDate]);
var ticks = data.length > 10 ? 10 : data.length;
this.axis.scale(renderer.x).ticks(ticks);
// remove dots axis
$(containerSel + " svg g.axis g").remove();
d3.select(containerSel + " svg g.axis")
.call(this.axis);
$.each(this.get('nodeAttributes'), function (i, v) {
d3.select(containerSel + " path.line[getter='" + v + "']")
.data([data])
.transition()
.attr("d", renderer.area());
});
// lines between charts
$(containerSel + " path.horizontal-line").each(
function (i, path) {
d3.select(path).data([
[
{date:minDate},
{date:maxDate}
]
]).attr("d", renderer.line());
}
);
}
});