Merge branch 'master' into gh-pages

This commit is contained in:
Rufus Pollock
2012-04-11 13:19:05 +01:00
14 changed files with 171 additions and 119 deletions

View File

@@ -12,7 +12,7 @@ this.recline.Backend = this.recline.Backend || {};
// Override Backbone.sync to hand off to sync function in relevant backend // Override Backbone.sync to hand off to sync function in relevant backend
Backbone.sync = function(method, model, options) { Backbone.sync = function(method, model, options) {
return model.backend.sync(method, model, options); return model.backend.sync(method, model, options);
} };
// ## recline.Backend.Base // ## recline.Backend.Base
// //

View File

@@ -38,14 +38,14 @@ this.recline.Backend = this.recline.Backend || {};
var self = this; var self = this;
var base = this.get('dataproxy_url'); var base = this.get('dataproxy_url');
var data = { var data = {
url: dataset.get('url') url: dataset.get('url'),
, 'max-results': queryObj.size 'max-results': queryObj.size,
, type: dataset.get('format') type: dataset.get('format')
}; };
var jqxhr = $.ajax({ var jqxhr = $.ajax({
url: base url: base,
, data: data data: data,
, dataType: 'jsonp' dataType: 'jsonp'
}); });
var dfd = $.Deferred(); var dfd = $.Deferred();
this._wrapInTimeout(jqxhr).done(function(results) { this._wrapInTimeout(jqxhr).done(function(results) {

View File

@@ -59,37 +59,33 @@ this.recline.Backend = this.recline.Backend || {};
} }
}, },
_normalizeQuery: function(queryObj) { _normalizeQuery: function(queryObj) {
if (queryObj.toJSON) { var out = queryObj.toJSON ? queryObj.toJSON() : _.extend({}, queryObj);
var out = queryObj.toJSON(); if (out.q !== undefined && out.q.trim() === '') {
} else {
var out = _.extend({}, queryObj);
}
if (out.q != undefined && out.q.trim() === '') {
delete out.q; delete out.q;
} }
if (!out.q) { if (!out.q) {
out.query = { out.query = {
match_all: {} match_all: {}
} };
} else { } else {
out.query = { out.query = {
query_string: { query_string: {
query: out.q query: out.q
} }
} };
delete out.q; delete out.q;
} }
// now do filters (note the *plural*) // now do filters (note the *plural*)
if (out.filters && out.filters.length) { if (out.filters && out.filters.length) {
if (!out.filter) { if (!out.filter) {
out.filter = {} out.filter = {};
} }
if (!out.filter.and) { if (!out.filter.and) {
out.filter.and = []; out.filter.and = [];
} }
out.filter.and = out.filter.and.concat(out.filters); out.filter.and = out.filter.and.concat(out.filters);
} }
if (out.filters != undefined) { if (out.filters !== undefined) {
delete out.filters; delete out.filters;
} }
return out; return out;
@@ -107,10 +103,10 @@ this.recline.Backend = this.recline.Backend || {};
// TODO: fail case // TODO: fail case
jqxhr.done(function(results) { jqxhr.done(function(results) {
_.each(results.hits.hits, function(hit) { _.each(results.hits.hits, function(hit) {
if (!'id' in hit._source && hit._id) { if (!('id' in hit._source) && hit._id) {
hit._source.id = hit._id; hit._source.id = hit._id;
} }
}) });
if (results.facets) { if (results.facets) {
results.hits.facets = results.facets; results.hits.facets = results.facets;
} }

View File

@@ -23,12 +23,12 @@ this.recline.Backend = this.recline.Backend || {};
return url; return url;
} else { } else {
// https://docs.google.com/spreadsheet/ccc?key=XXXX#gid=0 // https://docs.google.com/spreadsheet/ccc?key=XXXX#gid=0
var regex = /.*spreadsheet\/ccc?.*key=([^#?&+]+).*/ var regex = /.*spreadsheet\/ccc?.*key=([^#?&+]+).*/;
var matches = url.match(regex); var matches = url.match(regex);
if (matches) { if (matches) {
var key = matches[1]; var key = matches[1];
var worksheet = 1; var worksheet = 1;
var out = 'https://spreadsheets.google.com/feeds/list/' + key + '/' + worksheet + '/public/values?alt=json' var out = 'https://spreadsheets.google.com/feeds/list/' + key + '/' + worksheet + '/public/values?alt=json';
return out; return out;
} else { } else {
alert('Failed to extract gdocs key from ' + url); alert('Failed to extract gdocs key from ' + url);
@@ -52,8 +52,9 @@ this.recline.Backend = this.recline.Backend || {};
// cache data onto dataset (we have loaded whole gdoc it seems!) // cache data onto dataset (we have loaded whole gdoc it seems!)
model._dataCache = result.data; model._dataCache = result.data;
dfd.resolve(model); dfd.resolve(model);
}) });
return dfd.promise(); } return dfd.promise();
}
}, },
query: function(dataset, queryObj) { query: function(dataset, queryObj) {
@@ -64,7 +65,9 @@ this.recline.Backend = this.recline.Backend || {};
// TODO: factor this out as a common method with other backends // TODO: factor this out as a common method with other backends
var objs = _.map(dataset._dataCache, function (d) { var objs = _.map(dataset._dataCache, function (d) {
var obj = {}; var obj = {};
_.each(_.zip(fields, d), function (x) { obj[x[0]] = x[1]; }) _.each(_.zip(fields, d), function (x) {
obj[x[0]] = x[1];
});
return obj; return obj;
}); });
dfd.resolve(this._docsToQueryResult(objs)); dfd.resolve(this._docsToQueryResult(objs));
@@ -101,8 +104,8 @@ this.recline.Backend = this.recline.Backend || {};
if (gdocsSpreadsheet.feed.entry.length > 0) { if (gdocsSpreadsheet.feed.entry.length > 0) {
for (var k in gdocsSpreadsheet.feed.entry[0]) { for (var k in gdocsSpreadsheet.feed.entry[0]) {
if (k.substr(0, 3) == 'gsx') { if (k.substr(0, 3) == 'gsx') {
var col = k.substr(4) var col = k.substr(4);
results.field.push(col); results.field.push(col);
} }
} }
} }

View File

@@ -15,7 +15,7 @@ this.recline.Backend = this.recline.Backend || {};
}; };
reader.onerror = function (e) { reader.onerror = function (e) {
alert('Failed to load file. Code: ' + e.target.error.code); alert('Failed to load file. Code: ' + e.target.error.code);
} };
reader.readAsText(file); reader.readAsText(file);
}; };
@@ -33,7 +33,7 @@ this.recline.Backend = this.recline.Backend || {};
}); });
var dataset = recline.Backend.createDataset(data, fields); var dataset = recline.Backend.createDataset(data, fields);
return dataset; return dataset;
} };
// Converts a Comma Separated Values string into an array of arrays. // Converts a Comma Separated Values string into an array of arrays.
// Each line in the CSV becomes an array. // Each line in the CSV becomes an array.

View File

@@ -15,7 +15,7 @@ this.recline.Backend = this.recline.Backend || {};
// If not defined (or id not provided) id will be autogenerated. // If not defined (or id not provided) id will be autogenerated.
my.createDataset = function(data, fields, metadata) { my.createDataset = function(data, fields, metadata) {
if (!metadata) { if (!metadata) {
var metadata = {}; metadata = {};
} }
if (!metadata.id) { if (!metadata.id) {
metadata.id = String(Math.floor(Math.random() * 100000000) + 1); metadata.id = String(Math.floor(Math.random() * 100000000) + 1);
@@ -78,8 +78,8 @@ this.recline.Backend = this.recline.Backend || {};
}, },
sync: function(method, model, options) { sync: function(method, model, options) {
var self = this; var self = this;
var dfd = $.Deferred();
if (method === "read") { if (method === "read") {
var dfd = $.Deferred();
if (model.__type__ == 'Dataset') { if (model.__type__ == 'Dataset') {
var rawDataset = this.datasets[model.id]; var rawDataset = this.datasets[model.id];
model.set(rawDataset.metadata); model.set(rawDataset.metadata);
@@ -89,7 +89,6 @@ this.recline.Backend = this.recline.Backend || {};
} }
return dfd.promise(); return dfd.promise();
} else if (method === 'update') { } else if (method === 'update') {
var dfd = $.Deferred();
if (model.__type__ == 'Document') { if (model.__type__ == 'Document') {
_.each(self.datasets[model.dataset.id].documents, function(doc, idx) { _.each(self.datasets[model.dataset.id].documents, function(doc, idx) {
if(doc.id === model.id) { if(doc.id === model.id) {
@@ -100,7 +99,6 @@ this.recline.Backend = this.recline.Backend || {};
} }
return dfd.promise(); return dfd.promise();
} else if (method === 'delete') { } else if (method === 'delete') {
var dfd = $.Deferred();
if (model.__type__ == 'Document') { if (model.__type__ == 'Document') {
var rawDataset = self.datasets[model.dataset.id]; var rawDataset = self.datasets[model.dataset.id];
var newdocs = _.reject(rawDataset.documents, function(doc) { var newdocs = _.reject(rawDataset.documents, function(doc) {

View File

@@ -179,13 +179,27 @@ my.Field = Backbone.Model.extend({
if ('0' in data) { if ('0' in data) {
throw new Error('Looks like you did not pass a proper hash with id to Field constructor'); throw new Error('Looks like you did not pass a proper hash with id to Field constructor');
} }
if (this.attributes.label == null) { if (this.attributes.label === null) {
this.set({label: this.id}); this.set({label: this.id});
} }
if (options) { if (options) {
this.renderer = options.renderer; this.renderer = options.renderer;
this.deriver = options.deriver; this.deriver = options.deriver;
} }
if (!this.renderer) {
this.renderer = this.defaultRenderers[this.get('type')];
}
},
defaultRenderers: {
object: function(val, field, doc) {
return JSON.stringify(val);
},
'float': function(val, field, doc) {
var format = field.get('format');
if (format === 'percentage') {
return val + '%';
}
}
} }
}); });
@@ -217,7 +231,7 @@ my.FieldList = Backbone.Collection.extend({
// * query: Query in ES Query DSL <http://www.elasticsearch.org/guide/reference/api/search/query.html> // * query: Query in ES Query DSL <http://www.elasticsearch.org/guide/reference/api/search/query.html>
// * filter: See filters and <a href="http://www.elasticsearch.org/guide/reference/query-dsl/filtered-query.html">Filtered Query</a> // * filter: See filters and <a href="http://www.elasticsearch.org/guide/reference/query-dsl/filtered-query.html">Filtered Query</a>
// * fields: set of fields to return - http://www.elasticsearch.org/guide/reference/api/search/fields.html // * fields: set of fields to return - http://www.elasticsearch.org/guide/reference/api/search/fields.html
// * facets: TODO - see http://www.elasticsearch.org/guide/reference/api/search/facets/ // * facets: specification of facets - see http://www.elasticsearch.org/guide/reference/api/search/facets/
// //
// Additions: // Additions:
// //
@@ -245,13 +259,13 @@ my.FieldList = Backbone.Collection.extend({
my.Query = Backbone.Model.extend({ my.Query = Backbone.Model.extend({
defaults: function() { defaults: function() {
return { return {
size: 100 size: 100,
, from: 0 from: 0,
, facets: {} facets: {},
// <http://www.elasticsearch.org/guide/reference/query-dsl/and-filter.html> // <http://www.elasticsearch.org/guide/reference/query-dsl/and-filter.html>
// , filter: {} // , filter: {}
, filters: [] filters: []
} };
}, },
// #### addTermFilter // #### addTermFilter
// //
@@ -297,6 +311,17 @@ my.Query = Backbone.Model.extend({
}; };
this.set({facets: facets}, {silent: true}); this.set({facets: facets}, {silent: true});
this.trigger('facet:add', this); this.trigger('facet:add', this);
},
addHistogramFacet: function(fieldId) {
var facets = this.get('facets');
facets[fieldId] = {
date_histogram: {
field: fieldId,
interval: 'day'
}
};
this.set({facets: facets}, {silent: true});
this.trigger('facet:add', this);
} }
}); });
@@ -347,7 +372,7 @@ my.Facet = Backbone.Model.extend({
other: 0, other: 0,
missing: 0, missing: 0,
terms: [] terms: []
} };
} }
}); });

View File

@@ -2,8 +2,8 @@
var util = function() { var util = function() {
var templates = { var templates = {
transformActions: '<li><a data-action="transform" class="menuAction" href="JavaScript:void(0);">Global transform...</a></li>' transformActions: '<li><a data-action="transform" class="menuAction" href="JavaScript:void(0);">Global transform...</a></li>',
, cellEditor: ' \ cellEditor: ' \
<div class="menu-container data-table-cell-editor"> \ <div class="menu-container data-table-cell-editor"> \
<textarea class="data-table-cell-editor-editor" bind="textarea">{{value}}</textarea> \ <textarea class="data-table-cell-editor-editor" bind="textarea">{{value}}</textarea> \
<div id="data-table-cell-editor-actions"> \ <div id="data-table-cell-editor-actions"> \
@@ -13,8 +13,8 @@ var util = function() {
</div> \ </div> \
</div> \ </div> \
</div> \ </div> \
' ',
, editPreview: ' \ editPreview: ' \
<div class="expression-preview-table-wrapper"> \ <div class="expression-preview-table-wrapper"> \
<table> \ <table> \
<thead> \ <thead> \
@@ -63,7 +63,7 @@ var util = function() {
function registerEmitter() { function registerEmitter() {
var Emitter = function(obj) { var Emitter = function(obj) {
this.emit = function(obj, channel) { this.emit = function(obj, channel) {
if (!channel) var channel = 'data'; if (!channel) channel = 'data';
this.trigger(channel, obj); this.trigger(channel, obj);
}; };
}; };
@@ -80,7 +80,7 @@ var util = function() {
104: "8", 105: "9", 106: "*", 107: "+", 109: "-", 110: ".", 111 : "/", 104: "8", 105: "9", 106: "*", 107: "+", 109: "-", 110: ".", 111 : "/",
112: "f1", 113: "f2", 114: "f3", 115: "f4", 116: "f5", 117: "f6", 118: "f7", 119: "f8", 112: "f1", 113: "f2", 114: "f3", 115: "f4", 116: "f5", 117: "f6", 118: "f7", 119: "f8",
120: "f9", 121: "f10", 122: "f11", 123: "f12", 144: "numlock", 145: "scroll", 191: "/", 224: "meta" 120: "f9", 121: "f10", 122: "f11", 123: "f12", 144: "numlock", 145: "scroll", 191: "/", 224: "meta"
} };
window.addEventListener("keyup", function(e) { window.addEventListener("keyup", function(e) {
var pressed = shortcuts[e.keyCode]; var pressed = shortcuts[e.keyCode];
if(_.include(keys, pressed)) app.emitter.emit("keyup", pressed); if(_.include(keys, pressed)) app.emitter.emit("keyup", pressed);
@@ -126,10 +126,11 @@ var util = function() {
if ( !options ) options = {data: {}}; if ( !options ) options = {data: {}};
if ( !options.data ) options = {data: options}; if ( !options.data ) options = {data: options};
var html = $.mustache( templates[template], options.data ); var html = $.mustache( templates[template], options.data );
var targetDom = null;
if (target instanceof jQuery) { if (target instanceof jQuery) {
var targetDom = target; targetDom = target;
} else { } else {
var targetDom = $( "." + target + ":first" ); targetDom = $( "." + target + ":first" );
} }
if( options.append ) { if( options.append ) {
targetDom.append( html ); targetDom.append( html );

View File

@@ -80,10 +80,10 @@ my.FlotGraph = Backbone.View.extend({
', ',
events: { events: {
'change form select': 'onEditorSubmit' 'change form select': 'onEditorSubmit',
, 'click .editor-add': 'addSeries' 'click .editor-add': 'addSeries',
, 'click .action-remove-series': 'removeSeries' 'click .action-remove-series': 'removeSeries',
, 'click .action-toggle-help': 'toggleHelp' 'click .action-toggle-help': 'toggleHelp'
}, },
initialize: function(options, config) { initialize: function(options, config) {
@@ -129,12 +129,12 @@ my.FlotGraph = Backbone.View.extend({
var series = this.$series.map(function () { var series = this.$series.map(function () {
return $(this).val(); return $(this).val();
}); });
this.chartConfig.series = $.makeArray(series) this.chartConfig.series = $.makeArray(series);
this.chartConfig.group = this.el.find('.editor-group select').val(); this.chartConfig.group = this.el.find('.editor-group select').val();
this.chartConfig.graphType = this.el.find('.editor-type select').val(); this.chartConfig.graphType = this.el.find('.editor-type select').val();
// update navigation // update navigation
var qs = my.parseHashQueryString(); var qs = my.parseHashQueryString();
qs['graph'] = JSON.stringify(this.chartConfig); qs.graph = JSON.stringify(this.chartConfig);
my.setHashQueryString(qs); my.setHashQueryString(qs);
this.redraw(); this.redraw();
}, },
@@ -147,8 +147,8 @@ my.FlotGraph = Backbone.View.extend({
// Uncaught Invalid dimensions for plot, width = 0, height = 0 // 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' // * 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]); var areWeVisible = !jQuery.expr.filters.hidden(this.el[0]);
if ((!areWeVisible || this.model.currentDocuments.length == 0)) { if ((!areWeVisible || this.model.currentDocuments.length === 0)) {
return return;
} }
var series = this.createSeries(); var series = this.createSeries();
var options = this.getGraphOptions(this.chartConfig.graphType); var options = this.getGraphOptions(this.chartConfig.graphType);
@@ -181,7 +181,7 @@ my.FlotGraph = Backbone.View.extend({
} }
} }
return val; return val;
} };
// TODO: we should really use tickFormatter and 1 interval ticks if (and // TODO: we should really use tickFormatter and 1 interval ticks if (and
// only if) x-axis values are non-numeric // only if) x-axis values are non-numeric
// However, that is non-trivial to work out from a dataset (datasets may // However, that is non-trivial to work out from a dataset (datasets may
@@ -191,21 +191,21 @@ my.FlotGraph = Backbone.View.extend({
series: { series: {
lines: { show: true } lines: { show: true }
} }
} },
, points: { points: {
series: { series: {
points: { show: true } points: { show: true }
}, },
grid: { hoverable: true, clickable: true } grid: { hoverable: true, clickable: true }
} },
, 'lines-and-points': { 'lines-and-points': {
series: { series: {
points: { show: true }, points: { show: true },
lines: { show: true } lines: { show: true }
}, },
grid: { hoverable: true, clickable: true } grid: { hoverable: true, clickable: true }
} },
, bars: { bars: {
series: { series: {
lines: {show: false}, lines: {show: false},
bars: { bars: {
@@ -225,7 +225,7 @@ my.FlotGraph = Backbone.View.extend({
max: self.model.currentDocuments.length - 0.5 max: self.model.currentDocuments.length - 0.5
} }
} }
} };
return options[typeId]; return options[typeId];
}, },

View File

@@ -25,10 +25,10 @@ my.DataGrid = Backbone.View.extend({
}, },
events: { events: {
'click .column-header-menu': 'onColumnHeaderClick' 'click .column-header-menu': 'onColumnHeaderClick',
, 'click .row-header-menu': 'onRowHeaderClick' 'click .row-header-menu': 'onRowHeaderClick',
, 'click .root-header-menu': 'onRootHeaderClick' 'click .root-header-menu': 'onRootHeaderClick',
, 'click .data-table-menu li a': 'onMenuClick' 'click .data-table-menu li a': 'onMenuClick'
}, },
// TODO: delete or re-enable (currently this code is not used from anywhere except deprecated or disabled methods (see above)). // TODO: delete or re-enable (currently this code is not used from anywhere except deprecated or disabled methods (see above)).
@@ -67,33 +67,35 @@ my.DataGrid = Backbone.View.extend({
var self = this; var self = this;
e.preventDefault(); e.preventDefault();
var actions = { var actions = {
bulkEdit: function() { self.showTransformColumnDialog('bulkEdit', {name: self.state.currentColumn}) }, bulkEdit: function() { self.showTransformColumnDialog('bulkEdit', {name: self.state.currentColumn}); },
facet: function() { facet: function() {
self.model.queryState.addFacet(self.state.currentColumn); self.model.queryState.addFacet(self.state.currentColumn);
}, },
facet_histogram: function() {
self.model.queryState.addHistogramFacet(self.state.currentColumn);
},
filter: function() { filter: function() {
self.model.queryState.addTermFilter(self.state.currentColumn, ''); self.model.queryState.addTermFilter(self.state.currentColumn, '');
}, },
transform: function() { self.showTransformDialog('transform') }, transform: function() { self.showTransformDialog('transform'); },
sortAsc: function() { self.setColumnSort('asc') }, sortAsc: function() { self.setColumnSort('asc'); },
sortDesc: function() { self.setColumnSort('desc') }, sortDesc: function() { self.setColumnSort('desc'); },
hideColumn: function() { self.hideColumn() }, hideColumn: function() { self.hideColumn(); },
showColumn: function() { self.showColumn(e) }, showColumn: function() { self.showColumn(e); },
deleteRow: function() { deleteRow: function() {
var doc = _.find(self.model.currentDocuments.models, function(doc) { var doc = _.find(self.model.currentDocuments.models, function(doc) {
// important this is == as the currentRow will be string (as comes // important this is == as the currentRow will be string (as comes
// from DOM) while id may be int // from DOM) while id may be int
return doc.id == self.state.currentRow return doc.id == self.state.currentRow;
}); });
doc.destroy().then(function() { doc.destroy().then(function() {
self.model.currentDocuments.remove(doc); self.model.currentDocuments.remove(doc);
my.notify("Row deleted successfully"); my.notify("Row deleted successfully");
}) }).fail(function(err) {
.fail(function(err) { my.notify("Errorz! " + err);
my.notify("Errorz! " + err) });
})
} }
} };
actions[$(e.target).attr('data-action')](); actions[$(e.target).attr('data-action')]();
}, },
@@ -109,7 +111,7 @@ my.DataGrid = Backbone.View.extend({
$el.append(view.el); $el.append(view.el);
util.observeExit($el, function() { util.observeExit($el, function() {
util.hide('dialog'); util.hide('dialog');
}) });
$('.dialog').draggable({ handle: '.dialog-header', cursor: 'move' }); $('.dialog').draggable({ handle: '.dialog-header', cursor: 'move' });
}, },
@@ -123,7 +125,7 @@ my.DataGrid = Backbone.View.extend({
$el.append(view.el); $el.append(view.el);
util.observeExit($el, function() { util.observeExit($el, function() {
util.hide('dialog'); util.hide('dialog');
}) });
$('.dialog').draggable({ handle: '.dialog-header', cursor: 'move' }); $('.dialog').draggable({ handle: '.dialog-header', cursor: 'move' });
}, },
@@ -164,7 +166,8 @@ my.DataGrid = Backbone.View.extend({
<div class="btn-group column-header-menu"> \ <div class="btn-group column-header-menu"> \
<a class="btn dropdown-toggle" data-toggle="dropdown"><i class="icon-cog"></i><span class="caret"></span></a> \ <a class="btn dropdown-toggle" data-toggle="dropdown"><i class="icon-cog"></i><span class="caret"></span></a> \
<ul class="dropdown-menu data-table-menu pull-right"> \ <ul class="dropdown-menu data-table-menu pull-right"> \
<li><a data-action="facet" href="JavaScript:void(0);">Facet on this Field</a></li> \ <li><a data-action="facet" href="JavaScript:void(0);">Term Facet</a></li> \
<li><a data-action="facet_histogram" href="JavaScript:void(0);">Date Histogram Facet</a></li> \
<li><a data-action="filter" href="JavaScript:void(0);">Text Filter</a></li> \ <li><a data-action="filter" href="JavaScript:void(0);">Text Filter</a></li> \
<li class="divider"></li> \ <li class="divider"></li> \
<li><a data-action="sortAsc" href="JavaScript:void(0);">Sort ascending</a></li> \ <li><a data-action="sortAsc" href="JavaScript:void(0);">Sort ascending</a></li> \
@@ -185,10 +188,10 @@ my.DataGrid = Backbone.View.extend({
', ',
toTemplateJSON: function() { toTemplateJSON: function() {
var modelData = this.model.toJSON() var modelData = this.model.toJSON();
modelData.notEmpty = ( this.fields.length > 0 ) modelData.notEmpty = ( this.fields.length > 0 );
// TODO: move this sort of thing into a toTemplateJSON method on Dataset? // TODO: move this sort of thing into a toTemplateJSON method on Dataset?
modelData.fields = _.map(this.fields, function(field) { return field.toJSON() }); modelData.fields = _.map(this.fields, function(field) { return field.toJSON(); });
return modelData; return modelData;
}, },
render: function() { render: function() {
@@ -208,7 +211,7 @@ my.DataGrid = Backbone.View.extend({
}); });
newView.render(); newView.render();
}); });
this.el.toggleClass('no-hidden', (self.hiddenFields.length == 0)); this.el.toggleClass('no-hidden', (self.hiddenFields.length === 0));
return this; return this;
} }
}); });
@@ -267,9 +270,9 @@ my.DataGridRow = Backbone.View.extend({
return { return {
field: field.id, field: field.id,
value: doc.getFieldValue(field) value: doc.getFieldValue(field)
} };
}) });
return { id: this.id, cells: cellData } return { id: this.id, cells: cellData };
}, },
render: function() { render: function() {

View File

@@ -59,7 +59,7 @@ my.Map = Backbone.View.extend({
if (!self.mapReady){ if (!self.mapReady){
self._setupMap(); self._setupMap();
} }
self.redraw() self.redraw();
}); });
return this; return this;
@@ -79,9 +79,9 @@ my.Map = Backbone.View.extend({
if (feature){ if (feature){
// Build popup contents // Build popup contents
// TODO: mustache? // TODO: mustache?
html = '' var html = '';
for (key in doc.attributes){ for (key in doc.attributes) {
html += '<div><strong>' + key + '</strong>: '+ doc.attributes[key] + '</div>' html += '<div><strong>' + key + '</strong>: '+ doc.attributes[key] + '</div>';
} }
feature.properties = {popupContent: html}; feature.properties = {popupContent: html};
@@ -105,9 +105,9 @@ my.Map = Backbone.View.extend({
type: 'Point', type: 'Point',
coordinates: [ coordinates: [
doc.attributes[this._lonFieldName], doc.attributes[this._lonFieldName],
doc.attributes[this._latFieldName], doc.attributes[this._latFieldName]
] ]
} };
} }
return null; return null;
} }

View File

@@ -132,8 +132,8 @@ my.ColumnTransform = Backbone.View.extend({
', ',
events: { events: {
'click .okButton': 'onSubmit' 'click .okButton': 'onSubmit',
, 'keydown .expression-preview-code': 'onEditorKeydown' 'keydown .expression-preview-code': 'onEditorKeydown'
}, },
initialize: function() { initialize: function() {
@@ -143,7 +143,7 @@ my.ColumnTransform = Backbone.View.extend({
render: function() { render: function() {
var htmls = $.mustache(this.template, var htmls = $.mustache(this.template,
{name: this.state.currentColumn} {name: this.state.currentColumn}
) );
this.el.html(htmls); this.el.html(htmls);
// Put in the basic (identity) transform script // Put in the basic (identity) transform script
// TODO: put this into the template? // TODO: put this into the template?
@@ -181,7 +181,7 @@ my.ColumnTransform = Backbone.View.extend({
_.each(toUpdate, function(editedDoc) { _.each(toUpdate, function(editedDoc) {
var realDoc = self.model.currentDocuments.get(editedDoc.id); var realDoc = self.model.currentDocuments.get(editedDoc.id);
realDoc.set(editedDoc); realDoc.set(editedDoc);
realDoc.save().then(onCompletedUpdate).fail(onCompletedUpdate) realDoc.save().then(onCompletedUpdate).fail(onCompletedUpdate);
}); });
}, },

View File

@@ -123,7 +123,7 @@ my.DataExplorer = Backbone.View.extend({
my.notify('Data loaded', {category: 'success'}); my.notify('Data loaded', {category: 'success'});
// update navigation // update navigation
var qs = my.parseHashQueryString(); var qs = my.parseHashQueryString();
qs['reclineQuery'] = JSON.stringify(self.model.queryState.toJSON()); qs.reclineQuery = JSON.stringify(self.model.queryState.toJSON());
var out = my.getNewHashForQueryString(qs); var out = my.getNewHashForQueryString(qs);
self.router.navigate(out); self.router.navigate(out);
}); });
@@ -172,7 +172,7 @@ my.DataExplorer = Backbone.View.extend({
$(this.el).html(template); $(this.el).html(template);
var $dataViewContainer = this.el.find('.data-view-container'); var $dataViewContainer = this.el.find('.data-view-container');
_.each(this.pageViews, function(view, pageName) { _.each(this.pageViews, function(view, pageName) {
$dataViewContainer.append(view.view.el) $dataViewContainer.append(view.view.el);
}); });
var queryEditor = new my.QueryEditor({ var queryEditor = new my.QueryEditor({
model: this.model.queryState model: this.model.queryState
@@ -250,8 +250,8 @@ my.QueryEditor = Backbone.View.extend({
', ',
events: { events: {
'submit form': 'onFormSubmit' 'submit form': 'onFormSubmit',
, 'click .action-pagination-update': 'onPaginationUpdate' 'click .action-pagination-update': 'onPaginationUpdate'
}, },
initialize: function() { initialize: function() {
@@ -270,10 +270,11 @@ my.QueryEditor = Backbone.View.extend({
onPaginationUpdate: function(e) { onPaginationUpdate: function(e) {
e.preventDefault(); e.preventDefault();
var $el = $(e.target); var $el = $(e.target);
var newFrom = 0;
if ($el.parent().hasClass('prev')) { if ($el.parent().hasClass('prev')) {
var newFrom = this.model.get('from') - Math.max(0, this.model.get('size')); newFrom = this.model.get('from') - Math.max(0, this.model.get('size'));
} else { } else {
var newFrom = this.model.get('from') + this.model.get('size'); newFrom = this.model.get('from') + this.model.get('size');
} }
this.model.set({from: newFrom}); this.model.set({from: newFrom});
}, },
@@ -346,7 +347,7 @@ my.FilterEditor = Backbone.View.extend({
fieldId: fieldId, fieldId: fieldId,
label: fieldId, label: fieldId,
value: filter.term[fieldId] value: filter.term[fieldId]
} };
}); });
var out = $.mustache(this.template, tmplData); var out = $.mustache(this.template, tmplData);
this.el.html(out); this.el.html(out);
@@ -399,6 +400,9 @@ my.FacetViewer = Backbone.View.extend({
{{#terms}} \ {{#terms}} \
<li><a class="facet-choice js-facet-filter" data-value="{{term}}">{{term}} ({{count}})</a></li> \ <li><a class="facet-choice js-facet-filter" data-value="{{term}}">{{term}} ({{count}})</a></li> \
{{/terms}} \ {{/terms}} \
{{#entries}} \
<li><a class="facet-choice js-facet-filter" data-value="{{time}}">{{term}} ({{count}})</a></li> \
{{/entries}} \
</ul> \ </ul> \
</div> \ </div> \
{{/facets}} \ {{/facets}} \
@@ -421,6 +425,15 @@ my.FacetViewer = Backbone.View.extend({
facets: this.model.facets.toJSON(), facets: this.model.facets.toJSON(),
fields: this.model.fields.toJSON() fields: this.model.fields.toJSON()
}; };
tmplData.facets = _.map(tmplData.facets, function(facet) {
if (facet._type === 'date_histogram') {
facet.entries = _.map(facet.entries, function(entry) {
entry.term = new Date(entry.time).toDateString();
return entry;
});
}
return facet;
});
var templated = $.mustache(this.template, tmplData); var templated = $.mustache(this.template, tmplData);
this.el.html(templated); this.el.html(templated);
// are there actually any facets to show? // are there actually any facets to show?
@@ -450,15 +463,15 @@ var urlPathRegex = /^([^?]+)(\?.*)?/;
// Parse the Hash section of a URL into path and query string // Parse the Hash section of a URL into path and query string
my.parseHashUrl = function(hashUrl) { my.parseHashUrl = function(hashUrl) {
var parsed = urlPathRegex.exec(hashUrl); var parsed = urlPathRegex.exec(hashUrl);
if (parsed == null) { if (parsed === null) {
return {}; return {};
} else { } else {
return { return {
path: parsed[1], path: parsed[1],
query: parsed[2] || '' query: parsed[2] || ''
} };
} }
} };
// Parse a URL query string (?xyz=abc...) into a dictionary. // Parse a URL query string (?xyz=abc...) into a dictionary.
my.parseQueryString = function(q) { my.parseQueryString = function(q) {
@@ -479,13 +492,13 @@ my.parseQueryString = function(q) {
urlParams[d(e[1])] = d(e[2]); urlParams[d(e[1])] = d(e[2]);
} }
return urlParams; return urlParams;
} };
// Parse the query string out of the URL hash // Parse the query string out of the URL hash
my.parseHashQueryString = function() { my.parseHashQueryString = function() {
q = my.parseHashUrl(window.location.hash).query; q = my.parseHashUrl(window.location.hash).query;
return my.parseQueryString(q); return my.parseQueryString(q);
} };
// Compse a Query String // Compse a Query String
my.composeQueryString = function(queryParams) { my.composeQueryString = function(queryParams) {
@@ -496,7 +509,7 @@ my.composeQueryString = function(queryParams) {
}); });
queryString += items.join('&'); queryString += items.join('&');
return queryString; return queryString;
} };
my.getNewHashForQueryString = function(queryParams) { my.getNewHashForQueryString = function(queryParams) {
var queryPart = my.composeQueryString(queryParams); var queryPart = my.composeQueryString(queryParams);
@@ -506,11 +519,11 @@ my.getNewHashForQueryString = function(queryParams) {
} else { } else {
return queryPart; return queryPart;
} }
} };
my.setHashQueryString = function(queryParams) { my.setHashQueryString = function(queryParams) {
window.location.hash = my.getNewHashForQueryString(queryParams); window.location.hash = my.getNewHashForQueryString(queryParams);
} };
// ## notify // ## notify
// //
@@ -520,7 +533,7 @@ my.setHashQueryString = function(queryParams) {
// * persist: if true alert is persistent, o/w hidden after 3s (default = false) // * persist: if true alert is persistent, o/w hidden after 3s (default = false)
// * loader: if true show loading spinner // * loader: if true show loading spinner
my.notify = function(message, options) { my.notify = function(message, options) {
if (!options) var options = {}; if (!options) options = {};
var tmplData = _.extend({ var tmplData = _.extend({
msg: message, msg: message,
category: 'warning' category: 'warning'
@@ -542,7 +555,7 @@ my.notify = function(message, options) {
}); });
}, 1000); }, 1000);
} }
} };
// ## clearNotifications // ## clearNotifications
// //
@@ -550,7 +563,7 @@ my.notify = function(message, options) {
my.clearNotifications = function() { my.clearNotifications = function() {
var $notifications = $('.recline-data-explorer .alert-messages .alert'); var $notifications = $('.recline-data-explorer .alert-messages .alert');
$notifications.remove(); $notifications.remove();
} };
})(jQuery, recline.View); })(jQuery, recline.View);

View File

@@ -38,7 +38,20 @@ test('Field: basics', function () {
equal('XX', out[0].label); equal('XX', out[0].label);
}); });
test('Field: deriver and renderer', function () { test('Field: default renderers', function () {
var doc = new recline.Model.Document({x: 12.3, myobject: {a: 1, b: 2}});
var field = new recline.Model.Field({id: 'myobject', type: 'object'});
var out = doc.getFieldValue(field);
var exp = '{"a":1,"b":2}';
equal(out, exp);
var field = new recline.Model.Field({id: 'x', type: 'float', format: 'percentage'});
var out = doc.getFieldValue(field);
var exp = '12.3%';
equal(out, exp);
});
test('Field: custom deriver and renderer', function () {
var doc = new recline.Model.Document({x: 123}); var doc = new recline.Model.Document({x: 123});
var cellRenderer = function(value, field) { var cellRenderer = function(value, field) {
return '<span class="field-' + field.id + '">' + value + '</span>'; return '<span class="field-' + field.id + '">' + value + '</span>';