From 976a5836a90bb20b91d905d42b3b88f996165ca9 Mon Sep 17 00:00:00 2001 From: Lance Edgar Date: Fri, 3 Feb 2023 17:08:33 -0600 Subject: [PATCH] Purge even more jquery stuff and related static files etc. from old themes this might be the end of it..?? --- tailbone/static/css/base.css | 120 +- tailbone/static/css/filters.css | 28 +- tailbone/static/css/forms.css | 90 +- tailbone/static/css/grids.css | 4 + .../falafel => }/css/grids.rowstatus.css | 0 tailbone/static/css/jquery.loadmask.css | 40 - tailbone/static/css/jquery.ui.menubar.css | 15 - tailbone/static/css/jquery.ui.tailbone.css | 14 - tailbone/static/css/jquery.ui.timepicker.css | 57 - tailbone/static/css/layout.css | 291 ++-- tailbone/static/js/jquery.ui.tailbone.js | 452 ----- tailbone/static/js/lib/jquery.loadmask.min.js | 10 - tailbone/static/js/lib/jquery.ui.menubar.js | 331 ---- .../static/js/lib/jquery.ui.timepicker.js | 1496 ----------------- tailbone/static/js/tailbone.edit-shifts.js | 193 --- tailbone/static/js/tailbone.feedback.js | 98 +- tailbone/static/js/tailbone.js | 386 ----- tailbone/static/js/tailbone.timesheet.edit.js | 267 --- tailbone/static/themes/falafel/css/base.css | 14 - .../static/themes/falafel/css/filters.css | 22 - tailbone/static/themes/falafel/css/forms.css | 61 - tailbone/static/themes/falafel/css/grids.css | 15 - tailbone/static/themes/falafel/css/layout.css | 150 -- .../themes/falafel/js/tailbone.feedback.js | 54 - tailbone/templates/autocomplete.mako | 58 - tailbone/templates/base.mako | 65 +- tailbone/templates/customers/view.mako | 17 - tailbone/templates/email-bounces/view.mako | 2 - tailbone/templates/forms/deform.mako | 99 -- tailbone/templates/forms/form_readonly.mako | 8 - tailbone/templates/grids/search.mako | 37 - tailbone/templates/master/versions.mako | 11 - tailbone/templates/people/view_profile.mako | 402 ----- .../templates/purchases/batches/create.mako | 95 +- .../templates/purchases/credits/index.mako | 156 +- tailbone/templates/shifts/base.mako | 99 +- tailbone/templates/shifts/schedule_edit.mako | 129 +- tailbone/templates/shifts/timesheet.mako | 4 +- tailbone/templates/shifts/timesheet_edit.mako | 82 +- tailbone/templates/tempmon/probes/create.mako | 17 - tailbone/templates/tempmon/probes/edit.mako | 17 - tailbone/views/purchases/credits.py | 12 +- 42 files changed, 366 insertions(+), 5152 deletions(-) rename tailbone/static/{themes/falafel => }/css/grids.rowstatus.css (100%) delete mode 100644 tailbone/static/css/jquery.loadmask.css delete mode 100644 tailbone/static/css/jquery.ui.menubar.css delete mode 100644 tailbone/static/css/jquery.ui.tailbone.css delete mode 100644 tailbone/static/css/jquery.ui.timepicker.css delete mode 100644 tailbone/static/js/jquery.ui.tailbone.js delete mode 100644 tailbone/static/js/lib/jquery.loadmask.min.js delete mode 100644 tailbone/static/js/lib/jquery.ui.menubar.js delete mode 100644 tailbone/static/js/lib/jquery.ui.timepicker.js delete mode 100644 tailbone/static/js/tailbone.edit-shifts.js delete mode 100644 tailbone/static/js/tailbone.js delete mode 100644 tailbone/static/js/tailbone.timesheet.edit.js delete mode 100644 tailbone/static/themes/falafel/css/base.css delete mode 100644 tailbone/static/themes/falafel/css/filters.css delete mode 100644 tailbone/static/themes/falafel/css/forms.css delete mode 100644 tailbone/static/themes/falafel/css/grids.css delete mode 100644 tailbone/static/themes/falafel/css/layout.css delete mode 100644 tailbone/static/themes/falafel/js/tailbone.feedback.js delete mode 100644 tailbone/templates/forms/deform.mako delete mode 100644 tailbone/templates/forms/form_readonly.mako delete mode 100644 tailbone/templates/grids/search.mako delete mode 100644 tailbone/templates/people/view_profile.mako delete mode 100644 tailbone/templates/tempmon/probes/create.mako delete mode 100644 tailbone/templates/tempmon/probes/edit.mako 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 = $(''); - 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 += ''; // Close the Hour td - } - - if (showMinutes) { - html += ''; - } - - html += ''; - - - if (showButtonPanel) { - var buttonPanel = ''; - } - html += '
' + - '
' + - hourLabel + - '
' + - ''; - - for (row = 1; row <= rows; row++) { - html += ''; - // AM - if (row == amFirstRow && showPeriodLabels) { - html += ''; - } - // PM - if (row == pmFirstRow && showPeriodLabels) { - html += ''; - } - 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 += '
' + amPmText[0] + '' + amPmText[1] + '
' + // Close the hours cells table - '
'; - html += this._generateHTMLMinutes(inst); - html += '
'; - if (showNowButton) { - buttonPanel += ''; - } - if (showDeselectButton) { - buttonPanel += ''; - } - if (showCloseButton) { - buttonPanel += ''; - } - - html += buttonPanel + '
'; - - 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 += '
' + - minuteLabel + - '
' + - ''; - - 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 '')} - -
- - - <%def name="tailbone_autocomplete_template()"> - -<%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 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 -## 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 name="extra_styles()"> <%def name="head_tags()"> @@ -201,14 +202,14 @@ @select="globalSearchSelect"> - -