Merge branch 'master' into gh-pages
This commit is contained in:
commit
0ce9ed366e
@ -67,7 +67,7 @@
|
||||
<script type="text/javascript" src="../src/widget.pager.js"></script>
|
||||
<script type="text/javascript" src="../src/widget.queryeditor.js"></script>
|
||||
<script type="text/javascript" src="../src/widget.filtereditor.js"></script>
|
||||
<script type="text/javascript" src="../src/widget.facetviewer.js"></script>
|
||||
<script type="text/javascript" src="../src/widget.fields.js"></script>
|
||||
<script type="text/javascript" src="../src/view.multiview.js"></script>
|
||||
|
||||
<!-- non-library javascript specific to this demo -->
|
||||
@ -84,22 +84,38 @@
|
||||
<ul class="nav pull-right">
|
||||
<li class="dropdown">
|
||||
<a data-toggle="dropdown" class="dropdown-toggle">
|
||||
Import <b class="caret"></b></a>
|
||||
<ul class="dropdown-menu js-import">
|
||||
Load Data <b class="caret"></b></a>
|
||||
<ul class="dropdown-menu js-load">
|
||||
<li>
|
||||
<a data-toggle="modal" href=".js-import-dialog-url">Import from URL</a>
|
||||
<a href="#" class="js-load-dialog-url" data-type="datahub" data-help="The link to the Dataset Data Resource on the DataHub to load from - note that the resource must have its Data API enabled">Load from the DataHub</a>
|
||||
</li>
|
||||
<li>
|
||||
<a data-toggle="modal" href=".js-import-dialog-file">Import from File</a>
|
||||
<a href="#" class="js-load-dialog-url" data-type="csv" data-help="Provide the link to the CSV file online">Load from CSV online</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#" class="js-load-dialog-url" data-type="excel" data-help="Provide the link to the Excel file online">Load from Excel online</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#" class="js-load-dialog-url" data-type="elasticsearch" data-help="Provide the link to the ElasticSearch endpoint (either an index or a type/table">Load from ElasticSearch</a>
|
||||
</li>
|
||||
<li class="divider"></li>
|
||||
<li>
|
||||
<a data-toggle="modal" href=".js-load-dialog-file">Load from CSV on disk</a>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>
|
||||
<a href=".js-share-and-embed-dialog" data-toggle="modal">
|
||||
<a href=".js-share-and-embed-dialog" data-toggle="modal">
|
||||
Share and Embed
|
||||
<i class="icon-share icon-white"></i>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href=".js-settings" data-toggle="modal">
|
||||
Settings
|
||||
<i class="icon-cog icon-white" style="margin-top: 1px;"></i>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
@ -127,7 +143,7 @@
|
||||
<li>Data grid</li>
|
||||
<li>Data editing including programmatic data transformation in javascript</li>
|
||||
<li>Visualizations includes graphs and maps</li>
|
||||
<li>Import and export from a variety of sources including online sources such as online Excel and CSV files, Google docs and
|
||||
<li>Load and export from a variety of sources including online sources such as online Excel and CSV files, Google docs and
|
||||
the <a href="http://datahub.io/">DataHub</a> and offline sources like CSV files on your local machine.</li>
|
||||
<li>Use online or offline - because the app is built in pure javascript and html you can use it anywhere there's a modern web browser. Using offline is as easy and downloading this web page to your local machine.</li>
|
||||
</ul>
|
||||
@ -149,41 +165,29 @@
|
||||
</div>
|
||||
|
||||
<!-- modals for menus -->
|
||||
<div class="modal fade in js-import-dialog-url" style="display: none;">
|
||||
<div class="modal fade in js-load-dialog-url" style="display: none;">
|
||||
<div class="modal-header">
|
||||
<a class="close" data-dismiss="modal">×</a>
|
||||
<h3>Import from URL</h3>
|
||||
<h3>Load from URL</h3>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<form class="js-import-url form-horizontal">
|
||||
<form class="js-load-url">
|
||||
<div class="control-group">
|
||||
<label class="control-label">URL</label>
|
||||
<div class="controls">
|
||||
<input type="text" name="source" class="input-xlarge" />
|
||||
<input type="text" name="source" class="span5" placeholder="URL to data source" />
|
||||
<p class="help-block"></p>
|
||||
<input name="backend_type" style="display: none;" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="control-group">
|
||||
<label class="control-label">Type of data</label>
|
||||
<div class="controls">
|
||||
<select name="backend_type">
|
||||
<option value="csv">CSV</option>
|
||||
<option vlaue="excel">Excel</option>
|
||||
<option value="gdocs">Google Spreadsheet</option>
|
||||
<option value="elasticsearch">ElasticSearch</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-actions">
|
||||
<button type="submit" class="btn btn-primary">Import »</button>
|
||||
</div>
|
||||
<button type="submit" class="btn btn-primary">Load »</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="modal fade in js-import-dialog-file" style="display: none;">
|
||||
<div class="modal fade in js-load-dialog-file" style="display: none;">
|
||||
<div class="modal-header">
|
||||
<a class="close" data-dismiss="modal">×</a>
|
||||
<h3>Import from File</h3>
|
||||
<h3>Load from File</h3>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<form class="form-horizontal">
|
||||
@ -213,7 +217,7 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-actions">
|
||||
<button type="submit" class="btn btn-primary">Import »</button>
|
||||
<button type="submit" class="btn btn-primary">Load »</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
@ -231,6 +235,27 @@
|
||||
<textarea class="view-embed" style="width: 100%; height: 200px;"></textarea>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="modal fade in js-settings" style="display: none;">
|
||||
<div class="modal-header">
|
||||
<a class="close" data-dismiss="modal">×</a>
|
||||
<h3>Settings</h3>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<form class="form-horizontal">
|
||||
<div class="control-group">
|
||||
<label class="control-label">DataHub API Key</label>
|
||||
<div class="controls">
|
||||
<input type="text" name="datahub_api_key" value="" />
|
||||
<p class="help-block"><strong>Getting your API key:</strong> Register/Login to <a href="http://datahub.io/">http://datahub.io/</a> and then visit your user home page (click on the link at the top right). On your home page your API key is located at the top of the page in the section showing your main user details.</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-actions">
|
||||
<button type="submit" class="btn btn-primary">Save »</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@ -6,8 +6,10 @@ jQuery(function($) {
|
||||
|
||||
var ExplorerApp = Backbone.View.extend({
|
||||
events: {
|
||||
'submit form.js-import-url': '_onImportURL',
|
||||
'submit .js-import-dialog-file form': '_onImportFile'
|
||||
'click .nav .js-load-dialog-url': '_onLoadURLDialog',
|
||||
'submit form.js-load-url': '_onLoadURL',
|
||||
'submit .js-load-dialog-file form': '_onLoadFile',
|
||||
'submit .js-settings form': '_onSettingsSave'
|
||||
},
|
||||
|
||||
initialize: function() {
|
||||
@ -45,6 +47,7 @@ var ExplorerApp = Backbone.View.extend({
|
||||
if (dataset) {
|
||||
this.createExplorer(dataset, state);
|
||||
}
|
||||
this._initializeSettings();
|
||||
},
|
||||
|
||||
viewHome: function() {
|
||||
@ -153,33 +156,52 @@ var ExplorerApp = Backbone.View.extend({
|
||||
setupLoader: function(callback) {
|
||||
// pre-populate webstore load form with an example url
|
||||
var demoUrl = 'http://thedatahub.org/api/data/b9aae52b-b082-4159-b46f-7bb9c158d013';
|
||||
$('form.js-import-url input[name="source"]').val(demoUrl);
|
||||
$('form.js-load-url input[name="source"]').val(demoUrl);
|
||||
},
|
||||
|
||||
_onImportURL: function(e) {
|
||||
_onLoadURLDialog: function(e) {
|
||||
e.preventDefault();
|
||||
$('.modal.js-import-dialog-url').modal('hide');
|
||||
var $link = $(e.target);
|
||||
var $modal = $('.modal.js-load-dialog-url');
|
||||
$modal.find('h3').text($link.text());
|
||||
$modal.modal('show');
|
||||
$modal.find('input[name="source"]').val('');
|
||||
$modal.find('input[name="backend_type"]').val($link.attr('data-type'));
|
||||
$modal.find('.help-block').text($link.attr('data-help'));
|
||||
},
|
||||
|
||||
_onLoadURL: function(e) {
|
||||
e.preventDefault();
|
||||
$('.modal.js-load-dialog-url').modal('hide');
|
||||
var $form = $(e.target);
|
||||
var source = $form.find('input[name="source"]').val();
|
||||
var datasetInfo = {
|
||||
id: 'my-dataset',
|
||||
url: source,
|
||||
webstore_url: source
|
||||
url: source
|
||||
};
|
||||
var type = $form.find('select[name="backend_type"]').val();
|
||||
var type = $form.find('input[name="backend_type"]').val();
|
||||
if (type === 'csv' || type === 'excel') {
|
||||
datasetInfo.format = type;
|
||||
type = 'dataproxy';
|
||||
}
|
||||
if (type === 'datahub') {
|
||||
// have a full resource url so convert to data API
|
||||
if (source.indexOf('dataset') != -1) {
|
||||
var parts = source.split('/');
|
||||
datasetInfo.url = parts[0] + '/' + parts[1] + '/' + parts[2] + '/api/data/' + parts[parts.length-1];
|
||||
}
|
||||
type = 'elasticsearch';
|
||||
}
|
||||
console.log(datasetInfo.url);
|
||||
var dataset = new recline.Model.Dataset(datasetInfo, type);
|
||||
this.createExplorer(dataset);
|
||||
},
|
||||
|
||||
_onImportFile: function(e) {
|
||||
_onLoadFile: function(e) {
|
||||
var self = this;
|
||||
e.preventDefault();
|
||||
var $form = $(e.target);
|
||||
$('.modal.js-import-dialog-file').modal('hide');
|
||||
$('.modal.js-load-dialog-file').modal('hide');
|
||||
var $file = $form.find('input[type="file"]')[0];
|
||||
var file = $file.files[0];
|
||||
var options = {
|
||||
@ -192,13 +214,34 @@ var ExplorerApp = Backbone.View.extend({
|
||||
},
|
||||
options
|
||||
);
|
||||
},
|
||||
|
||||
_getSettings: function() {
|
||||
var settings = localStorage.getItem('dataexplorer.settings');
|
||||
settings = JSON.parse(settings) || {};
|
||||
return settings;
|
||||
},
|
||||
|
||||
_initializeSettings: function() {
|
||||
var settings = this._getSettings();
|
||||
$('.modal.js-settings form input[name="datahub_api_key"]').val(settings.datahubApiKey);
|
||||
},
|
||||
|
||||
_onSettingsSave: function(e) {
|
||||
var self = this;
|
||||
e.preventDefault();
|
||||
var $form = $(e.target);
|
||||
$('.modal.js-settings').modal('hide');
|
||||
var datahubKey = $form.find('input[name="datahub_api_key"]').val();
|
||||
var settings = this._getSettings();
|
||||
settings.datahubApiKey = datahubKey;
|
||||
localStorage.setItem('dataexplorer.settings', JSON.stringify(settings));
|
||||
}
|
||||
});
|
||||
|
||||
// provide a demonstration in memory dataset
|
||||
function localDataset() {
|
||||
var dataset = Fixture.getDataset();
|
||||
dataset.queryState.addFacet('country');
|
||||
return dataset;
|
||||
}
|
||||
|
||||
|
||||
@ -1,10 +1,17 @@
|
||||
.recline-data-explorer .data-view-container {
|
||||
display: block;
|
||||
clear: both;
|
||||
}
|
||||
|
||||
.recline-data-explorer .data-view-sidebar {
|
||||
float: right;
|
||||
margin-left: 8px;
|
||||
}
|
||||
|
||||
.recline-data-explorer .header .navigation {
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.recline-data-explorer .header .navigation,
|
||||
.recline-data-explorer .header .navigation li,
|
||||
.recline-data-explorer .header .pagination,
|
||||
.recline-data-explorer .header .pagination form
|
||||
{
|
||||
@ -13,8 +20,6 @@
|
||||
|
||||
.recline-data-explorer .header .navigation {
|
||||
float: left;
|
||||
margin-left: 0;
|
||||
padding-left: 0;
|
||||
}
|
||||
|
||||
.recline-data-explorer .header .menu-right {
|
||||
@ -97,6 +102,43 @@
|
||||
display: inline;
|
||||
}
|
||||
|
||||
/**********************************************************
|
||||
* Fields Widget
|
||||
*********************************************************/
|
||||
|
||||
.recline-fields-view {
|
||||
width: 200px;
|
||||
}
|
||||
|
||||
.recline-fields-view .fields-list {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.recline-fields-view .fields-list .accordion-heading a,
|
||||
.recline-fields-view .fields-list .accordion-heading h4 {
|
||||
display: inline;
|
||||
}
|
||||
|
||||
.recline-fields-view .fields-list .accordion-heading h4 {
|
||||
margin-left: 10px;
|
||||
}
|
||||
|
||||
.recline-fields-view .clear {
|
||||
clear: both;
|
||||
}
|
||||
|
||||
.recline-fields-view .facet-items {
|
||||
list-style-type: none;
|
||||
margin-left: 0;
|
||||
}
|
||||
|
||||
.recline-fields-view .facet-item .term {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.recline-fields-view .facet-item .count {
|
||||
}
|
||||
|
||||
/**********************************************************
|
||||
* Notifications
|
||||
*********************************************************/
|
||||
|
||||
@ -31,11 +31,15 @@ Recline has dependencies on some third-party libraries, notably JQuery and Backb
|
||||
|
||||
Optional dependencies:
|
||||
|
||||
* JQuery Mustache (required for all views)
|
||||
* [Mustache.js](https://github.com/janl/mustache.js/) >= 0.5.0-dev (required for all views)
|
||||
* [JQuery Flot](http://code.google.com/p/flot/) >= 0.7 (required for for graph view)
|
||||
* [Leaflet](http://leaflet.cloudmade.com/) >= 0.3.1 (required for map view
|
||||
* [Verite Timeline](https://github.com/VeriteCo/Timeline/) as of 2012-05-02
|
||||
* [Bootstrap](http://twitter.github.com/bootstrap/) >= v2.0 (default option for CSS and UI JS but you can use your own)
|
||||
|
||||
If you grab the full zipball for Recline this will include all of the relevant
|
||||
dependencies in the vendor directory.
|
||||
|
||||
### Example
|
||||
|
||||
Here is an example of the page setup for an app using every Recline component:
|
||||
|
||||
827
recline.js
827
recline.js
File diff suppressed because it is too large
Load Diff
@ -15,7 +15,7 @@ this.recline.Backend.Memory = this.recline.Backend.Memory || {};
|
||||
// @param metadata: (optional) dataset metadata - see recline.Model.Dataset.
|
||||
// If not defined (or id not provided) id will be autogenerated.
|
||||
my.createDataset = function(data, fields, metadata) {
|
||||
var wrapper = new my.DataWrapper(data, fields);
|
||||
var wrapper = new my.Store(data, fields);
|
||||
var backend = new my.Backbone();
|
||||
var dataset = new recline.Model.Dataset(metadata, backend);
|
||||
dataset._dataCache = wrapper;
|
||||
@ -29,7 +29,13 @@ this.recline.Backend.Memory = this.recline.Backend.Memory || {};
|
||||
// Turn a simple array of JS objects into a mini data-store with
|
||||
// functionality like querying, faceting, updating (by ID) and deleting (by
|
||||
// ID).
|
||||
my.DataWrapper = function(data, fields) {
|
||||
//
|
||||
// @param data list of hashes for each record/row in the data ({key:
|
||||
// value, key: value})
|
||||
// @param fields (optional) list of field hashes (each hash defining a field
|
||||
// as per recline.Model.Field). If fields not specified they will be taken
|
||||
// from the data.
|
||||
my.Store = function(data, fields) {
|
||||
var self = this;
|
||||
this.data = data;
|
||||
if (fields) {
|
||||
|
||||
45
src/model.js
45
src/model.js
@ -111,6 +111,31 @@ my.Dataset = Backbone.Model.extend({
|
||||
return data;
|
||||
},
|
||||
|
||||
// Get a summary for each field in the form of a `Facet`.
|
||||
//
|
||||
// @return null as this is async function. Provides deferred/promise interface.
|
||||
getFieldsSummary: function() {
|
||||
var self = this;
|
||||
var query = new my.Query();
|
||||
query.set({size: 0});
|
||||
this.fields.each(function(field) {
|
||||
query.addFacet(field.id);
|
||||
});
|
||||
var dfd = $.Deferred();
|
||||
this.backend.query(this, query.toJSON()).done(function(queryResult) {
|
||||
if (queryResult.facets) {
|
||||
_.each(queryResult.facets, function(facetResult, facetId) {
|
||||
facetResult.id = facetId;
|
||||
var facet = new my.Facet(facetResult);
|
||||
// TODO: probably want replace rather than reset (i.e. just replace the facet with this id)
|
||||
self.fields.get(facetId).facets.reset(facet);
|
||||
});
|
||||
}
|
||||
dfd.resolve(queryResult);
|
||||
});
|
||||
return dfd.promise();
|
||||
},
|
||||
|
||||
// ### _backendFromString(backendString)
|
||||
//
|
||||
// See backend argument to initialize for details
|
||||
@ -190,13 +215,22 @@ my.Record = Backbone.Model.extend({
|
||||
// For the provided Field get the corresponding rendered computed data value
|
||||
// for this record.
|
||||
getFieldValue: function(field) {
|
||||
val = this.getFieldValueUnrendered(field);
|
||||
if (field.renderer) {
|
||||
val = field.renderer(val, field, this.toJSON());
|
||||
}
|
||||
return val;
|
||||
},
|
||||
|
||||
// ### getFieldValueUnrendered
|
||||
//
|
||||
// For the provided Field get the corresponding computed data value
|
||||
// for this record.
|
||||
getFieldValueUnrendered: function(field) {
|
||||
var val = this.get(field.id);
|
||||
if (field.deriver) {
|
||||
val = field.deriver(val, field, this);
|
||||
}
|
||||
if (field.renderer) {
|
||||
val = field.renderer(val, field, this);
|
||||
}
|
||||
return val;
|
||||
},
|
||||
|
||||
@ -233,9 +267,9 @@ my.RecordList = Backbone.Collection.extend({
|
||||
// Following additional instance properties:
|
||||
//
|
||||
// @property {Function} renderer: a function to render the data for this field.
|
||||
// Signature: function(value, field, doc) where value is the value of this
|
||||
// Signature: function(value, field, record) where value is the value of this
|
||||
// cell, field is corresponding field object and record is the record
|
||||
// object. Note that implementing functions can ignore arguments (e.g.
|
||||
// object (as simple JS object). Note that implementing functions can ignore arguments (e.g.
|
||||
// function(value) would be a valid formatter function).
|
||||
//
|
||||
// @property {Function} deriver: a function to derive/compute the value of data
|
||||
@ -282,6 +316,7 @@ my.Field = Backbone.Model.extend({
|
||||
if (!this.renderer) {
|
||||
this.renderer = this.defaultRenderers[this.get('type')];
|
||||
}
|
||||
this.facets = new my.FacetList();
|
||||
},
|
||||
defaultRenderers: {
|
||||
object: function(val, field, doc) {
|
||||
|
||||
@ -68,12 +68,26 @@ my.SlickGrid = Backbone.View.extend({
|
||||
|
||||
// We need all columns, even the hidden ones, to show on the column picker
|
||||
var columns = [];
|
||||
// custom formatter as default one escapes html
|
||||
// plus this way we distinguish between rendering/formatting and computed value (so e.g. sort still works ...)
|
||||
// row = row index, cell = cell index, value = value, columnDef = column definition, dataContext = full row values
|
||||
var formatter = function(row, cell, value, columnDef, dataContext) {
|
||||
var field = self.model.fields.get(columnDef.id);
|
||||
if (field.renderer) {
|
||||
return field.renderer(value, field, dataContext);
|
||||
} else {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
_.each(this.model.fields.toJSON(),function(field){
|
||||
var column = {id:field['id'],
|
||||
name:field['label'],
|
||||
field:field['id'],
|
||||
sortable: true,
|
||||
minWidth: 80};
|
||||
var column = {
|
||||
id:field['id'],
|
||||
name:field['label'],
|
||||
field:field['id'],
|
||||
sortable: true,
|
||||
minWidth: 80,
|
||||
formatter: formatter
|
||||
};
|
||||
|
||||
var widthInfo = _.find(self.state.get('columnsWidth'),function(c){return c.column == field.id});
|
||||
if (widthInfo){
|
||||
@ -113,7 +127,7 @@ my.SlickGrid = Backbone.View.extend({
|
||||
this.model.currentRecords.each(function(doc){
|
||||
var row = {};
|
||||
self.model.fields.each(function(field){
|
||||
row[field.id] = doc.getFieldValue(field);
|
||||
row[field.id] = doc.getFieldValueUnrendered(field);
|
||||
});
|
||||
data.push(row);
|
||||
});
|
||||
|
||||
@ -92,6 +92,14 @@ my.Timeline = Backbone.View.extend({
|
||||
out.timeline.date.push(tlEntry);
|
||||
}
|
||||
});
|
||||
// if no entries create a placeholder entry to prevent Timeline crashing with error
|
||||
if (out.timeline.date.length === 0) {
|
||||
var tlEntry = {
|
||||
"startDate": '2000,1,1',
|
||||
"headline": 'No data to show!'
|
||||
};
|
||||
out.timeline.date.push(tlEntry);
|
||||
}
|
||||
return out;
|
||||
},
|
||||
|
||||
|
||||
@ -75,21 +75,26 @@ my.MultiView = Backbone.View.extend({
|
||||
<div class="alert-messages"></div> \
|
||||
\
|
||||
<div class="header"> \
|
||||
<ul class="navigation"> \
|
||||
<div class="navigation"> \
|
||||
<div class="btn-group" data-toggle="buttons-radio"> \
|
||||
{{#views}} \
|
||||
<li><a href="#{{id}}" data-view="{{id}}" class="btn">{{label}}</a> \
|
||||
<a href="#{{id}}" data-view="{{id}}" class="btn">{{label}}</a> \
|
||||
{{/views}} \
|
||||
</ul> \
|
||||
</div> \
|
||||
</div> \
|
||||
<div class="recline-results-info"> \
|
||||
Results found <span class="doc-count">{{docCount}}</span> \
|
||||
</div> \
|
||||
<div class="menu-right"> \
|
||||
<a href="#" class="btn" data-action="filters">Filters</a> \
|
||||
<a href="#" class="btn" data-action="facets">Facets</a> \
|
||||
<div class="btn-group" data-toggle="buttons-checkbox"> \
|
||||
<a href="#" class="btn" data-action="filters">Filters</a> \
|
||||
<a href="#" class="btn active" data-action="fields">Fields</a> \
|
||||
</div> \
|
||||
</div> \
|
||||
<div class="query-editor-here" style="display:inline;"></div> \
|
||||
<div class="clearfix"></div> \
|
||||
</div> \
|
||||
<div class="data-view-sidebar"></div> \
|
||||
<div class="data-view-container"></div> \
|
||||
</div> \
|
||||
',
|
||||
@ -212,19 +217,17 @@ my.MultiView = Backbone.View.extend({
|
||||
});
|
||||
this.$filterEditor = filterEditor.el;
|
||||
this.el.find('.header').append(filterEditor.el);
|
||||
var facetViewer = new recline.View.FacetViewer({
|
||||
var fieldsView = new recline.View.Fields({
|
||||
model: this.model
|
||||
});
|
||||
this.$facetViewer = facetViewer.el;
|
||||
this.el.find('.header').append(facetViewer.el);
|
||||
this.$fieldsView = fieldsView.el;
|
||||
this.el.find('.data-view-sidebar').append(fieldsView.el);
|
||||
},
|
||||
|
||||
updateNav: function(pageName) {
|
||||
this.el.find('.navigation li').removeClass('active');
|
||||
this.el.find('.navigation li a').removeClass('disabled');
|
||||
var $el = this.el.find('.navigation li a[data-view="' + pageName + '"]');
|
||||
$el.parent().addClass('active');
|
||||
$el.addClass('disabled');
|
||||
this.el.find('.navigation a').removeClass('active');
|
||||
var $el = this.el.find('.navigation a[data-view="' + pageName + '"]');
|
||||
$el.addClass('active');
|
||||
// show the specific page
|
||||
_.each(this.pageViews, function(view, idx) {
|
||||
if (view.id === pageName) {
|
||||
@ -241,9 +244,9 @@ my.MultiView = Backbone.View.extend({
|
||||
e.preventDefault();
|
||||
var action = $(e.target).attr('data-action');
|
||||
if (action === 'filters') {
|
||||
this.$filterEditor.show();
|
||||
} else if (action === 'facets') {
|
||||
this.$facetViewer.show();
|
||||
this.$filterEditor.toggle();
|
||||
} else if (action === 'fields') {
|
||||
this.$fieldsView.toggle();
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
93
src/widget.fields.js
Normal file
93
src/widget.fields.js
Normal file
@ -0,0 +1,93 @@
|
||||
/*jshint multistr:true */
|
||||
|
||||
// Field Info
|
||||
//
|
||||
// For each field
|
||||
//
|
||||
// Id / Label / type / format
|
||||
|
||||
// Editor -- to change type (and possibly format)
|
||||
// Editor for show/hide ...
|
||||
|
||||
// Summaries of fields
|
||||
//
|
||||
// Top values / number empty
|
||||
// If number: max, min average ...
|
||||
|
||||
// Box to boot transform editor ...
|
||||
|
||||
this.recline = this.recline || {};
|
||||
this.recline.View = this.recline.View || {};
|
||||
|
||||
(function($, my) {
|
||||
|
||||
my.Fields = Backbone.View.extend({
|
||||
className: 'recline-fields-view',
|
||||
template: ' \
|
||||
<div class="accordion fields-list well"> \
|
||||
{{#fields}} \
|
||||
<div class="accordion-group field"> \
|
||||
<div class="accordion-heading"> \
|
||||
<h4> \
|
||||
{{label}} \
|
||||
<small> \
|
||||
<i class="icon-file" title="Field type"></i> {{type}} \
|
||||
<a class="accordion-toggle" data-toggle="collapse" href="#collapse{{id}}"> » \
|
||||
</a> \
|
||||
</small> \
|
||||
</h4> \
|
||||
</div> \
|
||||
<div id="collapse{{id}}" class="accordion-body collapse in"> \
|
||||
<div class="accordion-inner"> \
|
||||
{{#facets}} \
|
||||
<div class="facet-summary" data-facet="{{id}}"> \
|
||||
<ul class="facet-items"> \
|
||||
{{#terms}} \
|
||||
<li class="facet-item"><span class="term">{{term}}</span> <span class="count">[{{count}}]</span></li> \
|
||||
{{/terms}} \
|
||||
</ul> \
|
||||
</div> \
|
||||
{{/facets}} \
|
||||
<div class="clear"></div> \
|
||||
</div> \
|
||||
</div> \
|
||||
</div> \
|
||||
{{/fields}} \
|
||||
</div> \
|
||||
',
|
||||
|
||||
events: {
|
||||
},
|
||||
initialize: function(model) {
|
||||
var self = this;
|
||||
this.el = $(this.el);
|
||||
_.bindAll(this, 'render');
|
||||
|
||||
this.model.fields.bind('all', function() {
|
||||
self.model.fields.each(function(field) {
|
||||
field.facets.bind('all', self.render);
|
||||
});
|
||||
// fields can get reset or changed in which case we need to recalculate
|
||||
self.model.getFieldsSummary();
|
||||
self.render();
|
||||
});
|
||||
this.render();
|
||||
},
|
||||
render: function() {
|
||||
var self = this;
|
||||
var tmplData = {
|
||||
fields: []
|
||||
};
|
||||
this.model.fields.each(function(field) {
|
||||
var out = field.toJSON();
|
||||
out.facets = field.facets.toJSON();
|
||||
tmplData.fields.push(out);
|
||||
});
|
||||
var templated = Mustache.render(this.template, tmplData);
|
||||
this.el.html(templated);
|
||||
this.el.find('.collapse').collapse('hide');
|
||||
}
|
||||
});
|
||||
|
||||
})(jQuery, recline.View);
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
(function ($) {
|
||||
|
||||
module("Backend Memory - DataWrapper");
|
||||
module("Backend Memory - Store");
|
||||
|
||||
var memoryData = [
|
||||
{id: 0, x: 1, y: 2, z: 3, country: 'DE', label: 'first'}
|
||||
@ -13,7 +13,7 @@ var memoryData = [
|
||||
|
||||
var _wrapData = function() {
|
||||
var dataCopy = $.extend(true, [], memoryData);
|
||||
return new recline.Backend.Memory.DataWrapper(dataCopy);
|
||||
return new recline.Backend.Memory.Store(dataCopy);
|
||||
}
|
||||
|
||||
test('basics', function () {
|
||||
|
||||
@ -49,7 +49,7 @@
|
||||
<script type="text/javascript" src="../src/widget.pager.js"></script>
|
||||
<script type="text/javascript" src="../src/widget.queryeditor.js"></script>
|
||||
<script type="text/javascript" src="../src/widget.filtereditor.js"></script>
|
||||
<script type="text/javascript" src="../src/widget.facetviewer.js"></script>
|
||||
<script type="text/javascript" src="../src/widget.fields.js"></script>
|
||||
<script type="text/javascript" src="../src/view.multiview.js"></script>
|
||||
|
||||
<script type="text/javascript" src="view-grid.test.js"></script>
|
||||
|
||||
@ -125,6 +125,21 @@ test('Dataset _prepareQuery', function () {
|
||||
deepEqual(out, exp);
|
||||
});
|
||||
|
||||
test('Dataset getFieldsSummary', function () {
|
||||
var dataset = Fixture.getDataset();
|
||||
dataset.getFieldsSummary().done(function() {
|
||||
var countryField = dataset.fields.get('country');
|
||||
var facet = countryField.facets.models[0];
|
||||
equal(facet.get('terms').length, 3);
|
||||
var exp = [
|
||||
{ count: 3, term: 'UK' },
|
||||
{ count: 2, term: 'DE' },
|
||||
{ count: 1, term: 'US' }
|
||||
];
|
||||
deepEqual(facet.get('terms'), exp);
|
||||
});
|
||||
});
|
||||
|
||||
// =================================
|
||||
// Query
|
||||
|
||||
|
||||
@ -65,7 +65,7 @@ test('renderers', function () {
|
||||
var dataset = Fixture.getDataset();
|
||||
|
||||
dataset.fields.get('country').renderer = function(val, field, doc){
|
||||
return 'Country: ' + val;
|
||||
return '<a href="abc">Country: ' + val + '</a>';
|
||||
};
|
||||
|
||||
var deriver = function(val, field, doc){
|
||||
@ -73,7 +73,6 @@ test('renderers', function () {
|
||||
}
|
||||
dataset.fields.add(new recline.Model.Field({id:'computed'},{deriver:deriver}));
|
||||
|
||||
|
||||
var view = new recline.View.SlickGrid({
|
||||
model: dataset
|
||||
});
|
||||
@ -84,6 +83,7 @@ test('renderers', function () {
|
||||
view.grid.init();
|
||||
|
||||
equal($(view.grid.getCellNode(0,view.grid.getColumnIndex('country'))).text(),'Country: DE');
|
||||
equal($(view.grid.getCellNode(0,view.grid.getColumnIndex('country'))).html(),'<a href="abc">Country: DE</a>');
|
||||
equal($(view.grid.getCellNode(0,view.grid.getColumnIndex('computed'))).text(),'10');
|
||||
view.remove();
|
||||
});
|
||||
|
||||
@ -61,9 +61,9 @@ test('initialize state', function () {
|
||||
|
||||
// check the correct view is visible
|
||||
var css = explorer.el.find('.navigation a[data-view="graph"]').attr('class').split(' ');
|
||||
ok(_.contains(css, 'disabled'), css);
|
||||
ok(_.contains(css, 'active'), css);
|
||||
var css = explorer.el.find('.navigation a[data-view="grid"]').attr('class').split(' ');
|
||||
ok(!(_.contains(css, 'disabled')), css);
|
||||
ok(!(_.contains(css, 'active')), css);
|
||||
|
||||
// check pass through of view config
|
||||
deepEqual(explorer.state.get('view-grid')['hiddenFields'], ['x']);
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user