From 79e36da5e360a4ca6b746431da1c1aea4ae7a42c Mon Sep 17 00:00:00 2001 From: schittlur <78377530+schittlur@users.noreply.github.com> Date: Wed, 21 Feb 2024 16:50:49 -0500 Subject: [PATCH 1/7] django UI customization updates --- app/core/fixtures/admin_theme_data.json | 65 + app/openlxp_xss_project/settings.py | 2 + app/static/admin/css/autocomplete.css | 57 +- app/static/admin/css/base.css | 272 +- app/static/admin/css/changelists.css | 92 +- app/static/admin/css/forms.css | 58 +- app/static/admin/css/login.css | 33 +- app/static/admin/css/nav_sidebar.css | 21 +- app/static/admin/css/responsive.css | 33 +- app/static/admin/css/rtl.css | 24 +- app/static/admin/css/widgets.css | 76 +- app/static/admin/fonts/LICENSE.txt | 404 +- app/static/admin/js/SelectBox.js | 4 +- app/static/admin/js/actions.js | 313 +- app/static/admin/js/actions.min.js | 7 - .../admin/js/admin/DateTimeShortcuts.js | 6 +- .../admin/js/admin/RelatedObjectLookups.js | 8 +- app/static/admin/js/autocomplete.js | 5 +- app/static/admin/js/calendar.js | 14 + app/static/admin/js/cancel.js | 42 +- app/static/admin/js/collapse.js | 10 +- app/static/admin/js/collapse.min.js | 2 - app/static/admin/js/core.js | 7 + app/static/admin/js/inlines.min.js | 11 - app/static/admin/js/popup_response.js | 62 +- app/static/admin/js/prepopulate.min.js | 1 - app/static/admin/js/urlify.js | 17 +- .../collapsible-inlines.js | 48 + .../admin_interface/css/admin-interface.css | 515 +++ .../admin_interface/css/change-form.css | 34 + app/static/admin_interface/css/changelist.css | 43 + app/static/admin_interface/css/fieldsets.css | 14 + .../admin_interface/css/file-upload.css | 52 + app/static/admin_interface/css/header.css | 50 + app/static/admin_interface/css/inlines.css | 105 + .../admin_interface/css/jquery.ui.tabs.css | 247 ++ .../admin_interface/css/language-chooser.css | 72 + .../css/list-filter-dropdown.css | 27 + .../admin_interface/css/list-filter.css | 28 + app/static/admin_interface/css/login.css | 65 + app/static/admin_interface/css/modules.css | 12 + .../admin_interface/css/nav-sidebar.css | 78 + .../admin_interface/css/object-tools.css | 10 + app/static/admin_interface/css/paginator.css | 33 + .../admin_interface/css/recent-actions.css | 10 + app/static/admin_interface/css/rtl.css | 34 + .../css/sticky-form-controls.css | 95 + .../admin_interface/css/tabbed-changeform.css | 67 + .../css/third-party/ckeditor.css | 126 + .../css/third-party/import-export.css | 7 + .../css/third-party/json-widget.css | 27 + .../css/third-party/modeltranslation.css | 17 + .../css/third-party/rangefilter.css | 25 + .../css/third-party/sorl-thumbnail.css | 67 + .../css/third-party/streamfield.css | 220 + .../css/third-party/tabbed-admin.css | 37 + .../css/third-party/tinymce.css | 3 + app/static/admin_interface/css/widgets.css | 153 + .../favico/favico-0.3.10-patched.js | 913 +++++ .../favico/favico-0.3.10-patched.min.js | 1 + .../foldable-apps/foldable-apps.css | 70 + .../foldable-apps/foldable-apps.js | 35 + .../magnific-popup/jquery.magnific-popup.js | 1867 +++++++++ .../magnific-popup/magnific-popup.css | 351 ++ .../related-modal/related-modal.css | 82 + .../related-modal/related-modal.js | 168 + .../tabbed-changeform/tabbed-changeform.js | 73 + .../ckeditor/ckeditor/skins/light/LICENSE | 21 + .../ckeditor/ckeditor/skins/light/README.md | 2 + .../ckeditor/ckeditor/skins/light/bower.json | 13 + .../ckeditor/ckeditor/skins/light/dialog.css | 5 + .../ckeditor/ckeditor/skins/light/editor.css | 1348 +++++++ .../ckeditor/skins/light/editor_gecko.css | 6 + .../ckeditor/skins/light/editor_ie.css | 5 + .../ckeditor/skins/light/editor_ie7.css | 5 + .../ckeditor/skins/light/editor_ie8.css | 5 + .../ckeditor/ckeditor/skins/light/icons.png | Bin 0 -> 22758 bytes .../ckeditor/skins/light/icons_hidpi.png | Bin 0 -> 33069 bytes .../ckeditor/skins/light/images/arrow.png | Bin 0 -> 261 bytes .../ckeditor/skins/light/images/close.png | Bin 0 -> 824 bytes .../skins/light/images/hidpi/close.png | Bin 0 -> 1792 bytes .../skins/light/images/hidpi/lock-open.png | Bin 0 -> 1503 bytes .../skins/light/images/hidpi/lock.png | Bin 0 -> 1616 bytes .../skins/light/images/hidpi/refresh.png | Bin 0 -> 2320 bytes .../ckeditor/skins/light/images/lock-open.png | Bin 0 -> 736 bytes .../ckeditor/skins/light/images/lock.png | Bin 0 -> 728 bytes .../ckeditor/skins/light/images/refresh.png | Bin 0 -> 953 bytes .../ckeditor/ckeditor/skins/light/skin.js | 322 ++ app/static/colorfield/colorfield.js | 12 + app/static/colorfield/jscolor/jscolor.js | 3524 +++++++++++++++++ app/static/colorfield/jscolor/jscolor.min.js | 1 + .../streamfield/admin_popup_response.js | 24 + .../streamfield/js/admin_popup_response.js | 24 + requirements.txt | 8 + start-app.sh | 1 + 95 files changed, 12120 insertions(+), 733 deletions(-) create mode 100644 app/core/fixtures/admin_theme_data.json delete mode 100644 app/static/admin/js/actions.min.js delete mode 100644 app/static/admin/js/collapse.min.js delete mode 100644 app/static/admin/js/inlines.min.js delete mode 100644 app/static/admin/js/prepopulate.min.js create mode 100644 app/static/admin_interface/collapsible-inlines/collapsible-inlines.js create mode 100644 app/static/admin_interface/css/admin-interface.css create mode 100644 app/static/admin_interface/css/change-form.css create mode 100644 app/static/admin_interface/css/changelist.css create mode 100644 app/static/admin_interface/css/fieldsets.css create mode 100644 app/static/admin_interface/css/file-upload.css create mode 100644 app/static/admin_interface/css/header.css create mode 100644 app/static/admin_interface/css/inlines.css create mode 100644 app/static/admin_interface/css/jquery.ui.tabs.css create mode 100644 app/static/admin_interface/css/language-chooser.css create mode 100644 app/static/admin_interface/css/list-filter-dropdown.css create mode 100644 app/static/admin_interface/css/list-filter.css create mode 100644 app/static/admin_interface/css/login.css create mode 100644 app/static/admin_interface/css/modules.css create mode 100644 app/static/admin_interface/css/nav-sidebar.css create mode 100644 app/static/admin_interface/css/object-tools.css create mode 100644 app/static/admin_interface/css/paginator.css create mode 100644 app/static/admin_interface/css/recent-actions.css create mode 100644 app/static/admin_interface/css/rtl.css create mode 100644 app/static/admin_interface/css/sticky-form-controls.css create mode 100644 app/static/admin_interface/css/tabbed-changeform.css create mode 100644 app/static/admin_interface/css/third-party/ckeditor.css create mode 100644 app/static/admin_interface/css/third-party/import-export.css create mode 100644 app/static/admin_interface/css/third-party/json-widget.css create mode 100644 app/static/admin_interface/css/third-party/modeltranslation.css create mode 100644 app/static/admin_interface/css/third-party/rangefilter.css create mode 100644 app/static/admin_interface/css/third-party/sorl-thumbnail.css create mode 100644 app/static/admin_interface/css/third-party/streamfield.css create mode 100644 app/static/admin_interface/css/third-party/tabbed-admin.css create mode 100644 app/static/admin_interface/css/third-party/tinymce.css create mode 100644 app/static/admin_interface/css/widgets.css create mode 100644 app/static/admin_interface/favico/favico-0.3.10-patched.js create mode 100644 app/static/admin_interface/favico/favico-0.3.10-patched.min.js create mode 100644 app/static/admin_interface/foldable-apps/foldable-apps.css create mode 100644 app/static/admin_interface/foldable-apps/foldable-apps.js create mode 100644 app/static/admin_interface/magnific-popup/jquery.magnific-popup.js create mode 100644 app/static/admin_interface/magnific-popup/magnific-popup.css create mode 100644 app/static/admin_interface/related-modal/related-modal.css create mode 100644 app/static/admin_interface/related-modal/related-modal.js create mode 100644 app/static/admin_interface/tabbed-changeform/tabbed-changeform.js create mode 100644 app/static/ckeditor/ckeditor/skins/light/LICENSE create mode 100644 app/static/ckeditor/ckeditor/skins/light/README.md create mode 100644 app/static/ckeditor/ckeditor/skins/light/bower.json create mode 100644 app/static/ckeditor/ckeditor/skins/light/dialog.css create mode 100644 app/static/ckeditor/ckeditor/skins/light/editor.css create mode 100644 app/static/ckeditor/ckeditor/skins/light/editor_gecko.css create mode 100644 app/static/ckeditor/ckeditor/skins/light/editor_ie.css create mode 100644 app/static/ckeditor/ckeditor/skins/light/editor_ie7.css create mode 100644 app/static/ckeditor/ckeditor/skins/light/editor_ie8.css create mode 100644 app/static/ckeditor/ckeditor/skins/light/icons.png create mode 100644 app/static/ckeditor/ckeditor/skins/light/icons_hidpi.png create mode 100644 app/static/ckeditor/ckeditor/skins/light/images/arrow.png create mode 100644 app/static/ckeditor/ckeditor/skins/light/images/close.png create mode 100644 app/static/ckeditor/ckeditor/skins/light/images/hidpi/close.png create mode 100644 app/static/ckeditor/ckeditor/skins/light/images/hidpi/lock-open.png create mode 100644 app/static/ckeditor/ckeditor/skins/light/images/hidpi/lock.png create mode 100644 app/static/ckeditor/ckeditor/skins/light/images/hidpi/refresh.png create mode 100644 app/static/ckeditor/ckeditor/skins/light/images/lock-open.png create mode 100644 app/static/ckeditor/ckeditor/skins/light/images/lock.png create mode 100644 app/static/ckeditor/ckeditor/skins/light/images/refresh.png create mode 100644 app/static/ckeditor/ckeditor/skins/light/skin.js create mode 100644 app/static/colorfield/colorfield.js create mode 100644 app/static/colorfield/jscolor/jscolor.js create mode 100644 app/static/colorfield/jscolor/jscolor.min.js create mode 100644 app/static/streamfield/admin_popup_response.js create mode 100644 app/static/streamfield/js/admin_popup_response.js diff --git a/app/core/fixtures/admin_theme_data.json b/app/core/fixtures/admin_theme_data.json new file mode 100644 index 0000000..aea1cb0 --- /dev/null +++ b/app/core/fixtures/admin_theme_data.json @@ -0,0 +1,65 @@ +[ + { + "model": "admin_interface.theme", + "pk": 1, + "fields": { + "name": "XSS Configuration Portal", + "active": true, + "title": "XSS Configuration Portal", + "title_color": "#FFFFFF", + "title_visible": true, + "logo": "admin-interface/logo/dodLogo.png", + "logo_color": "#FFFFFF", + "logo_max_width": 10, + "logo_max_height": 10, + "logo_visible": true, + "favicon": "admin-interface/favicon/dodLogo.png", + "env_name": "", + "env_color": "#077391", + "env_visible_in_header": true, + "env_visible_in_favicon": false, + "language_chooser_active": true, + "language_chooser_control": "default-select", + "language_chooser_display": "name", + "css_header_background_color": "#077391", + "css_header_text_color": "#E7F8FD", + "css_header_link_color": "#FFFFFF", + "css_header_link_hover_color": "#C9F0DD", + "css_module_background_color": "#077391", + "css_module_background_selected_color": "#B9E5F5", + "css_module_text_color": "#FFFFFF", + "css_module_link_color": "#FFFFFF", + "css_module_link_selected_color": "#FFFFFF", + "css_module_link_hover_color": "#C9F0DD", + "css_module_rounded_corners": true, + "css_generic_link_color": "#077391", + "css_generic_link_hover_color": "#09A3CE", + "css_generic_link_active_color": "#05556B", + "css_save_button_background_color": "#077391", + "css_save_button_background_hover_color": "#09A3CE", + "css_save_button_text_color": "#FFFFFF", + "css_delete_button_background_color": "#BA2121", + "css_delete_button_background_hover_color": "#A41515", + "css_delete_button_text_color": "#FFFFFF", + "related_modal_active": true, + "related_modal_background_color": "#000000", + "related_modal_background_opacity": "0.3", + "related_modal_rounded_corners": true, + "related_modal_close_button_visible": true, + "list_filter_highlight": true, + "list_filter_dropdown": true, + "list_filter_sticky": true, + "list_filter_removal_links": false, + "foldable_apps": true, + "show_fieldsets_as_tabs": false, + "show_inlines_as_tabs": false, + "collapsible_stacked_inlines": false, + "collapsible_stacked_inlines_collapsed": true, + "collapsible_tabular_inlines": false, + "collapsible_tabular_inlines_collapsed": true, + "recent_actions_visible": true, + "form_submit_sticky": false, + "form_pagination_sticky": false + } + } + ] \ No newline at end of file diff --git a/app/openlxp_xss_project/settings.py b/app/openlxp_xss_project/settings.py index 0c7d8de..a318819 100644 --- a/app/openlxp_xss_project/settings.py +++ b/app/openlxp_xss_project/settings.py @@ -35,6 +35,8 @@ # Application definition INSTALLED_APPS = [ + "admin_interface", + "colorfield", 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', diff --git a/app/static/admin/css/autocomplete.css b/app/static/admin/css/autocomplete.css index 3ef95d1..69c94e7 100644 --- a/app/static/admin/css/autocomplete.css +++ b/app/static/admin/css/autocomplete.css @@ -14,7 +14,7 @@ select.admin-autocomplete { .select2-container--admin-autocomplete.select2-container--focus .select2-selection, .select2-container--admin-autocomplete.select2-container--open .select2-selection { - border-color: #999; + border-color: var(--body-quiet-color); min-height: 30px; } @@ -29,13 +29,13 @@ select.admin-autocomplete { } .select2-container--admin-autocomplete .select2-selection--single { - background-color: #fff; - border: 1px solid #ccc; + background-color: var(--body-bg); + border: 1px solid var(--border-color); border-radius: 4px; } .select2-container--admin-autocomplete .select2-selection--single .select2-selection__rendered { - color: #444; + color: var(--body-fg); line-height: 30px; } @@ -46,7 +46,7 @@ select.admin-autocomplete { } .select2-container--admin-autocomplete .select2-selection--single .select2-selection__placeholder { - color: #999; + color: var(--body-quiet-color); } .select2-container--admin-autocomplete .select2-selection--single .select2-selection__arrow { @@ -80,7 +80,7 @@ select.admin-autocomplete { } .select2-container--admin-autocomplete.select2-container--disabled .select2-selection--single { - background-color: #eee; + background-color: var(--darkened-bg); cursor: default; } @@ -94,8 +94,8 @@ select.admin-autocomplete { } .select2-container--admin-autocomplete .select2-selection--multiple { - background-color: white; - border: 1px solid #ccc; + background-color: var(--body-bg); + border: 1px solid var(--border-color); border-radius: 4px; cursor: text; } @@ -104,8 +104,10 @@ select.admin-autocomplete { box-sizing: border-box; list-style: none; margin: 0; - padding: 0 5px; + padding: 0 10px 5px 5px; width: 100%; + display: flex; + flex-wrap: wrap; } .select2-container--admin-autocomplete .select2-selection--multiple .select2-selection__rendered li { @@ -113,7 +115,7 @@ select.admin-autocomplete { } .select2-container--admin-autocomplete .select2-selection--multiple .select2-selection__placeholder { - color: #999; + color: var(--body-quiet-color); margin-top: 5px; float: left; } @@ -123,11 +125,13 @@ select.admin-autocomplete { float: right; font-weight: bold; margin: 5px; + position: absolute; + right: 0; } .select2-container--admin-autocomplete .select2-selection--multiple .select2-selection__choice { - background-color: #e4e4e4; - border: 1px solid #ccc; + background-color: var(--darkened-bg); + border: 1px solid var(--border-color); border-radius: 4px; cursor: default; float: left; @@ -137,7 +141,7 @@ select.admin-autocomplete { } .select2-container--admin-autocomplete .select2-selection--multiple .select2-selection__choice__remove { - color: #999; + color: var(--body-quiet-color); cursor: pointer; display: inline-block; font-weight: bold; @@ -145,7 +149,7 @@ select.admin-autocomplete { } .select2-container--admin-autocomplete .select2-selection--multiple .select2-selection__choice__remove:hover { - color: #333; + color: var(--body-fg); } .select2-container--admin-autocomplete[dir="rtl"] .select2-selection--multiple .select2-selection__choice, .select2-container--admin-autocomplete[dir="rtl"] .select2-selection--multiple .select2-selection__placeholder, .select2-container--admin-autocomplete[dir="rtl"] .select2-selection--multiple .select2-search--inline { @@ -163,12 +167,12 @@ select.admin-autocomplete { } .select2-container--admin-autocomplete.select2-container--focus .select2-selection--multiple { - border: solid #999 1px; + border: solid var(--body-quiet-color) 1px; outline: 0; } .select2-container--admin-autocomplete.select2-container--disabled .select2-selection--multiple { - background-color: #eee; + background-color: var(--darkened-bg); cursor: default; } @@ -186,12 +190,20 @@ select.admin-autocomplete { border-bottom-right-radius: 0; } +.select2-container--admin-autocomplete .select2-search--dropdown { + background: var(--darkened-bg); +} + .select2-container--admin-autocomplete .select2-search--dropdown .select2-search__field { - border: 1px solid #ccc; + background: var(--body-bg); + color: var(--body-fg); + border: 1px solid var(--border-color); + border-radius: 4px; } .select2-container--admin-autocomplete .select2-search--inline .select2-search__field { background: transparent; + color: var(--body-fg); border: none; outline: 0; box-shadow: none; @@ -201,6 +213,8 @@ select.admin-autocomplete { .select2-container--admin-autocomplete .select2-results > .select2-results__options { max-height: 200px; overflow-y: auto; + color: var(--body-fg); + background: var(--body-bg); } .select2-container--admin-autocomplete .select2-results__option[role=group] { @@ -208,11 +222,12 @@ select.admin-autocomplete { } .select2-container--admin-autocomplete .select2-results__option[aria-disabled=true] { - color: #999; + color: var(--body-quiet-color); } .select2-container--admin-autocomplete .select2-results__option[aria-selected=true] { - background-color: #ddd; + background-color: var(--selected-bg); + color: var(--body-fg); } .select2-container--admin-autocomplete .select2-results__option .select2-results__option { @@ -249,8 +264,8 @@ select.admin-autocomplete { } .select2-container--admin-autocomplete .select2-results__option--highlighted[aria-selected] { - background-color: #79aec8; - color: white; + background-color: var(--primary); + color: var(--primary-fg); } .select2-container--admin-autocomplete .select2-results__group { diff --git a/app/static/admin/css/base.css b/app/static/admin/css/base.css index c428519..1cb3acd 100644 --- a/app/static/admin/css/base.css +++ b/app/static/admin/css/base.css @@ -4,6 +4,93 @@ @import url(fonts.css); +/* VARIABLE DEFINITIONS */ +:root { + --primary: #79aec8; + --secondary: #417690; + --accent: #f5dd5d; + --primary-fg: #fff; + + --body-fg: #333; + --body-bg: #fff; + --body-quiet-color: #666; + --body-loud-color: #000; + + --header-color: #ffc; + --header-branding-color: var(--accent); + --header-bg: var(--secondary); + --header-link-color: var(--primary-fg); + + --breadcrumbs-fg: #c4dce8; + --breadcrumbs-link-fg: var(--body-bg); + --breadcrumbs-bg: var(--primary); + + --link-fg: #447e9b; + --link-hover-color: #036; + --link-selected-fg: #5b80b2; + + --hairline-color: #e8e8e8; + --border-color: #ccc; + + --error-fg: #ba2121; + + --message-success-bg: #dfd; + --message-warning-bg: #ffc; + --message-error-bg: #ffefef; + + --darkened-bg: #f8f8f8; /* A bit darker than --body-bg */ + --selected-bg: #e4e4e4; /* E.g. selected table cells */ + --selected-row: #ffc; + + --button-fg: #fff; + --button-bg: var(--primary); + --button-hover-bg: #609ab6; + --default-button-bg: var(--secondary); + --default-button-hover-bg: #205067; + --close-button-bg: #888; /* Previously #bbb, contrast 1.92 */ + --close-button-hover-bg: #747474; + --delete-button-bg: #ba2121; + --delete-button-hover-bg: #a41515; + + --object-tools-fg: var(--button-fg); + --object-tools-bg: var(--close-button-bg); + --object-tools-hover-bg: var(--close-button-hover-bg); +} + +@media (prefers-color-scheme: dark) { + :root { + --primary: #264b5d; + --primary-fg: #eee; + + --body-fg: #eeeeee; + --body-bg: #121212; + --body-quiet-color: #e0e0e0; + --body-loud-color: #ffffff; + + --breadcrumbs-link-fg: #e0e0e0; + --breadcrumbs-bg: var(--primary); + + --link-fg: #81d4fa; + --link-hover-color: #4ac1f7; + --link-selected-fg: #6f94c6; + + --hairline-color: #272727; + --border-color: #353535; + + --error-fg: #e35f5f; + --message-success-bg: #006b1b; + --message-warning-bg: #583305; + --message-error-bg: #570808; + + --darkened-bg: #212121; + --selected-bg: #1b1b1b; + --selected-row: #00363a; + + --close-button-bg: #333333; + --close-button-hover-bg: #666666; + } +} + html, body { height: 100%; } @@ -13,19 +100,20 @@ body { padding: 0; font-size: 14px; font-family: "Roboto","Lucida Grande","DejaVu Sans","Bitstream Vera Sans",Verdana,Arial,sans-serif; - color: #333; - background: #fff; + color: var(--body-fg); + background: var(--body-bg); } /* LINKS */ a:link, a:visited { - color: #447e9b; + color: var(--link-fg); text-decoration: none; + transition: color 0.15s, background 0.15s; } a:focus, a:hover { - color: #036; + color: var(--link-hover-color); } a:focus { @@ -37,7 +125,7 @@ a img { } a.section:link, a.section:visited { - color: #fff; + color: var(--header-link-color); text-decoration: none; } @@ -64,7 +152,7 @@ h1 { margin: 0 0 20px; font-weight: 300; font-size: 20px; - color: #666; + color: var(--body-quiet-color); } h2 { @@ -80,7 +168,7 @@ h2.subhead { h3 { font-size: 14px; margin: .8em 0 .3em 0; - color: #666; + color: var(--body-quiet-color); font-weight: bold; } @@ -93,7 +181,7 @@ h4 { h5 { font-size: 10px; margin: 1.5em 0 .5em 0; - color: #666; + color: var(--body-quiet-color); text-transform: uppercase; letter-spacing: 1px; } @@ -131,7 +219,7 @@ fieldset { min-width: 0; padding: 0; border: none; - border-top: 1px solid #eee; + border-top: 1px solid var(--hairline-color); } blockquote { @@ -144,14 +232,14 @@ blockquote { code, pre { font-family: "Bitstream Vera Sans Mono", Monaco, "Courier New", Courier, monospace; - color: #666; + color: var(--body-quiet-color); font-size: 12px; overflow-x: auto; } pre.literal-block { margin: 10px; - background: #eee; + background: var(--darkened-bg); padding: 6px 8px; } @@ -161,8 +249,8 @@ code strong { hr { clear: both; - color: #eee; - background-color: #eee; + color: var(--hairline-color); + background-color: var(--hairline-color); height: 1px; border: none; margin: 0; @@ -183,7 +271,7 @@ hr { .help, p.help, form p.help, div.help, form div.help, div.help li { font-size: 11px; - color: #999; + color: var(--body-quiet-color); } div.help ul { @@ -199,7 +287,7 @@ p img, h1 img, h2 img, h3 img, h4 img, td img { } .quiet, a.quiet:link, a.quiet:visited { - color: #999; + color: var(--body-quiet-color); font-weight: normal; } @@ -211,20 +299,23 @@ p img, h1 img, h2 img, h3 img, h4 img, td img { white-space: nowrap; } +.hidden { + display: none; +} + /* TABLES */ table { border-collapse: collapse; - border-color: #ccc; + border-color: var(--border-color); } td, th { font-size: 13px; line-height: 16px; - border-bottom: 1px solid #eee; + border-bottom: 1px solid var(--hairline-color); vertical-align: top; padding: 8px; - font-family: "Roboto", "Lucida Grande", Verdana, Arial, sans-serif; } th { @@ -234,37 +325,37 @@ th { thead th, tfoot td { - color: #666; + color: var(--body-quiet-color); padding: 5px 10px; font-size: 11px; - background: #fff; + background: var(--body-bg); border: none; - border-top: 1px solid #eee; - border-bottom: 1px solid #eee; + border-top: 1px solid var(--hairline-color); + border-bottom: 1px solid var(--hairline-color); } tfoot td { border-bottom: none; - border-top: 1px solid #eee; + border-top: 1px solid var(--hairline-color); } thead th.required { - color: #000; + color: var(--body-loud-color); } tr.alt { - background: #f6f6f6; + background: var(--darkened-bg); } tr:nth-child(odd), .row-form-errors { - background: #fff; + background: var(--body-bg); } tr:nth-child(even), tr:nth-child(even) .errorlist, tr:nth-child(odd) + .row-form-errors, tr:nth-child(odd) + .row-form-errors .errorlist { - background: #f9f9f9; + background: var(--darkened-bg); } /* SORTABLE TABLES */ @@ -273,15 +364,15 @@ thead th { padding: 5px 10px; line-height: normal; text-transform: uppercase; - background: #f6f6f6; + background: var(--darkened-bg); } thead th a:link, thead th a:visited { - color: #666; + color: var(--body-quiet-color); } thead th.sorted { - background: #eee; + background: var(--selected-bg); } thead th.sorted .text { @@ -300,7 +391,7 @@ table thead th .text a { } table thead th .text a:focus, table thead th .text a:hover { - background: #eee; + background: var(--selected-bg); } thead th.sorted a.sortremove { @@ -347,12 +438,12 @@ table thead th.sorted .sortoptions a.sortremove:after { left: 3px; font-weight: 200; font-size: 18px; - color: #999; + color: var(--body-quiet-color); } table thead th.sorted .sortoptions a.sortremove:focus:after, table thead th.sorted .sortoptions a.sortremove:hover:after { - color: #447e9b; + color: var(--link-fg); } table thead th.sorted .sortoptions a.sortremove:focus, @@ -399,16 +490,18 @@ textarea { input[type=text], input[type=password], input[type=email], input[type=url], input[type=number], input[type=tel], textarea, select, .vTextField { - border: 1px solid #ccc; + border: 1px solid var(--border-color); border-radius: 4px; padding: 5px 6px; margin-top: 0; + color: var(--body-fg); + background-color: var(--body-bg); } input[type=text]:focus, input[type=password]:focus, input[type=email]:focus, input[type=url]:focus, input[type=number]:focus, input[type=tel]:focus, textarea:focus, select:focus, .vTextField:focus { - border-color: #999; + border-color: var(--body-quiet-color); } select { @@ -424,12 +517,13 @@ select[multiple] { /* FORM BUTTONS */ .button, input[type=submit], input[type=button], .submit-row input, a.button { - background: #79aec8; + background: var(--button-bg); padding: 10px 15px; border: none; border-radius: 4px; - color: #fff; + color: var(--button-fg); cursor: pointer; + transition: background 0.15s; } a.button { @@ -439,7 +533,7 @@ a.button { .button:active, input[type=submit]:active, input[type=button]:active, .button:focus, input[type=submit]:focus, input[type=button]:focus, .button:hover, input[type=submit]:hover, input[type=button]:hover { - background: #609ab6; + background: var(--button-hover-bg); } .button[disabled], input[type=submit][disabled], input[type=button][disabled] { @@ -450,13 +544,13 @@ a.button { float: right; border: none; font-weight: 400; - background: #417690; + background: var(--default-button-bg); } .button.default:active, input[type=submit].default:active, .button.default:focus, input[type=submit].default:focus, .button.default:hover, input[type=submit].default:hover { - background: #205067; + background: var(--default-button-hover-bg); } .button[disabled].default, @@ -471,7 +565,7 @@ input[type=button][disabled].default { .module { border: none; margin-bottom: 30px; - background: #fff; + background: var(--body-bg); } .module p, .module ul, .module h3, .module h4, .module dl, .module pre { @@ -497,8 +591,8 @@ input[type=button][disabled].default { font-weight: 400; font-size: 13px; text-align: left; - background: #79aec8; - color: #fff; + background: var(--primary); + color: var(--header-link-color); } .module caption, @@ -525,18 +619,18 @@ ul.messagelist li { font-size: 13px; padding: 10px 10px 10px 65px; margin: 0 0 10px 0; - background: #dfd url(../img/icon-yes.svg) 40px 12px no-repeat; + background: var(--message-success-bg) url(../img/icon-yes.svg) 40px 12px no-repeat; background-size: 16px auto; - color: #333; + color: var(--body-fg); } ul.messagelist li.warning { - background: #ffc url(../img/icon-alert.svg) 40px 14px no-repeat; + background: var(--message-warning-bg) url(../img/icon-alert.svg) 40px 14px no-repeat; background-size: 14px auto; } ul.messagelist li.error { - background: #ffefef url(../img/icon-no.svg) 40px 12px no-repeat; + background: var(--message-error-bg) url(../img/icon-no.svg) 40px 12px no-repeat; background-size: 16px auto; } @@ -546,24 +640,26 @@ ul.messagelist li.error { display: block; padding: 10px 12px; margin: 0 0 10px 0; - color: #ba2121; - border: 1px solid #ba2121; + color: var(--error-fg); + border: 1px solid var(--error-fg); border-radius: 4px; - background-color: #fff; + background-color: var(--body-bg); background-position: 5px 12px; + overflow-wrap: break-word; } ul.errorlist { margin: 0 0 4px; padding: 0; - color: #ba2121; - background: #fff; + color: var(--error-fg); + background: var(--body-bg); } ul.errorlist li { font-size: 13px; display: block; margin-bottom: 4px; + overflow-wrap: break-word; } ul.errorlist li:first-child { @@ -587,7 +683,7 @@ td ul.errorlist li { .form-row.errors { margin: 0; border: none; - border-bottom: 1px solid #eee; + border-bottom: 1px solid var(--hairline-color); background: none; } @@ -597,7 +693,7 @@ td ul.errorlist li { .errors input, .errors select, .errors textarea, td ul.errorlist + input, td ul.errorlist + select, td ul.errorlist + textarea { - border: 1px solid #ba2121; + border: 1px solid var(--error-fg); } .description { @@ -608,20 +704,19 @@ td ul.errorlist + input, td ul.errorlist + select, td ul.errorlist + textarea { /* BREADCRUMBS */ div.breadcrumbs { - background: #79aec8; + background: var(--breadcrumbs-bg); padding: 10px 40px; border: none; - font-size: 14px; - color: #c4dce8; + color: var(--breadcrumbs-fg); text-align: left; } div.breadcrumbs a { - color: #fff; + color: var(--breadcrumbs-link-fg); } div.breadcrumbs a:focus, div.breadcrumbs a:hover { - color: #c4dce8; + color: var(--breadcrumbs-fg); } /* ACTION ICONS */ @@ -647,11 +742,11 @@ div.breadcrumbs a:focus, div.breadcrumbs a:hover { } a.deletelink:link, a.deletelink:visited { - color: #CC3434; + color: #CC3434; /* XXX Probably unused? */ } a.deletelink:focus, a.deletelink:hover { - color: #993333; + color: #993333; /* XXX Probably unused? */ text-decoration: none; } @@ -666,14 +761,6 @@ a.deletelink:focus, a.deletelink:hover { margin-top: -48px; } -.form-row .object-tools { - margin-top: 5px; - margin-bottom: 5px; - float: none; - height: 2em; - padding-left: 3.5em; -} - .object-tools li { display: block; float: left; @@ -689,29 +776,29 @@ a.deletelink:focus, a.deletelink:hover { display: block; float: left; padding: 3px 12px; - background: #999; + background: var(--object-tools-bg); + color: var(--object-tools-fg); font-weight: 400; font-size: 11px; text-transform: uppercase; letter-spacing: 0.5px; - color: #fff; } .object-tools a:focus, .object-tools a:hover { - background-color: #417690; + background-color: var(--object-tools-hover-bg); } .object-tools a:focus{ text-decoration: none; } -.object-tools a.viewsitelink, .object-tools a.golink,.object-tools a.addlink { +.object-tools a.viewsitelink, .object-tools a.addlink { background-repeat: no-repeat; background-position: right 7px center; padding-right: 26px; } -.object-tools a.viewsitelink, .object-tools a.golink { +.object-tools a.viewsitelink { background-image: url(../img/tooltag-arrowright.svg); } @@ -813,13 +900,13 @@ table#change-history tbody th { justify-content: space-between; align-items: center; padding: 10px 40px; - background: #417690; - color: #ffc; + background: var(--header-bg); + color: var(--header-color); overflow: hidden; } #header a:link, #header a:visited { - color: #fff; + color: var(--header-link-color); } #header a:focus , #header a:hover { @@ -835,11 +922,11 @@ table#change-history tbody th { margin: 0 20px 0 0; font-weight: 300; font-size: 24px; - color: #f5dd5d; + color: var(--accent); } #branding h1, #branding h1 a:link, #branding h1 a:visited { - color: #f5dd5d; + color: var(--accent); } #branding h2 { @@ -847,7 +934,7 @@ table#change-history tbody th { font-size: 14px; margin: -8px 0 8px 0; font-weight: normal; - color: #ffc; + color: var(--header-color); } #branding a:hover { @@ -871,14 +958,14 @@ table#change-history tbody th { #user-tools a:focus, #user-tools a:hover { text-decoration: none; - border-bottom-color: #79aec8; - color: #79aec8; + border-bottom-color: var(--primary); + color: var(--primary); } /* SIDEBAR */ #content-related { - background: #f8f8f8; + background: var(--darkened-bg); } #content-related .module { @@ -886,8 +973,7 @@ table#change-history tbody th { } #content-related h3 { - font-size: 14px; - color: #666; + color: var(--body-quiet-color); padding: 0 16px; margin: 0 0 16px; } @@ -916,22 +1002,22 @@ table#change-history tbody th { background: none; padding: 16px; margin-bottom: 16px; - border-bottom: 1px solid #eaeaea; + border-bottom: 1px solid var(--hairline-color); font-size: 18px; - color: #333; + color: var(--body-fg); } .delete-confirmation form input[type="submit"] { - background: #ba2121; + background: var(--delete-button-bg); border-radius: 4px; padding: 10px 15px; - color: #fff; + color: var(--button-fg); } .delete-confirmation form input[type="submit"]:active, .delete-confirmation form input[type="submit"]:focus, .delete-confirmation form input[type="submit"]:hover { - background: #a41515; + background: var(--delete-button-hover-bg); } .delete-confirmation form .cancel-link { @@ -939,17 +1025,17 @@ table#change-history tbody th { vertical-align: middle; height: 15px; line-height: 15px; - background: #ddd; border-radius: 4px; padding: 10px 15px; - color: #333; + color: var(--button-fg); + background: var(--close-button-bg); margin: 0 0 0 10px; } .delete-confirmation form .cancel-link:active, .delete-confirmation form .cancel-link:focus, .delete-confirmation form .cancel-link:hover { - background: #ccc; + background: var(--close-button-hover-bg); } /* POPUP */ diff --git a/app/static/admin/css/changelists.css b/app/static/admin/css/changelists.css index a164254..b4a1557 100644 --- a/app/static/admin/css/changelists.css +++ b/app/static/admin/css/changelists.css @@ -40,13 +40,13 @@ } #changelist .toplinks { - border-bottom: 1px solid #ddd; + border-bottom: 1px solid var(--hairline-color); } #changelist .paginator { - color: #666; - border-bottom: 1px solid #eee; - background: #fff; + color: var(--body-quiet-color); + border-bottom: 1px solid var(--hairline-color); + background: var(--body-bg); overflow: hidden; } @@ -68,7 +68,7 @@ } #changelist table tfoot { - color: #666; + color: var(--body-quiet-color); } /* TOOLBAR */ @@ -76,22 +76,22 @@ #toolbar { padding: 8px 10px; margin-bottom: 15px; - border-top: 1px solid #eee; - border-bottom: 1px solid #eee; - background: #f8f8f8; - color: #666; + border-top: 1px solid var(--hairline-color); + border-bottom: 1px solid var(--hairline-color); + background: var(--darkened-bg); + color: var(--body-quiet-color); } #toolbar form input { border-radius: 4px; font-size: 14px; padding: 5px; - color: #333; + color: var(--body-fg); } #toolbar #searchbar { height: 19px; - border: 1px solid #ccc; + border: 1px solid var(--border-color); padding: 2px 5px; margin: 0; vertical-align: top; @@ -100,24 +100,24 @@ } #toolbar #searchbar:focus { - border-color: #999; + border-color: var(--body-quiet-color); } #toolbar form input[type="submit"] { - border: 1px solid #ccc; + border: 1px solid var(--border-color); font-size: 13px; padding: 4px 8px; margin: 0; vertical-align: middle; - background: #fff; + background: var(--body-bg); box-shadow: 0 -15px 20px -10px rgba(0, 0, 0, 0.15) inset; cursor: pointer; - color: #333; + color: var(--body-fg); } #toolbar form input[type="submit"]:focus, #toolbar form input[type="submit"]:hover { - border-color: #999; + border-color: var(--body-quiet-color); } #changelist-search img { @@ -130,8 +130,7 @@ #changelist-filter { flex: 0 0 240px; order: 1; - width: 240px; - background: #f8f8f8; + background: var(--darkened-bg); border-left: none; margin: 0 0 0 30px; } @@ -147,7 +146,6 @@ #changelist-filter h3 { font-weight: 400; - font-size: 14px; padding: 0 15px; margin-bottom: 10px; } @@ -155,7 +153,7 @@ #changelist-filter ul { margin: 5px 0; padding: 0 15px 15px; - border-bottom: 1px solid #eaeaea; + border-bottom: 1px solid var(--hairline-color); } #changelist-filter ul:last-child { @@ -170,31 +168,31 @@ #changelist-filter a { display: block; - color: #999; + color: var(--body-quiet-color); text-overflow: ellipsis; overflow-x: hidden; } #changelist-filter li.selected { - border-left: 5px solid #eaeaea; + border-left: 5px solid var(--hairline-color); padding-left: 10px; margin-left: -15px; } #changelist-filter li.selected a { - color: #5b80b2; + color: var(--link-selected-fg); } #changelist-filter a:focus, #changelist-filter a:hover, #changelist-filter li.selected a:focus, #changelist-filter li.selected a:hover { - color: #036; + color: var(--link-hover-color); } #changelist-filter #changelist-filter-clear a { font-size: 13px; padding-bottom: 10px; - border-bottom: 1px solid #eaeaea; + border-bottom: 1px solid var(--hairline-color); } /* DATE DRILLDOWN */ @@ -215,12 +213,12 @@ } .change-list ul.toplinks .date-back a { - color: #999; + color: var(--body-quiet-color); } .change-list ul.toplinks .date-back a:focus, .change-list ul.toplinks .date-back a:hover { - color: #036; + color: var(--link-hover-color); } /* PAGINATOR */ @@ -231,26 +229,26 @@ padding-bottom: 10px; line-height: 22px; margin: 0; - border-top: 1px solid #ddd; + border-top: 1px solid var(--hairline-color); width: 100%; } .paginator a:link, .paginator a:visited { padding: 2px 6px; - background: #79aec8; + background: var(--button-bg); text-decoration: none; - color: #fff; + color: var(--button-fg); } .paginator a.showall { border: none; background: none; - color: #5b80b2; + color: var(--link-fg); } .paginator a.showall:focus, .paginator a.showall:hover { background: none; - color: #036; + color: var(--link-hover-color); } .paginator .end { @@ -266,7 +264,7 @@ .paginator a:focus, .paginator a:hover { color: white; - background: #036; + background: var(--link-hover-color); } /* ACTIONS */ @@ -281,22 +279,22 @@ } #changelist table tbody tr.selected { - background-color: #FFFFCC; + background-color: var(--selected-row); } #changelist .actions { padding: 10px; - background: #fff; + background: var(--body-bg); border-top: none; border-bottom: none; line-height: 24px; - color: #999; + color: var(--body-quiet-color); width: 100%; } -#changelist .actions.selected { - background: #fffccf; - border-top: 1px solid #fffee8; +#changelist .actions.selected { /* XXX Probably unused? */ + background: var(--body-bg); + border-top: 1px solid var(--body-bg); border-bottom: 1px solid #edecd6; } @@ -306,7 +304,6 @@ #changelist .actions span.question { font-size: 13px; margin: 0 0.5em; - display: none; } #changelist .actions:last-child { @@ -316,9 +313,8 @@ #changelist .actions select { vertical-align: top; height: 24px; - background: none; - color: #000; - border: 1px solid #ccc; + color: var(--body-fg); + border: 1px solid var(--border-color); border-radius: 4px; font-size: 14px; padding: 0 0 0 4px; @@ -327,7 +323,7 @@ } #changelist .actions select:focus { - border-color: #999; + border-color: var(--body-quiet-color); } #changelist .actions label { @@ -338,18 +334,18 @@ #changelist .actions .button { font-size: 13px; - border: 1px solid #ccc; + border: 1px solid var(--border-color); border-radius: 4px; - background: #fff; + background: var(--body-bg); box-shadow: 0 -15px 20px -10px rgba(0, 0, 0, 0.15) inset; cursor: pointer; height: 24px; line-height: 1; padding: 4px 8px; margin: 0; - color: #333; + color: var(--body-fg); } #changelist .actions .button:focus, #changelist .actions .button:hover { - border-color: #999; + border-color: var(--body-quiet-color); } diff --git a/app/static/admin/css/forms.css b/app/static/admin/css/forms.css index 89d5748..89b2270 100644 --- a/app/static/admin/css/forms.css +++ b/app/static/admin/css/forms.css @@ -6,7 +6,7 @@ overflow: hidden; padding: 10px; font-size: 13px; - border-bottom: 1px solid #eee; + border-bottom: 1px solid var(--hairline-color); } .form-row img, .form-row input { @@ -22,21 +22,17 @@ form .form-row p { padding-left: 0; } -.hidden { - display: none; -} - /* FORM LABELS */ label { font-weight: normal; - color: #666; + color: var(--body-quiet-color); font-size: 13px; } .required label, label.required { font-weight: bold; - color: #333; + color: var(--body-fg); } /* RADIO BUTTONS */ @@ -219,24 +215,24 @@ fieldset.collapsed h2, fieldset.collapsed { } fieldset.collapsed { - border: 1px solid #eee; + border: 1px solid var(--hairline-color); border-radius: 4px; overflow: hidden; } fieldset.collapsed h2 { - background: #f8f8f8; - color: #666; + background: var(--darkened-bg); + color: var(--body-quiet-color); } fieldset .collapse-toggle { - color: #fff; + color: var(--header-link-color); } fieldset.collapsed .collapse-toggle { background: transparent; display: inline; - color: #447e9b; + color: var(--link-fg); } /* MONOSPACE TEXTAREAS */ @@ -250,8 +246,8 @@ fieldset.monospace textarea { .submit-row { padding: 12px 14px; margin: 0 0 20px; - background: #f8f8f8; - border: 1px solid #eee; + background: var(--darkened-bg); + border: 1px solid var(--hairline-color); border-radius: 4px; text-align: right; overflow: hidden; @@ -283,35 +279,35 @@ body.popup .submit-row { .submit-row a.deletelink { display: block; - background: #ba2121; + background: var(--delete-button-bg); border-radius: 4px; padding: 10px 15px; height: 15px; line-height: 15px; - color: #fff; + color: var(--button-fg); } .submit-row a.closelink { display: inline-block; - background: #bbbbbb; + background: var(--close-button-bg); border-radius: 4px; padding: 10px 15px; height: 15px; line-height: 15px; margin: 0 0 0 5px; - color: #fff; + color: var(--button-fg); } .submit-row a.deletelink:focus, .submit-row a.deletelink:hover, .submit-row a.deletelink:active { - background: #a41515; + background: var(--delete-button-hover-bg); } .submit-row a.closelink:focus, .submit-row a.closelink:hover, .submit-row a.closelink:active { - background: #aaaaaa; + background: var(--close-button-hover-bg); } /* CUSTOM FORM FIELDS */ @@ -390,12 +386,12 @@ body.popup .submit-row { .inline-related h3 { margin: 0; - color: #666; + color: var(--body-quiet-color); padding: 5px; font-size: 13px; - background: #f8f8f8; - border-top: 1px solid #eee; - border-bottom: 1px solid #eee; + background: var(--darkened-bg); + border-top: 1px solid var(--hairline-color); + border-bottom: 1px solid var(--hairline-color); } .inline-related h3 span.delete { @@ -409,7 +405,7 @@ body.popup .submit-row { .inline-related fieldset { margin: 0; - background: #fff; + background: var(--body-bg); border: none; width: 100%; } @@ -421,7 +417,7 @@ body.popup .submit-row { text-align: left; font-weight: bold; background: #bcd; - color: #fff; + color: var(--body-bg); } .inline-group .tabular fieldset.module { @@ -460,7 +456,7 @@ body.popup .submit-row { overflow: hidden; font-size: 9px; font-weight: bold; - color: #666; + color: var(--body-quiet-color); _width: 700px; } @@ -477,15 +473,15 @@ body.popup .submit-row { .inline-group div.add-row, .inline-group .tabular tr.add-row td { - color: #666; - background: #f8f8f8; + color: var(--body-quiet-color); + background: var(--darkened-bg); padding: 8px 10px; - border-bottom: 1px solid #eee; + border-bottom: 1px solid var(--hairline-color); } .inline-group .tabular tr.add-row td { padding: 8px 10px; - border-bottom: 1px solid #eee; + border-bottom: 1px solid var(--hairline-color); } .inline-group ul.tools a.add, diff --git a/app/static/admin/css/login.css b/app/static/admin/css/login.css index 062b36e..10d9d22 100644 --- a/app/static/admin/css/login.css +++ b/app/static/admin/css/login.css @@ -1,7 +1,7 @@ /* LOGIN FORM */ .login { - background: #f8f8f8; + background: var(--darkened-bg); height: auto; } @@ -16,7 +16,7 @@ } .login #header h1 a { - color: #fff; + color: var(--header-link-color); } .login #content { @@ -24,8 +24,8 @@ } .login #container { - background: #fff; - border: 1px solid #eaeaea; + background: var(--body-bg); + border: 1px solid var(--hairline-color); border-radius: 4px; overflow: hidden; width: 28em; @@ -34,44 +34,25 @@ height: auto; } -.login #content-main { - width: 100%; -} - .login .form-row { padding: 4px 0; - float: left; - width: 100%; - border-bottom: none; } .login .form-row label { - padding-right: 0.5em; + display: block; line-height: 2em; - font-size: 1em; - clear: both; - color: #333; } .login .form-row #id_username, .login .form-row #id_password { - clear: both; padding: 8px; width: 100%; box-sizing: border-box; } -.login span.help { - font-size: 10px; - display: block; -} - .login .submit-row { - clear: both; - padding: 1em 0 0 9.4em; + padding: 1em 0 0 0; margin: 0; - border: none; - background: none; - text-align: left; + text-align: center; } .login .password-reset-link { diff --git a/app/static/admin/css/nav_sidebar.css b/app/static/admin/css/nav_sidebar.css index 784d087..f3c2fd8 100644 --- a/app/static/admin/css/nav_sidebar.css +++ b/app/static/admin/css/nav_sidebar.css @@ -12,22 +12,23 @@ justify-content: center; flex: 0 0 23px; width: 23px; - border-right: 1px solid #eaeaea; - background-color: #ffffff; + border: 0; + border-right: 1px solid var(--hairline-color); + background-color: var(--body-bg); cursor: pointer; font-size: 20px; - color: #447e9b; + color: var(--link-fg); padding: 0; } [dir="rtl"] .toggle-nav-sidebar { - border-left: 1px solid #eaeaea; + border-left: 1px solid var(--hairline-color); border-right: 0; } .toggle-nav-sidebar:hover, .toggle-nav-sidebar:focus { - background-color: #f6f6f6; + background-color: var(--darkened-bg); } #nav-sidebar { @@ -36,13 +37,13 @@ left: -276px; margin-left: -276px; border-top: 1px solid transparent; - border-right: 1px solid #eaeaea; - background-color: #ffffff; + border-right: 1px solid var(--hairline-color); + background-color: var(--body-bg); overflow: auto; } [dir="rtl"] #nav-sidebar { - border-left: 1px solid #eaeaea; + border-left: 1px solid var(--hairline-color); border-right: 0; left: 0; margin-left: 0; @@ -91,12 +92,12 @@ #nav-sidebar .current-app .section:link, #nav-sidebar .current-app .section:visited { - color: #ffc; + color: var(--header-color); font-weight: bold; } #nav-sidebar .current-model { - background: #ffc; + background: var(--selected-row); } .main > #nav-sidebar + .content { diff --git a/app/static/admin/css/responsive.css b/app/static/admin/css/responsive.css index 053e841..8c6dd81 100644 --- a/app/static/admin/css/responsive.css +++ b/app/static/admin/css/responsive.css @@ -140,7 +140,7 @@ input[type="submit"], button { } #changelist .actions select { - background: #fff; + background: var(--body-bg); } #changelist .actions .button { @@ -166,7 +166,7 @@ input[type="submit"], button { .filtered .actions, #changelist .paginator { - border-top-color: #eee; + border-top-color: var(--hairline-color); /* XXX Is this used at all? */ } #changelist .results + .paginator { @@ -213,7 +213,7 @@ input[type="submit"], button { fieldset .fieldBox + .fieldBox { margin-top: 10px; padding-top: 10px; - border-top: 1px solid #eee; + border-top: 1px solid var(--hairline-color); } textarea { @@ -399,11 +399,11 @@ input[type="submit"], button { .datetime .timezonewarning { display: block; font-size: 11px; - color: #999; + color: var(--body-quiet-color); } .datetimeshortcuts { - color: #ccc; + color: var(--border-color); /* XXX Redundant, .datetime span also sets #ccc */ } .form-row .datetime input.vDateField, .form-row .datetime input.vTimeField { @@ -655,7 +655,7 @@ input[type="submit"], button { margin-bottom: -3px; } - form .aligned ul.radiolist li + li { + form .aligned ul.radiolist:not(.inline) li + li { margin-top: 5px; } @@ -740,7 +740,7 @@ input[type="submit"], button { /* Inlines */ .inline-group[data-inline-type="stacked"] .inline-related { - border: 2px solid #eee; + border: 1px solid var(--hairline-color); border-radius: 4px; margin-top: 15px; overflow: auto; @@ -750,18 +750,19 @@ input[type="submit"], button { box-sizing: border-box; } - .inline-group[data-inline-type="stacked"] .inline-related + .inline-related { - margin-top: 30px; - } - .inline-group[data-inline-type="stacked"] .inline-related .module { padding: 0 10px; } - .inline-group[data-inline-type="stacked"] .inline-related .module .form-row:last-child { + .inline-group[data-inline-type="stacked"] .inline-related .module .form-row { + border-top: 1px solid var(--hairline-color); border-bottom: none; } + .inline-group[data-inline-type="stacked"] .inline-related .module .form-row:first-child { + border-top: none; + } + .inline-group[data-inline-type="stacked"] .inline-related h3 { padding: 10px; border-top-width: 0; @@ -791,7 +792,7 @@ input[type="submit"], button { .inline-group[data-inline-type="stacked"] div.add-row { margin-top: 15px; - border: 1px solid #eee; + border: 1px solid var(--hairline-color); border-radius: 4px; } @@ -885,9 +886,7 @@ input[type="submit"], button { } .login .form-row label { - display: block; margin: 0 0 5px; - padding: 0; line-height: 1.2; } @@ -895,7 +894,7 @@ input[type="submit"], button { padding: 15px 0 0; } - .login br, .login .submit-row label { + .login br { display: none; } @@ -963,7 +962,7 @@ input[type="submit"], button { } .timelist a { - background: #fff; + background: var(--body-bg); padding: 4px; } diff --git a/app/static/admin/css/rtl.css b/app/static/admin/css/rtl.css index a40aad0..0447f89 100644 --- a/app/static/admin/css/rtl.css +++ b/app/static/admin/css/rtl.css @@ -1,25 +1,3 @@ -body { - direction: rtl; -} - -/* LOGIN */ - -.login .form-row { - float: right; -} - -.login .form-row label { - float: right; - padding-left: 0.5em; - padding-right: 0; - text-align: left; -} - -.login .submit-row { - clear: both; - padding: 1em 9.4em 0 0; -} - /* GLOBAL */ th { @@ -119,7 +97,7 @@ thead th.sorted .text { border-left: none; padding-left: 10px; margin-left: 0; - border-right: 5px solid #eaeaea; + border-right: 5px solid var(--hairline-color); padding-right: 10px; margin-right: -15px; } diff --git a/app/static/admin/css/widgets.css b/app/static/admin/css/widgets.css index 14ef12d..c7d6456 100644 --- a/app/static/admin/css/widgets.css +++ b/app/static/admin/css/widgets.css @@ -22,26 +22,25 @@ } .selector-available h2, .selector-chosen h2 { - border: 1px solid #ccc; + border: 1px solid var(--border-color); border-radius: 4px 4px 0 0; } .selector-chosen h2 { - background: #79aec8; - color: #fff; + background: var(--primary); + color: var(--header-link-color); } .selector .selector-available h2 { - background: #f8f8f8; - color: #666; + background: var(--darkened-bg); + color: var(--body-quiet-color); } .selector .selector-filter { - background: white; - border: 1px solid #ccc; + border: 1px solid var(--border-color); border-width: 0 1px; padding: 8px; - color: #999; + color: var(--body-quiet-color); font-size: 10px; margin: 0; text-align: left; @@ -66,7 +65,7 @@ .selector ul.selector-chooser { float: left; width: 22px; - background-color: #eee; + background-color: var(--selected-bg); border-radius: 10px; margin: 10em 5px 0 5px; padding: 0; @@ -91,7 +90,7 @@ text-indent: -3000px; overflow: hidden; cursor: default; - opacity: 0.3; + opacity: 0.55; } .active.selector-add, .active.selector-remove { @@ -126,14 +125,14 @@ a.selector-chooseall, a.selector-clearall { overflow: hidden; font-weight: bold; line-height: 16px; - color: #666; + color: var(--body-quiet-color); text-decoration: none; - opacity: 0.3; + opacity: 0.55; } a.active.selector-chooseall:focus, a.active.selector-clearall:focus, a.active.selector-chooseall:hover, a.active.selector-clearall:hover { - color: #447e9b; + color: var(--link-fg); } a.active.selector-chooseall, a.active.selector-clearall { @@ -261,7 +260,7 @@ p.datetime { line-height: 20px; margin: 0; padding: 0; - color: #666; + color: var(--body-quiet-color); font-weight: bold; } @@ -269,7 +268,7 @@ p.datetime { white-space: nowrap; font-weight: normal; font-size: 11px; - color: #ccc; + color: var(--body-quiet-color); } .datetime input, .form-row .datetime input.vDateField, .form-row .datetime input.vTimeField { @@ -313,7 +312,7 @@ table p.datetime { .timezonewarning { font-size: 11px; - color: #999; + color: var(--body-quiet-color); } /* URL */ @@ -322,7 +321,7 @@ p.url { line-height: 20px; margin: 0; padding: 0; - color: #666; + color: var(--body-quiet-color); font-size: 11px; font-weight: bold; } @@ -337,7 +336,7 @@ p.file-upload { line-height: 20px; margin: 0; padding: 0; - color: #666; + color: var(--body-quiet-color); font-size: 11px; font-weight: bold; } @@ -355,7 +354,7 @@ p.file-upload { } span.clearable-file-input label { - color: #333; + color: var(--body-fg); font-size: 11px; display: inline; float: none; @@ -368,8 +367,9 @@ span.clearable-file-input label { font-size: 12px; width: 19em; text-align: center; - background: white; - border: 1px solid #ddd; + background: var(--body-bg); + color: var(--body-fg); + border: 1px solid var(--hairline-color); border-radius: 4px; box-shadow: 0 2px 4px rgba(0, 0, 0, 0.15); overflow: hidden; @@ -397,20 +397,20 @@ span.clearable-file-input label { margin: 0; text-align: center; border-top: none; - background: #f5dd5d; font-weight: 700; font-size: 12px; color: #333; + background: var(--accent); } .calendar th { padding: 8px 5px; - background: #f8f8f8; - border-bottom: 1px solid #ddd; + background: var(--darkened-bg); + border-bottom: 1px solid var(--border-color); font-weight: 400; font-size: 12px; text-align: center; - color: #666; + color: var(--body-quiet-color); } .calendar td { @@ -418,17 +418,17 @@ span.clearable-file-input label { font-size: 12px; text-align: center; padding: 0; - border-top: 1px solid #eee; + border-top: 1px solid var(--hairline-color); border-bottom: none; } .calendar td.selected a { - background: #79aec8; - color: #fff; + background: var(--primary); + color: var(--button-fg); } .calendar td.nonday { - background: #f8f8f8; + background: var(--darkened-bg); } .calendar td.today a { @@ -440,17 +440,17 @@ span.clearable-file-input label { font-weight: 400; padding: 6px; text-decoration: none; - color: #444; + color: var(--body-quiet-color); } .calendar td a:focus, .timelist a:focus, .calendar td a:hover, .timelist a:hover { - background: #79aec8; + background: var(--primary); color: white; } .calendar td a:active, .timelist a:active { - background: #417690; + background: var(--header-bg); color: white; } @@ -464,16 +464,16 @@ span.clearable-file-input label { .calendarnav a:link, #calendarnav a:visited, #calendarnav a:focus, #calendarnav a:hover { - color: #999; + color: var(--body-quiet-color); } .calendar-shortcuts { - background: white; + background: var(--body-bg); + color: var(--body-quiet-color); font-size: 11px; line-height: 11px; - border-top: 1px solid #eee; + border-top: 1px solid var(--hairline-color); padding: 8px 0; - color: #ccc; } .calendarbox .calendarnav-previous, .calendarbox .calendarnav-next { @@ -511,8 +511,8 @@ span.clearable-file-input label { padding: 4px 0; font-size: 12px; background: #eee; - border-top: 1px solid #ddd; - color: #333; + border-top: 1px solid var(--border-color); + color: var(--body-fg); } .calendar-cancel:focus, .calendar-cancel:hover { diff --git a/app/static/admin/fonts/LICENSE.txt b/app/static/admin/fonts/LICENSE.txt index d645695..75b5248 100644 --- a/app/static/admin/fonts/LICENSE.txt +++ b/app/static/admin/fonts/LICENSE.txt @@ -1,202 +1,202 @@ - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/app/static/admin/js/SelectBox.js b/app/static/admin/js/SelectBox.js index 1927b4c..ace6d9d 100644 --- a/app/static/admin/js/SelectBox.js +++ b/app/static/admin/js/SelectBox.js @@ -13,6 +13,7 @@ redisplay: function(id) { // Repopulate HTML select box from cache const box = document.getElementById(id); + const scroll_value_from_top = box.scrollTop; box.innerHTML = ''; for (const node of SelectBox.cache[id]) { if (node.displayed) { @@ -22,6 +23,7 @@ box.appendChild(new_option); } } + box.scrollTop = scroll_value_from_top; }, filter: function(id, text) { // Redisplay the HTML select box, displaying only the choices containing ALL @@ -31,7 +33,7 @@ node.displayed = 1; const node_text = node.text.toLowerCase(); for (const token of tokens) { - if (node_text.indexOf(token) === -1) { + if (!node_text.includes(token)) { node.displayed = 0; break; // Once the first token isn't found we're done } diff --git a/app/static/admin/js/actions.js b/app/static/admin/js/actions.js index dae6992..2830e91 100644 --- a/app/static/admin/js/actions.js +++ b/app/static/admin/js/actions.js @@ -1,154 +1,201 @@ /*global gettext, interpolate, ngettext*/ 'use strict'; { - const $ = django.jQuery; - let lastChecked; + function show(selector) { + document.querySelectorAll(selector).forEach(function(el) { + el.classList.remove('hidden'); + }); + } - $.fn.actions = function(opts) { - const options = $.extend({}, $.fn.actions.defaults, opts); - const actionCheckboxes = $(this); - let list_editable_changed = false; - const showQuestion = function() { - $(options.acrossClears).hide(); - $(options.acrossQuestions).show(); - $(options.allContainer).hide(); - }, - showClear = function() { - $(options.acrossClears).show(); - $(options.acrossQuestions).hide(); - $(options.actionContainer).toggleClass(options.selectedClass); - $(options.allContainer).show(); - $(options.counterContainer).hide(); - }, - reset = function() { - $(options.acrossClears).hide(); - $(options.acrossQuestions).hide(); - $(options.allContainer).hide(); - $(options.counterContainer).show(); - }, - clearAcross = function() { - reset(); - $(options.acrossInput).val(0); - $(options.actionContainer).removeClass(options.selectedClass); - }, - checker = function(checked) { - if (checked) { - showQuestion(); - } else { - reset(); - } - $(actionCheckboxes).prop("checked", checked) - .parent().parent().toggleClass(options.selectedClass, checked); - }, - updateCounter = function() { - const sel = $(actionCheckboxes).filter(":checked").length; - // data-actions-icnt is defined in the generated HTML - // and contains the total amount of objects in the queryset - const actions_icnt = $('.action-counter').data('actionsIcnt'); - $(options.counterContainer).html(interpolate( - ngettext('%(sel)s of %(cnt)s selected', '%(sel)s of %(cnt)s selected', sel), { - sel: sel, - cnt: actions_icnt - }, true)); - $(options.allToggle).prop("checked", function() { - let value; - if (sel === actionCheckboxes.length) { - value = true; - showQuestion(); - } else { - value = false; - clearAcross(); - } - return value; - }); - }; - // Show counter by default - $(options.counterContainer).show(); - // Check state of checkboxes and reinit state if needed - $(this).filter(":checked").each(function(i) { - $(this).parent().parent().toggleClass(options.selectedClass); - updateCounter(); - if ($(options.acrossInput).val() === 1) { - showClear(); - } + function hide(selector) { + document.querySelectorAll(selector).forEach(function(el) { + el.classList.add('hidden'); + }); + } + + function showQuestion(options) { + hide(options.acrossClears); + show(options.acrossQuestions); + hide(options.allContainer); + } + + function showClear(options) { + show(options.acrossClears); + hide(options.acrossQuestions); + document.querySelector(options.actionContainer).classList.remove(options.selectedClass); + show(options.allContainer); + hide(options.counterContainer); + } + + function reset(options) { + hide(options.acrossClears); + hide(options.acrossQuestions); + hide(options.allContainer); + show(options.counterContainer); + } + + function clearAcross(options) { + reset(options); + const acrossInputs = document.querySelectorAll(options.acrossInput); + acrossInputs.forEach(function(acrossInput) { + acrossInput.value = 0; }); - $(options.allToggle).show().on('click', function() { - checker($(this).prop("checked")); - updateCounter(); + document.querySelector(options.actionContainer).classList.remove(options.selectedClass); + } + + function checker(actionCheckboxes, options, checked) { + if (checked) { + showQuestion(options); + } else { + reset(options); + } + actionCheckboxes.forEach(function(el) { + el.checked = checked; + el.closest('tr').classList.toggle(options.selectedClass, checked); }); - $("a", options.acrossQuestions).on('click', function(event) { - event.preventDefault(); - $(options.acrossInput).val(1); - showClear(); + } + + function updateCounter(actionCheckboxes, options) { + const sel = Array.from(actionCheckboxes).filter(function(el) { + return el.checked; + }).length; + const counter = document.querySelector(options.counterContainer); + // data-actions-icnt is defined in the generated HTML + // and contains the total amount of objects in the queryset + const actions_icnt = Number(counter.dataset.actionsIcnt); + counter.textContent = interpolate( + ngettext('%(sel)s of %(cnt)s selected', '%(sel)s of %(cnt)s selected', sel), { + sel: sel, + cnt: actions_icnt + }, true); + const allToggle = document.getElementById(options.allToggleId); + allToggle.checked = sel === actionCheckboxes.length; + if (allToggle.checked) { + showQuestion(options); + } else { + clearAcross(options); + } + } + + const defaults = { + actionContainer: "div.actions", + counterContainer: "span.action-counter", + allContainer: "div.actions span.all", + acrossInput: "div.actions input.select-across", + acrossQuestions: "div.actions span.question", + acrossClears: "div.actions span.clear", + allToggleId: "action-toggle", + selectedClass: "selected" + }; + + window.Actions = function(actionCheckboxes, options) { + options = Object.assign({}, defaults, options); + let list_editable_changed = false; + let lastChecked = null; + let shiftPressed = false; + + document.addEventListener('keydown', (event) => { + shiftPressed = event.shiftKey; }); - $("a", options.acrossClears).on('click', function(event) { - event.preventDefault(); - $(options.allToggle).prop("checked", false); - clearAcross(); - checker(0); - updateCounter(); + + document.addEventListener('keyup', (event) => { + shiftPressed = event.shiftKey; }); - lastChecked = null; - $(actionCheckboxes).on('click', function(event) { - if (!event) { event = window.event; } - const target = event.target ? event.target : event.srcElement; - if (lastChecked && $.data(lastChecked) !== $.data(target) && event.shiftKey === true) { - let inrange = false; - $(lastChecked).prop("checked", target.checked) - .parent().parent().toggleClass(options.selectedClass, target.checked); - $(actionCheckboxes).each(function() { - if ($.data(this) === $.data(lastChecked) || $.data(this) === $.data(target)) { - inrange = (inrange) ? false : true; - } - if (inrange) { - $(this).prop("checked", target.checked) - .parent().parent().toggleClass(options.selectedClass, target.checked); - } + + document.getElementById(options.allToggleId).addEventListener('click', function(event) { + checker(actionCheckboxes, options, this.checked); + updateCounter(actionCheckboxes, options); + }); + + document.querySelectorAll(options.acrossQuestions + " a").forEach(function(el) { + el.addEventListener('click', function(event) { + event.preventDefault(); + const acrossInputs = document.querySelectorAll(options.acrossInput); + acrossInputs.forEach(function(acrossInput) { + acrossInput.value = 1; }); - } - $(target).parent().parent().toggleClass(options.selectedClass, target.checked); - lastChecked = target; - updateCounter(); + showClear(options); + }); }); - $('form#changelist-form table#result_list tr').on('change', 'td:gt(0) :input', function() { - list_editable_changed = true; + + document.querySelectorAll(options.acrossClears + " a").forEach(function(el) { + el.addEventListener('click', function(event) { + event.preventDefault(); + document.getElementById(options.allToggleId).checked = false; + clearAcross(options); + checker(actionCheckboxes, options, false); + updateCounter(actionCheckboxes, options); + }); }); - $('form#changelist-form button[name="index"]').on('click', function(event) { - if (list_editable_changed) { - return confirm(gettext("You have unsaved changes on individual editable fields. If you run an action, your unsaved changes will be lost.")); + + function affectedCheckboxes(target, withModifier) { + const multiSelect = (lastChecked && withModifier && lastChecked !== target); + if (!multiSelect) { + return [target]; } - }); - $('form#changelist-form input[name="_save"]').on('click', function(event) { - let action_changed = false; - $('select option:selected', options.actionContainer).each(function() { - if ($(this).val()) { - action_changed = true; + const checkboxes = Array.from(actionCheckboxes); + const targetIndex = checkboxes.findIndex(el => el === target); + const lastCheckedIndex = checkboxes.findIndex(el => el === lastChecked); + const startIndex = Math.min(targetIndex, lastCheckedIndex); + const endIndex = Math.max(targetIndex, lastCheckedIndex); + const filtered = checkboxes.filter((el, index) => (startIndex <= index) && (index <= endIndex)); + return filtered; + }; + + Array.from(document.getElementById('result_list').tBodies).forEach(function(el) { + el.addEventListener('change', function(event) { + const target = event.target; + if (target.classList.contains('action-select')) { + const checkboxes = affectedCheckboxes(target, shiftPressed); + checker(checkboxes, options, target.checked); + updateCounter(actionCheckboxes, options); + lastChecked = target; + } else { + list_editable_changed = true; } }); - if (action_changed) { - if (list_editable_changed) { - return confirm(gettext("You have selected an action, but you haven’t saved your changes to individual fields yet. Please click OK to save. You’ll need to re-run the action.")); - } else { - return confirm(gettext("You have selected an action, and you haven’t made any changes on individual fields. You’re probably looking for the Go button rather than the Save button.")); + }); + + document.querySelector('#changelist-form button[name=index]').addEventListener('click', function() { + if (list_editable_changed) { + const confirmed = confirm(gettext("You have unsaved changes on individual editable fields. If you run an action, your unsaved changes will be lost.")); + if (!confirmed) { + event.preventDefault(); } } }); + + const el = document.querySelector('#changelist-form input[name=_save]'); + // The button does not exist if no fields are editable. + if (el) { + el.addEventListener('click', function(event) { + if (document.querySelector('[name=action]').value) { + const text = list_editable_changed + ? gettext("You have selected an action, but you haven’t saved your changes to individual fields yet. Please click OK to save. You’ll need to re-run the action.") + : gettext("You have selected an action, and you haven’t made any changes on individual fields. You’re probably looking for the Go button rather than the Save button."); + if (!confirm(text)) { + event.preventDefault(); + } + } + }); + } }; - /* Setup plugin defaults */ - $.fn.actions.defaults = { - actionContainer: "div.actions", - counterContainer: "span.action-counter", - allContainer: "div.actions span.all", - acrossInput: "div.actions input.select-across", - acrossQuestions: "div.actions span.question", - acrossClears: "div.actions span.clear", - allToggle: "#action-toggle", - selectedClass: "selected" - }; - $(document).ready(function() { - const $actionsEls = $('tr input.action-select'); - if ($actionsEls.length > 0) { - $actionsEls.actions(); + + // Call function fn when the DOM is loaded and ready. If it is already + // loaded, call the function now. + // http://youmightnotneedjquery.com/#ready + function ready(fn) { + if (document.readyState !== 'loading') { + fn(); + } else { + document.addEventListener('DOMContentLoaded', fn); + } + } + + ready(function() { + const actionsEls = document.querySelectorAll('tr input.action-select'); + if (actionsEls.length > 0) { + Actions(actionsEls); } }); } diff --git a/app/static/admin/js/actions.min.js b/app/static/admin/js/actions.min.js deleted file mode 100644 index 29fd0d8..0000000 --- a/app/static/admin/js/actions.min.js +++ /dev/null @@ -1,7 +0,0 @@ -'use strict';{const a=django.jQuery;let e;a.fn.actions=function(g){const b=a.extend({},a.fn.actions.defaults,g),f=a(this);let k=!1;const l=function(){a(b.acrossClears).hide();a(b.acrossQuestions).show();a(b.allContainer).hide()},m=function(){a(b.acrossClears).show();a(b.acrossQuestions).hide();a(b.actionContainer).toggleClass(b.selectedClass);a(b.allContainer).show();a(b.counterContainer).hide()},n=function(){a(b.acrossClears).hide();a(b.acrossQuestions).hide();a(b.allContainer).hide();a(b.counterContainer).show()}, -p=function(){n();a(b.acrossInput).val(0);a(b.actionContainer).removeClass(b.selectedClass)},q=function(c){c?l():n();a(f).prop("checked",c).parent().parent().toggleClass(b.selectedClass,c)},h=function(){const c=a(f).filter(":checked").length,d=a(".action-counter").data("actionsIcnt");a(b.counterContainer).html(interpolate(ngettext("%(sel)s of %(cnt)s selected","%(sel)s of %(cnt)s selected",c),{sel:c,cnt:d},!0));a(b.allToggle).prop("checked",function(){let a;c===f.length?(a=!0,l()):(a=!1,p());return a})}; -a(b.counterContainer).show();a(this).filter(":checked").each(function(c){a(this).parent().parent().toggleClass(b.selectedClass);h();1===a(b.acrossInput).val()&&m()});a(b.allToggle).show().on("click",function(){q(a(this).prop("checked"));h()});a("a",b.acrossQuestions).on("click",function(c){c.preventDefault();a(b.acrossInput).val(1);m()});a("a",b.acrossClears).on("click",function(c){c.preventDefault();a(b.allToggle).prop("checked",!1);p();q(0);h()});e=null;a(f).on("click",function(c){c||(c=window.event); -const d=c.target?c.target:c.srcElement;if(e&&a.data(e)!==a.data(d)&&!0===c.shiftKey){let c=!1;a(e).prop("checked",d.checked).parent().parent().toggleClass(b.selectedClass,d.checked);a(f).each(function(){if(a.data(this)===a.data(e)||a.data(this)===a.data(d))c=c?!1:!0;c&&a(this).prop("checked",d.checked).parent().parent().toggleClass(b.selectedClass,d.checked)})}a(d).parent().parent().toggleClass(b.selectedClass,d.checked);e=d;h()});a("form#changelist-form table#result_list tr").on("change","td:gt(0) :input", -function(){k=!0});a('form#changelist-form button[name="index"]').on("click",function(a){if(k)return confirm(gettext("You have unsaved changes on individual editable fields. If you run an action, your unsaved changes will be lost."))});a('form#changelist-form input[name="_save"]').on("click",function(c){let d=!1;a("select option:selected",b.actionContainer).each(function(){a(this).val()&&(d=!0)});if(d)return k?confirm(gettext("You have selected an action, but you haven\u2019t saved your changes to individual fields yet. Please click OK to save. You\u2019ll need to re-run the action.")): -confirm(gettext("You have selected an action, and you haven\u2019t made any changes on individual fields. You\u2019re probably looking for the Go button rather than the Save button."))})};a.fn.actions.defaults={actionContainer:"div.actions",counterContainer:"span.action-counter",allContainer:"div.actions span.all",acrossInput:"div.actions input.select-across",acrossQuestions:"div.actions span.question",acrossClears:"div.actions span.clear",allToggle:"#action-toggle",selectedClass:"selected"};a(document).ready(function(){const g= -a("tr input.action-select");0=h.val()-f.val()&&k.parent().hide();p(d.closest(".inline-group"));a.added&&a.added(d);b(document).trigger("formset:added",[d,a.prefix])}, -r=function(b){b.is("tr")?b.children(":last").append('
'+a.deleteText+"
"):b.is("ul")||b.is("ol")?b.append('
  • '+a.deleteText+"
  • "):b.children(":first").append(''+a.deleteText+"");b.find("a."+a.deleteCssClass).on("click",u.bind(this))},u=function(g){g.preventDefault();var d=b(g.target).closest("."+a.formCssClass);g=d.closest(".inline-group"); -var f=d.prev();f.length&&f.hasClass("row-form-errors")&&f.remove();d.remove();--n;a.removed&&a.removed(d);b(document).trigger("formset:removed",[d,a.prefix]);d=b("."+a.formCssClass);b("#id_"+a.prefix+"-TOTAL_FORMS").val(d.length);(""===h.val()||0'+a.addText+"");k=l.find("tr:last a")}else e.filter(":last").after('"), -k=e.filter(":last").next().find("a");k.on("click",t)})();c=""===h.val()||0 tr.form-row",b(c).tabularFormset(c,a.options)}})})}; diff --git a/app/static/admin/js/popup_response.js b/app/static/admin/js/popup_response.js index 2b1d3dd..dc24ef2 100644 --- a/app/static/admin/js/popup_response.js +++ b/app/static/admin/js/popup_response.js @@ -1,16 +1,48 @@ -/*global opener */ -'use strict'; -{ - const initData = JSON.parse(document.getElementById('django-admin-popup-response-constants').dataset.popupResponse); - switch(initData.action) { - case 'change': - opener.dismissChangeRelatedObjectPopup(window, initData.value, initData.obj, initData.new_value); - break; - case 'delete': - opener.dismissDeleteRelatedObjectPopup(window, initData.value); - break; - default: - opener.dismissAddRelatedObjectPopup(window, initData.value, initData.obj); - break; +(function() { + + 'use strict'; + + var windowRef = window; + var windowRefProxy; + var windowName, widgetName; + var openerRef = windowRef.opener; + if (!openerRef) { + // related modal is active + openerRef = windowRef.parent; + windowName = windowRef.name; + widgetName = windowName.replace(/^(change|add|delete|lookup)_/, ''); + if (typeof(openerRef.id_to_windowname) === 'function') { + // django < 3.1 compatibility + widgetName = openerRef.id_to_windowname(widgetName); + } + windowRefProxy = { + name: widgetName, + location: windowRef.location, + close: function() { + openerRef.dismissRelatedObjectModal(); + } + }; + windowRef = windowRefProxy; } -} + + // default django popup_response.js + var initData = JSON.parse(document.getElementById('django-admin-popup-response-constants').dataset.popupResponse); + switch (initData.action) { + case 'change': + if (typeof(openerRef.dismissChangeRelatedObjectPopup) === 'function') { + openerRef.dismissChangeRelatedObjectPopup(windowRef, initData.value, initData.obj, initData.new_value); + } + break; + case 'delete': + if (typeof(openerRef.dismissDeleteRelatedObjectPopup) === 'function') { + openerRef.dismissDeleteRelatedObjectPopup(windowRef, initData.value); + } + break; + default: + if (typeof(openerRef.dismissAddRelatedObjectPopup) === 'function') { + openerRef.dismissAddRelatedObjectPopup(windowRef, initData.value, initData.obj); + } + break; + } + +})(); diff --git a/app/static/admin/js/prepopulate.min.js b/app/static/admin/js/prepopulate.min.js deleted file mode 100644 index 11ead49..0000000 --- a/app/static/admin/js/prepopulate.min.js +++ /dev/null @@ -1 +0,0 @@ -'use strict';{const b=django.jQuery;b.fn.prepopulate=function(d,f,g){return this.each(function(){const a=b(this),h=function(){if(!a.data("_changed")){var e=[];b.each(d,function(a,c){c=b(c);0 fieldset.module'); + fieldsetEl.addClass('collapse'); + var fieldsetHasErrors = (fieldsetEl.children('.errors').length > 0); + if (fieldsetHasErrors === true) { + fieldsetCollapsed = false; + } + if (fieldsetCollapsed === true) { + fieldsetEl.addClass('collapsed'); + } + var collapseToggleText = (fieldsetCollapsed ? gettext('Show') : gettext('Hide')); + var collapseToggleHTML = ' (' + collapseToggleText + ')'; + var headerEl = fieldsetEl.find('> h2,> h3'); + if (headerEl.find(".collapse-toggle").length === 0) { + // don't add collapse toggle button if already present + headerEl.append(collapseToggleHTML); + } + } + + var stackedInlinesOptionSel = '.admin-interface.collapsible-stacked-inlines'; + var stackedInlinesSel = stackedInlinesOptionSel + ' .inline-group[data-inline-type="stacked"]'; + var stackedInlinesCollapsed = $(stackedInlinesOptionSel).hasClass('collapsible-stacked-inlines-collapsed'); + + var tabularInlinesOptionSel = '.admin-interface.collapsible-tabular-inlines'; + var tabularInlinesSel = tabularInlinesOptionSel + ' .inline-group[data-inline-type="tabular"] .inline-related.tabular'; + var tabularInlinesCollapsed = $(stackedInlinesOptionSel).hasClass('collapsible-tabular-inlines-collapsed'); + + $(stackedInlinesSel).each(function() { + collapsibleInline(this, stackedInlinesCollapsed); + }); + + $(tabularInlinesSel).each(function() { + collapsibleInline(this, tabularInlinesCollapsed); + }); + + }); + + })(django.jQuery); +} diff --git a/app/static/admin_interface/css/admin-interface.css b/app/static/admin_interface/css/admin-interface.css new file mode 100644 index 0000000..268c1af --- /dev/null +++ b/app/static/admin_interface/css/admin-interface.css @@ -0,0 +1,515 @@ +@media (prefers-color-scheme: dark) { + :root .admin-interface { + --primary: #79aec8; + --secondary: #417690; + --accent: #f5dd5d; + --primary-fg: #fff; + --body-fg: #333; + --body-bg: #fff; + --body-quiet-color: #666; + --body-loud-color: #000; + --header-color: #ffc; + --header-branding-color: var(--accent); + --header-bg: var(--secondary); + --header-link-color: var(--primary-fg); + --breadcrumbs-fg: #c4dce8; + --breadcrumbs-link-fg: var(--body-bg); + --breadcrumbs-bg: var(--primary); + --link-fg: #447e9b; + --link-hover-color: #036; + --link-selected-fg: #5b80b2; + --hairline-color: #e8e8e8; + --border-color: #ccc; + --error-fg: #ba2121; + --message-success-bg: #dfd; + --message-warning-bg: #ffc; + --message-error-bg: #ffefef; + --darkened-bg: #f8f8f8; + --selected-bg: #e4e4e4; + --selected-row: #ffc; + --button-fg: #fff; + --button-bg: var(--primary); + --button-hover-bg: #609ab6; + --default-button-bg: var(--secondary); + --default-button-hover-bg: #205067; + --close-button-bg: #888; + --close-button-hover-bg: #747474; + --delete-button-bg: #ba2121; + --delete-button-hover-bg: #a41515; + --object-tools-fg: var(--button-fg); + --object-tools-bg: var(--close-button-bg); + --object-tools-hover-bg: var(--close-button-hover-bg); + } +} + +.admin-interface { + overflow-x: hidden; +} + +.admin-interface #header { + background: var(--admin-interface-header-background-color); + color: var(--admin-interface-header-text-color); +} + +.admin-interface #header + #main { + border-top: var(--admin-interface-main-border-top); +} + +.admin-interface .environment-label { +} + +.admin-interface .environment-label::before { + content: ""; + display: inline-block; + width: 8px; + height: 8px; + background-color: var(--admin-interface-env-color); + border-radius: 100%; + margin-right: 6px; + box-sizing: border-box; + -moz-box-sizing: border-box; + -webkit-box-sizing: border-box; +} + +.admin-interface .environment-label::after { + content: " - "; +} + +@media (max-width: 1024px) { + .admin-interface .environment-label::after { + content: ""; + } +} + +.admin-interface .language-chooser { + display: inline-block; + position: absolute; + top: 15px; + right: 15px; + z-index: 10; +} + +@media (min-width: 768px) { + .admin-interface .language-chooser { + right: 30px; + } +} + +@media (min-width: 1024px) { + .admin-interface .language-chooser { + position: static; + float: right; + margin-left: 20px; + } +} + +.admin-interface .language-chooser-hidden-form { + display: none; +} + +.admin-interface .language-chooser-select-form { + display: inline-block; +} + +.admin-interface #branding h1, +.admin-interface.login #header h1, +.admin-interface.login #header h1 a { + color: var(--admin-interface-title-color); +} + +.admin-interface #branding h1 a { + color: inherit; +} + +.admin-interface #branding h1 .logo.default { + background-color: transparent; + background-repeat: no-repeat; + background-position: center center; + background-size: 104px 36px; + background-image: var(--admin-interface-logo-default-background-image); +} + +.admin-interface #branding h1 img.logo, +.admin-interface.login #header #branding h1 img.logo { + max-width: var(--admin-interface-logo-max-width); + max-height: var(--admin-interface-logo-max-height); +} + +.admin-interface #header #user-tools a, +.admin-interface #header #user-tools #logout-form button { + color: var(--admin-interface-header-link-color); +} + +.admin-interface #header #user-tools a:hover, +.admin-interface #header #user-tools a:active, +.admin-interface #header #user-tools #logout-form button:hover, +.admin-interface #header #user-tools #logout-form button:active { + color: var(--admin-interface-header-link-hover-color); + border-bottom-color: rgba(255, 255, 255, 0.5); +} + +.admin-interface #header #user-tools button.theme-toggle svg { + color: transparent; + fill: var(--admin-interface-header-link-color); +} + +.admin-interface #header #user-tools button.theme-toggle:hover svg, +.admin-interface #header #user-tools button.theme-toggle:active svg { + color: transparent; + fill: var(--admin-interface-header-link-hover-color); +} + +.admin-interface #nav-sidebar .current-app .section:link, +.admin-interface #nav-sidebar .current-app .section:visited { + color: var(--admin-interface-module-link-selected-color); + font-weight: normal; +} + +.admin-interface #nav-sidebar .current-app .section:focus, +.admin-interface #nav-sidebar .current-app .section:hover { + color: var(--admin-interface-module-link-hover-color); +} + +.admin-interface #nav-sidebar .current-model { + background: var(--admin-interface-module-background-selected-color); +} + +.admin-interface #changelist table tbody tr.selected { + background-color: var(--admin-interface-module-background-selected-color); +} + +.admin-interface .module h2, +.admin-interface .module caption, +.admin-interface .module.filtered h2 { + background: var(--admin-interface-module-background-color); + color: var(--admin-interface-module-text-color); +} + +.admin-interface .module a.section:link, +.admin-interface .module a.section:visited { + color: var(--admin-interface-module-link-color); +} + +.admin-interface .module a.section:active, +.admin-interface .module a.section:hover { + color: var(--admin-interface-module-link-hover-color); +} + +.admin-interface div.breadcrumbs { + background: var(--admin-interface-module-background-color); + color: var(--admin-interface-module-text-color); +} + +.admin-interface div.breadcrumbs a { + color: var(--admin-interface-module-link-color); +} + +.admin-interface div.breadcrumbs a:active, +.admin-interface div.breadcrumbs a:focus, +.admin-interface div.breadcrumbs a:hover { + color: var(--admin-interface-module-link-hover-color); +} + +.admin-interface fieldset.collapse a.collapse-toggle, +.admin-interface fieldset.collapse.collapsed a.collapse-toggle, +.admin-interface .inline-group .inline-related fieldset.module a.collapse-toggle, +.admin-interface .inline-group .inline-related fieldset.module.collapsed a.collapse-toggle { + color: var(--admin-interface-module-link-color); +} + +.admin-interface fieldset.collapse a.collapse-toggle:hover, +.admin-interface fieldset.collapse a.collapse-toggle:active, +.admin-interface fieldset.collapse.collapsed a.collapse-toggle:hover, +.admin-interface fieldset.collapse.collapsed a.collapse-toggle:active, +.admin-interface .inline-group .inline-related fieldset.module a.collapse-toggle:hover, +.admin-interface .inline-group .inline-related fieldset.module a.collapse-toggle:active, +.admin-interface .inline-group .inline-related fieldset.module.collapsed a.collapse-toggle:hover, +.admin-interface .inline-group .inline-related fieldset.module.collapsed a.collapse-toggle:active { + color: var(--admin-interface-module-link-hover-color); +} + +.admin-interface .inline-group h2 { + background: var(--admin-interface-module-background-color); + color: var(--admin-interface-module-text-color); +} + +.admin-interface .selector .selector-chosen h2 { + border-color: var(--admin-interface-module-background-color); + background: var(--admin-interface-module-background-color); + color: var(--admin-interface-module-text-color); +} + +.admin-interface .selector .selector-available h2, +.admin-interface .selector .selector-chosen h2 { + border-bottom-left-radius: 0px; + border-bottom-right-radius: 0px; +} + +.admin-interface .selector a.selector-chooseall:focus, +.admin-interface .selector a.selector-chooseall:hover, +.admin-interface .selector a.selector-clearall:focus, +.admin-interface .selector a.selector-clearall:hover { + color: var(--admin-interface-generic-link-hover-color); +} + +.admin-interface a:link, +.admin-interface a:visited { + color: var(--admin-interface-generic-link-color); +} + +.admin-interface a:hover { + color: var(--admin-interface-generic-link-hover-color); +} + +.admin-interface thead th a, +.admin-interface thead th a:link, +.admin-interface thead th a:visited, +.admin-interface thead th a:focus, +.admin-interface thead th a:hover { + color: #666666; +} + +.admin-interface .button, +.admin-interface input[type=submit], +.admin-interface input[type=button], +.admin-interface .submit-row input, +.admin-interface a.button { + background: var(--admin-interface-save-button-background-color); + color: var(--admin-interface-save-button-text-color); +} + +.admin-interface .button:active, +.admin-interface .button:focus, +.admin-interface .button:hover, +.admin-interface input[type=submit]:active, +.admin-interface input[type=submit]:focus, +.admin-interface input[type=submit]:hover, +.admin-interface input[type=button]:active, +.admin-interface input[type=button]:focus, +.admin-interface input[type=button]:hover { + background: var(--admin-interface-save-button-background-hover-color); + color: var(--admin-interface-save-button-text-color); + outline: none; +} + +.admin-interface .button.default, +.admin-interface input[type=submit].default, +.admin-interface .submit-row input.default { + background: var(--admin-interface-save-button-background-color); + color: var(--admin-interface-save-button-text-color); + outline: none; +} + +.admin-interface .button.default:active, +.admin-interface .button.default:focus, +.admin-interface .button.default:hover, +.admin-interface input[type=submit].default:active, +.admin-interface input[type=submit].default:focus, +.admin-interface input[type=submit].default:hover, +.admin-interface.delete-confirmation form .cancel-link:hover { + background: var(--admin-interface-save-button-background-hover-color); + color: var(--admin-interface-save-button-text-color); + outline: none; +} + +.admin-interface .submit-row a.deletelink:link, +.admin-interface .submit-row a.deletelink:visited, +.admin-interface.delete-confirmation form input[type="submit"] { + background: var(--admin-interface-delete-button-background-color); + color: var(--admin-interface-delete-button-text-color); +} + +.admin-interface .submit-row a.deletelink:hover, +.admin-interface.delete-confirmation form input[type="submit"]:hover { + background: var(--admin-interface-delete-button-background-hover-color); + color: var(--admin-interface-delete-button-text-color); +} + +.admin-interface .paginator a, +.admin-interface .paginator a:link, +.admin-interface .paginator a:visited, +.admin-interface .paginator .this-page { + border-radius: var(--admin-interface-module-border-radius); +} + +.admin-interface .paginator a, +.admin-interface .paginator a:link, +.admin-interface .paginator a:visited { + background-color: #FFFFFF; + color: var(--admin-interface-generic-link-color); +} + +.admin-interface .paginator a:hover, +.admin-interface .paginator a:active { + background-color: #F8F8F8; + color: var(--admin-interface-generic-link-hover-color); +} + +.admin-interface .paginator .this-page { + background-color: var(--admin-interface-module-background-color); + color: var(--admin-interface-module-link-color); +} + +.admin-interface .paginator a.showall, +.admin-interface .paginator a.showall:link, +.admin-interface .paginator a.showall:visited { + color: var(--admin-interface-generic-link-color); +} + +.admin-interface .paginator a.showall:hover, +.admin-interface .paginator a.showall:active { + color: var(--admin-interface-generic-link-hover-color); +} + +/* list-filter sticky */ +@media (min-width: 768px) { + .admin-interface.list-filter-sticky .module.filtered #changelist-filter { + position: sticky; + top: 30px; + float: right; + z-index: 30; + display: flex; + flex-direction: column; + overflow-y: auto; + scrollbar-width: thin; + height: 100%; + max-height: calc(100vh - 60px); + } + .admin-interface.list-filter-sticky.sticky-pagination .module.filtered #changelist-filter { + max-height: calc(100vh - 125px); + } + + /* feature not available for django < 3.1.2 */ + .admin-interface.list-filter-sticky .module.filtered #toolbar + #changelist-filter { + position: absolute; + top: 0px; + z-index: 30; + max-height: calc(100vh - 105px); + } + .admin-interface.list-filter-sticky.sticky-pagination .module.filtered #toolbar + #changelist-filter { + max-height: calc(100vh - 170px); + } +} + +.admin-interface .module.filtered #changelist-filter { + border-radius: var(--admin-interface-module-border-radius); +} + +.admin-interface .module.filtered #changelist-filter h3#changelist-filter-clear { + margin-bottom: 0; +} + +.admin-interface .module.filtered #changelist-filter .changelist-filter-clear a { + font-size: 13px; + margin: .3em 0; + padding: 0 15px; +} + +.admin-interface .module.filtered #changelist-filter .changelist-filter-clear a:focus, +.admin-interface .module.filtered #changelist-filter .changelist-filter-clear a:hover, +.admin-interface .module.filtered #changelist-filter #changelist-filter-clear a:focus, +.admin-interface .module.filtered #changelist-filter #changelist-filter-clear a:hover { + color: #666; + text-decoration: none; +} + +.admin-interface .module.filtered #changelist-filter .changelist-filter-clear a span { + font-weight: bold; +} + +.admin-interface .module.filtered #changelist-filter li a:focus, +.admin-interface .module.filtered #changelist-filter li a:hover { + color: #666; + text-decoration: none; +} + +.admin-interface.list-filter-highlight .module.filtered #changelist-filter h3.active { + font-weight: bold; +} + +.admin-interface.list-filter-highlight .module.filtered #changelist-filter ul.active li.selected { + color: var(--admin-interface-module-text-color); + background: var(--admin-interface-module-background-color); + margin-left: -10px; + padding-left: 5px; + margin-right: -10px; + border-left: 5px solid var(--admin-interface-module-background-color); + border-right: 5px solid var(--admin-interface-module-background-color); + border-radius: var(--admin-interface-module-border-radius); +} + +.admin-interface.list-filter-highlight .module.filtered #changelist-filter ul.active li.selected a, +.admin-interface.list-filter-highlight .module.filtered #changelist-filter ul.active li.selected a:link, +.admin-interface.list-filter-highlight .module.filtered #changelist-filter ul.active li.selected a:visited, +.admin-interface.list-filter-highlight .module.filtered #changelist-filter ul.active li.selected a:focus, +.admin-interface.list-filter-highlight .module.filtered #changelist-filter ul.active li.selected a:hover { + background: inherit; + color: inherit; +} + +.admin-interface .module.filtered #changelist-filter li.selected a, +.admin-interface .module.filtered #changelist-filter li.selected a:link, +.admin-interface .module.filtered #changelist-filter li.selected a:visited, +.admin-interface .module.filtered #changelist-filter li.selected a:focus, +.admin-interface .module.filtered #changelist-filter li.selected a:hover { + color: var(--admin-interface-generic-link-hover-color); +} + +/* begin fix issue #11 - Inline border bottom should not be rounded */ +.admin-interface .module h2, +.admin-interface.dashboard .module caption, +.admin-interface #nav-sidebar .module th, +.admin-interface #nav-sidebar .module caption, +.admin-interface .module.filtered h2 { + border-radius: var(--admin-interface-module-border-radius); +} + +.admin-interface .inline-group h2 { + border-bottom-left-radius: 0px; + border-bottom-right-radius: 0px; +} + +.admin-interface .module.collapse.collapsed h2 { + /* fix collapsed inlines rounded bottom borders */ + border-bottom-left-radius: var(--admin-interface-module-border-radius); + border-bottom-right-radius: var(--admin-interface-module-border-radius); +} + +/* end fix */ + +.admin-interface #content-related { + border-radius: var(--admin-interface-module-border-radius); +} + +.admin-interface .select2-container--admin-autocomplete .select2-results__option--highlighted[aria-selected] { + background-color: var(--admin-interface-module-background-color); + color: var(--admin-interface-module-text-color); +} + +.admin-interface #toggle-nav-sidebar { + border-top-right-radius: var(--admin-interface-module-border-radius); + border-bottom-right-radius: var(--admin-interface-module-border-radius); + color: var(--admin-interface-generic-link-color); +} + +.admin-interface #toggle-nav-sidebar:focus, +.admin-interface #toggle-nav-sidebar:hover, +.admin-interface #toggle-nav-sidebar:active { + color: var(--admin-interface-generic-link-hover-color); +} + +.admin-interface .calendar td.selected a, +.admin-interface .calendar td a:active, +.admin-interface .calendar td a:focus, +.admin-interface .calendar td a:hover, +.admin-interface .timelist a:active, +.admin-interface .timelist a:focus, +.admin-interface .timelist a:hover { + background: var(--admin-interface-module-background-color); +} + +.admin-interface .calendarbox .calendarnav-previous, +.admin-interface .calendarbox .calendarnav-next { + transition: none; + filter: invert(100%); +} diff --git a/app/static/admin_interface/css/change-form.css b/app/static/admin_interface/css/change-form.css new file mode 100644 index 0000000..362ccc5 --- /dev/null +++ b/app/static/admin_interface/css/change-form.css @@ -0,0 +1,34 @@ +/* fix form submit buttons alignemnt and ordering */ +@media (min-width: 768px) { + .admin-interface .submit-row { + flex-direction: row-reverse; + } + .admin-interface .submit-row input[name="_save"] { + order: 1; + } + .admin-interface .submit-row input[name="_continue"] { + order: 2; + } + .admin-interface .submit-row input[name="_addanother"] { + order: 3; + } + .admin-interface .submit-row a.deletelink { + order: 4; + margin-left: 0; + margin-right: auto; + } +} +/* endfix */ + +/* fix help-text horizontal alignment when using show-fieldsets-as-tabs / show-inlines-as-tabs options - #317 */ +.admin-interface.show-fieldsets-as-tabs fieldset > .form-row .help, +.admin-interface.show-inlines-as-tabs .inline-related > fieldset .help { + margin-left: 0; + padding-left: 0; +} +.admin-interface.show-fieldsets-as-tabs fieldset > .form-row div:has(.related-widget-wrapper .selector) + .help, +.admin-interface.show-inlines-as-tabs .inline-related > fieldset div:has(.related-widget-wrapper .selector) + .help { + display: flex; + width: 100%; +} +/* endfix */ diff --git a/app/static/admin_interface/css/changelist.css b/app/static/admin_interface/css/changelist.css new file mode 100644 index 0000000..3c1cbcd --- /dev/null +++ b/app/static/admin_interface/css/changelist.css @@ -0,0 +1,43 @@ +@media (min-width: 1024px) { + .admin-interface #changelist .actions .button, + .admin-interface #changelist .actions .action-counter { + margin-left: 8px; + } +} + +.admin-interface #changelist-form .results { + scrollbar-width: thin; +} + +/* fix searchbar overriden padding */ +.admin-interface #changelist #changelist-search #searchbar { + padding: 2px 5px 3px 5px; +} + +@media (min-width: 1024px) { + .admin-interface #changelist #changelist-search #searchbar, + .admin-interface #changelist #changelist-search input[type="submit"], + .admin-interface #changelist #changelist-search .quiet { + margin-left: 8px; + } + .admin-interface #changelist #changelist-search label img { + vertical-align: text-top; + margin-right: 0px; + } +} + +@media (max-width: 1024px) { + .admin-interface #changelist #toolbar { + border-top: 1px solid #eee; + border-bottom: 1px solid #eee; + } + /* fixed changelist search size when there are search results and .quiet is visible */ + .admin-interface #changelist-search label img { + margin-top: 2px; + } + .admin-interface #changelist-search .quiet { + margin: 0 0 0 10px; + align-self: center; + flex-basis: content; + } +} diff --git a/app/static/admin_interface/css/fieldsets.css b/app/static/admin_interface/css/fieldsets.css new file mode 100644 index 0000000..dc7fabe --- /dev/null +++ b/app/static/admin_interface/css/fieldsets.css @@ -0,0 +1,14 @@ +.admin-interface fieldset.collapse { + border: 1px solid transparent; +} + +.admin-interface fieldset.collapse.collapsed a.collapse-toggle, +.admin-interface fieldset.collapse a.collapse-toggle, +.admin-interface .inline-group .inline-related fieldset.module a.collapse-toggle, +.admin-interface .inline-group .inline-related fieldset.module.collapsed a.collapse-toggle { + font-weight: normal; + text-transform: lowercase; + font-size: 12px; + text-decoration: underline; + padding: 0 1px; +} diff --git a/app/static/admin_interface/css/file-upload.css b/app/static/admin_interface/css/file-upload.css new file mode 100644 index 0000000..8aec5fc --- /dev/null +++ b/app/static/admin_interface/css/file-upload.css @@ -0,0 +1,52 @@ +.admin-interface .file-thumbnail > a { + display: inline-block; +} + +.admin-interface .aligned p.file-upload { + display: table; + margin-left: 0; +} + +.admin-interface form .form-row p.file-upload > a { + margin-right: 20px; +} + +.admin-interface form .form-row p.file-upload .clearable-file-input { + display: inline-block; +} + +.admin-interface form .form-row p.file-upload .clearable-file-input label { + padding-bottom: 0px; + margin-left: 5px; +} + +.admin-interface form .form-row p.file-upload > input[type="file"] { + margin-top: 0px; +} + +@media (max-width:767px){ + + .admin-interface form .form-row p.file-upload { + width: 100%; + } + + .admin-interface form .form-row p.file-upload > a { + margin-right: 0px; + display: block; + white-space: pre-wrap; + word-break: break-word; + } + + .admin-interface form .form-row p.file-upload .clearable-file-input { + display: block; + margin-top: 10px; + margin-left: 0; + margin-bottom: -10px; + } + + .admin-interface form .form-row p.file-upload > input[type="file"] { + display: block; + width: auto; + padding: 0px; + } +} diff --git a/app/static/admin_interface/css/header.css b/app/static/admin_interface/css/header.css new file mode 100644 index 0000000..4d9e3a9 --- /dev/null +++ b/app/static/admin_interface/css/header.css @@ -0,0 +1,50 @@ +.admin-interface #header { + height: auto; + box-sizing: border-box; + display: flex; + justify-content: space-between; + align-items: center; +} + +@media (max-width:1024px) { + .admin-interface #header { + align-items: start; + } +} + +.admin-interface #branding h1 img.logo { + margin-top: 10px; + margin-bottom: 10px; + margin-right: 15px; + display: inline-block !important; /* override inline display:none; */ +} + +.admin-interface #branding h1 span { + display: inline-block; +} + +.admin-interface #branding h1 img.logo+span { + white-space: nowrap; +} + +.admin-interface #user-tools { + margin-top: 10px; + margin-bottom: 10px; + white-space: nowrap; + align-self: flex-start; +} + +.admin-interface #user-tools br { + display: none; +} + +@media (max-width: 768px) { + .admin-interface #user-tools br { + display: block; + } +} + +/* hide theme toggle button until dark theme will be supported */ +.admin-interface #user-tools button.theme-toggle { + display: none; +} diff --git a/app/static/admin_interface/css/inlines.css b/app/static/admin_interface/css/inlines.css new file mode 100644 index 0000000..1ebc730 --- /dev/null +++ b/app/static/admin_interface/css/inlines.css @@ -0,0 +1,105 @@ +/* begin fix help text icon on newline */ +.admin-interface .inline-group thead th { + white-space:nowrap; +} + +.admin-interface .inline-group thead th img { + vertical-align: -2px; + margin-left: 5px; +} + +.admin-interface .inline-group .inlinechangelink { + margin-left: 10px; +} + +.admin-interface .inline-group .inline-related.tabular .inlinechangelink { + background-size: contain; +} +/* end fix */ + +/* begin fix restrict tabular-inline horizontal-scroll to inline-group instead of whole page */ +.admin-interface .inline-group[data-inline-type="tabular"] { + overflow-x: auto; +} +/* end fix */ + +/* begin fix stacked-inline margin-bottom in responsive small viewport */ +.admin-interface .inline-group[data-inline-type="stacked"] .module { + margin-bottom: 0px; +} +/* end fix */ + +/* begin fix tabular inlines horizontal scroll */ +.admin-interface .inline-related.tabular { + overflow-x: scroll; + overflow-y: hidden; +} +.admin-interface .inline-related.tabular fieldset.module { + display: contents; + width: 100%; + white-space: nowrap; + position: relative; +} +.admin-interface .inline-related.tabular fieldset.module h2 { + position: sticky; + left: 0; +} +.admin-interface .inline-related.tabular fieldset.module table { + scrollbar-width: thin; +} +.admin-interface .inline-related.tabular fieldset.module table tbody tr { + position: relative; +} +/* end fix */ + +.admin-interface .inline-related h3 { + padding: 6px 10px; +} + +/* begin fix issue #12 - Inlines bad delete buttons alignement */ +.admin-interface .inline-group .tabular thead th:last-child:not([class]):not([style]) { + text-align: right; +} + +.admin-interface .inline-group .tabular tr td { + vertical-align: top; +} + +.admin-interface .inline-group .tabular tr td.delete { + text-align: right; + padding-right: 15px; + vertical-align: top; +} + +.admin-interface .inline-group .tabular tr td input[type="checkbox"] { + margin: 7px 0px; +} + +.admin-interface .inline-group .tabular tr td.delete a.inline-deletelink { + margin-top: 4px; + overflow: hidden; + text-indent: 9999px; +} +/* end fix */ + +/* begin fix remove button at the end od dynamically added inline rows */ +.admin-interface .inline-group .tabular tr td:not(.delete) * + div:has(> a.inline-deletelink) { + display: inline-block; + margin-bottom: -3px; + margin-left: 10px; +} +/* end fix */ + +/* begin fix issue #13 - Datetime widget broken in long inlines */ +.admin-interface .inline-group .inline-related p.datetime { + white-space: nowrap; +} +/* end fix */ + +/* begin fix inline horizontal scroll caused by checkbox-row */ +@media (max-width:767px){ + .admin-interface form .form-row > div.checkbox-row { + width: 100%; + } +} +/* end fix */ diff --git a/app/static/admin_interface/css/jquery.ui.tabs.css b/app/static/admin_interface/css/jquery.ui.tabs.css new file mode 100644 index 0000000..dfc2f94 --- /dev/null +++ b/app/static/admin_interface/css/jquery.ui.tabs.css @@ -0,0 +1,247 @@ +/* +* jQuery UI CSS Framework +* Copyright (c) 2010 AUTHORS.txt (http://jqueryui.com/about) +* Dual licensed under the MIT (MIT-LICENSE.txt) and GPL (GPL-LICENSE.txt) licenses. +* http://ajax.googleapis.com/ajax/libs/jqueryui/1.8.2/themes/base/jquery.ui.core.css +*/ + +/* +backward compatibility: +.ui-tabs-selected: jquery ui < 1.10 +.ui-tabs-active classes jquery ui >= 1.10 +*/ + +/* Layout helpers +----------------------------------*/ +.ui-helper-hidden { + display: none; +} + +.ui-helper-hidden-accessible { + position: absolute; + left: -99999999px; +} + +.ui-helper-reset { + margin: 0; + padding: 0; + border: 0; + outline: 0; + line-height: 1.3; + text-decoration: none; + font-size: 100%; + list-style: none; +} + +.ui-helper-clearfix:after { + content: "."; + display: block; + height: 0; + clear: both; + visibility: hidden; +} + +.ui-helper-clearfix { + display: inline-block; +} + +/* required comment for clearfix to work in Opera \*/ +* html .ui-helper-clearfix { + height: 1%; +} + +.ui-helper-clearfix { + display: block; +} + +.ui-helper-zfix { + width: 100%; + height: 100%; + top: 0; + left: 0; + position: absolute; + opacity: 0; + filter: Alpha(Opacity=0); +} + +.ui-state-disabled { + cursor: default !important; +} + +.ui-icon { + display: block; + text-indent: -99999px; + overflow: hidden; + background-repeat: no-repeat; +} + +/* http://ajax.googleapis.com/ajax/libs/jqueryui/1.8.2/themes/base/jquery.ui.tabs.css */ +.ui-widget-overlay { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; +} + +.ui-tabs { + position: relative; + padding: .2em; + zoom: 1; +} + +/* position: relative prevents IE scroll bug (element with position: relative inside container with overflow: auto appear as "fixed") */ +.ui-tabs .ui-tabs-nav { + margin: 0; + padding: .2em .2em 0; +} + +.ui-tabs .ui-tabs-nav li { + list-style: none; + float: left; + position: relative; + top: 1px; + margin: 0 .2em 1px 0; + border-bottom: 0 !important; + padding: 0; + white-space: nowrap; +} + +.ui-tabs .ui-tabs-nav li a { + float: left; + padding: .5em 1em; + text-decoration: none; +} + +.ui-tabs .ui-tabs-nav li.ui-tabs-active, .ui-tabs .ui-tabs-nav li.ui-tabs-selected { + margin-bottom: 0; + padding-bottom: 1px; +} + +.ui-tabs .ui-tabs-nav li.ui-tabs-active a, .ui-tabs .ui-tabs-nav li.ui-tabs-selected a, .ui-tabs .ui-tabs-nav li.ui-state-disabled a, .ui-tabs .ui-tabs-nav li.ui-state-processing a { + cursor: text; +} + +.ui-tabs .ui-tabs-nav li a, .ui-tabs.ui-tabs-collapsible .ui-tabs-nav li.ui-tabs-selected a { + cursor: pointer; +} + +/* first selector in group seems obsolete, but required to overcome bug in Opera applying cursor: text overall if defined elsewhere... */ +.ui-tabs .ui-tabs-panel { + display: block; + border-width: 0; + padding: 1em 1.4em; + background: none; +} + +.ui-tabs .ui-tabs-hide { + position: absolute; + display: none; +} + +/* Custom tabs theme */ +.admin-interface .ui-tabs { + padding: 0; +} + +.admin-interface .ui-tabs, +.admin-interface .ui-tabs .ui-widget-header, +.admin-interface .ui-tabs .ui-widget-header .ui-state-default { + border: none; + background: transparent; +} + +.admin-interface .ui-tabs .ui-tabs-nav { + padding: 10px 0 0 10px; + border-bottom: none; +} + +.admin-interface .ui-tabs .ui-tabs-nav li { + margin: 0 0 0 -1px; +} + +.admin-interface .ui-tabs .ui-tabs-nav li.required { + font-weight: bold; +} + +.admin-interface .ui-tabs .ui-tabs-nav li a { + border: 1px solid #eeeeee; + background-color: #f8f8f8; + border-bottom: none; + color: #666666; + padding: 7px 14px 8px 14px; + margin-top: 1px; + -moz-border-radius-topright: 4px; + -webkit-border-top-right-radius: 4px; + -moz-border-radius-topleft: 4px; + -webkit-border-top-left-radius: 4px; + border-top-left-radius: 4px; + border-top-right-radius: 4px; + font-size: 12px; + text-transform: uppercase; + outline: none; +} + +.admin-interface .ui-tabs .ui-tabs-nav li.ui-tabs-active a, +.admin-interface .ui-tabs .ui-tabs-nav li.ui-tabs-selected a { + padding: 8px 14px 8px 14px; + margin-top: 0px; + margin-bottom: -1px; + font-weight: bold; + background-color: #FFFFFF; + color: var(--admin-interface-module-background-color); + border-bottom: 1px solid #FFFFFF; +} + +.admin-interface .ui-tabs .ui-tabs-panel { + border: 1px solid #eeeeee; + border-radius: 4px; + padding: 10px; + margin-bottom: 30px; + overflow: hidden; +} + +.admin-interface .inline-group .tabular .ui-tabs .ui-tabs-panel { + padding: 8px; +} + +.admin-interface .inline-group .tabular .ui-tabs .ui-tabs-nav { + padding-left: 4px; +} + +.admin-interface .inline-group .tabular tr td { + vertical-align: top; +} + +.admin-interface .inline-group .tabular tr.has_original td.original, +.admin-interface .inline-group .tabular tr td.delete { + vertical-align: top; +} + +.admin-interface .inline-group .tabular .datetime > input { + margin-right: 5px; +} + +.admin-interface .inline-group .tabular .datetime br { + display: none; +} + +.admin-interface #changelist .row1:not(.selected):hover, +.admin-interface #changelist .row2:not(.selected):hover { + background: #f9f9f9; +} + +.admin-interface .row2 { + background: #fcfcfc; +} + +.admin-interface .row2 .ui-tabs .ui-tabs-nav li a { + background-color: #f5f5f5; + border: 1px solid #ebebeb; +} + +.admin-interface .row2 .ui-tabs .ui-tabs-nav li.ui-tabs-active a, +.admin-interface .row2 .ui-tabs .ui-tabs-nav li.ui-tabs-selected a { + background-color: #fcfcfc; + border-bottom: 1px solid #fcfcfc; +} diff --git a/app/static/admin_interface/css/language-chooser.css b/app/static/admin_interface/css/language-chooser.css new file mode 100644 index 0000000..8459b9c --- /dev/null +++ b/app/static/admin_interface/css/language-chooser.css @@ -0,0 +1,72 @@ +.admin-interface .language-chooser { + display: inline-block; + position: absolute; + top: 15px; + right: 15px; + z-index: 10; +} + +@media (min-width: 768px) { + .admin-interface .language-chooser { + right: 30px; + } +} + +@media (min-width: 1024px) { + .admin-interface .language-chooser { + position: static; + margin-left: 20px; + } +} + +.admin-interface .language-chooser .language-chooser-hidden-form { + display: none; +} + +.admin-interface .language-chooser .language-chooser-select-form { + display: inline-block; + position: relative; + z-index: 0; +} + +.admin-interface .language-chooser select { + width: auto; + min-width: auto; +} + +.admin-interface .language-chooser.minimal .language-chooser-select-form::after { + content: ""; + position: absolute; + right: 2px; + top: 50%; + border: solid var(--admin-interface-header-text-color); + border-width: 0px 0px 1px 1px; + display: inline-block; + padding: 2px; + transform: rotate(-45deg); + -webkit-transform: rotate(-45deg); + pointer-events: none; + margin-top: -4px; +} + +.admin-interface .language-chooser.minimal .language-chooser-select-form:hover select { + border-bottom: 1px solid transparent; + color: var(--admin-interface-header-link-hover-color); +} + +.admin-interface .language-chooser.minimal select { + -webkit-appearance: none; + -moz-appearance: none; + appearance: none; + background-color: transparent; + border: none; + border-bottom: 1px solid rgba(255, 255, 255, 0.25); + border-radius: 0; + color: var(--admin-interface-header-link-color); + cursor: pointer; + font-weight: inherit; + font-size: inherit; + height: auto; + margin: 0; + padding: 0 15px 0 0; +} diff --git a/app/static/admin_interface/css/list-filter-dropdown.css b/app/static/admin_interface/css/list-filter-dropdown.css new file mode 100644 index 0000000..207b3c7 --- /dev/null +++ b/app/static/admin_interface/css/list-filter-dropdown.css @@ -0,0 +1,27 @@ +/* +list-filter-dropdown +*/ + +.admin-interface #changelist-filter .list-filter-dropdown { + margin-top: 15px; + margin-bottom: 15px; +} + +.admin-interface #changelist-filter h2 + .list-filter-dropdown, +.admin-interface #changelist-filter .list-filter-dropdown + .list-filter-dropdown { + margin-top: 5px; +} + +.admin-interface #changelist-filter .list-filter-dropdown h3 { + margin-top: 0 !important; +} + +.admin-interface #changelist-filter .list-filter-dropdown select { + background-color: #FFFFFF; + width: calc(100% - 30px); + margin-right: 15px; +} + +.admin-interface.list-filter-highlight #changelist-filter .list-filter-dropdown h3.active + div select { + font-weight: bold; +} diff --git a/app/static/admin_interface/css/list-filter.css b/app/static/admin_interface/css/list-filter.css new file mode 100644 index 0000000..113abdb --- /dev/null +++ b/app/static/admin_interface/css/list-filter.css @@ -0,0 +1,28 @@ +.admin-interface .module.filtered h2 { + border-bottom-left-radius: 0; + border-bottom-right-radius: 0; +} + +.admin-interface .module.filtered #changelist-filter { + min-width: 240px; +} + +@media (max-width: 1024px) { + .admin-interface .module.filtered #changelist-filter { + min-width: 200px; + } +} + +.admin-interface .module.filtered #changelist-filter h2 { + font-size: 11px; + padding: 10px 15px; +} + +.admin-interface .module.filtered #changelist-filter h2 + h3 { + margin-top: 0px; +} + +.admin-interface .module.filtered #changelist-filter h3 { + margin-top: 12px; + margin-bottom: 12px; +} diff --git a/app/static/admin_interface/css/login.css b/app/static/admin_interface/css/login.css new file mode 100644 index 0000000..3114373 --- /dev/null +++ b/app/static/admin_interface/css/login.css @@ -0,0 +1,65 @@ +.admin-interface.login #container { + width: 100%; + max-width: 360px; + margin: 15px auto; + box-sizing: border-box; + -moz-box-sizing: border-box; + -webkit-box-sizing: border-box; +} + +.admin-interface.login #content { + padding: 15px 30px 30px 30px; +} + +@media (min-width:768px){ + .admin-interface.login #container { + margin: 90px auto; + } +} + +.admin-interface.login #header { + min-height: auto; + padding: 10px 30px; + line-height: 30px; + align-items: center; + justify-content: flex-start; +} + +.admin-interface.login #header #branding h1 { + margin-right:0; +} + +.admin-interface.login #header #branding h1 img.logo { + margin-right: 0; +} + +.admin-interface.login #header #branding h1 img.logo+span { + display: block; +} + +.admin-interface.login #login-form { + display: flex; + flex-direction: column; +} + +.admin-interface.login .submit-row { + float: left; + width: 100%; + margin-top: 20px; + padding-top: 0; + padding-left: 0; + text-align: right; +} + +.admin-interface.login .submit-row label { + display: none; +} + +.admin-interface.login .submit-row input[type="submit"] { + width: 100%; + text-transform: uppercase; +} + +.admin-interface.login #footer { + display: none; +} diff --git a/app/static/admin_interface/css/modules.css b/app/static/admin_interface/css/modules.css new file mode 100644 index 0000000..6208181 --- /dev/null +++ b/app/static/admin_interface/css/modules.css @@ -0,0 +1,12 @@ +/* begin fix lateral padding to align text with field labels */ +.admin-interface .module h2, +.admin-interface.dashboard .module caption, +.admin-interface.dashboard .module th, +.admin-interface .module.filtered h2, +.admin-interface .inline-group h2, +.admin-interface #nav-sidebar .module caption, +.admin-interface #nav-sidebar .module th { + padding-left: 10px; + padding-right: 10px; +} +/* end fix */ diff --git a/app/static/admin_interface/css/nav-sidebar.css b/app/static/admin_interface/css/nav-sidebar.css new file mode 100644 index 0000000..14356e3 --- /dev/null +++ b/app/static/admin_interface/css/nav-sidebar.css @@ -0,0 +1,78 @@ +/* fix nav-sidebar (added in django 3.1.0) */ +.admin-interface #toggle-nav-sidebar { + top: 10px; + left: 0; + z-index: 20; + flex: 0 0 30px; + width: 30px; + height: 45px; + margin-top: 10px; + margin-right: -30px; + background-color: #FFFFFF; + font-size: 16px; + border: 1px solid #eaeaea; + border-left: none; + outline: none; + -webkit-box-shadow: 4px 4px 8px -4px #DBDBDB; + -moz-box-shadow: 4px 4px 8px -4px #DBDBDB; + box-shadow: 4px 4px 8px -4px #DBDBDB; + /*transition: left .3s;*/ +} + +.admin-interface .toggle-nav-sidebar::before { + margin-top: -2px; +} + +.admin-interface .main > #nav-sidebar + .content, +.admin-interface .main.shifted > #nav-sidebar + .content { + max-width: 100%; +} + +/* hide nav-sidebar below 1280px to prevent horizontal overflow issues */ +@media (max-width:1279px) { + .admin-interface #nav-sidebar, + .admin-interface #toggle-nav-sidebar { + display: none; + } +} + +.admin-interface #nav-sidebar { + flex: 0 0 360px; + left: -360px; + margin-left: -360px; + box-sizing: border-box; + -moz-box-sizing: border-box; + -webkit-box-sizing: border-box; + padding: 40px 40px 40px 40px; + border-top: none; + border-bottom: none; + border-left: none; + scrollbar-width: thin; + /*transition: left .3s, margin-left .3s;*/ +} + +.admin-interface #nav-filter { + background-color: transparent; + border-radius: 4px; + height: 30px; + margin: 0 0 30px 0; + padding: 5px 6px; + outline-width: initial; +} + +@media (min-width:1280px) { + .admin-interface #main.shifted > #toggle-nav-sidebar { + left: 359px; + } + .admin-interface #main.shifted > #nav-sidebar { + left: 0px; + margin-left: 0; + } + .admin-interface #main:not(.shifted) > .content { + max-width: 100%; + } + .admin-interface.change-list:not(.popup) #main.shifted > #nav-sidebar + .content, + .admin-interface.change-form:not(.popup) #main.shifted > #nav-sidebar + .content { + max-width: calc(100% - 360px); + } +} diff --git a/app/static/admin_interface/css/object-tools.css b/app/static/admin_interface/css/object-tools.css new file mode 100644 index 0000000..4e5aa1c --- /dev/null +++ b/app/static/admin_interface/css/object-tools.css @@ -0,0 +1,10 @@ +/* top-right buttons color on hover -> just a lighten grey */ +.admin-interface .object-tools a { + color: #FFFFFF; +} +.admin-interface .object-tools a:focus, +.admin-interface .object-tools a:hover, +.admin-interface .object-tools li:focus a, +.admin-interface .object-tools li:hover a { + background-color: #AAAAAA; +} diff --git a/app/static/admin_interface/css/paginator.css b/app/static/admin_interface/css/paginator.css new file mode 100644 index 0000000..4598a6b --- /dev/null +++ b/app/static/admin_interface/css/paginator.css @@ -0,0 +1,33 @@ +.admin-interface #changelist .paginator { + margin-top: -1px; /* merge 2 borders into 1 */ + line-height: 1.5em; + padding-top: 12px; + padding-bottom: 12px; +} + +.admin-interface .paginator a, +.admin-interface .paginator a:link, +.admin-interface .paginator a:visited, +.admin-interface .paginator .this-page { + padding: 7px 12px; +} + +.admin-interface .paginator a, +.admin-interface .paginator .this-page { + margin-left: 0px; +} + +.admin-interface .paginator .this-page, +.admin-interface .paginator a.end { + margin-right: 25px; +} + +.admin-interface .paginator .this-page + a:not(.showall) { + margin-left: -25px; +} + +body.admin-interface .paginator a.showall, +body.admin-interface .paginator a.showall:link, +body.admin-interface .paginator a.showall:visited { + margin-left: 20px; +} diff --git a/app/static/admin_interface/css/recent-actions.css b/app/static/admin_interface/css/recent-actions.css new file mode 100644 index 0000000..6e25164 --- /dev/null +++ b/app/static/admin_interface/css/recent-actions.css @@ -0,0 +1,10 @@ +.admin-interface.dashboard #content { + width: auto; + max-width: 600px; + margin-right: 0; + margin-left: 0; +} + +.admin-interface.dashboard #content #recent-actions-module { + display: none; +} diff --git a/app/static/admin_interface/css/rtl.css b/app/static/admin_interface/css/rtl.css new file mode 100644 index 0000000..30c0b9d --- /dev/null +++ b/app/static/admin_interface/css/rtl.css @@ -0,0 +1,34 @@ +[dir="rtl"] .admin-interface, +[dir="rtl"] .admin-interface * { + font-family: 'Vazir', sans-serif !important; +} + +[dir="rtl"] .admin-interface .main .toggle-nav-sidebar.sticky { + left: auto !important; + right: 0px !important; + margin-right: 0px !important; + margin-left: 10px; + border: 1px solid #eaeaea !important; + border-right: none !important; + border-top-right-radius: 0px !important; + border-bottom-right-radius: 0px !important; + border-top-left-radius: 4px; + border-bottom-left-radius: 4px; + box-shadow: -4px 2px 8px -2px #DBDBDB !important; +} + +[dir="rtl"] .admin-interface #main.shifted > #toggle-nav-sidebar { + right: 359px !important; +} + +[dir="rtl"] .admin-interface #main > #nav-sidebar { + margin-right: -360px !important; + margin-left: 0px !important; + right: -320px !important; +} + +[dir="rtl"] .admin-interface #main.shifted > #nav-sidebar { + border-left: 1px solid #eaeaea; + margin-right: 0px !important; + padding: 40px 0px 40px 40px !important; +} diff --git a/app/static/admin_interface/css/sticky-form-controls.css b/app/static/admin_interface/css/sticky-form-controls.css new file mode 100644 index 0000000..4307367 --- /dev/null +++ b/app/static/admin_interface/css/sticky-form-controls.css @@ -0,0 +1,95 @@ +/* sticky pagination */ + +.admin-interface.sticky-pagination.change-list #content-main { + padding-bottom: 4.375rem; +} + +.admin-interface.sticky-pagination.change-list .paginator { + width: 100%; + position: fixed; + bottom: 0; + right: 0; + z-index: 40; + box-sizing: border-box; + padding-left: 15px; + padding-right: 15px; + white-space: nowrap; + text-overflow: ellipsis; + border-radius: 0; + border-top: 1px solid #EEEEEE !important; + border-bottom: none; + margin: 0; +} + +.admin-interface.sticky-pagination.change-list.popup .paginator { + padding-left: 20px; + padding-right: 20px; +} + +@media (min-width:768px) { + .admin-interface.sticky-pagination.change-list:not(.popup) .paginator { + padding-left: 30px; + padding-right: 30px; + } +} + +@media (min-width:1024px) { + .admin-interface.sticky-pagination.change-list:not(.popup) .paginator { + padding-left: 40px; + padding-right: 40px; + } +} + +@media (min-width:1280px) { + .admin-interface.sticky-pagination.change-list:not(.popup) #main.shifted > #nav-sidebar + .content .paginator { + width: calc(100% - 360px); + } +} + +/* sticky submit */ + +@media (min-width:768px) { + .admin-interface.sticky-submit.change-form #content-main { + padding-bottom: 4.375rem; + } + + .admin-interface.sticky-submit.change-form .submit-row:last-of-type { + width: 100%; + position: fixed; + bottom: 0; + right: 0; + z-index: 40; + box-sizing: border-box; + padding-left: 15px; + padding-right: 15px; + white-space: nowrap; + text-overflow: ellipsis; + border-radius: 0; + border-top: 1px solid #EEEEEE; + border-bottom: none !important; + margin: 0; + } + + .admin-interface.sticky-submit.change-form.popup .submit-row:last-of-type { + padding-left: 20px; + padding-right: 20px; + } + + .admin-interface.sticky-submit.change-form:not(.popup) .submit-row:last-of-type { + padding-left: 30px; + padding-right: 30px; + } +} + +@media (min-width:1024px) { + .admin-interface.sticky-submit.change-form:not(.popup) .submit-row:last-of-type { + padding-left: 40px; + padding-right: 40px; + } +} + +@media (min-width:1280px) { + .admin-interface.sticky-submit.change-form:not(.popup) #main.shifted > #nav-sidebar + .content .submit-row:last-of-type { + width: calc(100% - 359px); + } +} diff --git a/app/static/admin_interface/css/tabbed-changeform.css b/app/static/admin_interface/css/tabbed-changeform.css new file mode 100644 index 0000000..a0f6718 --- /dev/null +++ b/app/static/admin_interface/css/tabbed-changeform.css @@ -0,0 +1,67 @@ +.admin-interface .tabbed-changeform-tabs { + display: flex; + flex-direction: row; + flex-wrap: nowrap; + overflow-x: auto; + overflow-y: hidden; + scrollbar-width: thin; + padding-bottom: 15px; +} + +@-moz-document url-prefix() { + .admin-interface .tabbed-changeform-tabs { + padding-bottom: 13px; + } +} + +.admin-interface .tabbed-changeform-tabs .tabbed-changeform-tablink { + appearance: none; + -webkit-appearance: none; + border: 1px solid transparent; + border-bottom: 1px solid var(--border-color); + border-radius: var(--admin-interface-module-border-radius); + border-bottom-left-radius: 0px; + border-bottom-right-radius: 0px; + flex-shrink: 0; + flex-grow: 0; + cursor: pointer; + padding: 10px 15px; + margin: 0; + background-color: var(--admin-interface-module-header-text-color); + color: var(--admin-interface-generic-link-color); + font-size: 13px; + font-weight: bold; + outline: none !important; +} + +.admin-interface .tabbed-changeform-tabs .tabbed-changeform-tablink + .tabbed-changeform-tablink { + margin-left: -1px; +} + +.admin-interface .tabbed-changeform-tabs .tabbed-changeform-tablink:hover { + color: var(--admin-interface-generic-link-hover-color); +} + +.admin-interface .tabbed-changeform-tabs .tabbed-changeform-tablink:focus { + color: var(--admin-interface-generic-link-hover-color); +} + +.admin-interface .tabbed-changeform-tabs .tabbed-changeform-tablink.active { + border: 1px solid var(--border-color); + border-bottom: 1px solid transparent; + color: var(--admin-interface-generic-link-active-color); +} + +.admin-interface .tabbed-changeform-tabs-remaining-space { + flex: 1; + border-bottom: 1px solid var(--border-color); +} + +.admin-interface .tabbed-changeform-tabcontent { + display: none; + padding: 1em 0; +} + +.admin-interface .tabbed-changeform-tabcontent.active { + display: block; +} diff --git a/app/static/admin_interface/css/third-party/ckeditor.css b/app/static/admin_interface/css/third-party/ckeditor.css new file mode 100644 index 0000000..952120c --- /dev/null +++ b/app/static/admin_interface/css/third-party/ckeditor.css @@ -0,0 +1,126 @@ +/* +ckeditor + light theme +https://github.com/Ikimea/ckeditor-light-theme +*/ + +.admin-interface .cke { + border: none; + box-sizing: border-box; + -moz-box-sizing: border-box; + -webkit-box-sizing: border-box; +} + +.admin-interface .cke_inner, +.admin-interface .cke_wysiwyg_frame { + border-top-left-radius: 4px; + border-top-right-radius: 4px; + border-bottom-left-radius: 4px; + border-bottom-right-radius: 4px; +} + +.admin-interface .cke_inner { + border: 1px solid #CCCCCC; +} + +.admin-interface .cke_chrome { + -moz-box-shadow: none; + -webkit-box-shadow: none; + box-shadow: none; +} + +.admin-interface .cke_top { + background: #f8f8f8; + border-top: none; + border-top-left-radius: 4px; + border-top-right-radius: 4px; + border-bottom: 1px solid #EEEEEE; + padding-left: 10px; + padding-right: 10px; + -moz-box-shadow: none; + -webkit-box-shadow: none; + box-shadow: none; +} + +.admin-interface .cke_toolgroup { + background: none; + -moz-box-shadow: none; + -webkit-box-shadow: none; + box-shadow: none; +} + +.admin-interface .cke_bottom { + background: #f8f8f8; + border-top: 1px solid #EEEEEE; + + border-bottom-left-radius: 4px; + border-bottom-right-radius: 4px; + + -moz-box-shadow: none; + -webkit-box-shadow: none; + box-shadow: none; +} + +.admin-interface .cke_source { + padding: 13px 15px; + box-sizing: border-box; +} + +.admin-interface a.cke_button, +.admin-interface a.cke_button:active, +.admin-interface a.cke_button:hover, +.admin-interface a.cke_button:focus { + box-shadow: none; + -moz-box-shadow: none; + -webkit-box-shadow: none; + background-image: none; + border-radius: 4px; + border: none; +} + +.admin-interface a.cke_button:active, +.admin-interface a.cke_button:hover, +.admin-interface a.cke_button:focus { + background-color: #E8E8E8 !important; +} + +.admin-interface a.cke_button.cke_button_on { + background-color: #CCCCCC !important; +} + +.admin-interface a.cke_button.cke_button_disabled { + background-color: transparent !important; +} + +.admin-interface .cke_resizer { + border-color: transparent #666666 transparent transparent; +} + +@media (max-width: 767px){ + + .admin-interface .django-ckeditor-widget, + .admin-interface .cke { + width: 100% !important; + } + + .admin-interface .cke_top { + padding-left: 10px; + padding-right: 10px; + } + + .admin-interface .cke_toolbar { + height: auto; + } + + .admin-interface .cke_contents { + height: auto; + } + + .admin-interface .tabular .django-ckeditor-widget, + .admin-interface .tabular .cke { + width: 400px !important; + } + + .admin-interface .tabular .cke_contents { + height: 90px !important; + } +} diff --git a/app/static/admin_interface/css/third-party/import-export.css b/app/static/admin_interface/css/third-party/import-export.css new file mode 100644 index 0000000..8ea0a09 --- /dev/null +++ b/app/static/admin_interface/css/third-party/import-export.css @@ -0,0 +1,7 @@ +/* Fix left/right scrolling broken with django-import-export #165 */ +.admin-interface table.import-preview { + display: block; + width: 100%; + max-width: 100%; + overflow: auto; +} diff --git a/app/static/admin_interface/css/third-party/json-widget.css b/app/static/admin_interface/css/third-party/json-widget.css new file mode 100644 index 0000000..e0a999d --- /dev/null +++ b/app/static/admin_interface/css/third-party/json-widget.css @@ -0,0 +1,27 @@ +/* +django-json-widget support +https://github.com/jmrivas86/django-json-widget +*/ + +.admin-interface div.jsoneditor { + border: 1px solid var(--admin-interface-module-background-color); + border-radius: var(--admin-interface-jsoneditor-border-radius); + overflow: var(--admin-interface-jsoneditor-overflow); +} + +.admin-interface div.jsoneditor-menu { + background-color: var(--admin-interface-module-background-color); + border-bottom: 1px solid var(--admin-interface-module-background-color); +} + +.admin-interface div.jsoneditor-menu a.jsoneditor-poweredBy { + color: var(--admin-interface-module-link-color); +} + +.admin-interface div.jsoneditor-contextmenu ul li button.jsoneditor-selected, +.admin-interface div.jsoneditor-contextmenu ul li button.jsoneditor-selected:focus, +.admin-interface div.jsoneditor-contextmenu ul li button.jsoneditor-selected:hover { + background-color: var(--admin-interface-module-background-selected-color); + color: #000000; + font-weight: bold; +} diff --git a/app/static/admin_interface/css/third-party/modeltranslation.css b/app/static/admin_interface/css/third-party/modeltranslation.css new file mode 100644 index 0000000..9e1890b --- /dev/null +++ b/app/static/admin_interface/css/third-party/modeltranslation.css @@ -0,0 +1,17 @@ +/* +django-modeltranslation support +https://github.com/deschler/django-modeltranslation +*/ + +.admin-interface #content h1 select { + text-transform: uppercase; + margin-left: 15px; + min-width: 50px; +} + +.admin-interface .ui-tabs .ui-tabs-panel[id^=tab_id_] { + border: none; + border-top: 1px solid #eeeeee; + padding: 0; + margin-bottom: 0; +} diff --git a/app/static/admin_interface/css/third-party/rangefilter.css b/app/static/admin_interface/css/third-party/rangefilter.css new file mode 100644 index 0000000..25b4f0b --- /dev/null +++ b/app/static/admin_interface/css/third-party/rangefilter.css @@ -0,0 +1,25 @@ +.admin-interface #changelist-filter .admindatefilter { + border-bottom: 1px solid var(--hairline-color); +} + +.admin-interface #changelist-filter .admindatefilter .button, +.admin-interface #changelist-filter .admindatefilter .submit-row input, +.admin-interface #changelist-filter .admindatefilter a.button, +.admin-interface #changelist-filter .admindatefilter input[type="submit"], +.admin-interface #changelist-filter .admindatefilter input[type="button"], +.admin-interface #changelist-filter .admindatefilter input[type="reset"] { + background: var(--admin-interface-module-background-color); + color: var(--admin-interface-module-link-color); + padding: 6px 10px; + font-size: 12px; + margin-right: 4px; +} + +.admin-interface #changelist-filter .admindatefilter .button:hover, +.admin-interface #changelist-filter .admindatefilter .submit-row input:hover, +.admin-interface #changelist-filter .admindatefilter a.button:hover, +.admin-interface #changelist-filter .admindatefilter input[type="submit"]:hover, +.admin-interface #changelist-filter .admindatefilter input[type="button"]:hover, +.admin-interface #changelist-filter .admindatefilter input[type="reset"]:hover { + color: var(--admin-interface-module-link-hover-color); +} diff --git a/app/static/admin_interface/css/third-party/sorl-thumbnail.css b/app/static/admin_interface/css/third-party/sorl-thumbnail.css new file mode 100644 index 0000000..44c6190 --- /dev/null +++ b/app/static/admin_interface/css/third-party/sorl-thumbnail.css @@ -0,0 +1,67 @@ +/* +sorl-thumbnail - improved AdminImageMixin widget layout +https://github.com/mariocesar/sorl-thumbnail +*/ + +.admin-interface a.thumbnail + a { + font-weight: normal; +} + +.admin-interface a.thumbnail + a + input[type="checkbox"] { + margin: 10px 0px 10px 18px; +} + +@media (max-width: 767px){ + .admin-interface a.thumbnail + a { + display: block; + margin-top: 3px; + white-space: pre-wrap; + word-break: break-word; + } + .admin-interface a.thumbnail + a + input[type="checkbox"] { + margin: 15px 0; + } +} + +.admin-interface a.thumbnail ~ label { + color: #333; + font-size: 11px; + display: inline; + float: none; + margin-left: 2px; +} + +.admin-interface.change-form div[style="float:left"] { + font-size: 11px; + font-weight: bold; + color: #666; + margin-bottom: 5px; +} + +@media (max-width: 767px){ + .admin-interface.change-form div[style="float:left"] { + font-size: 12px; + width: 100%; + } +} + +.admin-interface .aligned .form-row a.thumbnail ~ input[type="file"] { + margin-top: 0px; +} + +@media (max-width:767px){ + .admin-interface .aligned .form-row a.thumbnail ~ input[type="file"] { + width: auto; + padding: 0px; + display: block; + margin-top: 3px; + } + + .admin-interface div[style="float:left"] { + margin-bottom: 0px; + } + + .admin-interface div[style="float:left"] + div.help { + margin-top: 0px !important; + } +} diff --git a/app/static/admin_interface/css/third-party/streamfield.css b/app/static/admin_interface/css/third-party/streamfield.css new file mode 100644 index 0000000..b1fbb01 --- /dev/null +++ b/app/static/admin_interface/css/third-party/streamfield.css @@ -0,0 +1,220 @@ +/* +django-streamfield support +https://github.com/raagin/django-streamfield/ +*/ + +.admin-interface .form-row.field-stream { + margin: 0; + padding: 0; + border-bottom: none; +} + +.admin-interface .form-row.field-stream label[for=id_stream] { + display: none; +} + +.admin-interface .streamfield_app { + clear: both; + width: 100%; +} + +.admin-interface .streamfield_app .stream-help-text { + margin-bottom: 15px; + display: flex; + flex-direction: column; + clear: both; +} + +.admin-interface .streamfield_app .stream-help-text .stream-help-text__title { + align-self: flex-end; + user-select: none; + padding: 8px; + padding-right: 0; + color: var(--admin-interface-generic-link-color); +} + +.admin-interface .streamfield_app .stream-help-text .stream-help-text__title:hover { + color: var(--admin-interface-generic-link-hover-color); +} + +.admin-interface .streamfield_app .stream-help-text .stream-help-text__content { + background: var(--admin-interface-module-background-selected-color); + border-radius: var(--admin-interface-module-border-radius); + border: 1px solid rgba(0,0,0,0.1); + padding: 15px; +} + +.admin-interface .streamfield_app .stream-help-text .stream-help-text__content > ul { + margin: 0; + padding: 0; +} + +.admin-interface .streamfield_app .collapse-handlers .collapse-handler { + user-select: none; + padding: 8px; + padding-right: 0; + margin: 0 0 5px 0; + color: var(--admin-interface-generic-link-color); + text-decoration: none; +} + +.admin-interface .streamfield_app .collapse-handlers .collapse-handler:hover { + color: var(--admin-interface-generic-link-hover-color); + text-decoration: underline; +} + +.admin-interface .streamfield_app .stream-model-block { + position: relative; + box-shadow: none; + border: 1px solid rgba(0,0,0,0.1); + border-radius: var(--admin-interface-module-border-radius); + overflow: hidden; +} + +.admin-interface .streamfield_app .stream-model-block, +.admin-interface .streamfield_app .streamfield-models.collapsed .stream-model-block { + margin-bottom: 10px; +} + +.admin-interface .streamfield_app .stream-model-block .streamblock__block__title { + display: flex; + flex-direction: row; + justify-content: space-between; + align-items: center; + margin: 0; + padding: 10px 10px 10px 20px; +} + +.admin-interface .streamfield_app .stream-model-block .streamblock__block__title .streamblock__block__model_title { + white-space: nowrap; + flex-shrink: 0; + margin-right: 20px; +} + +.admin-interface .streamfield_app .stream-model-block .streamblock__block__title span { + font-size: 18px; +} + +.admin-interface .streamfield_app .stream-model-block .streamblock__block__title .streamblock__block-handle { + position: static; + right: 0; + top: 0; + color: var(--admin-interface-generic-link-color); +} + +.admin-interface .streamfield_app .stream-model-block .streamblock__block__title .streamblock__block-handle:hover { + color: var(--admin-interface-generic-link-hover-color); +} + +.admin-interface .streamfield_app .stream-model-block .streamblock__block__title .streamblock__block-handle { + display: flex; + justify-content: center; + align-items: center; +} + +.admin-interface .streamfield_app .stream-model-block .streamblock__block__title .streamblock__block-handle .block-move, +.admin-interface .streamfield_app .stream-model-block .streamblock__block__title .streamblock__block-handle .block-delete { + display: flex; + justify-content: center; + align-items: center; + width: 40px; + height: 40px; + font-weight: normal; + background: none; + flex-shrink: 0; + color: inherit; + font-size: 16px; +} + +.admin-interface .streamfield_app .stream-model-block .streamblock__block__title .streamblock__block-handle .block-move { + cursor: move; /* fallback if grab cursor is unsupported */ + cursor: grab; + cursor: -moz-grab; + cursor: -webkit-grab; +} + +.admin-interface .streamfield_app .stream-model-block .streamblock__block__title .streamblock__block-handle .block-move:before { + content: "↕"; + + display: block; +} + +.admin-interface .streamfield_app .stream-model-block .streamblock__block__title .streamblock__block-handle .block-delete:before { + content: "×"; + display: block; + font-size: 18px; +} + +.admin-interface .streamfield_app .stream-model-block .streamblock__block__subtitle { + display: -webkit-box; + -webkit-box-orient: vertical; + -webkit-line-clamp: 2; + overflow: hidden; + text-overflow: ellipsis; + flex-shrink: 1; + margin-right: 10px; +} + +.admin-interface .streamfield_app .block-fields > div { + margin-bottom: 15px; +} + +.admin-interface .streamfield_app .stream-model-block .stream-model-block__content { + background-color: #f8f8f8; + padding: 20px; +} + +.admin-interface .streamfield_app .stream-model-block .stream-model-block__content.no-subblocks.abstract-block { + display: none; +} + +.admin-interface .streamfield_app .stream-model-block .add_here { + display: none !important; +} + +.admin-interface .streamfield_app .stream-insert-new-block { + margin-bottom: 20px; +} + +.admin-interface .streamfield_app .stream-insert-new-block .add-new-block-button { + color: var(--admin-interface-generic-link-color); + text-decoration: none; +} + +.admin-interface .streamfield_app .stream-insert-new-block .add-new-block-button:hover { + color: var(--admin-interface-generic-link-hover-color); + text-decoration: underline; +} + +.admin-interface .streamfield_app .stream-insert-new-block ul { + display: block; + width: 100%; + margin: 10px 0 0 0; + padding: 0; + user-select: none; +} + +.admin-interface .streamfield_app .stream-insert-new-block ul li { + display: inline-block; + font-size: 12px; + margin: 0; + padding: 0; +} + +.admin-interface .streamfield_app .stream-btn { + font-weight: normal; + text-decoration: none; + background-color: var(--admin-interface-generic-link-color); + padding: 6px 12px; + border-radius: 4px; +} + +.admin-interface .streamfield_app .stream-btn:hover { + text-decoration: none; + background-color: var(--admin-interface-generic-link-hover-color); +} + +.admin-interface .streamfield_app .stream-insert-new-block ul li .stream-btn { + margin-top: 5px; + margin-left: 5px; +} diff --git a/app/static/admin_interface/css/third-party/tabbed-admin.css b/app/static/admin_interface/css/third-party/tabbed-admin.css new file mode 100644 index 0000000..010296c --- /dev/null +++ b/app/static/admin_interface/css/third-party/tabbed-admin.css @@ -0,0 +1,37 @@ +/* +django-tabbed-admin support +https://github.com/omji/django-tabbed-admin +*/ + +/* Hide tabs until ready */ +/* +.admin-interface #tabs ul { + display: none; +} + +.admin-interface #tabs ul.ui-tabs-nav { + display: block; +} +*/ + +.admin-interface .ui-tabs .ui-tabs-panel[id^=tabs] .module.aligned:last-child { + margin-bottom: 0; +} + +.admin-interface .ui-tabs .ui-tabs-panel[id^=tabs] .module.aligned:last-child .form-row:last-child { + border-bottom: none; +} + +@media (max-width: 350px){ + .admin-interface .ui-tabs .ui-tabs-panel[id^=tabs] .vTextField, + .admin-interface .inline-related .vTextField { + width: 17em; + } +} + +@media (max-width: 767px){ + /* fix horizontal overflow - responsive.css:563 */ + .admin-interface .ui-tabs .ui-tabs-panel[id^=tabs] .aligned .form-row > div:not([class]) { + width: 100% !important; + } +} diff --git a/app/static/admin_interface/css/third-party/tinymce.css b/app/static/admin_interface/css/third-party/tinymce.css new file mode 100644 index 0000000..76ed1b4 --- /dev/null +++ b/app/static/admin_interface/css/third-party/tinymce.css @@ -0,0 +1,3 @@ +.admin-interface textarea.tinymce ~ p.help { + margin-top:5px !important; +} diff --git a/app/static/admin_interface/css/widgets.css b/app/static/admin_interface/css/widgets.css new file mode 100644 index 0000000..6c1be6d --- /dev/null +++ b/app/static/admin_interface/css/widgets.css @@ -0,0 +1,153 @@ +/* fix generic flex container */ +.admin-interface .flex-container { + width: 100%; +} +/* end-fix */ + +/* fix flex container for boolean fields */ +.admin-interface .flex-container.checkbox-row { + align-items: center; +} +.admin-interface .flex-container.checkbox-row label.vCheckboxLabel { + margin-top: 0 !important; + padding-left: 5px !important; + padding-bottom: 0; +} +/* end-fix */ + +/* fix flex container for related-lookup (raw_id_fields) */ +.admin-interface .flex-container:has(.vForeignKeyRawIdAdminField) { + align-items: center; +} + +.admin-interface .flex-container:has(.vForeignKeyRawIdAdminField) .related-lookup { + margin-left: 12px; +} + +.admin-interface .flex-container:has(.vForeignKeyRawIdAdminField) .related-lookup + * { + margin-left: 8px; +} +/* end-fix */ + +/* fix flex container input fields height inheritance */ +.admin-interface .flex-container label + input[type="text"], +.admin-interface .flex-container label + input[type="password"], +.admin-interface .flex-container label + input[type="email"], +.admin-interface .flex-container label + input[type="url"], +.admin-interface .flex-container label + input[type="number"], +.admin-interface .flex-container label + input[type="tel"] { + height: 1.375em; +} +/* end-fix */ + +/* fixed time widget header border radius */ +.admin-interface .clockbox.module h2 { + border-bottom-left-radius: 0; + border-bottom-right-radius: 0; +} +/* end-fix */ + +/* fixed related widget and select2 */ +/* begin fix issue #10 - Related widget broken in long tabular inline */ +.admin-interface .related-widget-wrapper { + display: flex; + flex-wrap: nowrap; + white-space: nowrap; + height: fit-content; +} +/* end fix */ + +/* fix related widget links icons size and vertical alignment */ +.admin-interface .related-widget-wrapper .related-widget-wrapper-link { + display: flex; + align-items: center; + margin-bottom: 4px; +} +.admin-interface .related-widget-wrapper .related-widget-wrapper-link img { + width: 16px; + height: 16px; +} +/* end fix */ + +/* fix related widget links add button position on many-to-many selector */ +.admin-interface .related-widget-wrapper .related-widget-wrapper-link:has(+ .selector) { + order: 2; + align-self: flex-start; + margin-top: 8px; +} +/* end fix */ + +/* fix related widget links margin from select2 (autocomplete_fields) */ +.admin-interface .related-widget-wrapper .select2-container + .related-widget-wrapper-link { + margin-left: 12px !important; +} +/* end fix */ + +/* improve responsive selector */ + +/* fix [stacked, not-stacked] equalize horizontal and vertical select padding for selector */ +.admin-interface .selector .selector-available select, +.admin-interface .selector .selector-chosen select { + padding: 7px 10px; + display: block; +} + +/* fix [stacked, not-stacked] select options text overflow */ +.admin-interface .selector .selector-available select option, +.admin-interface .selector .selector-chosen select option { + overflow: hidden; + text-overflow: ellipsis; +} + +/* fix [not-stacked] equalize selectors height by adding the height of the .selector-available filter-bar */ +.admin-interface .selector:not(.stacked) .selector-chosen select { + height: calc(46px + 17.2em) !important; +} + +@media (max-width: 767px) { + /* fixed responsive widgets */ + .admin-interface .aligned.collapsed .form-row { + display: none; + } + + .admin-interface .aligned .form-row > div { + display: flex; + width: 100%; + max-width: 100vw; + flex-direction: column; + align-items: flex-start; + } + + .admin-interface .aligned .form-row .help { + margin-left: 0; + } + + .admin-interface .aligned .form-row .checkbox-row label { + margin: 10px 0 0 0; + padding: 0; + } + + .admin-interface .aligned .form-row input[type="file"], + .admin-interface .aligned .form-row input[type="text"], + .admin-interface .aligned .form-row input[type="email"] { + width: 100%; + } + + /* fix textarea horizontal scroll on Firefox */ + .admin-interface .aligned .form-row textarea { + width: 100% !important; + flex: 0 1 auto; + } + + .admin-interface .aligned .form-row .datetime input[type="text"] { + width: 50%; + } + + .admin-interface .aligned .form-row span + .file-upload { + margin-top: 10px; + } + + .admin-interface .aligned .form-row .file-upload input[type="file"] { + margin-top: 5px; + } +} diff --git a/app/static/admin_interface/favico/favico-0.3.10-patched.js b/app/static/admin_interface/favico/favico-0.3.10-patched.js new file mode 100644 index 0000000..bc79135 --- /dev/null +++ b/app/static/admin_interface/favico/favico-0.3.10-patched.js @@ -0,0 +1,913 @@ +/** + * @license MIT or GPL-2.0 + * @fileOverview Favico animations + * @author Miroslav Magda, http://blog.ejci.net + * @source: https://github.com/ejci/favico.js + * @version 0.3.10 + */ + +/** + * Create new favico instance + * @param {Object} Options + * @return {Object} Favico object + * @example + * var favico = new Favico({ + * bgColor : '#d00', + * textColor : '#fff', + * fontFamily : 'sans-serif', + * fontStyle : 'bold', + * type : 'circle', + * position : 'down', + * animation : 'slide', + * elementId: false, + * element: null, + * dataUrl: function(url){}, + * win: window + * }); + */ +(function () { + + var Favico = (function (opt) { + 'use strict'; + opt = (opt) ? opt : {}; + var _def = { + bgColor: '#d00', + textColor: '#fff', + fontFamily: 'sans-serif', //Arial,Verdana,Times New Roman,serif,sans-serif,... + fontStyle: 'bold', //normal,italic,oblique,bold,bolder,lighter,100,200,300,400,500,600,700,800,900 + type: 'circle', + position: 'down', // down, up, left, leftup (upleft) + animation: 'slide', + elementId: false, + element: null, + dataUrl: false, + win: window + }; + var _opt, _orig, _h, _w, _canvas, _context, _img, _ready, _lastBadge, _running, _readyCb, _stop, _browser, _animTimeout, _drawTimeout, _doc; + + _browser = {}; + _browser.ff = typeof InstallTrigger != 'undefined'; + _browser.chrome = !!window.chrome; + _browser.opera = !!window.opera || navigator.userAgent.indexOf('Opera') >= 0; + _browser.ie = /*@cc_on!@*/false; + _browser.safari = Object.prototype.toString.call(window.HTMLElement).indexOf('Constructor') > 0; + _browser.supported = (_browser.chrome || _browser.ff || _browser.opera); + + var _queue = []; + _readyCb = function () { + }; + _ready = _stop = false; + /** + * Initialize favico + */ + var init = function () { + //merge initial options + _opt = merge(_def, opt); + _opt.bgColor = hexToRgb(_opt.bgColor); + _opt.textColor = hexToRgb(_opt.textColor); + _opt.position = _opt.position.toLowerCase(); + _opt.animation = (animation.types['' + _opt.animation]) ? _opt.animation : _def.animation; + + _doc = _opt.win.document; + + var isUp = _opt.position.indexOf('up') > -1; + var isLeft = _opt.position.indexOf('left') > -1; + + //transform the animations + if (isUp || isLeft) { + for (var a in animation.types) { + for (var i = 0; i < animation.types[a].length; i++) { + var step = animation.types[a][i]; + + if (isUp) { + if (step.y < 0.6) { + step.y = step.y - 0.4; + } else { + step.y = step.y - 2 * step.y + (1 - step.w); + } + } + + if (isLeft) { + if (step.x < 0.6) { + step.x = step.x - 0.4; + } else { + step.x = step.x - 2 * step.x + (1 - step.h); + } + } + + animation.types[a][i] = step; + } + } + } + _opt.type = (type['' + _opt.type]) ? _opt.type : _def.type; + + _orig = link. getIcons(); + //create temp canvas + _canvas = document.createElement('canvas'); + //create temp image + _img = document.createElement('img'); + var lastIcon = _orig[_orig.length - 1]; + if (lastIcon.hasAttribute('href')) { + _img.setAttribute('crossOrigin', 'anonymous'); + //get width/height + _img.onload = function () { + _h = (_img.height > 0) ? _img.height : 32; + _w = (_img.width > 0) ? _img.width : 32; + _canvas.height = _h; + _canvas.width = _w; + _context = _canvas.getContext('2d'); + icon.ready(); + }; + _img.setAttribute('src', lastIcon.getAttribute('href')); + } else { + _h = 32; + _w = 32; + _img.height = _h; + _img.width = _w; + _canvas.height = _h; + _canvas.width = _w; + _context = _canvas.getContext('2d'); + icon.ready(); + } + + }; + /** + * Icon namespace + */ + var icon = {}; + /** + * Icon is ready (reset icon) and start animation (if ther is any) + */ + icon.ready = function () { + _ready = true; + icon.reset(); + _readyCb(); + }; + /** + * Reset icon to default state + */ + icon.reset = function () { + //reset + if (!_ready) { + return; + } + _queue = []; + _lastBadge = false; + _running = false; + _context.clearRect(0, 0, _w, _h); + _context.drawImage(_img, 0, 0, _w, _h); + //_stop=true; + link.setIcon(_canvas); + //webcam('stop'); + //video('stop'); + window.clearTimeout(_animTimeout); + window.clearTimeout(_drawTimeout); + }; + /** + * Start animation + */ + icon.start = function () { + if (!_ready || _running) { + return; + } + var finished = function () { + _lastBadge = _queue[0]; + _running = false; + if (_queue.length > 0) { + _queue.shift(); + icon.start(); + } else { + + } + }; + if (_queue.length > 0) { + _running = true; + var run = function () { + // apply options for this animation + ['type', 'animation', 'bgColor', 'textColor', 'fontFamily', 'fontStyle'].forEach(function (a) { + if (a in _queue[0].options) { + _opt[a] = _queue[0].options[a]; + } + }); + animation.run(_queue[0].options, function () { + finished(); + }, false); + }; + if (_lastBadge) { + animation.run(_lastBadge.options, function () { + run(); + }, true); + } else { + run(); + } + } + }; + + /** + * Badge types + */ + var type = {}; + var options = function (opt) { + opt.n = ((typeof opt.n) === 'number') ? Math.abs(opt.n | 0) : opt.n; + opt.x = _w * opt.x; + opt.y = _h * opt.y; + opt.w = _w * opt.w; + opt.h = _h * opt.h; + opt.len = ("" + opt.n).length; + return opt; + }; + + /** + * Generate circle + * @param {Object} opt Badge options + */ + type.circle = function (opt) { + opt = options(opt); + var more = false; + if (opt.len === 2) { + opt.x = opt.x - opt.w * 0.4; + opt.w = opt.w * 1.4; + more = true; + } else if (opt.len >= 3) { + opt.x = opt.x - opt.w * 0.65; + opt.w = opt.w * 1.65; + more = true; + } + + // begin patch + // draw a smaller badge without text + opt.w = 14; + opt.h = 14; + opt.x = (_w - opt.w); + opt.y = (_h - opt.h); + // end patch + + _context.clearRect(0, 0, _w, _h); + _context.drawImage(_img, 0, 0, _w, _h); + _context.beginPath(); + _context.font = _opt.fontStyle + " " + Math.floor(opt.h * (opt.n > 99 ? 0.85 : 1)) + "px " + _opt.fontFamily; + _context.textAlign = 'center'; + if (more) { + _context.moveTo(opt.x + opt.w / 2, opt.y); + _context.lineTo(opt.x + opt.w - opt.h / 2, opt.y); + _context.quadraticCurveTo(opt.x + opt.w, opt.y, opt.x + opt.w, opt.y + opt.h / 2); + _context.lineTo(opt.x + opt.w, opt.y + opt.h - opt.h / 2); + _context.quadraticCurveTo(opt.x + opt.w, opt.y + opt.h, opt.x + opt.w - opt.h / 2, opt.y + opt.h); + _context.lineTo(opt.x + opt.h / 2, opt.y + opt.h); + _context.quadraticCurveTo(opt.x, opt.y + opt.h, opt.x, opt.y + opt.h - opt.h / 2); + _context.lineTo(opt.x, opt.y + opt.h / 2); + _context.quadraticCurveTo(opt.x, opt.y, opt.x + opt.h / 2, opt.y); + } else { + _context.arc(opt.x + opt.w / 2, opt.y + opt.h / 2, opt.h / 2, 0, 2 * Math.PI); + } + _context.fillStyle = 'rgba(' + _opt.bgColor.r + ',' + _opt.bgColor.g + ',' + _opt.bgColor.b + ',' + opt.o + ')'; + _context.fill(); + _context.closePath(); + _context.beginPath(); + _context.stroke(); + _context.fillStyle = 'rgba(' + _opt.textColor.r + ',' + _opt.textColor.g + ',' + _opt.textColor.b + ',' + opt.o + ')'; + //_context.fillText((more) ? '9+' : opt.n, Math.floor(opt.x + opt.w / 2), Math.floor(opt.y + opt.h - opt.h * 0.15)); + if ((typeof opt.n) === 'number' && opt.n > 999) { + _context.fillText(((opt.n > 9999) ? 9 : Math.floor(opt.n / 1000)) + 'k+', Math.floor(opt.x + opt.w / 2), Math.floor(opt.y + opt.h - opt.h * 0.2)); + } else { + _context.fillText(opt.n, Math.floor(opt.x + opt.w / 2), Math.floor(opt.y + opt.h - opt.h * 0.15)); + } + _context.closePath(); + }; + /** + * Generate rectangle + * @param {Object} opt Badge options + */ + type.rectangle = function (opt) { + opt = options(opt); + var more = false; + if (opt.len === 2) { + opt.x = opt.x - opt.w * 0.4; + opt.w = opt.w * 1.4; + more = true; + } else if (opt.len >= 3) { + opt.x = opt.x - opt.w * 0.65; + opt.w = opt.w * 1.65; + more = true; + } + _context.clearRect(0, 0, _w, _h); + _context.drawImage(_img, 0, 0, _w, _h); + _context.beginPath(); + _context.font = _opt.fontStyle + " " + Math.floor(opt.h * (opt.n > 99 ? 0.9 : 1)) + "px " + _opt.fontFamily; + _context.textAlign = 'center'; + _context.fillStyle = 'rgba(' + _opt.bgColor.r + ',' + _opt.bgColor.g + ',' + _opt.bgColor.b + ',' + opt.o + ')'; + _context.fillRect(opt.x, opt.y, opt.w, opt.h); + _context.fillStyle = 'rgba(' + _opt.textColor.r + ',' + _opt.textColor.g + ',' + _opt.textColor.b + ',' + opt.o + ')'; + //_context.fillText((more) ? '9+' : opt.n, Math.floor(opt.x + opt.w / 2), Math.floor(opt.y + opt.h - opt.h * 0.15)); + if ((typeof opt.n) === 'number' && opt.n > 999) { + _context.fillText(((opt.n > 9999) ? 9 : Math.floor(opt.n / 1000)) + 'k+', Math.floor(opt.x + opt.w / 2), Math.floor(opt.y + opt.h - opt.h * 0.2)); + } else { + _context.fillText(opt.n, Math.floor(opt.x + opt.w / 2), Math.floor(opt.y + opt.h - opt.h * 0.15)); + } + _context.closePath(); + }; + + /** + * Set badge + */ + var badge = function (number, opts) { + opts = ((typeof opts) === 'string' ? { + animation: opts + } : opts) || {}; + _readyCb = function () { + try { + if (typeof (number) === 'number' ? (number > 0) : (number !== '')) { + var q = { + type: 'badge', + options: { + n: number + } + }; + if ('animation' in opts && animation.types['' + opts.animation]) { + q.options.animation = '' + opts.animation; + } + if ('type' in opts && type['' + opts.type]) { + q.options.type = '' + opts.type; + } + ['bgColor', 'textColor'].forEach(function (o) { + if (o in opts) { + q.options[o] = hexToRgb(opts[o]); + } + }); + ['fontStyle', 'fontFamily'].forEach(function (o) { + if (o in opts) { + q.options[o] = opts[o]; + } + }); + _queue.push(q); + if (_queue.length > 100) { + throw new Error('Too many badges requests in queue.'); + } + icon.start(); + } else { + icon.reset(); + } + } catch (e) { + throw new Error('Error setting badge. Message: ' + e.message); + } + }; + if (_ready) { + _readyCb(); + } + }; + + /** + * Set image as icon + */ + var image = function (imageElement) { + _readyCb = function () { + try { + var w = imageElement.width; + var h = imageElement.height; + var newImg = document.createElement('img'); + var ratio = (w / _w < h / _h) ? (w / _w) : (h / _h); + newImg.setAttribute('crossOrigin', 'anonymous'); + newImg.onload=function(){ + _context.clearRect(0, 0, _w, _h); + _context.drawImage(newImg, 0, 0, _w, _h); + link.setIcon(_canvas); + }; + newImg.setAttribute('src', imageElement.getAttribute('src')); + newImg.height = (h / ratio); + newImg.width = (w / ratio); + } catch (e) { + throw new Error('Error setting image. Message: ' + e.message); + } + }; + if (_ready) { + _readyCb(); + } + }; + /** + * Set the icon from a source url. Won't work with badges. + */ + var rawImageSrc = function (url) { + _readyCb = function() { + link.setIconSrc(url); + }; + if (_ready) { + _readyCb(); + } + }; + /** + * Set video as icon + */ + var video = function (videoElement) { + _readyCb = function () { + try { + if (videoElement === 'stop') { + _stop = true; + icon.reset(); + _stop = false; + return; + } + //var w = videoElement.width; + //var h = videoElement.height; + //var ratio = (w / _w < h / _h) ? (w / _w) : (h / _h); + videoElement.addEventListener('play', function () { + drawVideo(this); + }, false); + + } catch (e) { + throw new Error('Error setting video. Message: ' + e.message); + } + }; + if (_ready) { + _readyCb(); + } + }; + /** + * Set video as icon + */ + var webcam = function (action) { + //UR + if (!window.URL || !window.URL.createObjectURL) { + window.URL = window.URL || {}; + window.URL.createObjectURL = function (obj) { + return obj; + }; + } + if (_browser.supported) { + var newVideo = false; + navigator.getUserMedia = navigator.getUserMedia || navigator.oGetUserMedia || navigator.msGetUserMedia || navigator.mozGetUserMedia || navigator.webkitGetUserMedia; + _readyCb = function () { + try { + if (action === 'stop') { + _stop = true; + icon.reset(); + _stop = false; + return; + } + newVideo = document.createElement('video'); + newVideo.width = _w; + newVideo.height = _h; + navigator.getUserMedia({ + video: true, + audio: false + }, function (stream) { + newVideo.src = URL.createObjectURL(stream); + newVideo.play(); + drawVideo(newVideo); + }, function () { + }); + } catch (e) { + throw new Error('Error setting webcam. Message: ' + e.message); + } + }; + if (_ready) { + _readyCb(); + } + } + + }; + + var setOpt = function (key, value) { + var opts = key; + if (!(value == null && Object.prototype.toString.call(key) == '[object Object]')) { + opts = {}; + opts[key] = value; + } + + var keys = Object.keys(opts); + for (var i = 0; i < keys.length; i++) { + if (keys[i] == 'bgColor' || keys[i] == 'textColor') { + _opt[keys[i]] = hexToRgb(opts[keys[i]]); + } else { + _opt[keys[i]] = opts[keys[i]]; + } + } + + _queue.push(_lastBadge); + icon.start(); + }; + + /** + * Draw video to context and repeat :) + */ + function drawVideo(video) { + if (video.paused || video.ended || _stop) { + return false; + } + //nasty hack for FF webcam (Thanks to Julian Ćwirko, kontakt@redsunmedia.pl) + try { + _context.clearRect(0, 0, _w, _h); + _context.drawImage(video, 0, 0, _w, _h); + } catch (e) { + + } + _drawTimeout = setTimeout(function () { + drawVideo(video); + }, animation.duration); + link.setIcon(_canvas); + } + + var link = {}; + /** + * Get icons from HEAD tag or create a new element + */ + link.getIcons = function () { + var elms = []; + //get link element + var getLinks = function () { + var icons = []; + var links = _doc.getElementsByTagName('head')[0].getElementsByTagName('link'); + for (var i = 0; i < links.length; i++) { + if ((/(^|\s)icon(\s|$)/i).test(links[i].getAttribute('rel'))) { + icons.push(links[i]); + } + } + return icons; + }; + if (_opt.element) { + elms = [_opt.element]; + } else if (_opt.elementId) { + //if img element identified by elementId + elms = [_doc.getElementById(_opt.elementId)]; + elms[0].setAttribute('href', elms[0].getAttribute('src')); + } else { + //if link element + elms = getLinks(); + if (elms.length === 0) { + elms = [_doc.createElement('link')]; + elms[0].setAttribute('rel', 'icon'); + _doc.getElementsByTagName('head')[0].appendChild(elms[0]); + } + } + elms.forEach(function(item) { + item.setAttribute('type', 'image/png'); + }); + return elms; + }; + link.setIcon = function (canvas) { + var url = canvas.toDataURL('image/png'); + link.setIconSrc(url); + }; + link.setIconSrc = function (url) { + if (_opt.dataUrl) { + //if using custom exporter + _opt.dataUrl(url); + } + if (_opt.element) { + _opt.element.setAttribute('href', url); + _opt.element.setAttribute('src', url); + } else if (_opt.elementId) { + //if is attached to element (image) + var elm = _doc.getElementById(_opt.elementId); + elm.setAttribute('href', url); + elm.setAttribute('src', url); + } else { + //if is attached to fav icon + if (_browser.ff || _browser.opera) { + //for FF we need to "recreate" element, atach to dom and remove old + //var originalType = _orig.getAttribute('rel'); + var old = _orig[_orig.length - 1]; + var newIcon = _doc.createElement('link'); + _orig = [newIcon]; + //_orig.setAttribute('rel', originalType); + if (_browser.opera) { + newIcon.setAttribute('rel', 'icon'); + } + newIcon.setAttribute('rel', 'icon'); + newIcon.setAttribute('type', 'image/png'); + _doc.getElementsByTagName('head')[0].appendChild(newIcon); + newIcon.setAttribute('href', url); + if (old.parentNode) { + old.parentNode.removeChild(old); + } + } else { + _orig.forEach(function(icon) { + icon.setAttribute('href', url); + }); + } + } + }; + + //http://stackoverflow.com/questions/5623838/rgb-to-hex-and-hex-to-rgb#answer-5624139 + //HEX to RGB convertor + function hexToRgb(hex) { + var shorthandRegex = /^#?([a-f\d])([a-f\d])([a-f\d])$/i; + hex = hex.replace(shorthandRegex, function (m, r, g, b) { + return r + r + g + g + b + b; + }); + var result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex); + return result ? { + r: parseInt(result[1], 16), + g: parseInt(result[2], 16), + b: parseInt(result[3], 16) + } : false; + } + + /** + * Merge options + */ + function merge(def, opt) { + var mergedOpt = {}; + var attrname; + for (attrname in def) { + mergedOpt[attrname] = def[attrname]; + } + for (attrname in opt) { + mergedOpt[attrname] = opt[attrname]; + } + return mergedOpt; + } + + /** + * Cross-browser page visibility shim + * http://stackoverflow.com/questions/12536562/detect-whether-a-window-is-visible + */ + function isPageHidden() { + return _doc.hidden || _doc.msHidden || _doc.webkitHidden || _doc.mozHidden; + } + + /** + * @namespace animation + */ + var animation = {}; + /** + * Animation "frame" duration + */ + animation.duration = 40; + /** + * Animation types (none,fade,pop,slide) + */ + animation.types = {}; + animation.types.fade = [{ + x: 0.4, + y: 0.4, + w: 0.6, + h: 0.6, + o: 0.0 + }, { + x: 0.4, + y: 0.4, + w: 0.6, + h: 0.6, + o: 0.1 + }, { + x: 0.4, + y: 0.4, + w: 0.6, + h: 0.6, + o: 0.2 + }, { + x: 0.4, + y: 0.4, + w: 0.6, + h: 0.6, + o: 0.3 + }, { + x: 0.4, + y: 0.4, + w: 0.6, + h: 0.6, + o: 0.4 + }, { + x: 0.4, + y: 0.4, + w: 0.6, + h: 0.6, + o: 0.5 + }, { + x: 0.4, + y: 0.4, + w: 0.6, + h: 0.6, + o: 0.6 + }, { + x: 0.4, + y: 0.4, + w: 0.6, + h: 0.6, + o: 0.7 + }, { + x: 0.4, + y: 0.4, + w: 0.6, + h: 0.6, + o: 0.8 + }, { + x: 0.4, + y: 0.4, + w: 0.6, + h: 0.6, + o: 0.9 + }, { + x: 0.4, + y: 0.4, + w: 0.6, + h: 0.6, + o: 1.0 + }]; + animation.types.none = [{ + x: 0.4, + y: 0.4, + w: 0.6, + h: 0.6, + o: 1 + }]; + animation.types.pop = [{ + x: 1, + y: 1, + w: 0, + h: 0, + o: 1 + }, { + x: 0.9, + y: 0.9, + w: 0.1, + h: 0.1, + o: 1 + }, { + x: 0.8, + y: 0.8, + w: 0.2, + h: 0.2, + o: 1 + }, { + x: 0.7, + y: 0.7, + w: 0.3, + h: 0.3, + o: 1 + }, { + x: 0.6, + y: 0.6, + w: 0.4, + h: 0.4, + o: 1 + }, { + x: 0.5, + y: 0.5, + w: 0.5, + h: 0.5, + o: 1 + }, { + x: 0.4, + y: 0.4, + w: 0.6, + h: 0.6, + o: 1 + }]; + animation.types.popFade = [{ + x: 0.75, + y: 0.75, + w: 0, + h: 0, + o: 0 + }, { + x: 0.65, + y: 0.65, + w: 0.1, + h: 0.1, + o: 0.2 + }, { + x: 0.6, + y: 0.6, + w: 0.2, + h: 0.2, + o: 0.4 + }, { + x: 0.55, + y: 0.55, + w: 0.3, + h: 0.3, + o: 0.6 + }, { + x: 0.50, + y: 0.50, + w: 0.4, + h: 0.4, + o: 0.8 + }, { + x: 0.45, + y: 0.45, + w: 0.5, + h: 0.5, + o: 0.9 + }, { + x: 0.4, + y: 0.4, + w: 0.6, + h: 0.6, + o: 1 + }]; + animation.types.slide = [{ + x: 0.4, + y: 1, + w: 0.6, + h: 0.6, + o: 1 + }, { + x: 0.4, + y: 0.9, + w: 0.6, + h: 0.6, + o: 1 + }, { + x: 0.4, + y: 0.9, + w: 0.6, + h: 0.6, + o: 1 + }, { + x: 0.4, + y: 0.8, + w: 0.6, + h: 0.6, + o: 1 + }, { + x: 0.4, + y: 0.7, + w: 0.6, + h: 0.6, + o: 1 + }, { + x: 0.4, + y: 0.6, + w: 0.6, + h: 0.6, + o: 1 + }, { + x: 0.4, + y: 0.5, + w: 0.6, + h: 0.6, + o: 1 + }, { + x: 0.4, + y: 0.4, + w: 0.6, + h: 0.6, + o: 1 + }]; + /** + * Run animation + * @param {Object} opt Animation options + * @param {Object} cb Callabak after all steps are done + * @param {Object} revert Reverse order? true|false + * @param {Object} step Optional step number (frame bumber) + */ + animation.run = function (opt, cb, revert, step) { + var animationType = animation.types[isPageHidden() ? 'none' : _opt.animation]; + if (revert === true) { + step = (typeof step !== 'undefined') ? step : animationType.length - 1; + } else { + step = (typeof step !== 'undefined') ? step : 0; + } + cb = (cb) ? cb : function () { + }; + if ((step < animationType.length) && (step >= 0)) { + type[_opt.type](merge(opt, animationType[step])); + _animTimeout = setTimeout(function () { + if (revert) { + step = step - 1; + } else { + step = step + 1; + } + animation.run(opt, cb, revert, step); + }, animation.duration); + + link.setIcon(_canvas); + } else { + cb(); + return; + } + }; + //auto init + init(); + return { + badge: badge, + video: video, + image: image, + rawImageSrc: rawImageSrc, + webcam: webcam, + setOpt: setOpt, + reset: icon.reset, + browser: { + supported: _browser.supported + } + }; + }); + + // AMD / RequireJS + if (typeof define !== 'undefined' && define.amd) { + define([], function () { + return Favico; + }); + } + // CommonJS + else if (typeof module !== 'undefined' && module.exports) { + module.exports = Favico; + } + // included directly via