diff --git a/dist/recline.css b/dist/recline.css
index 610a56a7..bc71116c 100644
--- a/dist/recline.css
+++ b/dist/recline.css
@@ -1,23 +1,23 @@
-.recline-graph .graph {
+.recline-flot .graph {
height: 500px;
overflow: hidden;
}
-.recline-graph .legend table {
+.recline-flot .legend table {
width: auto;
margin-bottom: 0;
}
-.recline-graph .legend td {
+.recline-flot .legend td {
padding: 5px;
line-height: 13px;
}
-.recline-graph .graph .alert {
+.recline-flot .graph .alert {
width: 450px;
}
-#recline-graph-tooltip {
+#recline-flot-tooltip {
position: absolute;
background-color: #FEE !important;
color: #000000 !important;
diff --git a/dist/recline.dataset.js b/dist/recline.dataset.js
index 23fce729..92183702 100644
--- a/dist/recline.dataset.js
+++ b/dist/recline.dataset.js
@@ -4,6 +4,7 @@ this.recline.Model = this.recline.Model || {};
(function(my) {
+// use either jQuery or Underscore Deferred depending on what is available
var Deferred = _.isUndefined(this.jQuery) ? _.Deferred : jQuery.Deferred;
// ## Dataset
@@ -596,6 +597,9 @@ this.recline.Backend.Memory = this.recline.Backend.Memory || {};
(function(my) {
my.__type__ = 'memory';
+ // private data - use either jQuery or Underscore Deferred depending on what is available
+ var Deferred = _.isUndefined(this.jQuery) ? _.Deferred : jQuery.Deferred;
+
// ## Data Wrapper
//
// Turn a simple array of JS objects into a mini data-store with
@@ -639,7 +643,7 @@ this.recline.Backend.Memory = this.recline.Backend.Memory || {};
this.save = function(changes, dataset) {
var self = this;
- var dfd = new _.Deferred();
+ var dfd = new Deferred();
// TODO _.each(changes.creates) { ... }
_.each(changes.updates, function(record) {
self.update(record);
@@ -652,7 +656,7 @@ this.recline.Backend.Memory = this.recline.Backend.Memory || {};
},
this.query = function(queryObj) {
- var dfd = new _.Deferred();
+ var dfd = new Deferred();
var numRows = queryObj.size || this.records.length;
var start = queryObj.from || 0;
var results = this.records;
@@ -820,7 +824,7 @@ this.recline.Backend.Memory = this.recline.Backend.Memory || {};
};
this.transform = function(editFunc) {
- var dfd = new _.Deferred();
+ var dfd = new Deferred();
// TODO: should we clone before mapping? Do not see the point atm.
self.records = _.map(self.records, editFunc);
// now deal with deletes (i.e. nulls)
diff --git a/dist/recline.js b/dist/recline.js
index 192b861b..297cc26b 100644
--- a/dist/recline.js
+++ b/dist/recline.js
@@ -27,6 +27,9 @@ this.recline.Backend.Ckan = this.recline.Backend.Ckan || {};
my.__type__ = 'ckan';
+ // private - use either jQuery or Underscore Deferred depending on what is available
+ var Deferred = _.isUndefined(this.jQuery) ? _.Deferred : jQuery.Deferred;
+
// Default CKAN API endpoint used for requests (you can change this but it will affect every request!)
//
// DEPRECATION: this will be removed in v0.7. Please set endpoint attribute on dataset instead
@@ -41,7 +44,7 @@ this.recline.Backend.Ckan = this.recline.Backend.Ckan || {};
dataset.id = out.resource_id;
var wrapper = my.DataStore(out.endpoint);
}
- var dfd = new _.Deferred();
+ var dfd = new Deferred();
var jqxhr = wrapper.search({resource_id: dataset.id, limit: 0});
jqxhr.done(function(results) {
// map ckan types to our usual types ...
@@ -84,7 +87,7 @@ this.recline.Backend.Ckan = this.recline.Backend.Ckan || {};
var wrapper = my.DataStore(out.endpoint);
}
var actualQuery = my._normalizeQuery(queryObj, dataset);
- var dfd = new _.Deferred();
+ var dfd = new Deferred();
var jqxhr = wrapper.search(actualQuery);
jqxhr.done(function(results) {
var out = {
@@ -145,6 +148,9 @@ this.recline.Backend.CSV = this.recline.Backend.CSV || {};
(function(my) {
my.__type__ = 'csv';
+ // use either jQuery or Underscore Deferred depending on what is available
+ var Deferred = _.isUndefined(this.jQuery) ? _.Deferred : jQuery.Deferred;
+
// ## fetch
//
// fetch supports 3 options depending on the attribute provided on the dataset argument
@@ -163,7 +169,7 @@ this.recline.Backend.CSV = this.recline.Backend.CSV || {};
// }
//
my.fetch = function(dataset) {
- var dfd = new _.Deferred();
+ var dfd = new Deferred();
if (dataset.file) {
var reader = new FileReader();
var encoding = dataset.encoding || 'UTF-8';
@@ -438,6 +444,10 @@ this.recline.Backend.DataProxy = this.recline.Backend.DataProxy || {};
// Needed because use JSONP so do not receive e.g. 500 errors
my.timeout = 5000;
+
+ // use either jQuery or Underscore Deferred depending on what is available
+ var Deferred = _.isUndefined(this.jQuery) ? _.Deferred : jQuery.Deferred;
+
// ## load
//
// Load data from a URL via the [DataProxy](http://github.com/okfn/dataproxy).
@@ -454,7 +464,7 @@ this.recline.Backend.DataProxy = this.recline.Backend.DataProxy || {};
data: data,
dataType: 'jsonp'
});
- var dfd = new _.Deferred();
+ var dfd = new Deferred();
_wrapInTimeout(jqxhr).done(function(results) {
if (results.error) {
dfd.reject(results.error);
@@ -478,7 +488,7 @@ this.recline.Backend.DataProxy = this.recline.Backend.DataProxy || {};
// Many of backends use JSONP and so will not get error messages and this is
// a crude way to catch those errors.
var _wrapInTimeout = function(ourFunction) {
- var dfd = new _.Deferred();
+ var dfd = new Deferred();
var timer = setTimeout(function() {
dfd.reject({
message: 'Request Error: Backend did not respond after ' + (my.timeout / 1000) + ' seconds'
@@ -504,6 +514,9 @@ this.recline.Backend.ElasticSearch = this.recline.Backend.ElasticSearch || {};
(function($, my) {
my.__type__ = 'elasticsearch';
+ // use either jQuery or Underscore Deferred depending on what is available
+ var Deferred = _.isUndefined(this.jQuery) ? _.Deferred : jQuery.Deferred;
+
// ## ElasticSearch Wrapper
//
// A simple JS wrapper around an [ElasticSearch](http://www.elasticsearch.org/) endpoints.
@@ -678,7 +691,7 @@ this.recline.Backend.ElasticSearch = this.recline.Backend.ElasticSearch || {};
// ### fetch
my.fetch = function(dataset) {
var es = new my.Wrapper(dataset.url, my.esOptions);
- var dfd = new _.Deferred();
+ var dfd = new Deferred();
es.mapping().done(function(schema) {
if (!schema){
@@ -706,7 +719,7 @@ this.recline.Backend.ElasticSearch = this.recline.Backend.ElasticSearch || {};
my.save = function(changes, dataset) {
var es = new my.Wrapper(dataset.url, my.esOptions);
if (changes.creates.length + changes.updates.length + changes.deletes.length > 1) {
- var dfd = new _.Deferred();
+ var dfd = new Deferred();
msg = 'Saving more than one item at a time not yet supported';
alert(msg);
dfd.reject(msg);
@@ -724,7 +737,7 @@ this.recline.Backend.ElasticSearch = this.recline.Backend.ElasticSearch || {};
// ### query
my.query = function(queryObj, dataset) {
- var dfd = new _.Deferred();
+ var dfd = new Deferred();
var es = new my.Wrapper(dataset.url, my.esOptions);
var jqxhr = es.query(queryObj);
jqxhr.done(function(results) {
@@ -786,6 +799,9 @@ this.recline.Backend.GDocs = this.recline.Backend.GDocs || {};
(function(my) {
my.__type__ = 'gdocs';
+ // use either jQuery or Underscore Deferred depending on what is available
+ var Deferred = _.isUndefined(this.jQuery) ? _.Deferred : jQuery.Deferred;
+
// ## Google spreadsheet backend
//
// Fetch data from a Google Docs spreadsheet.
@@ -810,13 +826,13 @@ this.recline.Backend.GDocs = this.recline.Backend.GDocs || {};
// * fields: array of Field objects
// * records: array of objects for each row
my.fetch = function(dataset) {
- var dfd = new _.Deferred();
+ var dfd = new Deferred();
var urls = my.getGDocsAPIUrls(dataset.url);
// TODO cover it with tests
// get the spreadsheet title
(function () {
- var titleDfd = new _.Deferred();
+ var titleDfd = new Deferred();
jQuery.getJSON(urls.spreadsheet, function (d) {
titleDfd.resolve({
@@ -950,6 +966,9 @@ this.recline.Backend.Memory = this.recline.Backend.Memory || {};
(function(my) {
my.__type__ = 'memory';
+ // private data - use either jQuery or Underscore Deferred depending on what is available
+ var Deferred = _.isUndefined(this.jQuery) ? _.Deferred : jQuery.Deferred;
+
// ## Data Wrapper
//
// Turn a simple array of JS objects into a mini data-store with
@@ -993,7 +1012,7 @@ this.recline.Backend.Memory = this.recline.Backend.Memory || {};
this.save = function(changes, dataset) {
var self = this;
- var dfd = new _.Deferred();
+ var dfd = new Deferred();
// TODO _.each(changes.creates) { ... }
_.each(changes.updates, function(record) {
self.update(record);
@@ -1006,7 +1025,7 @@ this.recline.Backend.Memory = this.recline.Backend.Memory || {};
},
this.query = function(queryObj) {
- var dfd = new _.Deferred();
+ var dfd = new Deferred();
var numRows = queryObj.size || this.records.length;
var start = queryObj.from || 0;
var results = this.records;
@@ -1174,7 +1193,7 @@ this.recline.Backend.Memory = this.recline.Backend.Memory || {};
};
this.transform = function(editFunc) {
- var dfd = new _.Deferred();
+ var dfd = new Deferred();
// TODO: should we clone before mapping? Do not see the point atm.
self.records = _.map(self.records, editFunc);
// now deal with deletes (i.e. nulls)
@@ -1194,6 +1213,9 @@ this.recline.Backend.Solr = this.recline.Backend.Solr || {};
(function($, my) {
my.__type__ = 'solr';
+ // use either jQuery or Underscore Deferred depending on what is available
+ var Deferred = _.isUndefined(this.jQuery) ? _.Deferred : jQuery.Deferred;
+
// ### fetch
//
// dataset must have a solr or url attribute pointing to solr endpoint
@@ -1207,7 +1229,7 @@ this.recline.Backend.Solr = this.recline.Backend.Solr || {};
dataType: 'jsonp',
jsonp: 'json.wrf'
});
- var dfd = new _.Deferred();
+ var dfd = new Deferred();
jqxhr.done(function(results) {
// if we get 0 results we cannot get fields
var fields = []
@@ -1240,7 +1262,7 @@ this.recline.Backend.Solr = this.recline.Backend.Solr || {};
dataType: 'jsonp',
jsonp: 'json.wrf'
});
- var dfd = new _.Deferred();
+ var dfd = new Deferred();
jqxhr.done(function(results) {
var out = {
total: results.response.numFound,
@@ -1391,6 +1413,7 @@ this.recline.Model = this.recline.Model || {};
(function(my) {
+// use either jQuery or Underscore Deferred depending on what is available
var Deferred = _.isUndefined(this.jQuery) ? _.Deferred : jQuery.Deferred;
// ## Dataset
@@ -2001,7 +2024,7 @@ this.recline.View = this.recline.View || {};
// generate the element itself (you can then append view.el to the DOM.
my.Flot = Backbone.View.extend({
template: ' \
-
\
+
\
\
\
Hey there!
\
@@ -2068,9 +2091,6 @@ my.Flot = Backbone.View.extend({
// 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() - 240);
var series = this.createSeries();
var options = this.getGraphOptions(this.state.attributes.graphType, series[0].data.length);
this.plot = $.plot(this.$graph, series, options);
@@ -2091,7 +2111,7 @@ my.Flot = Backbone.View.extend({
this.previousTooltipPoint.y !== item.seriesIndex) {
this.previousTooltipPoint.x = item.dataIndex;
this.previousTooltipPoint.y = item.seriesIndex;
- $("#recline-graph-tooltip").remove();
+ $("#recline-flot-tooltip").remove();
var x = item.datapoint[0].toFixed(2),
y = item.datapoint[1].toFixed(2);
@@ -2113,13 +2133,13 @@ my.Flot = Backbone.View.extend({
yLocation = item.pageY - 20;
}
- $('
' + content + '
').css({
+ $('
' + content + '
').css({
top: yLocation,
left: xLocation
}).appendTo("body").fadeIn(200);
}
} else {
- $("#recline-graph-tooltip").remove();
+ $("#recline-flot-tooltip").remove();
this.previousTooltipPoint.x = null;
this.previousTooltipPoint.y = null;
}
@@ -2485,7 +2505,7 @@ this.recline.View = this.recline.View || {};
(function($, my) {
-// ## Graph view for a Dataset using Flot graphing library.
+// ## Graph view for a Dataset using Flotr2 graphing library.
//
// Initialization arguments (in a hash in first parameter):
//
@@ -2501,7 +2521,7 @@ this.recline.View = this.recline.View || {};
//
// 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({
+my.Flotr2 = Backbone.View.extend({
template: ' \
\
\
@@ -2535,7 +2555,7 @@ my.Graph = Backbone.View.extend({
options.state
);
this.state = new recline.Model.ObjectState(stateData);
- this.editor = new my.GraphControls({
+ this.editor = new my.Flotr2Controls({
model: this.model,
state: this.state.toJSON()
});
@@ -2556,9 +2576,9 @@ my.Graph = Backbone.View.extend({
},
redraw: function() {
- // There appear to be issues generating a Flot graph if either:
+ // 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 -- Flot will complain with
+ // * 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'
@@ -2587,7 +2607,7 @@ my.Graph = Backbone.View.extend({
// ### getGraphOptions
//
- // Get options for Flot Graph
+ // Get options for Flotr2 Graph
//
// needs to be function as can depend on state
//
@@ -2784,7 +2804,7 @@ my.Graph = Backbone.View.extend({
}
});
-my.GraphControls = Backbone.View.extend({
+my.Flotr2Controls = Backbone.View.extend({
className: "editor",
template: ' \
\
@@ -2940,6 +2960,11 @@ my.GraphControls = Backbone.View.extend({
})(jQuery, recline.View);
+this.recline = this.recline || {};
+this.recline.View = this.recline.View || {};
+this.recline.View.Graph = this.recline.View.Flot;
+this.recline.View.GraphControls = this.recline.View.FlotControls;
+
/*jshint multistr:true */
this.recline = this.recline || {};
diff --git a/docs/tutorial-maps.markdown b/docs/tutorial-maps.markdown
index 6d67f62d..914d7d52 100644
--- a/docs/tutorial-maps.markdown
+++ b/docs/tutorial-maps.markdown
@@ -20,9 +20,7 @@ See the instructions in the [basic views tutorial](tutorial-views.html).
### Creating a Dataset
-Again like the views tutorial:
-
-Here's some example data We are going to work with:
+Just like in the main tutorial, here's some example data We are going to work with:
{% highlight javascript %}
{% include data.js %}
diff --git a/download.markdown b/download.markdown
index 8ad29dde..41b72ea0 100644
--- a/download.markdown
+++ b/download.markdown
@@ -85,7 +85,7 @@ All the views require, in addition to those needed for recline.dataset.js:
Individual views have additional dependencies such as:
-* [JQuery Flot](http://code.google.com/p/flot/) >= 0.7 (required for for graph view)
+* [JQuery Flot](http://www.flotcharts.org/) >= 0.7 (required for for graph view)
* [Leaflet](http://leaflet.cloudmade.com/) >= 0.4.4 (required for map view)
* [Leaflet.markercluster](https://github.com/danzel/Leaflet.markercluster) as of 2012-09-12 (required for marker clustering)
* [Verite Timeline](https://github.com/VeriteCo/Timeline/) as of 2012-05-02 (required for the timeline view)
diff --git a/src/backend.ckan.js b/src/backend.ckan.js
index dafe6ecd..62fc91e7 100644
--- a/src/backend.ckan.js
+++ b/src/backend.ckan.js
@@ -27,6 +27,9 @@ this.recline.Backend.Ckan = this.recline.Backend.Ckan || {};
my.__type__ = 'ckan';
+ // private - use either jQuery or Underscore Deferred depending on what is available
+ var Deferred = _.isUndefined(this.jQuery) ? _.Deferred : jQuery.Deferred;
+
// Default CKAN API endpoint used for requests (you can change this but it will affect every request!)
//
// DEPRECATION: this will be removed in v0.7. Please set endpoint attribute on dataset instead
@@ -41,7 +44,7 @@ this.recline.Backend.Ckan = this.recline.Backend.Ckan || {};
dataset.id = out.resource_id;
var wrapper = my.DataStore(out.endpoint);
}
- var dfd = new _.Deferred();
+ var dfd = new Deferred();
var jqxhr = wrapper.search({resource_id: dataset.id, limit: 0});
jqxhr.done(function(results) {
// map ckan types to our usual types ...
@@ -84,7 +87,7 @@ this.recline.Backend.Ckan = this.recline.Backend.Ckan || {};
var wrapper = my.DataStore(out.endpoint);
}
var actualQuery = my._normalizeQuery(queryObj, dataset);
- var dfd = new _.Deferred();
+ var dfd = new Deferred();
var jqxhr = wrapper.search(actualQuery);
jqxhr.done(function(results) {
var out = {
diff --git a/src/backend.couchdb.js b/src/backend.couchdb.js
index 8c2c9ad9..994cfc9b 100755
--- a/src/backend.couchdb.js
+++ b/src/backend.couchdb.js
@@ -5,6 +5,9 @@ this.recline.Backend.CouchDB = this.recline.Backend.CouchDB || {};
(function($, my) {
my.__type__ = 'couchdb';
+// use either jQuery or Underscore Deferred depending on what is available
+var Deferred = _.isUndefined(this.jQuery) ? _.Deferred : jQuery.Deferred;
+
// ## CouchDB Wrapper
//
// Connecting to [CouchDB] (http://www.couchdb.apache.org/) endpoints.
@@ -197,7 +200,7 @@ my.__type__ = 'couchdb';
var db_url = dataset.db_url;
var view_url = dataset.view_url;
var cdb = new my.CouchDBWrapper(db_url, view_url);
- var dfd = new _.Deferred();
+ var dfd = new Deferred();
// if 'doc' attribute is present, return schema of that
// else return schema of 'value' attribute which contains
@@ -239,7 +242,7 @@ my.__type__ = 'couchdb';
//
//
my.save = function (changes, dataset) {
- var dfd = new _.Deferred();
+ var dfd = new Deferred();
var total = changes.creates.length + changes.updates.length + changes.deletes.length;
var results = {'done': [], 'fail': [] };
@@ -280,7 +283,7 @@ my.save = function (changes, dataset) {
// @param {Object} recline.Dataset instance
// @param {Object} recline.Query instance.
my.query = function(queryObj, dataset) {
- var dfd = new _.Deferred();
+ var dfd = new Deferred();
var db_url = dataset.db_url;
var view_url = dataset.view_url;
var query_options = dataset.query_options;
@@ -475,7 +478,7 @@ function randomId(length, chars) {
}
_createDocument = function (new_doc, dataset) {
- var dfd = new _.Deferred();
+ var dfd = new Deferred();
var db_url = dataset.db_url;
var view_url = dataset.view_url;
var _id = new_doc['id'];
@@ -497,7 +500,7 @@ _createDocument = function (new_doc, dataset) {
};
_updateDocument = function (new_doc, dataset) {
- var dfd = new _.Deferred();
+ var dfd = new Deferred();
var db_url = dataset.db_url;
var view_url = dataset.view_url;
var _id = new_doc['id'];
@@ -527,7 +530,7 @@ _updateDocument = function (new_doc, dataset) {
};
_deleteDocument = function (del_doc, dataset) {
- var dfd = new _.Deferred();
+ var dfd = new Deferred();
var db_url = dataset.db_url;
var view_url = dataset.view_url;
var _id = del_doc['id'];
diff --git a/src/backend.csv.js b/src/backend.csv.js
index 454015f0..aacfc5e9 100644
--- a/src/backend.csv.js
+++ b/src/backend.csv.js
@@ -6,6 +6,9 @@ this.recline.Backend.CSV = this.recline.Backend.CSV || {};
(function(my) {
my.__type__ = 'csv';
+ // use either jQuery or Underscore Deferred depending on what is available
+ var Deferred = _.isUndefined(this.jQuery) ? _.Deferred : jQuery.Deferred;
+
// ## fetch
//
// fetch supports 3 options depending on the attribute provided on the dataset argument
@@ -24,7 +27,7 @@ this.recline.Backend.CSV = this.recline.Backend.CSV || {};
// }
//
my.fetch = function(dataset) {
- var dfd = new _.Deferred();
+ var dfd = new Deferred();
if (dataset.file) {
var reader = new FileReader();
var encoding = dataset.encoding || 'UTF-8';
diff --git a/src/backend.dataproxy.js b/src/backend.dataproxy.js
index b8f17826..44db50cf 100644
--- a/src/backend.dataproxy.js
+++ b/src/backend.dataproxy.js
@@ -10,6 +10,10 @@ this.recline.Backend.DataProxy = this.recline.Backend.DataProxy || {};
// Needed because use JSONP so do not receive e.g. 500 errors
my.timeout = 5000;
+
+ // use either jQuery or Underscore Deferred depending on what is available
+ var Deferred = _.isUndefined(this.jQuery) ? _.Deferred : jQuery.Deferred;
+
// ## load
//
// Load data from a URL via the [DataProxy](http://github.com/okfn/dataproxy).
@@ -26,7 +30,7 @@ this.recline.Backend.DataProxy = this.recline.Backend.DataProxy || {};
data: data,
dataType: 'jsonp'
});
- var dfd = new _.Deferred();
+ var dfd = new Deferred();
_wrapInTimeout(jqxhr).done(function(results) {
if (results.error) {
dfd.reject(results.error);
@@ -50,7 +54,7 @@ this.recline.Backend.DataProxy = this.recline.Backend.DataProxy || {};
// Many of backends use JSONP and so will not get error messages and this is
// a crude way to catch those errors.
var _wrapInTimeout = function(ourFunction) {
- var dfd = new _.Deferred();
+ var dfd = new Deferred();
var timer = setTimeout(function() {
dfd.reject({
message: 'Request Error: Backend did not respond after ' + (my.timeout / 1000) + ' seconds'
diff --git a/src/backend.elasticsearch.js b/src/backend.elasticsearch.js
index 56075a3c..82ba52de 100644
--- a/src/backend.elasticsearch.js
+++ b/src/backend.elasticsearch.js
@@ -5,6 +5,9 @@ this.recline.Backend.ElasticSearch = this.recline.Backend.ElasticSearch || {};
(function($, my) {
my.__type__ = 'elasticsearch';
+ // use either jQuery or Underscore Deferred depending on what is available
+ var Deferred = _.isUndefined(this.jQuery) ? _.Deferred : jQuery.Deferred;
+
// ## ElasticSearch Wrapper
//
// A simple JS wrapper around an [ElasticSearch](http://www.elasticsearch.org/) endpoints.
@@ -179,7 +182,7 @@ this.recline.Backend.ElasticSearch = this.recline.Backend.ElasticSearch || {};
// ### fetch
my.fetch = function(dataset) {
var es = new my.Wrapper(dataset.url, my.esOptions);
- var dfd = new _.Deferred();
+ var dfd = new Deferred();
es.mapping().done(function(schema) {
if (!schema){
@@ -207,7 +210,7 @@ this.recline.Backend.ElasticSearch = this.recline.Backend.ElasticSearch || {};
my.save = function(changes, dataset) {
var es = new my.Wrapper(dataset.url, my.esOptions);
if (changes.creates.length + changes.updates.length + changes.deletes.length > 1) {
- var dfd = new _.Deferred();
+ var dfd = new Deferred();
msg = 'Saving more than one item at a time not yet supported';
alert(msg);
dfd.reject(msg);
@@ -225,7 +228,7 @@ this.recline.Backend.ElasticSearch = this.recline.Backend.ElasticSearch || {};
// ### query
my.query = function(queryObj, dataset) {
- var dfd = new _.Deferred();
+ var dfd = new Deferred();
var es = new my.Wrapper(dataset.url, my.esOptions);
var jqxhr = es.query(queryObj);
jqxhr.done(function(results) {
diff --git a/src/backend.gdocs.js b/src/backend.gdocs.js
index 3f1812a8..4d18aa9b 100644
--- a/src/backend.gdocs.js
+++ b/src/backend.gdocs.js
@@ -5,6 +5,9 @@ this.recline.Backend.GDocs = this.recline.Backend.GDocs || {};
(function(my) {
my.__type__ = 'gdocs';
+ // use either jQuery or Underscore Deferred depending on what is available
+ var Deferred = _.isUndefined(this.jQuery) ? _.Deferred : jQuery.Deferred;
+
// ## Google spreadsheet backend
//
// Fetch data from a Google Docs spreadsheet.
@@ -29,13 +32,13 @@ this.recline.Backend.GDocs = this.recline.Backend.GDocs || {};
// * fields: array of Field objects
// * records: array of objects for each row
my.fetch = function(dataset) {
- var dfd = new _.Deferred();
+ var dfd = new Deferred();
var urls = my.getGDocsAPIUrls(dataset.url);
// TODO cover it with tests
// get the spreadsheet title
(function () {
- var titleDfd = new _.Deferred();
+ var titleDfd = new Deferred();
jQuery.getJSON(urls.spreadsheet, function (d) {
titleDfd.resolve({
diff --git a/src/backend.solr.js b/src/backend.solr.js
deleted file mode 100644
index 51b4ab71..00000000
--- a/src/backend.solr.js
+++ /dev/null
@@ -1,65 +0,0 @@
-this.recline = this.recline || {};
-this.recline.Backend = this.recline.Backend || {};
-this.recline.Backend.Solr = this.recline.Backend.Solr || {};
-
-(function($, my) {
- my.__type__ = 'solr';
-
- // ### fetch
- //
- // dataset must have a solr or url attribute pointing to solr endpoint
- my.fetch = function(dataset) {
- var jqxhr = $.ajax({
- url: dataset.solr || dataset.url,
- data: {
- rows: 1,
- wt: 'json'
- },
- dataType: 'jsonp',
- jsonp: 'json.wrf'
- });
- var dfd = new _.Deferred();
- jqxhr.done(function(results) {
- // if we get 0 results we cannot get fields
- var fields = []
- if (results.response.numFound > 0) {
- fields = _.map(_.keys(results.response.docs[0]), function(fieldName) {
- return { id: fieldName };
- });
- }
- var out = {
- fields: fields,
- useMemoryStore: false
- };
- dfd.resolve(out);
- });
- return dfd.promise();
- }
-
- // TODO - much work on proper query support is needed!!
- my.query = function(queryObj, dataset) {
- var q = queryObj.q || '*:*';
- var data = {
- q: q,
- rows: queryObj.size,
- start: queryObj.from,
- wt: 'json'
- };
- var jqxhr = $.ajax({
- url: dataset.solr || dataset.url,
- data: data,
- dataType: 'jsonp',
- jsonp: 'json.wrf'
- });
- var dfd = new _.Deferred();
- jqxhr.done(function(results) {
- var out = {
- total: results.response.numFound,
- hits: results.response.docs
- };
- dfd.resolve(out);
- });
- return dfd.promise();
- };
-
-}(jQuery, this.recline.Backend.Solr));
diff --git a/src/model.js b/src/model.js
index c279b655..2f405762 100644
--- a/src/model.js
+++ b/src/model.js
@@ -4,7 +4,7 @@ this.recline.Model = this.recline.Model || {};
(function(my) {
-// private - use either jQuery or Underscore Deferred depending on what is available
+// use either jQuery or Underscore Deferred depending on what is available
var Deferred = _.isUndefined(this.jQuery) ? _.Deferred : jQuery.Deferred;
// ## Dataset
diff --git a/src/view.flot.js b/src/view.flot.js
index d810b723..ea87f40f 100644
--- a/src/view.flot.js
+++ b/src/view.flot.js
@@ -23,7 +23,7 @@ this.recline.View = this.recline.View || {};
// generate the element itself (you can then append view.el to the DOM.
my.Flot = Backbone.View.extend({
template: ' \
-
\
+
\
\
\
Hey there!
\
@@ -90,9 +90,6 @@ my.Flot = Backbone.View.extend({
// 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() - 240);
var series = this.createSeries();
var options = this.getGraphOptions(this.state.attributes.graphType, series[0].data.length);
this.plot = $.plot(this.$graph, series, options);
@@ -113,7 +110,7 @@ my.Flot = Backbone.View.extend({
this.previousTooltipPoint.y !== item.seriesIndex) {
this.previousTooltipPoint.x = item.dataIndex;
this.previousTooltipPoint.y = item.seriesIndex;
- $("#recline-graph-tooltip").remove();
+ $("#recline-flot-tooltip").remove();
var x = item.datapoint[0].toFixed(2),
y = item.datapoint[1].toFixed(2);
@@ -135,13 +132,13 @@ my.Flot = Backbone.View.extend({
yLocation = item.pageY - 20;
}
- $('
' + content + '
').css({
+ $('
' + content + '
').css({
top: yLocation,
left: xLocation
}).appendTo("body").fadeIn(200);
}
} else {
- $("#recline-graph-tooltip").remove();
+ $("#recline-flot-tooltip").remove();
this.previousTooltipPoint.x = null;
this.previousTooltipPoint.y = null;
}
diff --git a/src/view.flotr2.js b/src/view.flotr2.js
new file mode 100644
index 00000000..9259e046
--- /dev/null
+++ b/src/view.flotr2.js
@@ -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: ' \
+
\
+
\
+
\
+
Hey there!
\
+
There\'s no graph here yet because we don\'t know what fields you\'d like to see plotted.
\
+
Please tell us by using the menu on the right and a graph will automatically appear.
\
+
\
+
\
+
\
+',
+
+ 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: ' \
+
\
+ \
+
\
+',
+ templateSeriesEditor: ' \
+
\
+ \
+
\
+ \
+
\
+
\
+ ',
+ 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);
+
diff --git a/src/view.graph.js b/src/view.graph.js
index dc640ca0..0517b2f8 100644
--- a/src/view.graph.js
+++ b/src/view.graph.js
@@ -1,462 +1,5 @@
-/*jshint multistr:true */
-
this.recline = this.recline || {};
this.recline.View = this.recline.View || {};
-
-(function($, my) {
-
-// ## 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: ' \
-
\
-
\
-
\
-
Hey there!
\
-
There\'s no graph here yet because we don\'t know what fields you\'d like to see plotted.
\
-
Please tell us by using the menu on the right and a graph will automatically appear.
\
-
\
-
\
-
\
-',
-
- 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: ' \
-
\
- \
-
\
-',
- templateSeriesEditor: ' \
-
\
- \
-
\
- \
-
\
-
\
- ',
- 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);
+this.recline.View.Graph = this.recline.View.Flot;
+this.recline.View.GraphControls = this.recline.View.FlotControls;
diff --git a/test/backend.solr.test.js b/test/backend.solr.test.js
deleted file mode 100644
index 2b7ec65a..00000000
--- a/test/backend.solr.test.js
+++ /dev/null
@@ -1,85 +0,0 @@
-(function ($) {
-module("Backend SOLR");
-
-test("fetch", function() {
- var dataset = new recline.Model.Dataset({
- url: 'http://openspending.org/api/search',
- backend: 'solr'
- });
- // stop();
-
- var stub = sinon.stub($, 'ajax', function(options) {
- return {
- done: function(callback) {
- callback(sample_data);
- return this;
- },
- fail: function() {
- }
- };
- });
-
- dataset.fetch().done(function(dataset) {
- var exp = [
- "_id",
- "amount",
- "category.label_facet",
- "dataset",
- "from.label_facet",
- "id",
- "subcategory.label_facet",
- "time.label_facet",
- "to.label_facet"
- ];
- deepEqual(
- exp,
- _.pluck(dataset.fields.toJSON(), 'id')
- );
- // check we've mapped types correctly
- equal(dataset.fields.get('amount').get('type'), 'string');
-
- // fetch does a query so we can check for records
- equal(dataset.recordCount, 10342132);
- equal(dataset.records.length, 2);
- equal(dataset.records.at(0).get('id'), '3e3e25d7737634127b76d5ee4a7df280987013c7');
- // start();
- });
- $.ajax.restore();
-});
-
-var sample_data = {
- "response": {
- "docs": [
- {
- "_id": "south-african-national-gov-budget-2012-13::3e3e25d7737634127b76d5ee4a7df280987013c7",
- "amount": 30905738200000.0,
- "category.label_facet": "General public services",
- "dataset": "south-african-national-gov-budget-2012-13",
- "from.label_facet": "National Treasury",
- "id": "3e3e25d7737634127b76d5ee4a7df280987013c7",
- "subcategory.label_facet": "Transfers of a general character between different levels of government",
- "time.label_facet": "01. April 2012",
- "to.label_facet": "Provincial Equitable Share"
- },
- {
- "_id": "south-african-national-gov-budget-2012-13::738849e28e6b3c45e5b0001e142b51479b3a3e41",
- "amount": 8938807300000.0,
- "category.label_facet": "General public services",
- "dataset": "south-african-national-gov-budget-2012-13",
- "from.label_facet": "National Treasury",
- "id": "738849e28e6b3c45e5b0001e142b51479b3a3e41",
- "subcategory.label_facet": "Public debt transactions",
- "time.label_facet": "01. April 2012",
- "to.label_facet": "State Debt Costs"
- }
- ],
- "numFound": 10342132,
- "start": 0
- },
- "responseHeader": {
- "QTime": 578,
- "status": 0
- }
-};
-
-})(this.jQuery);
diff --git a/test/index.html b/test/index.html
index 7a102b4f..cfa1151e 100644
--- a/test/index.html
+++ b/test/index.html
@@ -10,7 +10,6 @@
-
@@ -39,7 +38,6 @@
-
@@ -48,14 +46,14 @@
-
-
+
+
@@ -66,7 +64,7 @@
-
+
diff --git a/test/view.graph.test.js b/test/view.flotr2.test.js
similarity index 87%
rename from test/view.graph.test.js
rename to test/view.flotr2.test.js
index 25a04d6c..89edfada 100644
--- a/test/view.graph.test.js
+++ b/test/view.flotr2.test.js
@@ -1,8 +1,8 @@
-module("View - Graph");
+module("View - Flotr2");
test('basics', function () {
var dataset = Fixture.getDataset();
- var view = new recline.View.Graph({
+ var view = new recline.View.Flotr2({
model: dataset
});
$('.fixtures').append(view.el);
@@ -14,7 +14,7 @@ test('basics', function () {
test('initialize', function () {
var dataset = Fixture.getDataset();
- var view = new recline.View.Graph({
+ var view = new recline.View.Flotr2({
model: dataset,
state: {
'graphType': 'lines',
@@ -40,7 +40,7 @@ test('initialize', function () {
test('dates in graph view', function () {
expect(0);
var dataset = Fixture.getDataset();
- var view = new recline.View.Graph({
+ var view = new recline.View.Flotr2({
model: dataset,
state: {
'graphType': 'lines',
@@ -53,9 +53,9 @@ test('dates in graph view', function () {
view.remove();
});
-test('GraphControls basics', function () {
+test('Flotr2Controls basics', function () {
var dataset = Fixture.getDataset();
- var view = new recline.View.GraphControls({
+ var view = new recline.View.Flotr2Controls({
model: dataset,
state: {
graphType: 'bars',
@@ -72,7 +72,7 @@ test('GraphControls basics', function () {
test('Overriding graph options', function () {
var dataset = Fixture.getDataset();
var randomWidth = Math.random();
- var view = new recline.View.Graph({
+ var view = new recline.View.Flotr2({
model: dataset,
state: {
'graphType': 'bars',
@@ -83,4 +83,4 @@ test('Overriding graph options', function () {
});
equal(view.getGraphOptions('bars').bars.barWidth, randomWidth)
view.remove();
-});
\ No newline at end of file
+});