diff --git a/tailbone/static/css/base.css b/tailbone/static/css/base.css
index 689fb000..0fa02dbb 100644
--- a/tailbone/static/css/base.css
+++ b/tailbone/static/css/base.css
@@ -1,122 +1,14 @@
-/******************************
- * General
- ******************************/
-
-* {
- margin: 0px;
-}
-
-body {
- font-family: Verdana, Arial, sans-serif;
- font-size: 11pt;
-}
-
-a {
- color: #0972a5;
- text-decoration: none;
-}
-
-a:hover {
- text-decoration: underline;
-}
-
-h1 {
- margin-bottom: 15px;
-}
-
-h2 {
- font-size: 12pt;
- margin: 20px auto 10px auto;
-}
-
-li {
- line-height: 2em;
-}
-
-p {
- margin-bottom: 5px;
-}
-
-.left {
- float: left;
- text-align: left;
-}
-
-.right {
- text-align: right;
-}
-
-.wrapper {
- overflow: auto;
-}
-
-div.buttons {
- clear: both;
- margin-top: 10px;
-}
-
-div.dialog {
- display: none;
-}
-
-div.flash-message {
- background-color: #dddddd;
- margin-bottom: 8px;
- padding: 3px;
-}
-
-div.flash-messages div.ui-state-highlight {
- padding: .3em;
- margin-bottom: 8px;
-}
-
-div.error-messages div.ui-state-error {
- padding: .3em;
- margin-bottom: 8px;
-}
-
-.flash-messages,
-.error-messages {
- margin: 0.5em 0 0 0;
-}
-
-ul.error {
- color: #dd6666;
- font-weight: bold;
- padding: 0px;
-}
-
-ul.error li {
- list-style-type: none;
-}
-
-pre.is-family-sans-serif {
- background-color: white;
- font-family: Verdana, Arial, sans-serif;
- font-size: 11pt;
- padding: 1em;
-}
-
-/******************************
- * jQuery UI tweaks
- ******************************/
-
-ul.ui-menu {
- max-height: 30em;
-}
-
/******************************
* tweaks for root user
******************************/
-.menubar .root-user .ui-button-text,
-.menubar .root-user.ui-menu-item a {
+.navbar .navbar-end .navbar-link.root-user,
+.navbar .navbar-end .navbar-link.root-user:hover,
+.navbar .navbar-end .navbar-link.root-user.is_active,
+.navbar .navbar-end .navbar-item.root-user,
+.navbar .navbar-end .navbar-item.root-user:hover,
+.navbar .navbar-end .navbar-item.root-user.is_active {
background-color: red;
- color: black;
font-weight: bold;
}
-
-.menubar .root-user.ui-menu-item a {
- padding-left: 1em;
-}
diff --git a/tailbone/static/css/filters.css b/tailbone/static/css/filters.css
index c4d59025..6deff7b0 100644
--- a/tailbone/static/css/filters.css
+++ b/tailbone/static/css/filters.css
@@ -1,28 +1,22 @@
/******************************
- * Filters
+ * Grid Filters
******************************/
-div.filters form {
- margin-bottom: 10px;
+.filters .filter {
+ margin-bottom: 0.5rem;
}
-div.filters div.filter {
- margin-bottom: 10px;
+.filters .filter-fieldname .field,
+.filters .filter-fieldname .field label {
+ width: 100%;
}
-div.filters div.filter label {
- margin-right: 8px;
+.filters .filter-fieldname .field label {
+ justify-content: left;
}
-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;
+.filters .filter-verb .select,
+.filters .filter-verb .select select {
+ width: 100%;
}
diff --git a/tailbone/static/css/forms.css b/tailbone/static/css/forms.css
index 42364b14..de4b1ebe 100644
--- a/tailbone/static/css/forms.css
+++ b/tailbone/static/css/forms.css
@@ -1,34 +1,37 @@
/******************************
- * Form Wrapper
+ * forms
******************************/
-div.form-wrapper {
- overflow: auto;
-}
-
-
-/******************************
- * Forms
- ******************************/
-
-div.form,
-div.fieldset-form,
-div.fieldset {
- clear: left;
- float: left;
- margin-top: 10px;
-}
-
+/* note that this should only apply to "normal" primary forms */
+/* TODO: replace this with bulma equivalent */
.form {
padding-left: 5em;
}
+/* note that this should only apply to "normal" primary forms */
+.form-wrapper .form .field.is-horizontal .field-label .label {
+ text-align: left;
+ white-space: nowrap;
+ width: 18em;
+}
+
+/* note that this should only apply to "normal" primary forms */
+.form-wrapper .form .field.is-horizontal .field-body {
+ min-width: 30em;
+}
+
+/* note that this should only apply to "normal" primary forms */
+.form-wrapper .form .field.is-horizontal .field-body .select,
+.form-wrapper .form .field.is-horizontal .field-body .select select {
+ width: 100%;
+}
/******************************
- * Fieldsets
+ * field-wrappers
******************************/
+/* TODO: replace this with bulma equivalent */
.field-wrapper {
clear: both;
min-height: 30px;
@@ -36,16 +39,12 @@ div.fieldset {
margin: 15px;
}
-.field-wrapper.with-error {
- background-color: #ddcccc;
- border: 2px solid #dd6666;
- padding-bottom: 1em;
-}
-
+/* TODO: replace this with bulma equivalent */
.field-wrapper .field-row {
display: table-row;
}
+/* TODO: replace this with bulma equivalent */
.field-wrapper label {
display: table-cell;
vertical-align: top;
@@ -55,47 +54,8 @@ div.fieldset {
white-space: nowrap;
}
-.field-wrapper.with-error label {
- padding-left: 1em;
-}
-
-.field-wrapper .field-error {
- padding: 1em 0 0.5em 1em;
-}
-
-.field-wrapper .field-error .error-msg {
- color: #dd6666;
- font-weight: bold;
-}
-
+/* TODO: replace this with bulma equivalent */
.field-wrapper .field {
display: table-cell;
line-height: 25px;
}
-
-.field-wrapper .field input[type=text],
-.field-wrapper .field input[type=password],
-.field-wrapper .field select,
-.field-wrapper .field textarea {
- width: 320px;
-}
-
-label input[type="checkbox"],
-label input[type="radio"] {
- margin-right: 0.5em;
-}
-
-.field ul {
- margin: 0px;
- padding-left: 15px;
-}
-
-
-/******************************
- * Buttons
- ******************************/
-
-div.buttons {
- clear: both;
- margin: 10px 0px;
-}
diff --git a/tailbone/static/css/grids.css b/tailbone/static/css/grids.css
index 3725c8e3..da5814c4 100644
--- a/tailbone/static/css/grids.css
+++ b/tailbone/static/css/grids.css
@@ -261,6 +261,10 @@
* main actions
******************************/
+a.grid-action {
+ white-space: nowrap;
+}
+
.grid .actions {
width: 1px;
}
diff --git a/tailbone/static/themes/falafel/css/grids.rowstatus.css b/tailbone/static/css/grids.rowstatus.css
similarity index 100%
rename from tailbone/static/themes/falafel/css/grids.rowstatus.css
rename to tailbone/static/css/grids.rowstatus.css
diff --git a/tailbone/static/css/jquery.loadmask.css b/tailbone/static/css/jquery.loadmask.css
deleted file mode 100644
index 6aa1caa1..00000000
--- a/tailbone/static/css/jquery.loadmask.css
+++ /dev/null
@@ -1,40 +0,0 @@
-.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;
-}
\ No newline at end of file
diff --git a/tailbone/static/css/jquery.ui.menubar.css b/tailbone/static/css/jquery.ui.menubar.css
deleted file mode 100644
index 8b175f28..00000000
--- a/tailbone/static/css/jquery.ui.menubar.css
+++ /dev/null
@@ -1,15 +0,0 @@
-/*
- * 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; }
diff --git a/tailbone/static/css/jquery.ui.tailbone.css b/tailbone/static/css/jquery.ui.tailbone.css
deleted file mode 100644
index b6ce1023..00000000
--- a/tailbone/static/css/jquery.ui.tailbone.css
+++ /dev/null
@@ -1,14 +0,0 @@
-
-/**********************************************************************
- * jquery.ui.tailbone.css
- *
- * jQuery UI tweaks for Tailbone
- **********************************************************************/
-
-.ui-widget {
- font-size: 1em;
-}
-
-.ui-menu-item a {
- display: block;
-}
diff --git a/tailbone/static/css/jquery.ui.timepicker.css b/tailbone/static/css/jquery.ui.timepicker.css
deleted file mode 100644
index b5930fb7..00000000
--- a/tailbone/static/css/jquery.ui.timepicker.css
+++ /dev/null
@@ -1,57 +0,0 @@
-/*
- * Timepicker stylesheet
- * Highly inspired from datepicker
- * FG - Nov 2010 - Web3R
- *
- * version 0.0.3 : Fixed some settings, more dynamic
- * version 0.0.4 : Removed width:100% on tables
- * version 0.1.1 : set width 0 on tables to fix an ie6 bug
- */
-
-.ui-timepicker-inline { display: inline; }
-
-#ui-timepicker-div { padding: 0.2em; }
-.ui-timepicker-table { display: inline-table; width: 0; }
-.ui-timepicker-table table { margin:0.15em 0 0 0; border-collapse: collapse; }
-
-.ui-timepicker-hours, .ui-timepicker-minutes { padding: 0.2em; }
-
-.ui-timepicker-table .ui-timepicker-title { line-height: 1.8em; text-align: center; }
-.ui-timepicker-table td { padding: 0.1em; width: 2.2em; }
-.ui-timepicker-table th.periods { padding: 0.1em; width: 2.2em; }
-
-/* span for disabled cells */
-.ui-timepicker-table td span {
- display:block;
- padding:0.2em 0.3em 0.2em 0.5em;
- width: 1.2em;
-
- text-align:right;
- text-decoration:none;
-}
-/* anchors for clickable cells */
-.ui-timepicker-table td a {
- display:block;
- padding:0.2em 0.3em 0.2em 0.5em;
- width: 1.2em;
- cursor: pointer;
- text-align:right;
- text-decoration:none;
-}
-
-
-/* buttons and button pane styling */
-.ui-timepicker .ui-timepicker-buttonpane {
- background-image: none; margin: .7em 0 0 0; padding:0 .2em; border-left: 0; border-right: 0; border-bottom: 0;
-}
-.ui-timepicker .ui-timepicker-buttonpane button { margin: .5em .2em .4em; cursor: pointer; padding: .2em .6em .3em .6em; width:auto; overflow:visible; }
-/* The close button */
-.ui-timepicker .ui-timepicker-close { float: right }
-
-/* the now button */
-.ui-timepicker .ui-timepicker-now { float: left; }
-
-/* the deselect button */
-.ui-timepicker .ui-timepicker-deselect { float: left; }
-
-
diff --git a/tailbone/static/css/layout.css b/tailbone/static/css/layout.css
index 6c1b926f..cc4d0015 100644
--- a/tailbone/static/css/layout.css
+++ b/tailbone/static/css/layout.css
@@ -1,152 +1,90 @@
/******************************
- * Main Layout
+ * main layout
******************************/
-html, body, #body-wrapper {
- height: 100%;
-}
-
-body > #body-wrapper {
- height: auto;
- min-height: 100%;
-}
-
-#body-wrapper {
- margin: 0 1em;
- width: auto;
-}
-
-#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 div.login {
- float: right;
-}
-
-/* new stuff from 'better' theme begins here */
-
-header .global {
- background-color: #eaeaea;
- height: 60px;
-}
-
-header .global a.home,
-header .global a.global,
-header .global span.global {
- display: block;
- float: left;
- font-size: 2em;
- font-weight: bold;
- line-height: 60px;
- margin-left: 10px;
-}
-
-header .global a.home img {
- display: block;
- float: left;
- padding: 5px 5px 5px 30px;
-}
-
-header .global .grid-nav {
- display: inline-block;
- font-size: 16px;
- font-weight: bold;
- line-height: 60px;
- margin-left: 5em;
-}
-
-header .global .grid-nav .ui-button,
-header .global .grid-nav span.viewing {
- margin-left: 1em;
-}
-
-header .global .feedback {
- float: right;
- line-height: 60px;
- margin-right: 1em;
-}
-
-header .global .after-feedback {
- float: right;
- line-height: 60px;
- margin-right: 1em;
-}
-
-header .page {
- border-bottom: 1px solid lightgrey;
- padding: 0.5em;
-}
-
-header .page h1 {
- margin: 0;
- padding: 0 0 0 0.5em;
-}
-
-/******************************
- * Logo
- ******************************/
-
-#logo {
- display: block;
- margin: 40px auto;
-}
-
-
-/****************************************
- * content
- ****************************************/
-
-body > #body-wrapper {
- margin: 0px;
- position: relative;
+body {
+ display: flex;
+ flex-direction: column;
+ min-height: 100vh;
}
.content-wrapper {
- height: 100%;
- padding-bottom: 30px;
+ display: flex;
+ flex: 1;
+ flex-direction: column;
+ justify-content: space-between;
}
-#scrollpane {
- height: 100%;
+
+/******************************
+ * header
+ ******************************/
+
+/* this is the one in the very top left of screen, next to logo and linked to
+the home page */
+#global-header-title {
+ margin-left: 0.3rem;
}
-#scrollpane .inner-content {
- padding: 0 0.5em 0.5em 0.5em;
+header .level {
+ /* TODO: not sure what this 60px was supposed to do? but it broke the */
+ /* styles for the feedback dialog, so disabled it is.
+ /* height: 60px; */
+ /* line-height: 60px; */
+ padding-left: 0.5em;
+ padding-right: 0.5em;
}
+header .level #header-logo {
+ display: inline-block;
+}
+
+header .level .global-title,
+header .level-left .global-title {
+ font-size: 2em;
+ font-weight: bold;
+}
+
+/* indent nested menu items a bit */
+header .navbar-item.nested {
+ padding-left: 2.5rem;
+}
+
+header span.header-text {
+ font-size: 2em;
+ font-weight: bold;
+ margin-right: 10px;
+}
+
+header .level .theme-picker {
+ display: inline-flex;
+}
+
+#content-title {
+ padding: 0.3rem;
+}
+
+#content-title h1 {
+ font-size: 2rem;
+ margin-left: 1rem;
+}
+
+/******************************
+ * content
+ ******************************/
+
+#page-body {
+ padding: 0.4em;
+}
/******************************
* context menu
******************************/
#context-menu {
- list-style-type: none;
- margin: 0.5em;
+ margin-bottom: 1em;
+ margin-left: 1em;
text-align: right;
white-space: nowrap;
}
@@ -155,11 +93,19 @@ body > #body-wrapper {
* "object helper" panel
******************************/
+.object-helpers .panel-heading {
+ white-space: nowrap;
+}
+
+.object-helpers a {
+ white-space: nowrap;
+}
+
.object-helper {
border: 1px solid black;
margin: 1em;
padding: 1em;
- min-width: 20em;
+ width: 20em;
}
.object-helper-content {
@@ -167,87 +113,38 @@ body > #body-wrapper {
}
/******************************
- * Panels
+ * markdown
******************************/
-.panel,
-.panel-grid {
- border-left: 1px solid Black;
- margin-bottom: 1em;
+.rendered-markdown p,
+.rendered-markdown ul {
+ margin-bottom: 1rem;
}
-.panel {
- border-bottom: 1px solid Black;
- border-right: 1px solid Black;
- padding: 0px;
+.rendered-markdown .codehilite {
+ margin-bottom: 2rem;
}
-.panel h2,
-.panel-grid h2 {
- border-bottom: 1px solid Black;
- border-top: 1px solid Black;
- padding: 5px;
- margin: 0px;
+/******************************
+ * fix datepicker within modals
+ * TODO: someday this may not be necessary? cf.
+ * https://github.com/buefy/buefy/issues/292#issuecomment-347365637
+ ******************************/
+
+.modal .animation-content .modal-card {
+ overflow: visible !important;
}
-.panel-grid h2 {
- border-right: 1px solid Black;
+.modal-card-body {
+ overflow: visible !important;
}
-.panel-body {
- overflow: auto;
- padding: 5px;
-}
-
-/****************************************
- * footer
- ****************************************/
-
-#footer {
- border-top: 1px solid lightgray;
- bottom: 0;
- font-size: 9pt;
- height: 20px;
- left: 0;
- line-height: 20px;
- margin: 0;
- position: absolute;
- width: 100%;
-}
/******************************
* feedback
******************************/
-#feedback-dialog {
- display: none;
-}
-
-#feedback-dialog p {
- margin-top: 1em;
-}
-
-#feedback-dialog .red {
+.feedback-dialog .red {
color: red;
font-weight: bold;
}
-
-#feedback-dialog .field-wrapper {
- margin-top: 1em;
- padding: 0;
-}
-
-#feedback-dialog .field {
- margin-bottom: 0;
- margin-top: 0.5em;
-}
-
-#feedback-dialog .referrer .field {
- clear: both;
- float: none;
- margin-top: 1em;
-}
-
-#feedback-dialog textarea {
- width: auto;
-}
diff --git a/tailbone/static/js/jquery.ui.tailbone.js b/tailbone/static/js/jquery.ui.tailbone.js
deleted file mode 100644
index 06904e59..00000000
--- a/tailbone/static/js/jquery.ui.tailbone.js
+++ /dev/null
@@ -1,452 +0,0 @@
-
-/**********************************************************************
- * jQuery UI plugins for Tailbone
- **********************************************************************/
-
-/**********************************************************************
- * gridcore plugin
- **********************************************************************/
-
-(function($) {
-
- $.widget('tailbone.gridcore', {
-
- _create: function() {
-
- var that = this;
-
- // Add hover highlight effect to grid rows during mouse-over.
- // this.element.on('mouseenter', 'tbody tr:not(.header)', function() {
- this.element.on('mouseenter', 'tr:not(.header)', function() {
- $(this).addClass('hovering');
- });
- // this.element.on('mouseleave', 'tbody tr:not(.header)', function() {
- this.element.on('mouseleave', 'tr:not(.header)', function() {
- $(this).removeClass('hovering');
- });
-
- // do some extra stuff for grids with checkboxes
-
- // mark rows selected on page load, as needed
- this.element.find('tr:not(.header) td.checkbox :checkbox:checked').each(function() {
- $(this).parents('tr:first').addClass('selected');
- });
-
- // (un-)check all rows when clicking check-all box in header
- if (this.element.find('tr.header td.checkbox :checkbox').length) {
- this.element.on('click', 'tr.header td.checkbox :checkbox', function() {
- var checked = $(this).prop('checked');
- var rows = that.element.find('tr:not(.header)');
- rows.find('td.checkbox :checkbox').prop('checked', checked);
- if (checked) {
- rows.addClass('selected');
- } else {
- rows.removeClass('selected');
- }
- that.element.trigger('gridchecked', that.count_selected());
- });
- }
-
- // when row with checkbox is clicked, toggle selected status,
- // unless clicking checkbox (since that already toggles it) or a
- // link (since that does something completely different)
- this.element.on('click', 'tr:not(.header)', function(event) {
- var el = $(event.target);
- if (!el.is('a') && !el.is(':checkbox')) {
- $(this).find('td.checkbox :checkbox').click();
- }
- });
-
- this.element.on('change', 'tr:not(.header) td.checkbox :checkbox', function() {
- if (this.checked) {
- $(this).parents('tr:first').addClass('selected');
- } else {
- $(this).parents('tr:first').removeClass('selected');
- }
- that.element.trigger('gridchecked', that.count_selected());
- });
-
- // Show 'more' actions when user hovers over 'more' link.
- this.element.on('mouseenter', '.actions a.more', function() {
- that.element.find('.actions div.more').hide();
- $(this).siblings('div.more')
- .show()
- .position({my: 'left-5 top-4', at: 'left top', of: $(this)});
- });
- this.element.on('mouseleave', '.actions div.more', function() {
- $(this).hide();
- });
-
- // Add speed bump for "Delete Row" action, if grid is so configured.
- if (this.element.data('delete-speedbump')) {
- this.element.on('click', 'tr:not(.header) .actions a.delete', function() {
- return confirm("Are you sure you wish to delete this object?");
- });
- }
- },
-
- count_selected: function() {
- return this.element.find('tr:not(.header) td.checkbox :checkbox:checked').length;
- },
-
- // TODO: deprecate / remove this?
- count_checked: function() {
- return this.count_selected();
- },
-
- selected_rows: function() {
- return this.element.find('tr:not(.header) td.checkbox :checkbox:checked').parents('tr:first');
- },
-
- all_uuids: function() {
- var uuids = [];
- this.element.find('tr:not(.header)').each(function() {
- uuids.push($(this).data('uuid'));
- });
- return uuids;
- },
-
- selected_uuids: function() {
- var uuids = [];
- this.element.find('tr:not(.header) td.checkbox :checkbox:checked').each(function() {
- uuids.push($(this).parents('tr:first').data('uuid'));
- });
- return uuids;
- }
-
- });
-
-})( jQuery );
-
-
-/**********************************************************************
- * gridwrapper plugin
- **********************************************************************/
-
-(function($) {
-
- $.widget('tailbone.gridwrapper', {
-
- _create: function() {
-
- var that = this;
-
- // Snag some element references.
- this.filters = this.element.find('.newfilters');
- this.filters_form = this.filters.find('form');
- this.add_filter = this.filters.find('#add-filter');
- this.apply_filters = this.filters.find('#apply-filters');
- this.default_filters = this.filters.find('#default-filters');
- this.clear_filters = this.filters.find('#clear-filters');
- this.save_defaults = this.filters.find('#save-defaults');
- this.grid = this.element.find('.grid');
-
- // add standard grid behavior
- this.grid.gridcore();
-
- // Enhance filters etc.
- this.filters.find('.filter').gridfilter();
- this.apply_filters.button('option', 'icons', {primary: 'ui-icon-search'});
- this.default_filters.button('option', 'icons', {primary: 'ui-icon-home'});
- this.clear_filters.button('option', 'icons', {primary: 'ui-icon-trash'});
- this.save_defaults.button('option', 'icons', {primary: 'ui-icon-disk'});
- if (! this.filters.find('.active:checked').length) {
- this.apply_filters.button('disable');
- }
- this.add_filter.selectmenu({
- width: '15em',
-
- // Initially disabled if contains no enabled filter options.
- disabled: this.add_filter.find('option:enabled').length == 1,
-
- // When add-filter choice is made, show/focus new filter value input,
- // and maybe hide the add-filter selection or show the apply button.
- change: function (event, ui) {
- var filter = that.filters.find('#filter-' + ui.item.value);
- var select = $(this);
- var option = ui.item.element;
- filter.gridfilter('active', true);
- filter.gridfilter('focus');
- select.val('');
- option.attr('disabled', 'disabled');
- select.selectmenu('refresh');
- if (select.find('option:enabled').length == 1) { // prompt is always enabled
- select.selectmenu('disable');
- }
- that.apply_filters.button('enable');
- }
- });
-
- this.add_filter.on('selectmenuopen', function(event, ui) {
- show_all_options($(this));
- });
-
- // Intercept filters form submittal, and submit via AJAX instead.
- this.filters_form.on('submit', function() {
- var settings = {filter: true, partial: true};
- if (that.filters_form.find('input[name="save-current-filters-as-defaults"]').val() == 'true') {
- settings['save-current-filters-as-defaults'] = true;
- }
- that.filters.find('.filter').each(function() {
-
- // currently active filters will be included in form data
- if ($(this).gridfilter('active')) {
- settings[$(this).data('key')] = $(this).gridfilter('value');
- settings[$(this).data('key') + '.verb'] = $(this).gridfilter('verb');
-
- // others will be hidden from view
- } else {
- $(this).gridfilter('hide');
- }
- });
-
- // if no filters are visible, disable submit button
- if (! that.filters.find('.filter:visible').length) {
- that.apply_filters.button('disable');
- }
-
- // okay, submit filters to server and refresh grid
- that.refresh(settings);
- return false;
- });
-
- // When user clicks Default Filters button, refresh page with
- // instructions for the server to reset filters to default settings.
- this.default_filters.click(function() {
- that.filters_form.off('submit');
- that.filters_form.find('input[name="reset-to-default-filters"]').val('true');
- that.element.mask("Refreshing data...");
- that.filters_form.get(0).submit();
- });
-
- // When user clicks Save Defaults button, refresh the grid as with
- // Apply Filters, but add an instruction for the server to save
- // current settings as defaults for the user.
- this.save_defaults.click(function() {
- that.filters_form.find('input[name="save-current-filters-as-defaults"]').val('true');
- that.filters_form.submit();
- that.filters_form.find('input[name="save-current-filters-as-defaults"]').val('false');
- });
-
- // When user clicks Clear Filters button, deactivate all filters
- // and refresh the grid.
- this.clear_filters.click(function() {
- that.filters.find('.filter').each(function() {
- if ($(this).gridfilter('active')) {
- $(this).gridfilter('active', false);
- }
- });
- that.filters_form.submit();
- });
-
- // Refresh data when user clicks a sortable column header.
- this.element.on('click', 'tr.header a', function() {
- var td = $(this).parent();
- var data = {
- sortkey: $(this).data('sortkey'),
- sortdir: (td.hasClass('asc')) ? 'desc' : 'asc',
- page: 1,
- partial: true
- };
- that.refresh(data);
- return false;
- });
-
- // Refresh data when user chooses a new page size setting.
- this.element.on('change', '.pager #pagesize', function() {
- var settings = {
- partial: true,
- pagesize: $(this).val()
- };
- that.refresh(settings);
- });
-
- // Refresh data when user clicks a pager link.
- this.element.on('click', '.pager a', function() {
- that.refresh(this.search.substring(1)); // remove leading '?'
- return false;
- });
- },
-
- // Refreshes the visible data within the grid, according to the given settings.
- refresh: function(settings) {
- var that = this;
- this.element.mask("Refreshing data...");
- $.get(this.grid.data('url'), settings, function(data) {
- that.grid.replaceWith(data);
- that.grid = that.element.find('.grid');
- that.grid.gridcore();
- that.element.unmask();
- });
- },
-
- results_count: function(as_text) {
- var count = null;
- var match = /showing \d+ thru \d+ of (\S+)/.exec(this.element.find('.pager .showing').text());
- if (match) {
- count = match[1];
- if (!as_text) {
- count = parseInt(count, 10);
- }
- }
- return count;
- },
-
- all_uuids: function() {
- return this.grid.gridcore('all_uuids');
- },
-
- selected_uuids: function() {
- return this.grid.gridcore('selected_uuids');
- }
-
- });
-
-})( jQuery );
-
-
-/**********************************************************************
- * gridfilter plugin
- **********************************************************************/
-
-(function($) {
-
- $.widget('tailbone.gridfilter', {
-
- _create: function() {
-
- var that = this;
-
- // Track down some important elements.
- this.checkbox = this.element.find('input[name$="-active"]');
- this.label = this.element.find('label');
- this.inputs = this.element.find('.inputs');
- this.add_filter = this.element.parents('.grid-wrapper').find('#add-filter');
-
- // Hide the checkbox and label, and add button for toggling active status.
- this.checkbox.addClass('ui-helper-hidden-accessible');
- this.label.hide();
- this.activebutton = $(' ')
- .insertAfter(this.label)
- .text(this.label.text())
- .button({
- icons: {primary: 'ui-icon-blank'}
- });
-
- // Enhance verb dropdown as selectmenu.
- this.verb_select = this.inputs.find('.verb');
- this.valueless_verbs = {};
- $.each(this.verb_select.data('hide-value-for').split(' '), function(index, value) {
- that.valueless_verbs[value] = true;
- });
- this.verb_select.selectmenu({
- width: '15em',
- change: function(event, ui) {
- if (ui.item.value in that.valueless_verbs) {
- that.inputs.find('.value').hide();
- } else {
- that.inputs.find('.value').show();
- that.focus();
- that.select();
- }
- }
- });
-
- this.verb_select.on('selectmenuopen', function(event, ui) {
- show_all_options($(this));
- });
-
- // Enhance any date values with datepicker widget.
- this.inputs.find('.value input[data-datepicker="true"]').datepicker({
- dateFormat: 'yy-mm-dd',
- changeYear: true,
- changeMonth: true
- });
-
- // Enhance any choice/dropdown values with selectmenu.
- this.inputs.find('.value select').selectmenu({
- // provide sane width for value dropdown
- width: '15em'
- });
-
- this.inputs.find('.value select').on('selectmenuopen', function(event, ui) {
- show_all_options($(this));
- });
-
- // Listen for button click, to keep checkbox in sync.
- this._on(this.activebutton, {
- click: function(e) {
- var checked = !this.checkbox.is(':checked');
- this.checkbox.prop('checked', checked);
- this.refresh();
- if (checked) {
- this.focus();
- }
- }
- });
-
- // Update the initial state of the button according to checkbox.
- this.refresh();
- },
-
- refresh: function() {
- if (this.checkbox.is(':checked')) {
- this.activebutton.button('option', 'icons', {primary: 'ui-icon-check'});
- if (this.verb() in this.valueless_verbs) {
- this.inputs.find('.value').hide();
- } else {
- this.inputs.find('.value').show();
- }
- this.inputs.show();
- } else {
- this.activebutton.button('option', 'icons', {primary: 'ui-icon-blank'});
- this.inputs.hide();
- }
- },
-
- active: function(value) {
- if (value === undefined) {
- return this.checkbox.is(':checked');
- }
- if (value) {
- if (!this.checkbox.is(':checked')) {
- this.checkbox.prop('checked', true);
- this.refresh();
- this.element.show();
- }
- } else if (this.checkbox.is(':checked')) {
- this.checkbox.prop('checked', false);
- this.refresh();
- }
- },
-
- hide: function() {
- this.active(false);
- this.element.hide();
- var option = this.add_filter.find('option[value="' + this.element.data('key') + '"]');
- option.attr('disabled', false);
- if (this.add_filter.selectmenu('option', 'disabled')) {
- this.add_filter.selectmenu('enable');
- }
- this.add_filter.selectmenu('refresh');
- },
-
- focus: function() {
- this.inputs.find('.value input').focus();
- },
-
- select: function() {
- this.inputs.find('.value input').select();
- },
-
- value: function() {
- return this.inputs.find('.value input, .value select').val();
- },
-
- verb: function() {
- return this.inputs.find('.verb').val();
- }
-
- });
-
-})( jQuery );
diff --git a/tailbone/static/js/lib/jquery.loadmask.min.js b/tailbone/static/js/lib/jquery.loadmask.min.js
deleted file mode 100644
index d77373c8..00000000
--- a/tailbone/static/js/lib/jquery.loadmask.min.js
+++ /dev/null
@@ -1,10 +0,0 @@
-/**
- * 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('
');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('
');b.append(""+c+"
");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);
\ No newline at end of file
diff --git a/tailbone/static/js/lib/jquery.ui.menubar.js b/tailbone/static/js/lib/jquery.ui.menubar.js
deleted file mode 100644
index a1559091..00000000
--- a/tailbone/static/js/lib/jquery.ui.menubar.js
+++ /dev/null
@@ -1,331 +0,0 @@
-/*
- * 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( " " );
- 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( " " );
-
- 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
- // TODO: heh well given the above comment i'm not sure what the
- // implications might be for disabling the focus() call..but it
- // messes with text input focus in undesirable ways..so disable it
- // we will..until we know why we shouldn't
- // .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 ));
diff --git a/tailbone/static/js/lib/jquery.ui.timepicker.js b/tailbone/static/js/lib/jquery.ui.timepicker.js
deleted file mode 100644
index d8a0cfb7..00000000
--- a/tailbone/static/js/lib/jquery.ui.timepicker.js
+++ /dev/null
@@ -1,1496 +0,0 @@
-/*
- * jQuery UI Timepicker
- *
- * Copyright 2010-2013, Francois Gelinas
- * Dual licensed under the MIT or GPL Version 2 licenses.
- * http://jquery.org/license
- *
- * http://fgelinas.com/code/timepicker
- *
- * Depends:
- * jquery.ui.core.js
- * jquery.ui.position.js (only if position settings are used)
- *
- * Change version 0.1.0 - moved the t-rex up here
- *
- ____
- ___ .-~. /_"-._
- `-._~-. / /_ "~o\ :Y
- \ \ / : \~x. ` ')
- ] Y / | Y< ~-.__j
- / ! _.--~T : l l< /.-~
- / / ____.--~ . ` l /~\ \<|Y
- / / .-~~" /| . ',-~\ \L|
- / / / .^ \ Y~Y \.^>/l_ "--'
- / Y .-"( . l__ j_j l_/ /~_.-~ .
- Y l / \ ) ~~~." / `/"~ / \.__/l_
- | \ _.-" ~-{__ l : l._Z~-.___.--~
- | ~---~ / ~~"---\_ ' __[>
- l . _.^ ___ _>-y~
- \ \ . .-~ .-~ ~>--" /
- \ ~---" / ./ _.-'
- "-.,_____.,_ _.--~\ _.-~
- ~~ ( _} -Row
- `. ~(
- ) \
- /,`--'~\--'~\
- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- ->T-Rex<-
-*/
-
-(function ($) {
-
- $.extend($.ui, { timepicker: { version: "0.3.3"} });
-
- var PROP_NAME = 'timepicker',
- tpuuid = new Date().getTime();
-
- /* Time picker manager.
- Use the singleton instance of this class, $.timepicker, to interact with the time picker.
- Settings for (groups of) time pickers are maintained in an instance object,
- allowing multiple different settings on the same page. */
-
- function Timepicker() {
- this.debug = true; // Change this to true to start debugging
- this._curInst = null; // The current instance in use
- this._disabledInputs = []; // List of time picker inputs that have been disabled
- this._timepickerShowing = false; // True if the popup picker is showing , false if not
- this._inDialog = false; // True if showing within a "dialog", false if not
- this._dialogClass = 'ui-timepicker-dialog'; // The name of the dialog marker class
- this._mainDivId = 'ui-timepicker-div'; // The ID of the main timepicker division
- this._inlineClass = 'ui-timepicker-inline'; // The name of the inline marker class
- this._currentClass = 'ui-timepicker-current'; // The name of the current hour / minutes marker class
- this._dayOverClass = 'ui-timepicker-days-cell-over'; // The name of the day hover marker class
-
- this.regional = []; // Available regional settings, indexed by language code
- this.regional[''] = { // Default regional settings
- hourText: 'Hour', // Display text for hours section
- minuteText: 'Minute', // Display text for minutes link
- amPmText: ['AM', 'PM'], // Display text for AM PM
- closeButtonText: 'Done', // Text for the confirmation button (ok button)
- nowButtonText: 'Now', // Text for the now button
- deselectButtonText: 'Deselect' // Text for the deselect button
- };
- this._defaults = { // Global defaults for all the time picker instances
- showOn: 'focus', // 'focus' for popup on focus,
- // 'button' for trigger button, or 'both' for either (not yet implemented)
- button: null, // 'button' element that will trigger the timepicker
- showAnim: 'fadeIn', // Name of jQuery animation for popup
- showOptions: {}, // Options for enhanced animations
- appendText: '', // Display text following the input box, e.g. showing the format
-
- beforeShow: null, // Define a callback function executed before the timepicker is shown
- onSelect: null, // Define a callback function when a hour / minutes is selected
- onClose: null, // Define a callback function when the timepicker is closed
-
- timeSeparator: ':', // The character to use to separate hours and minutes.
- periodSeparator: ' ', // The character to use to separate the time from the time period.
- showPeriod: false, // Define whether or not to show AM/PM with selected time
- showPeriodLabels: true, // Show the AM/PM labels on the left of the time picker
- showLeadingZero: true, // Define whether or not to show a leading zero for hours < 10. [true/false]
- showMinutesLeadingZero: true, // Define whether or not to show a leading zero for minutes < 10.
- altField: '', // Selector for an alternate field to store selected time into
- defaultTime: 'now', // Used as default time when input field is empty or for inline timePicker
- // (set to 'now' for the current time, '' for no highlighted time)
- myPosition: 'left top', // Position of the dialog relative to the input.
- // see the position utility for more info : http://jqueryui.com/demos/position/
- atPosition: 'left bottom', // Position of the input element to match
- // Note : if the position utility is not loaded, the timepicker will attach left top to left bottom
- //NEW: 2011-02-03
- onHourShow: null, // callback for enabling / disabling on selectable hours ex : function(hour) { return true; }
- onMinuteShow: null, // callback for enabling / disabling on time selection ex : function(hour,minute) { return true; }
-
- hours: {
- starts: 0, // first displayed hour
- ends: 23 // last displayed hour
- },
- minutes: {
- starts: 0, // first displayed minute
- ends: 55, // last displayed minute
- interval: 5, // interval of displayed minutes
- manual: [] // optional extra manual entries for minutes
- },
- rows: 4, // number of rows for the input tables, minimum 2, makes more sense if you use multiple of 2
- // 2011-08-05 0.2.4
- showHours: true, // display the hours section of the dialog
- showMinutes: true, // display the minute section of the dialog
- optionalMinutes: false, // optionally parse inputs of whole hours with minutes omitted
-
- // buttons
- showCloseButton: false, // shows an OK button to confirm the edit
- showNowButton: false, // Shows the 'now' button
- showDeselectButton: false, // Shows the deselect time button
-
- maxTime: {
- hour: null,
- minute: null
- },
- minTime: {
- hour: null,
- minute: null
- }
-
- };
- $.extend(this._defaults, this.regional['']);
-
- this.tpDiv = $('
');
- }
-
- $.extend(Timepicker.prototype, {
- /* Class name added to elements to indicate already configured with a time picker. */
- markerClassName: 'hasTimepicker',
-
- /* Debug logging (if enabled). */
- log: function () {
- if (this.debug)
- console.log.apply('', arguments);
- },
-
- _widgetTimepicker: function () {
- return this.tpDiv;
- },
-
- /* Override the default settings for all instances of the time picker.
- @param settings object - the new settings to use as defaults (anonymous object)
- @return the manager object */
- setDefaults: function (settings) {
- extendRemove(this._defaults, settings || {});
- return this;
- },
-
- /* Attach the time picker to a jQuery selection.
- @param target element - the target input field or division or span
- @param settings object - the new settings to use for this time picker instance (anonymous) */
- _attachTimepicker: function (target, settings) {
- // check for settings on the control itself - in namespace 'time:'
- var inlineSettings = null;
- for (var attrName in this._defaults) {
- var attrValue = target.getAttribute('time:' + attrName);
- if (attrValue) {
- inlineSettings = inlineSettings || {};
- try {
- inlineSettings[attrName] = eval(attrValue);
- } catch (err) {
- inlineSettings[attrName] = attrValue;
- }
- }
- }
- var nodeName = target.nodeName.toLowerCase();
- var inline = (nodeName == 'div' || nodeName == 'span');
-
- if (!target.id) {
- this.uuid += 1;
- target.id = 'tp' + this.uuid;
- }
- var inst = this._newInst($(target), inline);
- inst.settings = $.extend({}, settings || {}, inlineSettings || {});
- if (nodeName == 'input') {
- this._connectTimepicker(target, inst);
- // init inst.hours and inst.minutes from the input value
- this._setTimeFromField(inst);
- } else if (inline) {
- this._inlineTimepicker(target, inst);
- }
-
-
- },
-
- /* Create a new instance object. */
- _newInst: function (target, inline) {
- var id = target[0].id.replace(/([^A-Za-z0-9_-])/g, '\\\\$1'); // escape jQuery meta chars
- return {
- id: id, input: target, // associated target
- inline: inline, // is timepicker inline or not :
- tpDiv: (!inline ? this.tpDiv : // presentation div
- $('
'))
- };
- },
-
- /* Attach the time picker to an input field. */
- _connectTimepicker: function (target, inst) {
- var input = $(target);
- inst.append = $([]);
- inst.trigger = $([]);
- if (input.hasClass(this.markerClassName)) { return; }
- this._attachments(input, inst);
- input.addClass(this.markerClassName).
- keydown(this._doKeyDown).
- keyup(this._doKeyUp).
- bind("setData.timepicker", function (event, key, value) {
- inst.settings[key] = value;
- }).
- bind("getData.timepicker", function (event, key) {
- return this._get(inst, key);
- });
- $.data(target, PROP_NAME, inst);
- },
-
- /* Handle keystrokes. */
- _doKeyDown: function (event) {
- var inst = $.timepicker._getInst(event.target);
- var handled = true;
- inst._keyEvent = true;
- if ($.timepicker._timepickerShowing) {
- switch (event.keyCode) {
- case 9: $.timepicker._hideTimepicker();
- handled = false;
- break; // hide on tab out
- case 13:
- $.timepicker._updateSelectedValue(inst);
- $.timepicker._hideTimepicker();
-
- return false; // don't submit the form
- break; // select the value on enter
- case 27: $.timepicker._hideTimepicker();
- break; // hide on escape
- default: handled = false;
- }
- }
- else if (event.keyCode == 36 && event.ctrlKey) { // display the time picker on ctrl+home
- $.timepicker._showTimepicker(this);
- }
- else {
- handled = false;
- }
- if (handled) {
- event.preventDefault();
- event.stopPropagation();
- }
- },
-
- /* Update selected time on keyUp */
- /* Added verion 0.0.5 */
- _doKeyUp: function (event) {
- var inst = $.timepicker._getInst(event.target);
- $.timepicker._setTimeFromField(inst);
- $.timepicker._updateTimepicker(inst);
- },
-
- /* Make attachments based on settings. */
- _attachments: function (input, inst) {
- var appendText = this._get(inst, 'appendText');
- var isRTL = this._get(inst, 'isRTL');
- if (inst.append) { inst.append.remove(); }
- if (appendText) {
- inst.append = $('' + appendText + ' ');
- input[isRTL ? 'before' : 'after'](inst.append);
- }
- input.unbind('focus.timepicker', this._showTimepicker);
- input.unbind('click.timepicker', this._adjustZIndex);
-
- if (inst.trigger) { inst.trigger.remove(); }
-
- var showOn = this._get(inst, 'showOn');
- if (showOn == 'focus' || showOn == 'both') { // pop-up time picker when in the marked field
- input.bind("focus.timepicker", this._showTimepicker);
- input.bind("click.timepicker", this._adjustZIndex);
- }
- if (showOn == 'button' || showOn == 'both') { // pop-up time picker when 'button' element is clicked
- var button = this._get(inst, 'button');
-
- // Add button if button element is not set
- if(button == null) {
- button = $('... ');
- input.after(button);
- }
-
- $(button).bind("click.timepicker", function () {
- if ($.timepicker._timepickerShowing && $.timepicker._lastInput == input[0]) {
- $.timepicker._hideTimepicker();
- } else if (!inst.input.is(':disabled')) {
- $.timepicker._showTimepicker(input[0]);
- }
- return false;
- });
-
- }
- },
-
-
- /* Attach an inline time picker to a div. */
- _inlineTimepicker: function(target, inst) {
- var divSpan = $(target);
- if (divSpan.hasClass(this.markerClassName))
- return;
- divSpan.addClass(this.markerClassName).append(inst.tpDiv).
- bind("setData.timepicker", function(event, key, value){
- inst.settings[key] = value;
- }).bind("getData.timepicker", function(event, key){
- return this._get(inst, key);
- });
- $.data(target, PROP_NAME, inst);
-
- this._setTimeFromField(inst);
- this._updateTimepicker(inst);
- inst.tpDiv.show();
- },
-
- _adjustZIndex: function(input) {
- input = input.target || input;
- var inst = $.timepicker._getInst(input);
- inst.tpDiv.css('zIndex', $.timepicker._getZIndex(input) +1);
- },
-
- /* Pop-up the time picker for a given input field.
- @param input element - the input field attached to the time picker or
- event - if triggered by focus */
- _showTimepicker: function (input) {
- input = input.target || input;
- if (input.nodeName.toLowerCase() != 'input') { input = $('input', input.parentNode)[0]; } // find from button/image trigger
-
- if ($.timepicker._isDisabledTimepicker(input) || $.timepicker._lastInput == input) { return; } // already here
-
- // fix v 0.0.8 - close current timepicker before showing another one
- $.timepicker._hideTimepicker();
-
- var inst = $.timepicker._getInst(input);
- if ($.timepicker._curInst && $.timepicker._curInst != inst) {
- $.timepicker._curInst.tpDiv.stop(true, true);
- }
- var beforeShow = $.timepicker._get(inst, 'beforeShow');
- extendRemove(inst.settings, (beforeShow ? beforeShow.apply(input, [input, inst]) : {}));
- inst.lastVal = null;
- $.timepicker._lastInput = input;
-
- $.timepicker._setTimeFromField(inst);
-
- // calculate default position
- if ($.timepicker._inDialog) { input.value = ''; } // hide cursor
- if (!$.timepicker._pos) { // position below input
- $.timepicker._pos = $.timepicker._findPos(input);
- $.timepicker._pos[1] += input.offsetHeight; // add the height
- }
- var isFixed = false;
- $(input).parents().each(function () {
- isFixed |= $(this).css('position') == 'fixed';
- return !isFixed;
- });
-
- var offset = { left: $.timepicker._pos[0], top: $.timepicker._pos[1] };
-
- $.timepicker._pos = null;
- // determine sizing offscreen
- inst.tpDiv.css({ position: 'absolute', display: 'block', top: '-1000px' });
- $.timepicker._updateTimepicker(inst);
-
-
- // position with the ui position utility, if loaded
- if ( ( ! inst.inline ) && ( typeof $.ui.position == 'object' ) ) {
- inst.tpDiv.position({
- of: inst.input,
- my: $.timepicker._get( inst, 'myPosition' ),
- at: $.timepicker._get( inst, 'atPosition' ),
- // offset: $( "#offset" ).val(),
- // using: using,
- collision: 'flip'
- });
- var offset = inst.tpDiv.offset();
- $.timepicker._pos = [offset.top, offset.left];
- }
-
-
- // reset clicked state
- inst._hoursClicked = false;
- inst._minutesClicked = false;
-
- // fix width for dynamic number of time pickers
- // and adjust position before showing
- offset = $.timepicker._checkOffset(inst, offset, isFixed);
- inst.tpDiv.css({ position: ($.timepicker._inDialog && $.blockUI ?
- 'static' : (isFixed ? 'fixed' : 'absolute')), display: 'none',
- left: offset.left + 'px', top: offset.top + 'px'
- });
- if ( ! inst.inline ) {
- var showAnim = $.timepicker._get(inst, 'showAnim');
- var duration = $.timepicker._get(inst, 'duration');
-
- var postProcess = function () {
- $.timepicker._timepickerShowing = true;
- var borders = $.timepicker._getBorders(inst.tpDiv);
- inst.tpDiv.find('iframe.ui-timepicker-cover'). // IE6- only
- css({ left: -borders[0], top: -borders[1],
- width: inst.tpDiv.outerWidth(), height: inst.tpDiv.outerHeight()
- });
- };
-
- // Fixed the zIndex problem for real (I hope) - FG - v 0.2.9
- $.timepicker._adjustZIndex(input);
- //inst.tpDiv.css('zIndex', $.timepicker._getZIndex(input) +1);
-
- if ($.effects && $.effects[showAnim]) {
- inst.tpDiv.show(showAnim, $.timepicker._get(inst, 'showOptions'), duration, postProcess);
- }
- else {
- inst.tpDiv.show((showAnim ? duration : null), postProcess);
- }
- if (!showAnim || !duration) { postProcess(); }
- if (inst.input.is(':visible') && !inst.input.is(':disabled')) { inst.input.focus(); }
- $.timepicker._curInst = inst;
- }
- },
-
- // This is an enhanced copy of the zIndex function of UI core 1.8.?? For backward compatibility.
- // Enhancement returns maximum zindex value discovered while traversing parent elements,
- // rather than the first zindex value found. Ensures the timepicker popup will be in front,
- // even in funky scenarios like non-jq dialog containers with large fixed zindex values and
- // nested zindex-influenced elements of their own.
- _getZIndex: function (target) {
- var elem = $(target);
- var maxValue = 0;
- var position, value;
- while (elem.length && elem[0] !== document) {
- position = elem.css("position");
- if (position === "absolute" || position === "relative" || position === "fixed") {
- value = parseInt(elem.css("zIndex"), 10);
- if (!isNaN(value) && value !== 0) {
- if (value > maxValue) { maxValue = value; }
- }
- }
- elem = elem.parent();
- }
-
- return maxValue;
- },
-
- /* Refresh the time picker
- @param target element - The target input field or inline container element. */
- _refreshTimepicker: function(target) {
- var inst = this._getInst(target);
- if (inst) {
- this._updateTimepicker(inst);
- }
- },
-
-
- /* Generate the time picker content. */
- _updateTimepicker: function (inst) {
- inst.tpDiv.empty().append(this._generateHTML(inst));
- this._rebindDialogEvents(inst);
-
- },
-
- _rebindDialogEvents: function (inst) {
- var borders = $.timepicker._getBorders(inst.tpDiv),
- self = this;
- inst.tpDiv
- .find('iframe.ui-timepicker-cover') // IE6- only
- .css({ left: -borders[0], top: -borders[1],
- width: inst.tpDiv.outerWidth(), height: inst.tpDiv.outerHeight()
- })
- .end()
- // after the picker html is appended bind the click & double click events (faster in IE this way
- // then letting the browser interpret the inline events)
- // the binding for the minute cells also exists in _updateMinuteDisplay
- .find('.ui-timepicker-minute-cell')
- .unbind()
- .bind("click", { fromDoubleClick:false }, $.proxy($.timepicker.selectMinutes, this))
- .bind("dblclick", { fromDoubleClick:true }, $.proxy($.timepicker.selectMinutes, this))
- .end()
- .find('.ui-timepicker-hour-cell')
- .unbind()
- .bind("click", { fromDoubleClick:false }, $.proxy($.timepicker.selectHours, this))
- .bind("dblclick", { fromDoubleClick:true }, $.proxy($.timepicker.selectHours, this))
- .end()
- .find('.ui-timepicker td a')
- .unbind()
- .bind('mouseout', function () {
- $(this).removeClass('ui-state-hover');
- if (this.className.indexOf('ui-timepicker-prev') != -1) $(this).removeClass('ui-timepicker-prev-hover');
- if (this.className.indexOf('ui-timepicker-next') != -1) $(this).removeClass('ui-timepicker-next-hover');
- })
- .bind('mouseover', function () {
- if ( ! self._isDisabledTimepicker(inst.inline ? inst.tpDiv.parent()[0] : inst.input[0])) {
- $(this).parents('.ui-timepicker-calendar').find('a').removeClass('ui-state-hover');
- $(this).addClass('ui-state-hover');
- if (this.className.indexOf('ui-timepicker-prev') != -1) $(this).addClass('ui-timepicker-prev-hover');
- if (this.className.indexOf('ui-timepicker-next') != -1) $(this).addClass('ui-timepicker-next-hover');
- }
- })
- .end()
- .find('.' + this._dayOverClass + ' a')
- .trigger('mouseover')
- .end()
- .find('.ui-timepicker-now').bind("click", function(e) {
- $.timepicker.selectNow(e);
- }).end()
- .find('.ui-timepicker-deselect').bind("click",function(e) {
- $.timepicker.deselectTime(e);
- }).end()
- .find('.ui-timepicker-close').bind("click",function(e) {
- $.timepicker._hideTimepicker();
- }).end();
- },
-
- /* Generate the HTML for the current state of the time picker. */
- _generateHTML: function (inst) {
-
- var h, m, row, col, html, hoursHtml, minutesHtml = '',
- showPeriod = (this._get(inst, 'showPeriod') == true),
- showPeriodLabels = (this._get(inst, 'showPeriodLabels') == true),
- showLeadingZero = (this._get(inst, 'showLeadingZero') == true),
- showHours = (this._get(inst, 'showHours') == true),
- showMinutes = (this._get(inst, 'showMinutes') == true),
- amPmText = this._get(inst, 'amPmText'),
- rows = this._get(inst, 'rows'),
- amRows = 0,
- pmRows = 0,
- amItems = 0,
- pmItems = 0,
- amFirstRow = 0,
- pmFirstRow = 0,
- hours = Array(),
- hours_options = this._get(inst, 'hours'),
- hoursPerRow = null,
- hourCounter = 0,
- hourLabel = this._get(inst, 'hourText'),
- showCloseButton = this._get(inst, 'showCloseButton'),
- closeButtonText = this._get(inst, 'closeButtonText'),
- showNowButton = this._get(inst, 'showNowButton'),
- nowButtonText = this._get(inst, 'nowButtonText'),
- showDeselectButton = this._get(inst, 'showDeselectButton'),
- deselectButtonText = this._get(inst, 'deselectButtonText'),
- showButtonPanel = showCloseButton || showNowButton || showDeselectButton;
-
-
-
- // prepare all hours and minutes, makes it easier to distribute by rows
- for (h = hours_options.starts; h <= hours_options.ends; h++) {
- hours.push (h);
- }
- hoursPerRow = Math.ceil(hours.length / rows); // always round up
-
- if (showPeriodLabels) {
- for (hourCounter = 0; hourCounter < hours.length; hourCounter++) {
- if (hours[hourCounter] < 12) {
- amItems++;
- }
- else {
- pmItems++;
- }
- }
- hourCounter = 0;
-
- amRows = Math.floor(amItems / hours.length * rows);
- pmRows = Math.floor(pmItems / hours.length * rows);
-
- // assign the extra row to the period that is more densely populated
- if (rows != amRows + pmRows) {
- // Make sure: AM Has Items and either PM Does Not, AM has no rows yet, or AM is more dense
- if (amItems && (!pmItems || !amRows || (pmRows && amItems / amRows >= pmItems / pmRows))) {
- amRows++;
- } else {
- pmRows++;
- }
- }
- amFirstRow = Math.min(amRows, 1);
- pmFirstRow = amRows + 1;
-
- if (amRows == 0) {
- hoursPerRow = Math.ceil(pmItems / pmRows);
- } else if (pmRows == 0) {
- hoursPerRow = Math.ceil(amItems / amRows);
- } else {
- hoursPerRow = Math.ceil(Math.max(amItems / amRows, pmItems / pmRows));
- }
- }
-
-
- html = '';
-
- if (showHours) {
-
- html += '' +
- '' +
- '';
-
- for (row = 1; row <= rows; row++) {
- html += '';
- // AM
- if (row == amFirstRow && showPeriodLabels) {
- html += '' + amPmText[0] + ' ';
- }
- // PM
- if (row == pmFirstRow && showPeriodLabels) {
- html += '' + amPmText[1] + ' ';
- }
- for (col = 1; col <= hoursPerRow; col++) {
- if (showPeriodLabels && row < pmFirstRow && hours[hourCounter] >= 12) {
- html += this._generateHTMLHourCell(inst, undefined, showPeriod, showLeadingZero);
- } else {
- html += this._generateHTMLHourCell(inst, hours[hourCounter], showPeriod, showLeadingZero);
- hourCounter++;
- }
- }
- html += ' ';
- }
- html += '
' + // Close the hours cells table
- ' '; // Close the Hour td
- }
-
- if (showMinutes) {
- html += '';
- html += this._generateHTMLMinutes(inst);
- html += ' ';
- }
-
- html += ' ';
-
-
- if (showButtonPanel) {
- var buttonPanel = '';
- if (showNowButton) {
- buttonPanel += ''
- + nowButtonText + ' ';
- }
- if (showDeselectButton) {
- buttonPanel += ''
- + deselectButtonText + ' ';
- }
- if (showCloseButton) {
- buttonPanel += ''
- + closeButtonText + ' ';
- }
-
- html += buttonPanel + '
';
- }
- html += '
';
-
- return html;
- },
-
- /* Special function that update the minutes selection in currently visible timepicker
- * called on hour selection when onMinuteShow is defined */
- _updateMinuteDisplay: function (inst) {
- var newHtml = this._generateHTMLMinutes(inst);
- inst.tpDiv.find('td.ui-timepicker-minutes').html(newHtml);
- this._rebindDialogEvents(inst);
- // after the picker html is appended bind the click & double click events (faster in IE this way
- // then letting the browser interpret the inline events)
- // yes I know, duplicate code, sorry
-/* .find('.ui-timepicker-minute-cell')
- .bind("click", { fromDoubleClick:false }, $.proxy($.timepicker.selectMinutes, this))
- .bind("dblclick", { fromDoubleClick:true }, $.proxy($.timepicker.selectMinutes, this));
-*/
-
- },
-
- /*
- * Generate the minutes table
- * This is separated from the _generateHTML function because is can be called separately (when hours changes)
- */
- _generateHTMLMinutes: function (inst) {
-
- var m, row, html = '',
- rows = this._get(inst, 'rows'),
- minutes = Array(),
- minutes_options = this._get(inst, 'minutes'),
- minutesPerRow = null,
- minuteCounter = 0,
- showMinutesLeadingZero = (this._get(inst, 'showMinutesLeadingZero') == true),
- onMinuteShow = this._get(inst, 'onMinuteShow'),
- minuteLabel = this._get(inst, 'minuteText');
-
- if ( ! minutes_options.starts) {
- minutes_options.starts = 0;
- }
- if ( ! minutes_options.ends) {
- minutes_options.ends = 59;
- }
- if ( ! minutes_options.manual) {
- minutes_options.manual = [];
- }
- for (m = minutes_options.starts; m <= minutes_options.ends; m += minutes_options.interval) {
- minutes.push(m);
- }
- for (i = 0; i < minutes_options.manual.length;i++) {
- var currMin = minutes_options.manual[i];
-
- // Validate & filter duplicates of manual minute input
- if (typeof currMin != 'number' || currMin < 0 || currMin > 59 || $.inArray(currMin, minutes) >= 0) {
- continue;
- }
- minutes.push(currMin);
- }
-
- // Sort to get correct order after adding manual minutes
- // Use compare function to sort by number, instead of string (default)
- minutes.sort(function(a, b) {
- return a-b;
- });
-
- minutesPerRow = Math.round(minutes.length / rows + 0.49); // always round up
-
- /*
- * The minutes table
- */
- // if currently selected minute is not enabled, we have a problem and need to select a new minute.
- if (onMinuteShow &&
- (onMinuteShow.apply((inst.input ? inst.input[0] : null), [inst.hours , inst.minutes]) == false) ) {
- // loop minutes and select first available
- for (minuteCounter = 0; minuteCounter < minutes.length; minuteCounter += 1) {
- m = minutes[minuteCounter];
- if (onMinuteShow.apply((inst.input ? inst.input[0] : null), [inst.hours, m])) {
- inst.minutes = m;
- break;
- }
- }
- }
-
-
-
- html += '' +
- '';
-
- minuteCounter = 0;
- for (row = 1; row <= rows; row++) {
- html += '';
- while (minuteCounter < row * minutesPerRow) {
- var m = minutes[minuteCounter];
- var displayText = '';
- if (m !== undefined ) {
- displayText = (m < 10) && showMinutesLeadingZero ? "0" + m.toString() : m.toString();
- }
- html += this._generateHTMLMinuteCell(inst, m, displayText);
- minuteCounter++;
- }
- html += ' ';
- }
-
- html += '
';
-
- return html;
- },
-
- /* Generate the content of a "Hour" cell */
- _generateHTMLHourCell: function (inst, hour, showPeriod, showLeadingZero) {
-
- var displayHour = hour;
- if ((hour > 12) && showPeriod) {
- displayHour = hour - 12;
- }
- if ((displayHour == 0) && showPeriod) {
- displayHour = 12;
- }
- if ((displayHour < 10) && showLeadingZero) {
- displayHour = '0' + displayHour;
- }
-
- var html = "";
- var enabled = true;
- var onHourShow = this._get(inst, 'onHourShow'); //custom callback
- var maxTime = this._get(inst, 'maxTime');
- var minTime = this._get(inst, 'minTime');
-
- if (hour == undefined) {
- html = ' ';
- return html;
- }
-
- if (onHourShow) {
- enabled = onHourShow.apply((inst.input ? inst.input[0] : null), [hour]);
- }
-
- if (enabled) {
- if ( !isNaN(parseInt(maxTime.hour)) && hour > maxTime.hour ) enabled = false;
- if ( !isNaN(parseInt(minTime.hour)) && hour < minTime.hour ) enabled = false;
- }
-
- if (enabled) {
- html = '' +
- '' +
- displayHour.toString() +
- ' ';
- }
- else {
- html =
- '' +
- '' +
- displayHour.toString() +
- ' ' +
- ' ';
- }
- return html;
- },
-
- /* Generate the content of a "Hour" cell */
- _generateHTMLMinuteCell: function (inst, minute, displayText) {
- var html = "";
- var enabled = true;
- var hour = inst.hours;
- var onMinuteShow = this._get(inst, 'onMinuteShow'); //custom callback
- var maxTime = this._get(inst, 'maxTime');
- var minTime = this._get(inst, 'minTime');
-
- if (onMinuteShow) {
- //NEW: 2011-02-03 we should give the hour as a parameter as well!
- enabled = onMinuteShow.apply((inst.input ? inst.input[0] : null), [inst.hours,minute]); //trigger callback
- }
-
- if (minute == undefined) {
- html = ' ';
- return html;
- }
-
- if (enabled && hour !== null) {
- if ( !isNaN(parseInt(maxTime.hour)) && !isNaN(parseInt(maxTime.minute)) && hour >= maxTime.hour && minute > maxTime.minute ) enabled = false;
- if ( !isNaN(parseInt(minTime.hour)) && !isNaN(parseInt(minTime.minute)) && hour <= minTime.hour && minute < minTime.minute ) enabled = false;
- }
-
- if (enabled) {
- html = '' +
- '' +
- displayText +
- ' ';
- }
- else {
-
- html = '' +
- '' +
- displayText +
- ' ' +
- ' ';
- }
- return html;
- },
-
-
- /* Detach a timepicker from its control.
- @param target element - the target input field or division or span */
- _destroyTimepicker: function(target) {
- var $target = $(target);
- var inst = $.data(target, PROP_NAME);
- if (!$target.hasClass(this.markerClassName)) {
- return;
- }
- var nodeName = target.nodeName.toLowerCase();
- $.removeData(target, PROP_NAME);
- if (nodeName == 'input') {
- inst.append.remove();
- inst.trigger.remove();
- $target.removeClass(this.markerClassName)
- .unbind('focus.timepicker', this._showTimepicker)
- .unbind('click.timepicker', this._adjustZIndex);
- } else if (nodeName == 'div' || nodeName == 'span')
- $target.removeClass(this.markerClassName).empty();
- },
-
- /* Enable the date picker to a jQuery selection.
- @param target element - the target input field or division or span */
- _enableTimepicker: function(target) {
- var $target = $(target),
- target_id = $target.attr('id'),
- inst = $.data(target, PROP_NAME);
-
- if (!$target.hasClass(this.markerClassName)) {
- return;
- }
- var nodeName = target.nodeName.toLowerCase();
- if (nodeName == 'input') {
- target.disabled = false;
- var button = this._get(inst, 'button');
- $(button).removeClass('ui-state-disabled').disabled = false;
- inst.trigger.filter('button').
- each(function() { this.disabled = false; }).end();
- }
- else if (nodeName == 'div' || nodeName == 'span') {
- var inline = $target.children('.' + this._inlineClass);
- inline.children().removeClass('ui-state-disabled');
- inline.find('button').each(
- function() { this.disabled = false }
- )
- }
- this._disabledInputs = $.map(this._disabledInputs,
- function(value) { return (value == target_id ? null : value); }); // delete entry
- },
-
- /* Disable the time picker to a jQuery selection.
- @param target element - the target input field or division or span */
- _disableTimepicker: function(target) {
- var $target = $(target);
- var inst = $.data(target, PROP_NAME);
- if (!$target.hasClass(this.markerClassName)) {
- return;
- }
- var nodeName = target.nodeName.toLowerCase();
- if (nodeName == 'input') {
- var button = this._get(inst, 'button');
-
- $(button).addClass('ui-state-disabled').disabled = true;
- target.disabled = true;
-
- inst.trigger.filter('button').
- each(function() { this.disabled = true; }).end();
-
- }
- else if (nodeName == 'div' || nodeName == 'span') {
- var inline = $target.children('.' + this._inlineClass);
- inline.children().addClass('ui-state-disabled');
- inline.find('button').each(
- function() { this.disabled = true }
- )
-
- }
- this._disabledInputs = $.map(this._disabledInputs,
- function(value) { return (value == target ? null : value); }); // delete entry
- this._disabledInputs[this._disabledInputs.length] = $target.attr('id');
- },
-
- /* Is the first field in a jQuery collection disabled as a timepicker?
- @param target_id element - the target input field or division or span
- @return boolean - true if disabled, false if enabled */
- _isDisabledTimepicker: function (target_id) {
- if ( ! target_id) { return false; }
- for (var i = 0; i < this._disabledInputs.length; i++) {
- if (this._disabledInputs[i] == target_id) { return true; }
- }
- return false;
- },
-
- /* Check positioning to remain on screen. */
- _checkOffset: function (inst, offset, isFixed) {
- var tpWidth = inst.tpDiv.outerWidth();
- var tpHeight = inst.tpDiv.outerHeight();
- var inputWidth = inst.input ? inst.input.outerWidth() : 0;
- var inputHeight = inst.input ? inst.input.outerHeight() : 0;
- var viewWidth = document.documentElement.clientWidth + $(document).scrollLeft();
- var viewHeight = document.documentElement.clientHeight + $(document).scrollTop();
-
- offset.left -= (this._get(inst, 'isRTL') ? (tpWidth - inputWidth) : 0);
- offset.left -= (isFixed && offset.left == inst.input.offset().left) ? $(document).scrollLeft() : 0;
- offset.top -= (isFixed && offset.top == (inst.input.offset().top + inputHeight)) ? $(document).scrollTop() : 0;
-
- // now check if timepicker is showing outside window viewport - move to a better place if so.
- offset.left -= Math.min(offset.left, (offset.left + tpWidth > viewWidth && viewWidth > tpWidth) ?
- Math.abs(offset.left + tpWidth - viewWidth) : 0);
- offset.top -= Math.min(offset.top, (offset.top + tpHeight > viewHeight && viewHeight > tpHeight) ?
- Math.abs(tpHeight + inputHeight) : 0);
-
- return offset;
- },
-
- /* Find an object's position on the screen. */
- _findPos: function (obj) {
- var inst = this._getInst(obj);
- var isRTL = this._get(inst, 'isRTL');
- while (obj && (obj.type == 'hidden' || obj.nodeType != 1)) {
- obj = obj[isRTL ? 'previousSibling' : 'nextSibling'];
- }
- var position = $(obj).offset();
- return [position.left, position.top];
- },
-
- /* Retrieve the size of left and top borders for an element.
- @param elem (jQuery object) the element of interest
- @return (number[2]) the left and top borders */
- _getBorders: function (elem) {
- var convert = function (value) {
- return { thin: 1, medium: 2, thick: 3}[value] || value;
- };
- return [parseFloat(convert(elem.css('border-left-width'))),
- parseFloat(convert(elem.css('border-top-width')))];
- },
-
-
- /* Close time picker if clicked elsewhere. */
- _checkExternalClick: function (event) {
- if (!$.timepicker._curInst) { return; }
- var $target = $(event.target);
- if ($target[0].id != $.timepicker._mainDivId &&
- $target.parents('#' + $.timepicker._mainDivId).length == 0 &&
- !$target.hasClass($.timepicker.markerClassName) &&
- !$target.hasClass($.timepicker._triggerClass) &&
- $.timepicker._timepickerShowing && !($.timepicker._inDialog && $.blockUI))
- $.timepicker._hideTimepicker();
- },
-
- /* Hide the time picker from view.
- @param input element - the input field attached to the time picker */
- _hideTimepicker: function (input) {
- var inst = this._curInst;
- if (!inst || (input && inst != $.data(input, PROP_NAME))) { return; }
- if (this._timepickerShowing) {
- var showAnim = this._get(inst, 'showAnim');
- var duration = this._get(inst, 'duration');
- var postProcess = function () {
- $.timepicker._tidyDialog(inst);
- this._curInst = null;
- };
- if ($.effects && $.effects[showAnim]) {
- inst.tpDiv.hide(showAnim, $.timepicker._get(inst, 'showOptions'), duration, postProcess);
- }
- else {
- inst.tpDiv[(showAnim == 'slideDown' ? 'slideUp' :
- (showAnim == 'fadeIn' ? 'fadeOut' : 'hide'))]((showAnim ? duration : null), postProcess);
- }
- if (!showAnim) { postProcess(); }
-
- this._timepickerShowing = false;
-
- this._lastInput = null;
- if (this._inDialog) {
- this._dialogInput.css({ position: 'absolute', left: '0', top: '-100px' });
- if ($.blockUI) {
- $.unblockUI();
- $('body').append(this.tpDiv);
- }
- }
- this._inDialog = false;
-
- var onClose = this._get(inst, 'onClose');
- if (onClose) {
- onClose.apply(
- (inst.input ? inst.input[0] : null),
- [(inst.input ? inst.input.val() : ''), inst]); // trigger custom callback
- }
-
- }
- },
-
-
-
- /* Tidy up after a dialog display. */
- _tidyDialog: function (inst) {
- inst.tpDiv.removeClass(this._dialogClass).unbind('.ui-timepicker');
- },
-
- /* Retrieve the instance data for the target control.
- @param target element - the target input field or division or span
- @return object - the associated instance data
- @throws error if a jQuery problem getting data */
- _getInst: function (target) {
- try {
- return $.data(target, PROP_NAME);
- }
- catch (err) {
- throw 'Missing instance data for this timepicker';
- }
- },
-
- /* Get a setting value, defaulting if necessary. */
- _get: function (inst, name) {
- return inst.settings[name] !== undefined ?
- inst.settings[name] : this._defaults[name];
- },
-
- /* Parse existing time and initialise time picker. */
- _setTimeFromField: function (inst) {
- if (inst.input.val() == inst.lastVal) { return; }
- var defaultTime = this._get(inst, 'defaultTime');
-
- var timeToParse = defaultTime == 'now' ? this._getCurrentTimeRounded(inst) : defaultTime;
- if ((inst.inline == false) && (inst.input.val() != '')) { timeToParse = inst.input.val() }
-
- if (timeToParse instanceof Date) {
- inst.hours = timeToParse.getHours();
- inst.minutes = timeToParse.getMinutes();
- } else {
- var timeVal = inst.lastVal = timeToParse;
- if (timeToParse == '') {
- inst.hours = -1;
- inst.minutes = -1;
- } else {
- var time = this.parseTime(inst, timeVal);
- inst.hours = time.hours;
- inst.minutes = time.minutes;
- }
- }
-
-
- $.timepicker._updateTimepicker(inst);
- },
-
- /* Update or retrieve the settings for an existing time picker.
- @param target element - the target input field or division or span
- @param name object - the new settings to update or
- string - the name of the setting to change or retrieve,
- when retrieving also 'all' for all instance settings or
- 'defaults' for all global defaults
- @param value any - the new value for the setting
- (omit if above is an object or to retrieve a value) */
- _optionTimepicker: function(target, name, value) {
- var inst = this._getInst(target);
- if (arguments.length == 2 && typeof name == 'string') {
- return (name == 'defaults' ? $.extend({}, $.timepicker._defaults) :
- (inst ? (name == 'all' ? $.extend({}, inst.settings) :
- this._get(inst, name)) : null));
- }
- var settings = name || {};
- if (typeof name == 'string') {
- settings = {};
- settings[name] = value;
- }
- if (inst) {
- extendRemove(inst.settings, settings);
- if (this._curInst == inst) {
- this._hideTimepicker();
- this._updateTimepicker(inst);
- }
- if (inst.inline) {
- this._updateTimepicker(inst);
- }
- }
- },
-
-
- /* Set the time for a jQuery selection.
- @param target element - the target input field or division or span
- @param time String - the new time */
- _setTimeTimepicker: function(target, time) {
- var inst = this._getInst(target);
- if (inst) {
- this._setTime(inst, time);
- this._updateTimepicker(inst);
- this._updateAlternate(inst, time);
- }
- },
-
- /* Set the time directly. */
- _setTime: function(inst, time, noChange) {
- var origHours = inst.hours;
- var origMinutes = inst.minutes;
- if (time instanceof Date) {
- inst.hours = time.getHours();
- inst.minutes = time.getMinutes();
- } else {
- var time = this.parseTime(inst, time);
- inst.hours = time.hours;
- inst.minutes = time.minutes;
- }
-
- if ((origHours != inst.hours || origMinutes != inst.minutes) && !noChange) {
- inst.input.trigger('change');
- }
- this._updateTimepicker(inst);
- this._updateSelectedValue(inst);
- },
-
- /* Return the current time, ready to be parsed, rounded to the closest minute by interval */
- _getCurrentTimeRounded: function (inst) {
- var currentTime = new Date(),
- currentMinutes = currentTime.getMinutes(),
- minutes_options = this._get(inst, 'minutes'),
- // round to closest interval
- adjustedMinutes = Math.round(currentMinutes / minutes_options.interval) * minutes_options.interval;
- currentTime.setMinutes(adjustedMinutes);
- return currentTime;
- },
-
- /*
- * Parse a time string into hours and minutes
- */
- parseTime: function (inst, timeVal) {
- var retVal = new Object();
- retVal.hours = -1;
- retVal.minutes = -1;
-
- if(!timeVal)
- return '';
-
- var timeSeparator = this._get(inst, 'timeSeparator'),
- amPmText = this._get(inst, 'amPmText'),
- showHours = this._get(inst, 'showHours'),
- showMinutes = this._get(inst, 'showMinutes'),
- optionalMinutes = this._get(inst, 'optionalMinutes'),
- showPeriod = (this._get(inst, 'showPeriod') == true),
- p = timeVal.indexOf(timeSeparator);
-
- // check if time separator found
- if (p != -1) {
- retVal.hours = parseInt(timeVal.substr(0, p), 10);
- retVal.minutes = parseInt(timeVal.substr(p + 1), 10);
- }
- // check for hours only
- else if ( (showHours) && ( !showMinutes || optionalMinutes ) ) {
- retVal.hours = parseInt(timeVal, 10);
- }
- // check for minutes only
- else if ( ( ! showHours) && (showMinutes) ) {
- retVal.minutes = parseInt(timeVal, 10);
- }
-
- if (showHours) {
- var timeValUpper = timeVal.toUpperCase();
- if ((retVal.hours < 12) && (showPeriod) && (timeValUpper.indexOf(amPmText[1].toUpperCase()) != -1)) {
- retVal.hours += 12;
- }
- // fix for 12 AM
- if ((retVal.hours == 12) && (showPeriod) && (timeValUpper.indexOf(amPmText[0].toUpperCase()) != -1)) {
- retVal.hours = 0;
- }
- }
-
- return retVal;
- },
-
- selectNow: function(event) {
- var id = $(event.target).attr("data-timepicker-instance-id"),
- $target = $(id),
- inst = this._getInst($target[0]);
- //if (!inst || (input && inst != $.data(input, PROP_NAME))) { return; }
- var currentTime = new Date();
- inst.hours = currentTime.getHours();
- inst.minutes = currentTime.getMinutes();
- this._updateSelectedValue(inst);
- this._updateTimepicker(inst);
- this._hideTimepicker();
- },
-
- deselectTime: function(event) {
- var id = $(event.target).attr("data-timepicker-instance-id"),
- $target = $(id),
- inst = this._getInst($target[0]);
- inst.hours = -1;
- inst.minutes = -1;
- this._updateSelectedValue(inst);
- this._hideTimepicker();
- },
-
-
- selectHours: function (event) {
- var $td = $(event.currentTarget),
- id = $td.attr("data-timepicker-instance-id"),
- newHours = parseInt($td.attr("data-hour")),
- fromDoubleClick = event.data.fromDoubleClick,
- $target = $(id),
- inst = this._getInst($target[0]),
- showMinutes = (this._get(inst, 'showMinutes') == true);
-
- // don't select if disabled
- if ( $.timepicker._isDisabledTimepicker($target.attr('id')) ) { return false }
-
- $td.parents('.ui-timepicker-hours:first').find('a').removeClass('ui-state-active');
- $td.children('a').addClass('ui-state-active');
- inst.hours = newHours;
-
- // added for onMinuteShow callback
- var onMinuteShow = this._get(inst, 'onMinuteShow'),
- maxTime = this._get(inst, 'maxTime'),
- minTime = this._get(inst, 'minTime');
- if (onMinuteShow || maxTime.minute || minTime.minute) {
- // this will trigger a callback on selected hour to make sure selected minute is allowed.
- this._updateMinuteDisplay(inst);
- }
-
- this._updateSelectedValue(inst);
-
- inst._hoursClicked = true;
- if ((inst._minutesClicked) || (fromDoubleClick) || (showMinutes == false)) {
- $.timepicker._hideTimepicker();
- }
- // return false because if used inline, prevent the url to change to a hashtag
- return false;
- },
-
- selectMinutes: function (event) {
- var $td = $(event.currentTarget),
- id = $td.attr("data-timepicker-instance-id"),
- newMinutes = parseInt($td.attr("data-minute")),
- fromDoubleClick = event.data.fromDoubleClick,
- $target = $(id),
- inst = this._getInst($target[0]),
- showHours = (this._get(inst, 'showHours') == true);
-
- // don't select if disabled
- if ( $.timepicker._isDisabledTimepicker($target.attr('id')) ) { return false }
-
- $td.parents('.ui-timepicker-minutes:first').find('a').removeClass('ui-state-active');
- $td.children('a').addClass('ui-state-active');
-
- inst.minutes = newMinutes;
- this._updateSelectedValue(inst);
-
- inst._minutesClicked = true;
- if ((inst._hoursClicked) || (fromDoubleClick) || (showHours == false)) {
- $.timepicker._hideTimepicker();
- // return false because if used inline, prevent the url to change to a hashtag
- return false;
- }
-
- // return false because if used inline, prevent the url to change to a hashtag
- return false;
- },
-
- _updateSelectedValue: function (inst) {
- var newTime = this._getParsedTime(inst);
- if (inst.input) {
- inst.input.val(newTime);
- inst.input.trigger('change');
- }
- var onSelect = this._get(inst, 'onSelect');
- if (onSelect) { onSelect.apply((inst.input ? inst.input[0] : null), [newTime, inst]); } // trigger custom callback
- this._updateAlternate(inst, newTime);
- return newTime;
- },
-
- /* this function process selected time and return it parsed according to instance options */
- _getParsedTime: function(inst) {
-
- if (inst.hours == -1 && inst.minutes == -1) {
- return '';
- }
-
- // default to 0 AM if hours is not valid
- if ((inst.hours < inst.hours.starts) || (inst.hours > inst.hours.ends )) { inst.hours = 0; }
- // default to 0 minutes if minute is not valid
- if ((inst.minutes < inst.minutes.starts) || (inst.minutes > inst.minutes.ends)) { inst.minutes = 0; }
-
- var period = "",
- showPeriod = (this._get(inst, 'showPeriod') == true),
- showLeadingZero = (this._get(inst, 'showLeadingZero') == true),
- showHours = (this._get(inst, 'showHours') == true),
- showMinutes = (this._get(inst, 'showMinutes') == true),
- optionalMinutes = (this._get(inst, 'optionalMinutes') == true),
- amPmText = this._get(inst, 'amPmText'),
- selectedHours = inst.hours ? inst.hours : 0,
- selectedMinutes = inst.minutes ? inst.minutes : 0,
- displayHours = selectedHours ? selectedHours : 0,
- parsedTime = '';
-
- // fix some display problem when hours or minutes are not selected yet
- if (displayHours == -1) { displayHours = 0 }
- if (selectedMinutes == -1) { selectedMinutes = 0 }
-
- if (showPeriod) {
- if (inst.hours == 0) {
- displayHours = 12;
- }
- if (inst.hours < 12) {
- period = amPmText[0];
- }
- else {
- period = amPmText[1];
- if (displayHours > 12) {
- displayHours -= 12;
- }
- }
- }
-
- var h = displayHours.toString();
- if (showLeadingZero && (displayHours < 10)) { h = '0' + h; }
-
- var m = selectedMinutes.toString();
- if (selectedMinutes < 10) { m = '0' + m; }
-
- if (showHours) {
- parsedTime += h;
- }
- if (showHours && showMinutes && (!optionalMinutes || m != 0)) {
- parsedTime += this._get(inst, 'timeSeparator');
- }
- if (showMinutes && (!optionalMinutes || m != 0)) {
- parsedTime += m;
- }
- if (showHours) {
- if (period.length > 0) { parsedTime += this._get(inst, 'periodSeparator') + period; }
- }
-
- return parsedTime;
- },
-
- /* Update any alternate field to synchronise with the main field. */
- _updateAlternate: function(inst, newTime) {
- var altField = this._get(inst, 'altField');
- if (altField) { // update alternate field too
- $(altField).each(function(i,e) {
- $(e).val(newTime);
- });
- }
- },
-
- _getTimeAsDateTimepicker: function(input) {
- var inst = this._getInst(input);
- if (inst.hours == -1 && inst.minutes == -1) {
- return '';
- }
-
- // default to 0 AM if hours is not valid
- if ((inst.hours < inst.hours.starts) || (inst.hours > inst.hours.ends )) { inst.hours = 0; }
- // default to 0 minutes if minute is not valid
- if ((inst.minutes < inst.minutes.starts) || (inst.minutes > inst.minutes.ends)) { inst.minutes = 0; }
-
- return new Date(0, 0, 0, inst.hours, inst.minutes, 0);
- },
- /* This might look unused but it's called by the $.fn.timepicker function with param getTime */
- /* added v 0.2.3 - gitHub issue #5 - Thanks edanuff */
- _getTimeTimepicker : function(input) {
- var inst = this._getInst(input);
- return this._getParsedTime(inst);
- },
- _getHourTimepicker: function(input) {
- var inst = this._getInst(input);
- if ( inst == undefined) { return -1; }
- return inst.hours;
- },
- _getMinuteTimepicker: function(input) {
- var inst= this._getInst(input);
- if ( inst == undefined) { return -1; }
- return inst.minutes;
- }
-
- });
-
-
-
- /* Invoke the timepicker functionality.
- @param options string - a command, optionally followed by additional parameters or
- Object - settings for attaching new timepicker functionality
- @return jQuery object */
- $.fn.timepicker = function (options) {
- /* Initialise the time picker. */
- if (!$.timepicker.initialized) {
- $(document).mousedown($.timepicker._checkExternalClick);
- $.timepicker.initialized = true;
- }
-
- /* Append timepicker main container to body if not exist. */
- if ($("#"+$.timepicker._mainDivId).length === 0) {
- $('body').append($.timepicker.tpDiv);
- }
-
- var otherArgs = Array.prototype.slice.call(arguments, 1);
- if (typeof options == 'string' && (options == 'getTime' || options == 'getTimeAsDate' || options == 'getHour' || options == 'getMinute' ))
- return $.timepicker['_' + options + 'Timepicker'].
- apply($.timepicker, [this[0]].concat(otherArgs));
- if (options == 'option' && arguments.length == 2 && typeof arguments[1] == 'string')
- return $.timepicker['_' + options + 'Timepicker'].
- apply($.timepicker, [this[0]].concat(otherArgs));
- return this.each(function () {
- typeof options == 'string' ?
- $.timepicker['_' + options + 'Timepicker'].
- apply($.timepicker, [this].concat(otherArgs)) :
- $.timepicker._attachTimepicker(this, options);
- });
- };
-
- /* jQuery extend now ignores nulls! */
- function extendRemove(target, props) {
- $.extend(target, props);
- for (var name in props)
- if (props[name] == null || props[name] == undefined)
- target[name] = props[name];
- return target;
- };
-
- $.timepicker = new Timepicker(); // singleton instance
- $.timepicker.initialized = false;
- $.timepicker.uuid = new Date().getTime();
- $.timepicker.version = "0.3.3";
-
- // Workaround for #4055
- // Add another global to avoid noConflict issues with inline event handlers
- window['TP_jQuery_' + tpuuid] = $;
-
-})(jQuery);
diff --git a/tailbone/static/js/tailbone.edit-shifts.js b/tailbone/static/js/tailbone.edit-shifts.js
deleted file mode 100644
index 87dc4a21..00000000
--- a/tailbone/static/js/tailbone.edit-shifts.js
+++ /dev/null
@@ -1,193 +0,0 @@
-
-/************************************************************
- *
- * tailbone.edit-shifts.js
- *
- * Common logic for editing time sheet / schedule data.
- *
- ************************************************************/
-
-
-var editing_day = null;
-var new_shift_id = 1;
-
-function add_shift(focus, uuid, start_time, end_time) {
- var shift = $('#snippets .shift').clone();
- if (! uuid) {
- uuid = 'new-' + (new_shift_id++).toString();
- }
- shift.attr('data-uuid', uuid);
- shift.children('input').each(function() {
- var name = $(this).attr('name') + '-' + uuid;
- $(this).attr('name', name);
- $(this).attr('id', name);
- });
- shift.children('input[name|="edit_start_time"]').val(start_time || '');
- shift.children('input[name|="edit_end_time"]').val(end_time || '');
- $('#day-editor .shifts').append(shift);
- shift.children('input').timepicker({showPeriod: true});
- if (focus) {
- shift.children('input:first').focus();
- }
-}
-
-function calc_minutes(start_time, end_time) {
- var start = parseTime(start_time);
- start = new Date(2000, 0, 1, start.hh, start.mm);
- var end = parseTime(end_time);
- end = new Date(2000, 0, 1, end.hh, end.mm);
- return Math.floor((end - start) / 1000 / 60);
-}
-
-function format_minutes(minutes) {
- var hours = Math.floor(minutes / 60);
- if (hours) {
- minutes -= hours * 60;
- }
- return hours.toString() + ':' + (minutes < 10 ? '0' : '') + minutes.toString();
-}
-
-// stolen from http://stackoverflow.com/a/1788084
-function parseTime(s) {
- var part = s.match(/(\d+):(\d+)(?: )?(am|pm)?/i);
- var hh = parseInt(part[1], 10);
- var mm = parseInt(part[2], 10);
- var ap = part[3] ? part[3].toUpperCase() : null;
- if (ap == 'AM') {
- if (hh == 12) {
- hh = 0;
- }
- } else if (ap == 'PM') {
- if (hh != 12) {
- hh += 12;
- }
- }
- return { hh: hh, mm: mm };
-}
-
-function time_input(shift, type) {
- var input = shift.children('input[name|="' + type + '_time"]');
- if (! input.length) {
- input = $(' ');
- shift.append(input);
- }
- return input;
-}
-
-function update_row_hours(row) {
- var minutes = 0;
- row.find('.day .shift:not(.deleted)').each(function() {
- var time_range = $.trim($(this).children('span').text()).split(' - ');
- minutes += calc_minutes(time_range[0], time_range[1]);
- });
- row.children('.total').text(minutes ? format_minutes(minutes) : '0');
-}
-
-$(function() {
-
- $('.timesheet').on('click', '.day', function() {
- editing_day = $(this);
- var editor = $('#day-editor');
- var employee = editing_day.siblings('.employee').text();
- var date = weekdays[editing_day.get(0).cellIndex - 1];
- var shifts = editor.children('.shifts');
- shifts.empty();
- editing_day.children('.shift:not(.deleted)').each(function() {
- var uuid = $(this).data('uuid');
- var time_range = $.trim($(this).children('span').text()).split(' - ');
- add_shift(false, uuid, time_range[0], time_range[1]);
- });
- if (! shifts.children('.shift').length) {
- add_shift();
- }
- editor.dialog({
- modal: true,
- title: employee + ' - ' + date,
- position: {my: 'center', at: 'center', of: editing_day},
- width: 'auto',
- autoResize: true,
- buttons: [
- {
- text: "Update",
- click: function() {
-
- // TODO: is this hacky? invoking timepicker to format the time values
- // in all cases, to avoid "invalid format" from user input
- editor.find('.shifts .shift').each(function() {
- var start_time = $(this).children('input[name|="edit_start_time"]');
- var end_time = $(this).children('input[name|="edit_end_time"]');
- $.timepicker._setTime(start_time.data('timepicker'), start_time.val());
- $.timepicker._setTime(end_time.data('timepicker'), end_time.val());
- });
-
- // create / update shifts in time table, as needed
- editor.find('.shifts .shift').each(function() {
- var uuid = $(this).data('uuid');
- var start_time = $(this).children('input[name|="edit_start_time"]').val();
- var end_time = $(this).children('input[name|="edit_end_time"]').val();
- var shift = editing_day.children('.shift[data-uuid="' + uuid + '"]');
- if (! shift.length) {
- shift = $('
');
- shift.append($(' '));
- editing_day.append(shift);
- }
- shift.children('span').text(start_time + ' - ' + end_time);
- time_input(shift, 'start').val(date + ' ' + start_time);
- time_input(shift, 'end').val(date + ' ' + end_time);
- });
-
- // remove shifts from time table, as needed
- editing_day.children('.shift').each(function() {
- var uuid = $(this).data('uuid');
- if (! editor.find('.shifts .shift[data-uuid="' + uuid + '"]').length) {
- if (uuid.match(/^new-/)) {
- $(this).remove();
- } else {
- $(this).addClass('deleted');
- $(this).append($(' '));
- }
- }
- });
-
- // mark day as modified, close dialog
- editing_day.addClass('modified');
- $('.save-changes').button('enable');
- $('.undo-changes').button('enable');
- update_row_hours(editing_day.parents('tr:first'));
- editor.dialog('close');
- data_modified = true;
- okay_to_leave = false;
- }
- },
- {
- text: "Cancel",
- click: function() {
- editor.dialog('close');
- }
- }
- ]
- });
- });
-
- $('#day-editor #add-shift').click(function() {
- add_shift(true);
- });
-
- $('#day-editor').on('click', '.shifts button', function() {
- $(this).parents('.shift:first').remove();
- });
-
- $('.save-changes').click(function() {
- $(this).button('disable').button('option', 'label', "Saving Changes...");
- okay_to_leave = true;
- $('#timetable-form').submit();
- });
-
- $('.undo-changes').click(function() {
- $(this).button('disable').button('option', 'label', "Refreshing...");
- okay_to_leave = true;
- location.href = location.href;
- });
-
-});
diff --git a/tailbone/static/js/tailbone.feedback.js b/tailbone/static/js/tailbone.feedback.js
index f6d44875..6f687b80 100644
--- a/tailbone/static/js/tailbone.feedback.js
+++ b/tailbone/static/js/tailbone.feedback.js
@@ -1,58 +1,54 @@
-$(function() {
+let FeedbackForm = {
+ props: ['action', 'message'],
+ template: '#feedback-template',
+ mixins: [FormPosterMixin],
+ methods: {
- $('#feedback').click(function() {
- var dialog = $('#feedback-dialog');
- var form = dialog.find('form');
- var textarea = form.find('textarea');
- dialog.find('.referrer .field').html(location.href);
- textarea.val('');
- dialog.dialog({
- title: "User Feedback",
- width: 600,
- modal: true,
- buttons: [
- {
- text: "Send",
- click: function(event) {
+ pleaseReplyChanged(value) {
+ this.$nextTick(() => {
+ this.$refs.userEmail.focus()
+ })
+ },
- var msg = $.trim(textarea.val());
- if (! msg) {
- alert("Please enter a message.");
- textarea.select();
- textarea.focus();
- return;
- }
+ showFeedback() {
+ this.showDialog = true
+ this.$nextTick(function() {
+ this.$refs.textarea.focus()
+ })
+ },
- disable_button(dialog_button(event));
+ sendFeedback() {
- var data = {
- _csrf: form.find('input[name="_csrf"]').val(),
- referrer: location.href,
- user: form.find('input[name="user"]').val(),
- user_name: form.find('input[name="user_name"]').val(),
- message: msg
- };
+ let params = {
+ referrer: this.referrer,
+ user: this.userUUID,
+ user_name: this.userName,
+ please_reply_to: this.pleaseReply ? this.userEmail : null,
+ message: this.message.trim(),
+ }
- $.ajax(form.attr('action'), {
- method: 'POST',
- data: data,
- success: function(data) {
- dialog.dialog('close');
- alert("Message successfully sent.\n\nThank you for your feedback.");
- }
- });
+ this.submitForm(this.action, params, response => {
- }
- },
- {
- text: "Cancel",
- click: function() {
- dialog.dialog('close');
- }
- }
- ]
- });
- });
-
-});
+ this.$buefy.toast.open({
+ message: "Message sent! Thank you for your feedback.",
+ type: 'is-info',
+ duration: 4000, // 4 seconds
+ })
+
+ this.showDialog = false
+ // clear out message, in case they need to send another
+ this.message = ""
+ })
+ },
+ }
+}
+
+let FeedbackFormData = {
+ referrer: null,
+ userUUID: null,
+ userName: null,
+ pleaseReply: false,
+ userEmail: null,
+ showDialog: false,
+}
diff --git a/tailbone/static/js/tailbone.js b/tailbone/static/js/tailbone.js
deleted file mode 100644
index 4d3212df..00000000
--- a/tailbone/static/js/tailbone.js
+++ /dev/null
@@ -1,386 +0,0 @@
-
-/************************************************************
- *
- * 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 UI button.
- */
-function disable_button(button, label) {
- $(button).button('disable');
- if (label === undefined) {
- label = $(button).data('working-label') || "Working, please wait...";
- }
- if (label) {
- if (label.slice(-3) != '...') {
- label += '...';
- }
- $(button).button('option', 'label', label);
- }
-}
-
-
-function disable_submit_button(form, label) {
- // for some reason chrome requires us to do things this way...
- // https://stackoverflow.com/questions/16867080/onclick-javascript-stops-form-submit-in-chrome
- // https://stackoverflow.com/questions/5691054/disable-submit-button-on-form-submit
- var submit = $(form).find('input[type="submit"]');
- if (! submit.length) {
- submit = $(form).find('button[type="submit"]');
- }
- if (submit.length) {
- disable_button(submit, label);
- }
-}
-
-
-/*
- * 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;
-}
-
-
-/*
- * Return a jQuery object containing a button from a dialog. This is a
- * convenience function to help with browser differences. It is assumed
- * that it is being called from within the relevant button click handler.
- * @param {event} event - Click event object.
- */
-function dialog_button(event) {
- var button = $(event.target);
-
- // TODO: not sure why this workaround is needed for Chrome..?
- if (! button.hasClass('ui-button')) {
- button = button.parents('.ui-button:first');
- }
-
- return button;
-}
-
-
-/**
- * Scroll screen as needed to ensure all options are visible, for the given
- * select menu widget.
- */
-function show_all_options(select) {
- if (! select.is(':visible')) {
- /*
- * Note that the following code was largely stolen from
- * http://brianseekford.com/2013/06/03/how-to-scroll-a-container-or-element-into-view-using-jquery-javascript-in-your-html/
- */
-
- var docViewTop = $(window).scrollTop();
- var docViewBottom = docViewTop + $(window).height();
-
- var widget = select.selectmenu('menuWidget');
- var elemTop = widget.offset().top;
- var elemBottom = elemTop + widget.height();
-
- var isScrolled = ((elemBottom <= docViewBottom) && (elemTop >= docViewTop));
-
- if (!isScrolled) {
- if (widget.height() > $(window).height()) { //then just bring to top of the container
- $(window).scrollTop(elemTop)
- } else { //try and and bring bottom of container to bottom of screen
- $(window).scrollTop(elemTop - ($(window).height() - widget.height()));
- }
- }
- }
-}
-
-
-/*
- * reference to existing timeout warning dialog, if any
- */
-var session_timeout_warning = null;
-
-
-/**
- * Warn user of impending session timeout.
- */
-function timeout_warning() {
- if (! session_timeout_warning) {
- session_timeout_warning = $('' +
- 'You will be logged out in ' +
- 'seconds...
');
- }
- session_timeout_warning.find('.seconds').text('60');
- session_timeout_warning.dialog({
- title: "Session Timeout Warning",
- modal: true,
- buttons: {
- "Stay Logged In": function() {
- session_timeout_warning.dialog('close');
- $.get(noop_url, set_timeout_warning_timer);
- },
- "Logout Now": function() {
- location.href = logout_url;
- }
- }
- });
- window.setTimeout(timeout_warning_update, 1000);
-}
-
-
-/**
- * Decrement the 'seconds' counter for the current timeout warning
- */
-function timeout_warning_update() {
- if (session_timeout_warning.is(':visible')) {
- var span = session_timeout_warning.find('.seconds');
- var seconds = parseInt(span.text()) - 1;
- if (seconds) {
- span.text(seconds.toString());
- window.setTimeout(timeout_warning_update, 1000);
- } else {
- location.href = logout_url;
- }
- }
-}
-
-
-/**
- * Warn user of impending session timeout.
- */
-function set_timeout_warning_timer() {
- // timout dialog says we're 60 seconds away, but we actually trigger when
- // 70 seconds away from supposed timeout, in case of timer drift?
- window.setTimeout(timeout_warning, session_timeout * 1000 - 70000);
-}
-
-
-/*
- * set initial timer for timeout warning, if applicable
- */
-if (session_timeout) {
- set_timeout_warning_timer();
-}
-
-
-$(function() {
-
- /*
- * enhance buttons
- */
- $('button, a.button').button();
- $('input[type=submit]').button();
- $('input[type=reset]').button();
- $('a.button.autodisable').click(function() {
- disable_button(this);
- });
- $('form.autodisable').submit(function() {
- disable_submit_button(this);
- });
-
- // quickie button
- $('#submit-quickie').button('option', 'icons', {primary: 'ui-icon-zoomin'});
-
- /*
- * enhance dropdowns
- */
- $('select[auto-enhance="true"]').selectmenu();
- $('select[auto-enhance="true"]').on('selectmenuopen', function(event, ui) {
- show_all_options($(this));
- });
-
- /* Also automatically disable any buttons marked for that. */
- $('a.button[disabled=disabled]').button('option', 'disabled', true);
-
- /*
- * Apply timepicker behavior to text inputs which are marked for it.
- */
- $('input[type=text].timepicker').timepicker({
- showPeriod: true
- });
-
- /*
- * When filter labels are clicked, (un)check the associated checkbox.
- */
- $('body').on('click', '.grid-wrapper .filter label', 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.
- */
- $('body').on('change', '#add-filter', 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.
- */
- $('body').on('submit', '.filters form', 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.
- */
- $('body').on('click', '.filters form button[type="reset"]', function() {
- var form = $(this).parents('form');
- form.find('div.filter').each(function() {
- $(this).find('div.value input').val('');
- });
- form.submit();
- return false;
- });
-
- $('body').on('click', '.grid thead 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', '.grid.hoverable tbody tr', function() {
- $(this).addClass('hovering');
- });
-
- $('body').on('mouseleave', '.grid.hoverable tbody tr', function() {
- $(this).removeClass('hovering');
- });
-
- $('body').on('click', '.grid tbody td.view', function() {
- var url = $(this).attr('url');
- if (url) {
- location.href = url;
- }
- });
-
- $('body').on('click', '.grid tbody td.edit', function() {
- var url = $(this).attr('url');
- if (url) {
- location.href = url;
- }
- });
-
- $('body').on('click', '.grid 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() {
- $('body').on('change', '.grid .pager #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);
- });
-
- });
-
- $('body').on('click', 'div.dialog button.close', function() {
- var dialog = $(this).parents('div.dialog:first');
- dialog.dialog('close');
- });
-
-});
diff --git a/tailbone/static/js/tailbone.timesheet.edit.js b/tailbone/static/js/tailbone.timesheet.edit.js
deleted file mode 100644
index f2fcb271..00000000
--- a/tailbone/static/js/tailbone.timesheet.edit.js
+++ /dev/null
@@ -1,267 +0,0 @@
-
-/************************************************************
- *
- * tailbone.timesheet.edit.js
- *
- * Common logic for editing time sheet / schedule data.
- *
- ************************************************************/
-
-
-var editing_day = null;
-var new_shift_id = 1;
-var show_timepicker = true;
-
-
-/*
- * Add a new shift entry to the editor dialog.
- * @param {boolean} focus - Whether to set focus to the start_time input
- * element after adding the shift.
- * @param {string} uuid - UUID value for the shift, if applicable.
- * @param {string} start_time - Value for start_time input element.
- * @param {string} end_time - Value for end_time input element.
- */
-
-function add_shift(focus, uuid, start_time, end_time) {
- var shift = $('#snippets .shift').clone();
- if (! uuid) {
- uuid = 'new-' + (new_shift_id++).toString();
- }
- shift.attr('data-uuid', uuid);
- shift.children('input').each(function() {
- var name = $(this).attr('name') + '-' + uuid;
- $(this).attr('name', name);
- $(this).attr('id', name);
- });
- shift.children('input[name|="edit_start_time"]').val(start_time);
- shift.children('input[name|="edit_end_time"]').val(end_time);
- $('#day-editor .shifts').append(shift);
-
- // maybe trick timepicker into never showing itself
- var args = {showPeriod: true};
- if (! show_timepicker) {
- args.showOn = 'button';
- args.button = '#nevershow';
- }
- shift.children('input').timepicker(args);
-
- if (focus) {
- shift.children('input:first').focus();
- }
-}
-
-
-/**
- * Calculate the number of minutes between given the times.
- * @param {string} start_time - Value from start_time input element.
- * @param {string} end_time - Value from end_time input element.
- */
-function calc_minutes(start_time, end_time) {
- var start = parseTime(start_time);
- var end = parseTime(end_time);
- if (start && end) {
- start = new Date(2000, 0, 1, start.hh, start.mm);
- end = new Date(2000, 0, 1, end.hh, end.mm);
- return Math.floor((end - start) / 1000 / 60);
- }
-}
-
-
-/**
- * Converts a number of minutes into string of HH:MM format.
- * @param {number} minutes - Number of minutes to be converted.
- */
-function format_minutes(minutes) {
- var hours = Math.floor(minutes / 60);
- if (hours) {
- minutes -= hours * 60;
- }
- return hours.toString() + ':' + (minutes < 10 ? '0' : '') + minutes.toString();
-}
-
-
-/**
- * NOTE: most of this logic was stolen from http://stackoverflow.com/a/1788084
- *
- * Parse a time string and convert to simple object with hh and mm keys.
- * @param {string} time - Time value in 'HH:MM PP' format, or close enough.
- */
-function parseTime(time) {
- if (time) {
- var part = time.match(/(\d+):(\d+)(?: )?(am|pm)?/i);
- if (part) {
- var hh = parseInt(part[1], 10);
- var mm = parseInt(part[2], 10);
- var ap = part[3] ? part[3].toUpperCase() : null;
- if (ap == 'AM') {
- if (hh == 12) {
- hh = 0;
- }
- } else if (ap == 'PM') {
- if (hh != 12) {
- hh += 12;
- }
- }
- return { hh: hh, mm: mm };
- }
- }
-}
-
-
-/**
- * Return a jQuery object containing the hidden start or end time input element
- * for the shift (i.e. within the *main* timesheet form). This will create the
- * input if necessary.
- * @param {jQuery} shift - A jQuery object for the shift itself.
- * @param {string} type - Should be 'start' or 'end' only.
- */
-function time_input(shift, type) {
- var input = shift.children('input[name|="' + type + '_time"]');
- if (! input.length) {
- input = $(' ');
- shift.append(input);
- }
- return input;
-}
-
-
-/**
- * Update the weekly hour total for a given row (employee).
- * @param {jQuery} row - A jQuery object for the row to be updated.
- */
-function update_row_hours(row) {
- var minutes = 0;
- row.find('.day .shift:not(.deleted)').each(function() {
- var time_range = $.trim($(this).children('span').text()).split(' - ');
- minutes += calc_minutes(time_range[0], time_range[1]);
- });
- row.children('.total').text(minutes ? format_minutes(minutes) : '0');
-}
-
-
-/**
- * Clean up user input within the editor dialog, e.g. '8:30am' => '08:30 AM'.
- * This also should ensure invalid input will become empty string.
- */
-function cleanup_editor_input() {
- // TODO: is this hacky? invoking timepicker to format the time values
- // in all cases, to avoid "invalid format" from user input
- var backward = false;
- $('#day-editor .shifts .shift').each(function() {
- var start_time = $(this).children('input[name|="edit_start_time"]');
- var end_time = $(this).children('input[name|="edit_end_time"]');
- $.timepicker._setTime(start_time.data('timepicker'), start_time.val() || '??');
- $.timepicker._setTime(end_time.data('timepicker'), end_time.val() || '??');
- var t_start = parseTime(start_time.val());
- var t_end = parseTime(end_time.val());
- if (t_start && t_end) {
- if ((t_start.hh > t_end.hh) || ((t_start.hh == t_end.hh) && (t_start.mm > t_end.mm))) {
- alert("Start time falls *after* end time! Please fix...");
- start_time.focus().select();
- backward = true;
- return false;
- }
- }
- });
- return !backward;
-}
-
-
-/**
- * Update the main timesheet table based on editor dialog input. This updates
- * both the displayed timesheet, as well as any hidden input elements on the
- * main form.
- */
-function update_timetable() {
-
- var date = weekdays[editing_day.get(0).cellIndex - 1];
-
- // add or update
- $('#day-editor .shifts .shift').each(function() {
- var uuid = $(this).data('uuid');
- var start_time = $(this).children('input[name|="edit_start_time"]').val();
- var end_time = $(this).children('input[name|="edit_end_time"]').val();
- var shift = editing_day.children('.shift[data-uuid="' + uuid + '"]');
- if (! shift.length) {
- if (! (start_time || end_time)) {
- return;
- }
- shift = $('
');
- shift.append($(' '));
- editing_day.append(shift);
- }
- shift.children('span').text((start_time || '??') + ' - ' + (end_time || '??'));
- start_time = start_time ? (date + ' ' + start_time) : '';
- end_time = end_time ? (date + ' ' + end_time) : '';
- time_input(shift, 'start').val(start_time);
- time_input(shift, 'end').val(end_time);
- });
-
-
- // remove / mark for deletion
- editing_day.children('.shift').each(function() {
- var uuid = $(this).data('uuid');
- if (! $('#day-editor .shifts .shift[data-uuid="' + uuid + '"]').length) {
- if (uuid.match(/^new-/)) {
- $(this).remove();
- } else {
- $(this).addClass('deleted');
- $(this).append($(' '));
- }
- }
- });
-
-}
-
-
-/**
- * Perform full "save" action for time sheet form, direct from day editor dialog.
- */
-function save_dialog() {
- if (! cleanup_editor_input()) {
- return false;
- }
- var save = $('#day-editor').parents('.ui-dialog').find('.ui-dialog-buttonpane button:first');
- save.button('disable').button('option', 'label', "Saving...");
- update_timetable();
- $('#timetable-form').submit();
- return true;
-}
-
-
-/*
- * on document load...
- */
-$(function() {
-
- /*
- * Within editor dialog, clicking Add Shift button will create a new/empty
- * shift and set focus to its start_time input.
- */
- $('#day-editor #add-shift').click(function() {
- add_shift(true);
- });
-
- /*
- * Within editor dialog, clicking a shift's "trash can" button will remove
- * the shift.
- */
- $('#day-editor').on('click', '.shifts button', function() {
- $(this).parents('.shift:first').remove();
- });
-
- /*
- * Within editor dialog, Enter press within time field "might" trigger
- * save. Note that this is only done for timesheet editing, not schedule.
- */
- $('#day-editor').on('keydown', '.shifts input[type="text"]', function(event) {
- if (!show_timepicker) { // TODO: this implies too much, should be cleaner
- if (event.which == 13) {
- save_dialog();
- return false;
- }
- }
- });
-
-});
diff --git a/tailbone/static/themes/falafel/css/base.css b/tailbone/static/themes/falafel/css/base.css
deleted file mode 100644
index 0fa02dbb..00000000
--- a/tailbone/static/themes/falafel/css/base.css
+++ /dev/null
@@ -1,14 +0,0 @@
-
-/******************************
- * tweaks for root user
- ******************************/
-
-.navbar .navbar-end .navbar-link.root-user,
-.navbar .navbar-end .navbar-link.root-user:hover,
-.navbar .navbar-end .navbar-link.root-user.is_active,
-.navbar .navbar-end .navbar-item.root-user,
-.navbar .navbar-end .navbar-item.root-user:hover,
-.navbar .navbar-end .navbar-item.root-user.is_active {
- background-color: red;
- font-weight: bold;
-}
diff --git a/tailbone/static/themes/falafel/css/filters.css b/tailbone/static/themes/falafel/css/filters.css
deleted file mode 100644
index 6deff7b0..00000000
--- a/tailbone/static/themes/falafel/css/filters.css
+++ /dev/null
@@ -1,22 +0,0 @@
-
-/******************************
- * Grid Filters
- ******************************/
-
-.filters .filter {
- margin-bottom: 0.5rem;
-}
-
-.filters .filter-fieldname .field,
-.filters .filter-fieldname .field label {
- width: 100%;
-}
-
-.filters .filter-fieldname .field label {
- justify-content: left;
-}
-
-.filters .filter-verb .select,
-.filters .filter-verb .select select {
- width: 100%;
-}
diff --git a/tailbone/static/themes/falafel/css/forms.css b/tailbone/static/themes/falafel/css/forms.css
deleted file mode 100644
index de4b1ebe..00000000
--- a/tailbone/static/themes/falafel/css/forms.css
+++ /dev/null
@@ -1,61 +0,0 @@
-
-/******************************
- * forms
- ******************************/
-
-/* note that this should only apply to "normal" primary forms */
-/* TODO: replace this with bulma equivalent */
-.form {
- padding-left: 5em;
-}
-
-/* note that this should only apply to "normal" primary forms */
-.form-wrapper .form .field.is-horizontal .field-label .label {
- text-align: left;
- white-space: nowrap;
- width: 18em;
-}
-
-/* note that this should only apply to "normal" primary forms */
-.form-wrapper .form .field.is-horizontal .field-body {
- min-width: 30em;
-}
-
-/* note that this should only apply to "normal" primary forms */
-.form-wrapper .form .field.is-horizontal .field-body .select,
-.form-wrapper .form .field.is-horizontal .field-body .select select {
- width: 100%;
-}
-
-/******************************
- * field-wrappers
- ******************************/
-
-/* TODO: replace this with bulma equivalent */
-.field-wrapper {
- clear: both;
- min-height: 30px;
- overflow: auto;
- margin: 15px;
-}
-
-/* TODO: replace this with bulma equivalent */
-.field-wrapper .field-row {
- display: table-row;
-}
-
-/* TODO: replace this with bulma equivalent */
-.field-wrapper label {
- display: table-cell;
- vertical-align: top;
- width: 18em;
- font-weight: bold;
- padding-top: 2px;
- white-space: nowrap;
-}
-
-/* TODO: replace this with bulma equivalent */
-.field-wrapper .field {
- display: table-cell;
- line-height: 25px;
-}
diff --git a/tailbone/static/themes/falafel/css/grids.css b/tailbone/static/themes/falafel/css/grids.css
deleted file mode 100644
index b24e9cb0..00000000
--- a/tailbone/static/themes/falafel/css/grids.css
+++ /dev/null
@@ -1,15 +0,0 @@
-
-/********************************************************************************
- * grids.css
- *
- * Style tweaks for the Buefy grids.
- ********************************************************************************/
-
-
-/******************************
- * actions column
- ******************************/
-
-a.grid-action {
- white-space: nowrap;
-}
diff --git a/tailbone/static/themes/falafel/css/layout.css b/tailbone/static/themes/falafel/css/layout.css
deleted file mode 100644
index cc4d0015..00000000
--- a/tailbone/static/themes/falafel/css/layout.css
+++ /dev/null
@@ -1,150 +0,0 @@
-
-/******************************
- * main layout
- ******************************/
-
-body {
- display: flex;
- flex-direction: column;
- min-height: 100vh;
-}
-
-.content-wrapper {
- display: flex;
- flex: 1;
- flex-direction: column;
- justify-content: space-between;
-}
-
-
-/******************************
- * header
- ******************************/
-
-/* this is the one in the very top left of screen, next to logo and linked to
-the home page */
-#global-header-title {
- margin-left: 0.3rem;
-}
-
-header .level {
- /* TODO: not sure what this 60px was supposed to do? but it broke the */
- /* styles for the feedback dialog, so disabled it is.
- /* height: 60px; */
- /* line-height: 60px; */
- padding-left: 0.5em;
- padding-right: 0.5em;
-}
-
-header .level #header-logo {
- display: inline-block;
-}
-
-header .level .global-title,
-header .level-left .global-title {
- font-size: 2em;
- font-weight: bold;
-}
-
-/* indent nested menu items a bit */
-header .navbar-item.nested {
- padding-left: 2.5rem;
-}
-
-header span.header-text {
- font-size: 2em;
- font-weight: bold;
- margin-right: 10px;
-}
-
-header .level .theme-picker {
- display: inline-flex;
-}
-
-#content-title {
- padding: 0.3rem;
-}
-
-#content-title h1 {
- font-size: 2rem;
- margin-left: 1rem;
-}
-
-/******************************
- * content
- ******************************/
-
-#page-body {
- padding: 0.4em;
-}
-
-/******************************
- * context menu
- ******************************/
-
-#context-menu {
- margin-bottom: 1em;
- margin-left: 1em;
- text-align: right;
- white-space: nowrap;
-}
-
-/******************************
- * "object helper" panel
- ******************************/
-
-.object-helpers .panel-heading {
- white-space: nowrap;
-}
-
-.object-helpers a {
- white-space: nowrap;
-}
-
-.object-helper {
- border: 1px solid black;
- margin: 1em;
- padding: 1em;
- width: 20em;
-}
-
-.object-helper-content {
- margin-top: 1em;
-}
-
-/******************************
- * markdown
- ******************************/
-
-.rendered-markdown p,
-.rendered-markdown ul {
- margin-bottom: 1rem;
-}
-
-.rendered-markdown .codehilite {
- margin-bottom: 2rem;
-}
-
-/******************************
- * fix datepicker within modals
- * TODO: someday this may not be necessary? cf.
- * https://github.com/buefy/buefy/issues/292#issuecomment-347365637
- ******************************/
-
-.modal .animation-content .modal-card {
- overflow: visible !important;
-}
-
-.modal-card-body {
- overflow: visible !important;
-}
-
-
-/******************************
- * feedback
- ******************************/
-
-.feedback-dialog .red {
- color: red;
- font-weight: bold;
-}
diff --git a/tailbone/static/themes/falafel/js/tailbone.feedback.js b/tailbone/static/themes/falafel/js/tailbone.feedback.js
deleted file mode 100644
index 6f687b80..00000000
--- a/tailbone/static/themes/falafel/js/tailbone.feedback.js
+++ /dev/null
@@ -1,54 +0,0 @@
-
-let FeedbackForm = {
- props: ['action', 'message'],
- template: '#feedback-template',
- mixins: [FormPosterMixin],
- methods: {
-
- pleaseReplyChanged(value) {
- this.$nextTick(() => {
- this.$refs.userEmail.focus()
- })
- },
-
- showFeedback() {
- this.showDialog = true
- this.$nextTick(function() {
- this.$refs.textarea.focus()
- })
- },
-
- sendFeedback() {
-
- let params = {
- referrer: this.referrer,
- user: this.userUUID,
- user_name: this.userName,
- please_reply_to: this.pleaseReply ? this.userEmail : null,
- message: this.message.trim(),
- }
-
- this.submitForm(this.action, params, response => {
-
- this.$buefy.toast.open({
- message: "Message sent! Thank you for your feedback.",
- type: 'is-info',
- duration: 4000, // 4 seconds
- })
-
- this.showDialog = false
- // clear out message, in case they need to send another
- this.message = ""
- })
- },
- }
-}
-
-let FeedbackFormData = {
- referrer: null,
- userUUID: null,
- userName: null,
- pleaseReply: false,
- userEmail: null,
- showDialog: false,
-}
diff --git a/tailbone/templates/autocomplete.mako b/tailbone/templates/autocomplete.mako
index 8c84aedd..4c413757 100644
--- a/tailbone/templates/autocomplete.mako
+++ b/tailbone/templates/autocomplete.mako
@@ -1,63 +1,5 @@
## -*- coding: utf-8; -*-
-## TODO: This function signature is getting out of hand...
-<%def name="autocomplete(field_name, service_url, field_value=None, field_display=None, width='300px', select=None, selected=None, cleared=None, change_clicked=None, options={})">
-
- ${h.hidden(field_name, id=field_name, value=field_value)}
- ${h.text(field_name+'-textbox', id=field_name+'-textbox', value=field_display,
- class_='autocomplete-textbox', style='display: none;' if field_value else '')}
-
- ${field_display or ''}
- Change
-
-
-
-%def>
-
<%def name="tailbone_autocomplete_template()">
-%def>
-<%def name="jquery()">
- ${h.javascript_link(h.get_liburl(request, 'jquery'))}
+ ## NOTE: this code was copied from
+ ## https://bulma.io/documentation/components/navbar/#navbar-menu
+
+ document.addEventListener('DOMContentLoaded', () => {
+
+ // Get all "navbar-burger" elements
+ const $navbarBurgers = Array.prototype.slice.call(document.querySelectorAll('.navbar-burger'), 0)
+
+ // Add a click event on each of them
+ $navbarBurgers.forEach( el => {
+ el.addEventListener('click', () => {
+
+ // Get the target from the "data-target" attribute
+ const target = el.dataset.target
+ const $target = document.getElementById(target)
+
+ // Toggle the "is-active" class on both the "navbar-burger" and the "navbar-menu"
+ el.classList.toggle('is-active')
+ $target.classList.toggle('is-active')
+
+ })
+ })
+ })
+
+
%def>
<%def name="vuejs()">
@@ -129,14 +138,12 @@
${self.buefy_styles()}
- ${h.stylesheet_link(request.static_url('tailbone:static/themes/falafel/css/base.css') + '?ver={}'.format(tailbone.__version__))}
- ${h.stylesheet_link(request.static_url('tailbone:static/themes/falafel/css/layout.css') + '?ver={}'.format(tailbone.__version__))}
+ ${h.stylesheet_link(request.static_url('tailbone:static/css/base.css') + '?ver={}'.format(tailbone.__version__))}
+ ${h.stylesheet_link(request.static_url('tailbone:static/css/layout.css') + '?ver={}'.format(tailbone.__version__))}
${h.stylesheet_link(request.static_url('tailbone:static/css/grids.css') + '?ver={}'.format(tailbone.__version__))}
- ${h.stylesheet_link(request.static_url('tailbone:static/themes/falafel/css/grids.css') + '?ver={}'.format(tailbone.__version__))}
- ${h.stylesheet_link(request.static_url('tailbone:static/themes/falafel/css/grids.rowstatus.css') + '?ver={}'.format(tailbone.__version__))}
-## ${h.stylesheet_link(request.static_url('tailbone:static/css/filters.css') + '?ver={}'.format(tailbone.__version__))}
- ${h.stylesheet_link(request.static_url('tailbone:static/themes/falafel/css/filters.css') + '?ver={}'.format(tailbone.__version__))}
- ${h.stylesheet_link(request.static_url('tailbone:static/themes/falafel/css/forms.css') + '?ver={}'.format(tailbone.__version__))}
+ ${h.stylesheet_link(request.static_url('tailbone:static/css/grids.rowstatus.css') + '?ver={}'.format(tailbone.__version__))}
+ ${h.stylesheet_link(request.static_url('tailbone:static/css/filters.css') + '?ver={}'.format(tailbone.__version__))}
+ ${h.stylesheet_link(request.static_url('tailbone:static/css/forms.css') + '?ver={}'.format(tailbone.__version__))}
${h.stylesheet_link(request.static_url('tailbone:static/css/diffs.css') + '?ver={}'.format(tailbone.__version__))}
${h.stylesheet_link(request.static_url('tailbone:static/css/codehilite.css') + '?ver={}'.format(tailbone.__version__))}
@@ -162,12 +169,6 @@
% endif
%def>
-## TODO: this is only being referenced by the progress template i think?
-## (so, should make a Buefy progress page at least)
-<%def name="jquery_theme()">
- ${h.stylesheet_link(h.get_liburl(request, 'jquery_ui'))}
-%def>
-
<%def name="extra_styles()">%def>
<%def name="head_tags()">%def>
@@ -201,14 +202,14 @@
@select="globalSearchSelect">
-
+
-