[recline]: Delete old files and code
This commit is contained in:
796
dist/recline.css
vendored
796
dist/recline.css
vendored
@@ -1,796 +0,0 @@
|
||||
.recline-flot .graph {
|
||||
height: 500px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.recline-flot .legend table {
|
||||
width: auto;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.recline-flot .legend td {
|
||||
padding: 5px;
|
||||
line-height: 13px;
|
||||
}
|
||||
|
||||
.recline-flot .graph .alert {
|
||||
width: 450px;
|
||||
}
|
||||
|
||||
#recline-flot-tooltip {
|
||||
position: absolute;
|
||||
background-color: #FEE;
|
||||
color: #000000;
|
||||
opacity: 0.8;
|
||||
border: 1px solid #fdd;
|
||||
}
|
||||
/**********************************************************
|
||||
* (Data) Grid
|
||||
*********************************************************/
|
||||
|
||||
table.recline-grid {
|
||||
table-layout: fixed;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.recline-grid .btn-group .dropdown-toggle {
|
||||
padding: 1px 3px;
|
||||
line-height: auto;
|
||||
}
|
||||
|
||||
.recline-grid td, .recline-grid th {
|
||||
border-left: 1px solid #ccc;
|
||||
padding: 3px 4px;
|
||||
text-align: left;
|
||||
word-wrap: break-word;
|
||||
white-space: normal;
|
||||
}
|
||||
|
||||
.recline-grid tbody tr {
|
||||
vertical-align: top;
|
||||
border-bottom: solid 1px #ccc;
|
||||
}
|
||||
|
||||
.recline-grid tbody tr:last-child {
|
||||
border-bottom: 1px solid #ccc;
|
||||
}
|
||||
|
||||
.recline-grid tbody td:last-child {
|
||||
border-right: 1px solid #ccc;
|
||||
}
|
||||
|
||||
/* direct borrowing from twitter buttons */
|
||||
.recline-grid th {
|
||||
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-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;
|
||||
}
|
||||
|
||||
/**********************************************************
|
||||
* Fixed Header - http://www.imaputz.com/cssStuff/bigFourVersion.html
|
||||
*********************************************************/
|
||||
|
||||
div.table-container {
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
/* Reset overflow value to hidden for all non-IE browsers. */
|
||||
html>body div.table-container {
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
thead.fixed-header tr {
|
||||
overflow-x: hidden;
|
||||
}
|
||||
|
||||
/* set table header to a fixed position. WinIE 6.x only */
|
||||
/* In WinIE 6.x, any element with a position property set to relative and is a child of */
|
||||
/* an element that has an overflow property set, the relative value translates into fixed. */
|
||||
/* Ex: parent element DIV with a class of table-container has an overflow property set to auto */
|
||||
thead.fixed-header tr {
|
||||
position: relative
|
||||
}
|
||||
|
||||
/* set THEAD element to have block level attributes. All other non-IE browsers */
|
||||
/* this enables overflow to work on TBODY element. All other non-IE, non-Mozilla browsers */
|
||||
html>body thead.fixed-header tr {
|
||||
display: block
|
||||
}
|
||||
|
||||
/* define the table content to be scrollable */
|
||||
/* set TBODY element to have block level attributes. All other non-IE browsers */
|
||||
/* this enables overflow to work on TBODY element. All other non-IE, non-Mozilla browsers */
|
||||
/* induced side effect is that child TDs no longer accept width: auto */
|
||||
tbody.scroll-content {
|
||||
display: block;
|
||||
max-height: 500px;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
/**********************************************************
|
||||
* Data Table Menus
|
||||
*********************************************************/
|
||||
|
||||
.column-header-menu, a.root-header-menu {
|
||||
float: right;
|
||||
}
|
||||
|
||||
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(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADIAAAAQCAYAAABUWyyMAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyJpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMC1jMDYwIDYxLjEzNDc3NywgMjAxMC8wMi8xMi0xNzozMjowMCAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNSBNYWNpbnRvc2giIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6OTNEODNFMjVEMDA5MTFERjk5NzhEQzZDRDUwRkEzMUEiIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6OTNEODNFMjZEMDA5MTFERjk5NzhEQzZDRDUwRkEzMUEiPiA8eG1wTU06RGVyaXZlZEZyb20gc3RSZWY6aW5zdGFuY2VJRD0ieG1wLmlpZDo5M0Q4M0UyM0QwMDkxMURGOTk3OERDNkNENTBGQTMxQSIgc3RSZWY6ZG9jdW1lbnRJRD0ieG1wLmRpZDo5M0Q4M0UyNEQwMDkxMURGOTk3OERDNkNENTBGQTMxQSIvPiA8L3JkZjpEZXNjcmlwdGlvbj4gPC9yZGY6UkRGPiA8L3g6eG1wbWV0YT4gPD94cGFja2V0IGVuZD0iciI/PpApVmcAAAKVSURBVHjaYvz//z/DcAAsIGLaxjf2QKoLiM2oaPYpIC7L8hc5COI4F9+hmR17e1UOskAFVgCxBJUDyQxqriQ97GCCCpBkATCUGQxVOTHY7KyMDFoKHMhKJXCwCQJgKDNEOAlisHk5mRh8LPgw7GChNEg2HfvI8OnbPzDbwYAH6BkmhmsPflA12EtnPmN4/vY3mF0YKgb2zJYTn5CV/EfxiKgAC4O7CS8DHzczw5PXvxl2nv7E8PP3fxTx87e/o1hipMrFcOfZT6BmVgYZUTawmKUWN8Pxa1+xOkpNhp2hLk6CQVKYleHcrW8MTYteMHz+/g9FfMW+9yh6Ih0FGA5e/MLAAIx5IzUusFiqtzDD7K1vYUpcmZA1+FnxM7z++AccyqBkAgphmMPY2ZiAHvsMFkcGMkAPgMQ+ff3L8PPXPzD++O0vztDtypBmuPXkJziUebiYwSEMcxiID/IYiEYJLKDjQWLP3v1h+AI0G4RhMQQF5UzoDvr5CxIDoJhQlmKHy527/Y3hLjDkD4BCBgsAxSAoiYEwrqQFchAoWcBiAOQge30euBwoJkAh37/6FVb9oBgEeQCE0ZKWC8488vj1LzCmJTgLTKZn0ZIquYAJOURBsQDKB68//GHgYEWkOlBMgPICKIYcoCGIC4BiFRSDuEIUFBtSQizg5AWKHRgAxQSoZALFECy54QKgZAbLKxgegZVAfFxM4LyiLMUGTPeQ0ujAhS9gz7mb8sI9iisWQclSG7UIRgFlM56CM3R3uhTY0bC0DkpOtx//AGd4mEdxxSIoWaIVwQyMoCYKsGanWTsFWM8wQmt2mtkBqmdgMfKCRna8wMGmth2MMI/EAfEdKltwB2ouAz3sYBwurV8mhmECAAIMAEe3EkMWh/DvAAAAAElFTkSuQmCC);
|
||||
background-repeat: no-repeat;
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
a.data-table-cell-edit:hover {
|
||||
background-position: -25px 0px;
|
||||
}
|
||||
|
||||
.recline-grid 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-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;
|
||||
}
|
||||
|
||||
/**********************************************************
|
||||
* Read-only mode
|
||||
*********************************************************/
|
||||
|
||||
.recline-read-only .recline-grid .write-op,
|
||||
.recline-read-only .recline-grid a.data-table-cell-edit
|
||||
{
|
||||
display: none;
|
||||
}
|
||||
|
||||
.recline-read-only a.row-header-menu {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/*************************
|
||||
* WCAG 2.0
|
||||
*************************/
|
||||
|
||||
.wcag_hide {
|
||||
position:absolute;
|
||||
top:0;
|
||||
left:-10000px;
|
||||
width:1px;
|
||||
height:1px;
|
||||
}
|
||||
|
||||
.wcag_hide2 {
|
||||
clip: rect(1px, 1px, 1px, 1px);
|
||||
display: block;
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
.wcag_show_on_focus {
|
||||
font-size: 0 !important;
|
||||
}
|
||||
|
||||
.wcag_show_on_focus:focus {
|
||||
font-size: 1em !important;
|
||||
}
|
||||
.recline-map .map {
|
||||
height: 500px;
|
||||
}
|
||||
|
||||
/**********************************************************
|
||||
* Editor
|
||||
*********************************************************/
|
||||
|
||||
.recline-map .editor {
|
||||
float: right;
|
||||
width: 200px;
|
||||
padding-left: 0px;
|
||||
margin-left: 10px;
|
||||
}
|
||||
|
||||
.recline-map .editor form {
|
||||
padding-left: 4px;
|
||||
}
|
||||
|
||||
.recline-map .editor select {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.recline-map .editor .editor-options {
|
||||
margin-top: 10px;
|
||||
border-top: 1px solid gray;
|
||||
padding: 5px 0;
|
||||
}
|
||||
.recline-data-explorer .data-view-container {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.recline-data-explorer .data-view-sidebar {
|
||||
float: right;
|
||||
margin-left: 8px;
|
||||
width: 220px;
|
||||
}
|
||||
|
||||
.recline-data-explorer .header .navigation {
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.recline-data-explorer .header .navigation,
|
||||
.recline-data-explorer .header .pagination,
|
||||
.recline-data-explorer .header .pagination form
|
||||
{
|
||||
display: inline;
|
||||
}
|
||||
|
||||
.recline-data-explorer .header .navigation {
|
||||
float: left;
|
||||
}
|
||||
|
||||
.recline-data-explorer .header .menu-right {
|
||||
float: right;
|
||||
margin-left: 5px;
|
||||
padding-left: 5px;
|
||||
}
|
||||
|
||||
.recline-results-info {
|
||||
line-height: 35px;
|
||||
margin-left: 20px;
|
||||
float: left;
|
||||
}
|
||||
|
||||
.recline-data-explorer .data-view-sidebar > div {
|
||||
margin-top: 5px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.recline-data-explorer .radio,
|
||||
.recline-data-explorer .checkbox {
|
||||
padding-left: 20px;
|
||||
}
|
||||
|
||||
.recline-data-explorer .editor-update-map {
|
||||
margin: 30px 0px 20px 0px;
|
||||
}
|
||||
|
||||
.recline-data-explorer label {
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
/**********************************************************
|
||||
* Query Editor
|
||||
*********************************************************/
|
||||
|
||||
.recline-query-editor {
|
||||
float: right;
|
||||
height: 35px;
|
||||
padding-right: 5px;
|
||||
margin-right: 5px;
|
||||
border-right: solid 2px #ddd;
|
||||
}
|
||||
|
||||
.header .input-prepend {
|
||||
margin-bottom: auto;
|
||||
}
|
||||
|
||||
.header .add-on {
|
||||
float: left;
|
||||
}
|
||||
|
||||
/* needed for Chrome but not FF */
|
||||
.header .add-on {
|
||||
margin-left: -27px;
|
||||
}
|
||||
|
||||
/* needed for FF but not chrome */
|
||||
.header .input-prepend {
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
.recline-query-editor form button {
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
/* label for screen reader */
|
||||
.recline-query-editor .form-inline label {
|
||||
position: absolute;
|
||||
top:0;
|
||||
left:-9999px
|
||||
}
|
||||
|
||||
/**********************************************************
|
||||
* Pager
|
||||
*********************************************************/
|
||||
|
||||
.recline-pager {
|
||||
float: left;
|
||||
margin: auto;
|
||||
display: block;
|
||||
margin-left: 20px;
|
||||
}
|
||||
|
||||
.recline-pager .pagination li {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.recline-pager .pagination label {
|
||||
display:none;
|
||||
}
|
||||
|
||||
.recline-pager .pagination input {
|
||||
width: 40px;
|
||||
height: 25px;
|
||||
padding: 2px 4px;
|
||||
margin: 0;
|
||||
margin-top: -2px;
|
||||
|
||||
border: 1px solid #cccccc;
|
||||
box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
|
||||
transition: border linear 0.2s, box-shadow linear 0.2s;
|
||||
border-radius: 4px;
|
||||
|
||||
-webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
|
||||
-webkit-transition: border linear 0.2s, box-shadow linear 0.2s;
|
||||
-webkit-border-radius: 4px;
|
||||
|
||||
-moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
|
||||
-moz-transition: border linear 0.2s, box-shadow linear 0.2s;
|
||||
-moz-border-radius: 4px;
|
||||
|
||||
-o-transition: border linear 0.2s, box-shadow linear 0.2s;
|
||||
}
|
||||
|
||||
.recline-pager .pagination a {
|
||||
float: none;
|
||||
margin-left: -5px;
|
||||
color: #555;
|
||||
}
|
||||
|
||||
.recline-pager .pagination .page-range {
|
||||
height: 34px;
|
||||
padding: 5px 8px;
|
||||
margin-left: -5px;
|
||||
border: 1px solid #ddd;
|
||||
}
|
||||
|
||||
.recline-pager .pagination .page-range a {
|
||||
padding: 0px 12px;
|
||||
border: none;
|
||||
}
|
||||
|
||||
.recline-pager .pagination .page-range a:hover {
|
||||
background-color: #ffffff;
|
||||
}
|
||||
|
||||
.recline-pager .pagination > li:first-child > a {
|
||||
border-bottom-left-radius: 4px;
|
||||
border-top-left-radius: 4px;
|
||||
border-bottom-right-radius: 0px;
|
||||
border-top-right-radius: 0px;
|
||||
height: 34px;
|
||||
}
|
||||
|
||||
.recline-pager .pagination > li:last-child > a {
|
||||
border-bottom-right-radius: 4px;
|
||||
border-top-right-radius: 4px;
|
||||
border-bottom-left-radius: 0px;
|
||||
border-top-left-radius: 0px;
|
||||
height: 34px;
|
||||
}
|
||||
|
||||
/**********************************************************
|
||||
* Filter Editor
|
||||
*********************************************************/
|
||||
|
||||
.recline-filter-editor {
|
||||
padding: 8px;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.recline-filter-editor .filters {
|
||||
margin: 20px 0px;
|
||||
}
|
||||
|
||||
.recline-filter-editor h3 {
|
||||
margin-top: 4px;
|
||||
}
|
||||
|
||||
.recline-filter-editor .filter {
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
.recline-filter-editor .filter .form-group {
|
||||
margin-bottom: 0px;
|
||||
}
|
||||
|
||||
.recline-filter-editor .filter input,
|
||||
.recline-filter-editor .filter label {
|
||||
margin: 0px;
|
||||
}
|
||||
|
||||
.recline-filter-editor .js-edit button {
|
||||
margin: 25px 0px 0px 0px;
|
||||
}
|
||||
|
||||
.recline-filter-editor .filter-term a {
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
.recline-filter-editor input,
|
||||
.recline-filter-editor select
|
||||
{
|
||||
width: 175px;
|
||||
}
|
||||
|
||||
.recline-filter-editor input {
|
||||
margin-top: 0.5em;
|
||||
margin-bottom: 10px;
|
||||
-webkit-border-radius: 4px;
|
||||
-moz-border-radius: 4px;
|
||||
border-radius: 4px;
|
||||
border: 1px solid #cccccc;
|
||||
}
|
||||
|
||||
.recline-filter-editor label {
|
||||
font-weight: normal;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.recline-filter-editor legend {
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
.recline-filter-editor .add-filter {
|
||||
margin-top: 1em;
|
||||
margin-bottom: 2em;
|
||||
}
|
||||
|
||||
.recline-filter-editor .update-filter {
|
||||
margin-top: 1em;
|
||||
}
|
||||
|
||||
/**********************************************************
|
||||
* Fields Widget
|
||||
*********************************************************/
|
||||
|
||||
.recline-fields-view {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.recline-fields-view .fields-list {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.recline-fields-view .panel {
|
||||
background-color: #f5f5f5;
|
||||
border: 1px solid #e5e5e5;
|
||||
}
|
||||
|
||||
.recline-fields-view .panel-group h3 {
|
||||
padding-left: 10px;
|
||||
}
|
||||
|
||||
.recline-fields-view .fields-list .panel-heading {
|
||||
padding: 2px 5px;
|
||||
margin: 1px 0px 1px 5px;
|
||||
}
|
||||
|
||||
.recline-fields-view .panel a,
|
||||
.recline-fields-view .panel h4 {
|
||||
display: inline;
|
||||
}
|
||||
|
||||
.recline-fields-view .panel a {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.recline-fields-view .panel h4 {
|
||||
word-wrap: break-word
|
||||
}
|
||||
|
||||
.recline-fields-view .clear {
|
||||
clear: both;
|
||||
}
|
||||
|
||||
.recline-fields-view .facet-items {
|
||||
list-style-type: none;
|
||||
margin-left: 0;
|
||||
}
|
||||
|
||||
.recline-fields-view .facet-item .term {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.recline-fields-view .facet-item .count {
|
||||
}
|
||||
|
||||
/**********************************************************
|
||||
* Notifications
|
||||
*********************************************************/
|
||||
|
||||
.recline-data-explorer .notification-loader {
|
||||
width: 18px;
|
||||
margin-left: 5px;
|
||||
background-image: url(data:image/gif;base64,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%3D%3D);
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.recline-data-explorer .alert-loader {
|
||||
position: absolute;
|
||||
width: 200px;
|
||||
left: 50%;
|
||||
margin-left: -100px;
|
||||
z-index: 10000;
|
||||
padding: 40px 0px 40px 0px;
|
||||
margin-top: -10px;
|
||||
text-align: center;
|
||||
font-size: 16px;
|
||||
font-weight: bold;
|
||||
-webkit-border-radius: 0px;
|
||||
-moz-border-radius: 0px;
|
||||
border-radius: 0px;
|
||||
border-top: none;
|
||||
}
|
||||
|
||||
/*
|
||||
IMPORTANT:
|
||||
In order to preserve the uniform grid appearance, all cell styles need to have padding, margin and border sizes.
|
||||
No built-in (selected, editable, highlight, flashing, invalid, loading, :focus) or user-specified CSS
|
||||
classes should alter those!
|
||||
*/
|
||||
|
||||
.recline-slickgrid .slick-header-columns .slick-header-column {
|
||||
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;
|
||||
font-weight: bold;
|
||||
border-right: 1px solid #ccc;
|
||||
border-top: 1px solid #ccc;
|
||||
border-bottom: 1px solid #bbb;
|
||||
-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);
|
||||
}
|
||||
|
||||
.recline-slickgrid .slick-header-column:hover, .slick-header-column-active {
|
||||
}
|
||||
|
||||
.recline-slickgrid .slick-header-column.ui-state-default {
|
||||
height: 26px;
|
||||
}
|
||||
|
||||
.recline-slickgrid .slick-headerrow {
|
||||
background: #fafafa;
|
||||
}
|
||||
|
||||
.recline-slickgrid .slick-headerrow-column {
|
||||
background: #fafafa;
|
||||
border-bottom: 0;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.recline-slickgrid .slick-row.ui-state-active {
|
||||
background: #F5F7D7;
|
||||
}
|
||||
|
||||
.recline-slickgrid .slick-row {
|
||||
position: absolute;
|
||||
background: white;
|
||||
border: 0px;
|
||||
line-height: 20px;
|
||||
}
|
||||
|
||||
.recline-slickgrid .slick-row.selected {
|
||||
z-index: 10;
|
||||
background: #DFE8F6;
|
||||
}
|
||||
|
||||
.recline-slickgrid .slick-cell {
|
||||
padding-left: 4px;
|
||||
padding-right: 4px;
|
||||
}
|
||||
|
||||
.recline-slickgrid .slick-group {
|
||||
border-bottom: 2px solid silver;
|
||||
}
|
||||
|
||||
.recline-slickgrid .slick-group-toggle {
|
||||
width: 9px;
|
||||
height: 9px;
|
||||
margin-right: 5px;
|
||||
}
|
||||
|
||||
.recline-slickgrid .slick-group-toggle.expanded {
|
||||
background: url(../images/collapse.gif) no-repeat center center;
|
||||
}
|
||||
|
||||
.recline-slickgrid .slick-group-toggle.collapsed {
|
||||
background: url(../images/expand.gif) no-repeat center center;
|
||||
}
|
||||
|
||||
.recline-slickgrid .slick-group-totals {
|
||||
color: gray;
|
||||
background: white;
|
||||
}
|
||||
|
||||
.recline-slickgrid .slick-cell.selected {
|
||||
background-color: beige;
|
||||
}
|
||||
|
||||
.recline-slickgrid .slick-cell.active {
|
||||
border-color: gray;
|
||||
border-style: solid;
|
||||
}
|
||||
|
||||
.recline-slickgrid .slick-sortable-placeholder {
|
||||
background: silver !important;
|
||||
}
|
||||
|
||||
.recline-slickgrid .slick-row[row$="1"], .slick-row[row$="3"], .slick-row[row$="5"], .slick-row[row$="7"], .slick-row[row$="9"] {
|
||||
background: #fafafa;
|
||||
}
|
||||
|
||||
.recline-slickgrid .slick-row.ui-state-active {
|
||||
background: #F5F7D7;
|
||||
}
|
||||
|
||||
.recline-slickgrid .slick-row.loading {
|
||||
opacity: 0.5;
|
||||
filter: alpha(opacity = 50);
|
||||
}
|
||||
|
||||
.recline-slickgrid .slick-cell.invalid {
|
||||
border-color: red;
|
||||
}
|
||||
|
||||
.recline-slickgrid .slick-row .slick-cell:first-child,
|
||||
.recline-slickgrid .slick-header {
|
||||
border-left: 1px solid #ccc;
|
||||
}
|
||||
|
||||
/* add one pixel extra as added one pixel to left border of header */
|
||||
.recline-slickgrid .slick-row .slick-cell {
|
||||
margin-right: -1px;
|
||||
}
|
||||
|
||||
/* Slick grid context menu (not part of the recline-slickgrid div) */
|
||||
.slick-contextmenu {
|
||||
border-radius: 5px
|
||||
}
|
||||
|
||||
.slick-contextmenu li {
|
||||
clear: both;
|
||||
height: 24px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.slick-contextmenu .divider {
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
.slick-contextmenu > li:hover {
|
||||
background-color: #0088cc;
|
||||
}
|
||||
|
||||
.slick-contextmenu .divider:hover {
|
||||
background-color: #E5E5E5;
|
||||
}
|
||||
|
||||
.slick-contextmenu li:hover > label {
|
||||
color: white;
|
||||
}
|
||||
|
||||
.slick-contextmenu input {
|
||||
float: left;
|
||||
margin-left: 15px;
|
||||
margin-top: 5px;
|
||||
}
|
||||
|
||||
.slick-contextmenu label {
|
||||
float: left;
|
||||
margin-right: 15px;
|
||||
margin-left: 5px;
|
||||
margin-top: 3px;
|
||||
color: #555;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.recline-slickgrid .recline-row-delete {
|
||||
font-size: 12px;
|
||||
padding: 3px;
|
||||
width: 29px;
|
||||
height: 18px;
|
||||
line-height: 13px;
|
||||
}
|
||||
|
||||
.recline-cell-reorder {
|
||||
font-size: 12px;
|
||||
padding: 1px;
|
||||
width: 31px;
|
||||
height: 14px;
|
||||
line-height: 13px;
|
||||
cursor: move;
|
||||
background: url("../images/drag-handle.png") no-repeat center center;
|
||||
}
|
||||
.recline-timeline {
|
||||
position: relative;
|
||||
}
|
||||
896
dist/recline.dataset.js
vendored
896
dist/recline.dataset.js
vendored
@@ -1,896 +0,0 @@
|
||||
// # Recline Backbone Models
|
||||
this.recline = this.recline || {};
|
||||
this.recline.Model = this.recline.Model || {};
|
||||
|
||||
(function(my) {
|
||||
"use strict";
|
||||
|
||||
// use either jQuery or Underscore Deferred depending on what is available
|
||||
var Deferred = (typeof jQuery !== "undefined" && jQuery.Deferred) || _.Deferred;
|
||||
|
||||
// ## <a id="dataset">Dataset</a>
|
||||
my.Dataset = Backbone.Model.extend({
|
||||
constructor: function Dataset() {
|
||||
Backbone.Model.prototype.constructor.apply(this, arguments);
|
||||
},
|
||||
|
||||
// ### initialize
|
||||
initialize: function() {
|
||||
var self = this;
|
||||
_.bindAll(this, 'query');
|
||||
this.backend = null;
|
||||
if (this.get('backend')) {
|
||||
this.backend = this._backendFromString(this.get('backend'));
|
||||
} else { // try to guess backend ...
|
||||
if (this.get('records')) {
|
||||
this.backend = recline.Backend.Memory;
|
||||
}
|
||||
}
|
||||
this.fields = new my.FieldList();
|
||||
this.records = new my.RecordList();
|
||||
this._changes = {
|
||||
deletes: [],
|
||||
updates: [],
|
||||
creates: []
|
||||
};
|
||||
this.facets = new my.FacetList();
|
||||
this.recordCount = null;
|
||||
this.queryState = new my.Query();
|
||||
this.queryState.bind('change facet:add', function () {
|
||||
self.query(); // We want to call query() without any arguments.
|
||||
});
|
||||
// store is what we query and save against
|
||||
// store will either be the backend or be a memory store if Backend fetch
|
||||
// tells us to use memory store
|
||||
this._store = this.backend;
|
||||
|
||||
// if backend has a handleQueryResultFunction, use that
|
||||
this._handleResult = (this.backend != null && _.has(this.backend, 'handleQueryResult')) ?
|
||||
this.backend.handleQueryResult : this._handleQueryResult;
|
||||
if (this.backend == recline.Backend.Memory) {
|
||||
this.fetch();
|
||||
}
|
||||
},
|
||||
|
||||
sync: function(method, model, options) {
|
||||
return this.backend.sync(method, model, options);
|
||||
},
|
||||
|
||||
// ### fetch
|
||||
//
|
||||
// Retrieve dataset and (some) records from the backend.
|
||||
fetch: function() {
|
||||
var self = this;
|
||||
var dfd = new Deferred();
|
||||
|
||||
if (this.backend !== recline.Backend.Memory) {
|
||||
this.backend.fetch(this.toJSON())
|
||||
.done(handleResults)
|
||||
.fail(function(args) {
|
||||
dfd.reject(args);
|
||||
});
|
||||
} else {
|
||||
// special case where we have been given data directly
|
||||
handleResults({
|
||||
records: this.get('records'),
|
||||
fields: this.get('fields'),
|
||||
useMemoryStore: true
|
||||
});
|
||||
}
|
||||
|
||||
function handleResults(results) {
|
||||
// if explicitly given the fields
|
||||
// (e.g. var dataset = new Dataset({fields: fields, ...})
|
||||
// use that field info over anything we get back by parsing the data
|
||||
// (results.fields)
|
||||
var fields = self.get('fields') || results.fields;
|
||||
|
||||
var out = self._normalizeRecordsAndFields(results.records, fields);
|
||||
if (results.useMemoryStore) {
|
||||
self._store = new recline.Backend.Memory.Store(out.records, out.fields);
|
||||
}
|
||||
|
||||
self.set(results.metadata);
|
||||
self.fields.reset(out.fields);
|
||||
self.query()
|
||||
.done(function() {
|
||||
dfd.resolve(self);
|
||||
})
|
||||
.fail(function(args) {
|
||||
dfd.reject(args);
|
||||
});
|
||||
}
|
||||
|
||||
return dfd.promise();
|
||||
},
|
||||
|
||||
// ### _normalizeRecordsAndFields
|
||||
//
|
||||
// Get a proper set of fields and records from incoming set of fields and records either of which may be null or arrays or objects
|
||||
//
|
||||
// e.g. fields = ['a', 'b', 'c'] and records = [ [1,2,3] ] =>
|
||||
// fields = [ {id: a}, {id: b}, {id: c}], records = [ {a: 1}, {b: 2}, {c: 3}]
|
||||
_normalizeRecordsAndFields: function(records, fields) {
|
||||
// if no fields get them from records
|
||||
if (!fields && records && records.length > 0) {
|
||||
// records is array then fields is first row of records ...
|
||||
if (records[0] instanceof Array) {
|
||||
fields = records[0];
|
||||
records = records.slice(1);
|
||||
} else {
|
||||
fields = _.map(_.keys(records[0]), function(key) {
|
||||
return {id: key};
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// fields is an array of strings (i.e. list of field headings/ids)
|
||||
if (fields && fields.length > 0 && (fields[0] === null || typeof(fields[0]) != 'object')) {
|
||||
// Rename duplicate fieldIds as each field name needs to be
|
||||
// unique.
|
||||
var seen = {};
|
||||
fields = _.map(fields, function(field, index) {
|
||||
if (field === null) {
|
||||
field = '';
|
||||
} else {
|
||||
field = field.toString();
|
||||
}
|
||||
// cannot use trim as not supported by IE7
|
||||
var fieldId = field.replace(/^\s+|\s+$/g, '');
|
||||
if (fieldId === '') {
|
||||
fieldId = '_noname_';
|
||||
field = fieldId;
|
||||
}
|
||||
while (fieldId in seen) {
|
||||
seen[field] += 1;
|
||||
fieldId = field + seen[field];
|
||||
}
|
||||
if (!(field in seen)) {
|
||||
seen[field] = 0;
|
||||
}
|
||||
// TODO: decide whether to keep original name as label ...
|
||||
// return { id: fieldId, label: field || fieldId }
|
||||
return { id: fieldId };
|
||||
});
|
||||
}
|
||||
// records is provided as arrays so need to zip together with fields
|
||||
// NB: this requires you to have fields to match arrays
|
||||
if (records && records.length > 0 && records[0] instanceof Array) {
|
||||
records = _.map(records, function(doc) {
|
||||
var tmp = {};
|
||||
_.each(fields, function(field, idx) {
|
||||
tmp[field.id] = doc[idx];
|
||||
});
|
||||
return tmp;
|
||||
});
|
||||
}
|
||||
return {
|
||||
fields: fields,
|
||||
records: records
|
||||
};
|
||||
},
|
||||
|
||||
save: function() {
|
||||
var self = this;
|
||||
// TODO: need to reset the changes ...
|
||||
return this._store.save(this._changes, this.toJSON());
|
||||
},
|
||||
|
||||
// ### query
|
||||
//
|
||||
// AJAX method with promise API to get records from the backend.
|
||||
//
|
||||
// It will query based on current query state (given by this.queryState)
|
||||
// updated by queryObj (if provided).
|
||||
//
|
||||
// Resulting RecordList are used to reset this.records and are
|
||||
// also returned.
|
||||
query: function(queryObj) {
|
||||
var self = this;
|
||||
var dfd = new Deferred();
|
||||
this.trigger('query:start');
|
||||
|
||||
if (queryObj) {
|
||||
var attributes = queryObj;
|
||||
if (queryObj instanceof my.Query) {
|
||||
attributes = queryObj.toJSON();
|
||||
}
|
||||
this.queryState.set(attributes, {silent: true});
|
||||
}
|
||||
var actualQuery = this.queryState.toJSON();
|
||||
|
||||
this._store.query(actualQuery, this.toJSON())
|
||||
.done(function(queryResult) {
|
||||
self._handleResult(queryResult);
|
||||
self.trigger('query:done');
|
||||
dfd.resolve(self.records);
|
||||
})
|
||||
.fail(function(args) {
|
||||
self.trigger('query:fail', args);
|
||||
dfd.reject(args);
|
||||
});
|
||||
return dfd.promise();
|
||||
},
|
||||
|
||||
_handleQueryResult: function(queryResult) {
|
||||
var self = this;
|
||||
self.recordCount = queryResult.total;
|
||||
var docs = _.map(queryResult.hits, function(hit) {
|
||||
var _doc = new my.Record(hit);
|
||||
_doc.fields = self.fields;
|
||||
_doc.bind('change', function(doc) {
|
||||
self._changes.updates.push(doc.toJSON());
|
||||
});
|
||||
_doc.bind('destroy', function(doc) {
|
||||
self._changes.deletes.push(doc.toJSON());
|
||||
});
|
||||
return _doc;
|
||||
});
|
||||
self.records.reset(docs);
|
||||
if (queryResult.facets) {
|
||||
var facets = _.map(queryResult.facets, function(facetResult, facetId) {
|
||||
facetResult.id = facetId;
|
||||
return new my.Facet(facetResult);
|
||||
});
|
||||
self.facets.reset(facets);
|
||||
}
|
||||
},
|
||||
|
||||
toTemplateJSON: function() {
|
||||
var data = this.toJSON();
|
||||
data.recordCount = this.recordCount;
|
||||
data.fields = this.fields.toJSON();
|
||||
return data;
|
||||
},
|
||||
|
||||
// ### getFieldsSummary
|
||||
//
|
||||
// Get a summary for each field in the form of a `Facet`.
|
||||
//
|
||||
// @return null as this is async function. Provides deferred/promise interface.
|
||||
getFieldsSummary: function() {
|
||||
var self = this;
|
||||
var query = new my.Query();
|
||||
query.set({size: 0});
|
||||
this.fields.each(function(field) {
|
||||
query.addFacet(field.id);
|
||||
});
|
||||
var dfd = new Deferred();
|
||||
this._store.query(query.toJSON(), this.toJSON()).done(function(queryResult) {
|
||||
if (queryResult.facets) {
|
||||
_.each(queryResult.facets, function(facetResult, facetId) {
|
||||
facetResult.id = facetId;
|
||||
var facet = new my.Facet(facetResult);
|
||||
// TODO: probably want replace rather than reset (i.e. just replace the facet with this id)
|
||||
self.fields.get(facetId).facets.reset(facet);
|
||||
});
|
||||
}
|
||||
dfd.resolve(queryResult);
|
||||
});
|
||||
return dfd.promise();
|
||||
},
|
||||
|
||||
// Deprecated (as of v0.5) - use record.summary()
|
||||
recordSummary: function(record) {
|
||||
return record.summary();
|
||||
},
|
||||
|
||||
// ### _backendFromString(backendString)
|
||||
//
|
||||
// Look up a backend module from a backend string (look in recline.Backend)
|
||||
_backendFromString: function(backendString) {
|
||||
var backend = null;
|
||||
if (recline && recline.Backend) {
|
||||
_.each(_.keys(recline.Backend), function(name) {
|
||||
if (name.toLowerCase() === backendString.toLowerCase()) {
|
||||
backend = recline.Backend[name];
|
||||
}
|
||||
});
|
||||
}
|
||||
return backend;
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
// ## <a id="record">A Record</a>
|
||||
//
|
||||
// A single record (or row) in the dataset
|
||||
my.Record = Backbone.Model.extend({
|
||||
constructor: function Record() {
|
||||
Backbone.Model.prototype.constructor.apply(this, arguments);
|
||||
},
|
||||
|
||||
// ### initialize
|
||||
//
|
||||
// Create a Record
|
||||
//
|
||||
// You usually will not do this directly but will have records created by
|
||||
// Dataset e.g. in query method
|
||||
//
|
||||
// Certain methods require presence of a fields attribute (identical to that on Dataset)
|
||||
initialize: function() {
|
||||
_.bindAll(this, 'getFieldValue');
|
||||
},
|
||||
|
||||
// ### getFieldValue
|
||||
//
|
||||
// For the provided Field get the corresponding rendered computed data value
|
||||
// for this record.
|
||||
//
|
||||
// NB: if field is undefined a default '' value will be returned
|
||||
getFieldValue: function(field) {
|
||||
var val = this.getFieldValueUnrendered(field);
|
||||
if (field && !_.isUndefined(field.renderer)) {
|
||||
val = field.renderer(val, field, this.toJSON());
|
||||
}
|
||||
return val;
|
||||
},
|
||||
|
||||
// ### getFieldValueUnrendered
|
||||
//
|
||||
// For the provided Field get the corresponding computed data value
|
||||
// for this record.
|
||||
//
|
||||
// NB: if field is undefined a default '' value will be returned
|
||||
getFieldValueUnrendered: function(field) {
|
||||
if (!field) {
|
||||
return '';
|
||||
}
|
||||
var val = this.get(field.id);
|
||||
if (field.deriver) {
|
||||
val = field.deriver(val, field, this);
|
||||
}
|
||||
return val;
|
||||
},
|
||||
|
||||
// ### summary
|
||||
//
|
||||
// Get a simple html summary of this record in form of key/value list
|
||||
summary: function(record) {
|
||||
var self = this;
|
||||
var html = '<div class="recline-record-summary">';
|
||||
this.fields.each(function(field) {
|
||||
if (field.id != 'id') {
|
||||
html += '<div class="' + field.id + '"><strong>' + field.get('label') + '</strong>: ' + self.getFieldValue(field) + '</div>';
|
||||
}
|
||||
});
|
||||
html += '</div>';
|
||||
return html;
|
||||
},
|
||||
|
||||
// Override Backbone save, fetch and destroy so they do nothing
|
||||
// Instead, Dataset object that created this Record should take care of
|
||||
// handling these changes (discovery will occur via event notifications)
|
||||
// WARNING: these will not persist *unless* you call save on Dataset
|
||||
fetch: function() {},
|
||||
save: function() {},
|
||||
destroy: function() { this.trigger('destroy', this); }
|
||||
});
|
||||
|
||||
|
||||
// ## A Backbone collection of Records
|
||||
my.RecordList = Backbone.Collection.extend({
|
||||
constructor: function RecordList() {
|
||||
Backbone.Collection.prototype.constructor.apply(this, arguments);
|
||||
},
|
||||
model: my.Record
|
||||
});
|
||||
|
||||
|
||||
// ## <a id="field">A Field (aka Column) on a Dataset</a>
|
||||
my.Field = Backbone.Model.extend({
|
||||
constructor: function Field() {
|
||||
Backbone.Model.prototype.constructor.apply(this, arguments);
|
||||
},
|
||||
// ### defaults - define default values
|
||||
defaults: {
|
||||
label: null,
|
||||
type: 'string',
|
||||
format: null,
|
||||
is_derived: false
|
||||
},
|
||||
// ### initialize
|
||||
//
|
||||
// @param {Object} data: standard Backbone model attributes
|
||||
//
|
||||
// @param {Object} options: renderer and/or deriver functions.
|
||||
initialize: function(data, options) {
|
||||
// if a hash not passed in the first argument throw error
|
||||
if ('0' in data) {
|
||||
throw new Error('Looks like you did not pass a proper hash with id to Field constructor');
|
||||
}
|
||||
if (this.attributes.label === null) {
|
||||
this.set({label: this.id});
|
||||
}
|
||||
if (this.attributes.type.toLowerCase() in this._typeMap) {
|
||||
this.attributes.type = this._typeMap[this.attributes.type.toLowerCase()];
|
||||
}
|
||||
if (options) {
|
||||
this.renderer = options.renderer;
|
||||
this.deriver = options.deriver;
|
||||
}
|
||||
if (!this.renderer) {
|
||||
this.renderer = this.defaultRenderers[this.get('type')];
|
||||
}
|
||||
this.facets = new my.FacetList();
|
||||
},
|
||||
_typeMap: {
|
||||
'text': 'string',
|
||||
'double': 'number',
|
||||
'float': 'number',
|
||||
'numeric': 'number',
|
||||
'int': 'integer',
|
||||
'datetime': 'date-time',
|
||||
'bool': 'boolean',
|
||||
'timestamp': 'date-time',
|
||||
'json': 'object'
|
||||
},
|
||||
defaultRenderers: {
|
||||
object: function(val, field, doc) {
|
||||
return JSON.stringify(val);
|
||||
},
|
||||
geo_point: function(val, field, doc) {
|
||||
return JSON.stringify(val);
|
||||
},
|
||||
'number': function(val, field, doc) {
|
||||
var format = field.get('format');
|
||||
if (format === 'percentage') {
|
||||
return val + '%';
|
||||
}
|
||||
return val;
|
||||
},
|
||||
'string': function(val, field, doc) {
|
||||
var format = field.get('format');
|
||||
if (format === 'markdown') {
|
||||
if (typeof Showdown !== 'undefined') {
|
||||
var showdown = new Showdown.converter();
|
||||
out = showdown.makeHtml(val);
|
||||
return out;
|
||||
} else {
|
||||
return val;
|
||||
}
|
||||
} else if (format == 'plain') {
|
||||
return val;
|
||||
} else {
|
||||
// as this is the default and default type is string may get things
|
||||
// here that are not actually strings
|
||||
if (val && typeof val === 'string') {
|
||||
val = val.replace(/(https?:\/\/[^ ]+)/g, '<a href="$1">$1</a>');
|
||||
}
|
||||
return val;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
my.FieldList = Backbone.Collection.extend({
|
||||
constructor: function FieldList() {
|
||||
Backbone.Collection.prototype.constructor.apply(this, arguments);
|
||||
},
|
||||
model: my.Field
|
||||
});
|
||||
|
||||
// ## <a id="query">Query</a>
|
||||
my.Query = Backbone.Model.extend({
|
||||
constructor: function Query() {
|
||||
Backbone.Model.prototype.constructor.apply(this, arguments);
|
||||
},
|
||||
defaults: function() {
|
||||
return {
|
||||
size: 100,
|
||||
from: 0,
|
||||
q: '',
|
||||
facets: {},
|
||||
filters: []
|
||||
};
|
||||
},
|
||||
_filterTemplates: {
|
||||
term: {
|
||||
type: 'term',
|
||||
// TODO do we need this attribute here?
|
||||
field: '',
|
||||
term: ''
|
||||
},
|
||||
range: {
|
||||
type: 'range',
|
||||
from: '',
|
||||
to: ''
|
||||
},
|
||||
geo_distance: {
|
||||
type: 'geo_distance',
|
||||
distance: 10,
|
||||
unit: 'km',
|
||||
point: {
|
||||
lon: 0,
|
||||
lat: 0
|
||||
}
|
||||
}
|
||||
},
|
||||
// ### addFilter(filter)
|
||||
//
|
||||
// Add a new filter specified by the filter hash and append to the list of filters
|
||||
//
|
||||
// @param filter an object specifying the filter - see _filterTemplates for examples. If only type is provided will generate a filter by cloning _filterTemplates
|
||||
addFilter: function(filter) {
|
||||
// crude deep copy
|
||||
var ourfilter = JSON.parse(JSON.stringify(filter));
|
||||
// not fully specified so use template and over-write
|
||||
if (_.keys(filter).length <= 3) {
|
||||
ourfilter = _.defaults(ourfilter, this._filterTemplates[filter.type]);
|
||||
}
|
||||
var filters = this.get('filters');
|
||||
filters.push(ourfilter);
|
||||
this.trigger('change:filters:new-blank');
|
||||
},
|
||||
replaceFilter: function(filter) {
|
||||
// delete filter on the same field, then add
|
||||
var filters = this.get('filters');
|
||||
var idx = -1;
|
||||
_.each(this.get('filters'), function(f, key, list) {
|
||||
if (filter.field == f.field) {
|
||||
idx = key;
|
||||
}
|
||||
});
|
||||
// trigger just one event (change:filters:new-blank) instead of one for remove and
|
||||
// one for add
|
||||
if (idx >= 0) {
|
||||
filters.splice(idx, 1);
|
||||
this.set({filters: filters});
|
||||
}
|
||||
this.addFilter(filter);
|
||||
},
|
||||
updateFilter: function(index, value) {
|
||||
},
|
||||
// ### removeFilter
|
||||
//
|
||||
// Remove a filter from filters at index filterIndex
|
||||
removeFilter: function(filterIndex) {
|
||||
var filters = this.get('filters');
|
||||
filters.splice(filterIndex, 1);
|
||||
this.set({filters: filters});
|
||||
this.trigger('change');
|
||||
},
|
||||
// ### addFacet
|
||||
//
|
||||
// Add a Facet to this query
|
||||
//
|
||||
// See <http://www.elasticsearch.org/guide/reference/api/search/facets/>
|
||||
addFacet: function(fieldId, size, silent) {
|
||||
var facets = this.get('facets');
|
||||
// Assume id and fieldId should be the same (TODO: this need not be true if we want to add two different type of facets on same field)
|
||||
if (_.contains(_.keys(facets), fieldId)) {
|
||||
return;
|
||||
}
|
||||
facets[fieldId] = {
|
||||
terms: { field: fieldId }
|
||||
};
|
||||
if (!_.isUndefined(size)) {
|
||||
facets[fieldId].terms.size = size;
|
||||
}
|
||||
this.set({facets: facets}, {silent: true});
|
||||
if (!silent) {
|
||||
this.trigger('facet:add', this);
|
||||
}
|
||||
},
|
||||
addHistogramFacet: function(fieldId) {
|
||||
var facets = this.get('facets');
|
||||
facets[fieldId] = {
|
||||
date_histogram: {
|
||||
field: fieldId,
|
||||
interval: 'day'
|
||||
}
|
||||
};
|
||||
this.set({facets: facets}, {silent: true});
|
||||
this.trigger('facet:add', this);
|
||||
},
|
||||
removeFacet: function(fieldId) {
|
||||
var facets = this.get('facets');
|
||||
// Assume id and fieldId should be the same (TODO: this need not be true if we want to add two different type of facets on same field)
|
||||
if (!_.contains(_.keys(facets), fieldId)) {
|
||||
return;
|
||||
}
|
||||
delete facets[fieldId];
|
||||
this.set({facets: facets}, {silent: true});
|
||||
this.trigger('facet:remove', this);
|
||||
},
|
||||
clearFacets: function() {
|
||||
var facets = this.get('facets');
|
||||
_.each(_.keys(facets), function(fieldId) {
|
||||
delete facets[fieldId];
|
||||
});
|
||||
this.trigger('facet:remove', this);
|
||||
},
|
||||
// trigger a facet add; use this to trigger a single event after adding
|
||||
// multiple facets
|
||||
refreshFacets: function() {
|
||||
this.trigger('facet:add', this);
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
|
||||
// ## <a id="facet">A Facet (Result)</a>
|
||||
my.Facet = Backbone.Model.extend({
|
||||
constructor: function Facet() {
|
||||
Backbone.Model.prototype.constructor.apply(this, arguments);
|
||||
},
|
||||
defaults: function() {
|
||||
return {
|
||||
_type: 'terms',
|
||||
total: 0,
|
||||
other: 0,
|
||||
missing: 0,
|
||||
terms: []
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
// ## A Collection/List of Facets
|
||||
my.FacetList = Backbone.Collection.extend({
|
||||
constructor: function FacetList() {
|
||||
Backbone.Collection.prototype.constructor.apply(this, arguments);
|
||||
},
|
||||
model: my.Facet
|
||||
});
|
||||
|
||||
// ## Object State
|
||||
//
|
||||
// Convenience Backbone model for storing (configuration) state of objects like Views.
|
||||
my.ObjectState = Backbone.Model.extend({
|
||||
});
|
||||
|
||||
|
||||
// ## Backbone.sync
|
||||
//
|
||||
// Override Backbone.sync to hand off to sync function in relevant backend
|
||||
// Backbone.sync = function(method, model, options) {
|
||||
// return model.backend.sync(method, model, options);
|
||||
// };
|
||||
|
||||
}(this.recline.Model));
|
||||
|
||||
this.recline = this.recline || {};
|
||||
this.recline.Backend = this.recline.Backend || {};
|
||||
this.recline.Backend.Memory = this.recline.Backend.Memory || {};
|
||||
|
||||
(function(my) {
|
||||
"use strict";
|
||||
my.__type__ = 'memory';
|
||||
|
||||
// private data - use either jQuery or Underscore Deferred depending on what is available
|
||||
var Deferred = (typeof jQuery !== "undefined" && jQuery.Deferred) || _.Deferred;
|
||||
|
||||
// ## Data Wrapper
|
||||
//
|
||||
// Turn a simple array of JS objects into a mini data-store with
|
||||
// functionality like querying, faceting, updating (by ID) and deleting (by
|
||||
// ID).
|
||||
//
|
||||
// @param records list of hashes for each record/row in the data ({key:
|
||||
// value, key: value})
|
||||
// @param fields (optional) list of field hashes (each hash defining a field
|
||||
// as per recline.Model.Field). If fields not specified they will be taken
|
||||
// from the data.
|
||||
my.Store = function(records, fields) {
|
||||
var self = this;
|
||||
this.records = records;
|
||||
// backwards compatability (in v0.5 records was named data)
|
||||
this.data = this.records;
|
||||
if (fields) {
|
||||
this.fields = fields;
|
||||
} else {
|
||||
if (records) {
|
||||
this.fields = _.map(records[0], function(value, key) {
|
||||
return {id: key, type: 'string'};
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
this.update = function(doc) {
|
||||
_.each(self.records, function(internalDoc, idx) {
|
||||
if(doc.id === internalDoc.id) {
|
||||
self.records[idx] = doc;
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
this.remove = function(doc) {
|
||||
var newdocs = _.reject(self.records, function(internalDoc) {
|
||||
return (doc.id === internalDoc.id);
|
||||
});
|
||||
this.records = newdocs;
|
||||
};
|
||||
|
||||
this.save = function(changes, dataset) {
|
||||
var self = this;
|
||||
var dfd = new Deferred();
|
||||
// TODO _.each(changes.creates) { ... }
|
||||
_.each(changes.updates, function(record) {
|
||||
self.update(record);
|
||||
});
|
||||
_.each(changes.deletes, function(record) {
|
||||
self.remove(record);
|
||||
});
|
||||
dfd.resolve();
|
||||
return dfd.promise();
|
||||
},
|
||||
|
||||
this.query = function(queryObj) {
|
||||
var dfd = new Deferred();
|
||||
var numRows = queryObj.size || this.records.length;
|
||||
var start = queryObj.from || 0;
|
||||
var results = this.records;
|
||||
|
||||
results = this._applyFilters(results, queryObj);
|
||||
results = this._applyFreeTextQuery(results, queryObj);
|
||||
|
||||
// TODO: this is not complete sorting!
|
||||
// What's wrong is we sort on the *last* entry in the sort list if there are multiple sort criteria
|
||||
_.each(queryObj.sort, function(sortObj) {
|
||||
var fieldName = sortObj.field;
|
||||
results = _.sortBy(results, function(doc) {
|
||||
var _out = doc[fieldName];
|
||||
return _out;
|
||||
});
|
||||
if (sortObj.order == 'desc') {
|
||||
results.reverse();
|
||||
}
|
||||
});
|
||||
var facets = this.computeFacets(results, queryObj);
|
||||
var out = {
|
||||
total: results.length,
|
||||
hits: results.slice(start, start+numRows),
|
||||
facets: facets
|
||||
};
|
||||
dfd.resolve(out);
|
||||
return dfd.promise();
|
||||
};
|
||||
|
||||
// in place filtering
|
||||
this._applyFilters = function(results, queryObj) {
|
||||
var filters = queryObj.filters;
|
||||
// register filters
|
||||
var filterFunctions = {
|
||||
term : term,
|
||||
terms : terms,
|
||||
range : range,
|
||||
geo_distance : geo_distance
|
||||
};
|
||||
var dataParsers = {
|
||||
integer: function (e) { return parseFloat(e, 10); },
|
||||
'float': function (e) { return parseFloat(e, 10); },
|
||||
number: function (e) { return parseFloat(e, 10); },
|
||||
string : function (e) { return e.toString(); },
|
||||
date : function (e) { return moment(e).valueOf(); },
|
||||
datetime : function (e) { return new Date(e).valueOf(); }
|
||||
};
|
||||
var keyedFields = {};
|
||||
_.each(self.fields, function(field) {
|
||||
keyedFields[field.id] = field;
|
||||
});
|
||||
function getDataParser(filter) {
|
||||
var fieldType = keyedFields[filter.field].type || 'string';
|
||||
return dataParsers[fieldType];
|
||||
}
|
||||
|
||||
// filter records
|
||||
return _.filter(results, function (record) {
|
||||
var passes = _.map(filters, function (filter) {
|
||||
return filterFunctions[filter.type](record, filter);
|
||||
});
|
||||
|
||||
// return only these records that pass all filters
|
||||
return _.all(passes, _.identity);
|
||||
});
|
||||
|
||||
// filters definitions
|
||||
function term(record, filter) {
|
||||
var parse = getDataParser(filter);
|
||||
var value = parse(record[filter.field]);
|
||||
var term = parse(filter.term);
|
||||
|
||||
return (value === term);
|
||||
}
|
||||
|
||||
function terms(record, filter) {
|
||||
var parse = getDataParser(filter);
|
||||
var value = parse(record[filter.field]);
|
||||
var terms = parse(filter.terms).split(",");
|
||||
|
||||
return (_.indexOf(terms, value) >= 0);
|
||||
}
|
||||
|
||||
function range(record, filter) {
|
||||
var fromnull = (_.isUndefined(filter.from) || filter.from === null || filter.from === '');
|
||||
var tonull = (_.isUndefined(filter.to) || filter.to === null || filter.to === '');
|
||||
var parse = getDataParser(filter);
|
||||
var value = parse(record[filter.field]);
|
||||
var from = parse(fromnull ? '' : filter.from);
|
||||
var to = parse(tonull ? '' : filter.to);
|
||||
|
||||
// if at least one end of range is set do not allow '' to get through
|
||||
// note that for strings '' <= {any-character} e.g. '' <= 'a'
|
||||
if ((!fromnull || !tonull) && value === '') {
|
||||
return false;
|
||||
}
|
||||
return ((fromnull || value >= from) && (tonull || value <= to));
|
||||
}
|
||||
|
||||
function geo_distance() {
|
||||
// TODO code here
|
||||
}
|
||||
};
|
||||
|
||||
// we OR across fields but AND across terms in query string
|
||||
this._applyFreeTextQuery = function(results, queryObj) {
|
||||
if (queryObj.q) {
|
||||
var terms = queryObj.q.split(' ');
|
||||
var patterns=_.map(terms, function(term) {
|
||||
return new RegExp(term.toLowerCase());
|
||||
});
|
||||
results = _.filter(results, function(rawdoc) {
|
||||
var matches = true;
|
||||
_.each(patterns, function(pattern) {
|
||||
var foundmatch = false;
|
||||
_.each(self.fields, function(field) {
|
||||
var value = rawdoc[field.id];
|
||||
if ((value !== null) && (value !== undefined)) {
|
||||
value = value.toString();
|
||||
} else {
|
||||
// value can be null (apparently in some cases)
|
||||
value = '';
|
||||
}
|
||||
// TODO regexes?
|
||||
foundmatch = foundmatch || (pattern.test(value.toLowerCase()));
|
||||
// TODO: early out (once we are true should break to spare unnecessary testing)
|
||||
// if (foundmatch) return true;
|
||||
});
|
||||
matches = matches && foundmatch;
|
||||
// TODO: early out (once false should break to spare unnecessary testing)
|
||||
// if (!matches) return false;
|
||||
});
|
||||
return matches;
|
||||
});
|
||||
}
|
||||
return results;
|
||||
};
|
||||
|
||||
this.computeFacets = function(records, queryObj) {
|
||||
var facetResults = {};
|
||||
if (!queryObj.facets) {
|
||||
return facetResults;
|
||||
}
|
||||
_.each(queryObj.facets, function(query, facetId) {
|
||||
// TODO: remove dependency on recline.Model
|
||||
facetResults[facetId] = new recline.Model.Facet({id: facetId}).toJSON();
|
||||
facetResults[facetId].termsall = {};
|
||||
});
|
||||
// faceting
|
||||
_.each(records, function(doc) {
|
||||
_.each(queryObj.facets, function(query, facetId) {
|
||||
var fieldId = query.terms.field;
|
||||
var val = doc[fieldId];
|
||||
var tmp = facetResults[facetId];
|
||||
if (val) {
|
||||
tmp.termsall[val] = tmp.termsall[val] ? tmp.termsall[val] + 1 : 1;
|
||||
} else {
|
||||
tmp.missing = tmp.missing + 1;
|
||||
}
|
||||
});
|
||||
});
|
||||
_.each(queryObj.facets, function(query, facetId) {
|
||||
var tmp = facetResults[facetId];
|
||||
var terms = _.map(tmp.termsall, function(count, term) {
|
||||
return { term: term, count: count };
|
||||
});
|
||||
tmp.terms = _.sortBy(terms, function(item) {
|
||||
// want descending order
|
||||
return -item.count;
|
||||
});
|
||||
tmp.terms = tmp.terms.slice(0, 10);
|
||||
});
|
||||
return facetResults;
|
||||
};
|
||||
};
|
||||
|
||||
}(this.recline.Backend.Memory));
|
||||
1
dist/recline.dataset.min.js
vendored
1
dist/recline.dataset.min.js
vendored
File diff suppressed because one or more lines are too long
4455
dist/recline.js
vendored
4455
dist/recline.js
vendored
File diff suppressed because it is too large
Load Diff
3
dist/recline.min.js
vendored
3
dist/recline.min.js
vendored
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user