Merge branch 'master' into gh-pages
This commit is contained in:
commit
1c93e1f67c
9
_includes/data.js
Normal file
9
_includes/data.js
Normal file
@ -0,0 +1,9 @@
|
||||
var data = [
|
||||
{id: 0, date: '2011-01-01', x: 1, y: 2, z: 3, country: 'DE', label: 'first', lat:52.56, lon:13.40},
|
||||
{id: 1, date: '2011-02-02', x: 2, y: 4, z: 24, country: 'UK', label: 'second', lat:54.97, lon:-1.60},
|
||||
{id: 2, date: '2011-03-03', x: 3, y: 6, z: 9, country: 'US', label: 'third', lat:40.00, lon:-75.5},
|
||||
{id: 3, date: '2011-04-04', x: 4, y: 8, z: 6, country: 'UK', label: 'fourth', lat:57.27, lon:-6.20},
|
||||
{id: 4, date: '2011-05-04', x: 5, y: 10, z: 15, country: 'UK', label: 'fifth', lat:51.58, lon:0},
|
||||
{id: 5, date: '2011-06-02', x: 6, y: 12, z: 18, country: 'DE', label: 'sixth', lat:51.04, lon:7.9}
|
||||
];
|
||||
|
||||
@ -97,36 +97,37 @@
|
||||
<div class="hero-unit">
|
||||
<h1>Welcome to the Recline Data Explorer</h1>
|
||||
<p>Recline allows you to explore and work with data in your browser and then share with others</p>
|
||||
In basic operation it's much like a spreadsheet - though it's
|
||||
feature set is a little different. In particular, the Data
|
||||
Explorer provides:
|
||||
<ul>
|
||||
<li>Data grid / spreadsheet</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
|
||||
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>
|
||||
<div class="row">
|
||||
<div class="span4">
|
||||
<h3>View the demo</h3>
|
||||
<p>Take a look at a local demo dataset.</p>
|
||||
<p><a class="btn btn-primary" href="?url=demo">View the demo dataset »</a></p>
|
||||
</div>
|
||||
<div class="span4">
|
||||
<h3>Read the tutorial</h3>
|
||||
<p>Take a look at the tutorial for using the data explorer:</p>
|
||||
<a class="btn btn-primary" href="#tutorial">Read the tutorial »</a>
|
||||
</div>
|
||||
<div class="span4">
|
||||
<h3>Import some data</h3>
|
||||
<p>Starting working with some data straight away. You can import some data <strong>using the menu at the top right</strong> of this page.</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row-fluid">
|
||||
<div class="span4">
|
||||
<div class="well">
|
||||
<h3>View the demo</h3>
|
||||
<p>Try out the demo using a local example dataset.</p>
|
||||
<p><a class="btn btn-primary" href="?url=demo#explorer">View the demo dataset »</a></p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="span4">
|
||||
<div class="well">
|
||||
<h3>Features</h3>
|
||||
<ul>
|
||||
<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
|
||||
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>
|
||||
</div>
|
||||
</div>
|
||||
<div class="span4">
|
||||
<div class="well">
|
||||
<h3>Get started</h3>
|
||||
<p>Get started straight away for example by importing some data from an external source <strong>using the menu at the top right</strong> of this page.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="page-explorer backbone-page">
|
||||
<div class="data-explorer-here"></div>
|
||||
@ -153,7 +154,7 @@
|
||||
<select name="backend_type">
|
||||
<option value="csv">CSV</option>
|
||||
<option vlaue="excel">Excel</option>
|
||||
<option value="gdocs">Google Spreadsheet</option>
|
||||
<option value="gdoc">Google Spreadsheet</option>
|
||||
<option value="elasticsearch">ElasticSearch</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
@ -21,7 +21,7 @@ var ExplorerApp = Backbone.View.extend({
|
||||
this.router.route(/explorer/, 'explorer', this.viewExplorer);
|
||||
Backbone.history.start();
|
||||
|
||||
var state = recline.Util.parseQueryString(window.location.search);
|
||||
var state = recline.Util.parseQueryString(decodeURIComponent(window.location.search));
|
||||
if (state) {
|
||||
_.each(state, function(value, key) {
|
||||
try {
|
||||
|
||||
56
css/grid.css
56
css/grid.css
@ -2,20 +2,22 @@
|
||||
* (Data) Grid
|
||||
*********************************************************/
|
||||
|
||||
table.recline-grid {
|
||||
table-layout: fixed;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.recline-grid .btn-group .dropdown-toggle {
|
||||
padding: 1px 3px;
|
||||
line-height: auto;
|
||||
}
|
||||
|
||||
.recline-grid {
|
||||
border: 1px solid #ccc;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.recline-grid td, .recline-grid th {
|
||||
border-left: 1px solid #ccc;
|
||||
padding: 3px 4px;
|
||||
text-align: left;
|
||||
word-wrap: break-word;
|
||||
white-space: normal;
|
||||
}
|
||||
|
||||
.recline-grid td {
|
||||
@ -26,6 +28,14 @@
|
||||
width: 20px;
|
||||
}
|
||||
|
||||
.recline-grid tbody tr:last-child {
|
||||
border-bottom: 1px solid #ccc;
|
||||
}
|
||||
|
||||
.recline-grid tbody td:last-child {
|
||||
border-right: 1px solid #ccc;
|
||||
}
|
||||
|
||||
/* direct borrowing from twitter buttons */
|
||||
.recline-grid th,
|
||||
.transform-column-view .expression-preview-table-wrapper th
|
||||
@ -53,6 +63,42 @@
|
||||
transition: 0.1s linear all;
|
||||
}
|
||||
|
||||
/**********************************************************
|
||||
* Fixed Header - http://www.imaputz.com/cssStuff/bigFourVersion.html
|
||||
*********************************************************/
|
||||
|
||||
div.table-container {
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
/* Reset overflow value to hidden for all non-IE browsers. */
|
||||
html>body div.table-container {
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
/* set table header to a fixed position. WinIE 6.x only */
|
||||
/* In WinIE 6.x, any element with a position property set to relative and is a child of */
|
||||
/* an element that has an overflow property set, the relative value translates into fixed. */
|
||||
/* Ex: parent element DIV with a class of table-container has an overflow property set to auto */
|
||||
thead.fixed-header tr {
|
||||
position: relative
|
||||
}
|
||||
|
||||
/* set THEAD element to have block level attributes. All other non-IE browsers */
|
||||
/* this enables overflow to work on TBODY element. All other non-IE, non-Mozilla browsers */
|
||||
html>body thead.fixed-header tr {
|
||||
display: block
|
||||
}
|
||||
|
||||
/* define the table content to be scrollable */
|
||||
/* set TBODY element to have block level attributes. All other non-IE browsers */
|
||||
/* this enables overflow to work on TBODY element. All other non-IE, non-Mozilla browsers */
|
||||
/* induced side effect is that child TDs no longer accept width: auto */
|
||||
tbody.scroll-content {
|
||||
display: block;
|
||||
max-height: 500px;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
/**********************************************************
|
||||
* Data Table Menus
|
||||
|
||||
@ -20,41 +20,42 @@ Before writing any code with Recline, you need to do the following preparation s
|
||||
2. Include the relevant CSS in the head section of your document:
|
||||
{% highlight html %}
|
||||
<!-- you do not have to use bootstrap but we use it by default -->
|
||||
<link rel="stylesheet" href="vendor/bootstrap/2.0.2/css/bootstrap.css">
|
||||
<link rel="stylesheet" href="vendor/bootstrap/2.0.2/css/bootstrap.css" />
|
||||
<!-- CSS for relevant view components - here we just have grid -->
|
||||
<link rel="stylesheet" href="css/grid.css" />{% endhighlight %}
|
||||
<link rel="stylesheet" href="css/grid.css" />{% endhighlight %}
|
||||
|
||||
3. Include the relevant Javascript files somewhere on the page (preferably before body close tag):
|
||||
{% highlight html %}
|
||||
<!-- you do not have to use bootstrap but we use it by default -->
|
||||
<link rel="stylesheet" href="vendor/bootstrap/2.0.2/css/bootstrap.css">
|
||||
<!-- 3rd party dependencies -->
|
||||
<script type="text/javascript" src="vendor/jquery/1.7.1/jquery.js"></script>
|
||||
<script type="text/javascript" src="vendor/underscore/1.1.6/underscore.js"></script>
|
||||
<script type="text/javascript" src="vendor/backbone/0.5.1/backbone.js"></script>
|
||||
<script type="text/javascript" src="vendor/jquery.mustache.js"></script>
|
||||
<!-- note that we could include individual components rather than whole of recline -->
|
||||
<!-- note that we could include individual components rather than whole of recline e.g.
|
||||
<script type="text/javascript" src="src/model.js"></script>
|
||||
<script type="text/javascript" src="src/backend/base.js"></script>
|
||||
<script type="text/javascript" src="src/backend/memory.js"></script>
|
||||
<script type="text/javascript" src="src/view-grid.js"></script>
|
||||
-->
|
||||
<script type="text/javascript" src="recline.js"></script>{% endhighlight %}
|
||||
|
||||
4. Create a div to hold the Recline view(s):
|
||||
{% highlight html %}
|
||||
<div id="recline-grid"></div>{% endhighlight %}
|
||||
<div id="mygrid"></div>{% endhighlight %}
|
||||
|
||||
You're now ready to start working with Recline.
|
||||
|
||||
### Creating a Dataset
|
||||
|
||||
We are going to be working with the following set of data:
|
||||
Here's some example data We are going to work with:
|
||||
|
||||
{% highlight javascript %}
|
||||
var data = [
|
||||
{id: 0, x: 1, y: 2, z: 3, country: 'UK', label: 'first'},
|
||||
{id: 1, x: 2, y: 4, z: 6, country: 'UK', label: 'second'},
|
||||
{id: 2, x: 3, y: 6, z: 9, country: 'US', label: 'third'}
|
||||
];
|
||||
{% include data.js %}
|
||||
{% endhighlight %}
|
||||
|
||||
Here we have 3 documents / rows each of which is a javascript object containing keys and values (note that all values here are 'simple' but there is no reason you cannot have full objects as values.
|
||||
In this data we have 6 documents / rows. Each document is a javascript object
|
||||
containing keys and values (note that all values here are 'simple' but there is
|
||||
no reason you cannot have objects as values allowing you to nest data.
|
||||
|
||||
We can now create a recline Dataset object (and memory backend) from this raw data:
|
||||
|
||||
@ -67,28 +68,25 @@ Note that behind the scenes Recline will create a Memory backend for this datase
|
||||
|
||||
### Setting up the Grid
|
||||
|
||||
Let's create a data grid view to display the dataset we have just created, binding the view to the `<div id="recline-grid"></div>` we created earlier:
|
||||
Let's create a data grid view to display the dataset we have just created, binding the view to the `<div id="mygrid"></div>` we created earlier:
|
||||
|
||||
{% highlight javascript %}
|
||||
var $el = $('#mygrid');
|
||||
var grid = new recline.View.Grid({
|
||||
model: dataset,
|
||||
el: $('#recline-grid')
|
||||
model: dataset
|
||||
});
|
||||
$el.append(grid.el);
|
||||
grid.render();
|
||||
{% endhighlight %}
|
||||
|
||||
And hey presto:
|
||||
|
||||
<div id="recline-grid" class="recline-read-only"> </div>
|
||||
<div id="mygrid" class="recline-read-only" style="margin-bottom: 30px; margin-top: -20px;"> </div>
|
||||
|
||||
<script type="text/javascript">
|
||||
var data = [
|
||||
{id: 0, x: 1, y: 2, z: 3, country: 'UK', label: 'first'}
|
||||
, {id: 1, x: 2, y: 4, z: 6, country: 'UK', label: 'second'}
|
||||
, {id: 2, x: 3, y: 6, z: 9, country: 'US', label: 'third'}
|
||||
];
|
||||
{% include data.js %}
|
||||
var dataset = recline.Backend.createDataset(data);
|
||||
var $el = $('#recline-grid');
|
||||
var $el = $('#mygrid');
|
||||
var grid = new recline.View.Grid({
|
||||
model: dataset,
|
||||
});
|
||||
@ -96,3 +94,83 @@ $el.append(grid.el);
|
||||
grid.render();
|
||||
</script>
|
||||
|
||||
### Creating a Graph
|
||||
|
||||
Let's create a graph view to display a line graph for this dataset.
|
||||
|
||||
First, create a new div for the graph:
|
||||
|
||||
{% highlight html %}
|
||||
<div id="mygraph"></div>
|
||||
{% endhighlight %}
|
||||
|
||||
Now let's create the graph, we will use the same dataset we had earlier:
|
||||
|
||||
{% highlight javascript %}
|
||||
var $el = $('#mygraph');
|
||||
var graph = new recline.View.Graph({
|
||||
model: dataset
|
||||
});
|
||||
$el.append(grid.el);
|
||||
graph.render();
|
||||
{% endhighlight %}
|
||||
|
||||
And ... we have a graph view -- with instructions on how to use the controls to
|
||||
create a graph -- but no graph. Go ahead and play around with the controls to
|
||||
create a graph of your choosing:
|
||||
|
||||
<div id="mygraph" style="margin-bottom: 30px;"> </div>
|
||||
|
||||
<script type="text/javascript">
|
||||
var $el = $('#mygraph');
|
||||
var graph = new recline.View.Graph({
|
||||
model: dataset
|
||||
});
|
||||
$el.append(graph.el);
|
||||
graph.render();
|
||||
</script>
|
||||
|
||||
But I wanted to create a graph not a graph editor. Can we do that? Yes you can!
|
||||
All you need to do is set the 'state' of the graph view:
|
||||
|
||||
{% highlight javascript %}
|
||||
var $el = $('#mygraph');
|
||||
var graph = new recline.View.Graph({
|
||||
model: dataset,
|
||||
state: {
|
||||
group: "date",
|
||||
series: ["x", "z"]
|
||||
}
|
||||
});
|
||||
$el.append(grid.el);
|
||||
graph.render();
|
||||
graph.redraw();
|
||||
{% endhighlight %}
|
||||
|
||||
We would get this rendered graph:
|
||||
|
||||
<div id="mygraph2" style="margin-bottom: 30px;"> </div>
|
||||
|
||||
<script type="text/javascript">
|
||||
var $el = $('#mygraph2');
|
||||
var graph = new recline.View.Graph({
|
||||
model: dataset,
|
||||
state: {
|
||||
graphType: "lines-and-points",
|
||||
group: "x",
|
||||
series: ["y", "z"]
|
||||
}
|
||||
});
|
||||
$el.append(graph.el);
|
||||
graph.render();
|
||||
graph.redraw();
|
||||
</script>
|
||||
|
||||
<div class="alert alert-info">
|
||||
<strong>State</strong>: The concept of a state is a common feature of Recline views being an object
|
||||
which stores information about the state and configuration of a given view. You
|
||||
can read more about it in the general <a href="../docs/view.html">Views
|
||||
documentation</a> as well as the documentation of individual views such as the
|
||||
<a href="../docs/view-graph.html">Graph View</a>.
|
||||
</div>
|
||||
|
||||
|
||||
102
recline.js
102
recline.js
@ -612,7 +612,7 @@ my.composeQueryString = function(queryParams) {
|
||||
if (typeof(value) === 'object') {
|
||||
value = JSON.stringify(value);
|
||||
}
|
||||
items.push(key + '=' + value);
|
||||
items.push(key + '=' + encodeURIComponent(value));
|
||||
});
|
||||
queryString += items.join('&');
|
||||
return queryString;
|
||||
@ -1177,8 +1177,9 @@ my.Grid = Backbone.View.extend({
|
||||
// ======================================================
|
||||
// #### Templating
|
||||
template: ' \
|
||||
<div class="table-container"> \
|
||||
<table class="recline-grid table-striped table-condensed" cellspacing="0"> \
|
||||
<thead> \
|
||||
<thead class="fixed-header"> \
|
||||
<tr> \
|
||||
{{#notEmpty}} \
|
||||
<th class="column-header"> \
|
||||
@ -1191,7 +1192,7 @@ my.Grid = Backbone.View.extend({
|
||||
</th> \
|
||||
{{/notEmpty}} \
|
||||
{{#fields}} \
|
||||
<th class="column-header {{#hidden}}hidden{{/hidden}}" data-field="{{id}}"> \
|
||||
<th class="column-header {{#hidden}}hidden{{/hidden}}" data-field="{{id}}" style="width: {{width}}px;"> \
|
||||
<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> \
|
||||
<ul class="dropdown-menu data-table-menu pull-right"> \
|
||||
@ -1210,17 +1211,24 @@ my.Grid = Backbone.View.extend({
|
||||
<span class="column-header-name">{{label}}</span> \
|
||||
</th> \
|
||||
{{/fields}} \
|
||||
<th class="last-header" style="width: {{lastHeaderWidth}}px; padding: 0; margin: 0;"></th> \
|
||||
</tr> \
|
||||
</thead> \
|
||||
<tbody></tbody> \
|
||||
<tbody class="scroll-content"></tbody> \
|
||||
</table> \
|
||||
</div> \
|
||||
',
|
||||
|
||||
toTemplateJSON: function() {
|
||||
var self = this;
|
||||
var modelData = this.model.toJSON();
|
||||
modelData.notEmpty = ( this.fields.length > 0 );
|
||||
// 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();
|
||||
});
|
||||
// last header width = scroll bar - border (2px) */
|
||||
modelData.lastHeaderWidth = this.scrollbarDimensions.width - 2;
|
||||
return modelData;
|
||||
},
|
||||
render: function() {
|
||||
@ -1228,6 +1236,20 @@ my.Grid = Backbone.View.extend({
|
||||
this.fields = this.model.fields.filter(function(field) {
|
||||
return _.indexOf(self.state.get('hiddenFields'), field.id) == -1;
|
||||
});
|
||||
this.scrollbarDimensions = this.scrollbarDimensions || this._scrollbarSize(); // skip measurement if already have dimensions
|
||||
var numFields = this.fields.length;
|
||||
// compute field widths (-20 for first menu col + 10px for padding on each col and finally 16px for the scrollbar)
|
||||
var fullWidth = self.el.width() - 20 - 10 * numFields - this.scrollbarDimensions.width;
|
||||
var width = parseInt(Math.max(50, fullWidth / numFields));
|
||||
var remainder = fullWidth - numFields * width;
|
||||
_.each(this.fields, function(field, idx) {
|
||||
// add the remainder to the first field width so we make up full col
|
||||
if (idx == 0) {
|
||||
field.set({width: width+remainder});
|
||||
} else {
|
||||
field.set({width: width});
|
||||
}
|
||||
});
|
||||
var htmls = $.mustache(this.template, this.toTemplateJSON());
|
||||
this.el.html(htmls);
|
||||
this.model.currentDocuments.forEach(function(doc) {
|
||||
@ -1240,8 +1262,25 @@ my.Grid = Backbone.View.extend({
|
||||
});
|
||||
newView.render();
|
||||
});
|
||||
// hide extra header col if no scrollbar to avoid unsightly overhang
|
||||
var $tbody = this.el.find('tbody')[0];
|
||||
if ($tbody.scrollHeight <= $tbody.offsetHeight) {
|
||||
this.el.find('th.last-header').hide();
|
||||
}
|
||||
this.el.find('.recline-grid').toggleClass('no-hidden', (self.state.get('hiddenFields').length === 0));
|
||||
return this;
|
||||
},
|
||||
|
||||
// ### _scrollbarSize
|
||||
//
|
||||
// Measure width of a vertical scrollbar and height of a horizontal scrollbar.
|
||||
//
|
||||
// @return: { width: pixelWidth, height: pixelHeight }
|
||||
_scrollbarSize: function() {
|
||||
var $c = $("<div style='position:absolute; top:-10000px; left:-10000px; width:100px; height:100px; overflow:scroll;'></div>").appendTo("body");
|
||||
var dim = { width: $c.width() - $c[0].clientWidth + 1, height: $c.height() - $c[0].clientHeight };
|
||||
$c.remove();
|
||||
return dim;
|
||||
}
|
||||
});
|
||||
|
||||
@ -1278,7 +1317,7 @@ my.GridRow = Backbone.View.extend({
|
||||
</div> \
|
||||
</td> \
|
||||
{{#cells}} \
|
||||
<td data-field="{{field}}"> \
|
||||
<td data-field="{{field}}" style="width: {{width}}px; max-width: {{width}}px;"> \
|
||||
<div class="data-table-cell-content"> \
|
||||
<a href="javascript:{}" class="data-table-cell-edit" title="Edit this cell"> </a> \
|
||||
<div class="data-table-cell-value">{{{value}}}</div> \
|
||||
@ -1298,6 +1337,7 @@ my.GridRow = Backbone.View.extend({
|
||||
var cellData = this._fields.map(function(field) {
|
||||
return {
|
||||
field: field.id,
|
||||
width: field.get('width'),
|
||||
value: doc.getFieldValue(field)
|
||||
};
|
||||
});
|
||||
@ -1795,7 +1835,7 @@ my.Map = Backbone.View.extend({
|
||||
// on [OpenStreetMap](http://openstreetmap.org).
|
||||
//
|
||||
_setupMap: function(){
|
||||
var self = this;
|
||||
|
||||
this.map = new L.Map(this.$map.get(0));
|
||||
|
||||
var mapUrl = "http://otile{s}.mqcdn.com/tiles/1.0.0/osm/{z}/{x}/{y}.png";
|
||||
@ -1835,14 +1875,6 @@ my.Map = Backbone.View.extend({
|
||||
|
||||
this.map.setView(new L.LatLng(0, 0), 2);
|
||||
|
||||
var popup = new L.Popup();
|
||||
this.map.on('click', function(e) {
|
||||
var latlngStr = '(' + e.latlng.lat.toFixed(3) + ', ' + e.latlng.lng.toFixed(3) + ')';
|
||||
popup.setLatLng(e.latlng);
|
||||
popup.setContent("You clicked the map at " + latlngStr);
|
||||
self.map.openPopup(popup);
|
||||
});
|
||||
|
||||
this.mapReady = true;
|
||||
},
|
||||
|
||||
@ -2043,10 +2075,11 @@ my.ColumnTransform = Backbone.View.extend({
|
||||
|
||||
// # Recline Views
|
||||
//
|
||||
// Recline Views are Backbone Views and in keeping with normal Backbone views
|
||||
// are Widgets / Components displaying something in the DOM. Like all Backbone
|
||||
// views they have a pointer to a model or a collection and is bound to an
|
||||
// element.
|
||||
// Recline Views are instances of Backbone Views and they act as 'WUI' (web
|
||||
// user interface) component displaying some model object in the DOM. Like all
|
||||
// Backbone views they have a pointer to a model (or a collection) and have an
|
||||
// associated DOM-style element (usually this element will be bound into the
|
||||
// page at some point).
|
||||
//
|
||||
// Views provided by core Recline are crudely divided into two types:
|
||||
//
|
||||
@ -2262,12 +2295,11 @@ my.DataExplorer = Backbone.View.extend({
|
||||
}
|
||||
|
||||
this.model.bind('query:start', function() {
|
||||
self.notify({message: 'Loading data', loader: true});
|
||||
self.notify({loader: true, persist: true});
|
||||
});
|
||||
this.model.bind('query:done', function() {
|
||||
self.clearNotifications();
|
||||
self.el.find('.doc-count').text(self.model.docCount || 'Unknown');
|
||||
self.notify({message: 'Data loaded', category: 'success'});
|
||||
});
|
||||
this.model.bind('query:fail', function(error) {
|
||||
self.clearNotifications();
|
||||
@ -2432,19 +2464,25 @@ my.DataExplorer = Backbone.View.extend({
|
||||
// * loader: if true show loading spinner
|
||||
notify: function(flash) {
|
||||
var tmplData = _.extend({
|
||||
message: '',
|
||||
category: 'warning'
|
||||
message: 'Loading',
|
||||
category: 'warning',
|
||||
loader: false
|
||||
},
|
||||
flash
|
||||
);
|
||||
var _template = ' \
|
||||
<div class="alert alert-{{category}} fade in" data-alert="alert"><a class="close" data-dismiss="alert" href="#">×</a> \
|
||||
{{message}} \
|
||||
{{#loader}} \
|
||||
if (tmplData.loader) {
|
||||
var _template = ' \
|
||||
<div class="alert alert-info alert-loader"> \
|
||||
{{message}} \
|
||||
<span class="notification-loader"> </span> \
|
||||
{{/loader}} \
|
||||
</div>';
|
||||
var _templated = $.mustache(_template, tmplData);
|
||||
</div>';
|
||||
} else {
|
||||
var _template = ' \
|
||||
<div class="alert alert-{{category}} fade in" data-alert="alert"><a class="close" data-dismiss="alert" href="#">×</a> \
|
||||
{{message}} \
|
||||
</div>';
|
||||
}
|
||||
var _templated = $($.mustache(_template, tmplData));
|
||||
_templated = $(_templated).appendTo($('.recline-data-explorer .alert-messages'));
|
||||
if (!flash.persist) {
|
||||
setTimeout(function() {
|
||||
@ -2460,7 +2498,9 @@ my.DataExplorer = Backbone.View.extend({
|
||||
// Clear all existing notifications
|
||||
clearNotifications: function() {
|
||||
var $notifications = $('.recline-data-explorer .alert-messages .alert');
|
||||
$notifications.remove();
|
||||
$notifications.fadeOut(1500, function() {
|
||||
$(this).remove();
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@ -56,7 +56,7 @@ my.composeQueryString = function(queryParams) {
|
||||
if (typeof(value) === 'object') {
|
||||
value = JSON.stringify(value);
|
||||
}
|
||||
items.push(key + '=' + value);
|
||||
items.push(key + '=' + encodeURIComponent(value));
|
||||
});
|
||||
queryString += items.join('&');
|
||||
return queryString;
|
||||
|
||||
@ -130,8 +130,9 @@ my.Grid = Backbone.View.extend({
|
||||
// ======================================================
|
||||
// #### Templating
|
||||
template: ' \
|
||||
<div class="table-container"> \
|
||||
<table class="recline-grid table-striped table-condensed" cellspacing="0"> \
|
||||
<thead> \
|
||||
<thead class="fixed-header"> \
|
||||
<tr> \
|
||||
{{#notEmpty}} \
|
||||
<th class="column-header"> \
|
||||
@ -144,7 +145,7 @@ my.Grid = Backbone.View.extend({
|
||||
</th> \
|
||||
{{/notEmpty}} \
|
||||
{{#fields}} \
|
||||
<th class="column-header {{#hidden}}hidden{{/hidden}}" data-field="{{id}}"> \
|
||||
<th class="column-header {{#hidden}}hidden{{/hidden}}" data-field="{{id}}" style="width: {{width}}px;"> \
|
||||
<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> \
|
||||
<ul class="dropdown-menu data-table-menu pull-right"> \
|
||||
@ -163,17 +164,24 @@ my.Grid = Backbone.View.extend({
|
||||
<span class="column-header-name">{{label}}</span> \
|
||||
</th> \
|
||||
{{/fields}} \
|
||||
<th class="last-header" style="width: {{lastHeaderWidth}}px; padding: 0; margin: 0;"></th> \
|
||||
</tr> \
|
||||
</thead> \
|
||||
<tbody></tbody> \
|
||||
<tbody class="scroll-content"></tbody> \
|
||||
</table> \
|
||||
</div> \
|
||||
',
|
||||
|
||||
toTemplateJSON: function() {
|
||||
var self = this;
|
||||
var modelData = this.model.toJSON();
|
||||
modelData.notEmpty = ( this.fields.length > 0 );
|
||||
// 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();
|
||||
});
|
||||
// last header width = scroll bar - border (2px) */
|
||||
modelData.lastHeaderWidth = this.scrollbarDimensions.width - 2;
|
||||
return modelData;
|
||||
},
|
||||
render: function() {
|
||||
@ -181,6 +189,20 @@ my.Grid = Backbone.View.extend({
|
||||
this.fields = this.model.fields.filter(function(field) {
|
||||
return _.indexOf(self.state.get('hiddenFields'), field.id) == -1;
|
||||
});
|
||||
this.scrollbarDimensions = this.scrollbarDimensions || this._scrollbarSize(); // skip measurement if already have dimensions
|
||||
var numFields = this.fields.length;
|
||||
// compute field widths (-20 for first menu col + 10px for padding on each col and finally 16px for the scrollbar)
|
||||
var fullWidth = self.el.width() - 20 - 10 * numFields - this.scrollbarDimensions.width;
|
||||
var width = parseInt(Math.max(50, fullWidth / numFields));
|
||||
var remainder = fullWidth - numFields * width;
|
||||
_.each(this.fields, function(field, idx) {
|
||||
// add the remainder to the first field width so we make up full col
|
||||
if (idx == 0) {
|
||||
field.set({width: width+remainder});
|
||||
} else {
|
||||
field.set({width: width});
|
||||
}
|
||||
});
|
||||
var htmls = $.mustache(this.template, this.toTemplateJSON());
|
||||
this.el.html(htmls);
|
||||
this.model.currentDocuments.forEach(function(doc) {
|
||||
@ -193,8 +215,25 @@ my.Grid = Backbone.View.extend({
|
||||
});
|
||||
newView.render();
|
||||
});
|
||||
// hide extra header col if no scrollbar to avoid unsightly overhang
|
||||
var $tbody = this.el.find('tbody')[0];
|
||||
if ($tbody.scrollHeight <= $tbody.offsetHeight) {
|
||||
this.el.find('th.last-header').hide();
|
||||
}
|
||||
this.el.find('.recline-grid').toggleClass('no-hidden', (self.state.get('hiddenFields').length === 0));
|
||||
return this;
|
||||
},
|
||||
|
||||
// ### _scrollbarSize
|
||||
//
|
||||
// Measure width of a vertical scrollbar and height of a horizontal scrollbar.
|
||||
//
|
||||
// @return: { width: pixelWidth, height: pixelHeight }
|
||||
_scrollbarSize: function() {
|
||||
var $c = $("<div style='position:absolute; top:-10000px; left:-10000px; width:100px; height:100px; overflow:scroll;'></div>").appendTo("body");
|
||||
var dim = { width: $c.width() - $c[0].clientWidth + 1, height: $c.height() - $c[0].clientHeight };
|
||||
$c.remove();
|
||||
return dim;
|
||||
}
|
||||
});
|
||||
|
||||
@ -231,7 +270,7 @@ my.GridRow = Backbone.View.extend({
|
||||
</div> \
|
||||
</td> \
|
||||
{{#cells}} \
|
||||
<td data-field="{{field}}"> \
|
||||
<td data-field="{{field}}" style="width: {{width}}px; max-width: {{width}}px;"> \
|
||||
<div class="data-table-cell-content"> \
|
||||
<a href="javascript:{}" class="data-table-cell-edit" title="Edit this cell"> </a> \
|
||||
<div class="data-table-cell-value">{{{value}}}</div> \
|
||||
@ -251,6 +290,7 @@ my.GridRow = Backbone.View.extend({
|
||||
var cellData = this._fields.map(function(field) {
|
||||
return {
|
||||
field: field.id,
|
||||
width: field.get('width'),
|
||||
value: doc.getFieldValue(field)
|
||||
};
|
||||
});
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user