diff --git a/app/index.html b/app/index.html index 0c56105f..4b16c85a 100644 --- a/app/index.html +++ b/app/index.html @@ -58,13 +58,16 @@ - + + - + + + diff --git a/app/js/app.js b/app/js/app.js index 990be82a..7de2ee58 100755 --- a/app/js/app.js +++ b/app/js/app.js @@ -108,7 +108,7 @@ var ExplorerApp = Backbone.View.extend({ } ]; - this.dataExplorer = new recline.View.DataExplorer({ + this.dataExplorer = new recline.View.MultiView({ model: dataset, el: $el, state: state, diff --git a/library-view.markdown b/library-view.markdown new file mode 100644 index 00000000..96ab9353 --- /dev/null +++ b/library-view.markdown @@ -0,0 +1,100 @@ +--- +layout: container +title: Library - Views +--- + +
+ initialize: {
+ model: {a recline.Model.Dataset instance}
+ // el: {do not specify - instead view should create}
+ state: {(optional) Object / Hash specifying initial state}
+ ...
+ }
+
+
+Note: Dataset Views in core Recline have a common layout on disk as follows,
+where ViewName is the named of View class:
+
+
+src/view-{lower-case-ViewName}.js
+css/{lower-case-ViewName}.css
+test/view-{lower-case-ViewName}.js
+
+
+### State
+
+State information exists in order to support state serialization into the url
+or elsewhere and reloading of application from a stored state.
+
+State is available not only for individual views (as described above) but for
+the dataset (e.g. the current query). For an example of pulling together state
+from across multiple components see `recline.View.DataExplorer`.
+
+### Flash Messages / Notifications
+
+To send 'flash messages' or notifications the convention is that views should
+fire an event named `recline:flash` with a payload that is a flash object with
+the following attributes (all optional):
+
+* message: message to show.
+* category: warning (default), success, error
+* persist: if true alert is persistent, o/w hidden after 3s (default=false)
+* loader: if true show a loading message
+
+Objects or views wishing to bind to flash messages may then subscribe to these
+events and take some action such as displaying them to the user. For an example
+of such behaviour see the DataExplorer view.
+
+### Writing your own Views
+
+See the existing Views.
+
diff --git a/library.html b/library.html
index 3593bbc9..850afcf3 100644
--- a/library.html
+++ b/library.html
@@ -112,7 +112,7 @@ title: Library - Home
Complementing the model are various Views (you can also easily write your own). Each view holds a pointer to a Dataset:
-// var myExplorer = new model.recline.DataExplorer({
+// var myExplorer = new model.recline.MultiView({
// model: {{recline.Model.Dataset instance}}
// el: {{an existing dom element}}
// views: {{dataset views}}
@@ -120,7 +120,7 @@ this.recline.View = this.recline.View || {};
// Graph).
//
// **views**: (optional) the dataset views (Grid, Graph etc) for
-// DataExplorer to show. This is an array of view hashes. If not provided
+// MultiView to show. This is an array of view hashes. If not provided
// initialize with (recline.View.)Grid, Graph, and Map views (with obvious id
// and labels!).
//
@@ -161,8 +161,8 @@ this.recline.View = this.recline.View || {};
// Note that at present we do *not* serialize information about the actual set
// of views in use -- e.g. those specified by the views argument -- but instead
// expect either that the default views are fine or that the client to have
-// initialized the DataExplorer with the relevant views themselves.
-my.DataExplorer = Backbone.View.extend({
+// initialized the MultiView with the relevant views themselves.
+my.MultiView = Backbone.View.extend({
template: ' \
\
\
@@ -453,12 +453,12 @@ my.DataExplorer = Backbone.View.extend({
}
});
-// ### DataExplorer.restore
+// ### MultiView.restore
//
-// Restore a DataExplorer instance from a serialized state including the associated dataset
-my.DataExplorer.restore = function(state) {
+// Restore a MultiView instance from a serialized state including the associated dataset
+my.MultiView.restore = function(state) {
var dataset = recline.Model.Dataset.restore(state);
- var explorer = new my.DataExplorer({
+ var explorer = new my.MultiView({
model: dataset,
state: state
});
diff --git a/src/widget.facetviewer.js b/src/widget.facetviewer.js
new file mode 100644
index 00000000..0bdd52cd
--- /dev/null
+++ b/src/widget.facetviewer.js
@@ -0,0 +1,80 @@
+/*jshint multistr:true */
+
+this.recline = this.recline || {};
+this.recline.View = this.recline.View || {};
+
+(function($, my) {
+
+my.FacetViewer = Backbone.View.extend({
+ className: 'recline-facet-viewer well',
+ template: ' \
+ × \
+ \
+ \
+ Facets
\
+ \
+ {{#facets}} \
+ \
+ {{id}} {{label}} \
+ \
+ \
+ {{/facets}} \
+ \
+ ',
+
+ events: {
+ 'click .js-hide': 'onHide',
+ 'click .js-facet-filter': 'onFacetFilter'
+ },
+ initialize: function(model) {
+ _.bindAll(this, 'render');
+ this.el = $(this.el);
+ this.model.facets.bind('all', this.render);
+ this.model.fields.bind('all', this.render);
+ this.render();
+ },
+ render: function() {
+ var tmplData = {
+ facets: this.model.facets.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.render(this.template, tmplData);
+ this.el.html(templated);
+ // are there actually any facets to show?
+ if (this.model.facets.length > 0) {
+ this.el.show();
+ } else {
+ this.el.hide();
+ }
+ },
+ onHide: function(e) {
+ e.preventDefault();
+ this.el.hide();
+ },
+ onFacetFilter: function(e) {
+ var $target= $(e.target);
+ var fieldId = $target.closest('.facet-summary').attr('data-facet');
+ var value = $target.attr('data-value');
+ this.model.queryState.addTermFilter(fieldId, value);
+ }
+});
+
+
+})(jQuery, recline.View);
+
diff --git a/src/widget.queryeditor.js b/src/widget.queryeditor.js
new file mode 100644
index 00000000..964ff97d
--- /dev/null
+++ b/src/widget.queryeditor.js
@@ -0,0 +1,65 @@
+/*jshint multistr:true */
+
+this.recline = this.recline || {};
+this.recline.View = this.recline.View || {};
+
+(function($, my) {
+
+my.QueryEditor = Backbone.View.extend({
+ className: 'recline-query-editor',
+ template: ' \
+ \
+ ',
+
+ events: {
+ 'submit form': 'onFormSubmit',
+ 'click .action-pagination-update': 'onPaginationUpdate'
+ },
+
+ initialize: function() {
+ _.bindAll(this, 'render');
+ this.el = $(this.el);
+ this.model.bind('change', this.render);
+ this.render();
+ },
+ onFormSubmit: function(e) {
+ e.preventDefault();
+ var query = this.el.find('.text-query input').val();
+ var newFrom = parseInt(this.el.find('input[name="from"]').val());
+ var newSize = parseInt(this.el.find('input[name="to"]').val()) - newFrom;
+ this.model.set({size: newSize, from: newFrom, q: query});
+ },
+ onPaginationUpdate: function(e) {
+ e.preventDefault();
+ var $el = $(e.target);
+ var newFrom = 0;
+ if ($el.parent().hasClass('prev')) {
+ newFrom = this.model.get('from') - Math.max(0, this.model.get('size'));
+ } else {
+ newFrom = this.model.get('from') + this.model.get('size');
+ }
+ this.model.set({from: newFrom});
+ },
+ render: function() {
+ var tmplData = this.model.toJSON();
+ tmplData.to = this.model.get('from') + this.model.get('size');
+ var templated = Mustache.render(this.template, tmplData);
+ this.el.html(templated);
+ }
+});
+
+})(jQuery, recline.View);
+
diff --git a/test/index.html b/test/index.html
index 1b9e301a..6a328121 100644
--- a/test/index.html
+++ b/test/index.html
@@ -41,20 +41,22 @@
-
+
+
+
-
+
diff --git a/test/view.test.js b/test/view.multiview.test.js
similarity index 89%
rename from test/view.test.js
rename to test/view.multiview.test.js
index b00b7f1d..fc7ce53a 100644
--- a/test/view.test.js
+++ b/test/view.multiview.test.js
@@ -6,7 +6,7 @@ test('basic explorer functionality', function () {
var $el = $('');
$('.fixtures .data-explorer-here').append($el);
var dataset = Fixture.getDataset();
- var explorer = new recline.View.DataExplorer({
+ var explorer = new recline.View.MultiView({
model: dataset,
el: $el
});
@@ -21,7 +21,7 @@ test('get State', function () {
var dataset = Fixture.getDataset();
var url = 'xyz';
dataset.set({url: url});
- var explorer = new recline.View.DataExplorer({
+ var explorer = new recline.View.MultiView({
model: dataset,
el: $el
});
@@ -41,7 +41,7 @@ test('initialize state', function () {
var $el = $('');
$('.fixtures .data-explorer-here').append($el);
var dataset = Fixture.getDataset();
- var explorer = new recline.View.DataExplorer({
+ var explorer = new recline.View.MultiView({
model: dataset,
el: $el,
state: {
@@ -74,11 +74,11 @@ test('initialize state', function () {
test('restore (from serialized state)', function() {
var dataset = Fixture.getDataset();
- var explorer = new recline.View.DataExplorer({
+ var explorer = new recline.View.MultiView({
model: dataset,
});
var state = explorer.state.toJSON();
- var explorerNew = recline.View.DataExplorer.restore(state);
+ var explorerNew = recline.View.MultiView.restore(state);
var out = explorerNew.state.toJSON();
equal(out.backend, state.backend);
});