Merge branch 'master' into gh-pages
This commit is contained in:
commit
9635bf8745
9
css/bootstrap.css
vendored
Normal file
9
css/bootstrap.css
vendored
Normal file
@ -0,0 +1,9 @@
|
||||
body {
|
||||
padding-top: 60px;
|
||||
}
|
||||
|
||||
/* we do not have a LH sidebar */
|
||||
.container-fluid > .content {
|
||||
margin-left: 0;
|
||||
}
|
||||
|
||||
514
css/data-explorer.css
Normal file
514
css/data-explorer.css
Normal file
@ -0,0 +1,514 @@
|
||||
.data-explorer .header .navigation,
|
||||
.data-explorer .header .navigation li,
|
||||
.data-explorer .header .pagination,
|
||||
.data-explorer .header .pagination form
|
||||
{
|
||||
display: inline;
|
||||
}
|
||||
|
||||
.data-explorer .header .navigation {
|
||||
float: left;
|
||||
margin-left: 0;
|
||||
}
|
||||
|
||||
.header .pagination {
|
||||
float: right;
|
||||
margin: 4px;
|
||||
}
|
||||
|
||||
.header .pagination label {
|
||||
float: none;
|
||||
}
|
||||
|
||||
.header .pagination input {
|
||||
width: 30px;
|
||||
}
|
||||
|
||||
.doc-count {
|
||||
font-weight: bold;
|
||||
font-size: 120%;
|
||||
}
|
||||
|
||||
.data-view-container {
|
||||
display: block;
|
||||
clear: both;
|
||||
}
|
||||
|
||||
/* 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;
|
||||
}
|
||||
|
||||
|
||||
/**********************************************************
|
||||
* Data Table
|
||||
*********************************************************/
|
||||
|
||||
/* direct borrowing from twitter buttons */
|
||||
.data-table th,
|
||||
.transform-column-view .expression-preview-table-wrapper th
|
||||
{
|
||||
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);
|
||||
text-shadow: 0 1px 1px rgba(255, 255, 255, 0.75);
|
||||
color: #333;
|
||||
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;
|
||||
}
|
||||
|
||||
.data-table {
|
||||
border: 1px solid #ccc;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.data-table td, .data-table th {
|
||||
border-left: 1px solid #ccc;
|
||||
padding: 3px 4px;
|
||||
}
|
||||
|
||||
.data-table tr td:first-child, .data-table tr th:first-child {
|
||||
width: 20px;
|
||||
}
|
||||
|
||||
/**********************************************************
|
||||
* Data Table Menus
|
||||
*********************************************************/
|
||||
|
||||
a.column-header-menu {
|
||||
float: right;
|
||||
display: block;
|
||||
margin: 0 4px 0 0;
|
||||
width: 17px;
|
||||
height: 19px;
|
||||
background-image: url(images/menu-dropdown.png);
|
||||
background-repeat: no-repeat;
|
||||
}
|
||||
|
||||
a.row-header-menu:hover {
|
||||
background-position: -17px 0px;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
a.row-header-menu {
|
||||
float: left;
|
||||
display: block;
|
||||
margin: -2px 0 -4px 0;
|
||||
width: 17px;
|
||||
height: 18px;
|
||||
background-image: url(images/menu-dropdown.png);
|
||||
background-repeat: no-repeat;
|
||||
}
|
||||
|
||||
a.column-header-menu:hover {
|
||||
background-position: -17px 0px;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.column-header-recon-stats-bar {
|
||||
margin-top: 10px;
|
||||
height: 4px;
|
||||
background: #ddd;
|
||||
border: 1px solid #ccc;
|
||||
position: relative;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.column-header-recon-stats-matched {
|
||||
position: absolute;
|
||||
height: 100%;
|
||||
background: #282;
|
||||
}
|
||||
|
||||
.column-header-recon-stats-blanks {
|
||||
position: absolute;
|
||||
height: 100%;
|
||||
background: #3d3;
|
||||
}
|
||||
|
||||
div.data-table-cell-content {
|
||||
line-height: 1.2;
|
||||
color: #222;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
div.data-table-cell-content-numeric {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
a.data-table-cell-edit {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
display: block;
|
||||
width: 25px;
|
||||
height: 16px;
|
||||
text-decoration: none;
|
||||
background-image: url(images/edit-map.png);
|
||||
background-repeat: no-repeat;
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
a.data-table-cell-edit:hover {
|
||||
background-position: -25px 0px;
|
||||
}
|
||||
|
||||
.data-table td:hover .data-table-cell-edit {
|
||||
visibility: visible;
|
||||
}
|
||||
|
||||
div.data-table-cell-content-numeric > a.data-table-cell-edit {
|
||||
left: 0px;
|
||||
right: auto;
|
||||
}
|
||||
|
||||
.data-table-value-nonstring {
|
||||
color: #282;
|
||||
}
|
||||
|
||||
.data-table-error {
|
||||
color: red;
|
||||
}
|
||||
|
||||
.data-table-menu-overlay {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
ul.data-table-menu {
|
||||
display: none;
|
||||
outline-style: none;
|
||||
background: white;
|
||||
color: black;
|
||||
font-size: 12px;
|
||||
height: auto;
|
||||
list-style: none;
|
||||
overflow: hidden;
|
||||
position: absolute;
|
||||
text-align: left;
|
||||
width: 120px;
|
||||
z-index: 666;
|
||||
border: 1px solid #CCC;
|
||||
border-right: 1px solid #666;
|
||||
border-bottom: 1px solid #666;
|
||||
margin: 0; padding: 0; }
|
||||
ul.data-table-menu * {
|
||||
margin: 0;
|
||||
padding: 0; }
|
||||
ul.data-table-menu a {
|
||||
line-height: 14px;
|
||||
color: black;
|
||||
display: block;
|
||||
padding: 5px 7px;
|
||||
text-decoration: none; }
|
||||
ul.data-table-menu li {
|
||||
height: 24px; }
|
||||
ul.data-table-menu li:hover {
|
||||
background-color: #DBE8F8 }
|
||||
|
||||
/* TODO: not sure the rest of this is needed */
|
||||
.data-table-cell-editor, .data-table-topic-popup {
|
||||
overflow: auto;
|
||||
border: 1px solid #bcf;
|
||||
background: #e3e9ff;
|
||||
padding: 5px;
|
||||
-moz-border-radius: 5px;
|
||||
-webkit-border-radius: 5px;
|
||||
border-radius: 5px;
|
||||
}
|
||||
|
||||
.data-table-topic-popup-header {
|
||||
padding: 0 0 5px;
|
||||
}
|
||||
|
||||
.data-table-cell-editor-editor {
|
||||
overflow: hidden;
|
||||
display: block;
|
||||
width: 98%;
|
||||
height: 3em;
|
||||
font-family: monospace;
|
||||
margin: 3px 0;
|
||||
}
|
||||
|
||||
.data-table-cell-copypaste-editor {
|
||||
overflow: hidden;
|
||||
display: block;
|
||||
width: 98%;
|
||||
height: 10em;
|
||||
font-family: monospace;
|
||||
margin: 3px 0;
|
||||
}
|
||||
|
||||
.data-table-cell-editor-action {
|
||||
float: left;
|
||||
vertical-align: bottom;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.data-table-cell-editor-key {
|
||||
font-size: 0.8em;
|
||||
color: #999;
|
||||
}
|
||||
|
||||
ul.sorting-dialog-blank-error-positions {
|
||||
margin: 0;
|
||||
padding: 5px;
|
||||
height: 10em;
|
||||
border: 1px solid #ccc;
|
||||
-moz-border-radius: 3px;
|
||||
-webkit-border-radius: 3px;
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
ul.sorting-dialog-blank-error-positions > li {
|
||||
display: block;
|
||||
border: 1px solid #ccc;
|
||||
background: #eee;
|
||||
padding: 5px;
|
||||
margin: 2px;
|
||||
cursor: move;
|
||||
-moz-border-radius: 3px;
|
||||
-webkit-border-radius: 3px;
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
|
||||
/**********************************************************
|
||||
* Dialogs
|
||||
*********************************************************/
|
||||
|
||||
.dialog-overlay {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: #666;
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
.dialog {
|
||||
position: fixed;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.dialog-frame {
|
||||
margin: 0 auto;
|
||||
text-align: left;
|
||||
background: white;
|
||||
border: 1px solid #3a5774;
|
||||
}
|
||||
|
||||
.dialog-border {
|
||||
border: 4px solid #c1d9ff;
|
||||
}
|
||||
|
||||
.dialog-header {
|
||||
background: #e0edfe;
|
||||
padding: 10px;
|
||||
font-weight: bold;
|
||||
font-size: 1.6em;
|
||||
color: #000;
|
||||
cursor: move;
|
||||
}
|
||||
|
||||
.dialog-body {
|
||||
overflow: auto;
|
||||
font-size: 1.3em;
|
||||
padding: 15px;
|
||||
}
|
||||
|
||||
.dialog-instruction {
|
||||
padding: 0 0 7px;
|
||||
}
|
||||
|
||||
.dialog-footer {
|
||||
font-size: 1.3em;
|
||||
background: #eee;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.dialog-busy {
|
||||
width: 400px;
|
||||
border: none;
|
||||
-moz-border-radius: 5px;
|
||||
-webkit-border-radius: 5px;
|
||||
border-radius: 5px;
|
||||
}
|
||||
|
||||
/**********************************************************
|
||||
* Transform Dialog
|
||||
*********************************************************/
|
||||
|
||||
#expression-preview-tabs .ui-tabs-nav li a {
|
||||
padding: 0.15em 1em;
|
||||
}
|
||||
|
||||
textarea.expression-preview-code {
|
||||
font-family: monospace;
|
||||
height: 5em;
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
.expression-preview-parsing-status {
|
||||
color: #999;
|
||||
}
|
||||
|
||||
.expression-preview-parsing-status.error {
|
||||
color: red;
|
||||
}
|
||||
|
||||
#expression-preview-tabs-preview,
|
||||
#expression-preview-tabs-help,
|
||||
#expression-preview-tabs-history,
|
||||
#expression-preview-tabs-starred {
|
||||
padding: 5px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
#expression-preview-tabs-preview > div,
|
||||
#expression-preview-tabs-help > div,
|
||||
#expression-preview-tabs-history > div,
|
||||
#expression-preview-tabs-starred {
|
||||
height: 200px;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
#expression-preview-tabs-preview td, #expression-preview-tabs-preview th,
|
||||
#expression-preview-tabs-help td, #expression-preview-tabs-help th,
|
||||
#expression-preview-tabs-history td, #expression-preview-tabs-history th,
|
||||
#expression-preview-tabs-starred td, #expression-preview-tabs-starred th {
|
||||
padding: 5px;
|
||||
}
|
||||
|
||||
.expression-preview-table-wrapper {
|
||||
padding: 7px;
|
||||
}
|
||||
|
||||
.expression-preview-container td {
|
||||
padding: 2px 5px;
|
||||
border-top: 1px solid #ccc;
|
||||
}
|
||||
|
||||
td.expression-preview-heading {
|
||||
border-top: none;
|
||||
background: #ddd;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
td.expression-preview-value {
|
||||
max-width: 250px !important;
|
||||
overflow-x: hidden;
|
||||
}
|
||||
|
||||
.expression-preview-special-value {
|
||||
color: #aaa;
|
||||
}
|
||||
|
||||
.expression-preview-help-container h3 {
|
||||
margin-top: 15px;
|
||||
margin-bottom: 7px;
|
||||
border-bottom: 1px solid #999;
|
||||
}
|
||||
|
||||
.expression-preview-doc-item-title {
|
||||
font-weight: bold;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.expression-preview-doc-item-params {
|
||||
}
|
||||
|
||||
.expression-preview-doc-item-returns {
|
||||
}
|
||||
|
||||
.expression-preview-doc-item-desc {
|
||||
color: #666;
|
||||
}
|
||||
|
||||
|
||||
/**********************************************************
|
||||
* Read-only mode
|
||||
*********************************************************/
|
||||
|
||||
.read-only .data-table tr td:first-child,
|
||||
.read-only .data-table tr th:first-child
|
||||
{
|
||||
display: none;
|
||||
}
|
||||
|
||||
.read-only .column-header-menu,
|
||||
.read-only .row-header-menu,
|
||||
.read-only a.data-table-cell-edit
|
||||
{
|
||||
display: none;
|
||||
}
|
||||
|
||||
50
css/graph-flot.css
Normal file
50
css/graph-flot.css
Normal file
@ -0,0 +1,50 @@
|
||||
.data-graph-container .graph {
|
||||
height: 500px;
|
||||
margin-right: 200px;
|
||||
}
|
||||
|
||||
.data-graph-container .legend table {
|
||||
width: auto;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.data-graph-container .legend td {
|
||||
padding: 5px;
|
||||
line-height: 13px;
|
||||
}
|
||||
|
||||
/**********************************************************
|
||||
* Editor
|
||||
*********************************************************/
|
||||
|
||||
.data-graph-container .editor {
|
||||
float: right;
|
||||
width: 200px;
|
||||
padding-left: 0px;
|
||||
}
|
||||
|
||||
.data-graph-container .editor-info {
|
||||
padding-left: 4px;
|
||||
}
|
||||
|
||||
.data-graph-container .editor-info {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.data-graph-container .editor form {
|
||||
padding-left: 4px;
|
||||
}
|
||||
|
||||
.data-graph-container .editor select {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.data-graph-container .editor-info {
|
||||
border-bottom: 1px solid #ddd;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.data-graph-container .editor-hide-info p {
|
||||
display: none;
|
||||
}
|
||||
|
||||
BIN
css/images/edit-map.png
Executable file
BIN
css/images/edit-map.png
Executable file
Binary file not shown.
|
After Width: | Height: | Size: 1.5 KiB |
BIN
css/images/menu-dropdown.png
Executable file
BIN
css/images/menu-dropdown.png
Executable file
Binary file not shown.
|
After Width: | Height: | Size: 1.1 KiB |
468
demo/index.html
Executable file → Normal file
468
demo/index.html
Executable file → Normal file
@ -1,13 +1,22 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Data Explorer</title>
|
||||
<link rel="stylesheet" href="style/reset.css" media="screen">
|
||||
<link rel="stylesheet" href="style/data-table.css" media="screen">
|
||||
<link rel="stylesheet" href="style/flot-graph.css" media="screen">
|
||||
<link rel="stylesheet" href="style/style.css" media="screen">
|
||||
<!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/1.4.0/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="../src/deps-min.js"></script>
|
||||
<script type="text/javascript" src="../vendor/bootstrap/1.4.0/bootstrap-alerts.js"></script>
|
||||
|
||||
<script type="text/javascript" src="../src/util.js"></script>
|
||||
<script type="text/javascript" src="../src/costco.js"></script>
|
||||
@ -15,437 +24,26 @@
|
||||
<script type="text/javascript" src="../src/view.js"></script>
|
||||
<script type="text/javascript" src="js/app.js"></script>
|
||||
</head>
|
||||
<body class="bod">
|
||||
<div class="container">
|
||||
<div class="menu-overlay" style="display: none; z-index: 101; "> </div>
|
||||
<ul class="menu">
|
||||
</ul>
|
||||
<div id="header">
|
||||
<div class="project-title">
|
||||
<a href="http://github.com/okfn/recline">Recline DataExplorer</a>
|
||||
/ Demo
|
||||
</div>
|
||||
<div class="project-actions">
|
||||
<form class="webstore-load">
|
||||
<label for="source">Source</label>
|
||||
<body>
|
||||
<div class="topbar">
|
||||
<div class="topbar-inner">
|
||||
<div class="container-fluid">
|
||||
<a class="brand" href="#">Recline Data Explorer</a>
|
||||
<ul class="nav secondary-nav">
|
||||
<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">
|
||||
<input type="text" name="source" size="50" />
|
||||
<input type="submit" name="" value="Update" />
|
||||
</form>
|
||||
</div>
|
||||
<div class="project-controls"></div>
|
||||
</div>
|
||||
<!--
|
||||
<div class="main_content">
|
||||
<div class="left-panel"></div>
|
||||
<div class="right-panel"></div>
|
||||
</div>
|
||||
<div class="data-table-container"></div>
|
||||
-->
|
||||
</div>
|
||||
|
||||
<div id="notification-container">
|
||||
<div id="notification">
|
||||
<img src="images/small-spinner.gif" class="notification-loader"><span id="notification-message">Loading...</span>
|
||||
</div>
|
||||
</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>
|
||||
|
||||
<script type='text/mustache' class="busyTemplate">
|
||||
<div id="loading-message">
|
||||
<img src="images/large-spinner.gif">
|
||||
<span> Working...</span>
|
||||
</div>
|
||||
</script>
|
||||
|
||||
<script type='text/mustache' class="controlsTemplate">
|
||||
<a id="logged-in-status" href="JavaScript:void(0);" class="secondary">{{text}}</a>
|
||||
</script>
|
||||
|
||||
<script type='text/mustache' class="actionsTemplate">
|
||||
<a class="button" data-action="import" href="javascript:{}"><span data-action="import" class="button-menu">Import</span></a>
|
||||
<!-- <a class="button" data-action="edit" href="javascript:{}"><span data-action="transform" class="button-menu">Edit</span></a> -->
|
||||
<a class="button" data-action="export" href="javascript:{}"><span data-action="export" class="button-menu">Export</span></a>
|
||||
</script>
|
||||
|
||||
<script type='text/mustache' class="importActionsTemplate">
|
||||
<li><a data-action="urlImport" class="menuAction" href="JavaScript:void(0);">JSON API</a></li>
|
||||
<li><a data-action="pasteImport" class="menuAction" href="JavaScript:void(0);">Paste JSON</a></li>
|
||||
<li><a data-action="uploadImport" class="menuAction" href="JavaScript:void(0);">Upload CSV</a></li>
|
||||
</script>
|
||||
|
||||
<script type='text/mustache' class="exportActionsTemplate">
|
||||
<li><a data-action="csv" class="menuAction" href="JavaScript:void(0);">CSV</a></li>
|
||||
<li><a data-action="json" class="menuAction" href="JavaScript:void(0);">JSON</a></li>
|
||||
</script>
|
||||
|
||||
<script type='text/mustache' class="transformActionsTemplate">
|
||||
<li><a data-action="transform" class="menuAction" href="JavaScript:void(0);">Global transform...</a></li>
|
||||
</script>
|
||||
|
||||
<script type='text/mustache' class="columnActionsTemplate">
|
||||
<li><a data-action="bulkEdit" class="menuAction" href="JavaScript:void(0);">Transform...</a></li>
|
||||
<li><a data-action="deleteColumn" class="menuAction" href="JavaScript:void(0);">Delete this column</a></li>
|
||||
</script>
|
||||
|
||||
<script type='text/mustache' class="rowActionsTemplate">
|
||||
<li><a data-action="deleteRow" class="menuAction" href="JavaScript:void(0);">Delete this row</a></li>
|
||||
</script>
|
||||
|
||||
<script type='text/mustache' class="titleTemplate"><span id="project-name-button" class="app-path-section">{{db_name}}</span></script>
|
||||
<script type='text/mustache' class="bulkTemplate">http://{{host}}/{{db_name}}/_bulk_docs</script>
|
||||
<script type='text/mustache' class="generatingTemplate"><div class="loading">Loading...</div></script>
|
||||
|
||||
<script type='text/mustache' class="tableContainerTemplate">
|
||||
<div id="tool-panel">
|
||||
<div id="summary-bar">
|
||||
<span id="docCount"></span>
|
||||
</div>
|
||||
<div id="download">
|
||||
</div>
|
||||
</div>
|
||||
<div id="view-panel">
|
||||
<div class="viewpanel-header">
|
||||
<div class="viewpanel-pagesize">
|
||||
<span>
|
||||
Show:
|
||||
</span>
|
||||
<a href="javascript:{}" class="viewPanel-pagingControls-page action">5</a>
|
||||
<a href="javascript:{}" class="viewPanel-pagingControls-page selected">10</a>
|
||||
<a href="javascript:{}" class="viewPanel-pagingControls-page action">25</a>
|
||||
<a href="javascript:{}" class="viewPanel-pagingControls-page action">50</a>
|
||||
<span>
|
||||
rows
|
||||
</span>
|
||||
</div>
|
||||
<div class="viewpanel-sorting">
|
||||
</div>
|
||||
<div class="viewpanel-paging">
|
||||
<a href="javascript:{}" class="first inaction">« first</a>
|
||||
<a href="javascript:{}" class="previous inaction">‹ previous</a>
|
||||
<span class="viewpanel-pagingcount">
|
||||
1 - 10
|
||||
</span>
|
||||
<a href="javascript:{}" class="next action">next ›</a>
|
||||
<a href="javascript:{}" class="last action">last »</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="data-table-container">
|
||||
</div>
|
||||
</div>
|
||||
</script>
|
||||
|
||||
<script type='text/mustache' class="dataTableTemplate">
|
||||
<table class="data-table" cellspacing="0">
|
||||
<tbody>
|
||||
<tr>
|
||||
{{#notEmpty}}<td class="column-header"></td>{{/notEmpty}}
|
||||
{{#headers}}
|
||||
<td class="column-header">
|
||||
<div class="column-header-title">
|
||||
<a class="column-header-menu"></a>
|
||||
<span class="column-header-name">{{.}}</span>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
{{/headers}}
|
||||
</tr>
|
||||
{{#rows}}
|
||||
<tr data-id="{{id}}">
|
||||
<td><a class="row-header-menu"></a></td>
|
||||
{{#cells}}
|
||||
<td data-header="{{header}}">
|
||||
<div class="data-table-cell-content">
|
||||
<a href="javascript:{}" class="data-table-cell-edit" title="Edit this cell"> </a>
|
||||
<div class="data-table-cell-value">{{value}}</div>
|
||||
</div>
|
||||
</td>
|
||||
{{/cells}}
|
||||
</tr>
|
||||
{{/rows}}
|
||||
</tbody>
|
||||
</table>
|
||||
</script>
|
||||
|
||||
<script type='text/mustache' class="signInTemplate">
|
||||
<div class="dialog-header">
|
||||
Sign in
|
||||
</div>
|
||||
<div class="dialog-body">
|
||||
<div class="grid-layout layout-tight layout-full">
|
||||
<form name="sign-in-form" id="sign-in-form">
|
||||
<table class="form-table">
|
||||
<tbody>
|
||||
<tr>
|
||||
<th>
|
||||
<label for="username">Username</label>
|
||||
</th>
|
||||
<td>
|
||||
<input type="text" size="25" id="username-input" name="username">
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>
|
||||
<label for="password">Password</label>
|
||||
</th>
|
||||
<td>
|
||||
<input type="password" size="25" id="password-input" name="password">
|
||||
</td>
|
||||
</tr>
|
||||
<input type="submit" style="height: 0px; width: 0px; border: none; padding: 0px;" hidefocus="true" />
|
||||
</tbody>
|
||||
</table>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<div class="dialog-footer">
|
||||
<button class="okButton button"> Sign in </button>
|
||||
<button class="cancelButton button">Cancel</button>
|
||||
</div>
|
||||
</script>
|
||||
|
||||
<script type='text/mustache' class="transformTemplate">
|
||||
<div class="dialog-header">
|
||||
Recursive transform on all rows
|
||||
</div>
|
||||
<div class="dialog-body">
|
||||
<div class="grid-layout layout-full">
|
||||
<p class="info">Traverse and transform objects by visiting every node on a recursive walk using <a href="https://github.com/substack/js-traverse">js-traverse</a>.</p>
|
||||
<table>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td colspan="4">
|
||||
<div class="grid-layout layout-tight layout-full">
|
||||
<table rows="4" cols="4">
|
||||
<tbody>
|
||||
<tr style="vertical-align: bottom;">
|
||||
<td colspan="4">
|
||||
Expression
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="3">
|
||||
<div class="input-container">
|
||||
<textarea class="expression-preview-code"></textarea>
|
||||
</div>
|
||||
</td>
|
||||
<td class="expression-preview-parsing-status" width="150" style="vertical-align: top;">
|
||||
No syntax error.
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="4">
|
||||
<div id="expression-preview-tabs" class="refine-tabs ui-tabs ui-widget ui-widget-content ui-corner-all">
|
||||
<span>Preview</span>
|
||||
<div id="expression-preview-tabs-preview" class="ui-tabs-panel ui-widget-content ui-corner-bottom">
|
||||
<div class="expression-preview-container" style="width: 652px; ">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<div class="dialog-footer">
|
||||
<button class="okButton button"> Update All </button>
|
||||
<button class="cancelButton button">Cancel</button>
|
||||
</div>
|
||||
</script>
|
||||
</div>
|
||||
|
||||
<script type='text/mustache' class="urlImportTemplate">
|
||||
<div class="dialog-header">
|
||||
Download and import from a URL or API
|
||||
<div class="container-fluid">
|
||||
<div class="content">
|
||||
<div class="data-explorer-here"></div>
|
||||
</div>
|
||||
<div class="dialog-body">
|
||||
<div class="grid-layout layout-full">
|
||||
<p class="info">
|
||||
Currently only <a href="http://en.wikipedia.org/wiki/JSONP">JSONP</a>-enabled APIs are supported, for example:
|
||||
</p>
|
||||
<p class="info">
|
||||
<code>https://api.github.com/repos/maxogden/recline/commits</code>
|
||||
</p>
|
||||
<form name="api-import-form" id="sign-in-form">
|
||||
<table class="form-table">
|
||||
<tbody>
|
||||
<tr>
|
||||
<th>
|
||||
<label for="url">URL</label>
|
||||
</th>
|
||||
<td>
|
||||
<input type="text" size="65" id="url-input" name="url">
|
||||
</td>
|
||||
</tr>
|
||||
<input type="submit" style="height: 0px; width: 0px; border: none; padding: 0px; display: none;" hidefocus="true" />
|
||||
</tbody>
|
||||
</table>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<div class="dialog-footer">
|
||||
<button class="okButton button"> Fetch </button>
|
||||
<button class="cancelButton button">Cancel</button>
|
||||
</div>
|
||||
</script>
|
||||
|
||||
<script type='text/mustache' class="pasteImportTemplate">
|
||||
<div class="dialog-header">
|
||||
Import raw copy & pasted JSON
|
||||
</div>
|
||||
<div class="dialog-body">
|
||||
<div class="grid-layout layout-tight layout-full">
|
||||
<p class="info">
|
||||
Paste in an array of JSON objects representing the documents that you would like to insert into the database.
|
||||
</p>
|
||||
<p class="info">
|
||||
<code>[{"woo": "pizza"}, {"tasty": "muffins"}]</code>
|
||||
</p>
|
||||
<div class="menu-container data-table-cell-editor">
|
||||
<textarea class="data-table-cell-copypaste-editor" bind="textarea">{{value}}</textarea>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="dialog-footer">
|
||||
<button class="okButton button"> Import </button>
|
||||
<button class="cancelButton button">Cancel</button>
|
||||
</div>
|
||||
</script>
|
||||
|
||||
<script type='text/mustache' class="uploadImportTemplate">
|
||||
<div class="dialog-header">
|
||||
Upload and import a CSV
|
||||
</div>
|
||||
<div class="dialog-body">
|
||||
<div class="grid-layout layout-tight layout-full">
|
||||
<strong>Please choose a CSV file to upload:</strong><br />
|
||||
<input type="file" id="file" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="dialog-footer">
|
||||
<button class="okButton button"> Import </button>
|
||||
<button class="cancelButton button">Cancel</button>
|
||||
</div>
|
||||
</script>
|
||||
|
||||
<script type='text/mustache' class="bulkEditTemplate">
|
||||
<div class="dialog-header">
|
||||
Functional transform on column {{name}}
|
||||
</div>
|
||||
<div class="dialog-body">
|
||||
<div class="grid-layout layout-tight layout-full">
|
||||
<table>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td colspan="4">
|
||||
<div class="grid-layout layout-tight layout-full">
|
||||
<table rows="4" cols="4">
|
||||
<tbody>
|
||||
<tr style="vertical-align: bottom;">
|
||||
<td colspan="4">
|
||||
Expression
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="3">
|
||||
<div class="input-container">
|
||||
<textarea class="expression-preview-code"></textarea>
|
||||
</div>
|
||||
</td>
|
||||
<td class="expression-preview-parsing-status" width="150" style="vertical-align: top;">
|
||||
No syntax error.
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="4">
|
||||
<div id="expression-preview-tabs" class="refine-tabs ui-tabs ui-widget ui-widget-content ui-corner-all">
|
||||
<span>Preview</span>
|
||||
<div id="expression-preview-tabs-preview" class="ui-tabs-panel ui-widget-content ui-corner-bottom">
|
||||
<div class="expression-preview-container" style="width: 652px; ">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<div class="dialog-footer">
|
||||
<button class="okButton button"> Update All </button>
|
||||
<button class="cancelButton button">Cancel</button>
|
||||
</div>
|
||||
</script>
|
||||
|
||||
<script type='text/mustache' class="cellEditorTemplate">
|
||||
<div class="menu-container data-table-cell-editor">
|
||||
<textarea class="data-table-cell-editor-editor" bind="textarea">{{value}}</textarea>
|
||||
<div id="data-table-cell-editor-actions">
|
||||
<div class="data-table-cell-editor-action">
|
||||
<button class="okButton button">Update</button>
|
||||
</div>
|
||||
<div class="data-table-cell-editor-action">
|
||||
<button class="cancelButton button">Cancel</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</script>
|
||||
|
||||
<script type='text/mustache' class="jsonTreeTemplate">
|
||||
<div class="dialog-header">
|
||||
Please highlight the array of JSON objects to convert to documents.
|
||||
</div>
|
||||
<div class="dialog-body">
|
||||
<div id="document-container">
|
||||
<div id="document-editor"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="dialog-footer">
|
||||
<button class="okButton button"> Import </button>
|
||||
<button class="cancelButton button">Cancel</button>
|
||||
</div>
|
||||
</script>
|
||||
|
||||
<script type='text/mustache' class="editPreviewTemplate">
|
||||
<div class="expression-preview-table-wrapper">
|
||||
<table>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td class="expression-preview-heading">
|
||||
before
|
||||
</td>
|
||||
<td class="expression-preview-heading">
|
||||
after
|
||||
</td>
|
||||
</tr>
|
||||
{{#rows}}
|
||||
<tr>
|
||||
<td class="expression-preview-value">
|
||||
{{before}}
|
||||
</td>
|
||||
<td class="expression-preview-value">
|
||||
{{after}}
|
||||
</td>
|
||||
</tr>
|
||||
{{/rows}}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</script>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
|
||||
@ -1,12 +1,12 @@
|
||||
$(function() {
|
||||
// do not like all these window globals ...
|
||||
// window.$container = $('.container .right-panel');
|
||||
window.$container = $('.container');
|
||||
window.$container = $('.data-explorer-here');
|
||||
var dataset = demoDataset();
|
||||
window.dataExplorer = new recline.View.DataExplorer({
|
||||
model: dataset
|
||||
el: window.$container
|
||||
, model: dataset
|
||||
});
|
||||
window.$container.append(window.dataExplorer.el);
|
||||
setupLoadFromWebstore(function(dataset) {
|
||||
window.dataExplorer.remove();
|
||||
window.dataExplorer = null;
|
||||
@ -15,6 +15,10 @@ $(function() {
|
||||
});
|
||||
window.$container.append(window.dataExplorer.el);
|
||||
});
|
||||
$('a.set-read-only').click(function() {
|
||||
window.dataExplorer.setReadOnly();
|
||||
alert('Read-only mode set');
|
||||
});
|
||||
})
|
||||
|
||||
function demoDataset() {
|
||||
|
||||
241
demo/original.html
Executable file
241
demo/original.html
Executable file
@ -0,0 +1,241 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Data Explorer</title>
|
||||
<link rel="stylesheet" href="style/reset.css" media="screen">
|
||||
<link rel="stylesheet" href="style/data-table.css" media="screen">
|
||||
<link rel="stylesheet" href="style/flot-graph.css" media="screen">
|
||||
<link rel="stylesheet" href="style/style.css" media="screen">
|
||||
<script type="text/javascript" src="../src/deps-min.js"></script>
|
||||
|
||||
<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/view.js"></script>
|
||||
<script type="text/javascript" src="js/app.js"></script>
|
||||
</head>
|
||||
<body class="bod">
|
||||
<div class="container">
|
||||
<div id="header">
|
||||
<div class="project-title">
|
||||
<a href="http://github.com/okfn/recline">Recline DataExplorer</a>
|
||||
/ Demo
|
||||
</div>
|
||||
<div class="project-actions">
|
||||
<form class="webstore-load">
|
||||
<label for="source">Source</label>
|
||||
<input type="text" name="source" size="50" />
|
||||
<input type="submit" name="" value="Update" />
|
||||
</form>
|
||||
</div>
|
||||
<div class="project-controls"></div>
|
||||
</div>
|
||||
<div class="data-explorer-here"></div>
|
||||
<!--
|
||||
<div class="main_content">
|
||||
<div class="left-panel"></div>
|
||||
<div class="right-panel"></div>
|
||||
</div>
|
||||
<div class="data-table-container"></div>
|
||||
-->
|
||||
</div>
|
||||
|
||||
<script type='text/mustache' class="busyTemplate">
|
||||
<div id="loading-message">
|
||||
<img src="images/large-spinner.gif">
|
||||
<span> Working...</span>
|
||||
</div>
|
||||
</script>
|
||||
|
||||
<script type='text/mustache' class="controlsTemplate">
|
||||
<a id="logged-in-status" href="JavaScript:void(0);" class="secondary">{{text}}</a>
|
||||
</script>
|
||||
|
||||
<script type='text/mustache' class="actionsTemplate">
|
||||
<a class="button" data-action="import" href="javascript:{}"><span data-action="import" class="button-menu">Import</span></a>
|
||||
<!-- <a class="button" data-action="edit" href="javascript:{}"><span data-action="transform" class="button-menu">Edit</span></a> -->
|
||||
<a class="button" data-action="export" href="javascript:{}"><span data-action="export" class="button-menu">Export</span></a>
|
||||
</script>
|
||||
|
||||
<script type='text/mustache' class="importActionsTemplate">
|
||||
<li><a data-action="urlImport" class="menuAction" href="JavaScript:void(0);">JSON API</a></li>
|
||||
<li><a data-action="pasteImport" class="menuAction" href="JavaScript:void(0);">Paste JSON</a></li>
|
||||
<li><a data-action="uploadImport" class="menuAction" href="JavaScript:void(0);">Upload CSV</a></li>
|
||||
</script>
|
||||
|
||||
<script type='text/mustache' class="exportActionsTemplate">
|
||||
<li><a data-action="csv" class="menuAction" href="JavaScript:void(0);">CSV</a></li>
|
||||
<li><a data-action="json" class="menuAction" href="JavaScript:void(0);">JSON</a></li>
|
||||
</script>
|
||||
|
||||
<script type='text/mustache' class="titleTemplate"><span id="project-name-button" class="app-path-section">{{db_name}}</span></script>
|
||||
<script type='text/mustache' class="bulkTemplate">http://{{host}}/{{db_name}}/_bulk_docs</script>
|
||||
<script type='text/mustache' class="generatingTemplate"><div class="loading">Loading...</div></script>
|
||||
|
||||
<script type='text/mustache' class="tableContainerTemplate">
|
||||
<div id="tool-panel">
|
||||
<div id="summary-bar">
|
||||
<span id="docCount"></span>
|
||||
</div>
|
||||
<div id="download">
|
||||
</div>
|
||||
</div>
|
||||
<div id="view-panel">
|
||||
<div class="viewpanel-header">
|
||||
<div class="viewpanel-pagesize">
|
||||
<span>
|
||||
Show:
|
||||
</span>
|
||||
<a href="javascript:{}" class="viewPanel-pagingControls-page action">5</a>
|
||||
<a href="javascript:{}" class="viewPanel-pagingControls-page selected">10</a>
|
||||
<a href="javascript:{}" class="viewPanel-pagingControls-page action">25</a>
|
||||
<a href="javascript:{}" class="viewPanel-pagingControls-page action">50</a>
|
||||
<span>
|
||||
rows
|
||||
</span>
|
||||
</div>
|
||||
<div class="viewpanel-sorting">
|
||||
</div>
|
||||
<div class="viewpanel-paging">
|
||||
<a href="javascript:{}" class="first inaction">« first</a>
|
||||
<a href="javascript:{}" class="previous inaction">‹ previous</a>
|
||||
<span class="viewpanel-pagingcount">
|
||||
1 - 10
|
||||
</span>
|
||||
<a href="javascript:{}" class="next action">next ›</a>
|
||||
<a href="javascript:{}" class="last action">last »</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="data-table-container">
|
||||
</div>
|
||||
</div>
|
||||
</script>
|
||||
|
||||
<script type='text/mustache' class="signInTemplate">
|
||||
<div class="dialog-header">
|
||||
Sign in
|
||||
</div>
|
||||
<div class="dialog-body">
|
||||
<div class="grid-layout layout-tight layout-full">
|
||||
<form name="sign-in-form" id="sign-in-form">
|
||||
<table class="form-table">
|
||||
<tbody>
|
||||
<tr>
|
||||
<th>
|
||||
<label for="username">Username</label>
|
||||
</th>
|
||||
<td>
|
||||
<input type="text" size="25" id="username-input" name="username">
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>
|
||||
<label for="password">Password</label>
|
||||
</th>
|
||||
<td>
|
||||
<input type="password" size="25" id="password-input" name="password">
|
||||
</td>
|
||||
</tr>
|
||||
<input type="submit" style="height: 0px; width: 0px; border: none; padding: 0px;" hidefocus="true" />
|
||||
</tbody>
|
||||
</table>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<div class="dialog-footer">
|
||||
<button class="okButton button"> Sign in </button>
|
||||
<button class="cancelButton button">Cancel</button>
|
||||
</div>
|
||||
</script>
|
||||
|
||||
<script type='text/mustache' class="urlImportTemplate">
|
||||
<div class="dialog-header">
|
||||
Download and import from a URL or API
|
||||
</div>
|
||||
<div class="dialog-body">
|
||||
<div class="grid-layout layout-full">
|
||||
<p class="info">
|
||||
Currently only <a href="http://en.wikipedia.org/wiki/JSONP">JSONP</a>-enabled APIs are supported, for example:
|
||||
</p>
|
||||
<p class="info">
|
||||
<code>https://api.github.com/repos/maxogden/recline/commits</code>
|
||||
</p>
|
||||
<form name="api-import-form" id="sign-in-form">
|
||||
<table class="form-table">
|
||||
<tbody>
|
||||
<tr>
|
||||
<th>
|
||||
<label for="url">URL</label>
|
||||
</th>
|
||||
<td>
|
||||
<input type="text" size="65" id="url-input" name="url">
|
||||
</td>
|
||||
</tr>
|
||||
<input type="submit" style="height: 0px; width: 0px; border: none; padding: 0px; display: none;" hidefocus="true" />
|
||||
</tbody>
|
||||
</table>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<div class="dialog-footer">
|
||||
<button class="okButton button"> Fetch </button>
|
||||
<button class="cancelButton button">Cancel</button>
|
||||
</div>
|
||||
</script>
|
||||
|
||||
<script type='text/mustache' class="pasteImportTemplate">
|
||||
<div class="dialog-header">
|
||||
Import raw copy & pasted JSON
|
||||
</div>
|
||||
<div class="dialog-body">
|
||||
<div class="grid-layout layout-tight layout-full">
|
||||
<p class="info">
|
||||
Paste in an array of JSON objects representing the documents that you would like to insert into the database.
|
||||
</p>
|
||||
<p class="info">
|
||||
<code>[{"woo": "pizza"}, {"tasty": "muffins"}]</code>
|
||||
</p>
|
||||
<div class="menu-container data-table-cell-editor">
|
||||
<textarea class="data-table-cell-copypaste-editor" bind="textarea">{{value}}</textarea>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="dialog-footer">
|
||||
<button class="okButton button"> Import </button>
|
||||
<button class="cancelButton button">Cancel</button>
|
||||
</div>
|
||||
</script>
|
||||
|
||||
<script type='text/mustache' class="uploadImportTemplate">
|
||||
<div class="dialog-header">
|
||||
Upload and import a CSV
|
||||
</div>
|
||||
<div class="dialog-body">
|
||||
<div class="grid-layout layout-tight layout-full">
|
||||
<strong>Please choose a CSV file to upload:</strong><br />
|
||||
<input type="file" id="file" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="dialog-footer">
|
||||
<button class="okButton button"> Import </button>
|
||||
<button class="cancelButton button">Cancel</button>
|
||||
</div>
|
||||
</script>
|
||||
|
||||
<script type='text/mustache' class="jsonTreeTemplate">
|
||||
<div class="dialog-header">
|
||||
Please highlight the array of JSON objects to convert to documents.
|
||||
</div>
|
||||
<div class="dialog-body">
|
||||
<div id="document-container">
|
||||
<div id="document-editor"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="dialog-footer">
|
||||
<button class="okButton button"> Import </button>
|
||||
<button class="cancelButton button">Cancel</button>
|
||||
</div>
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@ -173,7 +173,7 @@ a img {
|
||||
top: -4px;
|
||||
}
|
||||
|
||||
#notification-container {
|
||||
.notification-container {
|
||||
width: 400px;
|
||||
left: 520px;
|
||||
display: none;
|
||||
@ -183,7 +183,7 @@ a img {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
#notification {
|
||||
.notification {
|
||||
display: inline-block;
|
||||
margin: 0 auto;
|
||||
padding: 5px 8px 4px;
|
||||
|
||||
@ -43,7 +43,7 @@ a.button:hover span.icon.loading { background-image: url(images/loader-blue.gif)
|
||||
.chosen {border: 1px solid green}
|
||||
.info { padding: 0px 0px 10px 0px}
|
||||
.large-loader { position: relative; }
|
||||
.menu-overlay {
|
||||
.data-table-menu-overlay {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
@ -51,7 +51,7 @@ a.button:hover span.icon.loading { background-image: url(images/loader-blue.gif)
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
ul.menu {
|
||||
ul.data-table-menu {
|
||||
display: none;
|
||||
outline-style: none;
|
||||
background: white;
|
||||
@ -68,18 +68,18 @@ ul.menu {
|
||||
border-right: 1px solid #666;
|
||||
border-bottom: 1px solid #666;
|
||||
margin: 0; padding: 0; }
|
||||
ul.menu * {
|
||||
ul.data-table-menu * {
|
||||
margin: 0;
|
||||
padding: 0; }
|
||||
ul.menu a {
|
||||
ul.data-table-menu a {
|
||||
line-height: 14px;
|
||||
color: black;
|
||||
display: block;
|
||||
padding: 5px 7px;
|
||||
text-decoration: none; }
|
||||
ul.menu li {
|
||||
ul.data-table-menu li {
|
||||
height: 24px; }
|
||||
ul.menu li:hover {
|
||||
ul.data-table-menu li:hover {
|
||||
background-color: #DBE8F8 }
|
||||
|
||||
|
||||
@ -213,7 +213,7 @@ span.tooltip-status {
|
||||
|
||||
/* rgrp added mods */
|
||||
|
||||
.data-explorer .nav {
|
||||
.data-explorer .header {
|
||||
border-top: 15px solid #BCF;
|
||||
border-left: 5px solid #BCF;
|
||||
display: block;
|
||||
@ -227,6 +227,27 @@ span.tooltip-status {
|
||||
padding-top: 3px;
|
||||
}
|
||||
|
||||
.header .navigation,
|
||||
.header .navigation li,
|
||||
.header .pagination,
|
||||
.header .pagination form
|
||||
{
|
||||
display: inline;
|
||||
}
|
||||
|
||||
.header .pagination {
|
||||
float: right;
|
||||
}
|
||||
|
||||
.header .pagination input {
|
||||
width: 40px;
|
||||
}
|
||||
|
||||
.doc-count {
|
||||
font-weight: bold;
|
||||
font-size: 120%;
|
||||
}
|
||||
|
||||
.data-view-container {
|
||||
display: block;
|
||||
border-top: 1px solid #BCF;
|
||||
@ -239,13 +260,3 @@ span.tooltip-status {
|
||||
right: 0px;
|
||||
background: white;
|
||||
}
|
||||
|
||||
.nav-pagination {
|
||||
display: inline;
|
||||
float: right;
|
||||
list-style-type: none;
|
||||
}
|
||||
|
||||
.nav-pagination input {
|
||||
width: 40px;
|
||||
}
|
||||
|
||||
@ -25,8 +25,7 @@ var costco = function() {
|
||||
preview.push({before: JSON.stringify(before), after: JSON.stringify(after)});
|
||||
}
|
||||
}
|
||||
// TODO: 2012-01-05 Move this out of this function and up into (view) functions that call this
|
||||
util.render('editPreview', 'expression-preview-container', {rows: preview});
|
||||
return preview;
|
||||
}
|
||||
|
||||
function mapDocs(docs, editFunc) {
|
||||
|
||||
56
src/model.js
56
src/model.js
@ -6,26 +6,32 @@ recline.Model = function($) {
|
||||
var my = {};
|
||||
|
||||
// A Dataset model.
|
||||
//
|
||||
// Other than standard list of Backbone attributes it has two important attributes:
|
||||
//
|
||||
// * currentDocuments: a DocumentList containing the Documents we have currently loaded for viewing (you update currentDocuments by calling getRows)
|
||||
// * docCount: total number of documents in this dataset (obtained on a fetch for this Dataset)
|
||||
my.Dataset = Backbone.Model.extend({
|
||||
__type__: 'Dataset',
|
||||
initialize: function() {
|
||||
this.currentDocuments = new my.DocumentList();
|
||||
this.docCount = null;
|
||||
},
|
||||
|
||||
getLength: function() {
|
||||
return this.rowCount;
|
||||
},
|
||||
|
||||
// Get rows (documents) from the backend returning a recline.DocumentList
|
||||
// AJAX method with promise API to get rows (documents) from the backend.
|
||||
//
|
||||
// TODO: ? rename to getDocuments?
|
||||
// Resulting DocumentList are used to reset this.currentDocuments and are
|
||||
// also returned.
|
||||
//
|
||||
// :param numRows: passed onto backend getDocuments.
|
||||
// :param start: passed onto backend getDocuments.
|
||||
//
|
||||
// this does not fit very well with Backbone setup. Backbone really expects you to know the ids of objects your are fetching (which you do in classic RESTful ajax-y world). But this paradigm does not fill well with data set up we have here.
|
||||
// This also illustrates the limitations of separating the Dataset and the Backend
|
||||
getRows: function(numRows, start) {
|
||||
getDocuments: function(numRows, start) {
|
||||
var self = this;
|
||||
var dfd = $.Deferred();
|
||||
this.backend.getRows(this.id, numRows, start).then(function(rows) {
|
||||
this.backend.getDocuments(this.id, numRows, start).then(function(rows) {
|
||||
var docs = _.map(rows, function(row) {
|
||||
return new my.Document(row);
|
||||
});
|
||||
@ -33,6 +39,12 @@ my.Dataset = Backbone.Model.extend({
|
||||
dfd.resolve(self.currentDocuments);
|
||||
});
|
||||
return dfd.promise();
|
||||
},
|
||||
|
||||
toTemplateJSON: function() {
|
||||
var data = this.toJSON();
|
||||
data.docCount = this.docCount;
|
||||
return data;
|
||||
}
|
||||
});
|
||||
|
||||
@ -55,14 +67,16 @@ my.setBackend = function(backend) {
|
||||
|
||||
// Backend which just caches in memory
|
||||
//
|
||||
// Does not need to be a backbone model but provides some conveience
|
||||
// Does not need to be a backbone model but provides some conveniences
|
||||
my.BackendMemory = Backbone.Model.extend({
|
||||
// Initialize a Backend with a local in-memory dataset.
|
||||
//
|
||||
// NB: We can handle one and only one dataset at a time.
|
||||
//
|
||||
// :param dataset: the data for a dataset on which operations will be
|
||||
// performed. In the form of a hash with metadata and data attributes.
|
||||
// performed. Its form should be a hash with metadata and data
|
||||
// attributes.
|
||||
//
|
||||
// - metadata: hash of key/value attributes of any kind (but usually with title attribute)
|
||||
// - data: hash with 2 keys:
|
||||
// - headers: list of header names/labels
|
||||
@ -70,13 +84,13 @@ my.BackendMemory = Backbone.Model.extend({
|
||||
//
|
||||
// Example of data:
|
||||
//
|
||||
// {
|
||||
// headers: ['x', 'y', 'z']
|
||||
// , rows: [
|
||||
// {id: 0, x: 1, y: 2, z: 3}
|
||||
// , {id: 1, x: 2, y: 4, z: 6}
|
||||
// ]
|
||||
// };
|
||||
// {
|
||||
// headers: ['x', 'y', 'z']
|
||||
// , rows: [
|
||||
// {id: 0, x: 1, y: 2, z: 3}
|
||||
// , {id: 1, x: 2, y: 4, z: 6}
|
||||
// ]
|
||||
// };
|
||||
initialize: function(dataset) {
|
||||
// deep copy
|
||||
this._datasetAsData = $.extend(true, {}, dataset);
|
||||
@ -103,7 +117,7 @@ my.BackendMemory = Backbone.Model.extend({
|
||||
dataset.set({
|
||||
headers: rawDataset.data.headers
|
||||
});
|
||||
dataset.rowCount = rawDataset.data.rows.length;
|
||||
dataset.docCount = rawDataset.data.rows.length;
|
||||
dfd.resolve(dataset);
|
||||
}
|
||||
return dfd.promise();
|
||||
@ -131,7 +145,7 @@ my.BackendMemory = Backbone.Model.extend({
|
||||
alert('Not supported: sync on BackendMemory with method ' + method + ' and model ' + model);
|
||||
}
|
||||
},
|
||||
getRows: function(datasetId, numRows, start) {
|
||||
getDocuments: function(datasetId, numRows, start) {
|
||||
if (start === undefined) {
|
||||
start = 0;
|
||||
}
|
||||
@ -184,14 +198,14 @@ my.BackendWebstore = Backbone.Model.extend({
|
||||
dataset.set({
|
||||
headers: headers
|
||||
});
|
||||
dataset.rowCount = schema.count;
|
||||
dataset.docCount = schema.count;
|
||||
dfd.resolve(dataset, jqxhr);
|
||||
});
|
||||
return dfd.promise();
|
||||
}
|
||||
}
|
||||
},
|
||||
getRows: function(datasetId, numRows, start) {
|
||||
getDocuments: function(datasetId, numRows, start) {
|
||||
if (start === undefined) {
|
||||
start = 0;
|
||||
}
|
||||
|
||||
84
src/util.js
84
src/util.js
@ -1,4 +1,51 @@
|
||||
var util = function() {
|
||||
var templates = {
|
||||
transformActions: '<li><a data-action="transform" class="menuAction" href="JavaScript:void(0);">Global transform...</a></li>'
|
||||
, columnActions: ' \
|
||||
<li><a data-action="bulkEdit" class="menuAction" href="JavaScript:void(0);">Transform...</a></li> \
|
||||
<li><a data-action="deleteColumn" class="menuAction" href="JavaScript:void(0);">Delete this column</a></li> \
|
||||
'
|
||||
, rowActions: '<li><a data-action="deleteRow" class="menuAction" href="JavaScript:void(0);">Delete this row</a></li>'
|
||||
, cellEditor: ' \
|
||||
<div class="menu-container data-table-cell-editor"> \
|
||||
<textarea class="data-table-cell-editor-editor" bind="textarea">{{value}}</textarea> \
|
||||
<div id="data-table-cell-editor-actions"> \
|
||||
<div class="data-table-cell-editor-action"> \
|
||||
<button class="okButton btn primary">Update</button> \
|
||||
<button class="cancelButton btn danger">Cancel</button> \
|
||||
</div> \
|
||||
</div> \
|
||||
</div> \
|
||||
'
|
||||
, editPreview: ' \
|
||||
<div class="expression-preview-table-wrapper"> \
|
||||
<table> \
|
||||
<thead> \
|
||||
<tr> \
|
||||
<th class="expression-preview-heading"> \
|
||||
before \
|
||||
</th> \
|
||||
<th class="expression-preview-heading"> \
|
||||
after \
|
||||
</th> \
|
||||
</tr> \
|
||||
</thead> \
|
||||
<tbody> \
|
||||
{{#rows}} \
|
||||
<tr> \
|
||||
<td class="expression-preview-value"> \
|
||||
{{before}} \
|
||||
</td> \
|
||||
<td class="expression-preview-value"> \
|
||||
{{after}} \
|
||||
</td> \
|
||||
</tr> \
|
||||
{{/rows}} \
|
||||
</tbody> \
|
||||
</table> \
|
||||
</div> \
|
||||
'
|
||||
};
|
||||
|
||||
$.fn.serializeObject = function() {
|
||||
var o = {};
|
||||
@ -74,7 +121,7 @@ var util = function() {
|
||||
}
|
||||
|
||||
function position( thing, elem, offset ) {
|
||||
var position = $(elem.target).offset();
|
||||
var position = $(elem.target).position();
|
||||
if (offset) {
|
||||
if (offset.top) position.top += offset.top;
|
||||
if (offset.left) position.left += offset.left;
|
||||
@ -89,7 +136,7 @@ var util = function() {
|
||||
function render( template, target, options ) {
|
||||
if ( !options ) options = {data: {}};
|
||||
if ( !options.data ) options = {data: options};
|
||||
var html = $.mustache( $( "." + template + "Template:first" ).html(), options.data );
|
||||
var html = $.mustache( templates[template], options.data );
|
||||
if (target instanceof jQuery) {
|
||||
var targetDom = target;
|
||||
} else {
|
||||
@ -103,16 +150,31 @@ var util = function() {
|
||||
// TODO: remove (commented out as part of Backbon-i-fication
|
||||
// if (template in app.after) app.after[template]();
|
||||
}
|
||||
|
||||
function notify( message, options ) {
|
||||
if (!options) var options = {};
|
||||
$('#notification-container').show();
|
||||
$('#notification-message').text(message);
|
||||
if (!options.loader) $('.notification-loader').hide();
|
||||
if (options.loader) $('.notification-loader').show();
|
||||
if (!options.persist) setTimeout(function() { $('#notification-container').hide() }, 3000);
|
||||
}
|
||||
|
||||
function notify(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).remove();
|
||||
}, 3000);
|
||||
}
|
||||
}
|
||||
|
||||
function formatMetadata(data) {
|
||||
out = '<dl>';
|
||||
$.each(data, function(key, val) {
|
||||
|
||||
487
src/view.js
487
src/view.js
@ -5,86 +5,164 @@ recline.View = function($) {
|
||||
|
||||
var my = {};
|
||||
|
||||
// Parse a URL query string (?xyz=abc...) into a dictionary.
|
||||
function parseQueryString(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;
|
||||
}
|
||||
|
||||
// The primary view for the entire application.
|
||||
//
|
||||
// It should be initialized with a recline.Model.Dataset object and an existing
|
||||
// dom element to attach to (the existing DOM element is important for
|
||||
// rendering of FlotGraph subview).
|
||||
//
|
||||
// To pass in configuration options use the config key in initialization hash
|
||||
// e.g.
|
||||
//
|
||||
// var explorer = new DataExplorer({
|
||||
// config: {...}
|
||||
// })
|
||||
//
|
||||
// Config options:
|
||||
//
|
||||
// * displayCount: how many documents to display initially (default: 10)
|
||||
// * readOnly: true/false (default: false) value indicating whether to
|
||||
// operate in read-only mode (hiding all editing options).
|
||||
//
|
||||
// All other views as contained in this one.
|
||||
my.DataExplorer = Backbone.View.extend({
|
||||
tagName: 'div',
|
||||
className: 'data-explorer',
|
||||
template: ' \
|
||||
<div class="nav"> \
|
||||
<span class="nav-toggle"> \
|
||||
<input type="radio" id="datatable" name="nav-toggle" value="datatable" checked="checked" /> \
|
||||
<label for="nav-datatable">Data Table</label> \
|
||||
<input type="radio" id="nav-graph" name="nav-toggle" value="graph" /> \
|
||||
<label for="nav-graph">Graph</label> \
|
||||
</span> \
|
||||
<ul class="nav-pagination"> \
|
||||
<li><form class="display-count"><label for="per-page">Display count</label> <input name="displayCount" type="text" value="{{displayCount}}" /></form></li> \
|
||||
<div class="data-explorer"> \
|
||||
<div class="alert-messages"></div> \
|
||||
\
|
||||
<div class="header"> \
|
||||
<ul class="navigation"> \
|
||||
<li class="active"><a href="#grid" class="btn">Grid</a> \
|
||||
<li><a href="#graph" class="btn">Graph</a></li> \
|
||||
</ul> \
|
||||
<div class="pagination"> \
|
||||
<form class="display-count"> \
|
||||
Showing 0 to <input name="displayCount" type="text" value="{{displayCount}}" /> of <span class="doc-count">{{docCount}}</span> \
|
||||
</form> \
|
||||
</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> \
|
||||
',
|
||||
|
||||
events: {
|
||||
'change input[name="nav-toggle"]': 'navChange',
|
||||
'submit form.display-count': 'displayCountUpdate'
|
||||
'submit form.display-count': 'onDisplayCountUpdate'
|
||||
},
|
||||
|
||||
initialize: function(options) {
|
||||
var self = this;
|
||||
this.el = $(this.el);
|
||||
this.config = options.config || {};
|
||||
_.extend(this.config, {
|
||||
displayCount: 10
|
||||
, readOnly: false
|
||||
});
|
||||
this.draw();
|
||||
},
|
||||
|
||||
displayCountUpdate: function(e) {
|
||||
e.preventDefault();
|
||||
this.config.displayCount = parseInt(this.el.find('input[name="displayCount"]').val());
|
||||
this.draw();
|
||||
},
|
||||
|
||||
draw: function() {
|
||||
var self = this;
|
||||
this.el.empty();
|
||||
if (this.config.readOnly) {
|
||||
this.setReadOnly();
|
||||
}
|
||||
// Hash of 'page' views (i.e. those for whole page) keyed by page name
|
||||
this.pageViews = {
|
||||
grid: new my.DataTable({
|
||||
model: this.model
|
||||
})
|
||||
, graph: new my.FlotGraph({
|
||||
model: this.model
|
||||
})
|
||||
};
|
||||
// this must be called after pageViews are created
|
||||
this.render();
|
||||
this.$dataViewContainer = this.el.find('.data-view-container');
|
||||
|
||||
this.router = new Backbone.Router();
|
||||
this.setupRouting();
|
||||
Backbone.history.start();
|
||||
|
||||
// retrieve basic data like headers etc
|
||||
// note this.model and dataset returned are the same
|
||||
this.model.fetch().then(function(dataset) {
|
||||
self.el.find('.doc-count').text(self.model.docCount);
|
||||
// initialize of dataTable calls render
|
||||
self.dataTable = new my.DataTable({
|
||||
model: dataset
|
||||
});
|
||||
self.flotGraph = new my.FlotGraph({
|
||||
model: dataset
|
||||
});
|
||||
self.flotGraph.el.hide();
|
||||
self.$dataViewContainer.append(self.dataTable.el)
|
||||
self.$dataViewContainer.append(self.flotGraph.el);
|
||||
self.model.getRows(self.config.displayCount);
|
||||
self.model.getDocuments(self.config.displayCount);
|
||||
});
|
||||
},
|
||||
|
||||
render: function() {
|
||||
var template = $.mustache(this.template, this.config);
|
||||
$(this.el).html(template);
|
||||
onDisplayCountUpdate: function(e) {
|
||||
e.preventDefault();
|
||||
this.config.displayCount = parseInt(this.el.find('input[name="displayCount"]').val());
|
||||
this.model.getDocuments(this.config.displayCount);
|
||||
},
|
||||
|
||||
navChange: function(e) {
|
||||
// TODO: really ugly and will not scale to more widgets ...
|
||||
var widgetToShow = $(e.target).val();
|
||||
if (widgetToShow == 'datatable') {
|
||||
this.flotGraph.el.hide();
|
||||
this.dataTable.el.show();
|
||||
} else if (widgetToShow == 'graph') {
|
||||
this.flotGraph.el.show();
|
||||
this.dataTable.el.hide();
|
||||
// Have to call this here
|
||||
// If you attempt to render with flot when graph is hidden / invisible flot will complain with
|
||||
// Invalid dimensions for plot, width = 0, height = 0
|
||||
// (Could hack this by moving plot left -1000 or similar ...)
|
||||
this.flotGraph.createPlot();
|
||||
}
|
||||
setReadOnly: function() {
|
||||
this.el.addClass('read-only');
|
||||
},
|
||||
|
||||
render: function() {
|
||||
var tmplData = this.model.toTemplateJSON();
|
||||
tmplData.displayCount = this.config.displayCount;
|
||||
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.el)
|
||||
});
|
||||
},
|
||||
|
||||
setupRouting: function() {
|
||||
var self = this;
|
||||
this.router.route('', 'grid', function() {
|
||||
self.updateNav('grid');
|
||||
});
|
||||
this.router.route(/grid(\?.*)?/, 'view', function(queryString) {
|
||||
self.updateNav('grid', queryString);
|
||||
});
|
||||
this.router.route(/graph(\?.*)?/, 'graph', function(queryString) {
|
||||
self.updateNav('graph', queryString);
|
||||
// we have to call here due to fact plot may not have been able to draw
|
||||
// if it was hidden until now - see comments in FlotGraph.redraw
|
||||
qsParsed = parseQueryString(queryString);
|
||||
if ('graph' in qsParsed) {
|
||||
var chartConfig = JSON.parse(qsParsed['graph']);
|
||||
_.extend(self.pageViews['graph'].chartConfig, chartConfig);
|
||||
}
|
||||
self.pageViews['graph'].redraw();
|
||||
});
|
||||
},
|
||||
|
||||
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, pageViewName) {
|
||||
if (pageViewName === pageName) {
|
||||
view.el.show();
|
||||
} else {
|
||||
view.el.hide();
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
@ -103,29 +181,24 @@ my.DataTable = Backbone.View.extend({
|
||||
this.model.currentDocuments.bind('reset', this.render);
|
||||
this.model.currentDocuments.bind('remove', this.render);
|
||||
this.state = {};
|
||||
// this is nasty. Due to fact that .menu element is not inside this view but is elsewhere in DOM
|
||||
$('.menu li a').live('click', function(e) {
|
||||
// self.onMenuClick(e).apply(self);
|
||||
self.onMenuClick(e);
|
||||
});
|
||||
},
|
||||
|
||||
events: {
|
||||
// see initialize
|
||||
// 'click .menu li': 'onMenuClick',
|
||||
'click .column-header-menu': 'onColumnHeaderClick',
|
||||
'click .row-header-menu': 'onRowHeaderClick'
|
||||
'click .column-header-menu': 'onColumnHeaderClick'
|
||||
, 'click .row-header-menu': 'onRowHeaderClick'
|
||||
, 'click .data-table-menu li a': 'onMenuClick'
|
||||
},
|
||||
|
||||
showDialog: function(template, data) {
|
||||
if (!data) data = {};
|
||||
util.show('dialog');
|
||||
util.render(template, 'dialog-content', data);
|
||||
util.observeExit($('.dialog-content'), function() {
|
||||
util.hide('dialog');
|
||||
})
|
||||
$('.dialog').draggable({ handle: '.dialog-header', cursor: 'move' });
|
||||
},
|
||||
// TODO: delete or re-enable (currently this code is not used from anywhere except deprecated or disabled methods (see above)).
|
||||
// showDialog: function(template, data) {
|
||||
// if (!data) data = {};
|
||||
// util.show('dialog');
|
||||
// util.render(template, 'dialog-content', data);
|
||||
// util.observeExit($('.dialog-content'), function() {
|
||||
// util.hide('dialog');
|
||||
// })
|
||||
// $('.dialog').draggable({ handle: '.dialog-header', cursor: 'move' });
|
||||
// },
|
||||
|
||||
|
||||
// ======================================================
|
||||
@ -133,14 +206,14 @@ my.DataTable = Backbone.View.extend({
|
||||
|
||||
onColumnHeaderClick: function(e) {
|
||||
this.state.currentColumn = $(e.target).siblings().text();
|
||||
util.position('menu', e);
|
||||
util.render('columnActions', 'menu');
|
||||
util.position('data-table-menu', e);
|
||||
util.render('columnActions', 'data-table-menu');
|
||||
},
|
||||
|
||||
onRowHeaderClick: function(e) {
|
||||
this.state.currentRow = $(e.target).parents('tr:first').attr('data-id');
|
||||
util.position('menu', e);
|
||||
util.render('rowActions', 'menu');
|
||||
util.position('data-table-menu', e);
|
||||
util.render('rowActions', 'data-table-menu');
|
||||
},
|
||||
|
||||
onMenuClick: function(e) {
|
||||
@ -149,11 +222,13 @@ my.DataTable = Backbone.View.extend({
|
||||
var actions = {
|
||||
bulkEdit: function() { self.showTransformColumnDialog('bulkEdit', {name: self.state.currentColumn}) },
|
||||
transform: function() { self.showTransformDialog('transform') },
|
||||
// TODO: Delete or re-implement ...
|
||||
csv: function() { window.location.href = app.csvUrl },
|
||||
json: function() { window.location.href = "_rewrite/api/json" },
|
||||
urlImport: function() { showDialog('urlImport') },
|
||||
pasteImport: function() { showDialog('pasteImport') },
|
||||
uploadImport: function() { showDialog('uploadImport') },
|
||||
// END TODO
|
||||
deleteColumn: function() {
|
||||
var msg = "Are you sure? This will delete '" + self.state.currentColumn + "' from all documents.";
|
||||
// TODO:
|
||||
@ -176,7 +251,7 @@ my.DataTable = Backbone.View.extend({
|
||||
})
|
||||
}
|
||||
}
|
||||
util.hide('menu');
|
||||
util.hide('data-table-menu');
|
||||
actions[$(e.target).attr('data-action')]();
|
||||
},
|
||||
|
||||
@ -214,18 +289,20 @@ my.DataTable = Backbone.View.extend({
|
||||
// ======================================================
|
||||
// Core Templating
|
||||
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"> \
|
||||
<thead> \
|
||||
<tr> \
|
||||
{{#notEmpty}}<td class="column-header"></td>{{/notEmpty}} \
|
||||
{{#notEmpty}}<th class="column-header"></th>{{/notEmpty}} \
|
||||
{{#headers}} \
|
||||
<td class="column-header"> \
|
||||
<th class="column-header"> \
|
||||
<div class="column-header-title"> \
|
||||
<a class="column-header-menu"></a> \
|
||||
<span class="column-header-name">{{.}}</span> \
|
||||
</div> \
|
||||
</div> \
|
||||
</td> \
|
||||
</th> \
|
||||
{{/headers}} \
|
||||
</tr> \
|
||||
</thead> \
|
||||
@ -240,9 +317,7 @@ my.DataTable = Backbone.View.extend({
|
||||
},
|
||||
render: function() {
|
||||
var self = this;
|
||||
var template = $( ".dataTableTemplate:first" ).html()
|
||||
, htmls = $.mustache(template, this.toTemplateJSON())
|
||||
;
|
||||
var htmls = $.mustache(this.template, this.toTemplateJSON());
|
||||
this.el.html(htmls);
|
||||
this.model.currentDocuments.forEach(function(doc) {
|
||||
var tr = $('<tr />');
|
||||
@ -324,12 +399,15 @@ my.DataTableRow = Backbone.View.extend({
|
||||
var newData = {};
|
||||
newData[header] = newValue;
|
||||
this.model.set(newData);
|
||||
util.notify("Updating row...", {persist: true, loader: true});
|
||||
util.notify("Updating row...", {loader: true});
|
||||
this.model.save().then(function(response) {
|
||||
util.notify("Row updated successfully");
|
||||
util.notify("Row updated successfully", {category: 'success'});
|
||||
})
|
||||
.fail(function() {
|
||||
alert('error saving');
|
||||
util.notify('Error saving row', {
|
||||
category: 'error',
|
||||
persist: true
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
@ -392,8 +470,8 @@ my.ColumnTransform = Backbone.View.extend({
|
||||
</div> \
|
||||
</div> \
|
||||
<div class="dialog-footer"> \
|
||||
<button class="okButton button"> Update All </button> \
|
||||
<button class="cancelButton button">Cancel</button> \
|
||||
<button class="okButton btn primary"> Update All </button> \
|
||||
<button class="cancelButton btn danger">Cancel</button> \
|
||||
</div> \
|
||||
',
|
||||
|
||||
@ -462,7 +540,8 @@ my.ColumnTransform = Backbone.View.extend({
|
||||
var docs = self.model.currentDocuments.map(function(doc) {
|
||||
return doc.toJSON();
|
||||
});
|
||||
costco.previewTransform(docs, editFunc, self.state.currentColumn);
|
||||
var previewData = costco.previewTransform(docs, editFunc, self.state.currentColumn);
|
||||
util.render('editPreview', 'expression-preview-container', {rows: previewData});
|
||||
} else {
|
||||
errors.text(editFunc.errorMessage);
|
||||
}
|
||||
@ -538,93 +617,99 @@ my.DataTransform = Backbone.View.extend({
|
||||
});
|
||||
|
||||
|
||||
// Graph view for a Dataset using Flot graphing library.
|
||||
//
|
||||
// Initialization arguments:
|
||||
//
|
||||
// * model: recline.Model.Dataset
|
||||
// * config: (optional) graph configuration hash of form:
|
||||
//
|
||||
// {
|
||||
// group: {column name for x-axis},
|
||||
// series: [{column name for series A}, {column name series B}, ... ],
|
||||
// graphType: 'line'
|
||||
// }
|
||||
//
|
||||
// NB: should *not* provide an el argument to the view but must let the view
|
||||
// generate the element itself (you can then append view.el to the DOM.
|
||||
my.FlotGraph = Backbone.View.extend({
|
||||
|
||||
tagName: "div",
|
||||
className: "data-graph-container",
|
||||
|
||||
// TODO: normalize css
|
||||
template: ' \
|
||||
<div class="panel graph"></div> \
|
||||
<div class="editor"> \
|
||||
<div class="editor-info editor-hide-info"> \
|
||||
<h1><span></span>Help</h1> \
|
||||
<h3 class="action-toggle-help">Help »</h3> \
|
||||
<p>To create a chart select a column (group) to use as the x-axis \
|
||||
then another column (Series A) to plot against it.</p> \
|
||||
<p>You can add add \
|
||||
additional series by clicking the "Add series" button</p> \
|
||||
<p>Please note you must be logged in to save charts.</p> \
|
||||
</div> \
|
||||
<form> \
|
||||
<ul> \
|
||||
<li class="editor-type"> \
|
||||
<label>Graph Type</label> \
|
||||
<form class="form-stacked"> \
|
||||
<div class="clearfix"> \
|
||||
<label>Graph Type</label> \
|
||||
<div class="input editor-type"> \
|
||||
<select> \
|
||||
<option value="line">Line</option> \
|
||||
</select> \
|
||||
</li> \
|
||||
<li class="editor-group"> \
|
||||
<label>Group Column (x-axis)</label> \
|
||||
</div> \
|
||||
<label>Group Column (x-axis)</label> \
|
||||
<div class="input editor-group"> \
|
||||
<select> \
|
||||
{{#headers}} \
|
||||
<option value="{{.}}">{{.}}</option> \
|
||||
{{/headers}} \
|
||||
</select> \
|
||||
</li> \
|
||||
<li class="editor-series"> \
|
||||
<label>Series <span>A (y-axis)</span></label> \
|
||||
<select> \
|
||||
{{#headers}} \
|
||||
<option value="{{.}}">{{.}}</option> \
|
||||
{{/headers}} \
|
||||
</select> \
|
||||
</li> \
|
||||
</ul> \
|
||||
<div class="editor-buttons"> \
|
||||
<button class="editor-add">Add Series</button> \
|
||||
</div> \
|
||||
<div class="editor-series-group"> \
|
||||
<div class="editor-series"> \
|
||||
<label>Series <span>A (y-axis)</span></label> \
|
||||
<div class="input"> \
|
||||
<select> \
|
||||
{{#headers}} \
|
||||
<option value="{{.}}">{{.}}</option> \
|
||||
{{/headers}} \
|
||||
</select> \
|
||||
</div> \
|
||||
</div> \
|
||||
</div> \
|
||||
</div> \
|
||||
<div class="editor-buttons editor-submit"> \
|
||||
<div class="editor-buttons"> \
|
||||
<button class="btn editor-add">Add Series</button> \
|
||||
</div> \
|
||||
<div class="editor-buttons editor-submit" comment="hidden temporarily" style="display: none;"> \
|
||||
<button class="editor-save">Save</button> \
|
||||
<input type="hidden" class="editor-id" value="chart-1" /> \
|
||||
</div> \
|
||||
</form> \
|
||||
</div> \
|
||||
<div class="panel graph"></div> \
|
||||
</div> \
|
||||
',
|
||||
|
||||
initialize: function(options, chart) {
|
||||
events: {
|
||||
'change form select': 'onEditorSubmit'
|
||||
, 'click .editor-add': 'addSeries'
|
||||
, 'click .action-remove-series': 'removeSeries'
|
||||
, 'click .action-toggle-help': 'toggleHelp'
|
||||
},
|
||||
|
||||
initialize: function(options, config) {
|
||||
var self = this;
|
||||
this.el = $(this.el);
|
||||
_.bindAll(this, 'render');
|
||||
this.model.currentDocuments.bind('add', this.render);
|
||||
this.model.currentDocuments.bind('reset', this.render);
|
||||
this.chart = chart;
|
||||
this.chartConfig = {
|
||||
group: null,
|
||||
series: [],
|
||||
graphType: 'line'
|
||||
};
|
||||
},
|
||||
|
||||
events: {
|
||||
// 'change select': 'onEditorSubmit'
|
||||
},
|
||||
|
||||
onEditorSubmit: function(e) {
|
||||
var select = this.el.find('.editor-group select');
|
||||
this._getEditorData();
|
||||
this.plot.setData(this.createSeries());
|
||||
this.plot.resize();
|
||||
this.plot.setupGrid();
|
||||
this.plot.draw();
|
||||
},
|
||||
_getEditorData: function() {
|
||||
$editor = this
|
||||
var series = this.$series.map(function () {
|
||||
return $(this).val();
|
||||
});
|
||||
this.chartConfig.series = $.makeArray(series)
|
||||
this.chartConfig.group = this.el.find('.editor-group select').val();
|
||||
_.bindAll(this, 'render', 'redraw');
|
||||
// we need the model.headers to render properly
|
||||
this.model.bind('change', this.render);
|
||||
this.model.currentDocuments.bind('add', this.redraw);
|
||||
this.model.currentDocuments.bind('reset', this.redraw);
|
||||
this.chartConfig = _.extend({
|
||||
group: null,
|
||||
series: [],
|
||||
graphType: 'line'
|
||||
},
|
||||
config)
|
||||
this.render();
|
||||
},
|
||||
|
||||
toTemplateJSON: function() {
|
||||
@ -636,24 +721,56 @@ my.FlotGraph = Backbone.View.extend({
|
||||
$(this.el).html(htmls);
|
||||
// now set a load of stuff up
|
||||
this.$graph = this.el.find('.panel.graph');
|
||||
// event approach did not seem to work
|
||||
this.$series = this.el.find('.editor-series select');
|
||||
this.$seriesClone = this.$series.parent().clone();
|
||||
var self = this;
|
||||
this.el.find('form select').change(function() {
|
||||
self.onEditorSubmit.apply(self, arguments)
|
||||
});
|
||||
// for use later when adding additional series
|
||||
// could be simpler just to have a common template!
|
||||
this.$seriesClone = this.el.find('.editor-series').clone();
|
||||
this._updateSeries();
|
||||
return this;
|
||||
},
|
||||
|
||||
createPlot: function () {
|
||||
// only lines for the present
|
||||
options = {
|
||||
id: 'line',
|
||||
name: 'Line Chart'
|
||||
};
|
||||
this.plot = $.plot(this.$graph, this.createSeries(), options);
|
||||
return this;
|
||||
onEditorSubmit: function(e) {
|
||||
var select = this.el.find('.editor-group select');
|
||||
this._getEditorData();
|
||||
// update navigation
|
||||
// TODO: make this less invasive (e.g. preserve other keys in query string)
|
||||
window.location.hash = window.location.hash.split('?')[0] +
|
||||
'?graph=' + JSON.stringify(this.chartConfig);
|
||||
this.redraw();
|
||||
},
|
||||
|
||||
redraw: function() {
|
||||
// There appear to be issues generating a Flot graph if either:
|
||||
|
||||
// * The relevant div that graph attaches to his hidden at the moment of creating the plot -- Flot will complain with
|
||||
//
|
||||
// Uncaught Invalid dimensions for plot, width = 0, height = 0
|
||||
// * There is no data for the plot -- either same error or may have issues later with errors like 'non-existent node-value'
|
||||
var areWeVisible = !jQuery.expr.filters.hidden(this.el[0]);
|
||||
if (!this.plot && (!areWeVisible || this.model.currentDocuments.length == 0)) {
|
||||
return
|
||||
}
|
||||
// create this.plot and cache it
|
||||
if (!this.plot) {
|
||||
// only lines for the present
|
||||
options = {
|
||||
id: 'line',
|
||||
name: 'Line Chart'
|
||||
};
|
||||
this.plot = $.plot(this.$graph, this.createSeries(), options);
|
||||
}
|
||||
this.plot.setData(this.createSeries());
|
||||
this.plot.resize();
|
||||
this.plot.setupGrid();
|
||||
this.plot.draw();
|
||||
},
|
||||
|
||||
_getEditorData: function() {
|
||||
$editor = this
|
||||
var series = this.$series.map(function () {
|
||||
return $(this).val();
|
||||
});
|
||||
this.chartConfig.series = $.makeArray(series)
|
||||
this.chartConfig.group = this.el.find('.editor-group select').val();
|
||||
},
|
||||
|
||||
createSeries: function () {
|
||||
@ -676,19 +793,51 @@ my.FlotGraph = Backbone.View.extend({
|
||||
return series;
|
||||
},
|
||||
|
||||
// TODO: finish porting this function
|
||||
addSeries: function () {
|
||||
var element = this.seriesClone.clone(),
|
||||
// Public: Adds a new empty series select box to the editor.
|
||||
//
|
||||
// All but the first select box will have a remove button that allows them
|
||||
// to be removed.
|
||||
//
|
||||
// Returns itself.
|
||||
addSeries: function (e) {
|
||||
e.preventDefault();
|
||||
var element = this.$seriesClone.clone(),
|
||||
label = element.find('label'),
|
||||
index = this.series.length;
|
||||
|
||||
this.el.$series.parent().find('ul').append(element);
|
||||
this.updateSeries();
|
||||
|
||||
label.append('<a href="#remove">Remove</a>');
|
||||
label.find('span').text(String.fromCharCode(this.series.length + 64));
|
||||
index = this.$series.length;
|
||||
|
||||
this.el.find('.editor-series-group').append(element);
|
||||
this._updateSeries();
|
||||
label.append(' [<a href="#remove" class="action-remove-series">Remove</a>]');
|
||||
label.find('span').text(String.fromCharCode(this.$series.length + 64));
|
||||
return this;
|
||||
},
|
||||
|
||||
// Public: Removes a series list item from the editor.
|
||||
//
|
||||
// Also updates the labels of the remaining series elements.
|
||||
removeSeries: function (e) {
|
||||
e.preventDefault();
|
||||
var $el = $(e.target);
|
||||
$el.parent().parent().remove();
|
||||
this._updateSeries();
|
||||
this.$series.each(function (index) {
|
||||
if (index > 0) {
|
||||
var labelSpan = $(this).prev().find('span');
|
||||
labelSpan.text(String.fromCharCode(index + 65));
|
||||
}
|
||||
});
|
||||
this.onEditorSubmit();
|
||||
},
|
||||
|
||||
toggleHelp: function() {
|
||||
this.el.find('.editor-info').toggleClass('editor-hide-info');
|
||||
},
|
||||
|
||||
// Private: Resets the series property to reference the select elements.
|
||||
//
|
||||
// Returns itself.
|
||||
_updateSeries: function () {
|
||||
this.$series = this.el.find('.editor-series select');
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@ -31,12 +31,12 @@ test('new Dataset', function () {
|
||||
dataset.fetch().then(function(dataset) {
|
||||
equal(dataset.get('name'), metadata.name);
|
||||
deepEqual(dataset.get('headers'), indata.headers);
|
||||
equal(dataset.getLength(), 6);
|
||||
dataset.getRows(4, 2).then(function(documentList) {
|
||||
equal(dataset.docCount, 6);
|
||||
dataset.getDocuments(4, 2).then(function(documentList) {
|
||||
deepEqual(indata.rows[2], documentList.models[0].toJSON());
|
||||
});
|
||||
dataset.getRows().then(function(docList) {
|
||||
// Test getRows
|
||||
dataset.getDocuments().then(function(docList) {
|
||||
// Test getDocuments
|
||||
equal(docList.length, Math.min(10, indata.rows.length));
|
||||
var doc1 = docList.models[0];
|
||||
deepEqual(doc1.toJSON(), indata.rows[0]);
|
||||
@ -158,8 +158,8 @@ test('Webstore Backend', function() {
|
||||
|
||||
dataset.fetch().then(function(dataset) {
|
||||
deepEqual(['__id__', 'date', 'geometry', 'amount'], dataset.get('headers'));
|
||||
equal(3, dataset.rowCount)
|
||||
dataset.getRows().then(function(docList) {
|
||||
equal(3, dataset.docCount)
|
||||
dataset.getDocuments().then(function(docList) {
|
||||
equal(3, docList.length)
|
||||
equal("2009-01-01", docList.models[0].get('date'));
|
||||
});
|
||||
|
||||
113
vendor/bootstrap/1.4.0/bootstrap-alerts.js
vendored
Normal file
113
vendor/bootstrap/1.4.0/bootstrap-alerts.js
vendored
Normal file
@ -0,0 +1,113 @@
|
||||
/* ==========================================================
|
||||
* 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
Normal file
2467
vendor/bootstrap/1.4.0/bootstrap.css
vendored
Normal file
File diff suppressed because it is too large
Load Diff
Loading…
x
Reference in New Issue
Block a user