Merge branch 'master' into gh-pages
This commit is contained in:
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,31 +97,32 @@
|
|||||||
<div class="hero-unit">
|
<div class="hero-unit">
|
||||||
<h1>Welcome to the Recline Data Explorer</h1>
|
<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>
|
<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
|
</div>
|
||||||
feature set is a little different. In particular, the Data
|
<div class="row-fluid">
|
||||||
Explorer provides:
|
<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>
|
<ul>
|
||||||
<li>Data grid / spreadsheet</li>
|
<li>Data grid</li>
|
||||||
<li>Data editing including programmatic data transformation in javascript</li>
|
<li>Data editing including programmatic data transformation in javascript</li>
|
||||||
<li>Visualizations includes graphs and maps</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>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>
|
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>
|
<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>
|
</ul>
|
||||||
<div class="row">
|
</div>
|
||||||
<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>
|
||||||
<div class="span4">
|
<div class="span4">
|
||||||
<h3>Read the tutorial</h3>
|
<div class="well">
|
||||||
<p>Take a look at the tutorial for using the data explorer:</p>
|
<h3>Get started</h3>
|
||||||
<a class="btn btn-primary" href="#tutorial">Read the tutorial »</a>
|
<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 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>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -153,7 +154,7 @@
|
|||||||
<select name="backend_type">
|
<select name="backend_type">
|
||||||
<option value="csv">CSV</option>
|
<option value="csv">CSV</option>
|
||||||
<option vlaue="excel">Excel</option>
|
<option vlaue="excel">Excel</option>
|
||||||
<option value="gdocs">Google Spreadsheet</option>
|
<option value="gdoc">Google Spreadsheet</option>
|
||||||
<option value="elasticsearch">ElasticSearch</option>
|
<option value="elasticsearch">ElasticSearch</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ var ExplorerApp = Backbone.View.extend({
|
|||||||
this.router.route(/explorer/, 'explorer', this.viewExplorer);
|
this.router.route(/explorer/, 'explorer', this.viewExplorer);
|
||||||
Backbone.history.start();
|
Backbone.history.start();
|
||||||
|
|
||||||
var state = recline.Util.parseQueryString(window.location.search);
|
var state = recline.Util.parseQueryString(decodeURIComponent(window.location.search));
|
||||||
if (state) {
|
if (state) {
|
||||||
_.each(state, function(value, key) {
|
_.each(state, function(value, key) {
|
||||||
try {
|
try {
|
||||||
|
|||||||
56
css/grid.css
56
css/grid.css
@@ -2,20 +2,22 @@
|
|||||||
* (Data) Grid
|
* (Data) Grid
|
||||||
*********************************************************/
|
*********************************************************/
|
||||||
|
|
||||||
|
table.recline-grid {
|
||||||
|
table-layout: fixed;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
.recline-grid .btn-group .dropdown-toggle {
|
.recline-grid .btn-group .dropdown-toggle {
|
||||||
padding: 1px 3px;
|
padding: 1px 3px;
|
||||||
line-height: auto;
|
line-height: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
.recline-grid {
|
|
||||||
border: 1px solid #ccc;
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.recline-grid td, .recline-grid th {
|
.recline-grid td, .recline-grid th {
|
||||||
border-left: 1px solid #ccc;
|
border-left: 1px solid #ccc;
|
||||||
padding: 3px 4px;
|
padding: 3px 4px;
|
||||||
text-align: left;
|
text-align: left;
|
||||||
|
word-wrap: break-word;
|
||||||
|
white-space: normal;
|
||||||
}
|
}
|
||||||
|
|
||||||
.recline-grid td {
|
.recline-grid td {
|
||||||
@@ -26,6 +28,14 @@
|
|||||||
width: 20px;
|
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 */
|
/* direct borrowing from twitter buttons */
|
||||||
.recline-grid th,
|
.recline-grid th,
|
||||||
.transform-column-view .expression-preview-table-wrapper th
|
.transform-column-view .expression-preview-table-wrapper th
|
||||||
@@ -53,6 +63,42 @@
|
|||||||
transition: 0.1s linear all;
|
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
|
* 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:
|
2. Include the relevant CSS in the head section of your document:
|
||||||
{% highlight html %}
|
{% highlight html %}
|
||||||
<!-- you do not have to use bootstrap but we use it by default -->
|
<!-- 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 -->
|
<!-- 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):
|
3. Include the relevant Javascript files somewhere on the page (preferably before body close tag):
|
||||||
{% highlight html %}
|
{% 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 -->
|
<!-- 3rd party dependencies -->
|
||||||
<script type="text/javascript" src="vendor/jquery/1.7.1/jquery.js"></script>
|
<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/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/backbone/0.5.1/backbone.js"></script>
|
||||||
<script type="text/javascript" src="vendor/jquery.mustache.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 %}
|
<script type="text/javascript" src="recline.js"></script>{% endhighlight %}
|
||||||
|
|
||||||
4. Create a div to hold the Recline view(s):
|
4. Create a div to hold the Recline view(s):
|
||||||
{% highlight html %}
|
{% highlight html %}
|
||||||
<div id="recline-grid"></div>{% endhighlight %}
|
<div id="mygrid"></div>{% endhighlight %}
|
||||||
|
|
||||||
You're now ready to start working with Recline.
|
You're now ready to start working with Recline.
|
||||||
|
|
||||||
### Creating a Dataset
|
### 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 %}
|
{% highlight javascript %}
|
||||||
var data = [
|
{% include data.js %}
|
||||||
{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'}
|
|
||||||
];
|
|
||||||
{% endhighlight %}
|
{% 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:
|
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
|
### 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 %}
|
{% highlight javascript %}
|
||||||
|
var $el = $('#mygrid');
|
||||||
var grid = new recline.View.Grid({
|
var grid = new recline.View.Grid({
|
||||||
model: dataset,
|
model: dataset
|
||||||
el: $('#recline-grid')
|
|
||||||
});
|
});
|
||||||
|
$el.append(grid.el);
|
||||||
grid.render();
|
grid.render();
|
||||||
{% endhighlight %}
|
{% endhighlight %}
|
||||||
|
|
||||||
And hey presto:
|
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">
|
<script type="text/javascript">
|
||||||
var data = [
|
{% include data.js %}
|
||||||
{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'}
|
|
||||||
];
|
|
||||||
var dataset = recline.Backend.createDataset(data);
|
var dataset = recline.Backend.createDataset(data);
|
||||||
var $el = $('#recline-grid');
|
var $el = $('#mygrid');
|
||||||
var grid = new recline.View.Grid({
|
var grid = new recline.View.Grid({
|
||||||
model: dataset,
|
model: dataset,
|
||||||
});
|
});
|
||||||
@@ -96,3 +94,83 @@ $el.append(grid.el);
|
|||||||
grid.render();
|
grid.render();
|
||||||
</script>
|
</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>
|
||||||
|
|
||||||
|
|||||||
96
recline.js
96
recline.js
@@ -612,7 +612,7 @@ my.composeQueryString = function(queryParams) {
|
|||||||
if (typeof(value) === 'object') {
|
if (typeof(value) === 'object') {
|
||||||
value = JSON.stringify(value);
|
value = JSON.stringify(value);
|
||||||
}
|
}
|
||||||
items.push(key + '=' + value);
|
items.push(key + '=' + encodeURIComponent(value));
|
||||||
});
|
});
|
||||||
queryString += items.join('&');
|
queryString += items.join('&');
|
||||||
return queryString;
|
return queryString;
|
||||||
@@ -1177,8 +1177,9 @@ my.Grid = Backbone.View.extend({
|
|||||||
// ======================================================
|
// ======================================================
|
||||||
// #### Templating
|
// #### Templating
|
||||||
template: ' \
|
template: ' \
|
||||||
|
<div class="table-container"> \
|
||||||
<table class="recline-grid table-striped table-condensed" cellspacing="0"> \
|
<table class="recline-grid table-striped table-condensed" cellspacing="0"> \
|
||||||
<thead> \
|
<thead class="fixed-header"> \
|
||||||
<tr> \
|
<tr> \
|
||||||
{{#notEmpty}} \
|
{{#notEmpty}} \
|
||||||
<th class="column-header"> \
|
<th class="column-header"> \
|
||||||
@@ -1191,7 +1192,7 @@ my.Grid = Backbone.View.extend({
|
|||||||
</th> \
|
</th> \
|
||||||
{{/notEmpty}} \
|
{{/notEmpty}} \
|
||||||
{{#fields}} \
|
{{#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"> \
|
<div class="btn-group column-header-menu"> \
|
||||||
<a class="btn dropdown-toggle" data-toggle="dropdown"><i class="icon-cog"></i><span class="caret"></span></a> \
|
<a class="btn dropdown-toggle" data-toggle="dropdown"><i class="icon-cog"></i><span class="caret"></span></a> \
|
||||||
<ul class="dropdown-menu data-table-menu pull-right"> \
|
<ul class="dropdown-menu data-table-menu pull-right"> \
|
||||||
@@ -1210,17 +1211,24 @@ my.Grid = Backbone.View.extend({
|
|||||||
<span class="column-header-name">{{label}}</span> \
|
<span class="column-header-name">{{label}}</span> \
|
||||||
</th> \
|
</th> \
|
||||||
{{/fields}} \
|
{{/fields}} \
|
||||||
|
<th class="last-header" style="width: {{lastHeaderWidth}}px; padding: 0; margin: 0;"></th> \
|
||||||
</tr> \
|
</tr> \
|
||||||
</thead> \
|
</thead> \
|
||||||
<tbody></tbody> \
|
<tbody class="scroll-content"></tbody> \
|
||||||
</table> \
|
</table> \
|
||||||
|
</div> \
|
||||||
',
|
',
|
||||||
|
|
||||||
toTemplateJSON: function() {
|
toTemplateJSON: function() {
|
||||||
|
var self = this;
|
||||||
var modelData = this.model.toJSON();
|
var modelData = this.model.toJSON();
|
||||||
modelData.notEmpty = ( this.fields.length > 0 );
|
modelData.notEmpty = ( this.fields.length > 0 );
|
||||||
// TODO: move this sort of thing into a toTemplateJSON method on Dataset?
|
// TODO: move this sort of thing into a toTemplateJSON method on Dataset?
|
||||||
modelData.fields = _.map(this.fields, function(field) { return field.toJSON(); });
|
modelData.fields = _.map(this.fields, function(field) {
|
||||||
|
return field.toJSON();
|
||||||
|
});
|
||||||
|
// last header width = scroll bar - border (2px) */
|
||||||
|
modelData.lastHeaderWidth = this.scrollbarDimensions.width - 2;
|
||||||
return modelData;
|
return modelData;
|
||||||
},
|
},
|
||||||
render: function() {
|
render: function() {
|
||||||
@@ -1228,6 +1236,20 @@ my.Grid = Backbone.View.extend({
|
|||||||
this.fields = this.model.fields.filter(function(field) {
|
this.fields = this.model.fields.filter(function(field) {
|
||||||
return _.indexOf(self.state.get('hiddenFields'), field.id) == -1;
|
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());
|
var htmls = $.mustache(this.template, this.toTemplateJSON());
|
||||||
this.el.html(htmls);
|
this.el.html(htmls);
|
||||||
this.model.currentDocuments.forEach(function(doc) {
|
this.model.currentDocuments.forEach(function(doc) {
|
||||||
@@ -1240,8 +1262,25 @@ my.Grid = Backbone.View.extend({
|
|||||||
});
|
});
|
||||||
newView.render();
|
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));
|
this.el.find('.recline-grid').toggleClass('no-hidden', (self.state.get('hiddenFields').length === 0));
|
||||||
return this;
|
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> \
|
</div> \
|
||||||
</td> \
|
</td> \
|
||||||
{{#cells}} \
|
{{#cells}} \
|
||||||
<td data-field="{{field}}"> \
|
<td data-field="{{field}}" style="width: {{width}}px; max-width: {{width}}px;"> \
|
||||||
<div class="data-table-cell-content"> \
|
<div class="data-table-cell-content"> \
|
||||||
<a href="javascript:{}" class="data-table-cell-edit" title="Edit this cell"> </a> \
|
<a href="javascript:{}" class="data-table-cell-edit" title="Edit this cell"> </a> \
|
||||||
<div class="data-table-cell-value">{{{value}}}</div> \
|
<div class="data-table-cell-value">{{{value}}}</div> \
|
||||||
@@ -1298,6 +1337,7 @@ my.GridRow = Backbone.View.extend({
|
|||||||
var cellData = this._fields.map(function(field) {
|
var cellData = this._fields.map(function(field) {
|
||||||
return {
|
return {
|
||||||
field: field.id,
|
field: field.id,
|
||||||
|
width: field.get('width'),
|
||||||
value: doc.getFieldValue(field)
|
value: doc.getFieldValue(field)
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
@@ -1795,7 +1835,7 @@ my.Map = Backbone.View.extend({
|
|||||||
// on [OpenStreetMap](http://openstreetmap.org).
|
// on [OpenStreetMap](http://openstreetmap.org).
|
||||||
//
|
//
|
||||||
_setupMap: function(){
|
_setupMap: function(){
|
||||||
var self = this;
|
|
||||||
this.map = new L.Map(this.$map.get(0));
|
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";
|
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);
|
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;
|
this.mapReady = true;
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -2043,10 +2075,11 @@ my.ColumnTransform = Backbone.View.extend({
|
|||||||
|
|
||||||
// # Recline Views
|
// # Recline Views
|
||||||
//
|
//
|
||||||
// Recline Views are Backbone Views and in keeping with normal Backbone views
|
// Recline Views are instances of Backbone Views and they act as 'WUI' (web
|
||||||
// are Widgets / Components displaying something in the DOM. Like all Backbone
|
// user interface) component displaying some model object in the DOM. Like all
|
||||||
// views they have a pointer to a model or a collection and is bound to an
|
// Backbone views they have a pointer to a model (or a collection) and have an
|
||||||
// element.
|
// 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:
|
// 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() {
|
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() {
|
this.model.bind('query:done', function() {
|
||||||
self.clearNotifications();
|
self.clearNotifications();
|
||||||
self.el.find('.doc-count').text(self.model.docCount || 'Unknown');
|
self.el.find('.doc-count').text(self.model.docCount || 'Unknown');
|
||||||
self.notify({message: 'Data loaded', category: 'success'});
|
|
||||||
});
|
});
|
||||||
this.model.bind('query:fail', function(error) {
|
this.model.bind('query:fail', function(error) {
|
||||||
self.clearNotifications();
|
self.clearNotifications();
|
||||||
@@ -2432,19 +2464,25 @@ my.DataExplorer = Backbone.View.extend({
|
|||||||
// * loader: if true show loading spinner
|
// * loader: if true show loading spinner
|
||||||
notify: function(flash) {
|
notify: function(flash) {
|
||||||
var tmplData = _.extend({
|
var tmplData = _.extend({
|
||||||
message: '',
|
message: 'Loading',
|
||||||
category: 'warning'
|
category: 'warning',
|
||||||
|
loader: false
|
||||||
},
|
},
|
||||||
flash
|
flash
|
||||||
);
|
);
|
||||||
|
if (tmplData.loader) {
|
||||||
|
var _template = ' \
|
||||||
|
<div class="alert alert-info alert-loader"> \
|
||||||
|
{{message}} \
|
||||||
|
<span class="notification-loader"> </span> \
|
||||||
|
</div>';
|
||||||
|
} else {
|
||||||
var _template = ' \
|
var _template = ' \
|
||||||
<div class="alert alert-{{category}} fade in" data-alert="alert"><a class="close" data-dismiss="alert" href="#">×</a> \
|
<div class="alert alert-{{category}} fade in" data-alert="alert"><a class="close" data-dismiss="alert" href="#">×</a> \
|
||||||
{{message}} \
|
{{message}} \
|
||||||
{{#loader}} \
|
|
||||||
<span class="notification-loader"> </span> \
|
|
||||||
{{/loader}} \
|
|
||||||
</div>';
|
</div>';
|
||||||
var _templated = $.mustache(_template, tmplData);
|
}
|
||||||
|
var _templated = $($.mustache(_template, tmplData));
|
||||||
_templated = $(_templated).appendTo($('.recline-data-explorer .alert-messages'));
|
_templated = $(_templated).appendTo($('.recline-data-explorer .alert-messages'));
|
||||||
if (!flash.persist) {
|
if (!flash.persist) {
|
||||||
setTimeout(function() {
|
setTimeout(function() {
|
||||||
@@ -2460,7 +2498,9 @@ my.DataExplorer = Backbone.View.extend({
|
|||||||
// Clear all existing notifications
|
// Clear all existing notifications
|
||||||
clearNotifications: function() {
|
clearNotifications: function() {
|
||||||
var $notifications = $('.recline-data-explorer .alert-messages .alert');
|
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') {
|
if (typeof(value) === 'object') {
|
||||||
value = JSON.stringify(value);
|
value = JSON.stringify(value);
|
||||||
}
|
}
|
||||||
items.push(key + '=' + value);
|
items.push(key + '=' + encodeURIComponent(value));
|
||||||
});
|
});
|
||||||
queryString += items.join('&');
|
queryString += items.join('&');
|
||||||
return queryString;
|
return queryString;
|
||||||
|
|||||||
@@ -130,8 +130,9 @@ my.Grid = Backbone.View.extend({
|
|||||||
// ======================================================
|
// ======================================================
|
||||||
// #### Templating
|
// #### Templating
|
||||||
template: ' \
|
template: ' \
|
||||||
|
<div class="table-container"> \
|
||||||
<table class="recline-grid table-striped table-condensed" cellspacing="0"> \
|
<table class="recline-grid table-striped table-condensed" cellspacing="0"> \
|
||||||
<thead> \
|
<thead class="fixed-header"> \
|
||||||
<tr> \
|
<tr> \
|
||||||
{{#notEmpty}} \
|
{{#notEmpty}} \
|
||||||
<th class="column-header"> \
|
<th class="column-header"> \
|
||||||
@@ -144,7 +145,7 @@ my.Grid = Backbone.View.extend({
|
|||||||
</th> \
|
</th> \
|
||||||
{{/notEmpty}} \
|
{{/notEmpty}} \
|
||||||
{{#fields}} \
|
{{#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"> \
|
<div class="btn-group column-header-menu"> \
|
||||||
<a class="btn dropdown-toggle" data-toggle="dropdown"><i class="icon-cog"></i><span class="caret"></span></a> \
|
<a class="btn dropdown-toggle" data-toggle="dropdown"><i class="icon-cog"></i><span class="caret"></span></a> \
|
||||||
<ul class="dropdown-menu data-table-menu pull-right"> \
|
<ul class="dropdown-menu data-table-menu pull-right"> \
|
||||||
@@ -163,17 +164,24 @@ my.Grid = Backbone.View.extend({
|
|||||||
<span class="column-header-name">{{label}}</span> \
|
<span class="column-header-name">{{label}}</span> \
|
||||||
</th> \
|
</th> \
|
||||||
{{/fields}} \
|
{{/fields}} \
|
||||||
|
<th class="last-header" style="width: {{lastHeaderWidth}}px; padding: 0; margin: 0;"></th> \
|
||||||
</tr> \
|
</tr> \
|
||||||
</thead> \
|
</thead> \
|
||||||
<tbody></tbody> \
|
<tbody class="scroll-content"></tbody> \
|
||||||
</table> \
|
</table> \
|
||||||
|
</div> \
|
||||||
',
|
',
|
||||||
|
|
||||||
toTemplateJSON: function() {
|
toTemplateJSON: function() {
|
||||||
|
var self = this;
|
||||||
var modelData = this.model.toJSON();
|
var modelData = this.model.toJSON();
|
||||||
modelData.notEmpty = ( this.fields.length > 0 );
|
modelData.notEmpty = ( this.fields.length > 0 );
|
||||||
// TODO: move this sort of thing into a toTemplateJSON method on Dataset?
|
// TODO: move this sort of thing into a toTemplateJSON method on Dataset?
|
||||||
modelData.fields = _.map(this.fields, function(field) { return field.toJSON(); });
|
modelData.fields = _.map(this.fields, function(field) {
|
||||||
|
return field.toJSON();
|
||||||
|
});
|
||||||
|
// last header width = scroll bar - border (2px) */
|
||||||
|
modelData.lastHeaderWidth = this.scrollbarDimensions.width - 2;
|
||||||
return modelData;
|
return modelData;
|
||||||
},
|
},
|
||||||
render: function() {
|
render: function() {
|
||||||
@@ -181,6 +189,20 @@ my.Grid = Backbone.View.extend({
|
|||||||
this.fields = this.model.fields.filter(function(field) {
|
this.fields = this.model.fields.filter(function(field) {
|
||||||
return _.indexOf(self.state.get('hiddenFields'), field.id) == -1;
|
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());
|
var htmls = $.mustache(this.template, this.toTemplateJSON());
|
||||||
this.el.html(htmls);
|
this.el.html(htmls);
|
||||||
this.model.currentDocuments.forEach(function(doc) {
|
this.model.currentDocuments.forEach(function(doc) {
|
||||||
@@ -193,8 +215,25 @@ my.Grid = Backbone.View.extend({
|
|||||||
});
|
});
|
||||||
newView.render();
|
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));
|
this.el.find('.recline-grid').toggleClass('no-hidden', (self.state.get('hiddenFields').length === 0));
|
||||||
return this;
|
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> \
|
</div> \
|
||||||
</td> \
|
</td> \
|
||||||
{{#cells}} \
|
{{#cells}} \
|
||||||
<td data-field="{{field}}"> \
|
<td data-field="{{field}}" style="width: {{width}}px; max-width: {{width}}px;"> \
|
||||||
<div class="data-table-cell-content"> \
|
<div class="data-table-cell-content"> \
|
||||||
<a href="javascript:{}" class="data-table-cell-edit" title="Edit this cell"> </a> \
|
<a href="javascript:{}" class="data-table-cell-edit" title="Edit this cell"> </a> \
|
||||||
<div class="data-table-cell-value">{{{value}}}</div> \
|
<div class="data-table-cell-value">{{{value}}}</div> \
|
||||||
@@ -251,6 +290,7 @@ my.GridRow = Backbone.View.extend({
|
|||||||
var cellData = this._fields.map(function(field) {
|
var cellData = this._fields.map(function(field) {
|
||||||
return {
|
return {
|
||||||
field: field.id,
|
field: field.id,
|
||||||
|
width: field.get('width'),
|
||||||
value: doc.getFieldValue(field)
|
value: doc.getFieldValue(field)
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user