Merge branch 'master' into gh-pages
This commit is contained in:
commit
3d2d0fe7c0
@ -20,29 +20,31 @@
|
||||
|
||||
.header .recline-query-editor {
|
||||
float: right;
|
||||
margin: 4px;
|
||||
height: 30px;
|
||||
}
|
||||
|
||||
.header .recline-query-editor label {
|
||||
float: none;
|
||||
.header .recline-query-editor .input-prepend {
|
||||
margin-bottom: auto;
|
||||
}
|
||||
|
||||
.header .recline-query-editor label {
|
||||
float: none;
|
||||
}
|
||||
|
||||
.header .recline-query-editor input.text-query {
|
||||
.header .recline-query-editor .text-query input {
|
||||
float: left;
|
||||
margin-top: 1px;
|
||||
margin-right: 5px;
|
||||
}
|
||||
|
||||
.header .recline-query-editor .pagination input {
|
||||
width: 30px;
|
||||
height: 18px;
|
||||
padding: 2px 4px;
|
||||
margin-top: -4px;
|
||||
}
|
||||
|
||||
.header .recline-query-editor .pagination a {
|
||||
line-height: 28px;
|
||||
line-height: 26px;
|
||||
}
|
||||
|
||||
.header .recline-query-editor form .btn {
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
.data-view-container {
|
||||
@ -50,96 +52,15 @@
|
||||
clear: both;
|
||||
}
|
||||
|
||||
/* bootstrap btn */
|
||||
.btn {
|
||||
cursor: pointer;
|
||||
display: inline-block;
|
||||
background-color: #e6e6e6;
|
||||
background-repeat: no-repeat;
|
||||
background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#ffffff), color-stop(25%, #ffffff), to(#e6e6e6));
|
||||
background-image: -webkit-linear-gradient(#ffffff, #ffffff 25%, #e6e6e6);
|
||||
background-image: -moz-linear-gradient(top, #ffffff, #ffffff 25%, #e6e6e6);
|
||||
background-image: -ms-linear-gradient(#ffffff, #ffffff 25%, #e6e6e6);
|
||||
background-image: -o-linear-gradient(#ffffff, #ffffff 25%, #e6e6e6);
|
||||
background-image: linear-gradient(#ffffff, #ffffff 25%, #e6e6e6);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffff', endColorstr='#e6e6e6', GradientType=0);
|
||||
padding: 5px 14px 6px;
|
||||
text-shadow: 0 1px 1px rgba(255, 255, 255, 0.75);
|
||||
color: #333;
|
||||
font-size: 13px;
|
||||
line-height: normal;
|
||||
border: 1px solid #ccc;
|
||||
border-bottom-color: #bbb;
|
||||
-webkit-border-radius: 4px;
|
||||
-moz-border-radius: 4px;
|
||||
border-radius: 4px;
|
||||
-webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05);
|
||||
-moz-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05);
|
||||
box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05);
|
||||
-webkit-transition: 0.1s linear all;
|
||||
-moz-transition: 0.1s linear all;
|
||||
-ms-transition: 0.1s linear all;
|
||||
-o-transition: 0.1s linear all;
|
||||
transition: 0.1s linear all;
|
||||
}
|
||||
.btn:hover {
|
||||
background-position: 0 -15px;
|
||||
color: #333;
|
||||
text-decoration: none;
|
||||
}
|
||||
.btn:focus {
|
||||
outline: 1px dotted #666;
|
||||
}
|
||||
|
||||
/* twitter btn.disabled but for button link that is active. used in navigation */
|
||||
.active .btn {
|
||||
cursor: default;
|
||||
background-image: none;
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);
|
||||
filter: alpha(opacity=65);
|
||||
-khtml-opacity: 0.65;
|
||||
-moz-opacity: 0.65;
|
||||
opacity: 0.65;
|
||||
-webkit-box-shadow: none;
|
||||
-moz-box-shadow: none;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
|
||||
/**********************************************************
|
||||
* Notifications
|
||||
*********************************************************/
|
||||
|
||||
.notification-container {
|
||||
width: 400px;
|
||||
left: 520px;
|
||||
display: none;
|
||||
position: fixed;
|
||||
top: 0;
|
||||
z-index: 100;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.notification {
|
||||
display: inline-block;
|
||||
margin: 0 auto;
|
||||
padding: 5px 8px 4px;
|
||||
font-size: 1.3em;
|
||||
text-align: left;
|
||||
font-weight: bold;
|
||||
background: #fe8;
|
||||
-moz-border-radius: 2px;
|
||||
-webkit-border-radius: 2px;
|
||||
border-radius: 2px;
|
||||
}
|
||||
|
||||
.notification-action {
|
||||
padding-left: 10px;
|
||||
}
|
||||
|
||||
.notification-loader {
|
||||
padding: 0 3px 0 0;
|
||||
opacity: 0.3;
|
||||
width: 18px;
|
||||
margin-left: 5px;
|
||||
background: url(images/small-spinner.gif) no-repeat;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
|
||||
@ -155,11 +76,13 @@
|
||||
.data-table {
|
||||
border: 1px solid #ccc;
|
||||
font-size: 12px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.data-table td, .data-table th {
|
||||
border-left: 1px solid #ccc;
|
||||
padding: 3px 4px;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.data-table tr td:first-child, .data-table tr th:first-child {
|
||||
|
||||
|
Before Width: | Height: | Size: 1.8 KiB After Width: | Height: | Size: 1.8 KiB |
56
demo/built.html
Normal file
56
demo/built.html
Normal file
@ -0,0 +1,56 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<title>Recline Data Explorer Demo</title>
|
||||
<meta name="description" content="A demo of the Recline Data Explorer">
|
||||
<meta name="author" content="Rufus Pollock and Max Ogden">
|
||||
|
||||
<!-- Le HTML5 shim, for IE6-8 support of HTML elements -->
|
||||
<!--[if lt IE 9]>
|
||||
<script src="http://html5shim.googlecode.com/svn/trunk/html5.js"></script>
|
||||
<![endif]-->
|
||||
<link rel="stylesheet" href="../vendor/bootstrap/2.0.0/css/bootstrap.css">
|
||||
<link rel="stylesheet" href="../css/data-explorer.css">
|
||||
<link rel="stylesheet" href="../css/graph-flot.css">
|
||||
<link rel="stylesheet" href="../css/bootstrap.css">
|
||||
|
||||
<script type="text/javascript" src="../vendor/jquery-1.7.1.js"></script>
|
||||
<script type="text/javascript" src="../vendor/underscore-1.1.6.js"></script>
|
||||
<script type="text/javascript" src="../vendor/backbone-0.5.1.js"></script>
|
||||
<script type="text/javascript" src="../vendor/jquery-ui-1.8.14.custom.min.js"></script>
|
||||
<script type="text/javascript" src="../vendor/jquery.flot-0.7.js"></script>
|
||||
<script type="text/javascript" src="../vendor/jquery.mustache.js"></script>
|
||||
<script type="text/javascript" src="../vendor/bootstrap/2.0.0/bootstrap.js"></script>
|
||||
|
||||
<script type="text/javascript" src="../recline.js"></script>
|
||||
<script type="text/javascript" src="js/app.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div class="navbar navbar-fixed-top">
|
||||
<div class="navbar-inner">
|
||||
<div class="container-fluid">
|
||||
<a class="brand" href="#">Recline Data Explorer</a>
|
||||
<ul class="nav pull-right">
|
||||
<li><a class="set-read-only" title="Put into read-only mode">Read-only</a></li>
|
||||
</ul>
|
||||
<form class="webstore-load pull-right navbar-search" title="Update from the specified webstore dataset">
|
||||
<input type="text" name="source" size="50" />
|
||||
<select name="backend_type">
|
||||
<option value="elasticsearch">ElasticSearch</option>
|
||||
<option value="dataproxy">DataProxy</option>
|
||||
<option value="webstore">Webstore</option>
|
||||
</select>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="container-fluid">
|
||||
<div class="content">
|
||||
<div class="data-explorer-here"></div>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@ -10,31 +10,49 @@
|
||||
<!--[if lt IE 9]>
|
||||
<script src="http://html5shim.googlecode.com/svn/trunk/html5.js"></script>
|
||||
<![endif]-->
|
||||
<link rel="stylesheet" href="../vendor/bootstrap/1.4.0/bootstrap.css">
|
||||
<link rel="stylesheet" href="../vendor/bootstrap/2.0.0/css/bootstrap.css">
|
||||
<link rel="stylesheet" href="../css/data-explorer.css">
|
||||
<link rel="stylesheet" href="../css/graph-flot.css">
|
||||
<link rel="stylesheet" href="../css/bootstrap.css">
|
||||
<link rel="stylesheet" href="../vendor/bootstrap/2.0.0/css/bootstrap-responsive.css">
|
||||
|
||||
<script type="text/javascript" src="../vendor/000-jquery-1.6.1.min.js"></script>
|
||||
<script type="text/javascript" src="../vendor/000-underscore-1.1.6.js"></script>
|
||||
<!-- 3rd party libraries -->
|
||||
<script type="text/javascript" src="../vendor/jquery-1.7.1.js"></script>
|
||||
<script type="text/javascript" src="../vendor/underscore-1.1.6.js"></script>
|
||||
<script type="text/javascript" src="../vendor/backbone-0.5.1.js"></script>
|
||||
<script type="text/javascript" src="../vendor/jquery-ui-1.8.14.custom.min.js"></script>
|
||||
<script type="text/javascript" src="../vendor/jquery.flot-0.7.js"></script>
|
||||
<script type="text/javascript" src="../vendor/jquery.mustache.js"></script>
|
||||
<script type="text/javascript" src="../vendor/bootstrap/1.4.0/bootstrap-alerts.js"></script>
|
||||
<script type="text/javascript" src="../vendor/bootstrap/2.0.0/bootstrap.js"></script>
|
||||
|
||||
<script type="text/javascript" src="../recline.js"></script>
|
||||
<!-- recline library -->
|
||||
<!-- in normal use would just the single recline.js library file. However, for testing it
|
||||
is easier to reference individual files. See built.html for example using just recline.js -->
|
||||
<script type="text/javascript" src="../src/util.js"></script>
|
||||
<script type="text/javascript" src="../src/costco.js"></script>
|
||||
<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/backend/webstore.js"></script>
|
||||
<script type="text/javascript" src="../src/backend/dataproxy.js"></script>
|
||||
<script type="text/javascript" src="../src/backend/elasticsearch.js"></script>
|
||||
<script type="text/javascript" src="../src/view.js"></script>
|
||||
<script type="text/javascript" src="../src/view-grid.js"></script>
|
||||
<script type="text/javascript" src="../src/view-flot-graph.js"></script>
|
||||
<script type="text/javascript" src="../src/view-transform-dialog.js"></script>
|
||||
|
||||
<!-- non-library javascript specific to this demo -->
|
||||
<script type="text/javascript" src="js/app.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div class="topbar">
|
||||
<div class="topbar-inner">
|
||||
<div class="navbar navbar-fixed-top">
|
||||
<div class="navbar-inner">
|
||||
<div class="container-fluid">
|
||||
<a class="brand" href="#">Recline Data Explorer</a>
|
||||
<ul class="nav secondary-nav">
|
||||
<ul class="nav pull-right">
|
||||
<li><a class="set-read-only" title="Put into read-only mode">Read-only</a></li>
|
||||
</ul>
|
||||
<form class="webstore-load pull-right" title="Update from the specified webstore dataset">
|
||||
<form class="webstore-load pull-right navbar-search" title="Update from the specified webstore dataset">
|
||||
<input type="text" name="source" size="50" />
|
||||
<select name="backend_type">
|
||||
<option value="elasticsearch">ElasticSearch</option>
|
||||
|
||||
@ -70,7 +70,7 @@ function demoDataset() {
|
||||
|
||||
function setupLoadFromWebstore(callback) {
|
||||
// pre-populate webstore load form with an example url
|
||||
var demoUrl = 'http://webstore.thedatahub.org/rufuspollock/gold_prices/data';
|
||||
var demoUrl = 'http://thedatahub.org/api/data/b9aae52b-b082-4159-b46f-7bb9c158d013';
|
||||
$('form.webstore-load input[name="source"]').val(demoUrl);
|
||||
$('form.webstore-load').submit(function(e) {
|
||||
e.preventDefault();
|
||||
|
||||
@ -170,7 +170,7 @@ note this.model and dataset returned are the same</p> </td>
|
||||
<span class="nx">my</span><span class="p">.</span><span class="nx">QueryEditor</span> <span class="o">=</span> <span class="nx">Backbone</span><span class="p">.</span><span class="nx">View</span><span class="p">.</span><span class="nx">extend</span><span class="p">({</span>
|
||||
<span class="nx">className</span><span class="o">:</span> <span class="s1">'recline-query-editor'</span><span class="p">,</span>
|
||||
<span class="nx">template</span><span class="o">:</span> <span class="s1">' \</span>
|
||||
<span class="s1"> <form action="" method="GET"> \</span>
|
||||
<span class="s1"> <form action="" method="GET" class="form-inline"> \</span>
|
||||
<span class="s1"> <input type="text" name="q" value="{{q}}" class="text-query" /> \</span>
|
||||
<span class="s1"> <div class="pagination"> \</span>
|
||||
<span class="s1"> <ul> \</span>
|
||||
@ -260,7 +260,7 @@ note this.model and dataset returned are the same</p> </td>
|
||||
<span class="nb">window</span><span class="p">.</span><span class="nx">location</span><span class="p">.</span><span class="nx">hash</span> <span class="o">=</span> <span class="nb">window</span><span class="p">.</span><span class="nx">location</span><span class="p">.</span><span class="nx">hash</span><span class="p">.</span><span class="nx">split</span><span class="p">(</span><span class="s1">'?'</span><span class="p">)[</span><span class="mi">0</span><span class="p">]</span> <span class="o">+</span> <span class="nx">my</span><span class="p">.</span><span class="nx">composeQueryString</span><span class="p">(</span><span class="nx">queryParams</span><span class="p">);</span>
|
||||
<span class="p">}</span></pre></div> </td> </tr> <tr id="section-14"> <td class="docs"> <div class="pilwrap"> <a class="pilcrow" href="#section-14">¶</a> </div> <h2>notify</h2>
|
||||
|
||||
<p>Create a notification (a div.alert-message in div.alert-messsages) using provide messages and options. Options are:</p>
|
||||
<p>Create a notification (a div.alert in div.alert-messsages) using provide messages and options. Options are:</p>
|
||||
|
||||
<ul>
|
||||
<li>category: warning (default), success, error</li>
|
||||
@ -274,12 +274,11 @@ note this.model and dataset returned are the same</p> </td>
|
||||
<span class="p">},</span>
|
||||
<span class="nx">options</span><span class="p">);</span>
|
||||
<span class="kd">var</span> <span class="nx">_template</span> <span class="o">=</span> <span class="s1">' \</span>
|
||||
<span class="s1"> <div class="alert-message {{category}} fade in" data-alert="alert"><a class="close" href="#">×</a> \</span>
|
||||
<span class="s1"> <p>{{msg}} \</span>
|
||||
<span class="s1"> <div class="alert alert-{{category}} fade in" data-alert="alert"><a class="close" data-dismiss="alert" href="#">×</a> \</span>
|
||||
<span class="s1"> {{msg}} \</span>
|
||||
<span class="s1"> {{#loader}} \</span>
|
||||
<span class="s1"> <img src="images/small-spinner.gif" class="notification-loader"> \</span>
|
||||
<span class="s1"> {{/loader}} \</span>
|
||||
<span class="s1"> </p> \</span>
|
||||
<span class="s1"> </div>'</span><span class="p">;</span>
|
||||
<span class="kd">var</span> <span class="nx">_templated</span> <span class="o">=</span> <span class="nx">$</span><span class="p">.</span><span class="nx">mustache</span><span class="p">(</span><span class="nx">_template</span><span class="p">,</span> <span class="nx">tmplData</span><span class="p">);</span>
|
||||
<span class="nx">_templated</span> <span class="o">=</span> <span class="nx">$</span><span class="p">(</span><span class="nx">_templated</span><span class="p">).</span><span class="nx">appendTo</span><span class="p">(</span><span class="nx">$</span><span class="p">(</span><span class="s1">'.data-explorer .alert-messages'</span><span class="p">));</span>
|
||||
@ -293,7 +292,7 @@ note this.model and dataset returned are the same</p> </td>
|
||||
<span class="p">}</span></pre></div> </td> </tr> <tr id="section-15"> <td class="docs"> <div class="pilwrap"> <a class="pilcrow" href="#section-15">¶</a> </div> <h2>clearNotifications</h2>
|
||||
|
||||
<p>Clear all existing notifications</p> </td> <td class="code"> <div class="highlight"><pre><span class="nx">my</span><span class="p">.</span><span class="nx">clearNotifications</span> <span class="o">=</span> <span class="kd">function</span><span class="p">()</span> <span class="p">{</span>
|
||||
<span class="kd">var</span> <span class="nx">$notifications</span> <span class="o">=</span> <span class="nx">$</span><span class="p">(</span><span class="s1">'.data-explorer .alert-message'</span><span class="p">);</span>
|
||||
<span class="kd">var</span> <span class="nx">$notifications</span> <span class="o">=</span> <span class="nx">$</span><span class="p">(</span><span class="s1">'.data-explorer .alert-messages .alert'</span><span class="p">);</span>
|
||||
<span class="nx">$notifications</span><span class="p">.</span><span class="nx">remove</span><span class="p">();</span>
|
||||
<span class="p">}</span>
|
||||
|
||||
|
||||
45
make
45
make
@ -1,15 +1,34 @@
|
||||
#!/bin/bash
|
||||
echo "** Combining js files"
|
||||
cat src/*.js src/backend/*.js > recline.js
|
||||
#!/usr/bin/env python
|
||||
import sys
|
||||
import os
|
||||
|
||||
# build docs
|
||||
echo "** Building docs"
|
||||
docco src/model.js src/view.js src/view-grid.js src/view-flot-graph.js
|
||||
mkdir -p /tmp/recline-docs
|
||||
mkdir -p docs/backend
|
||||
PWD=`pwd`
|
||||
FILES=$PWD/src/backend/*.js
|
||||
DEST=$PWD/docs/backend
|
||||
cd /tmp/recline-docs && docco $FILES && mv docs/* $DEST
|
||||
echo "** Docs built ok"
|
||||
def cat():
|
||||
print("** Combining js files")
|
||||
cmd = 'cat src/*.js src/backend/*.js > recline.js'
|
||||
os.system(cmd)
|
||||
|
||||
def docs():
|
||||
# build docs
|
||||
print("** Building docs")
|
||||
cmd = 'docco src/model.js src/view.js src/view-grid.js src/view-flot-graph.js'
|
||||
os.system(cmd)
|
||||
os.makedirs('/tmp/recline-docs')
|
||||
os.system('mkdir -p docs/backend')
|
||||
files = '%s/src/backend/*.js' % os.getcwd()
|
||||
dest = '%s/docs/backend' % os.getcwd()
|
||||
os.system('cd /tmp/recline-docs && docco %s && mv docs/* %s' % (files, dest))
|
||||
print("** Docs built ok")
|
||||
|
||||
if __name__ == '__main__':
|
||||
if not len(sys.argv) > 1:
|
||||
print 'make cat | docs | all'
|
||||
sys.exit(1)
|
||||
action = sys.argv[1]
|
||||
if action == 'cat':
|
||||
cat()
|
||||
elif action == 'docs':
|
||||
docs()
|
||||
elif action == 'all':
|
||||
cat()
|
||||
docs()
|
||||
|
||||
|
||||
767
recline.js
767
recline.js
@ -812,7 +812,7 @@ my.DataGrid = Backbone.View.extend({
|
||||
template: ' \
|
||||
<div class="data-table-menu-overlay" style="display: none; z-index: 101; "> </div> \
|
||||
<ul class="data-table-menu"></ul> \
|
||||
<table class="data-table" cellspacing="0"> \
|
||||
<table class="data-table table-striped" cellspacing="0"> \
|
||||
<thead> \
|
||||
<tr> \
|
||||
{{#notEmpty}} \
|
||||
@ -872,7 +872,8 @@ my.DataGrid = Backbone.View.extend({
|
||||
// ## DataGridRow View for rendering an individual document.
|
||||
//
|
||||
// Since we want this to update in place it is up to creator to provider the element to attach to.
|
||||
// In addition you must pass in a fields in the constructor options. This should be list of fields for the DataGrid.
|
||||
//
|
||||
// In addition you *must* pass in a FieldList in the constructor options. This should be list of fields for the DataGrid.
|
||||
//
|
||||
// Additional options can be passed in a second hash argument. Options:
|
||||
//
|
||||
@ -881,6 +882,19 @@ my.DataGrid = Backbone.View.extend({
|
||||
// corresponding field object and document is the document object. Note
|
||||
// that implementing functions can ignore arguments (e.g.
|
||||
// function(value) would be a valid cellRenderer function).
|
||||
//
|
||||
// Example:
|
||||
//
|
||||
// <pre>
|
||||
// var row = new DataGridRow({
|
||||
// model: dataset-document,
|
||||
// el: dom-element,
|
||||
// fields: mydatasets.fields // a FieldList object
|
||||
// }, {
|
||||
// cellRenderer: my-cell-renderer-function
|
||||
// }
|
||||
// );
|
||||
// </pre>
|
||||
my.DataGridRow = Backbone.View.extend({
|
||||
initialize: function(initData, options) {
|
||||
_.bindAll(this, 'render');
|
||||
@ -932,9 +946,8 @@ my.DataGridRow = Backbone.View.extend({
|
||||
return this;
|
||||
},
|
||||
|
||||
// Cell Editor
|
||||
// ===========
|
||||
|
||||
// ===================
|
||||
// Cell Editor methods
|
||||
onEditClick: function(e) {
|
||||
var editing = this.el.find('.data-table-cell-editor-editor');
|
||||
if (editing.length > 0) {
|
||||
@ -976,339 +989,6 @@ my.DataGridRow = Backbone.View.extend({
|
||||
this.recline = this.recline || {};
|
||||
this.recline.View = this.recline.View || {};
|
||||
|
||||
(function($, my) {
|
||||
// ## DataExplorer
|
||||
//
|
||||
// The primary view for the entire application. Usage:
|
||||
//
|
||||
// <pre>
|
||||
// var myExplorer = new model.recline.DataExplorer({
|
||||
// model: {{recline.Model.Dataset instance}}
|
||||
// el: {{an existing dom element}}
|
||||
// views: {{page views}}
|
||||
// config: {{config options -- see below}}
|
||||
// });
|
||||
// </pre>
|
||||
//
|
||||
// ### Parameters
|
||||
//
|
||||
// **model**: (required) Dataset instance.
|
||||
//
|
||||
// **el**: (required) DOM element.
|
||||
//
|
||||
// **views**: (optional) the views (Grid, Graph etc) for DataExplorer to
|
||||
// show. This is an array of view hashes. If not provided
|
||||
// just initialize a DataGrid with id 'grid'. Example:
|
||||
//
|
||||
// <pre>
|
||||
// var views = [
|
||||
// {
|
||||
// id: 'grid', // used for routing
|
||||
// label: 'Grid', // used for view switcher
|
||||
// view: new recline.View.DataGrid({
|
||||
// model: dataset
|
||||
// })
|
||||
// },
|
||||
// {
|
||||
// id: 'graph',
|
||||
// label: 'Graph',
|
||||
// view: new recline.View.FlotGraph({
|
||||
// model: dataset
|
||||
// })
|
||||
// }
|
||||
// ];
|
||||
// </pre>
|
||||
//
|
||||
// **config**: Config options like:
|
||||
//
|
||||
// * readOnly: true/false (default: false) value indicating whether to
|
||||
// operate in read-only mode (hiding all editing options).
|
||||
//
|
||||
// NB: the element already being in the DOM is important for rendering of
|
||||
// FlotGraph subview.
|
||||
my.DataExplorer = Backbone.View.extend({
|
||||
template: ' \
|
||||
<div class="data-explorer"> \
|
||||
<div class="alert-messages"></div> \
|
||||
\
|
||||
<div class="header"> \
|
||||
<ul class="navigation"> \
|
||||
{{#views}} \
|
||||
<li><a href="#{{id}}" class="btn">{{label}}</a> \
|
||||
{{/views}} \
|
||||
</ul> \
|
||||
<div class="recline-results-info"> \
|
||||
Results found <span class="doc-count">{{docCount}}</span> \
|
||||
</div> \
|
||||
</div> \
|
||||
<div class="data-view-container"></div> \
|
||||
<div class="dialog-overlay" style="display: none; z-index: 101; "> </div> \
|
||||
<div class="dialog ui-draggable" style="display: none; z-index: 102; top: 101px; "> \
|
||||
<div class="dialog-frame" style="width: 700px; visibility: visible; "> \
|
||||
<div class="dialog-content dialog-border"></div> \
|
||||
</div> \
|
||||
</div> \
|
||||
</div> \
|
||||
',
|
||||
|
||||
initialize: function(options) {
|
||||
var self = this;
|
||||
this.el = $(this.el);
|
||||
this.config = _.extend({
|
||||
readOnly: false
|
||||
},
|
||||
options.config);
|
||||
if (this.config.readOnly) {
|
||||
this.setReadOnly();
|
||||
}
|
||||
// Hash of 'page' views (i.e. those for whole page) keyed by page name
|
||||
if (options.views) {
|
||||
this.pageViews = options.views;
|
||||
} else {
|
||||
this.pageViews = [{
|
||||
id: 'grid',
|
||||
label: 'Grid',
|
||||
view: new my.DataGrid({
|
||||
model: this.model
|
||||
})
|
||||
}];
|
||||
}
|
||||
// this must be called after pageViews are created
|
||||
this.render();
|
||||
|
||||
this.router = new Backbone.Router();
|
||||
this.setupRouting();
|
||||
|
||||
this.model.bind('query:start', function(eventName) {
|
||||
my.notify('Loading data', {loader: true});
|
||||
});
|
||||
this.model.bind('query:done', function(eventName) {
|
||||
my.clearNotifications();
|
||||
self.el.find('.doc-count').text(self.model.docCount || 'Unknown');
|
||||
my.notify('Data loaded', {category: 'success'});
|
||||
});
|
||||
this.model.bind('query:fail', function(eventName, error) {
|
||||
my.clearNotifications();
|
||||
my.notify(error.message, {category: 'error', persist: true});
|
||||
});
|
||||
|
||||
// retrieve basic data like fields etc
|
||||
// note this.model and dataset returned are the same
|
||||
this.model.fetch()
|
||||
.done(function(dataset) {
|
||||
self.el.find('.doc-count').text(self.model.docCount || 'Unknown');
|
||||
self.model.query();
|
||||
})
|
||||
.fail(function(error) {
|
||||
my.notify(error.message, {category: 'error', persist: true});
|
||||
});
|
||||
},
|
||||
|
||||
setReadOnly: function() {
|
||||
this.el.addClass('read-only');
|
||||
},
|
||||
|
||||
render: function() {
|
||||
var tmplData = this.model.toTemplateJSON();
|
||||
tmplData.displayCount = this.config.displayCount;
|
||||
tmplData.views = this.pageViews;
|
||||
var template = $.mustache(this.template, tmplData);
|
||||
$(this.el).html(template);
|
||||
var $dataViewContainer = this.el.find('.data-view-container');
|
||||
_.each(this.pageViews, function(view, pageName) {
|
||||
$dataViewContainer.append(view.view.el)
|
||||
});
|
||||
var queryEditor = new my.QueryEditor({
|
||||
model: this.model.queryState
|
||||
});
|
||||
this.el.find('.header').append(queryEditor.el);
|
||||
},
|
||||
|
||||
setupRouting: function() {
|
||||
var self = this;
|
||||
// Default route
|
||||
this.router.route('', this.pageViews[0].id, function() {
|
||||
self.updateNav(self.pageViews[0].id);
|
||||
});
|
||||
$.each(this.pageViews, function(idx, view) {
|
||||
self.router.route(/^([^?]+)(\?.*)?/, 'view', function(viewId, queryString) {
|
||||
self.updateNav(viewId, queryString);
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
updateNav: function(pageName, queryString) {
|
||||
this.el.find('.navigation li').removeClass('active');
|
||||
var $el = this.el.find('.navigation li a[href=#' + pageName + ']');
|
||||
$el.parent().addClass('active');
|
||||
// show the specific page
|
||||
_.each(this.pageViews, function(view, idx) {
|
||||
if (view.id === pageName) {
|
||||
view.view.el.show();
|
||||
} else {
|
||||
view.view.el.hide();
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
my.QueryEditor = Backbone.View.extend({
|
||||
className: 'recline-query-editor',
|
||||
template: ' \
|
||||
<form action="" method="GET"> \
|
||||
<input type="text" name="q" value="{{q}}" class="text-query" /> \
|
||||
<div class="pagination"> \
|
||||
<ul> \
|
||||
<li class="prev action-pagination-update"><a>« back</a></li> \
|
||||
<li class="active"><a><input name="from" type="text" value="{{from}}" /> – <input name="to" type="text" value="{{to}}" /> </a></li> \
|
||||
<li class="next action-pagination-update"><a>next »</a></li> \
|
||||
</ul> \
|
||||
</div> \
|
||||
<button type="submit" class="btn" style="">Update »</button> \
|
||||
</form> \
|
||||
',
|
||||
|
||||
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 newFrom = parseInt(this.el.find('input[name="from"]').val());
|
||||
var newSize = parseInt(this.el.find('input[name="to"]').val()) - newFrom;
|
||||
var query = this.el.find('.text-query').val();
|
||||
this.model.set({size: newSize, from: newFrom, q: query});
|
||||
},
|
||||
onPaginationUpdate: function(e) {
|
||||
e.preventDefault();
|
||||
var $el = $(e.target);
|
||||
if ($el.parent().hasClass('prev')) {
|
||||
var newFrom = this.model.get('from') - Math.max(0, this.model.get('size'));
|
||||
} else {
|
||||
var 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(this.template, tmplData);
|
||||
this.el.html(templated);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
/* ========================================================== */
|
||||
// ## Miscellaneous Utilities
|
||||
|
||||
var urlPathRegex = /^([^?]+)(\?.*)?/;
|
||||
|
||||
// Parse the Hash section of a URL into path and query string
|
||||
my.parseHashUrl = function(hashUrl) {
|
||||
var parsed = urlPathRegex.exec(hashUrl);
|
||||
if (parsed == null) {
|
||||
return {};
|
||||
} else {
|
||||
return {
|
||||
path: parsed[1],
|
||||
query: parsed[2] || ''
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Parse a URL query string (?xyz=abc...) into a dictionary.
|
||||
my.parseQueryString = function(q) {
|
||||
var urlParams = {},
|
||||
e, d = function (s) {
|
||||
return unescape(s.replace(/\+/g, " "));
|
||||
},
|
||||
r = /([^&=]+)=?([^&]*)/g;
|
||||
|
||||
if (q && q.length && q[0] === '?') {
|
||||
q = q.slice(1);
|
||||
}
|
||||
while (e = r.exec(q)) {
|
||||
// TODO: have values be array as query string allow repetition of keys
|
||||
urlParams[d(e[1])] = d(e[2]);
|
||||
}
|
||||
return urlParams;
|
||||
}
|
||||
|
||||
// Parse the query string out of the URL hash
|
||||
my.parseHashQueryString = function() {
|
||||
q = my.parseHashUrl(window.location.hash).query;
|
||||
return my.parseQueryString(q);
|
||||
}
|
||||
|
||||
// Compse a Query String
|
||||
my.composeQueryString = function(queryParams) {
|
||||
var queryString = '?';
|
||||
var items = [];
|
||||
$.each(queryParams, function(key, value) {
|
||||
items.push(key + '=' + JSON.stringify(value));
|
||||
});
|
||||
queryString += items.join('&');
|
||||
return queryString;
|
||||
}
|
||||
|
||||
my.setHashQueryString = function(queryParams) {
|
||||
window.location.hash = window.location.hash.split('?')[0] + my.composeQueryString(queryParams);
|
||||
}
|
||||
|
||||
// ## notify
|
||||
//
|
||||
// Create a notification (a div.alert-message in div.alert-messsages) using provide messages and options. Options are:
|
||||
//
|
||||
// * category: warning (default), success, error
|
||||
// * persist: if true alert is persistent, o/w hidden after 3s (default = false)
|
||||
// * loader: if true show loading spinner
|
||||
my.notify = function(message, options) {
|
||||
if (!options) var options = {};
|
||||
var tmplData = _.extend({
|
||||
msg: message,
|
||||
category: 'warning'
|
||||
},
|
||||
options);
|
||||
var _template = ' \
|
||||
<div class="alert-message {{category}} fade in" data-alert="alert"><a class="close" href="#">×</a> \
|
||||
<p>{{msg}} \
|
||||
{{#loader}} \
|
||||
<img src="images/small-spinner.gif" class="notification-loader"> \
|
||||
{{/loader}} \
|
||||
</p> \
|
||||
</div>';
|
||||
var _templated = $.mustache(_template, tmplData);
|
||||
_templated = $(_templated).appendTo($('.data-explorer .alert-messages'));
|
||||
if (!options.persist) {
|
||||
setTimeout(function() {
|
||||
$(_templated).fadeOut(1000, function() {
|
||||
$(this).remove();
|
||||
});
|
||||
}, 1000);
|
||||
}
|
||||
}
|
||||
|
||||
// ## clearNotifications
|
||||
//
|
||||
// Clear all existing notifications
|
||||
my.clearNotifications = function() {
|
||||
var $notifications = $('.data-explorer .alert-message');
|
||||
$notifications.remove();
|
||||
}
|
||||
|
||||
})(jQuery, recline.View);
|
||||
|
||||
this.recline = this.recline || {};
|
||||
this.recline.View = this.recline.View || {};
|
||||
|
||||
// Views module following classic module pattern
|
||||
(function($, my) {
|
||||
|
||||
@ -1512,6 +1192,356 @@ my.ColumnTransform = Backbone.View.extend({
|
||||
});
|
||||
|
||||
})(jQuery, recline.View);
|
||||
this.recline = this.recline || {};
|
||||
this.recline.View = this.recline.View || {};
|
||||
|
||||
(function($, my) {
|
||||
// ## DataExplorer
|
||||
//
|
||||
// The primary view for the entire application. Usage:
|
||||
//
|
||||
// <pre>
|
||||
// var myExplorer = new model.recline.DataExplorer({
|
||||
// model: {{recline.Model.Dataset instance}}
|
||||
// el: {{an existing dom element}}
|
||||
// views: {{page views}}
|
||||
// config: {{config options -- see below}}
|
||||
// });
|
||||
// </pre>
|
||||
//
|
||||
// ### Parameters
|
||||
//
|
||||
// **model**: (required) Dataset instance.
|
||||
//
|
||||
// **el**: (required) DOM element.
|
||||
//
|
||||
// **views**: (optional) the views (Grid, Graph etc) for DataExplorer to
|
||||
// show. This is an array of view hashes. If not provided
|
||||
// just initialize a DataGrid with id 'grid'. Example:
|
||||
//
|
||||
// <pre>
|
||||
// var views = [
|
||||
// {
|
||||
// id: 'grid', // used for routing
|
||||
// label: 'Grid', // used for view switcher
|
||||
// view: new recline.View.DataGrid({
|
||||
// model: dataset
|
||||
// })
|
||||
// },
|
||||
// {
|
||||
// id: 'graph',
|
||||
// label: 'Graph',
|
||||
// view: new recline.View.FlotGraph({
|
||||
// model: dataset
|
||||
// })
|
||||
// }
|
||||
// ];
|
||||
// </pre>
|
||||
//
|
||||
// **config**: Config options like:
|
||||
//
|
||||
// * readOnly: true/false (default: false) value indicating whether to
|
||||
// operate in read-only mode (hiding all editing options).
|
||||
//
|
||||
// NB: the element already being in the DOM is important for rendering of
|
||||
// FlotGraph subview.
|
||||
my.DataExplorer = Backbone.View.extend({
|
||||
template: ' \
|
||||
<div class="data-explorer"> \
|
||||
<div class="alert-messages"></div> \
|
||||
\
|
||||
<div class="header"> \
|
||||
<ul class="navigation"> \
|
||||
{{#views}} \
|
||||
<li><a href="#{{id}}" class="btn">{{label}}</a> \
|
||||
{{/views}} \
|
||||
</ul> \
|
||||
<div class="recline-results-info"> \
|
||||
Results found <span class="doc-count">{{docCount}}</span> \
|
||||
</div> \
|
||||
</div> \
|
||||
<div class="data-view-container"></div> \
|
||||
<div class="dialog-overlay" style="display: none; z-index: 101; "> </div> \
|
||||
<div class="dialog ui-draggable" style="display: none; z-index: 102; top: 101px; "> \
|
||||
<div class="dialog-frame" style="width: 700px; visibility: visible; "> \
|
||||
<div class="dialog-content dialog-border"></div> \
|
||||
</div> \
|
||||
</div> \
|
||||
</div> \
|
||||
',
|
||||
|
||||
initialize: function(options) {
|
||||
var self = this;
|
||||
this.el = $(this.el);
|
||||
this.config = _.extend({
|
||||
readOnly: false
|
||||
},
|
||||
options.config);
|
||||
if (this.config.readOnly) {
|
||||
this.setReadOnly();
|
||||
}
|
||||
// Hash of 'page' views (i.e. those for whole page) keyed by page name
|
||||
if (options.views) {
|
||||
this.pageViews = options.views;
|
||||
} else {
|
||||
this.pageViews = [{
|
||||
id: 'grid',
|
||||
label: 'Grid',
|
||||
view: new my.DataGrid({
|
||||
model: this.model
|
||||
})
|
||||
}];
|
||||
}
|
||||
// this must be called after pageViews are created
|
||||
this.render();
|
||||
|
||||
this.router = new Backbone.Router();
|
||||
this.setupRouting();
|
||||
|
||||
this.model.bind('query:start', function() {
|
||||
my.notify('Loading data', {loader: true});
|
||||
});
|
||||
this.model.bind('query:done', function() {
|
||||
my.clearNotifications();
|
||||
self.el.find('.doc-count').text(self.model.docCount || 'Unknown');
|
||||
my.notify('Data loaded', {category: 'success'});
|
||||
});
|
||||
this.model.bind('query:fail', function(error) {
|
||||
my.clearNotifications();
|
||||
var msg = '';
|
||||
if (typeof(error) == 'string') {
|
||||
msg = error;
|
||||
} else if (typeof(error) == 'object') {
|
||||
if (error.title) {
|
||||
msg = error.title + ': ';
|
||||
}
|
||||
if (error.message) {
|
||||
msg += error.message;
|
||||
}
|
||||
} else {
|
||||
msg = 'There was an error querying the backend';
|
||||
}
|
||||
my.notify(msg, {category: 'error', persist: true});
|
||||
});
|
||||
|
||||
// retrieve basic data like fields etc
|
||||
// note this.model and dataset returned are the same
|
||||
this.model.fetch()
|
||||
.done(function(dataset) {
|
||||
self.el.find('.doc-count').text(self.model.docCount || 'Unknown');
|
||||
self.model.query();
|
||||
})
|
||||
.fail(function(error) {
|
||||
my.notify(error.message, {category: 'error', persist: true});
|
||||
});
|
||||
},
|
||||
|
||||
setReadOnly: function() {
|
||||
this.el.addClass('read-only');
|
||||
},
|
||||
|
||||
render: function() {
|
||||
var tmplData = this.model.toTemplateJSON();
|
||||
tmplData.displayCount = this.config.displayCount;
|
||||
tmplData.views = this.pageViews;
|
||||
var template = $.mustache(this.template, tmplData);
|
||||
$(this.el).html(template);
|
||||
var $dataViewContainer = this.el.find('.data-view-container');
|
||||
_.each(this.pageViews, function(view, pageName) {
|
||||
$dataViewContainer.append(view.view.el)
|
||||
});
|
||||
var queryEditor = new my.QueryEditor({
|
||||
model: this.model.queryState
|
||||
});
|
||||
this.el.find('.header').append(queryEditor.el);
|
||||
},
|
||||
|
||||
setupRouting: function() {
|
||||
var self = this;
|
||||
// Default route
|
||||
this.router.route('', this.pageViews[0].id, function() {
|
||||
self.updateNav(self.pageViews[0].id);
|
||||
});
|
||||
$.each(this.pageViews, function(idx, view) {
|
||||
self.router.route(/^([^?]+)(\?.*)?/, 'view', function(viewId, queryString) {
|
||||
self.updateNav(viewId, queryString);
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
updateNav: function(pageName, queryString) {
|
||||
this.el.find('.navigation li').removeClass('active');
|
||||
this.el.find('.navigation li a').removeClass('disabled');
|
||||
var $el = this.el.find('.navigation li a[href=#' + pageName + ']');
|
||||
$el.parent().addClass('active');
|
||||
$el.addClass('disabled');
|
||||
// show the specific page
|
||||
_.each(this.pageViews, function(view, idx) {
|
||||
if (view.id === pageName) {
|
||||
view.view.el.show();
|
||||
} else {
|
||||
view.view.el.hide();
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
my.QueryEditor = Backbone.View.extend({
|
||||
className: 'recline-query-editor',
|
||||
template: ' \
|
||||
<form action="" method="GET" class="form-inline"> \
|
||||
<div class="input-prepend text-query"> \
|
||||
<span class="add-on"><i class="icon-search"></i></span> \
|
||||
<input type="text" name="q" value="{{q}}" class="span2" placeholder="Search data ..." class="search-query" /> \
|
||||
</div> \
|
||||
<div class="pagination"> \
|
||||
<ul> \
|
||||
<li class="prev action-pagination-update"><a>«</a></li> \
|
||||
<li class="active"><a><input name="from" type="text" value="{{from}}" /> – <input name="to" type="text" value="{{to}}" /> </a></li> \
|
||||
<li class="next action-pagination-update"><a>»</a></li> \
|
||||
</ul> \
|
||||
</div> \
|
||||
<button type="submit" class="btn" style="">Update »</button> \
|
||||
</form> \
|
||||
',
|
||||
|
||||
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 newFrom = parseInt(this.el.find('input[name="from"]').val());
|
||||
var newSize = parseInt(this.el.find('input[name="to"]').val()) - newFrom;
|
||||
var query = this.el.find('.text-query input').val();
|
||||
this.model.set({size: newSize, from: newFrom, q: query});
|
||||
},
|
||||
onPaginationUpdate: function(e) {
|
||||
e.preventDefault();
|
||||
var $el = $(e.target);
|
||||
if ($el.parent().hasClass('prev')) {
|
||||
var newFrom = this.model.get('from') - Math.max(0, this.model.get('size'));
|
||||
} else {
|
||||
var 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(this.template, tmplData);
|
||||
this.el.html(templated);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
/* ========================================================== */
|
||||
// ## Miscellaneous Utilities
|
||||
|
||||
var urlPathRegex = /^([^?]+)(\?.*)?/;
|
||||
|
||||
// Parse the Hash section of a URL into path and query string
|
||||
my.parseHashUrl = function(hashUrl) {
|
||||
var parsed = urlPathRegex.exec(hashUrl);
|
||||
if (parsed == null) {
|
||||
return {};
|
||||
} else {
|
||||
return {
|
||||
path: parsed[1],
|
||||
query: parsed[2] || ''
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Parse a URL query string (?xyz=abc...) into a dictionary.
|
||||
my.parseQueryString = function(q) {
|
||||
var urlParams = {},
|
||||
e, d = function (s) {
|
||||
return unescape(s.replace(/\+/g, " "));
|
||||
},
|
||||
r = /([^&=]+)=?([^&]*)/g;
|
||||
|
||||
if (q && q.length && q[0] === '?') {
|
||||
q = q.slice(1);
|
||||
}
|
||||
while (e = r.exec(q)) {
|
||||
// TODO: have values be array as query string allow repetition of keys
|
||||
urlParams[d(e[1])] = d(e[2]);
|
||||
}
|
||||
return urlParams;
|
||||
}
|
||||
|
||||
// Parse the query string out of the URL hash
|
||||
my.parseHashQueryString = function() {
|
||||
q = my.parseHashUrl(window.location.hash).query;
|
||||
return my.parseQueryString(q);
|
||||
}
|
||||
|
||||
// Compse a Query String
|
||||
my.composeQueryString = function(queryParams) {
|
||||
var queryString = '?';
|
||||
var items = [];
|
||||
$.each(queryParams, function(key, value) {
|
||||
items.push(key + '=' + JSON.stringify(value));
|
||||
});
|
||||
queryString += items.join('&');
|
||||
return queryString;
|
||||
}
|
||||
|
||||
my.setHashQueryString = function(queryParams) {
|
||||
window.location.hash = window.location.hash.split('?')[0] + my.composeQueryString(queryParams);
|
||||
}
|
||||
|
||||
// ## notify
|
||||
//
|
||||
// Create a notification (a div.alert in div.alert-messsages) using provide messages and options. Options are:
|
||||
//
|
||||
// * category: warning (default), success, error
|
||||
// * persist: if true alert is persistent, o/w hidden after 3s (default = false)
|
||||
// * loader: if true show loading spinner
|
||||
my.notify = function(message, options) {
|
||||
if (!options) var options = {};
|
||||
var tmplData = _.extend({
|
||||
msg: message,
|
||||
category: 'warning'
|
||||
},
|
||||
options);
|
||||
var _template = ' \
|
||||
<div class="alert alert-{{category}} fade in" data-alert="alert"><a class="close" data-dismiss="alert" href="#">×</a> \
|
||||
{{msg}} \
|
||||
{{#loader}} \
|
||||
<span class="notification-loader"> </span> \
|
||||
{{/loader}} \
|
||||
</div>';
|
||||
var _templated = $.mustache(_template, tmplData);
|
||||
_templated = $(_templated).appendTo($('.data-explorer .alert-messages'));
|
||||
if (!options.persist) {
|
||||
setTimeout(function() {
|
||||
$(_templated).fadeOut(1000, function() {
|
||||
$(this).remove();
|
||||
});
|
||||
}, 1000);
|
||||
}
|
||||
}
|
||||
|
||||
// ## clearNotifications
|
||||
//
|
||||
// Clear all existing notifications
|
||||
my.clearNotifications = function() {
|
||||
var $notifications = $('.data-explorer .alert-messages .alert');
|
||||
$notifications.remove();
|
||||
}
|
||||
|
||||
})(jQuery, recline.View);
|
||||
|
||||
// # Recline Backends
|
||||
//
|
||||
// Backends are connectors to backend data sources and stores
|
||||
@ -1580,29 +1610,10 @@ this.recline.Backend = this.recline.Backend || {};
|
||||
var self = this;
|
||||
if (method === "read") {
|
||||
if (model.__type__ == 'Dataset') {
|
||||
var base = self.get('dataproxy_url');
|
||||
// TODO: should we cache for extra efficiency
|
||||
var data = {
|
||||
url: model.get('url')
|
||||
, 'max-results': 1
|
||||
, type: model.get('format') || 'csv'
|
||||
};
|
||||
var jqxhr = $.ajax({
|
||||
url: base
|
||||
, data: data
|
||||
, dataType: 'jsonp'
|
||||
});
|
||||
// Do nothing as we will get fields in query step (and no metadata to
|
||||
// retrieve)
|
||||
var dfd = $.Deferred();
|
||||
my.wrapInTimeout(jqxhr).done(function(results) {
|
||||
model.fields.reset(_.map(results.fields, function(fieldId) {
|
||||
return {id: fieldId};
|
||||
})
|
||||
);
|
||||
dfd.resolve(model, jqxhr);
|
||||
})
|
||||
.fail(function(arguments) {
|
||||
dfd.reject(arguments);
|
||||
});
|
||||
dfd.resolve(model);
|
||||
return dfd.promise();
|
||||
}
|
||||
} else {
|
||||
@ -1622,7 +1633,14 @@ this.recline.Backend = this.recline.Backend || {};
|
||||
, dataType: 'jsonp'
|
||||
});
|
||||
var dfd = $.Deferred();
|
||||
jqxhr.done(function(results) {
|
||||
my.wrapInTimeout(jqxhr).done(function(results) {
|
||||
if (results.error) {
|
||||
dfd.reject(results.error);
|
||||
}
|
||||
dataset.fields.reset(_.map(results.fields, function(fieldId) {
|
||||
return {id: fieldId};
|
||||
})
|
||||
);
|
||||
var _out = _.map(results.data, function(doc) {
|
||||
var tmp = {};
|
||||
_.each(results.fields, function(key, idx) {
|
||||
@ -1631,6 +1649,9 @@ this.recline.Backend = this.recline.Backend || {};
|
||||
return tmp;
|
||||
});
|
||||
dfd.resolve(_out);
|
||||
})
|
||||
.fail(function(arguments) {
|
||||
dfd.reject(arguments);
|
||||
});
|
||||
return dfd.promise();
|
||||
}
|
||||
@ -1645,10 +1666,21 @@ this.recline.Backend = this.recline.Backend || {};
|
||||
(function($, my) {
|
||||
// ## ElasticSearch Backend
|
||||
//
|
||||
// Connecting to [ElasticSearch](http://www.elasticsearch.org/)
|
||||
// Connecting to [ElasticSearch](http://www.elasticsearch.org/).
|
||||
//
|
||||
// To use this backend ensure your Dataset has a elasticsearch_url,
|
||||
// webstore_url or url attribute (used in that order)
|
||||
// To use this backend ensure your Dataset has one of the following
|
||||
// attributes (first one found is used):
|
||||
//
|
||||
// <pre>
|
||||
// elasticsearch_url
|
||||
// webstore_url
|
||||
// url
|
||||
// </pre>
|
||||
//
|
||||
// This should point to the ES type url. E.G. for ES running on
|
||||
// localhost:9200 with index twitter and type tweet it would be
|
||||
//
|
||||
// <pre>http://localhost:9200/twitter/tweet</pre>
|
||||
my.ElasticSearch = Backbone.Model.extend({
|
||||
_getESUrl: function(dataset) {
|
||||
var out = dataset.get('elasticsearch_url');
|
||||
@ -1861,15 +1893,12 @@ this.recline.Backend = this.recline.Backend || {};
|
||||
(function($, my) {
|
||||
// ## Memory Backend - uses in-memory data
|
||||
//
|
||||
// This is very artificial and is really only designed for testing
|
||||
// purposes.
|
||||
//
|
||||
// To use it you should provide in your constructor data:
|
||||
//
|
||||
// * metadata (including fields array)
|
||||
// * documents: list of hashes, each hash being one doc. A doc *must* have an id attribute which is unique.
|
||||
//
|
||||
// Example:
|
||||
// Example:
|
||||
//
|
||||
// <pre>
|
||||
// // Backend setup
|
||||
@ -1886,7 +1915,7 @@ this.recline.Backend = this.recline.Backend || {};
|
||||
// ]
|
||||
// });
|
||||
// // later ...
|
||||
// var dataset = Dataset({id: 'my-id'});
|
||||
// var dataset = Dataset({id: 'my-id'}, 'memory');
|
||||
// dataset.fetch();
|
||||
// etc ...
|
||||
// </pre>
|
||||
|
||||
@ -24,29 +24,10 @@ this.recline.Backend = this.recline.Backend || {};
|
||||
var self = this;
|
||||
if (method === "read") {
|
||||
if (model.__type__ == 'Dataset') {
|
||||
var base = self.get('dataproxy_url');
|
||||
// TODO: should we cache for extra efficiency
|
||||
var data = {
|
||||
url: model.get('url')
|
||||
, 'max-results': 1
|
||||
, type: model.get('format') || 'csv'
|
||||
};
|
||||
var jqxhr = $.ajax({
|
||||
url: base
|
||||
, data: data
|
||||
, dataType: 'jsonp'
|
||||
});
|
||||
// Do nothing as we will get fields in query step (and no metadata to
|
||||
// retrieve)
|
||||
var dfd = $.Deferred();
|
||||
my.wrapInTimeout(jqxhr).done(function(results) {
|
||||
model.fields.reset(_.map(results.fields, function(fieldId) {
|
||||
return {id: fieldId};
|
||||
})
|
||||
);
|
||||
dfd.resolve(model, jqxhr);
|
||||
})
|
||||
.fail(function(arguments) {
|
||||
dfd.reject(arguments);
|
||||
});
|
||||
dfd.resolve(model);
|
||||
return dfd.promise();
|
||||
}
|
||||
} else {
|
||||
@ -66,7 +47,14 @@ this.recline.Backend = this.recline.Backend || {};
|
||||
, dataType: 'jsonp'
|
||||
});
|
||||
var dfd = $.Deferred();
|
||||
jqxhr.done(function(results) {
|
||||
my.wrapInTimeout(jqxhr).done(function(results) {
|
||||
if (results.error) {
|
||||
dfd.reject(results.error);
|
||||
}
|
||||
dataset.fields.reset(_.map(results.fields, function(fieldId) {
|
||||
return {id: fieldId};
|
||||
})
|
||||
);
|
||||
var _out = _.map(results.data, function(doc) {
|
||||
var tmp = {};
|
||||
_.each(results.fields, function(key, idx) {
|
||||
@ -75,6 +63,9 @@ this.recline.Backend = this.recline.Backend || {};
|
||||
return tmp;
|
||||
});
|
||||
dfd.resolve(_out);
|
||||
})
|
||||
.fail(function(arguments) {
|
||||
dfd.reject(arguments);
|
||||
});
|
||||
return dfd.promise();
|
||||
}
|
||||
|
||||
@ -160,7 +160,7 @@ my.DataGrid = Backbone.View.extend({
|
||||
template: ' \
|
||||
<div class="data-table-menu-overlay" style="display: none; z-index: 101; "> </div> \
|
||||
<ul class="data-table-menu"></ul> \
|
||||
<table class="data-table" cellspacing="0"> \
|
||||
<table class="data-table table-striped" cellspacing="0"> \
|
||||
<thead> \
|
||||
<tr> \
|
||||
{{#notEmpty}} \
|
||||
|
||||
47
src/view.js
47
src/view.js
@ -104,17 +104,30 @@ my.DataExplorer = Backbone.View.extend({
|
||||
this.router = new Backbone.Router();
|
||||
this.setupRouting();
|
||||
|
||||
this.model.bind('query:start', function(eventName) {
|
||||
this.model.bind('query:start', function() {
|
||||
my.notify('Loading data', {loader: true});
|
||||
});
|
||||
this.model.bind('query:done', function(eventName) {
|
||||
this.model.bind('query:done', function() {
|
||||
my.clearNotifications();
|
||||
self.el.find('.doc-count').text(self.model.docCount || 'Unknown');
|
||||
my.notify('Data loaded', {category: 'success'});
|
||||
});
|
||||
this.model.bind('query:fail', function(eventName, error) {
|
||||
this.model.bind('query:fail', function(error) {
|
||||
my.clearNotifications();
|
||||
my.notify(error.message, {category: 'error', persist: true});
|
||||
var msg = '';
|
||||
if (typeof(error) == 'string') {
|
||||
msg = error;
|
||||
} else if (typeof(error) == 'object') {
|
||||
if (error.title) {
|
||||
msg = error.title + ': ';
|
||||
}
|
||||
if (error.message) {
|
||||
msg += error.message;
|
||||
}
|
||||
} else {
|
||||
msg = 'There was an error querying the backend';
|
||||
}
|
||||
my.notify(msg, {category: 'error', persist: true});
|
||||
});
|
||||
|
||||
// retrieve basic data like fields etc
|
||||
@ -164,8 +177,10 @@ my.DataExplorer = Backbone.View.extend({
|
||||
|
||||
updateNav: function(pageName, queryString) {
|
||||
this.el.find('.navigation li').removeClass('active');
|
||||
this.el.find('.navigation li a').removeClass('disabled');
|
||||
var $el = this.el.find('.navigation li a[href=#' + pageName + ']');
|
||||
$el.parent().addClass('active');
|
||||
$el.addClass('disabled');
|
||||
// show the specific page
|
||||
_.each(this.pageViews, function(view, idx) {
|
||||
if (view.id === pageName) {
|
||||
@ -181,13 +196,16 @@ my.DataExplorer = Backbone.View.extend({
|
||||
my.QueryEditor = Backbone.View.extend({
|
||||
className: 'recline-query-editor',
|
||||
template: ' \
|
||||
<form action="" method="GET"> \
|
||||
<input type="text" name="q" value="{{q}}" class="text-query" /> \
|
||||
<form action="" method="GET" class="form-inline"> \
|
||||
<div class="input-prepend text-query"> \
|
||||
<span class="add-on"><i class="icon-search"></i></span> \
|
||||
<input type="text" name="q" value="{{q}}" class="span2" placeholder="Search data ..." class="search-query" /> \
|
||||
</div> \
|
||||
<div class="pagination"> \
|
||||
<ul> \
|
||||
<li class="prev action-pagination-update"><a>« back</a></li> \
|
||||
<li class="prev action-pagination-update"><a>«</a></li> \
|
||||
<li class="active"><a><input name="from" type="text" value="{{from}}" /> – <input name="to" type="text" value="{{to}}" /> </a></li> \
|
||||
<li class="next action-pagination-update"><a>next »</a></li> \
|
||||
<li class="next action-pagination-update"><a>»</a></li> \
|
||||
</ul> \
|
||||
</div> \
|
||||
<button type="submit" class="btn" style="">Update »</button> \
|
||||
@ -209,7 +227,7 @@ my.QueryEditor = Backbone.View.extend({
|
||||
e.preventDefault();
|
||||
var newFrom = parseInt(this.el.find('input[name="from"]').val());
|
||||
var newSize = parseInt(this.el.find('input[name="to"]').val()) - newFrom;
|
||||
var query = this.el.find('.text-query').val();
|
||||
var query = this.el.find('.text-query input').val();
|
||||
this.model.set({size: newSize, from: newFrom, q: query});
|
||||
},
|
||||
onPaginationUpdate: function(e) {
|
||||
@ -290,7 +308,7 @@ my.setHashQueryString = function(queryParams) {
|
||||
|
||||
// ## notify
|
||||
//
|
||||
// Create a notification (a div.alert-message in div.alert-messsages) using provide messages and options. Options are:
|
||||
// Create a notification (a div.alert in div.alert-messsages) using provide messages and options. Options are:
|
||||
//
|
||||
// * category: warning (default), success, error
|
||||
// * persist: if true alert is persistent, o/w hidden after 3s (default = false)
|
||||
@ -303,12 +321,11 @@ my.notify = function(message, options) {
|
||||
},
|
||||
options);
|
||||
var _template = ' \
|
||||
<div class="alert-message {{category}} fade in" data-alert="alert"><a class="close" href="#">×</a> \
|
||||
<p>{{msg}} \
|
||||
<div class="alert alert-{{category}} fade in" data-alert="alert"><a class="close" data-dismiss="alert" href="#">×</a> \
|
||||
{{msg}} \
|
||||
{{#loader}} \
|
||||
<img src="images/small-spinner.gif" class="notification-loader"> \
|
||||
<span class="notification-loader"> </span> \
|
||||
{{/loader}} \
|
||||
</p> \
|
||||
</div>';
|
||||
var _templated = $.mustache(_template, tmplData);
|
||||
_templated = $(_templated).appendTo($('.data-explorer .alert-messages'));
|
||||
@ -325,7 +342,7 @@ my.notify = function(message, options) {
|
||||
//
|
||||
// Clear all existing notifications
|
||||
my.clearNotifications = function() {
|
||||
var $notifications = $('.data-explorer .alert-message');
|
||||
var $notifications = $('.data-explorer .alert-messages .alert');
|
||||
$notifications.remove();
|
||||
}
|
||||
|
||||
|
||||
18
vendor/000-jquery-1.6.1.min.js
vendored
18
vendor/000-jquery-1.6.1.min.js
vendored
File diff suppressed because one or more lines are too long
113
vendor/bootstrap/1.4.0/bootstrap-alerts.js
vendored
113
vendor/bootstrap/1.4.0/bootstrap-alerts.js
vendored
@ -1,113 +0,0 @@
|
||||
/* ==========================================================
|
||||
* bootstrap-alerts.js v1.4.0
|
||||
* http://twitter.github.com/bootstrap/javascript.html#alerts
|
||||
* ==========================================================
|
||||
* Copyright 2011 Twitter, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
* ========================================================== */
|
||||
|
||||
|
||||
!function( $ ){
|
||||
|
||||
"use strict"
|
||||
|
||||
/* CSS TRANSITION SUPPORT (https://gist.github.com/373874)
|
||||
* ======================================================= */
|
||||
|
||||
var transitionEnd
|
||||
|
||||
$(document).ready(function () {
|
||||
|
||||
$.support.transition = (function () {
|
||||
var thisBody = document.body || document.documentElement
|
||||
, thisStyle = thisBody.style
|
||||
, support = thisStyle.transition !== undefined || thisStyle.WebkitTransition !== undefined || thisStyle.MozTransition !== undefined || thisStyle.MsTransition !== undefined || thisStyle.OTransition !== undefined
|
||||
return support
|
||||
})()
|
||||
|
||||
// set CSS transition event type
|
||||
if ( $.support.transition ) {
|
||||
transitionEnd = "TransitionEnd"
|
||||
if ( $.browser.webkit ) {
|
||||
transitionEnd = "webkitTransitionEnd"
|
||||
} else if ( $.browser.mozilla ) {
|
||||
transitionEnd = "transitionend"
|
||||
} else if ( $.browser.opera ) {
|
||||
transitionEnd = "oTransitionEnd"
|
||||
}
|
||||
}
|
||||
|
||||
})
|
||||
|
||||
/* ALERT CLASS DEFINITION
|
||||
* ====================== */
|
||||
|
||||
var Alert = function ( content, options ) {
|
||||
this.settings = $.extend({}, $.fn.alert.defaults, options)
|
||||
this.$element = $(content)
|
||||
.delegate(this.settings.selector, 'click', this.close)
|
||||
}
|
||||
|
||||
Alert.prototype = {
|
||||
|
||||
close: function (e) {
|
||||
var $element = $(this).parent('.alert-message')
|
||||
|
||||
e && e.preventDefault()
|
||||
$element.removeClass('in')
|
||||
|
||||
function removeElement () {
|
||||
$element.remove()
|
||||
}
|
||||
|
||||
$.support.transition && $element.hasClass('fade') ?
|
||||
$element.bind(transitionEnd, removeElement) :
|
||||
removeElement()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
/* ALERT PLUGIN DEFINITION
|
||||
* ======================= */
|
||||
|
||||
$.fn.alert = function ( options ) {
|
||||
|
||||
if ( options === true ) {
|
||||
return this.data('alert')
|
||||
}
|
||||
|
||||
return this.each(function () {
|
||||
var $this = $(this)
|
||||
|
||||
if ( typeof options == 'string' ) {
|
||||
return $this.data('alert')[options]()
|
||||
}
|
||||
|
||||
$(this).data('alert', new Alert( this, options ))
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
$.fn.alert.defaults = {
|
||||
selector: '.close'
|
||||
}
|
||||
|
||||
$(document).ready(function () {
|
||||
new Alert($('body'), {
|
||||
selector: '.alert-message[data-alert] .close'
|
||||
})
|
||||
})
|
||||
|
||||
}( window.jQuery || window.ender );
|
||||
2467
vendor/bootstrap/1.4.0/bootstrap.css
vendored
2467
vendor/bootstrap/1.4.0/bootstrap.css
vendored
File diff suppressed because it is too large
Load Diff
1722
vendor/bootstrap/2.0.0/bootstrap.js
vendored
Normal file
1722
vendor/bootstrap/2.0.0/bootstrap.js
vendored
Normal file
File diff suppressed because it is too large
Load Diff
567
vendor/bootstrap/2.0.0/css/bootstrap-responsive.css
vendored
Normal file
567
vendor/bootstrap/2.0.0/css/bootstrap-responsive.css
vendored
Normal file
@ -0,0 +1,567 @@
|
||||
/*!
|
||||
* Bootstrap Responsive v2.0.0
|
||||
*
|
||||
* Copyright 2012 Twitter, Inc
|
||||
* Licensed under the Apache License v2.0
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Designed and built with all the love in the world @twitter by @mdo and @fat.
|
||||
*/
|
||||
.hidden {
|
||||
display: none;
|
||||
visibility: hidden;
|
||||
}
|
||||
@media (max-width: 480px) {
|
||||
.nav-collapse {
|
||||
-webkit-transform: translate3d(0, 0, 0);
|
||||
}
|
||||
.page-header h1 small {
|
||||
display: block;
|
||||
line-height: 18px;
|
||||
}
|
||||
input[class*="span"],
|
||||
select[class*="span"],
|
||||
textarea[class*="span"],
|
||||
.uneditable-input {
|
||||
display: block;
|
||||
width: 100%;
|
||||
height: 28px;
|
||||
/* Make inputs at least the height of their button counterpart */
|
||||
|
||||
/* Makes inputs behave like true block-level elements */
|
||||
|
||||
-webkit-box-sizing: border-box;
|
||||
/* Older Webkit */
|
||||
|
||||
-moz-box-sizing: border-box;
|
||||
/* Older FF */
|
||||
|
||||
-ms-box-sizing: border-box;
|
||||
/* IE8 */
|
||||
|
||||
box-sizing: border-box;
|
||||
/* CSS3 spec*/
|
||||
|
||||
}
|
||||
.input-prepend input[class*="span"], .input-append input[class*="span"] {
|
||||
width: auto;
|
||||
}
|
||||
input[type="checkbox"], input[type="radio"] {
|
||||
border: 1px solid #ccc;
|
||||
}
|
||||
.form-horizontal .control-group > label {
|
||||
float: none;
|
||||
width: auto;
|
||||
padding-top: 0;
|
||||
text-align: left;
|
||||
}
|
||||
.form-horizontal .controls {
|
||||
margin-left: 0;
|
||||
}
|
||||
.form-horizontal .control-list {
|
||||
padding-top: 0;
|
||||
}
|
||||
.form-horizontal .form-actions {
|
||||
padding-left: 10px;
|
||||
padding-right: 10px;
|
||||
}
|
||||
.modal {
|
||||
position: absolute;
|
||||
top: 10px;
|
||||
left: 10px;
|
||||
right: 10px;
|
||||
width: auto;
|
||||
margin: 0;
|
||||
}
|
||||
.modal.fade.in {
|
||||
top: auto;
|
||||
}
|
||||
.modal-header .close {
|
||||
padding: 10px;
|
||||
margin: -10px;
|
||||
}
|
||||
.carousel-caption {
|
||||
position: static;
|
||||
}
|
||||
}
|
||||
@media (max-width: 768px) {
|
||||
.container {
|
||||
width: auto;
|
||||
padding: 0 20px;
|
||||
}
|
||||
.row-fluid {
|
||||
width: 100%;
|
||||
}
|
||||
.row {
|
||||
margin-left: 0;
|
||||
}
|
||||
.row > [class*="span"], .row-fluid > [class*="span"] {
|
||||
float: none;
|
||||
display: block;
|
||||
width: auto;
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
@media (min-width: 768px) and (max-width: 980px) {
|
||||
.row {
|
||||
margin-left: -20px;
|
||||
*zoom: 1;
|
||||
}
|
||||
.row:before, .row:after {
|
||||
display: table;
|
||||
content: "";
|
||||
}
|
||||
.row:after {
|
||||
clear: both;
|
||||
}
|
||||
[class*="span"] {
|
||||
float: left;
|
||||
margin-left: 20px;
|
||||
}
|
||||
.span1 {
|
||||
width: 42px;
|
||||
}
|
||||
.span2 {
|
||||
width: 104px;
|
||||
}
|
||||
.span3 {
|
||||
width: 166px;
|
||||
}
|
||||
.span4 {
|
||||
width: 228px;
|
||||
}
|
||||
.span5 {
|
||||
width: 290px;
|
||||
}
|
||||
.span6 {
|
||||
width: 352px;
|
||||
}
|
||||
.span7 {
|
||||
width: 414px;
|
||||
}
|
||||
.span8 {
|
||||
width: 476px;
|
||||
}
|
||||
.span9 {
|
||||
width: 538px;
|
||||
}
|
||||
.span10 {
|
||||
width: 600px;
|
||||
}
|
||||
.span11 {
|
||||
width: 662px;
|
||||
}
|
||||
.span12, .container {
|
||||
width: 724px;
|
||||
}
|
||||
.offset1 {
|
||||
margin-left: 82px;
|
||||
}
|
||||
.offset2 {
|
||||
margin-left: 144px;
|
||||
}
|
||||
.offset3 {
|
||||
margin-left: 206px;
|
||||
}
|
||||
.offset4 {
|
||||
margin-left: 268px;
|
||||
}
|
||||
.offset5 {
|
||||
margin-left: 330px;
|
||||
}
|
||||
.offset6 {
|
||||
margin-left: 392px;
|
||||
}
|
||||
.offset7 {
|
||||
margin-left: 454px;
|
||||
}
|
||||
.offset8 {
|
||||
margin-left: 516px;
|
||||
}
|
||||
.offset9 {
|
||||
margin-left: 578px;
|
||||
}
|
||||
.offset10 {
|
||||
margin-left: 640px;
|
||||
}
|
||||
.offset11 {
|
||||
margin-left: 702px;
|
||||
}
|
||||
.row-fluid {
|
||||
width: 100%;
|
||||
*zoom: 1;
|
||||
}
|
||||
.row-fluid:before, .row-fluid:after {
|
||||
display: table;
|
||||
content: "";
|
||||
}
|
||||
.row-fluid:after {
|
||||
clear: both;
|
||||
}
|
||||
.row-fluid > [class*="span"] {
|
||||
float: left;
|
||||
margin-left: 2.762430939%;
|
||||
}
|
||||
.row-fluid > [class*="span"]:first-child {
|
||||
margin-left: 0;
|
||||
}
|
||||
.row-fluid .span1 {
|
||||
width: 5.801104972%;
|
||||
}
|
||||
.row-fluid .span2 {
|
||||
width: 14.364640883%;
|
||||
}
|
||||
.row-fluid .span3 {
|
||||
width: 22.928176794%;
|
||||
}
|
||||
.row-fluid .span4 {
|
||||
width: 31.491712705%;
|
||||
}
|
||||
.row-fluid .span5 {
|
||||
width: 40.055248616%;
|
||||
}
|
||||
.row-fluid .span6 {
|
||||
width: 48.618784527%;
|
||||
}
|
||||
.row-fluid .span7 {
|
||||
width: 57.182320438000005%;
|
||||
}
|
||||
.row-fluid .span8 {
|
||||
width: 65.74585634900001%;
|
||||
}
|
||||
.row-fluid .span9 {
|
||||
width: 74.30939226%;
|
||||
}
|
||||
.row-fluid .span10 {
|
||||
width: 82.87292817100001%;
|
||||
}
|
||||
.row-fluid .span11 {
|
||||
width: 91.436464082%;
|
||||
}
|
||||
.row-fluid .span12 {
|
||||
width: 99.999999993%;
|
||||
}
|
||||
input.span1, textarea.span1, .uneditable-input.span1 {
|
||||
width: 32px;
|
||||
}
|
||||
input.span2, textarea.span2, .uneditable-input.span2 {
|
||||
width: 94px;
|
||||
}
|
||||
input.span3, textarea.span3, .uneditable-input.span3 {
|
||||
width: 156px;
|
||||
}
|
||||
input.span4, textarea.span4, .uneditable-input.span4 {
|
||||
width: 218px;
|
||||
}
|
||||
input.span5, textarea.span5, .uneditable-input.span5 {
|
||||
width: 280px;
|
||||
}
|
||||
input.span6, textarea.span6, .uneditable-input.span6 {
|
||||
width: 342px;
|
||||
}
|
||||
input.span7, textarea.span7, .uneditable-input.span7 {
|
||||
width: 404px;
|
||||
}
|
||||
input.span8, textarea.span8, .uneditable-input.span8 {
|
||||
width: 466px;
|
||||
}
|
||||
input.span9, textarea.span9, .uneditable-input.span9 {
|
||||
width: 528px;
|
||||
}
|
||||
input.span10, textarea.span10, .uneditable-input.span10 {
|
||||
width: 590px;
|
||||
}
|
||||
input.span11, textarea.span11, .uneditable-input.span11 {
|
||||
width: 652px;
|
||||
}
|
||||
input.span12, textarea.span12, .uneditable-input.span12 {
|
||||
width: 714px;
|
||||
}
|
||||
}
|
||||
@media (max-width: 980px) {
|
||||
body {
|
||||
padding-top: 0;
|
||||
}
|
||||
.navbar-fixed-top {
|
||||
position: static;
|
||||
margin-bottom: 18px;
|
||||
}
|
||||
.navbar-fixed-top .navbar-inner {
|
||||
padding: 5px;
|
||||
}
|
||||
.navbar .container {
|
||||
width: auto;
|
||||
padding: 0;
|
||||
}
|
||||
.navbar .brand {
|
||||
padding-left: 10px;
|
||||
padding-right: 10px;
|
||||
margin: 0 0 0 -5px;
|
||||
}
|
||||
.navbar .nav-collapse {
|
||||
clear: left;
|
||||
}
|
||||
.navbar .nav {
|
||||
float: none;
|
||||
margin: 0 0 9px;
|
||||
}
|
||||
.navbar .nav > li {
|
||||
float: none;
|
||||
}
|
||||
.navbar .nav > li > a {
|
||||
margin-bottom: 2px;
|
||||
}
|
||||
.navbar .nav > .divider-vertical {
|
||||
display: none;
|
||||
}
|
||||
.navbar .nav > li > a, .navbar .dropdown-menu a {
|
||||
padding: 6px 15px;
|
||||
font-weight: bold;
|
||||
color: #999999;
|
||||
-webkit-border-radius: 3px;
|
||||
-moz-border-radius: 3px;
|
||||
border-radius: 3px;
|
||||
}
|
||||
.navbar .dropdown-menu li + li a {
|
||||
margin-bottom: 2px;
|
||||
}
|
||||
.navbar .nav > li > a:hover, .navbar .dropdown-menu a:hover {
|
||||
background-color: #222222;
|
||||
}
|
||||
.navbar .dropdown-menu {
|
||||
position: static;
|
||||
top: auto;
|
||||
left: auto;
|
||||
float: none;
|
||||
display: block;
|
||||
max-width: none;
|
||||
margin: 0 15px;
|
||||
padding: 0;
|
||||
background-color: transparent;
|
||||
border: none;
|
||||
-webkit-border-radius: 0;
|
||||
-moz-border-radius: 0;
|
||||
border-radius: 0;
|
||||
-webkit-box-shadow: none;
|
||||
-moz-box-shadow: none;
|
||||
box-shadow: none;
|
||||
}
|
||||
.navbar .dropdown-menu:before, .navbar .dropdown-menu:after {
|
||||
display: none;
|
||||
}
|
||||
.navbar .dropdown-menu .divider {
|
||||
display: none;
|
||||
}
|
||||
.navbar-form, .navbar-search {
|
||||
float: none;
|
||||
padding: 9px 15px;
|
||||
margin: 9px 0;
|
||||
border-top: 1px solid #222222;
|
||||
border-bottom: 1px solid #222222;
|
||||
-webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1), 0 1px 0 rgba(255, 255, 255, 0.1);
|
||||
-moz-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1), 0 1px 0 rgba(255, 255, 255, 0.1);
|
||||
box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1), 0 1px 0 rgba(255, 255, 255, 0.1);
|
||||
}
|
||||
.navbar .nav.pull-right {
|
||||
float: none;
|
||||
margin-left: 0;
|
||||
}
|
||||
.navbar-static .navbar-inner {
|
||||
padding-left: 10px;
|
||||
padding-right: 10px;
|
||||
}
|
||||
.btn-navbar {
|
||||
display: block;
|
||||
}
|
||||
.nav-collapse {
|
||||
overflow: hidden;
|
||||
height: 0;
|
||||
}
|
||||
}
|
||||
@media (min-width: 980px) {
|
||||
.nav-collapse.collapse {
|
||||
height: auto !important;
|
||||
}
|
||||
}
|
||||
@media (min-width: 1200px) {
|
||||
.row {
|
||||
margin-left: -30px;
|
||||
*zoom: 1;
|
||||
}
|
||||
.row:before, .row:after {
|
||||
display: table;
|
||||
content: "";
|
||||
}
|
||||
.row:after {
|
||||
clear: both;
|
||||
}
|
||||
[class*="span"] {
|
||||
float: left;
|
||||
margin-left: 30px;
|
||||
}
|
||||
.span1 {
|
||||
width: 70px;
|
||||
}
|
||||
.span2 {
|
||||
width: 170px;
|
||||
}
|
||||
.span3 {
|
||||
width: 270px;
|
||||
}
|
||||
.span4 {
|
||||
width: 370px;
|
||||
}
|
||||
.span5 {
|
||||
width: 470px;
|
||||
}
|
||||
.span6 {
|
||||
width: 570px;
|
||||
}
|
||||
.span7 {
|
||||
width: 670px;
|
||||
}
|
||||
.span8 {
|
||||
width: 770px;
|
||||
}
|
||||
.span9 {
|
||||
width: 870px;
|
||||
}
|
||||
.span10 {
|
||||
width: 970px;
|
||||
}
|
||||
.span11 {
|
||||
width: 1070px;
|
||||
}
|
||||
.span12, .container {
|
||||
width: 1170px;
|
||||
}
|
||||
.offset1 {
|
||||
margin-left: 130px;
|
||||
}
|
||||
.offset2 {
|
||||
margin-left: 230px;
|
||||
}
|
||||
.offset3 {
|
||||
margin-left: 330px;
|
||||
}
|
||||
.offset4 {
|
||||
margin-left: 430px;
|
||||
}
|
||||
.offset5 {
|
||||
margin-left: 530px;
|
||||
}
|
||||
.offset6 {
|
||||
margin-left: 630px;
|
||||
}
|
||||
.offset7 {
|
||||
margin-left: 730px;
|
||||
}
|
||||
.offset8 {
|
||||
margin-left: 830px;
|
||||
}
|
||||
.offset9 {
|
||||
margin-left: 930px;
|
||||
}
|
||||
.offset10 {
|
||||
margin-left: 1030px;
|
||||
}
|
||||
.offset11 {
|
||||
margin-left: 1130px;
|
||||
}
|
||||
.row-fluid {
|
||||
width: 100%;
|
||||
*zoom: 1;
|
||||
}
|
||||
.row-fluid:before, .row-fluid:after {
|
||||
display: table;
|
||||
content: "";
|
||||
}
|
||||
.row-fluid:after {
|
||||
clear: both;
|
||||
}
|
||||
.row-fluid > [class*="span"] {
|
||||
float: left;
|
||||
margin-left: 2.564102564%;
|
||||
}
|
||||
.row-fluid > [class*="span"]:first-child {
|
||||
margin-left: 0;
|
||||
}
|
||||
.row-fluid .span1 {
|
||||
width: 5.982905983%;
|
||||
}
|
||||
.row-fluid .span2 {
|
||||
width: 14.529914530000001%;
|
||||
}
|
||||
.row-fluid .span3 {
|
||||
width: 23.076923077%;
|
||||
}
|
||||
.row-fluid .span4 {
|
||||
width: 31.623931624%;
|
||||
}
|
||||
.row-fluid .span5 {
|
||||
width: 40.170940171000005%;
|
||||
}
|
||||
.row-fluid .span6 {
|
||||
width: 48.717948718%;
|
||||
}
|
||||
.row-fluid .span7 {
|
||||
width: 57.264957265%;
|
||||
}
|
||||
.row-fluid .span8 {
|
||||
width: 65.81196581200001%;
|
||||
}
|
||||
.row-fluid .span9 {
|
||||
width: 74.358974359%;
|
||||
}
|
||||
.row-fluid .span10 {
|
||||
width: 82.905982906%;
|
||||
}
|
||||
.row-fluid .span11 {
|
||||
width: 91.45299145300001%;
|
||||
}
|
||||
.row-fluid .span12 {
|
||||
width: 100%;
|
||||
}
|
||||
input.span1, textarea.span1, .uneditable-input.span1 {
|
||||
width: 60px;
|
||||
}
|
||||
input.span2, textarea.span2, .uneditable-input.span2 {
|
||||
width: 160px;
|
||||
}
|
||||
input.span3, textarea.span3, .uneditable-input.span3 {
|
||||
width: 260px;
|
||||
}
|
||||
input.span4, textarea.span4, .uneditable-input.span4 {
|
||||
width: 360px;
|
||||
}
|
||||
input.span5, textarea.span5, .uneditable-input.span5 {
|
||||
width: 460px;
|
||||
}
|
||||
input.span6, textarea.span6, .uneditable-input.span6 {
|
||||
width: 560px;
|
||||
}
|
||||
input.span7, textarea.span7, .uneditable-input.span7 {
|
||||
width: 660px;
|
||||
}
|
||||
input.span8, textarea.span8, .uneditable-input.span8 {
|
||||
width: 760px;
|
||||
}
|
||||
input.span9, textarea.span9, .uneditable-input.span9 {
|
||||
width: 860px;
|
||||
}
|
||||
input.span10, textarea.span10, .uneditable-input.span10 {
|
||||
width: 960px;
|
||||
}
|
||||
input.span11, textarea.span11, .uneditable-input.span11 {
|
||||
width: 1060px;
|
||||
}
|
||||
input.span12, textarea.span12, .uneditable-input.span12 {
|
||||
width: 1160px;
|
||||
}
|
||||
.thumbnails {
|
||||
margin-left: -30px;
|
||||
}
|
||||
.thumbnails > li {
|
||||
margin-left: 30px;
|
||||
}
|
||||
}
|
||||
3365
vendor/bootstrap/2.0.0/css/bootstrap.css
vendored
Normal file
3365
vendor/bootstrap/2.0.0/css/bootstrap.css
vendored
Normal file
File diff suppressed because it is too large
Load Diff
BIN
vendor/bootstrap/2.0.0/img/glyphicons-halflings-white.png
vendored
Normal file
BIN
vendor/bootstrap/2.0.0/img/glyphicons-halflings-white.png
vendored
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 4.3 KiB |
BIN
vendor/bootstrap/2.0.0/img/glyphicons-halflings.png
vendored
Normal file
BIN
vendor/bootstrap/2.0.0/img/glyphicons-halflings.png
vendored
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 4.3 KiB |
9266
vendor/jquery-1.7.1.js
vendored
Normal file
9266
vendor/jquery-1.7.1.js
vendored
Normal file
File diff suppressed because it is too large
Load Diff
Loading…
x
Reference in New Issue
Block a user