[#287,view/graph][s]: move current view.graph.js to view.flotr2.js and setup recline.View.Graph to point to recline.View.Flot.
This commit is contained in:
@@ -13,7 +13,6 @@
|
|||||||
<!-- Recline CSS components -->
|
<!-- Recline CSS components -->
|
||||||
<link rel="stylesheet" href="{{page.root}}css/grid.css">
|
<link rel="stylesheet" href="{{page.root}}css/grid.css">
|
||||||
<link rel="stylesheet" href="{{page.root}}css/slickgrid.css">
|
<link rel="stylesheet" href="{{page.root}}css/slickgrid.css">
|
||||||
<link rel="stylesheet" href="{{page.root}}css/graph.css">
|
|
||||||
<link rel="stylesheet" href="{{page.root}}css/flot.css">
|
<link rel="stylesheet" href="{{page.root}}css/flot.css">
|
||||||
<link rel="stylesheet" href="{{page.root}}css/transform.css">
|
<link rel="stylesheet" href="{{page.root}}css/transform.css">
|
||||||
<link rel="stylesheet" href="{{page.root}}css/map.css">
|
<link rel="stylesheet" href="{{page.root}}css/map.css">
|
||||||
@@ -26,7 +25,6 @@
|
|||||||
<script type="text/javascript" src="{{page.root}}vendor/backbone/0.9.2/backbone.js"></script>
|
<script type="text/javascript" src="{{page.root}}vendor/backbone/0.9.2/backbone.js"></script>
|
||||||
<script type="text/javascript" src="{{page.root}}vendor/mustache/0.5.0-dev/mustache.js"></script>
|
<script type="text/javascript" src="{{page.root}}vendor/mustache/0.5.0-dev/mustache.js"></script>
|
||||||
<script type="text/javascript" src="{{page.root}}vendor/bootstrap/2.0.2/bootstrap.js"></script>
|
<script type="text/javascript" src="{{page.root}}vendor/bootstrap/2.0.2/bootstrap.js"></script>
|
||||||
<script type="text/javascript" src="{{page.root}}vendor/flotr2/flotr2.js"></script>
|
|
||||||
<!--[if lte IE 8]>
|
<!--[if lte IE 8]>
|
||||||
<script language="javascript" type="text/javascript" src="{{page.root}}vendor/flot/excanvas.min.js"></script>
|
<script language="javascript" type="text/javascript" src="{{page.root}}vendor/flot/excanvas.min.js"></script>
|
||||||
<![endif]-->
|
<![endif]-->
|
||||||
@@ -65,8 +63,8 @@
|
|||||||
<script type="text/javascript" src="{{page.root}}src/view.slickgrid.js"></script>
|
<script type="text/javascript" src="{{page.root}}src/view.slickgrid.js"></script>
|
||||||
<script type="text/javascript" src="{{page.root}}src/view.transform.js"></script>
|
<script type="text/javascript" src="{{page.root}}src/view.transform.js"></script>
|
||||||
<script type="text/javascript" src="{{page.root}}src/data.transform.js"></script>
|
<script type="text/javascript" src="{{page.root}}src/data.transform.js"></script>
|
||||||
<script type="text/javascript" src="{{page.root}}src/view.graph.js"></script>
|
|
||||||
<script type="text/javascript" src="{{page.root}}src/view.flot.js"></script>
|
<script type="text/javascript" src="{{page.root}}src/view.flot.js"></script>
|
||||||
|
<script type="text/javascript" src="{{page.root}}src/view.graph.js"></script>
|
||||||
<script type="text/javascript" src="{{page.root}}src/view.map.js"></script>
|
<script type="text/javascript" src="{{page.root}}src/view.map.js"></script>
|
||||||
<script type="text/javascript" src="{{page.root}}src/view.timeline.js"></script>
|
<script type="text/javascript" src="{{page.root}}src/view.timeline.js"></script>
|
||||||
<script type="text/javascript" src="{{page.root}}src/widget.pager.js"></script>
|
<script type="text/javascript" src="{{page.root}}src/widget.pager.js"></script>
|
||||||
|
|||||||
462
src/view.flotr2.js
Normal file
462
src/view.flotr2.js
Normal file
@@ -0,0 +1,462 @@
|
|||||||
|
/*jshint multistr:true */
|
||||||
|
|
||||||
|
this.recline = this.recline || {};
|
||||||
|
this.recline.View = this.recline.View || {};
|
||||||
|
|
||||||
|
(function($, my) {
|
||||||
|
|
||||||
|
// ## Graph view for a Dataset using Flotr2 graphing library.
|
||||||
|
//
|
||||||
|
// Initialization arguments (in a hash in first parameter):
|
||||||
|
//
|
||||||
|
// * model: recline.Model.Dataset
|
||||||
|
// * state: (optional) configuration hash of form:
|
||||||
|
//
|
||||||
|
// {
|
||||||
|
// group: {column name for x-axis},
|
||||||
|
// series: [{column name for series A}, {column name series B}, ... ],
|
||||||
|
// graphType: 'line',
|
||||||
|
// graphOptions: {custom [Flotr2 options](http://www.humblesoftware.com/flotr2/documentation#configuration)}
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// NB: should *not* provide an el argument to the view but must let the view
|
||||||
|
// generate the element itself (you can then append view.el to the DOM.
|
||||||
|
my.Flotr2 = Backbone.View.extend({
|
||||||
|
template: ' \
|
||||||
|
<div class="recline-graph"> \
|
||||||
|
<div class="panel graph" style="display: block;"> \
|
||||||
|
<div class="js-temp-notice alert alert-block"> \
|
||||||
|
<h3 class="alert-heading">Hey there!</h3> \
|
||||||
|
<p>There\'s no graph here yet because we don\'t know what fields you\'d like to see plotted.</p> \
|
||||||
|
<p>Please tell us by <strong>using the menu on the right</strong> and a graph will automatically appear.</p> \
|
||||||
|
</div> \
|
||||||
|
</div> \
|
||||||
|
</div> \
|
||||||
|
',
|
||||||
|
|
||||||
|
initialize: function(options) {
|
||||||
|
var self = this;
|
||||||
|
this.graphColors = ["#edc240", "#afd8f8", "#cb4b4b", "#4da74d", "#9440ed"];
|
||||||
|
|
||||||
|
this.el = $(this.el);
|
||||||
|
_.bindAll(this, 'render', 'redraw');
|
||||||
|
this.needToRedraw = false;
|
||||||
|
this.model.bind('change', this.render);
|
||||||
|
this.model.fields.bind('reset', this.render);
|
||||||
|
this.model.fields.bind('add', this.render);
|
||||||
|
this.model.records.bind('add', this.redraw);
|
||||||
|
this.model.records.bind('reset', this.redraw);
|
||||||
|
var stateData = _.extend({
|
||||||
|
group: null,
|
||||||
|
// so that at least one series chooser box shows up
|
||||||
|
series: [],
|
||||||
|
graphType: 'lines-and-points'
|
||||||
|
},
|
||||||
|
options.state
|
||||||
|
);
|
||||||
|
this.state = new recline.Model.ObjectState(stateData);
|
||||||
|
this.editor = new my.Flotr2Controls({
|
||||||
|
model: this.model,
|
||||||
|
state: this.state.toJSON()
|
||||||
|
});
|
||||||
|
this.editor.state.bind('change', function() {
|
||||||
|
self.state.set(self.editor.state.toJSON());
|
||||||
|
self.redraw();
|
||||||
|
});
|
||||||
|
this.elSidebar = this.editor.el;
|
||||||
|
},
|
||||||
|
|
||||||
|
render: function() {
|
||||||
|
var self = this;
|
||||||
|
var tmplData = this.model.toTemplateJSON();
|
||||||
|
var htmls = Mustache.render(this.template, tmplData);
|
||||||
|
$(this.el).html(htmls);
|
||||||
|
this.$graph = this.el.find('.panel.graph');
|
||||||
|
return this;
|
||||||
|
},
|
||||||
|
|
||||||
|
redraw: function() {
|
||||||
|
// There appear to be issues generating a Flotr2 graph if either:
|
||||||
|
|
||||||
|
// * The relevant div that graph attaches to his hidden at the moment of creating the plot -- Flotr2 will complain with
|
||||||
|
//
|
||||||
|
// Uncaught Invalid dimensions for plot, width = 0, height = 0
|
||||||
|
// * There is no data for the plot -- either same error or may have issues later with errors like 'non-existent node-value'
|
||||||
|
var areWeVisible = !jQuery.expr.filters.hidden(this.el[0]);
|
||||||
|
if ((!areWeVisible || this.model.records.length === 0)) {
|
||||||
|
this.needToRedraw = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// check we have something to plot
|
||||||
|
if (this.state.get('group') && this.state.get('series')) {
|
||||||
|
// faff around with width because flot draws axes *outside* of the element width which means graph can get push down as it hits element next to it
|
||||||
|
this.$graph.width(this.el.width() - 20);
|
||||||
|
var series = this.createSeries();
|
||||||
|
var options = this.getGraphOptions(this.state.attributes.graphType);
|
||||||
|
this.plot = Flotr.draw(this.$graph.get(0), series, options);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
show: function() {
|
||||||
|
// because we cannot redraw when hidden we may need to when becoming visible
|
||||||
|
if (this.needToRedraw) {
|
||||||
|
this.redraw();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
// ### getGraphOptions
|
||||||
|
//
|
||||||
|
// Get options for Flotr2 Graph
|
||||||
|
//
|
||||||
|
// needs to be function as can depend on state
|
||||||
|
//
|
||||||
|
// @param typeId graphType id (lines, lines-and-points etc)
|
||||||
|
getGraphOptions: function(typeId) {
|
||||||
|
var self = this;
|
||||||
|
|
||||||
|
var tickFormatter = function (x) {
|
||||||
|
return getFormattedX(x);
|
||||||
|
};
|
||||||
|
|
||||||
|
// infoboxes on mouse hover on points/bars etc
|
||||||
|
var trackFormatter = function (obj) {
|
||||||
|
var x = obj.x;
|
||||||
|
var y = obj.y;
|
||||||
|
// it's horizontal so we have to flip
|
||||||
|
if (self.state.attributes.graphType === 'bars') {
|
||||||
|
var _tmp = x;
|
||||||
|
x = y;
|
||||||
|
y = _tmp;
|
||||||
|
}
|
||||||
|
|
||||||
|
x = getFormattedX(x);
|
||||||
|
|
||||||
|
var content = _.template('<%= group %> = <%= x %>, <%= series %> = <%= y %>', {
|
||||||
|
group: self.state.attributes.group,
|
||||||
|
x: x,
|
||||||
|
series: obj.series.label,
|
||||||
|
y: y
|
||||||
|
});
|
||||||
|
|
||||||
|
return content;
|
||||||
|
};
|
||||||
|
|
||||||
|
var getFormattedX = function (x) {
|
||||||
|
var xfield = self.model.fields.get(self.state.attributes.group);
|
||||||
|
|
||||||
|
// time series
|
||||||
|
var xtype = xfield.get('type');
|
||||||
|
var isDateTime = (xtype === 'date' || xtype === 'date-time' || xtype === 'time');
|
||||||
|
|
||||||
|
if (self.model.records.models[parseInt(x)]) {
|
||||||
|
x = self.model.records.models[parseInt(x)].get(self.state.attributes.group);
|
||||||
|
if (isDateTime) {
|
||||||
|
x = new Date(x).toLocaleDateString();
|
||||||
|
}
|
||||||
|
} else if (isDateTime) {
|
||||||
|
x = new Date(parseInt(x)).toLocaleDateString();
|
||||||
|
}
|
||||||
|
return x;
|
||||||
|
}
|
||||||
|
|
||||||
|
var xaxis = {};
|
||||||
|
xaxis.tickFormatter = tickFormatter;
|
||||||
|
|
||||||
|
var yaxis = {};
|
||||||
|
yaxis.autoscale = true;
|
||||||
|
yaxis.autoscaleMargin = 0.02;
|
||||||
|
|
||||||
|
var mouse = {};
|
||||||
|
mouse.track = true;
|
||||||
|
mouse.relative = true;
|
||||||
|
mouse.trackFormatter = trackFormatter;
|
||||||
|
|
||||||
|
var legend = {};
|
||||||
|
legend.position = 'ne';
|
||||||
|
|
||||||
|
// mouse.lineColor is set in createSeries
|
||||||
|
var optionsPerGraphType = {
|
||||||
|
lines: {
|
||||||
|
legend: legend,
|
||||||
|
colors: this.graphColors,
|
||||||
|
lines: { show: true },
|
||||||
|
xaxis: xaxis,
|
||||||
|
yaxis: yaxis,
|
||||||
|
mouse: mouse
|
||||||
|
},
|
||||||
|
points: {
|
||||||
|
legend: legend,
|
||||||
|
colors: this.graphColors,
|
||||||
|
points: { show: true, hitRadius: 5 },
|
||||||
|
xaxis: xaxis,
|
||||||
|
yaxis: yaxis,
|
||||||
|
mouse: mouse,
|
||||||
|
grid: { hoverable: true, clickable: true }
|
||||||
|
},
|
||||||
|
'lines-and-points': {
|
||||||
|
legend: legend,
|
||||||
|
colors: this.graphColors,
|
||||||
|
points: { show: true, hitRadius: 5 },
|
||||||
|
lines: { show: true },
|
||||||
|
xaxis: xaxis,
|
||||||
|
yaxis: yaxis,
|
||||||
|
mouse: mouse,
|
||||||
|
grid: { hoverable: true, clickable: true }
|
||||||
|
},
|
||||||
|
bars: {
|
||||||
|
legend: legend,
|
||||||
|
colors: this.graphColors,
|
||||||
|
lines: { show: false },
|
||||||
|
xaxis: yaxis,
|
||||||
|
yaxis: xaxis,
|
||||||
|
mouse: {
|
||||||
|
track: true,
|
||||||
|
relative: true,
|
||||||
|
trackFormatter: trackFormatter,
|
||||||
|
fillColor: '#FFFFFF',
|
||||||
|
fillOpacity: 0.3,
|
||||||
|
position: 'e'
|
||||||
|
},
|
||||||
|
bars: {
|
||||||
|
show: true,
|
||||||
|
horizontal: true,
|
||||||
|
shadowSize: 0,
|
||||||
|
barWidth: 0.8
|
||||||
|
}
|
||||||
|
},
|
||||||
|
columns: {
|
||||||
|
legend: legend,
|
||||||
|
colors: this.graphColors,
|
||||||
|
lines: { show: false },
|
||||||
|
xaxis: xaxis,
|
||||||
|
yaxis: yaxis,
|
||||||
|
mouse: {
|
||||||
|
track: true,
|
||||||
|
relative: true,
|
||||||
|
trackFormatter: trackFormatter,
|
||||||
|
fillColor: '#FFFFFF',
|
||||||
|
fillOpacity: 0.3,
|
||||||
|
position: 'n'
|
||||||
|
},
|
||||||
|
bars: {
|
||||||
|
show: true,
|
||||||
|
horizontal: false,
|
||||||
|
shadowSize: 0,
|
||||||
|
barWidth: 0.8
|
||||||
|
}
|
||||||
|
},
|
||||||
|
grid: { hoverable: true, clickable: true }
|
||||||
|
};
|
||||||
|
|
||||||
|
if (self.state.get('graphOptions')){
|
||||||
|
return _.extend(optionsPerGraphType[typeId],
|
||||||
|
self.state.get('graphOptions')
|
||||||
|
)
|
||||||
|
}else{
|
||||||
|
return optionsPerGraphType[typeId];
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
createSeries: function() {
|
||||||
|
var self = this;
|
||||||
|
var series = [];
|
||||||
|
_.each(this.state.attributes.series, function(field) {
|
||||||
|
var points = [];
|
||||||
|
_.each(self.model.records.models, function(doc, index) {
|
||||||
|
var xfield = self.model.fields.get(self.state.attributes.group);
|
||||||
|
var x = doc.getFieldValue(xfield);
|
||||||
|
|
||||||
|
// time series
|
||||||
|
var xtype = xfield.get('type');
|
||||||
|
var isDateTime = (xtype === 'date' || xtype === 'date-time' || xtype === 'time');
|
||||||
|
|
||||||
|
if (isDateTime) {
|
||||||
|
// datetime
|
||||||
|
if (self.state.attributes.graphType != 'bars' && self.state.attributes.graphType != 'columns') {
|
||||||
|
// not bar or column
|
||||||
|
x = new Date(x).getTime();
|
||||||
|
} else {
|
||||||
|
// bar or column
|
||||||
|
x = index;
|
||||||
|
}
|
||||||
|
} else if (typeof x === 'string') {
|
||||||
|
// string
|
||||||
|
x = parseFloat(x);
|
||||||
|
if (isNaN(x)) {
|
||||||
|
x = index;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var yfield = self.model.fields.get(field);
|
||||||
|
var y = doc.getFieldValue(yfield);
|
||||||
|
|
||||||
|
// horizontal bar chart
|
||||||
|
if (self.state.attributes.graphType == 'bars') {
|
||||||
|
points.push([y, x]);
|
||||||
|
} else {
|
||||||
|
points.push([x, y]);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
series.push({data: points, label: field, mouse:{lineColor: self.graphColors[series.length]}});
|
||||||
|
});
|
||||||
|
return series;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
my.Flotr2Controls = Backbone.View.extend({
|
||||||
|
className: "editor",
|
||||||
|
template: ' \
|
||||||
|
<div class="editor"> \
|
||||||
|
<form class="form-stacked"> \
|
||||||
|
<div class="clearfix"> \
|
||||||
|
<label>Graph Type</label> \
|
||||||
|
<div class="input editor-type"> \
|
||||||
|
<select> \
|
||||||
|
<option value="lines-and-points">Lines and Points</option> \
|
||||||
|
<option value="lines">Lines</option> \
|
||||||
|
<option value="points">Points</option> \
|
||||||
|
<option value="bars">Bars</option> \
|
||||||
|
<option value="columns">Columns</option> \
|
||||||
|
</select> \
|
||||||
|
</div> \
|
||||||
|
<label>Group Column (Axis 1)</label> \
|
||||||
|
<div class="input editor-group"> \
|
||||||
|
<select> \
|
||||||
|
<option value="">Please choose ...</option> \
|
||||||
|
{{#fields}} \
|
||||||
|
<option value="{{id}}">{{label}}</option> \
|
||||||
|
{{/fields}} \
|
||||||
|
</select> \
|
||||||
|
</div> \
|
||||||
|
<div class="editor-series-group"> \
|
||||||
|
</div> \
|
||||||
|
</div> \
|
||||||
|
<div class="editor-buttons"> \
|
||||||
|
<button class="btn editor-add">Add Series</button> \
|
||||||
|
</div> \
|
||||||
|
<div class="editor-buttons editor-submit" comment="hidden temporarily" style="display: none;"> \
|
||||||
|
<button class="editor-save">Save</button> \
|
||||||
|
<input type="hidden" class="editor-id" value="chart-1" /> \
|
||||||
|
</div> \
|
||||||
|
</form> \
|
||||||
|
</div> \
|
||||||
|
',
|
||||||
|
templateSeriesEditor: ' \
|
||||||
|
<div class="editor-series js-series-{{seriesIndex}}"> \
|
||||||
|
<label>Series <span>{{seriesName}} (Axis 2)</span> \
|
||||||
|
[<a href="#remove" class="action-remove-series">Remove</a>] \
|
||||||
|
</label> \
|
||||||
|
<div class="input"> \
|
||||||
|
<select> \
|
||||||
|
{{#fields}} \
|
||||||
|
<option value="{{id}}">{{label}}</option> \
|
||||||
|
{{/fields}} \
|
||||||
|
</select> \
|
||||||
|
</div> \
|
||||||
|
</div> \
|
||||||
|
',
|
||||||
|
events: {
|
||||||
|
'change form select': 'onEditorSubmit',
|
||||||
|
'click .editor-add': '_onAddSeries',
|
||||||
|
'click .action-remove-series': 'removeSeries'
|
||||||
|
},
|
||||||
|
|
||||||
|
initialize: function(options) {
|
||||||
|
var self = this;
|
||||||
|
this.el = $(this.el);
|
||||||
|
_.bindAll(this, 'render');
|
||||||
|
this.model.fields.bind('reset', this.render);
|
||||||
|
this.model.fields.bind('add', this.render);
|
||||||
|
this.state = new recline.Model.ObjectState(options.state);
|
||||||
|
this.render();
|
||||||
|
},
|
||||||
|
|
||||||
|
render: function() {
|
||||||
|
var self = this;
|
||||||
|
var tmplData = this.model.toTemplateJSON();
|
||||||
|
var htmls = Mustache.render(this.template, tmplData);
|
||||||
|
this.el.html(htmls);
|
||||||
|
|
||||||
|
// set up editor from state
|
||||||
|
if (this.state.get('graphType')) {
|
||||||
|
this._selectOption('.editor-type', this.state.get('graphType'));
|
||||||
|
}
|
||||||
|
if (this.state.get('group')) {
|
||||||
|
this._selectOption('.editor-group', this.state.get('group'));
|
||||||
|
}
|
||||||
|
// ensure at least one series box shows up
|
||||||
|
var tmpSeries = [""];
|
||||||
|
if (this.state.get('series').length > 0) {
|
||||||
|
tmpSeries = this.state.get('series');
|
||||||
|
}
|
||||||
|
_.each(tmpSeries, function(series, idx) {
|
||||||
|
self.addSeries(idx);
|
||||||
|
self._selectOption('.editor-series.js-series-' + idx, series);
|
||||||
|
});
|
||||||
|
return this;
|
||||||
|
},
|
||||||
|
|
||||||
|
// Private: Helper function to select an option from a select list
|
||||||
|
//
|
||||||
|
_selectOption: function(id,value){
|
||||||
|
var options = this.el.find(id + ' select > option');
|
||||||
|
if (options) {
|
||||||
|
options.each(function(opt){
|
||||||
|
if (this.value == value) {
|
||||||
|
$(this).attr('selected','selected');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
onEditorSubmit: function(e) {
|
||||||
|
var select = this.el.find('.editor-group select');
|
||||||
|
var $editor = this;
|
||||||
|
var $series = this.el.find('.editor-series select');
|
||||||
|
var series = $series.map(function () {
|
||||||
|
return $(this).val();
|
||||||
|
});
|
||||||
|
var updatedState = {
|
||||||
|
series: $.makeArray(series),
|
||||||
|
group: this.el.find('.editor-group select').val(),
|
||||||
|
graphType: this.el.find('.editor-type select').val()
|
||||||
|
};
|
||||||
|
this.state.set(updatedState);
|
||||||
|
},
|
||||||
|
|
||||||
|
// Public: Adds a new empty series select box to the editor.
|
||||||
|
//
|
||||||
|
// @param [int] idx index of this series in the list of series
|
||||||
|
//
|
||||||
|
// Returns itself.
|
||||||
|
addSeries: function (idx) {
|
||||||
|
var data = _.extend({
|
||||||
|
seriesIndex: idx,
|
||||||
|
seriesName: String.fromCharCode(idx + 64 + 1)
|
||||||
|
}, this.model.toTemplateJSON());
|
||||||
|
|
||||||
|
var htmls = Mustache.render(this.templateSeriesEditor, data);
|
||||||
|
this.el.find('.editor-series-group').append(htmls);
|
||||||
|
return this;
|
||||||
|
},
|
||||||
|
|
||||||
|
_onAddSeries: function(e) {
|
||||||
|
e.preventDefault();
|
||||||
|
this.addSeries(this.state.get('series').length);
|
||||||
|
},
|
||||||
|
|
||||||
|
// Public: Removes a series list item from the editor.
|
||||||
|
//
|
||||||
|
// Also updates the labels of the remaining series elements.
|
||||||
|
removeSeries: function (e) {
|
||||||
|
e.preventDefault();
|
||||||
|
var $el = $(e.target);
|
||||||
|
$el.parent().parent().remove();
|
||||||
|
this.onEditorSubmit();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
})(jQuery, recline.View);
|
||||||
|
|
||||||
@@ -1,462 +1,5 @@
|
|||||||
/*jshint multistr:true */
|
|
||||||
|
|
||||||
this.recline = this.recline || {};
|
this.recline = this.recline || {};
|
||||||
this.recline.View = this.recline.View || {};
|
this.recline.View = this.recline.View || {};
|
||||||
|
this.recline.View.Graph = this.recline.View.Flot;
|
||||||
(function($, my) {
|
this.recline.View.GraphControls = this.recline.View.FlotControls;
|
||||||
|
|
||||||
// ## Graph view for a Dataset using Flot graphing library.
|
|
||||||
//
|
|
||||||
// Initialization arguments (in a hash in first parameter):
|
|
||||||
//
|
|
||||||
// * model: recline.Model.Dataset
|
|
||||||
// * state: (optional) configuration hash of form:
|
|
||||||
//
|
|
||||||
// {
|
|
||||||
// group: {column name for x-axis},
|
|
||||||
// series: [{column name for series A}, {column name series B}, ... ],
|
|
||||||
// graphType: 'line',
|
|
||||||
// graphOptions: {custom [Flotr2 options](http://www.humblesoftware.com/flotr2/documentation#configuration)}
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// NB: should *not* provide an el argument to the view but must let the view
|
|
||||||
// generate the element itself (you can then append view.el to the DOM.
|
|
||||||
my.Graph = Backbone.View.extend({
|
|
||||||
template: ' \
|
|
||||||
<div class="recline-graph"> \
|
|
||||||
<div class="panel graph" style="display: block;"> \
|
|
||||||
<div class="js-temp-notice alert alert-block"> \
|
|
||||||
<h3 class="alert-heading">Hey there!</h3> \
|
|
||||||
<p>There\'s no graph here yet because we don\'t know what fields you\'d like to see plotted.</p> \
|
|
||||||
<p>Please tell us by <strong>using the menu on the right</strong> and a graph will automatically appear.</p> \
|
|
||||||
</div> \
|
|
||||||
</div> \
|
|
||||||
</div> \
|
|
||||||
',
|
|
||||||
|
|
||||||
initialize: function(options) {
|
|
||||||
var self = this;
|
|
||||||
this.graphColors = ["#edc240", "#afd8f8", "#cb4b4b", "#4da74d", "#9440ed"];
|
|
||||||
|
|
||||||
this.el = $(this.el);
|
|
||||||
_.bindAll(this, 'render', 'redraw');
|
|
||||||
this.needToRedraw = false;
|
|
||||||
this.model.bind('change', this.render);
|
|
||||||
this.model.fields.bind('reset', this.render);
|
|
||||||
this.model.fields.bind('add', this.render);
|
|
||||||
this.model.records.bind('add', this.redraw);
|
|
||||||
this.model.records.bind('reset', this.redraw);
|
|
||||||
var stateData = _.extend({
|
|
||||||
group: null,
|
|
||||||
// so that at least one series chooser box shows up
|
|
||||||
series: [],
|
|
||||||
graphType: 'lines-and-points'
|
|
||||||
},
|
|
||||||
options.state
|
|
||||||
);
|
|
||||||
this.state = new recline.Model.ObjectState(stateData);
|
|
||||||
this.editor = new my.GraphControls({
|
|
||||||
model: this.model,
|
|
||||||
state: this.state.toJSON()
|
|
||||||
});
|
|
||||||
this.editor.state.bind('change', function() {
|
|
||||||
self.state.set(self.editor.state.toJSON());
|
|
||||||
self.redraw();
|
|
||||||
});
|
|
||||||
this.elSidebar = this.editor.el;
|
|
||||||
},
|
|
||||||
|
|
||||||
render: function() {
|
|
||||||
var self = this;
|
|
||||||
var tmplData = this.model.toTemplateJSON();
|
|
||||||
var htmls = Mustache.render(this.template, tmplData);
|
|
||||||
$(this.el).html(htmls);
|
|
||||||
this.$graph = this.el.find('.panel.graph');
|
|
||||||
return this;
|
|
||||||
},
|
|
||||||
|
|
||||||
redraw: function() {
|
|
||||||
// There appear to be issues generating a Flot graph if either:
|
|
||||||
|
|
||||||
// * The relevant div that graph attaches to his hidden at the moment of creating the plot -- Flot will complain with
|
|
||||||
//
|
|
||||||
// Uncaught Invalid dimensions for plot, width = 0, height = 0
|
|
||||||
// * There is no data for the plot -- either same error or may have issues later with errors like 'non-existent node-value'
|
|
||||||
var areWeVisible = !jQuery.expr.filters.hidden(this.el[0]);
|
|
||||||
if ((!areWeVisible || this.model.records.length === 0)) {
|
|
||||||
this.needToRedraw = true;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// check we have something to plot
|
|
||||||
if (this.state.get('group') && this.state.get('series')) {
|
|
||||||
// faff around with width because flot draws axes *outside* of the element width which means graph can get push down as it hits element next to it
|
|
||||||
this.$graph.width(this.el.width() - 20);
|
|
||||||
var series = this.createSeries();
|
|
||||||
var options = this.getGraphOptions(this.state.attributes.graphType);
|
|
||||||
this.plot = Flotr.draw(this.$graph.get(0), series, options);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
show: function() {
|
|
||||||
// because we cannot redraw when hidden we may need to when becoming visible
|
|
||||||
if (this.needToRedraw) {
|
|
||||||
this.redraw();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
// ### getGraphOptions
|
|
||||||
//
|
|
||||||
// Get options for Flot Graph
|
|
||||||
//
|
|
||||||
// needs to be function as can depend on state
|
|
||||||
//
|
|
||||||
// @param typeId graphType id (lines, lines-and-points etc)
|
|
||||||
getGraphOptions: function(typeId) {
|
|
||||||
var self = this;
|
|
||||||
|
|
||||||
var tickFormatter = function (x) {
|
|
||||||
return getFormattedX(x);
|
|
||||||
};
|
|
||||||
|
|
||||||
// infoboxes on mouse hover on points/bars etc
|
|
||||||
var trackFormatter = function (obj) {
|
|
||||||
var x = obj.x;
|
|
||||||
var y = obj.y;
|
|
||||||
// it's horizontal so we have to flip
|
|
||||||
if (self.state.attributes.graphType === 'bars') {
|
|
||||||
var _tmp = x;
|
|
||||||
x = y;
|
|
||||||
y = _tmp;
|
|
||||||
}
|
|
||||||
|
|
||||||
x = getFormattedX(x);
|
|
||||||
|
|
||||||
var content = _.template('<%= group %> = <%= x %>, <%= series %> = <%= y %>', {
|
|
||||||
group: self.state.attributes.group,
|
|
||||||
x: x,
|
|
||||||
series: obj.series.label,
|
|
||||||
y: y
|
|
||||||
});
|
|
||||||
|
|
||||||
return content;
|
|
||||||
};
|
|
||||||
|
|
||||||
var getFormattedX = function (x) {
|
|
||||||
var xfield = self.model.fields.get(self.state.attributes.group);
|
|
||||||
|
|
||||||
// time series
|
|
||||||
var xtype = xfield.get('type');
|
|
||||||
var isDateTime = (xtype === 'date' || xtype === 'date-time' || xtype === 'time');
|
|
||||||
|
|
||||||
if (self.model.records.models[parseInt(x)]) {
|
|
||||||
x = self.model.records.models[parseInt(x)].get(self.state.attributes.group);
|
|
||||||
if (isDateTime) {
|
|
||||||
x = new Date(x).toLocaleDateString();
|
|
||||||
}
|
|
||||||
} else if (isDateTime) {
|
|
||||||
x = new Date(parseInt(x)).toLocaleDateString();
|
|
||||||
}
|
|
||||||
return x;
|
|
||||||
}
|
|
||||||
|
|
||||||
var xaxis = {};
|
|
||||||
xaxis.tickFormatter = tickFormatter;
|
|
||||||
|
|
||||||
var yaxis = {};
|
|
||||||
yaxis.autoscale = true;
|
|
||||||
yaxis.autoscaleMargin = 0.02;
|
|
||||||
|
|
||||||
var mouse = {};
|
|
||||||
mouse.track = true;
|
|
||||||
mouse.relative = true;
|
|
||||||
mouse.trackFormatter = trackFormatter;
|
|
||||||
|
|
||||||
var legend = {};
|
|
||||||
legend.position = 'ne';
|
|
||||||
|
|
||||||
// mouse.lineColor is set in createSeries
|
|
||||||
var optionsPerGraphType = {
|
|
||||||
lines: {
|
|
||||||
legend: legend,
|
|
||||||
colors: this.graphColors,
|
|
||||||
lines: { show: true },
|
|
||||||
xaxis: xaxis,
|
|
||||||
yaxis: yaxis,
|
|
||||||
mouse: mouse
|
|
||||||
},
|
|
||||||
points: {
|
|
||||||
legend: legend,
|
|
||||||
colors: this.graphColors,
|
|
||||||
points: { show: true, hitRadius: 5 },
|
|
||||||
xaxis: xaxis,
|
|
||||||
yaxis: yaxis,
|
|
||||||
mouse: mouse,
|
|
||||||
grid: { hoverable: true, clickable: true }
|
|
||||||
},
|
|
||||||
'lines-and-points': {
|
|
||||||
legend: legend,
|
|
||||||
colors: this.graphColors,
|
|
||||||
points: { show: true, hitRadius: 5 },
|
|
||||||
lines: { show: true },
|
|
||||||
xaxis: xaxis,
|
|
||||||
yaxis: yaxis,
|
|
||||||
mouse: mouse,
|
|
||||||
grid: { hoverable: true, clickable: true }
|
|
||||||
},
|
|
||||||
bars: {
|
|
||||||
legend: legend,
|
|
||||||
colors: this.graphColors,
|
|
||||||
lines: { show: false },
|
|
||||||
xaxis: yaxis,
|
|
||||||
yaxis: xaxis,
|
|
||||||
mouse: {
|
|
||||||
track: true,
|
|
||||||
relative: true,
|
|
||||||
trackFormatter: trackFormatter,
|
|
||||||
fillColor: '#FFFFFF',
|
|
||||||
fillOpacity: 0.3,
|
|
||||||
position: 'e'
|
|
||||||
},
|
|
||||||
bars: {
|
|
||||||
show: true,
|
|
||||||
horizontal: true,
|
|
||||||
shadowSize: 0,
|
|
||||||
barWidth: 0.8
|
|
||||||
}
|
|
||||||
},
|
|
||||||
columns: {
|
|
||||||
legend: legend,
|
|
||||||
colors: this.graphColors,
|
|
||||||
lines: { show: false },
|
|
||||||
xaxis: xaxis,
|
|
||||||
yaxis: yaxis,
|
|
||||||
mouse: {
|
|
||||||
track: true,
|
|
||||||
relative: true,
|
|
||||||
trackFormatter: trackFormatter,
|
|
||||||
fillColor: '#FFFFFF',
|
|
||||||
fillOpacity: 0.3,
|
|
||||||
position: 'n'
|
|
||||||
},
|
|
||||||
bars: {
|
|
||||||
show: true,
|
|
||||||
horizontal: false,
|
|
||||||
shadowSize: 0,
|
|
||||||
barWidth: 0.8
|
|
||||||
}
|
|
||||||
},
|
|
||||||
grid: { hoverable: true, clickable: true }
|
|
||||||
};
|
|
||||||
|
|
||||||
if (self.state.get('graphOptions')){
|
|
||||||
return _.extend(optionsPerGraphType[typeId],
|
|
||||||
self.state.get('graphOptions')
|
|
||||||
)
|
|
||||||
}else{
|
|
||||||
return optionsPerGraphType[typeId];
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
createSeries: function() {
|
|
||||||
var self = this;
|
|
||||||
var series = [];
|
|
||||||
_.each(this.state.attributes.series, function(field) {
|
|
||||||
var points = [];
|
|
||||||
_.each(self.model.records.models, function(doc, index) {
|
|
||||||
var xfield = self.model.fields.get(self.state.attributes.group);
|
|
||||||
var x = doc.getFieldValue(xfield);
|
|
||||||
|
|
||||||
// time series
|
|
||||||
var xtype = xfield.get('type');
|
|
||||||
var isDateTime = (xtype === 'date' || xtype === 'date-time' || xtype === 'time');
|
|
||||||
|
|
||||||
if (isDateTime) {
|
|
||||||
// datetime
|
|
||||||
if (self.state.attributes.graphType != 'bars' && self.state.attributes.graphType != 'columns') {
|
|
||||||
// not bar or column
|
|
||||||
x = new Date(x).getTime();
|
|
||||||
} else {
|
|
||||||
// bar or column
|
|
||||||
x = index;
|
|
||||||
}
|
|
||||||
} else if (typeof x === 'string') {
|
|
||||||
// string
|
|
||||||
x = parseFloat(x);
|
|
||||||
if (isNaN(x)) {
|
|
||||||
x = index;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var yfield = self.model.fields.get(field);
|
|
||||||
var y = doc.getFieldValue(yfield);
|
|
||||||
|
|
||||||
// horizontal bar chart
|
|
||||||
if (self.state.attributes.graphType == 'bars') {
|
|
||||||
points.push([y, x]);
|
|
||||||
} else {
|
|
||||||
points.push([x, y]);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
series.push({data: points, label: field, mouse:{lineColor: self.graphColors[series.length]}});
|
|
||||||
});
|
|
||||||
return series;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
my.GraphControls = Backbone.View.extend({
|
|
||||||
className: "editor",
|
|
||||||
template: ' \
|
|
||||||
<div class="editor"> \
|
|
||||||
<form class="form-stacked"> \
|
|
||||||
<div class="clearfix"> \
|
|
||||||
<label>Graph Type</label> \
|
|
||||||
<div class="input editor-type"> \
|
|
||||||
<select> \
|
|
||||||
<option value="lines-and-points">Lines and Points</option> \
|
|
||||||
<option value="lines">Lines</option> \
|
|
||||||
<option value="points">Points</option> \
|
|
||||||
<option value="bars">Bars</option> \
|
|
||||||
<option value="columns">Columns</option> \
|
|
||||||
</select> \
|
|
||||||
</div> \
|
|
||||||
<label>Group Column (Axis 1)</label> \
|
|
||||||
<div class="input editor-group"> \
|
|
||||||
<select> \
|
|
||||||
<option value="">Please choose ...</option> \
|
|
||||||
{{#fields}} \
|
|
||||||
<option value="{{id}}">{{label}}</option> \
|
|
||||||
{{/fields}} \
|
|
||||||
</select> \
|
|
||||||
</div> \
|
|
||||||
<div class="editor-series-group"> \
|
|
||||||
</div> \
|
|
||||||
</div> \
|
|
||||||
<div class="editor-buttons"> \
|
|
||||||
<button class="btn editor-add">Add Series</button> \
|
|
||||||
</div> \
|
|
||||||
<div class="editor-buttons editor-submit" comment="hidden temporarily" style="display: none;"> \
|
|
||||||
<button class="editor-save">Save</button> \
|
|
||||||
<input type="hidden" class="editor-id" value="chart-1" /> \
|
|
||||||
</div> \
|
|
||||||
</form> \
|
|
||||||
</div> \
|
|
||||||
',
|
|
||||||
templateSeriesEditor: ' \
|
|
||||||
<div class="editor-series js-series-{{seriesIndex}}"> \
|
|
||||||
<label>Series <span>{{seriesName}} (Axis 2)</span> \
|
|
||||||
[<a href="#remove" class="action-remove-series">Remove</a>] \
|
|
||||||
</label> \
|
|
||||||
<div class="input"> \
|
|
||||||
<select> \
|
|
||||||
{{#fields}} \
|
|
||||||
<option value="{{id}}">{{label}}</option> \
|
|
||||||
{{/fields}} \
|
|
||||||
</select> \
|
|
||||||
</div> \
|
|
||||||
</div> \
|
|
||||||
',
|
|
||||||
events: {
|
|
||||||
'change form select': 'onEditorSubmit',
|
|
||||||
'click .editor-add': '_onAddSeries',
|
|
||||||
'click .action-remove-series': 'removeSeries'
|
|
||||||
},
|
|
||||||
|
|
||||||
initialize: function(options) {
|
|
||||||
var self = this;
|
|
||||||
this.el = $(this.el);
|
|
||||||
_.bindAll(this, 'render');
|
|
||||||
this.model.fields.bind('reset', this.render);
|
|
||||||
this.model.fields.bind('add', this.render);
|
|
||||||
this.state = new recline.Model.ObjectState(options.state);
|
|
||||||
this.render();
|
|
||||||
},
|
|
||||||
|
|
||||||
render: function() {
|
|
||||||
var self = this;
|
|
||||||
var tmplData = this.model.toTemplateJSON();
|
|
||||||
var htmls = Mustache.render(this.template, tmplData);
|
|
||||||
this.el.html(htmls);
|
|
||||||
|
|
||||||
// set up editor from state
|
|
||||||
if (this.state.get('graphType')) {
|
|
||||||
this._selectOption('.editor-type', this.state.get('graphType'));
|
|
||||||
}
|
|
||||||
if (this.state.get('group')) {
|
|
||||||
this._selectOption('.editor-group', this.state.get('group'));
|
|
||||||
}
|
|
||||||
// ensure at least one series box shows up
|
|
||||||
var tmpSeries = [""];
|
|
||||||
if (this.state.get('series').length > 0) {
|
|
||||||
tmpSeries = this.state.get('series');
|
|
||||||
}
|
|
||||||
_.each(tmpSeries, function(series, idx) {
|
|
||||||
self.addSeries(idx);
|
|
||||||
self._selectOption('.editor-series.js-series-' + idx, series);
|
|
||||||
});
|
|
||||||
return this;
|
|
||||||
},
|
|
||||||
|
|
||||||
// Private: Helper function to select an option from a select list
|
|
||||||
//
|
|
||||||
_selectOption: function(id,value){
|
|
||||||
var options = this.el.find(id + ' select > option');
|
|
||||||
if (options) {
|
|
||||||
options.each(function(opt){
|
|
||||||
if (this.value == value) {
|
|
||||||
$(this).attr('selected','selected');
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
onEditorSubmit: function(e) {
|
|
||||||
var select = this.el.find('.editor-group select');
|
|
||||||
var $editor = this;
|
|
||||||
var $series = this.el.find('.editor-series select');
|
|
||||||
var series = $series.map(function () {
|
|
||||||
return $(this).val();
|
|
||||||
});
|
|
||||||
var updatedState = {
|
|
||||||
series: $.makeArray(series),
|
|
||||||
group: this.el.find('.editor-group select').val(),
|
|
||||||
graphType: this.el.find('.editor-type select').val()
|
|
||||||
};
|
|
||||||
this.state.set(updatedState);
|
|
||||||
},
|
|
||||||
|
|
||||||
// Public: Adds a new empty series select box to the editor.
|
|
||||||
//
|
|
||||||
// @param [int] idx index of this series in the list of series
|
|
||||||
//
|
|
||||||
// Returns itself.
|
|
||||||
addSeries: function (idx) {
|
|
||||||
var data = _.extend({
|
|
||||||
seriesIndex: idx,
|
|
||||||
seriesName: String.fromCharCode(idx + 64 + 1)
|
|
||||||
}, this.model.toTemplateJSON());
|
|
||||||
|
|
||||||
var htmls = Mustache.render(this.templateSeriesEditor, data);
|
|
||||||
this.el.find('.editor-series-group').append(htmls);
|
|
||||||
return this;
|
|
||||||
},
|
|
||||||
|
|
||||||
_onAddSeries: function(e) {
|
|
||||||
e.preventDefault();
|
|
||||||
this.addSeries(this.state.get('series').length);
|
|
||||||
},
|
|
||||||
|
|
||||||
// Public: Removes a series list item from the editor.
|
|
||||||
//
|
|
||||||
// Also updates the labels of the remaining series elements.
|
|
||||||
removeSeries: function (e) {
|
|
||||||
e.preventDefault();
|
|
||||||
var $el = $(e.target);
|
|
||||||
$el.parent().parent().remove();
|
|
||||||
this.onEditorSubmit();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
})(jQuery, recline.View);
|
|
||||||
|
|
||||||
|
|||||||
@@ -53,8 +53,9 @@
|
|||||||
<script type="text/javascript" src="../src/view.grid.js"></script>
|
<script type="text/javascript" src="../src/view.grid.js"></script>
|
||||||
<script type="text/javascript" src="../src/view.slickgrid.js"></script>
|
<script type="text/javascript" src="../src/view.slickgrid.js"></script>
|
||||||
<script type="text/javascript" src="../src/view.transform.js"></script>
|
<script type="text/javascript" src="../src/view.transform.js"></script>
|
||||||
<script type="text/javascript" src="../src/view.graph.js"></script>
|
|
||||||
<script type="text/javascript" src="../src/view.flot.js"></script>
|
<script type="text/javascript" src="../src/view.flot.js"></script>
|
||||||
|
<script type="text/javascript" src="../src/view.flotr2.js"></script>
|
||||||
|
<script type="text/javascript" src="../src/view.graph.js"></script>
|
||||||
<script type="text/javascript" src="../src/view.map.js"></script>
|
<script type="text/javascript" src="../src/view.map.js"></script>
|
||||||
<script type="text/javascript" src="../src/view.timeline.js"></script>
|
<script type="text/javascript" src="../src/view.timeline.js"></script>
|
||||||
<script type="text/javascript" src="../src/widget.pager.js"></script>
|
<script type="text/javascript" src="../src/widget.pager.js"></script>
|
||||||
@@ -65,7 +66,7 @@
|
|||||||
|
|
||||||
<script type="text/javascript" src="view.grid.test.js"></script>
|
<script type="text/javascript" src="view.grid.test.js"></script>
|
||||||
<script type="text/javascript" src="view.slickgrid.test.js"></script>
|
<script type="text/javascript" src="view.slickgrid.test.js"></script>
|
||||||
<script type="text/javascript" src="view.graph.test.js"></script>
|
<script type="text/javascript" src="view.flotr2.test.js"></script>
|
||||||
<script type="text/javascript" src="view.flot.test.js"></script>
|
<script type="text/javascript" src="view.flot.test.js"></script>
|
||||||
<script type="text/javascript" src="view.map.test.js"></script>
|
<script type="text/javascript" src="view.map.test.js"></script>
|
||||||
<script type="text/javascript" src="view.timeline.test.js"></script>
|
<script type="text/javascript" src="view.timeline.test.js"></script>
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
module("View - Graph");
|
module("View - Flotr2");
|
||||||
|
|
||||||
test('basics', function () {
|
test('basics', function () {
|
||||||
var dataset = Fixture.getDataset();
|
var dataset = Fixture.getDataset();
|
||||||
var view = new recline.View.Graph({
|
var view = new recline.View.Flotr2({
|
||||||
model: dataset
|
model: dataset
|
||||||
});
|
});
|
||||||
$('.fixtures').append(view.el);
|
$('.fixtures').append(view.el);
|
||||||
@@ -14,7 +14,7 @@ test('basics', function () {
|
|||||||
|
|
||||||
test('initialize', function () {
|
test('initialize', function () {
|
||||||
var dataset = Fixture.getDataset();
|
var dataset = Fixture.getDataset();
|
||||||
var view = new recline.View.Graph({
|
var view = new recline.View.Flotr2({
|
||||||
model: dataset,
|
model: dataset,
|
||||||
state: {
|
state: {
|
||||||
'graphType': 'lines',
|
'graphType': 'lines',
|
||||||
@@ -40,7 +40,7 @@ test('initialize', function () {
|
|||||||
test('dates in graph view', function () {
|
test('dates in graph view', function () {
|
||||||
expect(0);
|
expect(0);
|
||||||
var dataset = Fixture.getDataset();
|
var dataset = Fixture.getDataset();
|
||||||
var view = new recline.View.Graph({
|
var view = new recline.View.Flotr2({
|
||||||
model: dataset,
|
model: dataset,
|
||||||
state: {
|
state: {
|
||||||
'graphType': 'lines',
|
'graphType': 'lines',
|
||||||
@@ -53,9 +53,9 @@ test('dates in graph view', function () {
|
|||||||
view.remove();
|
view.remove();
|
||||||
});
|
});
|
||||||
|
|
||||||
test('GraphControls basics', function () {
|
test('Flotr2Controls basics', function () {
|
||||||
var dataset = Fixture.getDataset();
|
var dataset = Fixture.getDataset();
|
||||||
var view = new recline.View.GraphControls({
|
var view = new recline.View.Flotr2Controls({
|
||||||
model: dataset,
|
model: dataset,
|
||||||
state: {
|
state: {
|
||||||
graphType: 'bars',
|
graphType: 'bars',
|
||||||
@@ -72,7 +72,7 @@ test('GraphControls basics', function () {
|
|||||||
test('Overriding graph options', function () {
|
test('Overriding graph options', function () {
|
||||||
var dataset = Fixture.getDataset();
|
var dataset = Fixture.getDataset();
|
||||||
var randomWidth = Math.random();
|
var randomWidth = Math.random();
|
||||||
var view = new recline.View.Graph({
|
var view = new recline.View.Flotr2({
|
||||||
model: dataset,
|
model: dataset,
|
||||||
state: {
|
state: {
|
||||||
'graphType': 'bars',
|
'graphType': 'bars',
|
||||||
Reference in New Issue
Block a user