Major overhaul for standalone operation.
This removes some of the `edbob` reliance, as well as borrowing some templates and styling etc. from Dtail.
|
@ -28,4 +28,6 @@ Static Assets
|
|||
|
||||
|
||||
def includeme(config):
|
||||
# TODO: Remove edbob.
|
||||
config.include('edbob.pyramid.static')
|
||||
config.add_static_view('tailbone', 'tailbone:static')
|
||||
|
|
28
tailbone/static/css/base.css
Normal file
|
@ -0,0 +1,28 @@
|
|||
|
||||
/******************************
|
||||
* General
|
||||
******************************/
|
||||
|
||||
body {
|
||||
font-family: Verdana, Arial, sans-serif;
|
||||
font-size: 82%;
|
||||
}
|
||||
|
||||
a {
|
||||
color: #3D6E1C;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
a:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
div.flash-messages div.ui-state-highlight {
|
||||
padding: .3em;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
div.error-messages div.ui-state-error {
|
||||
padding: .3em;
|
||||
margin-bottom: 8px;
|
||||
}
|
28
tailbone/static/css/filters.css
Normal file
|
@ -0,0 +1,28 @@
|
|||
|
||||
/******************************
|
||||
* Filters
|
||||
******************************/
|
||||
|
||||
div.filters form {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
div.filters div.filter {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
div.filters div.filter label {
|
||||
margin-right: 8px;
|
||||
}
|
||||
|
||||
div.filters div.filter select.filter-type {
|
||||
margin-right: 8px;
|
||||
}
|
||||
|
||||
div.filters div.filter div.value {
|
||||
display: inline;
|
||||
}
|
||||
|
||||
div.filters div.buttons * {
|
||||
margin-right: 8px;
|
||||
}
|
121
tailbone/static/css/forms.css
Normal file
|
@ -0,0 +1,121 @@
|
|||
|
||||
/******************************
|
||||
* Form Wrapper
|
||||
******************************/
|
||||
|
||||
div.form-wrapper {
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
|
||||
/******************************
|
||||
* Context Menu
|
||||
******************************/
|
||||
|
||||
div.form-wrapper ul.context-menu {
|
||||
float: right;
|
||||
list-style-type: none;
|
||||
margin: 0px;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
div.form-wrapper ul.context-menu li {
|
||||
line-height: 2em;
|
||||
}
|
||||
|
||||
|
||||
/******************************
|
||||
* Forms
|
||||
******************************/
|
||||
|
||||
div.form,
|
||||
div.fieldset-form,
|
||||
div.fieldset {
|
||||
float: left;
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
|
||||
/******************************
|
||||
* Fieldsets
|
||||
******************************/
|
||||
|
||||
div.field-wrapper {
|
||||
clear: both;
|
||||
min-height: 30px;
|
||||
overflow: auto;
|
||||
padding: 5px;
|
||||
}
|
||||
|
||||
div.field-wrapper.error {
|
||||
background-color: #ddcccc;
|
||||
border: 2px solid #dd6666;
|
||||
}
|
||||
|
||||
div.field-wrapper label {
|
||||
color: #000000;
|
||||
display: block;
|
||||
float: left;
|
||||
width: 160px;
|
||||
font-weight: bold;
|
||||
margin-top: 2px;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
div.field-wrapper div.field-error {
|
||||
color: #dd6666;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
div.field-wrapper div.field {
|
||||
display: block;
|
||||
float: left;
|
||||
margin-bottom: 5px;
|
||||
line-height: 25px;
|
||||
}
|
||||
|
||||
div.field-wrapper div.field input[type=text],
|
||||
div.field-wrapper div.field input[type=password],
|
||||
div.field-wrapper div.field select,
|
||||
div.field-wrapper div.field textarea {
|
||||
width: 320px;
|
||||
}
|
||||
|
||||
label input[type="checkbox"],
|
||||
label input[type="radio"] {
|
||||
margin-right: 0.5em;
|
||||
}
|
||||
|
||||
div.field ul {
|
||||
margin: 0px;
|
||||
padding-left: 15px;
|
||||
}
|
||||
|
||||
|
||||
/******************************
|
||||
* Buttons
|
||||
******************************/
|
||||
|
||||
div.buttons {
|
||||
clear: both;
|
||||
margin: 10px 0px;
|
||||
}
|
||||
|
||||
|
||||
/******************************
|
||||
* Employee Login Form
|
||||
******************************/
|
||||
|
||||
#employee-login-dialog label {
|
||||
width: 120px;
|
||||
}
|
||||
|
||||
#employee-login-dialog input[type="text"],
|
||||
#employee-login-dialog input[type="password"] {
|
||||
width: 80px;
|
||||
}
|
||||
|
||||
#employee-login-dialog div.buttons {
|
||||
margin: 10px 0px 0px 0px;
|
||||
text-align: center;
|
||||
}
|
40
tailbone/static/css/jquery.loadmask.css
Normal file
|
@ -0,0 +1,40 @@
|
|||
.loadmask {
|
||||
z-index: 100;
|
||||
position: absolute;
|
||||
top:0;
|
||||
left:0;
|
||||
-moz-opacity: 0.5;
|
||||
opacity: .50;
|
||||
filter: alpha(opacity=50);
|
||||
background-color: #CCC;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
zoom: 1;
|
||||
}
|
||||
.loadmask-msg {
|
||||
z-index: 20001;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
border:1px solid #6593cf;
|
||||
background: #c3daf9;
|
||||
padding:2px;
|
||||
}
|
||||
.loadmask-msg div {
|
||||
padding:5px 10px 5px 25px;
|
||||
background: #fbfbfb url('../img/loading.gif') no-repeat 5px 5px;
|
||||
line-height: 16px;
|
||||
border:1px solid #a3bad9;
|
||||
color:#222;
|
||||
font:normal 11px tahoma, arial, helvetica, sans-serif;
|
||||
cursor:wait;
|
||||
}
|
||||
.masked {
|
||||
overflow: hidden !important;
|
||||
}
|
||||
.masked-relative {
|
||||
position: relative !important;
|
||||
}
|
||||
.masked-hidden {
|
||||
visibility: hidden !important;
|
||||
}
|
15
tailbone/static/css/jquery.ui.menubar.css
vendored
Normal file
|
@ -0,0 +1,15 @@
|
|||
/*
|
||||
* jQuery UI Menubar @VERSION
|
||||
*
|
||||
* Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
|
||||
* Dual licensed under the MIT or GPL Version 2 licenses.
|
||||
* http://jquery.org/license
|
||||
*/
|
||||
.ui-menubar { list-style: none; margin: 0; padding-left: 0; }
|
||||
|
||||
.ui-menubar-item { float: left; }
|
||||
|
||||
.ui-menubar .ui-button { float: left; font-weight: normal; border-top-width: 0 !important; border-bottom-width: 0 !important; margin: 0; outline: none; }
|
||||
.ui-menubar .ui-menubar-link { border-right: 1px dashed transparent; border-left: 1px dashed transparent; }
|
||||
|
||||
.ui-menubar .ui-menu { width: 200px; position: absolute; z-index: 9999; font-weight: normal; }
|
64
tailbone/static/css/layout.css
Normal file
|
@ -0,0 +1,64 @@
|
|||
|
||||
/******************************
|
||||
* Main Layout
|
||||
******************************/
|
||||
|
||||
html, body, #body-wrapper {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
body > #body-wrapper {
|
||||
height: auto;
|
||||
min-height: 100%;
|
||||
}
|
||||
|
||||
#body-wrapper {
|
||||
margin: 0px auto;
|
||||
width: 1000px;
|
||||
}
|
||||
|
||||
#header {
|
||||
height: 50px;
|
||||
line-height: 50px;
|
||||
}
|
||||
|
||||
#body {
|
||||
padding-top: 10px;
|
||||
padding-bottom: 5em;
|
||||
}
|
||||
|
||||
#footer {
|
||||
clear: both;
|
||||
margin-top: -4em;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
|
||||
/******************************
|
||||
* Header
|
||||
******************************/
|
||||
|
||||
#header h1 {
|
||||
float: left;
|
||||
font-size: 25px;
|
||||
margin: 0px;
|
||||
}
|
||||
|
||||
#header h1.title {
|
||||
font-size: 20px;
|
||||
margin-left: 10px;
|
||||
}
|
||||
|
||||
#header div.login {
|
||||
float: right;
|
||||
}
|
||||
|
||||
|
||||
/******************************
|
||||
* Logo
|
||||
******************************/
|
||||
|
||||
#logo {
|
||||
display: block;
|
||||
margin: 40px auto;
|
||||
}
|
396
tailbone/static/css/normalize.css
vendored
Normal file
|
@ -0,0 +1,396 @@
|
|||
/*! normalize.css v2.1.0 | MIT License | git.io/normalize */
|
||||
|
||||
/* ==========================================================================
|
||||
HTML5 display definitions
|
||||
========================================================================== */
|
||||
|
||||
/**
|
||||
* Correct `block` display not defined in IE 8/9.
|
||||
*/
|
||||
|
||||
article,
|
||||
aside,
|
||||
details,
|
||||
figcaption,
|
||||
figure,
|
||||
footer,
|
||||
header,
|
||||
hgroup,
|
||||
main,
|
||||
nav,
|
||||
section,
|
||||
summary {
|
||||
display: block;
|
||||
}
|
||||
|
||||
/**
|
||||
* Correct `inline-block` display not defined in IE 8/9.
|
||||
*/
|
||||
|
||||
audio,
|
||||
canvas,
|
||||
video {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prevent modern browsers from displaying `audio` without controls.
|
||||
* Remove excess height in iOS 5 devices.
|
||||
*/
|
||||
|
||||
audio:not([controls]) {
|
||||
display: none;
|
||||
height: 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Address styling not present in IE 8/9.
|
||||
*/
|
||||
|
||||
[hidden] {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* ==========================================================================
|
||||
Base
|
||||
========================================================================== */
|
||||
|
||||
/**
|
||||
* 1. Set default font family to sans-serif.
|
||||
* 2. Prevent iOS text size adjust after orientation change, without disabling
|
||||
* user zoom.
|
||||
*/
|
||||
|
||||
html {
|
||||
font-family: sans-serif; /* 1 */
|
||||
-webkit-text-size-adjust: 100%; /* 2 */
|
||||
-ms-text-size-adjust: 100%; /* 2 */
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove default margin.
|
||||
*/
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
/* ==========================================================================
|
||||
Links
|
||||
========================================================================== */
|
||||
|
||||
/**
|
||||
* Address `outline` inconsistency between Chrome and other browsers.
|
||||
*/
|
||||
|
||||
a:focus {
|
||||
outline: thin dotted;
|
||||
}
|
||||
|
||||
/**
|
||||
* Improve readability when focused and also mouse hovered in all browsers.
|
||||
*/
|
||||
|
||||
a:active,
|
||||
a:hover {
|
||||
outline: 0;
|
||||
}
|
||||
|
||||
/* ==========================================================================
|
||||
Typography
|
||||
========================================================================== */
|
||||
|
||||
/**
|
||||
* Address variable `h1` font-size and margin within `section` and `article`
|
||||
* contexts in Firefox 4+, Safari 5, and Chrome.
|
||||
*/
|
||||
|
||||
h1 {
|
||||
font-size: 2em;
|
||||
margin: 0.67em 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Address styling not present in IE 8/9, Safari 5, and Chrome.
|
||||
*/
|
||||
|
||||
abbr[title] {
|
||||
border-bottom: 1px dotted;
|
||||
}
|
||||
|
||||
/**
|
||||
* Address style set to `bolder` in Firefox 4+, Safari 5, and Chrome.
|
||||
*/
|
||||
|
||||
b,
|
||||
strong {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
/**
|
||||
* Address styling not present in Safari 5 and Chrome.
|
||||
*/
|
||||
|
||||
dfn {
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
/**
|
||||
* Address differences between Firefox and other browsers.
|
||||
*/
|
||||
|
||||
hr {
|
||||
-moz-box-sizing: content-box;
|
||||
box-sizing: content-box;
|
||||
height: 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Address styling not present in IE 8/9.
|
||||
*/
|
||||
|
||||
mark {
|
||||
background: #ff0;
|
||||
color: #000;
|
||||
}
|
||||
|
||||
/**
|
||||
* Correct font family set oddly in Safari 5 and Chrome.
|
||||
*/
|
||||
|
||||
code,
|
||||
kbd,
|
||||
pre,
|
||||
samp {
|
||||
font-family: monospace, serif;
|
||||
font-size: 1em;
|
||||
}
|
||||
|
||||
/**
|
||||
* Improve readability of pre-formatted text in all browsers.
|
||||
*/
|
||||
|
||||
pre {
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set consistent quote types.
|
||||
*/
|
||||
|
||||
q {
|
||||
quotes: "\201C" "\201D" "\2018" "\2019";
|
||||
}
|
||||
|
||||
/**
|
||||
* Address inconsistent and variable font size in all browsers.
|
||||
*/
|
||||
|
||||
small {
|
||||
font-size: 80%;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prevent `sub` and `sup` affecting `line-height` in all browsers.
|
||||
*/
|
||||
|
||||
sub,
|
||||
sup {
|
||||
font-size: 75%;
|
||||
line-height: 0;
|
||||
position: relative;
|
||||
vertical-align: baseline;
|
||||
}
|
||||
|
||||
sup {
|
||||
top: -0.5em;
|
||||
}
|
||||
|
||||
sub {
|
||||
bottom: -0.25em;
|
||||
}
|
||||
|
||||
/* ==========================================================================
|
||||
Embedded content
|
||||
========================================================================== */
|
||||
|
||||
/**
|
||||
* Remove border when inside `a` element in IE 8/9.
|
||||
*/
|
||||
|
||||
img {
|
||||
border: 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Correct overflow displayed oddly in IE 9.
|
||||
*/
|
||||
|
||||
svg:not(:root) {
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
/* ==========================================================================
|
||||
Figures
|
||||
========================================================================== */
|
||||
|
||||
/**
|
||||
* Address margin not present in IE 8/9 and Safari 5.
|
||||
*/
|
||||
|
||||
figure {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
/* ==========================================================================
|
||||
Forms
|
||||
========================================================================== */
|
||||
|
||||
/**
|
||||
* Define consistent border, margin, and padding.
|
||||
*/
|
||||
|
||||
fieldset {
|
||||
border: 1px solid #c0c0c0;
|
||||
margin: 0 2px;
|
||||
padding: 0.35em 0.625em 0.75em;
|
||||
}
|
||||
|
||||
/**
|
||||
* 1. Correct `color` not being inherited in IE 8/9.
|
||||
* 2. Remove padding so people aren't caught out if they zero out fieldsets.
|
||||
*/
|
||||
|
||||
legend {
|
||||
border: 0; /* 1 */
|
||||
padding: 0; /* 2 */
|
||||
}
|
||||
|
||||
/**
|
||||
* 1. Correct font family not being inherited in all browsers.
|
||||
* 2. Correct font size not being inherited in all browsers.
|
||||
* 3. Address margins set differently in Firefox 4+, Safari 5, and Chrome.
|
||||
*/
|
||||
|
||||
button,
|
||||
input,
|
||||
select,
|
||||
textarea {
|
||||
font-family: inherit; /* 1 */
|
||||
font-size: 100%; /* 2 */
|
||||
margin: 0; /* 3 */
|
||||
}
|
||||
|
||||
/**
|
||||
* Address Firefox 4+ setting `line-height` on `input` using `!important` in
|
||||
* the UA stylesheet.
|
||||
*/
|
||||
|
||||
button,
|
||||
input {
|
||||
line-height: normal;
|
||||
}
|
||||
|
||||
/**
|
||||
* Address inconsistent `text-transform` inheritance for `button` and `select`.
|
||||
* All other form control elements do not inherit `text-transform` values.
|
||||
* Correct `button` style inheritance in Chrome, Safari 5+, and IE 8+.
|
||||
* Correct `select` style inheritance in Firefox 4+ and Opera.
|
||||
*/
|
||||
|
||||
button,
|
||||
select {
|
||||
text-transform: none;
|
||||
}
|
||||
|
||||
/**
|
||||
* 1. Avoid the WebKit bug in Android 4.0.* where (2) destroys native `audio`
|
||||
* and `video` controls.
|
||||
* 2. Correct inability to style clickable `input` types in iOS.
|
||||
* 3. Improve usability and consistency of cursor style between image-type
|
||||
* `input` and others.
|
||||
*/
|
||||
|
||||
button,
|
||||
html input[type="button"], /* 1 */
|
||||
input[type="reset"],
|
||||
input[type="submit"] {
|
||||
-webkit-appearance: button; /* 2 */
|
||||
cursor: pointer; /* 3 */
|
||||
}
|
||||
|
||||
/**
|
||||
* Re-set default cursor for disabled elements.
|
||||
*/
|
||||
|
||||
button[disabled],
|
||||
html input[disabled] {
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
/**
|
||||
* 1. Address box sizing set to `content-box` in IE 8/9.
|
||||
* 2. Remove excess padding in IE 8/9.
|
||||
*/
|
||||
|
||||
input[type="checkbox"],
|
||||
input[type="radio"] {
|
||||
box-sizing: border-box; /* 1 */
|
||||
padding: 0; /* 2 */
|
||||
}
|
||||
|
||||
/**
|
||||
* 1. Address `appearance` set to `searchfield` in Safari 5 and Chrome.
|
||||
* 2. Address `box-sizing` set to `border-box` in Safari 5 and Chrome
|
||||
* (include `-moz` to future-proof).
|
||||
*/
|
||||
|
||||
input[type="search"] {
|
||||
-webkit-appearance: textfield; /* 1 */
|
||||
-moz-box-sizing: content-box;
|
||||
-webkit-box-sizing: content-box; /* 2 */
|
||||
box-sizing: content-box;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove inner padding and search cancel button in Safari 5 and Chrome
|
||||
* on OS X.
|
||||
*/
|
||||
|
||||
input[type="search"]::-webkit-search-cancel-button,
|
||||
input[type="search"]::-webkit-search-decoration {
|
||||
-webkit-appearance: none;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove inner padding and border in Firefox 4+.
|
||||
*/
|
||||
|
||||
button::-moz-focus-inner,
|
||||
input::-moz-focus-inner {
|
||||
border: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 1. Remove default vertical scrollbar in IE 8/9.
|
||||
* 2. Improve readability and alignment in all browsers.
|
||||
*/
|
||||
|
||||
textarea {
|
||||
overflow: auto; /* 1 */
|
||||
vertical-align: top; /* 2 */
|
||||
}
|
||||
|
||||
/* ==========================================================================
|
||||
Tables
|
||||
========================================================================== */
|
||||
|
||||
/**
|
||||
* Remove most spacing between table cells.
|
||||
*/
|
||||
|
||||
table {
|
||||
border-collapse: collapse;
|
||||
border-spacing: 0;
|
||||
}
|
BIN
tailbone/static/css/smoothness/images/animated-overlay.gif
Normal file
After Width: | Height: | Size: 1.7 KiB |
After Width: | Height: | Size: 274 B |
After Width: | Height: | Size: 271 B |
After Width: | Height: | Size: 387 B |
After Width: | Height: | Size: 272 B |
After Width: | Height: | Size: 375 B |
After Width: | Height: | Size: 368 B |
After Width: | Height: | Size: 384 B |
After Width: | Height: | Size: 360 B |
After Width: | Height: | Size: 6.6 KiB |
After Width: | Height: | Size: 4.3 KiB |
After Width: | Height: | Size: 6.7 KiB |
After Width: | Height: | Size: 6.7 KiB |
After Width: | Height: | Size: 4.3 KiB |
5
tailbone/static/css/smoothness/jquery-ui-1.10.0.custom.min.css
vendored
Normal file
BIN
tailbone/static/img/Hymenocephalus_italicus.jpg
Normal file
After Width: | Height: | Size: 105 KiB |
BIN
tailbone/static/img/home_logo.png
Normal file
After Width: | Height: | Size: 20 KiB |
BIN
tailbone/static/img/loading.gif
Normal file
After Width: | Height: | Size: 771 B |
BIN
tailbone/static/img/rattail.ico
Normal file
After Width: | Height: | Size: 5.6 KiB |
5
tailbone/static/js/lib/jquery-1.9.1.min.js
vendored
Normal file
6
tailbone/static/js/lib/jquery-ui-1.10.0.custom.min.js
vendored
Normal file
10
tailbone/static/js/lib/jquery.loadmask.min.js
vendored
Normal file
|
@ -0,0 +1,10 @@
|
|||
/**
|
||||
* Copyright (c) 2009 Sergiy Kovalchuk (serg472@gmail.com)
|
||||
*
|
||||
* Dual licensed under the MIT (http://www.opensource.org/licenses/mit-license.php)
|
||||
* and GPL (http://www.opensource.org/licenses/gpl-license.php) licenses.
|
||||
*
|
||||
* Following code is based on Element.mask() implementation from ExtJS framework (http://extjs.com/)
|
||||
*
|
||||
*/
|
||||
(function(a){a.fn.mask=function(c,b){a(this).each(function(){if(b!==undefined&&b>0){var d=a(this);d.data("_mask_timeout",setTimeout(function(){a.maskElement(d,c)},b))}else{a.maskElement(a(this),c)}})};a.fn.unmask=function(){a(this).each(function(){a.unmaskElement(a(this))})};a.fn.isMasked=function(){return this.hasClass("masked")};a.maskElement=function(d,c){if(d.data("_mask_timeout")!==undefined){clearTimeout(d.data("_mask_timeout"));d.removeData("_mask_timeout")}if(d.isMasked()){a.unmaskElement(d)}if(d.css("position")=="static"){d.addClass("masked-relative")}d.addClass("masked");var e=a('<div class="loadmask"></div>');if(navigator.userAgent.toLowerCase().indexOf("msie")>-1){e.height(d.height()+parseInt(d.css("padding-top"))+parseInt(d.css("padding-bottom")));e.width(d.width()+parseInt(d.css("padding-left"))+parseInt(d.css("padding-right")))}if(navigator.userAgent.toLowerCase().indexOf("msie 6")>-1){d.find("select").addClass("masked-hidden")}d.append(e);if(c!==undefined){var b=a('<div class="loadmask-msg" style="display:none;"></div>');b.append("<div>"+c+"</div>");d.append(b);b.css("top",Math.round(d.height()/2-(b.height()-parseInt(b.css("padding-top"))-parseInt(b.css("padding-bottom")))/2)+"px");b.css("left",Math.round(d.width()/2-(b.width()-parseInt(b.css("padding-left"))-parseInt(b.css("padding-right")))/2)+"px");b.show()}};a.unmaskElement=function(b){if(b.data("_mask_timeout")!==undefined){clearTimeout(b.data("_mask_timeout"));b.removeData("_mask_timeout")}b.find(".loadmask-msg,.loadmask").remove();b.removeClass("masked");b.removeClass("masked-relative");b.find("select").removeClass("masked-hidden")}})(jQuery);
|
327
tailbone/static/js/lib/jquery.ui.menubar.js
vendored
Normal file
|
@ -0,0 +1,327 @@
|
|||
/*
|
||||
* jQuery UI Menubar @VERSION
|
||||
*
|
||||
* Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
|
||||
* Dual licensed under the MIT or GPL Version 2 licenses.
|
||||
* http://jquery.org/license
|
||||
*
|
||||
* http://docs.jquery.com/UI/Menubar
|
||||
*
|
||||
* Depends:
|
||||
* jquery.ui.core.js
|
||||
* jquery.ui.widget.js
|
||||
* jquery.ui.position.js
|
||||
* jquery.ui.menu.js
|
||||
*/
|
||||
(function( $ ) {
|
||||
|
||||
// TODO when mixing clicking menus and keyboard navigation, focus handling is broken
|
||||
// there has to be just one item that has tabindex
|
||||
$.widget( "ui.menubar", {
|
||||
version: "@VERSION",
|
||||
options: {
|
||||
autoExpand: false,
|
||||
buttons: false,
|
||||
items: "li",
|
||||
menuElement: "ul",
|
||||
menuIcon: false,
|
||||
position: {
|
||||
my: "left top",
|
||||
at: "left bottom"
|
||||
}
|
||||
},
|
||||
_create: function() {
|
||||
var that = this;
|
||||
this.menuItems = this.element.children( this.options.items );
|
||||
this.items = this.menuItems.children( "button, a" );
|
||||
|
||||
this.menuItems
|
||||
.addClass( "ui-menubar-item" )
|
||||
.attr( "role", "presentation" );
|
||||
// let only the first item receive focus
|
||||
this.items.slice(1).attr( "tabIndex", -1 );
|
||||
|
||||
this.element
|
||||
.addClass( "ui-menubar ui-widget-header ui-helper-clearfix" )
|
||||
.attr( "role", "menubar" );
|
||||
this._focusable( this.items );
|
||||
this._hoverable( this.items );
|
||||
this.items.siblings( this.options.menuElement )
|
||||
.menu({
|
||||
position: {
|
||||
within: this.options.position.within
|
||||
},
|
||||
select: function( event, ui ) {
|
||||
ui.item.parents( "ul.ui-menu:last" ).hide();
|
||||
that._close();
|
||||
// TODO what is this targetting? there's probably a better way to access it
|
||||
$(event.target).prev().focus();
|
||||
that._trigger( "select", event, ui );
|
||||
},
|
||||
menus: that.options.menuElement
|
||||
})
|
||||
.hide()
|
||||
.attr({
|
||||
"aria-hidden": "true",
|
||||
"aria-expanded": "false"
|
||||
})
|
||||
// TODO use _on
|
||||
.bind( "keydown.menubar", function( event ) {
|
||||
var menu = $( this );
|
||||
if ( menu.is( ":hidden" ) ) {
|
||||
return;
|
||||
}
|
||||
switch ( event.keyCode ) {
|
||||
case $.ui.keyCode.LEFT:
|
||||
that.previous( event );
|
||||
event.preventDefault();
|
||||
break;
|
||||
case $.ui.keyCode.RIGHT:
|
||||
that.next( event );
|
||||
event.preventDefault();
|
||||
break;
|
||||
}
|
||||
});
|
||||
this.items.each(function() {
|
||||
var input = $(this),
|
||||
// TODO menu var is only used on two places, doesn't quite justify the .each
|
||||
menu = input.next( that.options.menuElement );
|
||||
|
||||
// might be a non-menu button
|
||||
if ( menu.length ) {
|
||||
// TODO use _on
|
||||
input.bind( "click.menubar focus.menubar mouseenter.menubar", function( event ) {
|
||||
// ignore triggered focus event
|
||||
if ( event.type === "focus" && !event.originalEvent ) {
|
||||
return;
|
||||
}
|
||||
event.preventDefault();
|
||||
// TODO can we simplify or extractthis check? especially the last two expressions
|
||||
// there's a similar active[0] == menu[0] check in _open
|
||||
if ( event.type === "click" && menu.is( ":visible" ) && that.active && that.active[0] === menu[0] ) {
|
||||
that._close();
|
||||
return;
|
||||
}
|
||||
if ( ( that.open && event.type === "mouseenter" ) || event.type === "click" || that.options.autoExpand ) {
|
||||
if( that.options.autoExpand ) {
|
||||
clearTimeout( that.closeTimer );
|
||||
}
|
||||
|
||||
that._open( event, menu );
|
||||
}
|
||||
})
|
||||
// TODO use _on
|
||||
.bind( "keydown", function( event ) {
|
||||
switch ( event.keyCode ) {
|
||||
case $.ui.keyCode.SPACE:
|
||||
case $.ui.keyCode.UP:
|
||||
case $.ui.keyCode.DOWN:
|
||||
that._open( event, $( this ).next() );
|
||||
event.preventDefault();
|
||||
break;
|
||||
case $.ui.keyCode.LEFT:
|
||||
that.previous( event );
|
||||
event.preventDefault();
|
||||
break;
|
||||
case $.ui.keyCode.RIGHT:
|
||||
that.next( event );
|
||||
event.preventDefault();
|
||||
break;
|
||||
}
|
||||
})
|
||||
.attr( "aria-haspopup", "true" );
|
||||
|
||||
// TODO review if these options (menuIcon and buttons) are a good choice, maybe they can be merged
|
||||
if ( that.options.menuIcon ) {
|
||||
input.addClass( "ui-state-default" ).append( "<span class='ui-button-icon-secondary ui-icon ui-icon-triangle-1-s'></span>" );
|
||||
input.removeClass( "ui-button-text-only" ).addClass( "ui-button-text-icon-secondary" );
|
||||
}
|
||||
} else {
|
||||
// TODO use _on
|
||||
input.bind( "click.menubar mouseenter.menubar", function( event ) {
|
||||
if ( ( that.open && event.type === "mouseenter" ) || event.type === "click" ) {
|
||||
that._close();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
input
|
||||
.addClass( "ui-button ui-widget ui-button-text-only ui-menubar-link" )
|
||||
.attr( "role", "menuitem" )
|
||||
.wrapInner( "<span class='ui-button-text'></span>" );
|
||||
|
||||
if ( that.options.buttons ) {
|
||||
input.removeClass( "ui-menubar-link" ).addClass( "ui-state-default" );
|
||||
}
|
||||
});
|
||||
that._on( {
|
||||
keydown: function( event ) {
|
||||
if ( event.keyCode === $.ui.keyCode.ESCAPE && that.active && that.active.menu( "collapse", event ) !== true ) {
|
||||
var active = that.active;
|
||||
that.active.blur();
|
||||
that._close( event );
|
||||
active.prev().focus();
|
||||
}
|
||||
},
|
||||
focusin: function( event ) {
|
||||
clearTimeout( that.closeTimer );
|
||||
},
|
||||
focusout: function( event ) {
|
||||
that.closeTimer = setTimeout( function() {
|
||||
that._close( event );
|
||||
}, 150);
|
||||
},
|
||||
"mouseleave .ui-menubar-item": function( event ) {
|
||||
if ( that.options.autoExpand ) {
|
||||
that.closeTimer = setTimeout( function() {
|
||||
that._close( event );
|
||||
}, 150);
|
||||
}
|
||||
},
|
||||
"mouseenter .ui-menubar-item": function( event ) {
|
||||
clearTimeout( that.closeTimer );
|
||||
}
|
||||
});
|
||||
|
||||
// Keep track of open submenus
|
||||
this.openSubmenus = 0;
|
||||
},
|
||||
|
||||
_destroy : function() {
|
||||
this.menuItems
|
||||
.removeClass( "ui-menubar-item" )
|
||||
.removeAttr( "role" );
|
||||
|
||||
this.element
|
||||
.removeClass( "ui-menubar ui-widget-header ui-helper-clearfix" )
|
||||
.removeAttr( "role" )
|
||||
.unbind( ".menubar" );
|
||||
|
||||
this.items
|
||||
.unbind( ".menubar" )
|
||||
.removeClass( "ui-button ui-widget ui-button-text-only ui-menubar-link ui-state-default" )
|
||||
.removeAttr( "role" )
|
||||
.removeAttr( "aria-haspopup" )
|
||||
// TODO unwrap?
|
||||
.children( "span.ui-button-text" ).each(function( i, e ) {
|
||||
var item = $( this );
|
||||
item.parent().html( item.html() );
|
||||
})
|
||||
.end()
|
||||
.children( ".ui-icon" ).remove();
|
||||
|
||||
this.element.find( ":ui-menu" )
|
||||
.menu( "destroy" )
|
||||
.show()
|
||||
.removeAttr( "aria-hidden" )
|
||||
.removeAttr( "aria-expanded" )
|
||||
.removeAttr( "tabindex" )
|
||||
.unbind( ".menubar" );
|
||||
},
|
||||
|
||||
_close: function() {
|
||||
if ( !this.active || !this.active.length ) {
|
||||
return;
|
||||
}
|
||||
this.active
|
||||
.menu( "collapseAll" )
|
||||
.hide()
|
||||
.attr({
|
||||
"aria-hidden": "true",
|
||||
"aria-expanded": "false"
|
||||
});
|
||||
this.active
|
||||
.prev()
|
||||
.removeClass( "ui-state-active" )
|
||||
.removeAttr( "tabIndex" );
|
||||
this.active = null;
|
||||
this.open = false;
|
||||
this.openSubmenus = 0;
|
||||
},
|
||||
|
||||
_open: function( event, menu ) {
|
||||
// on a single-button menubar, ignore reopening the same menu
|
||||
if ( this.active && this.active[0] === menu[0] ) {
|
||||
return;
|
||||
}
|
||||
// TODO refactor, almost the same as _close above, but don't remove tabIndex
|
||||
if ( this.active ) {
|
||||
this.active
|
||||
.menu( "collapseAll" )
|
||||
.hide()
|
||||
.attr({
|
||||
"aria-hidden": "true",
|
||||
"aria-expanded": "false"
|
||||
});
|
||||
this.active
|
||||
.prev()
|
||||
.removeClass( "ui-state-active" );
|
||||
}
|
||||
// set tabIndex -1 to have the button skipped on shift-tab when menu is open (it gets focus)
|
||||
var button = menu.prev().addClass( "ui-state-active" ).attr( "tabIndex", -1 );
|
||||
this.active = menu
|
||||
.show()
|
||||
.position( $.extend({
|
||||
of: button
|
||||
}, this.options.position ) )
|
||||
.removeAttr( "aria-hidden" )
|
||||
.attr( "aria-expanded", "true" )
|
||||
.menu("focus", event, menu.children( ".ui-menu-item" ).first() )
|
||||
// TODO need a comment here why both events are triggered
|
||||
.focus()
|
||||
.focusin();
|
||||
this.open = true;
|
||||
},
|
||||
|
||||
next: function( event ) {
|
||||
if ( this.open && this.active.data( "menu" ).active.has( ".ui-menu" ).length ) {
|
||||
// Track number of open submenus and prevent moving to next menubar item
|
||||
this.openSubmenus++;
|
||||
return;
|
||||
}
|
||||
this.openSubmenus = 0;
|
||||
this._move( "next", "first", event );
|
||||
},
|
||||
|
||||
previous: function( event ) {
|
||||
if ( this.open && this.openSubmenus ) {
|
||||
// Track number of open submenus and prevent moving to previous menubar item
|
||||
this.openSubmenus--;
|
||||
return;
|
||||
}
|
||||
this.openSubmenus = 0;
|
||||
this._move( "prev", "last", event );
|
||||
},
|
||||
|
||||
_move: function( direction, filter, event ) {
|
||||
var next,
|
||||
wrapItem;
|
||||
if ( this.open ) {
|
||||
next = this.active.closest( ".ui-menubar-item" )[ direction + "All" ]( this.options.items ).first().children( ".ui-menu" ).eq( 0 );
|
||||
wrapItem = this.menuItems[ filter ]().children( ".ui-menu" ).eq( 0 );
|
||||
} else {
|
||||
if ( event ) {
|
||||
next = $( event.target ).closest( ".ui-menubar-item" )[ direction + "All" ]( this.options.items ).children( ".ui-menubar-link" ).eq( 0 );
|
||||
wrapItem = this.menuItems[ filter ]().children( ".ui-menubar-link" ).eq( 0 );
|
||||
} else {
|
||||
next = wrapItem = this.menuItems.children( "a" ).eq( 0 );
|
||||
}
|
||||
}
|
||||
|
||||
if ( next.length ) {
|
||||
if ( this.open ) {
|
||||
this._open( event, next );
|
||||
} else {
|
||||
next.removeAttr( "tabIndex")[0].focus();
|
||||
}
|
||||
} else {
|
||||
if ( this.open ) {
|
||||
this._open( event, wrapItem );
|
||||
} else {
|
||||
wrapItem.removeAttr( "tabIndex")[0].focus();
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
}( jQuery ));
|
260
tailbone/static/js/tailbone.js
Normal file
|
@ -0,0 +1,260 @@
|
|||
|
||||
/************************************************************
|
||||
*
|
||||
* tailbone.js
|
||||
*
|
||||
************************************************************/
|
||||
|
||||
|
||||
/*
|
||||
* Initialize the disabled filters array. This is populated from within the
|
||||
* /grids/search.mako template.
|
||||
*/
|
||||
var filters_to_disable = [];
|
||||
|
||||
|
||||
/*
|
||||
* Disables options within the "add filter" dropdown which correspond to those
|
||||
* filters already being displayed. Called from /grids/search.mako template.
|
||||
*/
|
||||
function disable_filter_options() {
|
||||
while (filters_to_disable.length) {
|
||||
var filter = filters_to_disable.shift();
|
||||
var option = $('#add-filter option[value="' + filter + '"]');
|
||||
option.attr('disabled', 'disabled');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Convenience function to disable a form button.
|
||||
*/
|
||||
function disable_button(button, label) {
|
||||
if (label) {
|
||||
$(button).html(label + ", please wait...");
|
||||
}
|
||||
$(button).attr('disabled', 'disabled');
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Load next / previous page of results to grid. This function is called on
|
||||
* the click event from the pager links, via inline script code.
|
||||
*/
|
||||
function grid_navigate_page(link, url) {
|
||||
var wrapper = $(link).parents('div.grid-wrapper');
|
||||
var grid = wrapper.find('div.grid');
|
||||
wrapper.mask("Loading...");
|
||||
$.get(url, function(data) {
|
||||
wrapper.unmask();
|
||||
grid.replaceWith(data);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Fetch the UUID value associated with a table row.
|
||||
*/
|
||||
function get_uuid(obj) {
|
||||
obj = $(obj);
|
||||
if (obj.attr('uuid')) {
|
||||
return obj.attr('uuid');
|
||||
}
|
||||
var tr = obj.parents('tr:first');
|
||||
if (tr.attr('uuid')) {
|
||||
return tr.attr('uuid');
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* get_dialog(id, callback)
|
||||
*
|
||||
* Returns a <DIV> element suitable for use as a jQuery dialog.
|
||||
*
|
||||
* ``id`` is used to construct a proper ID for the element and allows the
|
||||
* dialog to be resused if possible.
|
||||
*
|
||||
* ``callback``, if specified, should be a callback function for the dialog.
|
||||
* This function will be called whenever the dialog has been closed
|
||||
* "successfully" (i.e. data submitted) by the user, and should accept a single
|
||||
* ``data`` object which is the JSON response returned by the server.
|
||||
*/
|
||||
|
||||
function get_dialog(id, callback) {
|
||||
var dialog = $('#'+id+'-dialog');
|
||||
if (! dialog.length) {
|
||||
dialog = $('<div class="dialog" id="'+id+'-dialog"></div>');
|
||||
}
|
||||
if (callback) {
|
||||
dialog.attr('callback', callback);
|
||||
}
|
||||
return dialog;
|
||||
}
|
||||
|
||||
|
||||
$(function() {
|
||||
|
||||
/*
|
||||
* Initialize the menu bar.
|
||||
*/
|
||||
$('ul.menubar').menubar({
|
||||
buttons: true,
|
||||
menuIcon: true,
|
||||
autoExpand: true
|
||||
});
|
||||
|
||||
/*
|
||||
* When filter labels are clicked, (un)check the associated checkbox.
|
||||
*/
|
||||
$('div.grid-wrapper div.filter label').on('click', function() {
|
||||
var checkbox = $(this).prev('input[type="checkbox"]');
|
||||
if (checkbox.prop('checked')) {
|
||||
checkbox.prop('checked', false);
|
||||
return false;
|
||||
}
|
||||
checkbox.prop('checked', true);
|
||||
});
|
||||
|
||||
/*
|
||||
* When a new filter is selected in the "add filter" dropdown, show it in
|
||||
* the UI. This selects the filter's checkbox and puts focus to its input
|
||||
* element. If all available filters have been displayed, the "add filter"
|
||||
* dropdown will be hidden.
|
||||
*/
|
||||
$('#add-filter').on('change', function() {
|
||||
var select = $(this);
|
||||
var filters = select.parents('div.filters:first');
|
||||
var filter = filters.find('#filter-' + select.val());
|
||||
var checkbox = filter.find('input[type="checkbox"]:first');
|
||||
var input = filter.find(':last-child');
|
||||
|
||||
checkbox.prop('checked', true);
|
||||
filter.show();
|
||||
input.select();
|
||||
input.focus();
|
||||
|
||||
filters.find('input[type="submit"]').show();
|
||||
filters.find('button[type="reset"]').show();
|
||||
|
||||
select.find('option:selected').attr('disabled', true);
|
||||
select.val('add a filter');
|
||||
if (select.find('option:enabled').length == 1) {
|
||||
select.hide();
|
||||
}
|
||||
});
|
||||
|
||||
/*
|
||||
* When user clicks the grid filters search button, perform the search in
|
||||
* the background and reload the grid in-place.
|
||||
*/
|
||||
$('div.filters form').submit(function() {
|
||||
var form = $(this);
|
||||
var wrapper = form.parents('div.grid-wrapper');
|
||||
var grid = wrapper.find('div.grid');
|
||||
var data = form.serializeArray();
|
||||
data.push({name: 'partial', value: true});
|
||||
wrapper.mask("Loading...");
|
||||
$.get(grid.attr('url'), data, function(data) {
|
||||
wrapper.unmask();
|
||||
grid.replaceWith(data);
|
||||
});
|
||||
return false;
|
||||
});
|
||||
|
||||
/*
|
||||
* When user clicks the grid filters reset button, manually clear all
|
||||
* filter input elements, and submit a new search.
|
||||
*/
|
||||
$('div.filters form button[type="reset"]').click(function() {
|
||||
var form = $(this).parents('form');
|
||||
form.find('div.filter').each(function() {
|
||||
$(this).find('div.value input').val('');
|
||||
});
|
||||
form.submit();
|
||||
return false;
|
||||
});
|
||||
|
||||
$('div.grid-wrapper').on('click', 'div.grid th.sortable a', function() {
|
||||
var th = $(this).parent();
|
||||
var wrapper = th.parents('div.grid-wrapper');
|
||||
var grid = wrapper.find('div.grid');
|
||||
var data = {
|
||||
sort: th.attr('field'),
|
||||
dir: (th.hasClass('sorted') && th.hasClass('asc')) ? 'desc' : 'asc',
|
||||
page: 1,
|
||||
partial: true
|
||||
};
|
||||
wrapper.mask("Loading...");
|
||||
$.get(grid.attr('url'), data, function(data) {
|
||||
wrapper.unmask();
|
||||
grid.replaceWith(data);
|
||||
});
|
||||
return false;
|
||||
});
|
||||
|
||||
$('#body').on('mouseenter', 'div.grid.hoverable table tbody tr', function() {
|
||||
$(this).addClass('hovering');
|
||||
});
|
||||
|
||||
$('#body').on('mouseleave', 'div.grid.hoverable table tbody tr', function() {
|
||||
$(this).removeClass('hovering');
|
||||
});
|
||||
|
||||
$('div.grid-wrapper').on('click', 'div.grid table tbody td.view', function() {
|
||||
var url = $(this).attr('url');
|
||||
if (url) {
|
||||
location.href = url;
|
||||
}
|
||||
});
|
||||
|
||||
$('div.grid-wrapper').on('click', 'div.grid table tbody td.edit', function() {
|
||||
var url = $(this).attr('url');
|
||||
if (url) {
|
||||
location.href = url;
|
||||
}
|
||||
});
|
||||
|
||||
$('div.grid-wrapper').on('click', 'div.grid table tbody td.delete', function() {
|
||||
var url = $(this).attr('url');
|
||||
if (url) {
|
||||
if (confirm("Do you really wish to delete this object?")) {
|
||||
location.href = url;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
$('div.grid-wrapper').on('change', 'div.grid div.pager select#grid-page-count', function() {
|
||||
var select = $(this);
|
||||
var wrapper = select.parents('div.grid-wrapper');
|
||||
var grid = wrapper.find('div.grid');
|
||||
var data = {
|
||||
per_page: select.val(),
|
||||
partial: true
|
||||
};
|
||||
wrapper.mask("Loading...");
|
||||
$.get(grid.attr('url'), data, function(data) {
|
||||
wrapper.unmask();
|
||||
grid.replaceWith(data);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
/*
|
||||
* Add "check all" functionality to tables with checkboxes.
|
||||
*/
|
||||
$('body').on('click', 'div.grid table thead th.checkbox input[type="checkbox"]', function() {
|
||||
var table = $(this).parents('table:first');
|
||||
var checked = $(this).prop('checked');
|
||||
table.find('tbody tr').each(function() {
|
||||
$(this).find('td.checkbox input[type="checkbox"]').prop('checked', checked);
|
||||
});
|
||||
});
|
||||
|
||||
$('body').on('click', 'div.dialog button.close', function() {
|
||||
var dialog = $(this).parents('div.dialog:first');
|
||||
dialog.dialog('close');
|
||||
});
|
||||
|
||||
});
|