Merge branch 'master' into gh-pages
This commit is contained in:
64
dist/recline.js
vendored
64
dist/recline.js
vendored
@@ -2755,12 +2755,19 @@ this.recline = this.recline || {};
|
|||||||
this.recline.View = this.recline.View || {};
|
this.recline.View = this.recline.View || {};
|
||||||
|
|
||||||
(function($, my) {
|
(function($, my) {
|
||||||
|
// turn off unnecessary logging from VMM Timeline
|
||||||
|
VMM.debug = false;
|
||||||
|
|
||||||
|
// ## Timeline
|
||||||
|
//
|
||||||
|
// Timeline view using http://timeline.verite.co/
|
||||||
my.Timeline = Backbone.View.extend({
|
my.Timeline = Backbone.View.extend({
|
||||||
tagName: 'div',
|
tagName: 'div',
|
||||||
className: 'recline-timeline',
|
|
||||||
|
|
||||||
template: ' \
|
template: ' \
|
||||||
|
<div class="recline-timeline"> \
|
||||||
<div id="vmm-timeline-id"></div> \
|
<div id="vmm-timeline-id"></div> \
|
||||||
|
</div> \
|
||||||
',
|
',
|
||||||
|
|
||||||
// These are the default (case-insensitive) names of field that are used if found.
|
// These are the default (case-insensitive) names of field that are used if found.
|
||||||
@@ -2775,6 +2782,7 @@ my.Timeline = Backbone.View.extend({
|
|||||||
this.timeline = new VMM.Timeline();
|
this.timeline = new VMM.Timeline();
|
||||||
this._timelineIsInitialized = false;
|
this._timelineIsInitialized = false;
|
||||||
this.bind('view:show', function() {
|
this.bind('view:show', function() {
|
||||||
|
// only call _initTimeline once view in DOM as Timeline uses $ internally to look up element
|
||||||
if (self._timelineIsInitialized === false) {
|
if (self._timelineIsInitialized === false) {
|
||||||
self._initTimeline();
|
self._initTimeline();
|
||||||
}
|
}
|
||||||
@@ -2794,6 +2802,11 @@ my.Timeline = Backbone.View.extend({
|
|||||||
this.state = new recline.Model.ObjectState(stateData);
|
this.state = new recline.Model.ObjectState(stateData);
|
||||||
this._setupTemporalField();
|
this._setupTemporalField();
|
||||||
this.render();
|
this.render();
|
||||||
|
// can only call _initTimeline once view in DOM as Timeline uses $
|
||||||
|
// internally to look up element
|
||||||
|
if ($(this.elementId).length > 0) {
|
||||||
|
this._initTimeline();
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
render: function() {
|
render: function() {
|
||||||
@@ -2803,9 +2816,12 @@ my.Timeline = Backbone.View.extend({
|
|||||||
},
|
},
|
||||||
|
|
||||||
_initTimeline: function() {
|
_initTimeline: function() {
|
||||||
|
var $timeline = this.el.find(this.elementId);
|
||||||
// set width explicitly o/w timeline goes wider that screen for some reason
|
// set width explicitly o/w timeline goes wider that screen for some reason
|
||||||
this.el.find(this.elementId).width(this.el.parent().width());
|
var width = Math.max(this.el.width(), this.el.find('.recline-timeline').width());
|
||||||
// only call _initTimeline once view in DOM as Timeline uses $ internally to look up element
|
if (width) {
|
||||||
|
$timeline.width(width);
|
||||||
|
}
|
||||||
var config = {};
|
var config = {};
|
||||||
var data = this._timelineJSON();
|
var data = this._timelineJSON();
|
||||||
this.timeline.init(data, this.elementId, config);
|
this.timeline.init(data, this.elementId, config);
|
||||||
@@ -2819,6 +2835,30 @@ my.Timeline = Backbone.View.extend({
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
// Convert record to JSON for timeline
|
||||||
|
//
|
||||||
|
// Designed to be overridden in client apps
|
||||||
|
convertRecord: function(record, fields) {
|
||||||
|
return this._convertRecord(record, fields);
|
||||||
|
},
|
||||||
|
|
||||||
|
// Internal method to generate a Timeline formatted entry
|
||||||
|
_convertRecord: function(record, fields) {
|
||||||
|
var start = this._parseDate(record.get(this.state.get('startField')));
|
||||||
|
var end = this._parseDate(record.get(this.state.get('endField')));
|
||||||
|
if (start) {
|
||||||
|
var tlEntry = {
|
||||||
|
"startDate": start,
|
||||||
|
"endDate": end,
|
||||||
|
"headline": String(record.get('title') || ''),
|
||||||
|
"text": record.get('description') || record.summary()
|
||||||
|
};
|
||||||
|
return tlEntry;
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
_timelineJSON: function() {
|
_timelineJSON: function() {
|
||||||
var self = this;
|
var self = this;
|
||||||
var out = {
|
var out = {
|
||||||
@@ -2829,17 +2869,10 @@ my.Timeline = Backbone.View.extend({
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
this.model.currentRecords.each(function(doc) {
|
this.model.currentRecords.each(function(record) {
|
||||||
var start = self._parseDate(doc.get(self.state.get('startField')));
|
var newEntry = self.convertRecord(record, self.fields);
|
||||||
var end = self._parseDate(doc.get(self.state.get('endField')));
|
if (newEntry) {
|
||||||
if (start) {
|
out.timeline.date.push(newEntry);
|
||||||
var tlEntry = {
|
|
||||||
"startDate": start,
|
|
||||||
"endDate": end,
|
|
||||||
"headline": String(doc.get('title') || ''),
|
|
||||||
"text": doc.summary()
|
|
||||||
};
|
|
||||||
out.timeline.date.push(tlEntry);
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
// if no entries create a placeholder entry to prevent Timeline crashing with error
|
// if no entries create a placeholder entry to prevent Timeline crashing with error
|
||||||
@@ -2854,6 +2887,9 @@ my.Timeline = Backbone.View.extend({
|
|||||||
},
|
},
|
||||||
|
|
||||||
_parseDate: function(date) {
|
_parseDate: function(date) {
|
||||||
|
if (!date) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
var out = date.trim();
|
var out = date.trim();
|
||||||
out = out.replace(/(\d)th/g, '$1');
|
out = out.replace(/(\d)th/g, '$1');
|
||||||
out = out.replace(/(\d)st/g, '$1');
|
out = out.replace(/(\d)st/g, '$1');
|
||||||
|
|||||||
@@ -87,44 +87,57 @@ this.recline.Backend.ElasticSearch = this.recline.Backend.ElasticSearch || {};
|
|||||||
};
|
};
|
||||||
|
|
||||||
this._normalizeQuery = function(queryObj) {
|
this._normalizeQuery = function(queryObj) {
|
||||||
var out = queryObj && queryObj.toJSON ? queryObj.toJSON() : _.extend({}, queryObj);
|
var self = this;
|
||||||
if (out.q !== undefined && out.q.trim() === '') {
|
var queryInfo = (queryObj && queryObj.toJSON) ? queryObj.toJSON() : _.extend({}, queryObj);
|
||||||
delete out.q;
|
var out = {
|
||||||
|
constant_score: {
|
||||||
|
query: {}
|
||||||
}
|
}
|
||||||
if (!out.q) {
|
};
|
||||||
out.query = {
|
if (!queryInfo.q) {
|
||||||
|
out.constant_score.query = {
|
||||||
match_all: {}
|
match_all: {}
|
||||||
};
|
};
|
||||||
} else {
|
} else {
|
||||||
out.query = {
|
out.constant_score.query = {
|
||||||
query_string: {
|
query_string: {
|
||||||
query: out.q
|
query: queryInfo.q
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
delete out.q;
|
|
||||||
}
|
}
|
||||||
// now do filters (note the *plural*)
|
if (queryInfo.filters && queryInfo.filters.length) {
|
||||||
if (out.filters && out.filters.length) {
|
out.constant_score.filter = {
|
||||||
if (!out.filter) {
|
and: []
|
||||||
out.filter = {};
|
};
|
||||||
}
|
_.each(queryInfo.filters, function(filter) {
|
||||||
if (!out.filter.and) {
|
out.constant_score.filter.and.push(self._convertFilter(filter));
|
||||||
out.filter.and = [];
|
});
|
||||||
}
|
|
||||||
out.filter.and = out.filter.and.concat(out.filters);
|
|
||||||
}
|
|
||||||
if (out.filters !== undefined) {
|
|
||||||
delete out.filters;
|
|
||||||
}
|
}
|
||||||
return out;
|
return out;
|
||||||
};
|
},
|
||||||
|
|
||||||
|
this._convertFilter = function(filter) {
|
||||||
|
var out = {};
|
||||||
|
out[filter.type] = {}
|
||||||
|
if (filter.type === 'term') {
|
||||||
|
out.term[filter.field] = filter.term.toLowerCase();
|
||||||
|
} else if (filter.type === 'geo_distance') {
|
||||||
|
out.geo_distance[filter.field] = filter.point;
|
||||||
|
out.geo_distance.distance = filter.distance;
|
||||||
|
}
|
||||||
|
return out;
|
||||||
|
},
|
||||||
|
|
||||||
// ### query
|
// ### query
|
||||||
//
|
//
|
||||||
// @return deferred supporting promise API
|
// @return deferred supporting promise API
|
||||||
this.query = function(queryObj) {
|
this.query = function(queryObj) {
|
||||||
|
var esQuery = (queryObj && queryObj.toJSON) ? queryObj.toJSON() : _.extend({}, queryObj);
|
||||||
var queryNormalized = this._normalizeQuery(queryObj);
|
var queryNormalized = this._normalizeQuery(queryObj);
|
||||||
var data = {source: JSON.stringify(queryNormalized)};
|
delete esQuery.q;
|
||||||
|
delete esQuery.filters;
|
||||||
|
esQuery.query = queryNormalized;
|
||||||
|
var data = {source: JSON.stringify(esQuery)};
|
||||||
var url = this.endpoint + '/_search';
|
var url = this.endpoint + '/_search';
|
||||||
var jqxhr = recline.Backend.makeRequest({
|
var jqxhr = recline.Backend.makeRequest({
|
||||||
url: url,
|
url: url,
|
||||||
@@ -213,6 +226,12 @@ this.recline.Backend.ElasticSearch = this.recline.Backend.ElasticSearch || {};
|
|||||||
results.hits.facets = results.facets;
|
results.hits.facets = results.facets;
|
||||||
}
|
}
|
||||||
dfd.resolve(results.hits);
|
dfd.resolve(results.hits);
|
||||||
|
}).fail(function(errorObj) {
|
||||||
|
var out = {
|
||||||
|
title: 'Failed: ' + errorObj.status + ' code',
|
||||||
|
message: errorObj.responseText
|
||||||
|
};
|
||||||
|
dfd.reject(out);
|
||||||
});
|
});
|
||||||
return dfd.promise();
|
return dfd.promise();
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -74,8 +74,11 @@ this.recline.Backend.Memory = this.recline.Backend.Memory || {};
|
|||||||
var fieldName = _.keys(sortObj)[0];
|
var fieldName = _.keys(sortObj)[0];
|
||||||
results = _.sortBy(results, function(doc) {
|
results = _.sortBy(results, function(doc) {
|
||||||
var _out = doc[fieldName];
|
var _out = doc[fieldName];
|
||||||
return (sortObj[fieldName].order == 'asc') ? _out : -1*_out;
|
return _out;
|
||||||
});
|
});
|
||||||
|
if (sortObj[fieldName].order == 'desc') {
|
||||||
|
results.reverse();
|
||||||
|
}
|
||||||
});
|
});
|
||||||
var total = results.length;
|
var total = results.length;
|
||||||
var facets = this.computeFacets(results, queryObj);
|
var facets = this.computeFacets(results, queryObj);
|
||||||
@@ -90,10 +93,12 @@ this.recline.Backend.Memory = this.recline.Backend.Memory || {};
|
|||||||
// in place filtering
|
// in place filtering
|
||||||
this._applyFilters = function(results, queryObj) {
|
this._applyFilters = function(results, queryObj) {
|
||||||
_.each(queryObj.filters, function(filter) {
|
_.each(queryObj.filters, function(filter) {
|
||||||
|
// if a term filter ...
|
||||||
|
if (filter.type === 'term') {
|
||||||
results = _.filter(results, function(doc) {
|
results = _.filter(results, function(doc) {
|
||||||
var fieldId = _.keys(filter.term)[0];
|
return (doc[filter.field] == filter.term);
|
||||||
return (doc[fieldId] == filter.term[fieldId]);
|
|
||||||
});
|
});
|
||||||
|
}
|
||||||
});
|
});
|
||||||
return results;
|
return results;
|
||||||
};
|
};
|
||||||
|
|||||||
54
src/model.js
54
src/model.js
@@ -393,7 +393,7 @@ my.FieldList = Backbone.Collection.extend({
|
|||||||
// may just pass this straight through e.g. for an SQL backend this could be
|
// may just pass this straight through e.g. for an SQL backend this could be
|
||||||
// the full SQL query
|
// the full SQL query
|
||||||
//
|
//
|
||||||
// * filters: dict of ElasticSearch filters. These will be and-ed together for
|
// * filters: array of ElasticSearch filters. These will be and-ed together for
|
||||||
// execution.
|
// execution.
|
||||||
//
|
//
|
||||||
// **Examples**
|
// **Examples**
|
||||||
@@ -411,12 +411,44 @@ my.Query = Backbone.Model.extend({
|
|||||||
return {
|
return {
|
||||||
size: 100,
|
size: 100,
|
||||||
from: 0,
|
from: 0,
|
||||||
|
q: '',
|
||||||
facets: {},
|
facets: {},
|
||||||
// <http://www.elasticsearch.org/guide/reference/query-dsl/and-filter.html>
|
|
||||||
// , filter: {}
|
|
||||||
filters: []
|
filters: []
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
_filterTemplates: {
|
||||||
|
term: {
|
||||||
|
type: 'term',
|
||||||
|
field: '',
|
||||||
|
term: ''
|
||||||
|
},
|
||||||
|
geo_distance: {
|
||||||
|
distance: 10,
|
||||||
|
distance_unit: 'km',
|
||||||
|
point: {
|
||||||
|
lon: 0,
|
||||||
|
lat: 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
// ### addFilter
|
||||||
|
//
|
||||||
|
// Add a new filter (appended to the list of filters)
|
||||||
|
//
|
||||||
|
// @param filter an object specifying the filter - see _filterTemplates for examples. If only type is provided will generate a filter by cloning _filterTemplates
|
||||||
|
addFilter: function(filter) {
|
||||||
|
// crude deep copy
|
||||||
|
var ourfilter = JSON.parse(JSON.stringify(filter));
|
||||||
|
// not full specified so use template and over-write
|
||||||
|
if (_.keys(filter).length <= 2) {
|
||||||
|
ourfilter = _.extend(this._filterTemplates[filter.type], ourfilter);
|
||||||
|
}
|
||||||
|
var filters = this.get('filters');
|
||||||
|
filters.push(ourfilter);
|
||||||
|
this.trigger('change:filters:new-blank');
|
||||||
|
},
|
||||||
|
updateFilter: function(index, value) {
|
||||||
|
},
|
||||||
// #### addTermFilter
|
// #### addTermFilter
|
||||||
//
|
//
|
||||||
// Set (update or add) a terms filter to filters
|
// Set (update or add) a terms filter to filters
|
||||||
@@ -436,6 +468,22 @@ my.Query = Backbone.Model.extend({
|
|||||||
this.trigger('change:filters:new-blank');
|
this.trigger('change:filters:new-blank');
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
addGeoDistanceFilter: function(field) {
|
||||||
|
var filters = this.get('filters');
|
||||||
|
var filter = {
|
||||||
|
geo_distance: {
|
||||||
|
distance: '10km',
|
||||||
|
}
|
||||||
|
};
|
||||||
|
filter.geo_distance[field] = {
|
||||||
|
'lon': 0,
|
||||||
|
'lat': 0
|
||||||
|
};
|
||||||
|
filters.push(filter);
|
||||||
|
this.set({filters: filters});
|
||||||
|
// adding a new blank filter and do not want to trigger a new query
|
||||||
|
this.trigger('change:filters:new-blank');
|
||||||
|
},
|
||||||
// ### removeFilter
|
// ### removeFilter
|
||||||
//
|
//
|
||||||
// Remove a filter from filters at index filterIndex
|
// Remove a filter from filters at index filterIndex
|
||||||
|
|||||||
@@ -227,24 +227,42 @@ my.Map = Backbone.View.extend({
|
|||||||
// Private: Return a GeoJSON geomtry extracted from the record fields
|
// Private: Return a GeoJSON geomtry extracted from the record fields
|
||||||
//
|
//
|
||||||
_getGeometryFromRecord: function(doc){
|
_getGeometryFromRecord: function(doc){
|
||||||
if (this._geomReady()){
|
|
||||||
if (this.state.get('geomField')){
|
if (this.state.get('geomField')){
|
||||||
var value = doc.get(this.state.get('geomField'));
|
var value = doc.get(this.state.get('geomField'));
|
||||||
if (typeof(value) === 'string'){
|
if (typeof(value) === 'string'){
|
||||||
// We *may* have a GeoJSON string representation
|
// We *may* have a GeoJSON string representation
|
||||||
try {
|
try {
|
||||||
value = $.parseJSON(value);
|
value = $.parseJSON(value);
|
||||||
} catch(e) {
|
} catch(e) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (typeof(value) === 'string') {
|
||||||
|
value = value.replace('(', '').replace(')', '');
|
||||||
|
var parts = value.split(',');
|
||||||
|
var lat = parseFloat(parts[0]);
|
||||||
|
var lon = parseFloat(parts[1]);
|
||||||
|
if (!isNaN(lon) && !isNaN(parseFloat(lat))) {
|
||||||
|
return {
|
||||||
|
"type": "Point",
|
||||||
|
"coordinates": [lon, lat]
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
if (value && value.lat) {
|
} else if (value && value.slice) {
|
||||||
// not yet geojson so convert
|
// [ lon, lat ]
|
||||||
value = {
|
return {
|
||||||
|
"type": "Point",
|
||||||
|
"coordinates": [value[0], value[1]]
|
||||||
|
};
|
||||||
|
} else if (value && value.lat) {
|
||||||
|
// of form { lat: ..., lon: ...}
|
||||||
|
return {
|
||||||
"type": "Point",
|
"type": "Point",
|
||||||
"coordinates": [value.lon || value.lng, value.lat]
|
"coordinates": [value.lon || value.lng, value.lat]
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
// We now assume that contents of the field are a valid GeoJSON object
|
// We o/w assume that contents of the field are a valid GeoJSON object
|
||||||
return value;
|
return value;
|
||||||
} else if (this.state.get('lonField') && this.state.get('latField')){
|
} else if (this.state.get('lonField') && this.state.get('latField')){
|
||||||
// We'll create a GeoJSON like point object from the two lat/lon fields
|
// We'll create a GeoJSON like point object from the two lat/lon fields
|
||||||
@@ -258,7 +276,6 @@ my.Map = Backbone.View.extend({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
|
||||||
},
|
},
|
||||||
|
|
||||||
// Private: Check if there is a field with GeoJSON geometries or alternatively,
|
// Private: Check if there is a field with GeoJSON geometries or alternatively,
|
||||||
|
|||||||
@@ -135,35 +135,18 @@ my.SlickGrid = Backbone.View.extend({
|
|||||||
this.grid = new Slick.Grid(this.el, data, visibleColumns, options);
|
this.grid = new Slick.Grid(this.el, data, visibleColumns, options);
|
||||||
|
|
||||||
// Column sorting
|
// Column sorting
|
||||||
var gridSorter = function(field, ascending, grid, data){
|
var sortInfo = this.model.queryState.get('sort');
|
||||||
|
|
||||||
data.sort(function(a, b){
|
|
||||||
var result =
|
|
||||||
a[field] > b[field] ? 1 :
|
|
||||||
a[field] < b[field] ? -1 :
|
|
||||||
0
|
|
||||||
;
|
|
||||||
return ascending ? result : -result;
|
|
||||||
});
|
|
||||||
|
|
||||||
grid.setData(data);
|
|
||||||
grid.updateRowCount();
|
|
||||||
grid.render();
|
|
||||||
}
|
|
||||||
|
|
||||||
var sortInfo = this.state.get('columnsSort');
|
|
||||||
if (sortInfo){
|
if (sortInfo){
|
||||||
var sortAsc = !(sortInfo['direction'] == 'desc');
|
var column = _.keys(sortInfo[0])[0];
|
||||||
gridSorter(sortInfo.column, sortAsc, self.grid, data);
|
var sortAsc = !(sortInfo[0][column].order == 'desc');
|
||||||
this.grid.setSortColumn(sortInfo.column, sortAsc);
|
this.grid.setSortColumn(column, sortAsc);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.grid.onSort.subscribe(function(e, args){
|
this.grid.onSort.subscribe(function(e, args){
|
||||||
gridSorter(args.sortCol.field,args.sortAsc,self.grid,data);
|
var order = (args.sortAsc) ? 'asc':'desc';
|
||||||
self.state.set({columnsSort:{
|
var sort = [{}];
|
||||||
column:args.sortCol,
|
sort[0][args.sortCol.field] = {order: order};
|
||||||
direction: (args.sortAsc) ? 'asc':'desc'
|
self.model.query({sort: sort});
|
||||||
}});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
this.grid.onColumnsReordered.subscribe(function(e, args){
|
this.grid.onColumnsReordered.subscribe(function(e, args){
|
||||||
|
|||||||
@@ -4,12 +4,19 @@ this.recline = this.recline || {};
|
|||||||
this.recline.View = this.recline.View || {};
|
this.recline.View = this.recline.View || {};
|
||||||
|
|
||||||
(function($, my) {
|
(function($, my) {
|
||||||
|
// turn off unnecessary logging from VMM Timeline
|
||||||
|
VMM.debug = false;
|
||||||
|
|
||||||
|
// ## Timeline
|
||||||
|
//
|
||||||
|
// Timeline view using http://timeline.verite.co/
|
||||||
my.Timeline = Backbone.View.extend({
|
my.Timeline = Backbone.View.extend({
|
||||||
tagName: 'div',
|
tagName: 'div',
|
||||||
className: 'recline-timeline',
|
|
||||||
|
|
||||||
template: ' \
|
template: ' \
|
||||||
|
<div class="recline-timeline"> \
|
||||||
<div id="vmm-timeline-id"></div> \
|
<div id="vmm-timeline-id"></div> \
|
||||||
|
</div> \
|
||||||
',
|
',
|
||||||
|
|
||||||
// These are the default (case-insensitive) names of field that are used if found.
|
// These are the default (case-insensitive) names of field that are used if found.
|
||||||
@@ -24,6 +31,7 @@ my.Timeline = Backbone.View.extend({
|
|||||||
this.timeline = new VMM.Timeline();
|
this.timeline = new VMM.Timeline();
|
||||||
this._timelineIsInitialized = false;
|
this._timelineIsInitialized = false;
|
||||||
this.bind('view:show', function() {
|
this.bind('view:show', function() {
|
||||||
|
// only call _initTimeline once view in DOM as Timeline uses $ internally to look up element
|
||||||
if (self._timelineIsInitialized === false) {
|
if (self._timelineIsInitialized === false) {
|
||||||
self._initTimeline();
|
self._initTimeline();
|
||||||
}
|
}
|
||||||
@@ -43,6 +51,11 @@ my.Timeline = Backbone.View.extend({
|
|||||||
this.state = new recline.Model.ObjectState(stateData);
|
this.state = new recline.Model.ObjectState(stateData);
|
||||||
this._setupTemporalField();
|
this._setupTemporalField();
|
||||||
this.render();
|
this.render();
|
||||||
|
// can only call _initTimeline once view in DOM as Timeline uses $
|
||||||
|
// internally to look up element
|
||||||
|
if ($(this.elementId).length > 0) {
|
||||||
|
this._initTimeline();
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
render: function() {
|
render: function() {
|
||||||
@@ -52,9 +65,12 @@ my.Timeline = Backbone.View.extend({
|
|||||||
},
|
},
|
||||||
|
|
||||||
_initTimeline: function() {
|
_initTimeline: function() {
|
||||||
|
var $timeline = this.el.find(this.elementId);
|
||||||
// set width explicitly o/w timeline goes wider that screen for some reason
|
// set width explicitly o/w timeline goes wider that screen for some reason
|
||||||
this.el.find(this.elementId).width(this.el.parent().width());
|
var width = Math.max(this.el.width(), this.el.find('.recline-timeline').width());
|
||||||
// only call _initTimeline once view in DOM as Timeline uses $ internally to look up element
|
if (width) {
|
||||||
|
$timeline.width(width);
|
||||||
|
}
|
||||||
var config = {};
|
var config = {};
|
||||||
var data = this._timelineJSON();
|
var data = this._timelineJSON();
|
||||||
this.timeline.init(data, this.elementId, config);
|
this.timeline.init(data, this.elementId, config);
|
||||||
@@ -68,6 +84,30 @@ my.Timeline = Backbone.View.extend({
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
// Convert record to JSON for timeline
|
||||||
|
//
|
||||||
|
// Designed to be overridden in client apps
|
||||||
|
convertRecord: function(record, fields) {
|
||||||
|
return this._convertRecord(record, fields);
|
||||||
|
},
|
||||||
|
|
||||||
|
// Internal method to generate a Timeline formatted entry
|
||||||
|
_convertRecord: function(record, fields) {
|
||||||
|
var start = this._parseDate(record.get(this.state.get('startField')));
|
||||||
|
var end = this._parseDate(record.get(this.state.get('endField')));
|
||||||
|
if (start) {
|
||||||
|
var tlEntry = {
|
||||||
|
"startDate": start,
|
||||||
|
"endDate": end,
|
||||||
|
"headline": String(record.get('title') || ''),
|
||||||
|
"text": record.get('description') || record.summary()
|
||||||
|
};
|
||||||
|
return tlEntry;
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
_timelineJSON: function() {
|
_timelineJSON: function() {
|
||||||
var self = this;
|
var self = this;
|
||||||
var out = {
|
var out = {
|
||||||
@@ -78,17 +118,10 @@ my.Timeline = Backbone.View.extend({
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
this.model.currentRecords.each(function(doc) {
|
this.model.currentRecords.each(function(record) {
|
||||||
var start = self._parseDate(doc.get(self.state.get('startField')));
|
var newEntry = self.convertRecord(record, self.fields);
|
||||||
var end = self._parseDate(doc.get(self.state.get('endField')));
|
if (newEntry) {
|
||||||
if (start) {
|
out.timeline.date.push(newEntry);
|
||||||
var tlEntry = {
|
|
||||||
"startDate": start,
|
|
||||||
"endDate": end,
|
|
||||||
"headline": String(doc.get('title') || ''),
|
|
||||||
"text": doc.summary()
|
|
||||||
};
|
|
||||||
out.timeline.date.push(tlEntry);
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
// if no entries create a placeholder entry to prevent Timeline crashing with error
|
// if no entries create a placeholder entry to prevent Timeline crashing with error
|
||||||
@@ -103,6 +136,9 @@ my.Timeline = Backbone.View.extend({
|
|||||||
},
|
},
|
||||||
|
|
||||||
_parseDate: function(date) {
|
_parseDate: function(date) {
|
||||||
|
if (!date) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
var out = date.trim();
|
var out = date.trim();
|
||||||
out = out.replace(/(\d)th/g, '$1');
|
out = out.replace(/(\d)th/g, '$1');
|
||||||
out = out.replace(/(\d)st/g, '$1');
|
out = out.replace(/(\d)st/g, '$1');
|
||||||
|
|||||||
@@ -15,7 +15,8 @@ my.FilterEditor = Backbone.View.extend({
|
|||||||
<fieldset> \
|
<fieldset> \
|
||||||
<label>Filter type</label> \
|
<label>Filter type</label> \
|
||||||
<select class="filterType"> \
|
<select class="filterType"> \
|
||||||
<option value="term">Term (text) filter</option> \
|
<option value="term">Term (text)</option> \
|
||||||
|
<option value="geo_distance">Geo distance</option> \
|
||||||
</select> \
|
</select> \
|
||||||
<label>Field</label> \
|
<label>Field</label> \
|
||||||
<select class="fields"> \
|
<select class="fields"> \
|
||||||
@@ -27,21 +28,44 @@ my.FilterEditor = Backbone.View.extend({
|
|||||||
</fieldset> \
|
</fieldset> \
|
||||||
</form> \
|
</form> \
|
||||||
<form class="form-stacked js-edit"> \
|
<form class="form-stacked js-edit"> \
|
||||||
{{#termFilters}} \
|
{{#filters}} \
|
||||||
<div class="control-group filter-term filter" data-filter-id={{id}}> \
|
{{{filterRender}}} \
|
||||||
<label class="control-label" for="">{{label}}</label> \
|
{{/filters}} \
|
||||||
<div class="controls"> \
|
{{#filters.length}} \
|
||||||
<input type="text" value="{{value}}" name="{{fieldId}}" data-filter-field="{{fieldId}}" data-filter-id="{{id}}" data-filter-type="term" /> \
|
|
||||||
<a class="js-remove-filter" href="#">×</a> \
|
|
||||||
</div> \
|
|
||||||
</div> \
|
|
||||||
{{/termFilters}} \
|
|
||||||
{{#termFilters.length}} \
|
|
||||||
<button type="submit" class="btn">Update</button> \
|
<button type="submit" class="btn">Update</button> \
|
||||||
{{/termFilters.length}} \
|
{{/filters.length}} \
|
||||||
</form> \
|
</form> \
|
||||||
</div> \
|
</div> \
|
||||||
',
|
',
|
||||||
|
filterTemplates: {
|
||||||
|
term: ' \
|
||||||
|
<div class="filter-{{type}} filter"> \
|
||||||
|
<fieldset> \
|
||||||
|
<legend> \
|
||||||
|
{{field}} <small>{{type}}</small> \
|
||||||
|
<a class="js-remove-filter" href="#" title="Remove this filter">×</a> \
|
||||||
|
</legend> \
|
||||||
|
<input type="text" value="{{term}}" name="term" data-filter-field="{{field}}" data-filter-id="{{id}}" data-filter-type="{{type}}" /> \
|
||||||
|
</fieldset> \
|
||||||
|
</div> \
|
||||||
|
',
|
||||||
|
geo_distance: ' \
|
||||||
|
<div class="filter-{{type}} filter"> \
|
||||||
|
<fieldset> \
|
||||||
|
<legend> \
|
||||||
|
{{field}} <small>{{type}}</small> \
|
||||||
|
<a class="js-remove-filter" href="#" title="Remove this filter">×</a> \
|
||||||
|
</legend> \
|
||||||
|
<label class="control-label" for="">Longitude</label> \
|
||||||
|
<input type="text" value="{{point.lon}}" name="lon" data-filter-field="{{field}}" data-filter-id="{{id}}" data-filter-type="{{type}}" /> \
|
||||||
|
<label class="control-label" for="">Latitude</label> \
|
||||||
|
<input type="text" value="{{point.lat}}" name="lat" data-filter-field="{{field}}" data-filter-id="{{id}}" data-filter-type="{{type}}" /> \
|
||||||
|
<label class="control-label" for="">Distance (km)</label> \
|
||||||
|
<input type="text" value="{{distance}}" name="distance" data-filter-field="{{field}}" data-filter-id="{{id}}" data-filter-type="{{type}}" /> \
|
||||||
|
</fieldset> \
|
||||||
|
</div> \
|
||||||
|
'
|
||||||
|
},
|
||||||
events: {
|
events: {
|
||||||
'click .js-remove-filter': 'onRemoveFilter',
|
'click .js-remove-filter': 'onRemoveFilter',
|
||||||
'click .js-add-filter': 'onAddFilterShow',
|
'click .js-add-filter': 'onAddFilterShow',
|
||||||
@@ -51,30 +75,23 @@ my.FilterEditor = Backbone.View.extend({
|
|||||||
initialize: function() {
|
initialize: function() {
|
||||||
this.el = $(this.el);
|
this.el = $(this.el);
|
||||||
_.bindAll(this, 'render');
|
_.bindAll(this, 'render');
|
||||||
|
this.model.fields.bind('all', this.render);
|
||||||
this.model.queryState.bind('change', this.render);
|
this.model.queryState.bind('change', this.render);
|
||||||
this.model.queryState.bind('change:filters:new-blank', this.render);
|
this.model.queryState.bind('change:filters:new-blank', this.render);
|
||||||
this.render();
|
this.render();
|
||||||
},
|
},
|
||||||
render: function() {
|
render: function() {
|
||||||
|
var self = this;
|
||||||
var tmplData = $.extend(true, {}, this.model.queryState.toJSON());
|
var tmplData = $.extend(true, {}, this.model.queryState.toJSON());
|
||||||
// we will use idx in list as there id ...
|
// we will use idx in list as there id ...
|
||||||
tmplData.filters = _.map(tmplData.filters, function(filter, idx) {
|
tmplData.filters = _.map(tmplData.filters, function(filter, idx) {
|
||||||
filter.id = idx;
|
filter.id = idx;
|
||||||
return filter;
|
return filter;
|
||||||
});
|
});
|
||||||
tmplData.termFilters = _.filter(tmplData.filters, function(filter) {
|
|
||||||
return filter.term !== undefined;
|
|
||||||
});
|
|
||||||
tmplData.termFilters = _.map(tmplData.termFilters, function(filter) {
|
|
||||||
var fieldId = _.keys(filter.term)[0];
|
|
||||||
return {
|
|
||||||
id: filter.id,
|
|
||||||
fieldId: fieldId,
|
|
||||||
label: fieldId,
|
|
||||||
value: filter.term[fieldId]
|
|
||||||
};
|
|
||||||
});
|
|
||||||
tmplData.fields = this.model.fields.toJSON();
|
tmplData.fields = this.model.fields.toJSON();
|
||||||
|
tmplData.filterRender = function() {
|
||||||
|
return Mustache.render(self.filterTemplates[this.type], this);
|
||||||
|
};
|
||||||
var out = Mustache.render(this.template, tmplData);
|
var out = Mustache.render(this.template, tmplData);
|
||||||
this.el.html(out);
|
this.el.html(out);
|
||||||
},
|
},
|
||||||
@@ -90,9 +107,7 @@ my.FilterEditor = Backbone.View.extend({
|
|||||||
$target.hide();
|
$target.hide();
|
||||||
var filterType = $target.find('select.filterType').val();
|
var filterType = $target.find('select.filterType').val();
|
||||||
var field = $target.find('select.fields').val();
|
var field = $target.find('select.fields').val();
|
||||||
if (filterType === 'term') {
|
this.model.queryState.addFilter({type: filterType, field: field});
|
||||||
this.model.queryState.addTermFilter(field);
|
|
||||||
}
|
|
||||||
// trigger render explicitly as queryState change will not be triggered (as blank value for filter)
|
// trigger render explicitly as queryState change will not be triggered (as blank value for filter)
|
||||||
this.render();
|
this.render();
|
||||||
},
|
},
|
||||||
@@ -109,10 +124,20 @@ my.FilterEditor = Backbone.View.extend({
|
|||||||
var $form = $(e.target);
|
var $form = $(e.target);
|
||||||
_.each($form.find('input'), function(input) {
|
_.each($form.find('input'), function(input) {
|
||||||
var $input = $(input);
|
var $input = $(input);
|
||||||
var filterIndex = parseInt($input.attr('data-filter-id'));
|
var filterType = $input.attr('data-filter-type');
|
||||||
var value = $input.val();
|
|
||||||
var fieldId = $input.attr('data-filter-field');
|
var fieldId = $input.attr('data-filter-field');
|
||||||
filters[filterIndex].term[fieldId] = value;
|
var filterIndex = parseInt($input.attr('data-filter-id'));
|
||||||
|
var name = $input.attr('name');
|
||||||
|
var value = $input.val();
|
||||||
|
if (filterType === 'term') {
|
||||||
|
filters[filterIndex].term = value;
|
||||||
|
} else if (filterType === 'geo_distance') {
|
||||||
|
if (name === 'distance') {
|
||||||
|
filters[filterIndex].distance = parseFloat(value);
|
||||||
|
} else {
|
||||||
|
filters[filterIndex].point[name] = parseFloat(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
});
|
});
|
||||||
self.model.queryState.set({filters: filters});
|
self.model.queryState.set({filters: filters});
|
||||||
self.model.queryState.trigger('change');
|
self.model.queryState.trigger('change');
|
||||||
|
|||||||
@@ -3,32 +3,77 @@ module("Backend ElasticSearch - Wrapper");
|
|||||||
|
|
||||||
test("queryNormalize", function() {
|
test("queryNormalize", function() {
|
||||||
var backend = new recline.Backend.ElasticSearch.Wrapper();
|
var backend = new recline.Backend.ElasticSearch.Wrapper();
|
||||||
|
|
||||||
var in_ = new recline.Model.Query();
|
var in_ = new recline.Model.Query();
|
||||||
var out = backend._normalizeQuery(in_);
|
var out = backend._normalizeQuery(in_);
|
||||||
equal(out.size, 100);
|
var exp = {
|
||||||
|
constant_score: {
|
||||||
|
query: {
|
||||||
|
match_all: {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
deepEqual(out, exp);
|
||||||
|
|
||||||
var in_ = new recline.Model.Query();
|
var in_ = new recline.Model.Query();
|
||||||
in_.set({q: ''});
|
in_.set({q: ''});
|
||||||
var out = backend._normalizeQuery(in_);
|
var out = backend._normalizeQuery(in_);
|
||||||
equal(out.q, undefined);
|
deepEqual(out, exp);
|
||||||
deepEqual(out.query.match_all, {});
|
|
||||||
|
|
||||||
var in_ = new recline.Model.Query().toJSON();
|
|
||||||
in_.q = '';
|
|
||||||
var out = backend._normalizeQuery(in_);
|
|
||||||
equal(out.q, undefined);
|
|
||||||
deepEqual(out.query.match_all, {});
|
|
||||||
|
|
||||||
var in_ = new recline.Model.Query().toJSON();
|
|
||||||
in_.q = 'abc';
|
|
||||||
var out = backend._normalizeQuery(in_);
|
|
||||||
equal(out.query.query_string.query, 'abc');
|
|
||||||
|
|
||||||
var in_ = new recline.Model.Query();
|
var in_ = new recline.Model.Query();
|
||||||
in_.addTermFilter('xyz', 'XXX');
|
in_.attributes.q = 'abc';
|
||||||
in_ = in_.toJSON();
|
|
||||||
var out = backend._normalizeQuery(in_);
|
var out = backend._normalizeQuery(in_);
|
||||||
deepEqual(out.filter.and[0], {term: { xyz: 'XXX'}});
|
equal(out.constant_score.query.query_string.query, 'abc');
|
||||||
|
|
||||||
|
var in_ = new recline.Model.Query();
|
||||||
|
in_.addFilter({
|
||||||
|
type: 'term',
|
||||||
|
field: 'xyz',
|
||||||
|
term: 'XXX'
|
||||||
|
});
|
||||||
|
var out = backend._normalizeQuery(in_);
|
||||||
|
var exp = {
|
||||||
|
constant_score: {
|
||||||
|
query: {
|
||||||
|
match_all: {}
|
||||||
|
},
|
||||||
|
filter: {
|
||||||
|
and: [
|
||||||
|
{
|
||||||
|
term: {
|
||||||
|
xyz: 'XXX'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
deepEqual(out, exp);
|
||||||
|
|
||||||
|
var in_ = new recline.Model.Query();
|
||||||
|
in_.addFilter({
|
||||||
|
type: 'geo_distance',
|
||||||
|
field: 'xyz'
|
||||||
|
});
|
||||||
|
var out = backend._normalizeQuery(in_);
|
||||||
|
var exp = {
|
||||||
|
constant_score: {
|
||||||
|
query: {
|
||||||
|
match_all: {}
|
||||||
|
},
|
||||||
|
filter: {
|
||||||
|
and: [
|
||||||
|
{
|
||||||
|
geo_distance: {
|
||||||
|
distance: '10km',
|
||||||
|
'xyz': { lon: 0, lat: 0 }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
deepEqual(out, exp);
|
||||||
});
|
});
|
||||||
|
|
||||||
var mapping_data = {
|
var mapping_data = {
|
||||||
@@ -128,6 +173,7 @@ test("query", function() {
|
|||||||
return {
|
return {
|
||||||
done: function(callback) {
|
done: function(callback) {
|
||||||
callback(sample_data);
|
callback(sample_data);
|
||||||
|
return this;
|
||||||
},
|
},
|
||||||
fail: function() {
|
fail: function() {
|
||||||
}
|
}
|
||||||
@@ -224,10 +270,11 @@ test("query", function() {
|
|||||||
return {
|
return {
|
||||||
done: function(callback) {
|
done: function(callback) {
|
||||||
callback(sample_data);
|
callback(sample_data);
|
||||||
|
return this;
|
||||||
},
|
},
|
||||||
fail: function() {
|
fail: function() {
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -44,6 +44,22 @@ test('query sort', function () {
|
|||||||
};
|
};
|
||||||
var out = data.query(queryObj);
|
var out = data.query(queryObj);
|
||||||
equal(out.records[0].x, 6);
|
equal(out.records[0].x, 6);
|
||||||
|
|
||||||
|
var queryObj = {
|
||||||
|
sort: [
|
||||||
|
{'country': {order: 'desc'}}
|
||||||
|
]
|
||||||
|
};
|
||||||
|
var out = data.query(queryObj);
|
||||||
|
equal(out.records[0].country, 'US');
|
||||||
|
|
||||||
|
var queryObj = {
|
||||||
|
sort: [
|
||||||
|
{'country': {order: 'asc'}}
|
||||||
|
]
|
||||||
|
};
|
||||||
|
var out = data.query(queryObj);
|
||||||
|
equal(out.records[0].country, 'DE');
|
||||||
});
|
});
|
||||||
|
|
||||||
test('query string', function () {
|
test('query string', function () {
|
||||||
@@ -60,7 +76,7 @@ test('query string', function () {
|
|||||||
test('filters', function () {
|
test('filters', function () {
|
||||||
var data = _wrapData();
|
var data = _wrapData();
|
||||||
var query = new recline.Model.Query();
|
var query = new recline.Model.Query();
|
||||||
query.addTermFilter('country', 'UK');
|
query.addFilter({type: 'term', field: 'country', term: 'UK'});
|
||||||
var out = data.query(query.toJSON());
|
var out = data.query(query.toJSON());
|
||||||
equal(out.total, 3);
|
equal(out.total, 3);
|
||||||
deepEqual(_.pluck(out.records, 'country'), ['UK', 'UK', 'UK']);
|
deepEqual(_.pluck(out.records, 'country'), ['UK', 'UK', 'UK']);
|
||||||
@@ -198,7 +214,7 @@ test('query string', function () {
|
|||||||
|
|
||||||
test('filters', function () {
|
test('filters', function () {
|
||||||
var dataset = makeBackendDataset();
|
var dataset = makeBackendDataset();
|
||||||
dataset.queryState.addTermFilter('country', 'UK');
|
dataset.queryState.addFilter({type: 'term', field: 'country', term: 'UK'});
|
||||||
dataset.query().then(function() {
|
dataset.query().then(function() {
|
||||||
equal(dataset.currentRecords.length, 3);
|
equal(dataset.currentRecords.length, 3);
|
||||||
deepEqual(dataset.currentRecords.pluck('country'), ['UK', 'UK', 'UK']);
|
deepEqual(dataset.currentRecords.pluck('country'), ['UK', 'UK', 'UK']);
|
||||||
|
|||||||
@@ -150,6 +150,29 @@ test('Query', function () {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('Query.addFilter', function () {
|
test('Query.addFilter', function () {
|
||||||
|
var query = new recline.Model.Query();
|
||||||
|
query.addFilter({type: 'term', field: 'xyz'});
|
||||||
|
var exp = {
|
||||||
|
field: 'xyz',
|
||||||
|
type: 'term',
|
||||||
|
term: ''
|
||||||
|
};
|
||||||
|
deepEqual(query.get('filters')[0], exp);
|
||||||
|
|
||||||
|
query.addFilter({type: 'geo_distance', field: 'xyz'});
|
||||||
|
var exp = {
|
||||||
|
distance: '10km',
|
||||||
|
point: {
|
||||||
|
lon: 0,
|
||||||
|
lat: 0
|
||||||
|
},
|
||||||
|
field: 'xyz',
|
||||||
|
type: 'geo_distance'
|
||||||
|
};
|
||||||
|
deepEqual(exp, query.get('filters')[1]);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Query.addTermFilter', function () {
|
||||||
var query = new recline.Model.Query();
|
var query = new recline.Model.Query();
|
||||||
query.addTermFilter('xyz', 'this-value');
|
query.addTermFilter('xyz', 'this-value');
|
||||||
deepEqual({term: {xyz: 'this-value'}}, query.get('filters')[0]);
|
deepEqual({term: {xyz: 'this-value'}}, query.get('filters')[0]);
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
/**
|
/**
|
||||||
* QUnit - A JavaScript Unit Testing Framework
|
* QUnit v1.6.0 - A JavaScript Unit Testing Framework
|
||||||
*
|
*
|
||||||
* http://docs.jquery.com/QUnit
|
* http://docs.jquery.com/QUnit
|
||||||
*
|
*
|
||||||
* Copyright (c) 2011 John Resig, Jörn Zaefferer
|
* Copyright (c) 2012 John Resig, Jörn Zaefferer
|
||||||
* Dual licensed under the MIT (MIT-LICENSE.txt)
|
* Dual licensed under the MIT (MIT-LICENSE.txt)
|
||||||
* or GPL (GPL-LICENSE.txt) licenses.
|
* or GPL (GPL-LICENSE.txt) licenses.
|
||||||
*/
|
*/
|
||||||
@@ -54,6 +54,11 @@
|
|||||||
color: #fff;
|
color: #fff;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#qunit-header label {
|
||||||
|
display: inline-block;
|
||||||
|
padding-left: 0.5em;
|
||||||
|
}
|
||||||
|
|
||||||
#qunit-banner {
|
#qunit-banner {
|
||||||
height: 5px;
|
height: 5px;
|
||||||
}
|
}
|
||||||
@@ -186,6 +191,7 @@
|
|||||||
color: #710909;
|
color: #710909;
|
||||||
background-color: #fff;
|
background-color: #fff;
|
||||||
border-left: 26px solid #EE5757;
|
border-left: 26px solid #EE5757;
|
||||||
|
white-space: pre;
|
||||||
}
|
}
|
||||||
|
|
||||||
#qunit-tests > li:last-child {
|
#qunit-tests > li:last-child {
|
||||||
@@ -215,6 +221,9 @@
|
|||||||
|
|
||||||
border-bottom: 1px solid white;
|
border-bottom: 1px solid white;
|
||||||
}
|
}
|
||||||
|
#qunit-testresult .module-name {
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
/** Fixture */
|
/** Fixture */
|
||||||
|
|
||||||
@@ -222,4 +231,6 @@
|
|||||||
position: absolute;
|
position: absolute;
|
||||||
top: -10000px;
|
top: -10000px;
|
||||||
left: -10000px;
|
left: -10000px;
|
||||||
|
width: 1000px;
|
||||||
|
height: 1000px;
|
||||||
}
|
}
|
||||||
|
|||||||
1089
test/qunit/qunit.js
1089
test/qunit/qunit.js
File diff suppressed because it is too large
Load Diff
@@ -38,6 +38,7 @@ test('initialize', function () {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('dates in graph view', function () {
|
test('dates in graph view', function () {
|
||||||
|
expect(0);
|
||||||
var dataset = Fixture.getDataset();
|
var dataset = Fixture.getDataset();
|
||||||
var view = new recline.View.Graph({
|
var view = new recline.View.Graph({
|
||||||
model: dataset,
|
model: dataset,
|
||||||
|
|||||||
@@ -105,21 +105,25 @@ test('GeoJSON geom field', function () {
|
|||||||
view.remove();
|
view.remove();
|
||||||
});
|
});
|
||||||
|
|
||||||
test('geom field non-GeoJSON', function () {
|
test('_getGeometryFromRecord non-GeoJSON', function () {
|
||||||
var data = [{
|
var test = [
|
||||||
location: { lon: 47, lat: 53},
|
[{ lon: 47, lat: 53}, [47,53]],
|
||||||
title: 'abc'
|
["53.3,47.32", [47.32, 53.3]],
|
||||||
}];
|
["53.3, 47.32", [47.32, 53.3]],
|
||||||
var dataset = recline.Backend.Memory.createDataset(data);
|
["(53.3,47.32)", [47.32, 53.3]],
|
||||||
|
[[53.3,47.32], [53.3, 47.32]]
|
||||||
|
];
|
||||||
var view = new recline.View.Map({
|
var view = new recline.View.Map({
|
||||||
model: dataset
|
model: recline.Backend.Memory.createDataset([{a: 1}]),
|
||||||
|
state: {
|
||||||
|
geomField: 'location'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
_.each(test, function(item) {
|
||||||
|
var record = new recline.Model.Record({location: item[0]});
|
||||||
|
var out = view._getGeometryFromRecord(record);
|
||||||
|
deepEqual(out.coordinates, item[1]);
|
||||||
});
|
});
|
||||||
|
|
||||||
//Fire query, otherwise the map won't be initialized
|
|
||||||
dataset.query();
|
|
||||||
|
|
||||||
// Check that all features were created
|
|
||||||
equal(_getFeaturesCount(view.features), 1);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
test('Popup', function () {
|
test('Popup', function () {
|
||||||
|
|||||||
@@ -29,7 +29,6 @@ test('state', function () {
|
|||||||
state: {
|
state: {
|
||||||
hiddenColumns:['x','lat','title'],
|
hiddenColumns:['x','lat','title'],
|
||||||
columnsOrder:['lon','id','z','date', 'y', 'country'],
|
columnsOrder:['lon','id','z','date', 'y', 'country'],
|
||||||
columnsSort:{column:'country',direction:'desc'},
|
|
||||||
columnsWidth:[
|
columnsWidth:[
|
||||||
{column:'id',width: 250}
|
{column:'id',width: 250}
|
||||||
]
|
]
|
||||||
@@ -52,9 +51,6 @@ test('state', function () {
|
|||||||
// Column order
|
// Column order
|
||||||
deepEqual(_.pluck(headers,'title'),view.state.get('columnsOrder'));
|
deepEqual(_.pluck(headers,'title'),view.state.get('columnsOrder'));
|
||||||
|
|
||||||
// Column sorting
|
|
||||||
equal($(view.grid.getCellNode(0,view.grid.getColumnIndex('country'))).text(),'US');
|
|
||||||
|
|
||||||
// Column width
|
// Column width
|
||||||
equal($('.slick-header-column[title="id"]').width(),250);
|
equal($('.slick-header-column[title="id"]').width(),250);
|
||||||
|
|
||||||
|
|||||||
@@ -59,7 +59,8 @@ test('_parseDate', function () {
|
|||||||
[ 'August 1st 1914', '1914-08-01T00:00:00.000Z' ],
|
[ 'August 1st 1914', '1914-08-01T00:00:00.000Z' ],
|
||||||
[ '1914-08-01', '1914-08-01T00:00:00.000Z' ],
|
[ '1914-08-01', '1914-08-01T00:00:00.000Z' ],
|
||||||
[ '1914-08-01T08:00', '1914-08-01T08:00:00.000Z' ],
|
[ '1914-08-01T08:00', '1914-08-01T08:00:00.000Z' ],
|
||||||
[ 'afdaf afdaf', null ]
|
[ 'afdaf afdaf', null ],
|
||||||
|
[ null, null ]
|
||||||
];
|
];
|
||||||
_.each(testData, function(item) {
|
_.each(testData, function(item) {
|
||||||
var out = view._parseDate(item[0]);
|
var out = view._parseDate(item[0]);
|
||||||
|
|||||||
@@ -21,12 +21,12 @@ test('basics', function () {
|
|||||||
ok(!$addForm.is(":visible"));
|
ok(!$addForm.is(":visible"));
|
||||||
$editForm = view.el.find('form.js-edit');
|
$editForm = view.el.find('form.js-edit');
|
||||||
equal($editForm.find('.filter-term').length, 1)
|
equal($editForm.find('.filter-term').length, 1)
|
||||||
equal(_.keys(dataset.queryState.attributes.filters[0].term)[0], 'country');
|
equal(dataset.queryState.attributes.filters[0].field, 'country');
|
||||||
|
|
||||||
// now set filter value and apply
|
// now set filter value and apply
|
||||||
$editForm.find('input').val('UK');
|
$editForm.find('input').val('UK');
|
||||||
$editForm.submit();
|
$editForm.submit();
|
||||||
equal(dataset.queryState.attributes.filters[0].term.country, 'UK');
|
equal(dataset.queryState.attributes.filters[0].term, 'UK');
|
||||||
equal(dataset.currentRecords.length, 3);
|
equal(dataset.currentRecords.length, 3);
|
||||||
|
|
||||||
// now remove filter
|
// now remove filter
|
||||||
@@ -39,3 +39,29 @@ test('basics', function () {
|
|||||||
view.remove();
|
view.remove();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('geo_distance', function () {
|
||||||
|
var dataset = Fixture.getDataset();
|
||||||
|
var view = new recline.View.FilterEditor({
|
||||||
|
model: dataset
|
||||||
|
});
|
||||||
|
$('.fixtures').append(view.el);
|
||||||
|
|
||||||
|
var $addForm = view.el.find('form.js-add');
|
||||||
|
// submit the form
|
||||||
|
$addForm.find('select.filterType').val('geo_distance');
|
||||||
|
$addForm.find('select.fields').val('lon');
|
||||||
|
$addForm.submit();
|
||||||
|
|
||||||
|
// now check we have new filter
|
||||||
|
$editForm = view.el.find('form.js-edit');
|
||||||
|
equal($editForm.find('.filter-geo_distance').length, 1)
|
||||||
|
deepEqual(_.keys(dataset.queryState.attributes.filters[0]), ['distance',
|
||||||
|
'point', 'type', 'field']);
|
||||||
|
|
||||||
|
// now set filter value and apply
|
||||||
|
$editForm.find('input[name="lat"]').val(10);
|
||||||
|
$editForm.submit();
|
||||||
|
equal(dataset.queryState.attributes.filters[0].point.lat, 10);
|
||||||
|
|
||||||
|
view.remove();
|
||||||
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user