Merge branch 'master' into gh-pages

This commit is contained in:
Rufus Pollock 2012-03-11 01:30:50 +00:00
commit 3d2d0fe7c0
21 changed files with 15504 additions and 3130 deletions

View File

@ -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 {

View File

Before

Width:  |  Height:  |  Size: 1.8 KiB

After

Width:  |  Height:  |  Size: 1.8 KiB

56
demo/built.html Normal file
View 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>

View File

@ -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>

View File

@ -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();

View File

@ -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">&#39;recline-query-editor&#39;</span><span class="p">,</span>
<span class="nx">template</span><span class="o">:</span> <span class="s1">&#39; \</span>
<span class="s1"> &lt;form action=&quot;&quot; method=&quot;GET&quot;&gt; \</span>
<span class="s1"> &lt;form action=&quot;&quot; method=&quot;GET&quot; class=&quot;form-inline&quot;&gt; \</span>
<span class="s1"> &lt;input type=&quot;text&quot; name=&quot;q&quot; value=&quot;{{q}}&quot; class=&quot;text-query&quot; /&gt; \</span>
<span class="s1"> &lt;div class=&quot;pagination&quot;&gt; \</span>
<span class="s1"> &lt;ul&gt; \</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">&#39;?&#39;</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">&#182;</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">&#39; \</span>
<span class="s1"> &lt;div class=&quot;alert-message {{category}} fade in&quot; data-alert=&quot;alert&quot;&gt;&lt;a class=&quot;close&quot; href=&quot;#&quot;&gt;×&lt;/a&gt; \</span>
<span class="s1"> &lt;p&gt;{{msg}} \</span>
<span class="s1"> &lt;div class=&quot;alert alert-{{category}} fade in&quot; data-alert=&quot;alert&quot;&gt;&lt;a class=&quot;close&quot; data-dismiss=&quot;alert&quot; href=&quot;#&quot;&gt;×&lt;/a&gt; \</span>
<span class="s1"> {{msg}} \</span>
<span class="s1"> {{#loader}} \</span>
<span class="s1"> &lt;img src=&quot;images/small-spinner.gif&quot; class=&quot;notification-loader&quot;&gt; \</span>
<span class="s1"> {{/loader}} \</span>
<span class="s1"> &lt;/p&gt; \</span>
<span class="s1"> &lt;/div&gt;&#39;</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">&#39;.data-explorer .alert-messages&#39;</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">&#182;</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">&#39;.data-explorer .alert-message&#39;</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">&#39;.data-explorer .alert-messages .alert&#39;</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
View File

@ -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()

View File

@ -812,7 +812,7 @@ my.DataGrid = Backbone.View.extend({
template: ' \
<div class="data-table-menu-overlay" style="display: none; z-index: 101; ">&nbsp;</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; ">&nbsp;</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>&laquo; back</a></li> \
<li class="active"><a><input name="from" type="text" value="{{from}}" /> &ndash; <input name="to" type="text" value="{{to}}" /> </a></li> \
<li class="next action-pagination-update"><a>next &raquo;</a></li> \
</ul> \
</div> \
<button type="submit" class="btn" style="">Update &raquo;</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; ">&nbsp;</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>&laquo;</a></li> \
<li class="active"><a><input name="from" type="text" value="{{from}}" /> &ndash; <input name="to" type="text" value="{{to}}" /> </a></li> \
<li class="next action-pagination-update"><a>&raquo;</a></li> \
</ul> \
</div> \
<button type="submit" class="btn" style="">Update &raquo;</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">&nbsp;</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>

View File

@ -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();
}

View File

@ -160,7 +160,7 @@ my.DataGrid = Backbone.View.extend({
template: ' \
<div class="data-table-menu-overlay" style="display: none; z-index: 101; ">&nbsp;</div> \
<ul class="data-table-menu"></ul> \
<table class="data-table" cellspacing="0"> \
<table class="data-table table-striped" cellspacing="0"> \
<thead> \
<tr> \
{{#notEmpty}} \

View File

@ -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>&laquo; back</a></li> \
<li class="prev action-pagination-update"><a>&laquo;</a></li> \
<li class="active"><a><input name="from" type="text" value="{{from}}" /> &ndash; <input name="to" type="text" value="{{to}}" /> </a></li> \
<li class="next action-pagination-update"><a>next &raquo;</a></li> \
<li class="next action-pagination-update"><a>&raquo;</a></li> \
</ul> \
</div> \
<button type="submit" class="btn" style="">Update &raquo;</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">&nbsp;</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();
}

File diff suppressed because one or more lines are too long

View File

@ -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 );

File diff suppressed because it is too large Load Diff

1722
vendor/bootstrap/2.0.0/bootstrap.js vendored Normal file

File diff suppressed because it is too large Load Diff

View 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

File diff suppressed because it is too large Load Diff

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB

9266
vendor/jquery-1.7.1.js vendored Normal file

File diff suppressed because it is too large Load Diff