You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@airflow.apache.org by bb...@apache.org on 2025/02/20 21:25:22 UTC
(airflow) 01/08: First run at deleting the legacy UI and webserver
This is an automated email from the ASF dual-hosted git repository.
bbovenzi pushed a commit to branch remove-legacy-ui
in repository https://gitbox.apache.org/repos/asf/airflow.git
commit 61d43ec9362ccb45fadb7946001ec4febb742951
Author: Brent Bovenzi <br...@gmail.com>
AuthorDate: Tue Feb 18 15:57:27 2025 -0500
First run at deleting the legacy UI and webserver
---
.dockerignore | 2 -
.github/CODEOWNERS | 3 -
.github/boring-cyborg.yml | 19 -
.github/workflows/basic-tests.yml | 38 -
.gitignore | 2 -
.pre-commit-config.yaml | 64 +-
Dockerfile.ci | 135 +-
airflow/api_fastapi/core_api/app.py | 2 +-
.../core_api/init_dagbag.py} | 24 +-
airflow/cli/cli_config.py | 23 -
.../commands/local_commands/standalone_command.py | 8 -
.../commands/local_commands/webserver_command.py | 491 -
airflow/settings.py | 17 -
airflow/ui/CONTRIBUTING.md | 2 +-
airflow/ui/src/layouts/Nav/Nav.tsx | 7 +-
airflow/utils/airflow_flask_app.py | 2 -
airflow/www/.eslintignore | 7 -
airflow/www/.eslintrc | 67 -
airflow/www/.prettierignore | 5 -
airflow/www/.prettierrc | 10 -
airflow/www/.stylelintignore | 2 -
airflow/www/.stylelintrc | 3 -
airflow/www/alias-rest-types.js | 214 -
airflow/www/app.py | 183 -
airflow/www/babel.config.js | 35 -
airflow/www/constants.py | 28 -
airflow/www/decorators.py | 200 -
airflow/www/extensions/init_appbuilder.py | 685 --
airflow/www/extensions/init_views.py | 336 -
airflow/www/forms.py | 231 -
airflow/www/gunicorn_config.py | 43 -
airflow/www/jest-setup.js | 69 -
airflow/www/jest.config.js | 64 -
airflow/www/package.json | 156 -
airflow/www/static/airflow.gif | Bin 622963 -> 0 bytes
airflow/www/static/css/bootstrap-theme.css | 6215 ----------
airflow/www/static/css/chart.css | 79 -
airflow/www/static/css/dags.css | 145 -
airflow/www/static/css/flash.css | 57 -
airflow/www/static/css/graph.css | 192 -
airflow/www/static/css/loading-dots.css | 60 -
airflow/www/static/css/main.css | 676 --
airflow/www/static/css/material-icons.css | 84 -
airflow/www/static/css/switch.css | 103 -
airflow/www/static/declarations.d.ts | 20 -
airflow/www/static/js/App.tsx | 93 -
airflow/www/static/js/README.md | 43 -
airflow/www/static/js/api/index.ts | 119 -
airflow/www/static/js/api/useAsset.ts | 38 -
airflow/www/static/js/api/useAssetDependencies.ts | 100 -
airflow/www/static/js/api/useAssetEvents.ts | 87 -
airflow/www/static/js/api/useAssets.ts | 49 -
airflow/www/static/js/api/useAssetsSummary.ts | 85 -
airflow/www/static/js/api/useCalendarData.ts | 48 -
airflow/www/static/js/api/useClearRun.ts | 68 -
airflow/www/static/js/api/useClearTask.ts | 117 -
airflow/www/static/js/api/useClearTaskDryRun.ts | 110 -
airflow/www/static/js/api/useCreateAssetEvent.ts | 52 -
airflow/www/static/js/api/useDag.ts | 31 -
airflow/www/static/js/api/useDagCode.ts | 41 -
airflow/www/static/js/api/useDagDetails.ts | 53 -
airflow/www/static/js/api/useDagRuns.tsx | 50 -
airflow/www/static/js/api/useDags.tsx | 40 -
airflow/www/static/js/api/useEventLogs.tsx | 85 -
airflow/www/static/js/api/useExtraLinks.ts | 82 -
airflow/www/static/js/api/useGraphData.ts | 61 -
airflow/www/static/js/api/useGridData.test.ts | 63 -
airflow/www/static/js/api/useGridData.ts | 171 -
airflow/www/static/js/api/useHealth.ts | 37 -
.../www/static/js/api/useHistoricalMetricsData.ts | 40 -
airflow/www/static/js/api/useMappedInstances.ts | 56 -
airflow/www/static/js/api/useMarkFailedRun.ts | 61 -
airflow/www/static/js/api/useMarkFailedTask.ts | 106 -
airflow/www/static/js/api/useMarkSuccessRun.ts | 60 -
airflow/www/static/js/api/useMarkSuccessTask.ts | 106 -
airflow/www/static/js/api/useMarkTaskDryRun.ts | 93 -
airflow/www/static/js/api/usePools.ts | 41 -
airflow/www/static/js/api/useQueueRun.ts | 59 -
airflow/www/static/js/api/useRenderedK8s.ts | 43 -
airflow/www/static/js/api/useSetDagRunNote.ts | 66 -
.../www/static/js/api/useSetTaskInstanceNote.ts | 129 -
airflow/www/static/js/api/useTIHistory.ts | 62 -
airflow/www/static/js/api/useTaskDetail.tsx | 33 -
.../www/static/js/api/useTaskFailedDependency.ts | 63 -
airflow/www/static/js/api/useTaskInstance.ts | 66 -
airflow/www/static/js/api/useTaskLog.ts | 83 -
airflow/www/static/js/api/useTaskXcom.ts | 73 -
.../www/static/js/api/useUpstreamAssetEvents.ts | 51 -
airflow/www/static/js/assetUtils.js | 90 -
airflow/www/static/js/assets/AssetDetails.tsx | 111 -
airflow/www/static/js/assets/AssetEvents.tsx | 129 -
airflow/www/static/js/assets/AssetList.test.tsx | 125 -
airflow/www/static/js/assets/AssetsList.tsx | 178 -
airflow/www/static/js/assets/CreateAssetEvent.tsx | 113 -
airflow/www/static/js/assets/Graph/DagNode.tsx | 104 -
airflow/www/static/js/assets/Graph/Legend.tsx | 51 -
airflow/www/static/js/assets/Graph/Node.tsx | 100 -
airflow/www/static/js/assets/Graph/index.tsx | 177 -
airflow/www/static/js/assets/Main.tsx | 294 -
airflow/www/static/js/assets/SearchBar.tsx | 130 -
airflow/www/static/js/assets/index.tsx | 51 -
airflow/www/static/js/assets/types.ts | 24 -
.../historical-metrics/PieChart.tsx | 147 -
.../cluster-activity/historical-metrics/index.tsx | 95 -
.../www/static/js/cluster-activity/index.test.tsx | 165 -
airflow/www/static/js/cluster-activity/index.tsx | 64 -
.../js/cluster-activity/live-metrics/DagRuns.tsx | 121 -
.../js/cluster-activity/live-metrics/Dags.tsx | 69 -
.../js/cluster-activity/live-metrics/Health.tsx | 121 -
.../js/cluster-activity/live-metrics/Pools.tsx | 141 -
.../js/cluster-activity/live-metrics/index.tsx | 61 -
.../static/js/cluster-activity/nav/FilterBar.tsx | 104 -
.../www/static/js/cluster-activity/useFilters.tsx | 101 -
.../www/static/js/components/AssetEventCard.tsx | 145 -
airflow/www/static/js/components/AutoRefresh.tsx | 59 -
.../www/static/js/components/BreadcrumbText.tsx | 46 -
.../www/static/js/components/Clipboard.test.tsx | 42 -
airflow/www/static/js/components/Clipboard.tsx | 102 -
airflow/www/static/js/components/DateTimeInput.tsx | 84 -
airflow/www/static/js/components/ErrorAlert.tsx | 36 -
airflow/www/static/js/components/Graph/Edge.tsx | 68 -
airflow/www/static/js/components/InfoTooltip.tsx | 46 -
.../static/js/components/InstanceTooltip.test.tsx | 137 -
.../www/static/js/components/InstanceTooltip.tsx | 130 -
.../www/static/js/components/LinkButton.test.tsx | 38 -
airflow/www/static/js/components/LinkButton.tsx | 34 -
.../www/static/js/components/LoadingWrapper.tsx | 53 -
airflow/www/static/js/components/MultiSelect.tsx | 53 -
.../www/static/js/components/NewTable/NewCells.tsx | 36 -
.../www/static/js/components/NewTable/NewTable.tsx | 269 -
.../js/components/NewTable/createSkeleton.tsx | 47 -
.../js/components/NewTable/searchParams.test.ts | 63 -
.../static/js/components/NewTable/searchParams.ts | 126 -
.../js/components/NewTable/useTableUrlState.ts | 69 -
airflow/www/static/js/components/ReactECharts.tsx | 101 -
airflow/www/static/js/components/ReactMarkdown.tsx | 158 -
.../www/static/js/components/RenderedJsonField.tsx | 72 -
airflow/www/static/js/components/RunTypeIcon.tsx | 52 -
.../static/js/components/ShortcutCheatSheet.tsx | 102 -
.../static/js/components/SourceTaskInstance.tsx | 98 -
.../www/static/js/components/TabWithTooltip.tsx | 49 -
.../www/static/js/components/Table/CardList.tsx | 223 -
airflow/www/static/js/components/Table/Cells.tsx | 37 -
.../www/static/js/components/Table/Table.test.tsx | 257 -
airflow/www/static/js/components/Table/index.tsx | 292 -
airflow/www/static/js/components/Time.test.tsx | 89 -
airflow/www/static/js/components/Time.tsx | 55 -
airflow/www/static/js/components/Tooltip.tsx | 200 -
.../www/static/js/components/TriggeredDagRuns.tsx | 92 -
airflow/www/static/js/components/ViewTimeDelta.tsx | 61 -
airflow/www/static/js/components/utils.test.ts | 84 -
airflow/www/static/js/components/utils.ts | 38 -
airflow/www/static/js/connection_form.js | 392 -
airflow/www/static/js/context/autorefresh.tsx | 130 -
airflow/www/static/js/context/containerRef.tsx | 39 -
airflow/www/static/js/context/timezone.tsx | 63 -
airflow/www/static/js/dag.js | 118 -
airflow/www/static/js/dag/Main.tsx | 298 -
airflow/www/static/js/dag/StatusBox.tsx | 170 -
airflow/www/static/js/dag/TaskName.test.tsx | 53 -
airflow/www/static/js/dag/TaskName.tsx | 87 -
airflow/www/static/js/dag/details/EventLog.tsx | 283 -
airflow/www/static/js/dag/details/FilterTasks.tsx | 117 -
airflow/www/static/js/dag/details/Header.tsx | 139 -
.../static/js/dag/details/NotesAccordion.test.tsx | 120 -
.../www/static/js/dag/details/NotesAccordion.tsx | 207 -
airflow/www/static/js/dag/details/dag/Calendar.tsx | 212 -
airflow/www/static/js/dag/details/dag/Dag.tsx | 335 -
.../www/static/js/dag/details/dag/RunDuration.tsx | 54 -
.../static/js/dag/details/dag/RunDurationChart.tsx | 257 -
.../static/js/dag/details/dagCode/CodeBlock.tsx | 75 -
.../www/static/js/dag/details/dagCode/index.tsx | 60 -
.../js/dag/details/dagRun/AssetTriggerEvents.tsx | 87 -
.../www/static/js/dag/details/dagRun/ClearRun.tsx | 134 -
.../js/dag/details/dagRun/ConfirmationModal.tsx | 99 -
.../www/static/js/dag/details/dagRun/Details.tsx | 148 -
.../www/static/js/dag/details/dagRun/MarkRunAs.tsx | 182 -
airflow/www/static/js/dag/details/dagRun/index.tsx | 67 -
.../static/js/dag/details/gantt/GanttTooltip.tsx | 87 -
.../static/js/dag/details/gantt/InstanceBar.tsx | 142 -
airflow/www/static/js/dag/details/gantt/Row.tsx | 147 -
airflow/www/static/js/dag/details/gantt/index.tsx | 223 -
.../www/static/js/dag/details/graph/AssetNode.tsx | 161 -
.../static/js/dag/details/graph/DagNode.test.tsx | 139 -
.../www/static/js/dag/details/graph/DagNode.tsx | 179 -
airflow/www/static/js/dag/details/graph/Node.tsx | 118 -
airflow/www/static/js/dag/details/graph/index.tsx | 384 -
airflow/www/static/js/dag/details/graph/utils.ts | 236 -
airflow/www/static/js/dag/details/index.tsx | 549 -
.../static/js/dag/details/task/AllTaskDuration.tsx | 168 -
.../static/js/dag/details/task/TaskDuration.tsx | 266 -
airflow/www/static/js/dag/details/task/index.tsx | 60 -
.../dag/details/taskInstance/AssetUpdateEvents.tsx | 90 -
.../dag/details/taskInstance/BackToTaskSummary.tsx | 40 -
.../static/js/dag/details/taskInstance/Details.tsx | 360 -
.../js/dag/details/taskInstance/ExtraLinks.tsx | 93 -
.../js/dag/details/taskInstance/Logs/LogBlock.tsx | 129 -
.../dag/details/taskInstance/Logs/LogLink.test.tsx | 73 -
.../js/dag/details/taskInstance/Logs/LogLink.tsx | 69 -
.../dag/details/taskInstance/Logs/index.test.tsx | 241 -
.../js/dag/details/taskInstance/Logs/index.tsx | 290 -
.../dag/details/taskInstance/Logs/utils.test.tsx | 159 -
.../js/dag/details/taskInstance/Logs/utils.ts | 178 -
.../dag/details/taskInstance/MappedInstances.tsx | 153 -
.../www/static/js/dag/details/taskInstance/Nav.tsx | 72 -
.../js/dag/details/taskInstance/RenderedK8s.tsx | 55 -
.../dag/details/taskInstance/TaskDocumentation.tsx | 77 -
.../details/taskInstance/TaskFailedDependency.tsx | 77 -
.../js/dag/details/taskInstance/TriggererInfo.tsx | 65 -
.../js/dag/details/taskInstance/TrySelector.tsx | 121 -
.../js/dag/details/taskInstance/Xcom/XcomEntry.tsx | 99 -
.../js/dag/details/taskInstance/Xcom/index.tsx | 112 -
.../static/js/dag/details/taskInstance/index.tsx | 139 -
.../taskInstance/taskActions/ActionButton.tsx | 41 -
.../taskInstance/taskActions/ActionModal.tsx | 130 -
.../taskInstance/taskActions/ClearInstance.tsx | 276 -
.../taskInstance/taskActions/MarkInstanceAs.tsx | 328 -
airflow/www/static/js/dag/grid/ToggleGroups.tsx | 87 -
airflow/www/static/js/dag/grid/dagRuns/Bar.tsx | 193 -
airflow/www/static/js/dag/grid/dagRuns/Tooltip.tsx | 54 -
.../www/static/js/dag/grid/dagRuns/index.test.tsx | 181 -
airflow/www/static/js/dag/grid/dagRuns/index.tsx | 186 -
airflow/www/static/js/dag/grid/index.test.tsx | 325 -
airflow/www/static/js/dag/grid/index.tsx | 150 -
.../www/static/js/dag/grid/renderTaskRows.test.tsx | 138 -
airflow/www/static/js/dag/grid/renderTaskRows.tsx | 217 -
airflow/www/static/js/dag/index.tsx | 54 -
.../static/js/dag/keyboardShortcutIdentifier.ts | 71 -
airflow/www/static/js/dag/nav/FilterBar.tsx | 208 -
airflow/www/static/js/dag/nav/LegendRow.test.tsx | 66 -
airflow/www/static/js/dag/nav/LegendRow.tsx | 95 -
airflow/www/static/js/dag/useFilters.test.tsx | 190 -
airflow/www/static/js/dag/useFilters.tsx | 219 -
airflow/www/static/js/dag/useSelection.test.tsx | 70 -
airflow/www/static/js/dag/useSelection.ts | 99 -
airflow/www/static/js/dag/useToggleGroups.ts | 43 -
airflow/www/static/js/dag_dependencies.js | 248 -
airflow/www/static/js/dags.js | 592 -
airflow/www/static/js/datetime_utils.js | 134 -
airflow/www/static/js/duration_chart.js | 34 -
airflow/www/static/js/index.d.ts | 33 -
airflow/www/static/js/login/Form.tsx | 44 -
airflow/www/static/js/login/index.test.tsx | 42 -
airflow/www/static/js/login/index.tsx | 73 -
airflow/www/static/js/main.js | 328 -
airflow/www/static/js/task.js | 48 -
airflow/www/static/js/task_instances.js | 149 -
airflow/www/static/js/theme.ts | 77 -
airflow/www/static/js/ti_log.js | 250 -
airflow/www/static/js/toggle_theme.js | 46 -
airflow/www/static/js/trigger.js | 285 -
airflow/www/static/js/types/api-generated.ts | 5777 ---------
airflow/www/static/js/types/index.ts | 239 -
.../www/static/js/types/react-table-config.d.ts | 152 -
.../www/static/js/utils/URLSearchParamWrapper.ts | 35 -
airflow/www/static/js/utils/graph.ts | 259 -
airflow/www/static/js/utils/handleError.test.ts | 68 -
airflow/www/static/js/utils/handleError.ts | 36 -
airflow/www/static/js/utils/index.test.ts | 230 -
airflow/www/static/js/utils/index.ts | 236 -
airflow/www/static/js/utils/testUtils.tsx | 73 -
airflow/www/static/js/utils/useErrorToast.ts | 38 -
airflow/www/static/js/utils/useKeysPress.ts | 69 -
airflow/www/static/js/utils/useOffsetTop.ts | 44 -
airflow/www/static/js/variable_edit.js | 24 -
airflow/www/static/loading.gif | Bin 16671 -> 0 bytes
airflow/www/static/pin.svg | 53 -
airflow/www/static/pin_100.png | Bin 7501 -> 0 bytes
airflow/www/static/pin_25.png | Bin 1547 -> 0 bytes
airflow/www/static/pin_32.png | Bin 1201 -> 0 bytes
airflow/www/static/pin_35.png | Bin 2306 -> 0 bytes
airflow/www/static/pin_40.png | Bin 2685 -> 0 bytes
airflow/www/static/pin_large.png | Bin 24922 -> 0 bytes
airflow/www/static/robots.txt | 2 -
airflow/www/static/sort_asc.png | Bin 160 -> 0 bytes
airflow/www/static/sort_both.png | Bin 201 -> 0 bytes
airflow/www/static/sort_desc.png | Bin 158 -> 0 bytes
airflow/www/templates/airflow/_messages.html | 30 -
.../templates/airflow/asset_next_run_modal.html | 58 -
airflow/www/templates/airflow/assets.html | 51 -
airflow/www/templates/airflow/chart.html | 65 -
.../www/templates/airflow/cluster_activity.html | 48 -
airflow/www/templates/airflow/config.html | 54 -
airflow/www/templates/airflow/confirm.html | 44 -
airflow/www/templates/airflow/conn_create.html | 46 -
airflow/www/templates/airflow/conn_edit.html | 45 -
airflow/www/templates/airflow/dag.html | 293 -
.../www/templates/airflow/dag_dependencies.html | 80 -
airflow/www/templates/airflow/dags.html | 491 -
airflow/www/templates/airflow/error.html | 35 -
airflow/www/templates/airflow/grid.html | 53 -
airflow/www/templates/airflow/login.html | 44 -
airflow/www/templates/airflow/main.html | 146 -
airflow/www/templates/airflow/model_list.html | 93 -
.../templates/airflow/no_roles_permissions.html | 42 -
airflow/www/templates/airflow/noaccess.html | 26 -
airflow/www/templates/airflow/plugin.html | 48 -
airflow/www/templates/airflow/pool_list.html | 28 -
airflow/www/templates/airflow/providers.html | 49 -
airflow/www/templates/airflow/redoc.html | 38 -
airflow/www/templates/airflow/task.html | 84 -
airflow/www/templates/airflow/task_instance.html | 64 -
airflow/www/templates/airflow/ti_code.html | 30 -
airflow/www/templates/airflow/ti_log.html | 74 -
airflow/www/templates/airflow/traceback.html | 57 -
airflow/www/templates/airflow/trigger.html | 314 -
airflow/www/templates/airflow/variable_edit.html | 28 -
airflow/www/templates/airflow/variable_list.html | 57 -
airflow/www/templates/airflow/variable_show.html | 28 -
.../templates/airflow/variable_show_widget.html | 70 -
airflow/www/templates/airflow/xcom.html | 38 -
.../www/templates/analytics/google_analytics.html | 28 -
airflow/www/templates/analytics/matomo.html | 34 -
airflow/www/templates/analytics/metarouter.html | 25 -
airflow/www/templates/analytics/segment.html | 25 -
airflow/www/templates/appbuilder/custom_icons.html | 34 -
airflow/www/templates/appbuilder/dag_docs.html | 41 -
airflow/www/templates/appbuilder/flash.html | 74 -
airflow/www/templates/appbuilder/index.html | 20 -
airflow/www/templates/appbuilder/loading_dots.html | 24 -
airflow/www/templates/appbuilder/navbar.html | 69 -
airflow/www/templates/appbuilder/navbar_menu.html | 60 -
airflow/www/templates/appbuilder/navbar_right.html | 98 -
airflow/www/templates/swagger-ui/index.j2 | 87 -
airflow/www/tsconfig.json | 46 -
airflow/www/utils.py | 918 +-
airflow/www/views.py | 5706 ---------
airflow/www/webpack.config.js | 287 -
airflow/www/widgets.py | 82 -
airflow/www/yarn.lock | 12073 -------------------
chart/values.yaml | 21 -
codecov.yml | 1 -
contributing-docs/03_contributors_quick_start.rst | 2 -
contributing-docs/08_static_code_checks.rst | 9 -
contributing-docs/14_node_environment_setup.rst | 27 +-
dev/breeze/doc/03_developer_tasks.rst | 17 +-
dev/breeze/doc/10_advanced_breeze_topics.rst | 6 +-
dev/breeze/doc/images/output-commands.svg | 88 +-
dev/breeze/doc/images/output_shell.svg | 300 +-
dev/breeze/doc/images/output_shell.txt | 2 +-
dev/breeze/doc/images/output_start-airflow.svg | 98 +-
dev/breeze/doc/images/output_start-airflow.txt | 2 +-
dev/breeze/doc/images/output_static-checks.svg | 110 +-
dev/breeze/doc/images/output_static-checks.txt | 2 +-
.../airflow_breeze/commands/developer_commands.py | 43 +-
.../commands/developer_commands_config.py | 11 -
.../src/airflow_breeze/commands/setup_commands.py | 1 -
.../src/airflow_breeze/params/shell_params.py | 8 -
dev/breeze/src/airflow_breeze/pre_commit_ids.py | 4 -
.../src/airflow_breeze/utils/kubernetes_utils.py | 10 -
dev/breeze/src/airflow_breeze/utils/path_utils.py | 7 -
dev/breeze/src/airflow_breeze/utils/run_utils.py | 57 -
.../src/airflow_breeze/utils/selective_checks.py | 27 -
dev/breeze/src/airflow_breeze/utils/visuals.py | 3 -
docs/apache-airflow/howto/customize-ui.rst | 46 -
docs/conf.py | 2 +-
hatch_build.py | 3 -
.../databricks/plugins/databricks_workflow.py | 4 +-
.../databricks/plugins/test_databricks_workflow.py | 2 +-
.../providers/edge/plugins/edge_executor_plugin.py | 7 +-
pyproject.toml | 5 +-
scripts/ci/docker-compose/base-ports.yml | 1 -
scripts/cov/cli_coverage.py | 1 -
scripts/cov/www_coverage.py | 55 -
scripts/docker/entrypoint_ci.sh | 84 -
scripts/in_container/bin/run_tmux | 8 -
.../local_commands/test_webserver_command.py | 380 -
tests/www/test_app.py | 287 -
tests/www/test_utils.py | 703 --
tests/www/views/__init__.py | 16 -
tests/www/views/conftest.py | 138 -
tests/www/views/test_anonymous_as_admin_role.py | 66 -
tests/www/views/test_session.py | 110 -
tests/www/views/test_views.py | 592 -
tests/www/views/test_views_asset.py | 413 -
tests/www/views/test_views_base.py | 424 -
tests/www/views/test_views_cluster_activity.py | 156 -
tests/www/views/test_views_configuration.py | 76 -
tests/www/views/test_views_connection.py | 477 -
tests/www/views/test_views_dagrun.py | 272 -
tests/www/views/test_views_decorators.py | 154 -
tests/www/views/test_views_extra_links.py | 315 -
tests/www/views/test_views_grid.py | 571 -
tests/www/views/test_views_home.py | 299 -
tests/www/views/test_views_log.py | 576 -
tests/www/views/test_views_mount.py | 62 -
tests/www/views/test_views_paused.py | 51 -
tests/www/views/test_views_pool.py | 143 -
tests/www/views/test_views_rate_limit.py | 69 -
tests/www/views/test_views_rendered.py | 405 -
tests/www/views/test_views_robots.py | 40 -
tests/www/views/test_views_task_norun.py | 54 -
tests/www/views/test_views_tasks.py | 1211 --
tests/www/views/test_views_trigger_dag.py | 350 -
tests/www/views/test_views_variable.py | 227 -
tests_common/test_utils/www.py | 235 -
396 files changed, 363 insertions(+), 75112 deletions(-)
diff --git a/.dockerignore b/.dockerignore
index 1c22d361bab..ab7451f2be8 100644
--- a/.dockerignore
+++ b/.dockerignore
@@ -80,8 +80,6 @@
# Git version is dynamically generated
airflow/git_version
-# Exclude mode_modules pulled by "yarn" for compilation of www files generated by NPM
-airflow/www/node_modules
airflow/ui/node_modules
airflow/auth/managers/simple/ui/node_modules
diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS
index 1079ccb5fd4..42fbebe3cce 100644
--- a/.github/CODEOWNERS
+++ b/.github/CODEOWNERS
@@ -26,9 +26,6 @@
/airflow/api_connexion/ @ephraimbuddy @pierrejeambrun
/airflow/api_fastapi/ @ephraimbuddy @pierrejeambrun
-# WWW
-/airflow/www/ @ryanahamilton @ashb @bbovenzi @pierrejeambrun @jscheffl
-
# UI
/airflow/ui/ @bbovenzi @pierrejeambrun @ryanahamilton @jscheffl
diff --git a/.github/boring-cyborg.yml b/.github/boring-cyborg.yml
index 770f775c8fa..922078bb9d8 100644
--- a/.github/boring-cyborg.yml
+++ b/.github/boring-cyborg.yml
@@ -319,10 +319,6 @@ labelPRBasedOnFilePath:
- .pre-commit-config.yaml
- .rat-excludes
- .readthedocs.yml
- - airflow/www/.eslintignore
- - airflow/www/.eslintrc
- - airflow/www/.stylelintignore
- - airflow/www/.stylelintrc
kind:documentation:
- docs/**/*
@@ -331,22 +327,7 @@ labelPRBasedOnFilePath:
- chart/**/*
- docs/helm-chart/**
- area:Webserver:
- - airflow/cli/commands/local_commands/webserver_command.py
- - airflow/www/**/*
- - tests/cli/commands/local_commands/test_webserver_command.py
- - tests/www/**/*
-
area:UI:
- - airflow/www/static/**/*
- - airflow/www/templates/**/*
- - airflow/www/.eslintignore
- - airflow/www/.eslintrc
- - airflow/www/.stylelintignore
- - airflow/www/.stylelintrc
- - airflow/www/package.json
- - airflow/www/webpack.config.js
- - airflow/www/yarn.lock
- docs/apache-airflow/ui.rst
- airflow/ui/**/*
diff --git a/.github/workflows/basic-tests.yml b/.github/workflows/basic-tests.yml
index 62ac87721a3..4ec7a05fd3c 100644
--- a/.github/workflows/basic-tests.yml
+++ b/.github/workflows/basic-tests.yml
@@ -151,44 +151,6 @@ jobs:
retention-days: '2'
if: steps.restore-eslint-cache-simple-am-ui.outputs.stash-hit != 'true'
- tests-www:
- timeout-minutes: 10
- name: React WWW tests
- runs-on: ${{ fromJSON(inputs.runs-on-as-json-public) }}
- if: inputs.run-www-tests == 'true'
- steps:
- - name: "Cleanup repo"
- shell: bash
- run: docker run -v "${GITHUB_WORKSPACE}:/workspace" -u 0:0 bash -c "rm -rf /workspace/*"
- - name: "Checkout ${{ github.ref }} ( ${{ github.sha }} )"
- uses: actions/checkout@v4
- with:
- persist-credentials: false
- - name: "Cleanup docker"
- run: ./scripts/ci/cleanup_docker.sh
- - name: "Setup node"
- uses: actions/setup-node@v4
- with:
- node-version: 21
- - name: "Restore eslint cache (www)"
- uses: apache/infrastructure-actions/stash/restore@1c35b5ccf8fba5d4c3fdf25a045ca91aa0cbc468
- with:
- path: airflow/www/node_modules/
- key: cache-www-node-modules-v1-${{ runner.os }}-${{ hashFiles('airflow/www/**/yarn.lock') }}
- id: restore-eslint-cache
- - run: yarn --cwd airflow/www/ install --frozen-lockfile --non-interactive
- - run: yarn --cwd airflow/www/ run test
- env:
- FORCE_COLOR: 2
- - name: "Save eslint cache (www)"
- uses: apache/infrastructure-actions/stash/save@1c35b5ccf8fba5d4c3fdf25a045ca91aa0cbc468
- with:
- path: airflow/www/node_modules/
- key: cache-www-node-modules-v1-${{ runner.os }}-${{ hashFiles('airflow/www/**/yarn.lock') }}
- if-no-files-found: 'error'
- retention-days: '2'
- if: steps.restore-eslint-cache.outputs.stash-hit != 'true'
-
install-pre-commit:
timeout-minutes: 5
name: "Install pre-commit for cache"
diff --git a/.gitignore b/.gitignore
index 84edc6d083f..4272415bff8 100644
--- a/.gitignore
+++ b/.gitignore
@@ -10,8 +10,6 @@ airflow.db
# Airflow temporary artifacts
airflow/git_version
-airflow/www/static/coverage/
-airflow/www/*.log
airflow/ui/coverage/
logs/
airflow-webserver.pid
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index ab73a9dfe8f..c4c7d4b14f8 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -67,23 +67,13 @@ repos:
- id: insert-license
name: Add license for CSS/JS/JSX/PUML/TS/TSX
files: \.(css|jsx?|puml|tsx?)$
- exclude: ^\.github/.*$|^airflow/www/static/js/types/api-generated.ts$|ui/openapi-gen/|.*/dist/.*
+ exclude: ^\.github/.*$|ui/openapi-gen/|.*/dist/.*
args:
- --comment-style
- "/*!| *| */"
- --license-filepath
- scripts/ci/license-templates/LICENSE.txt
- --fuzzy-match-generates-todo
- - id: insert-license
- name: Add license for all JINJA template files
- files: ^airflow/www/templates/.*\.html$
- exclude: ^\.github/.*$
- args:
- - --comment-style
- - "{#||#}"
- - --license-filepath
- - scripts/ci/license-templates/LICENSE.txt
- - --fuzzy-match-generates-todo
- id: insert-license
name: Add license for all Shell files
exclude: ^\.github/.*$|^dev/breeze/autocomplete/.*$
@@ -312,7 +302,7 @@ repos:
exclude: material-icons\.css$|^images/.*$|^RELEASE_NOTES\.txt$|^.*package-lock\.json$|^.*/kinglear\.txt$|^.*pnpm-lock\.yaml$|.*/dist/.*
args:
- --ignore-words=docs/spelling_wordlist.txt
- - --skip=providers/.*/src/airflow/providers/*/*.rst,providers/*/docs/changelog.rst,airflow/www/*.log,docs/*/commits.rst,providers/*/docs/commits.rst,providers/*/*/docs/commits.rst,docs/apache-airflow/tutorial/pipeline_example.csv,*.min.js,*.lock,INTHEWILD.md
+ - --skip=providers/.*/src/airflow/providers/*/*.rst,providers/*/docs/changelog.rst,docs/*/commits.rst,providers/*/docs/commits.rst,providers/*/*/docs/commits.rst,docs/apache-airflow/tutorial/pipeline_example.csv,*.min.js,*.lock,INTHEWILD.md
- --exclude-file=.codespellignorelines
- repo: https://github.com/woodruffw/zizmor-pre-commit
rev: v1.0.0
@@ -616,7 +606,6 @@ repos:
^airflow/api_connexion/openapi/v1.yaml$|
^airflow/ui/openapi-gen/|
^airflow/cli/commands/local_commands/fastapi_api_command.py$|
- ^airflow/cli/commands/local_commands/webserver_command.py$|
^airflow/config_templates/|
^airflow/models/baseoperator.py$|
^airflow/operators/__init__.py$|
@@ -649,9 +638,6 @@ repos:
^airflow/ui/pnpm-lock.yaml$|
^airflow/utils/db.py$|
^airflow/utils/trigger_rule.py$|
- ^airflow/www/static/css/bootstrap-theme.css$|
- ^airflow/www/static/js/types/api-generated.ts$|
- ^airflow/www/templates/appbuilder/flash.html$|
^chart/values.schema.json$|
^dev/|
^docs/README.rst$|
@@ -858,13 +844,6 @@ repos:
entry: koalaman/shellcheck:v0.8.0 -x -a
files: \.(bash|sh)$|^hooks/build$|^hooks/push$
exclude: ^dev/breeze/autocomplete/.*$
- - id: lint-css
- name: stylelint
- entry: "stylelint"
- language: node
- files: ^airflow/www/.*\.(css|sass|scss)$
- # Keep dependency versions in sync w/ airflow/www/package.json
- additional_dependencies: ['stylelint@13.3.1', 'stylelint-config-standard@20.0.0', 'stylelint-config-prettier@9.0.5']
- id: compile-ui-assets
name: Compile ui assets (manual)
language: node
@@ -874,15 +853,6 @@ repos:
entry: ./scripts/ci/pre_commit/compile_ui_assets.py
pass_filenames: false
additional_dependencies: ['pnpm@9.7.1']
- - id: compile-ui-assets-dev
- name: Compile ui assets in dev mode (manual)
- language: node
- stages: ['manual']
- types_or: [javascript, ts, tsx]
- files: ^airflow/ui/|^airflow/auth/managers/simple/ui/
- entry: ./scripts/ci/pre_commit/compile_ui_assets_dev.py
- pass_filenames: false
- additional_dependencies: ['pnpm@9.7.1']
- id: compile-fab-assets
name: Compile FAB provider assets
language: node
@@ -891,24 +861,15 @@ repos:
entry: ./scripts/ci/pre_commit/compile_fab_assets.py
pass_filenames: false
additional_dependencies: [ 'yarn@1.22.21' ]
- - id: compile-www-assets
- name: Compile www assets (manual)
- language: node
- stages: ['manual']
- 'types_or': [javascript, ts, tsx]
- files: ^airflow/www/
- entry: ./scripts/ci/pre_commit/compile_www_assets.py
- pass_filenames: false
- additional_dependencies: ['yarn@1.22.21']
- - id: compile-www-assets-dev
- name: Compile www assets in dev mode (manual)
+ - id: compile-ui-assets-dev
+ name: Compile ui assets in dev mode (manual)
language: node
stages: ['manual']
- 'types_or': [javascript, ts, tsx]
- files: ^airflow/www/
- entry: ./scripts/ci/pre_commit/compile_www_assets_dev.py
+ types_or: [javascript, ts, tsx]
+ files: ^airflow/ui/|^airflow/auth/managers/simple/ui/
+ entry: ./scripts/ci/pre_commit/compile_ui_assets_dev.py
pass_filenames: false
- additional_dependencies: ['yarn@1.22.21']
+ additional_dependencies: ['pnpm@9.7.1']
- id: check-providers-subpackages-init-file-exist
name: Provider subpackage init files are there
pass_filenames: false
@@ -1194,15 +1155,6 @@ repos:
additional_dependencies: ['rich>=12.4.4']
pass_filenames: false
files: ^tests/.*\.py$
- - id: ts-compile-format-lint-www
- name: Compile / format / lint WWW
- description: TS types generation / ESLint / Prettier against UI files
- language: node
- 'types_or': [javascript, ts, tsx, yaml, css, json]
- files: ^airflow/www/static/(js|css)/|^airflow/api_connexion/openapi/v1\.yaml$
- entry: ./scripts/ci/pre_commit/lint_www.py
- additional_dependencies: ['yarn@1.22.21', "openapi-typescript@>=6.7.4"]
- pass_filenames: false
- id: ts-compile-format-lint-ui
name: Compile / format / lint UI
description: TS types generation / ESLint / Prettier new UI files
diff --git a/Dockerfile.ci b/Dockerfile.ci
index 2f547a72cce..0d8c297bb9c 100644
--- a/Dockerfile.ci
+++ b/Dockerfile.ci
@@ -737,47 +737,6 @@ ASSET_COMPILATION_WAIT_MULTIPLIER=${ASSET_COMPILATION_WAIT_MULTIPLIER:=1}
. "${IN_CONTAINER_DIR}/check_connectivity.sh"
-function wait_for_asset_compilation() {
- if [[ -f "${AIRFLOW_SOURCES}/.build/www/.asset_compile.lock" ]]; then
- echo
- echo "${COLOR_YELLOW}Waiting for asset compilation to complete in the background.${COLOR_RESET}"
- echo
- local counter=0
- while [[ -f "${AIRFLOW_SOURCES}/.build/www/.asset_compile.lock" ]]; do
- if (( counter % 5 == 2 )); then
- echo "${COLOR_BLUE}Still waiting .....${COLOR_RESET}"
- fi
- sleep 1
- ((counter=counter+1))
- if [[ ${counter} == 30*$ASSET_COMPILATION_WAIT_MULTIPLIER ]]; then
- echo
- echo "${COLOR_YELLOW}The asset compilation is taking too long.${COLOR_YELLOW}"
- echo """
-If it does not complete soon, you might want to stop it and remove file lock:
- * press Ctrl-C
- * run 'rm ${AIRFLOW_SOURCES}/.build/www/.asset_compile.lock'
-"""
- fi
- if [[ ${counter} == 60*$ASSET_COMPILATION_WAIT_MULTIPLIER ]]; then
- echo
- echo "${COLOR_RED}The asset compilation is taking too long. Exiting.${COLOR_RED}"
- echo "${COLOR_RED}refer to dev/breeze/doc/04_troubleshooting.rst for resolution steps.${COLOR_RED}"
- echo
- exit 1
- fi
- done
- fi
- if [ -f "${AIRFLOW_SOURCES}/.build/www/asset_compile.out" ]; then
- echo
- echo "${COLOR_RED}The asset compilation failed. Exiting.${COLOR_RESET}"
- echo
- cat "${AIRFLOW_SOURCES}/.build/www/asset_compile.out"
- rm "${AIRFLOW_SOURCES}/.build/www/asset_compile.out"
- echo
- exit 1
- fi
-}
-
function environment_initialization() {
if [[ ${SKIP_ENVIRONMENT_INITIALIZATION=} == "true" ]]; then
return
@@ -864,7 +823,6 @@ function environment_initialization() {
if [[ ${START_AIRFLOW:="false"} == "true" || ${START_AIRFLOW} == "True" ]]; then
export AIRFLOW__CORE__LOAD_EXAMPLES=${LOAD_EXAMPLES}
- wait_for_asset_compilation
# shellcheck source=scripts/in_container/bin/run_tmux
exec run_tmux
fi
@@ -1016,46 +974,6 @@ function check_airflow_python_client_installation() {
python "${IN_CONTAINER_DIR}/install_airflow_python_client.py"
}
-function start_webserver_with_examples(){
- if [[ ${START_WEBSERVER_WITH_EXAMPLES=} != "true" ]]; then
- return
- fi
- export AIRFLOW__CORE__LOAD_EXAMPLES=True
- export AIRFLOW__API__AUTH_BACKENDS=airflow.api.auth.backend.session,airflow.providers.fab.auth_manager.api.auth.backend.basic_auth
- export AIRFLOW__WEBSERVER__EXPOSE_CONFIG=True
- echo
- echo "${COLOR_BLUE}Initializing database${COLOR_RESET}"
- echo
- airflow db migrate
- echo
- echo "${COLOR_BLUE}Database initialized${COLOR_RESET}"
- echo
- echo "${COLOR_BLUE}Parsing example dags${COLOR_RESET}"
- echo
- airflow dags reserialize
- echo "Example dags parsing finished"
- echo "Create admin user"
- airflow users create -u admin -p admin -f Thor -l Administrator -r Admin -e admin@email.domain
- echo "Admin user created"
- echo
- echo "${COLOR_BLUE}Starting airflow webserver${COLOR_RESET}"
- echo
- airflow webserver --port 8080 --daemon
- echo
- echo "${COLOR_BLUE}Waiting for webserver to start${COLOR_RESET}"
- echo
- check_service_connection "Airflow webserver" "run_nc localhost 8080" 100
- EXIT_CODE=$?
- if [[ ${EXIT_CODE} != 0 ]]; then
- echo
- echo "${COLOR_RED}Webserver did not start properly${COLOR_RESET}"
- echo
- exit ${EXIT_CODE}
- fi
- echo
- echo "${COLOR_BLUE}Airflow webserver started${COLOR_RESET}"
-}
-
handle_mount_sources
determine_airflow_to_use
environment_initialization
@@ -1064,7 +982,6 @@ check_downgrade_sqlalchemy
check_downgrade_pendulum
check_force_lowest_dependencies
check_airflow_python_client_installation
-start_webserver_with_examples
check_run_tests "${@}"
exec /bin/bash "${@}"
@@ -1115,8 +1032,8 @@ ARG DEV_APT_COMMAND=""
ARG ADDITIONAL_DEV_APT_COMMAND=""
ARG ADDITIONAL_DEV_ENV_VARS=""
ARG ADDITIONAL_DEV_APT_DEPS="bash-completion dumb-init git graphviz krb5-user \
-less libenchant-2-2 libgcc-11-dev libgeos-dev libpq-dev net-tools netcat-openbsd \
-openssh-server postgresql-client software-properties-common rsync tmux unzip vim xxd"
+ less libenchant-2-2 libgcc-11-dev libgeos-dev libpq-dev net-tools netcat-openbsd \
+ openssh-server postgresql-client software-properties-common rsync tmux unzip vim xxd"
ARG ADDITIONAL_DEV_APT_ENV=""
@@ -1153,7 +1070,7 @@ RUN bash /scripts/docker/install_mysql.sh prod \
&& bash /scripts/docker/install_postgres.sh dev \
# The user is added to allow ssh debugging (you can connect with airflow/airflow over ssh)
&& adduser --gecos "First Last,RoomNumber,WorkPhone,HomePhone" --disabled-password \
- --quiet "airflow" --home "/home/airflow" \
+ --quiet "airflow" --home "/home/airflow" \
&& echo -e "airflow\nairflow" | passwd airflow 2>&1 \
&& echo "airflow ALL=(ALL) NOPASSWD: ALL" > /etc/sudoers.d/airflow \
&& chmod 0440 /etc/sudoers.d/airflow
@@ -1309,7 +1226,7 @@ ENV ADDITIONAL_PYTHON_DEPS=${ADDITIONAL_PYTHON_DEPS}
RUN --mount=type=cache,id=ci-$TARGETARCH-$DEPENDENCY_CACHE_EPOCH,target=/root/.cache/ \
bash /scripts/docker/install_packaging_tools.sh; \
if [[ -n "${ADDITIONAL_PYTHON_DEPS}" ]]; then \
- bash /scripts/docker/install_additional_dependencies.sh; \
+ bash /scripts/docker/install_additional_dependencies.sh; \
fi
COPY --from=scripts entrypoint_ci.sh /entrypoint
@@ -1319,7 +1236,7 @@ RUN chmod a+x /entrypoint /entrypoint-exec
# Install autocomplete for airflow and kubectl
RUN if command -v airflow; then \
- register-python-argcomplete airflow >> ~/.bashrc ; \
+ register-python-argcomplete airflow >> ~/.bashrc ; \
fi; \
echo "source /etc/bash_completion" >> ~/.bashrc
@@ -1346,27 +1263,27 @@ RUN ln -sf /usr/bin/dumb-init /usr/local/bin/dumb-init
EXPOSE 8080
LABEL org.apache.airflow.distro="debian" \
- org.apache.airflow.module="airflow" \
- org.apache.airflow.component="airflow" \
- org.apache.airflow.image="airflow-ci" \
- org.apache.airflow.version="${AIRFLOW_VERSION}" \
- org.apache.airflow.uid="0" \
- org.apache.airflow.gid="0" \
- org.apache.airflow.build-id="${BUILD_ID}" \
- org.apache.airflow.commit-sha="${COMMIT_SHA}" \
- org.opencontainers.image.source="${AIRFLOW_IMAGE_REPOSITORY}" \
- org.opencontainers.image.created="${AIRFLOW_IMAGE_DATE_CREATED}" \
- org.opencontainers.image.authors="dev@airflow.apache.org" \
- org.opencontainers.image.url="https://airflow.apache.org" \
- org.opencontainers.image.documentation="https://airflow.apache.org/docs/docker-stack/index.html" \
- org.opencontainers.image.source="https://github.com/apache/airflow" \
- org.opencontainers.image.version="${AIRFLOW_VERSION}" \
- org.opencontainers.image.revision="${COMMIT_SHA}" \
- org.opencontainers.image.vendor="Apache Software Foundation" \
- org.opencontainers.image.licenses="Apache-2.0" \
- org.opencontainers.image.ref.name="airflow-ci-image" \
- org.opencontainers.image.title="Continuous Integration Airflow Image" \
- org.opencontainers.image.description="Installed Apache Airflow with Continuous Integration dependencies"
+ org.apache.airflow.module="airflow" \
+ org.apache.airflow.component="airflow" \
+ org.apache.airflow.image="airflow-ci" \
+ org.apache.airflow.version="${AIRFLOW_VERSION}" \
+ org.apache.airflow.uid="0" \
+ org.apache.airflow.gid="0" \
+ org.apache.airflow.build-id="${BUILD_ID}" \
+ org.apache.airflow.commit-sha="${COMMIT_SHA}" \
+ org.opencontainers.image.source="${AIRFLOW_IMAGE_REPOSITORY}" \
+ org.opencontainers.image.created="${AIRFLOW_IMAGE_DATE_CREATED}" \
+ org.opencontainers.image.authors="dev@airflow.apache.org" \
+ org.opencontainers.image.url="https://airflow.apache.org" \
+ org.opencontainers.image.documentation="https://airflow.apache.org/docs/docker-stack/index.html" \
+ org.opencontainers.image.source="https://github.com/apache/airflow" \
+ org.opencontainers.image.version="${AIRFLOW_VERSION}" \
+ org.opencontainers.image.revision="${COMMIT_SHA}" \
+ org.opencontainers.image.vendor="Apache Software Foundation" \
+ org.opencontainers.image.licenses="Apache-2.0" \
+ org.opencontainers.image.ref.name="airflow-ci-image" \
+ org.opencontainers.image.title="Continuous Integration Airflow Image" \
+ org.opencontainers.image.description="Installed Apache Airflow with Continuous Integration dependencies"
ENTRYPOINT ["/usr/bin/dumb-init", "--", "/entrypoint"]
CMD []
diff --git a/airflow/api_fastapi/core_api/app.py b/airflow/api_fastapi/core_api/app.py
index 08f37812c3c..3e9a7911980 100644
--- a/airflow/api_fastapi/core_api/app.py
+++ b/airflow/api_fastapi/core_api/app.py
@@ -22,6 +22,7 @@ import warnings
from pathlib import Path
from typing import cast
+from airflow.api_fastapi.core_api.init_dagbag import get_dag_bag
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
from fastapi.middleware.gzip import GZipMiddleware
@@ -33,7 +34,6 @@ from starlette.templating import Jinja2Templates
from airflow.api_fastapi.core_api.middleware import FlaskExceptionsMiddleware
from airflow.exceptions import AirflowException
from airflow.settings import AIRFLOW_PATH
-from airflow.www.extensions.init_dagbag import get_dag_bag
log = logging.getLogger(__name__)
diff --git a/airflow/www/blueprints.py b/airflow/api_fastapi/core_api/init_dagbag.py
similarity index 59%
rename from airflow/www/blueprints.py
rename to airflow/api_fastapi/core_api/init_dagbag.py
index fda6b65397b..fc23a2f7e06 100644
--- a/airflow/www/blueprints.py
+++ b/airflow/api_fastapi/core_api/init_dagbag.py
@@ -1,4 +1,3 @@
-#
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
@@ -17,12 +16,23 @@
# under the License.
from __future__ import annotations
-from flask import Blueprint, redirect, url_for
+import os
+
+from airflow.models import DagBag
+from airflow.settings import DAGS_FOLDER
+
+
+def get_dag_bag() -> DagBag:
+ """Instantiate the appropriate DagBag based on the ``SKIP_DAGS_PARSING`` environment variable."""
+ if os.environ.get("SKIP_DAGS_PARSING") == "True":
+ return DagBag(os.devnull, include_examples=False)
+ return DagBag(DAGS_FOLDER, read_dags_from_db=True)
-routes = Blueprint("routes", __name__)
+def init_dagbag(app):
+ """
+ Create global DagBag for webserver and API.
-@routes.route("/")
-def index():
- """Return main Airflow page."""
- return redirect(url_for("Airflow.index"))
+ To access it use ``flask.current_app.dag_bag``.
+ """
+ app.dag_bag = get_dag_bag()
diff --git a/airflow/cli/cli_config.py b/airflow/cli/cli_config.py
index 12664960b24..f58f8577e27 100644
--- a/airflow/cli/cli_config.py
+++ b/airflow/cli/cli_config.py
@@ -1857,29 +1857,6 @@ core_commands: list[CLICommand] = [
ARG_VERBOSE,
),
),
- ActionCommand(
- name="webserver",
- help="Start an Airflow webserver instance",
- func=lazy_load_command("airflow.cli.commands.local_commands.webserver_command.webserver"),
- args=(
- ARG_PORT,
- ARG_WORKERS,
- ARG_WORKERCLASS,
- ARG_WORKER_TIMEOUT,
- ARG_HOSTNAME,
- ARG_PID,
- ARG_DAEMON,
- ARG_STDOUT,
- ARG_STDERR,
- ARG_ACCESS_LOGFILE,
- ARG_ERROR_LOGFILE,
- ARG_ACCESS_LOGFORMAT,
- ARG_LOG_FILE,
- ARG_SSL_CERT,
- ARG_SSL_KEY,
- ARG_DEBUG,
- ),
- ),
ActionCommand(
name="fastapi-api",
help="Start an Airflow FastAPI API instance",
diff --git a/airflow/cli/commands/local_commands/standalone_command.py b/airflow/cli/commands/local_commands/standalone_command.py
index d3e59f95eb0..be80d0bf297 100644
--- a/airflow/cli/commands/local_commands/standalone_command.py
+++ b/airflow/cli/commands/local_commands/standalone_command.py
@@ -83,12 +83,6 @@ class StandaloneCommand:
command=["dag-processor"],
env=env,
)
- self.subcommands["webserver"] = SubCommand(
- self,
- name="webserver",
- command=["webserver"],
- env=env,
- )
self.subcommands["fastapi-api"] = SubCommand(
self,
name="fastapi-api",
@@ -102,7 +96,6 @@ class StandaloneCommand:
env=env,
)
- self.web_server_port = conf.getint("webserver", "WEB_SERVER_PORT", fallback=8080)
# Run subcommand threads
for command in self.subcommands.values():
command.start()
@@ -152,7 +145,6 @@ class StandaloneCommand:
"""
color: dict[str, Color] = {
"fastapi-api": "magenta",
- "webserver": "green",
"scheduler": "blue",
"dag-processor": "yellow",
"triggerer": "cyan",
diff --git a/airflow/cli/commands/local_commands/webserver_command.py b/airflow/cli/commands/local_commands/webserver_command.py
deleted file mode 100644
index b97e6bd3386..00000000000
--- a/airflow/cli/commands/local_commands/webserver_command.py
+++ /dev/null
@@ -1,491 +0,0 @@
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements. See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership. The ASF licenses this file
-# to you 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.
-"""Webserver command."""
-
-from __future__ import annotations
-
-import logging
-import os
-import signal
-import subprocess
-import sys
-import textwrap
-import time
-from contextlib import suppress
-from pathlib import Path
-from time import sleep
-from typing import NoReturn
-
-import psutil
-from lockfile.pidlockfile import read_pid_from_pidfile
-
-from airflow import settings
-from airflow.cli.commands.local_commands.daemon_utils import run_command_with_daemon_option
-from airflow.configuration import conf
-from airflow.exceptions import AirflowException, AirflowWebServerTimeout
-from airflow.utils import cli as cli_utils
-from airflow.utils.cli import setup_locations
-from airflow.utils.hashlib_wrapper import md5
-from airflow.utils.log.logging_mixin import LoggingMixin
-from airflow.utils.providers_configuration_loader import providers_configuration_loaded
-
-log = logging.getLogger(__name__)
-
-
-class GunicornMonitor(LoggingMixin):
- """
- Runs forever.
-
- Monitoring the child processes of @gunicorn_master_proc and restarting
- workers occasionally or when files in the plug-in directory has been modified.
-
- Each iteration of the loop traverses one edge of this state transition
- diagram, where each state (node) represents
- [ num_ready_workers_running / num_workers_running ]. We expect most time to
- be spent in [n / n]. `bs` is the setting webserver.worker_refresh_batch_size.
- The horizontal transition at ? happens after the new worker parses all the
- dags (so it could take a while!)
- V ────────────────────────────────────────────────────────────────────────┐
- [n / n] ──TTIN──> [ [n, n+bs) / n + bs ] ────?───> [n + bs / n + bs] ──TTOU─┘
- ^ ^───────────────┘
- │
- │ ┌────────────────v
- └──────┴────── [ [0, n) / n ] <─── start
- We change the number of workers by sending TTIN and TTOU to the gunicorn
- master process, which increases and decreases the number of child workers
- respectively. Gunicorn guarantees that on TTOU workers are terminated
- gracefully and that the oldest worker is terminated.
-
- :param gunicorn_master_pid: PID for the main Gunicorn process
- :param num_workers_expected: Number of workers to run the Gunicorn web server
- :param master_timeout: Number of seconds the webserver waits before killing gunicorn master that
- doesn't respond
- :param worker_refresh_interval: Number of seconds to wait before refreshing a batch of workers.
- :param worker_refresh_batch_size: Number of workers to refresh at a time. When set to 0, worker
- refresh is disabled. When nonzero, airflow periodically refreshes webserver workers by
- bringing up new ones and killing old ones.
- :param reload_on_plugin_change: If set to True, Airflow will track files in plugins_folder directory.
- When it detects changes, then reload the gunicorn.
- """
-
- def __init__(
- self,
- gunicorn_master_pid: int,
- num_workers_expected: int,
- master_timeout: int,
- worker_refresh_interval: int,
- worker_refresh_batch_size: int,
- reload_on_plugin_change: bool,
- ):
- super().__init__()
- self.gunicorn_master_proc = psutil.Process(gunicorn_master_pid)
- self.num_workers_expected = num_workers_expected
- self.master_timeout = master_timeout
- self.worker_refresh_interval = worker_refresh_interval
- self.worker_refresh_batch_size = worker_refresh_batch_size
- self.reload_on_plugin_change = reload_on_plugin_change
-
- self._num_workers_running = 0
- self._num_ready_workers_running = 0
- self._last_refresh_time = time.monotonic() if worker_refresh_interval > 0 else None
- self._last_plugin_state = self._generate_plugin_state() if reload_on_plugin_change else None
- self._restart_on_next_plugin_check = False
-
- def _generate_plugin_state(self) -> dict[str, float]:
- """
- Get plugin states.
-
- Generate dict of filenames and last modification time of all files in settings.PLUGINS_FOLDER
- directory.
- """
- if not settings.PLUGINS_FOLDER:
- return {}
-
- all_filenames: list[str] = []
- for root, _, filenames in os.walk(settings.PLUGINS_FOLDER):
- all_filenames.extend(os.path.join(root, f) for f in filenames)
- plugin_state = {f: self._get_file_hash(f) for f in sorted(all_filenames)}
- return plugin_state
-
- @staticmethod
- def _get_file_hash(fname: str):
- """Calculate MD5 hash for file."""
- hash_md5 = md5()
- with open(fname, "rb") as f:
- for chunk in iter(lambda: f.read(4096), b""):
- hash_md5.update(chunk)
- return hash_md5.hexdigest()
-
- def _get_num_ready_workers_running(self) -> int:
- """Return number of ready Gunicorn workers by looking for READY_PREFIX in process name."""
- workers = psutil.Process(self.gunicorn_master_proc.pid).children()
-
- def ready_prefix_on_cmdline(proc):
- try:
- cmdline = proc.cmdline()
- if cmdline:
- return settings.GUNICORN_WORKER_READY_PREFIX in cmdline[0]
- except psutil.NoSuchProcess:
- pass
- return False
-
- nb_ready_workers = sum(1 for proc in workers if ready_prefix_on_cmdline(proc))
- return nb_ready_workers
-
- def _get_num_workers_running(self) -> int:
- """Return number of running Gunicorn workers processes."""
- workers = psutil.Process(self.gunicorn_master_proc.pid).children()
- return len(workers)
-
- def _wait_until_true(self, fn, timeout: int = 0) -> None:
- """Sleep until fn is true."""
- start_time = time.monotonic()
- while not fn():
- if 0 < timeout <= time.monotonic() - start_time:
- raise AirflowWebServerTimeout(f"No response from gunicorn master within {timeout} seconds")
- sleep(0.1)
-
- def _spawn_new_workers(self, count: int) -> None:
- """
- Send signal to kill the worker.
-
- :param count: The number of workers to spawn
- """
- excess = 0
- for _ in range(count):
- # TTIN: Increment the number of processes by one
- self.gunicorn_master_proc.send_signal(signal.SIGTTIN)
- excess += 1
- self._wait_until_true(
- lambda: self.num_workers_expected + excess == self._get_num_workers_running(),
- timeout=self.master_timeout,
- )
-
- def _kill_old_workers(self, count: int) -> None:
- """
- Send signal to kill the worker.
-
- :param count: The number of workers to kill
- """
- for _ in range(count):
- count -= 1
- # TTOU: Decrement the number of processes by one
- self.gunicorn_master_proc.send_signal(signal.SIGTTOU)
- self._wait_until_true(
- lambda: self.num_workers_expected + count == self._get_num_workers_running(),
- timeout=self.master_timeout,
- )
-
- def _reload_gunicorn(self) -> None:
- """
- Send signal to reload the gunicorn configuration.
-
- When gunicorn receive signals, it reloads the configuration,
- start the new worker processes with a new configuration and gracefully
- shutdown older workers.
- """
- # HUP: Reload the configuration.
- self.gunicorn_master_proc.send_signal(signal.SIGHUP)
- sleep(1)
- self._wait_until_true(
- lambda: self.num_workers_expected == self._get_num_workers_running(), timeout=self.master_timeout
- )
-
- def start(self) -> NoReturn:
- """Start monitoring the webserver."""
- try:
- self._wait_until_true(
- lambda: self.num_workers_expected == self._get_num_workers_running(),
- timeout=self.master_timeout,
- )
- while True:
- if not self.gunicorn_master_proc.is_running():
- sys.exit(1)
- self._check_workers()
- # Throttle loop
- sleep(1)
-
- except (AirflowWebServerTimeout, OSError) as err:
- self.log.error(err)
- self.log.error("Shutting down webserver")
- try:
- self.gunicorn_master_proc.terminate()
- self.gunicorn_master_proc.wait()
- finally:
- sys.exit(1)
-
- def _check_workers(self) -> None:
- num_workers_running = self._get_num_workers_running()
- num_ready_workers_running = self._get_num_ready_workers_running()
-
- # Whenever some workers are not ready, wait until all workers are ready
- if num_ready_workers_running < num_workers_running:
- self.log.debug(
- "[%d / %d] Some workers are starting up, waiting...",
- num_ready_workers_running,
- num_workers_running,
- )
- sleep(1)
- return
-
- # If there are too many workers, then kill a worker gracefully by asking gunicorn to reduce
- # number of workers
- if num_workers_running > self.num_workers_expected:
- excess = min(num_workers_running - self.num_workers_expected, self.worker_refresh_batch_size)
- self.log.debug(
- "[%d / %d] Killing %s workers", num_ready_workers_running, num_workers_running, excess
- )
- self._kill_old_workers(excess)
- return
-
- # If there are too few workers, start a new worker by asking gunicorn
- # to increase number of workers
- if num_workers_running < self.num_workers_expected:
- self.log.error(
- "[%d / %d] Some workers seem to have died and gunicorn did not restart them as expected",
- num_ready_workers_running,
- num_workers_running,
- )
- sleep(10)
- num_workers_running = self._get_num_workers_running()
- if num_workers_running < self.num_workers_expected:
- new_worker_count = min(
- self.num_workers_expected - num_workers_running, self.worker_refresh_batch_size
- )
- # log at info since we are trying fix an error logged just above
- self.log.info(
- "[%d / %d] Spawning %d workers",
- num_ready_workers_running,
- num_workers_running,
- new_worker_count,
- )
- self._spawn_new_workers(new_worker_count)
- return
-
- # Now the number of running and expected worker should be equal
-
- # If workers should be restarted periodically.
- if self.worker_refresh_interval > 0 and self._last_refresh_time:
- # and we refreshed the workers a long time ago, refresh the workers
- last_refresh_diff = time.monotonic() - self._last_refresh_time
- if self.worker_refresh_interval < last_refresh_diff:
- num_new_workers = self.worker_refresh_batch_size
- self.log.debug(
- "[%d / %d] Starting doing a refresh. Starting %d workers.",
- num_ready_workers_running,
- num_workers_running,
- num_new_workers,
- )
- self._spawn_new_workers(num_new_workers)
- self._last_refresh_time = time.monotonic()
- return
-
- # if we should check the directory with the plugin,
- if self.reload_on_plugin_change:
- # compare the previous and current contents of the directory
- new_state = self._generate_plugin_state()
- # If changed, wait until its content is fully saved.
- if new_state != self._last_plugin_state:
- self.log.debug(
- "[%d / %d] Plugins folder changed. The gunicorn will be restarted the next time the "
- "plugin directory is checked, if there is no change in it.",
- num_ready_workers_running,
- num_workers_running,
- )
- self._restart_on_next_plugin_check = True
- self._last_plugin_state = new_state
- elif self._restart_on_next_plugin_check:
- self.log.debug(
- "[%d / %d] Starts reloading the gunicorn configuration.",
- num_ready_workers_running,
- num_workers_running,
- )
- self._restart_on_next_plugin_check = False
- self._last_refresh_time = time.monotonic()
- self._reload_gunicorn()
-
-
-@cli_utils.action_cli
-@providers_configuration_loaded
-def webserver(args):
- """Start Airflow Webserver."""
- print(settings.HEADER)
-
- # Check for old/insecure config, and fail safe (i.e. don't launch) if the config is wildly insecure.
- if conf.get("webserver", "secret_key") == "temporary_key":
- from rich import print as rich_print
-
- rich_print(
- "[red][bold]ERROR:[/bold] The `secret_key` setting under the webserver config has an insecure "
- "value - Airflow has failed safe and refuses to start. Please change this value to a new, "
- "per-environment, randomly generated string, for example using this command `[cyan]openssl rand "
- "-hex 30[/cyan]`",
- file=sys.stderr,
- )
- sys.exit(1)
-
- access_logfile = args.access_logfile or conf.get("webserver", "access_logfile")
- error_logfile = args.error_logfile or conf.get("webserver", "error_logfile")
- access_logformat = args.access_logformat or conf.get("webserver", "access_logformat")
- num_workers = args.workers or conf.get("webserver", "workers")
- worker_timeout = args.worker_timeout or conf.get("webserver", "web_server_worker_timeout")
- ssl_cert = args.ssl_cert or conf.get("webserver", "web_server_ssl_cert")
- ssl_key = args.ssl_key or conf.get("webserver", "web_server_ssl_key")
- if not ssl_cert and ssl_key:
- raise AirflowException("An SSL certificate must also be provided for use with " + ssl_key)
- if ssl_cert and not ssl_key:
- raise AirflowException("An SSL key must also be provided for use with " + ssl_cert)
-
- from airflow.www.app import create_app
-
- if args.debug:
- print(f"Starting the web server on port {args.port} and host {args.hostname}.")
- app = create_app(testing=conf.getboolean("core", "unit_test_mode"))
- app.run(
- debug=True,
- use_reloader=not app.config["TESTING"],
- port=args.port,
- host=args.hostname,
- ssl_context=(ssl_cert, ssl_key) if ssl_cert and ssl_key else None,
- )
- else:
- print(
- textwrap.dedent(
- f"""\
- Running the Gunicorn Server with:
- Workers: {num_workers} {args.workerclass}
- Host: {args.hostname}:{args.port}
- Timeout: {worker_timeout}
- Logfiles: {access_logfile} {error_logfile}
- Access Logformat: {access_logformat}
- ================================================================="""
- )
- )
-
- pid_file, _, _, _ = setup_locations("webserver", pid=args.pid)
- run_args = [
- sys.executable,
- "-m",
- "gunicorn",
- "--workers",
- str(num_workers),
- "--worker-class",
- str(args.workerclass),
- "--timeout",
- str(worker_timeout),
- "--bind",
- args.hostname + ":" + str(args.port),
- "--name",
- "airflow-webserver",
- "--pid",
- pid_file,
- "--config",
- "python:airflow.www.gunicorn_config",
- ]
-
- if args.access_logfile:
- run_args += ["--access-logfile", str(args.access_logfile)]
-
- if args.error_logfile:
- run_args += ["--error-logfile", str(args.error_logfile)]
-
- if args.access_logformat and args.access_logformat.strip():
- run_args += ["--access-logformat", str(args.access_logformat)]
-
- if args.daemon:
- run_args += ["--daemon"]
-
- if ssl_cert:
- run_args += ["--certfile", ssl_cert, "--keyfile", ssl_key]
-
- run_args += ["airflow.www.app:cached_app()"]
-
- if conf.getboolean("webserver", "reload_on_plugin_change", fallback=False):
- log.warning(
- "Setting reload_on_plugin_change = true prevents running Gunicorn with preloading. "
- "This means the app cannot be loaded before workers are forked, and each worker has a "
- "separate copy of the app. This may cause IntegrityError during webserver startup, and "
- "should be avoided in production."
- )
- else:
- # To prevent different workers creating the web app and
- # all writing to the database at the same time, we use the --preload option.
- run_args += ["--preload"]
-
- def kill_proc(signum: int, gunicorn_master_proc: psutil.Process | subprocess.Popen) -> NoReturn:
- log.info("Received signal: %s. Closing gunicorn.", signum)
- gunicorn_master_proc.terminate()
- with suppress(TimeoutError):
- gunicorn_master_proc.wait(timeout=30)
- if isinstance(gunicorn_master_proc, subprocess.Popen):
- still_running = gunicorn_master_proc.poll() is not None
- else:
- still_running = gunicorn_master_proc.is_running()
- if still_running:
- gunicorn_master_proc.kill()
- sys.exit(0)
-
- def monitor_gunicorn(gunicorn_master_proc: psutil.Process | subprocess.Popen) -> NoReturn:
- # Register signal handlers
- signal.signal(signal.SIGINT, lambda signum, _: kill_proc(signum, gunicorn_master_proc))
- signal.signal(signal.SIGTERM, lambda signum, _: kill_proc(signum, gunicorn_master_proc))
-
- # These run forever until SIG{INT, TERM, KILL, ...} signal is sent
- GunicornMonitor(
- gunicorn_master_pid=gunicorn_master_proc.pid,
- num_workers_expected=num_workers,
- master_timeout=conf.getint("webserver", "web_server_master_timeout"),
- worker_refresh_interval=conf.getint("webserver", "worker_refresh_interval", fallback=30),
- worker_refresh_batch_size=conf.getint("webserver", "worker_refresh_batch_size", fallback=1),
- reload_on_plugin_change=conf.getboolean(
- "webserver", "reload_on_plugin_change", fallback=False
- ),
- ).start()
-
- def start_and_monitor_gunicorn(args):
- if args.daemon:
- subprocess.Popen(run_args, close_fds=True)
-
- # Reading pid of gunicorn master as it will be different that
- # the one of process spawned above.
- gunicorn_master_proc_pid = None
- while not gunicorn_master_proc_pid:
- sleep(0.1)
- gunicorn_master_proc_pid = read_pid_from_pidfile(pid_file)
-
- # Run Gunicorn monitor
- gunicorn_master_proc = psutil.Process(gunicorn_master_proc_pid)
- monitor_gunicorn(gunicorn_master_proc)
- else:
- with subprocess.Popen(run_args, close_fds=True) as gunicorn_master_proc:
- monitor_gunicorn(gunicorn_master_proc)
-
- if args.daemon:
- # This makes possible errors get reported before daemonization
- os.environ["SKIP_DAGS_PARSING"] = "True"
- create_app(None)
- os.environ.pop("SKIP_DAGS_PARSING")
-
- pid_file_path = Path(pid_file)
- monitor_pid_file = str(pid_file_path.with_name(f"{pid_file_path.stem}-monitor{pid_file_path.suffix}"))
- run_command_with_daemon_option(
- args=args,
- process_name="webserver",
- callback=lambda: start_and_monitor_gunicorn(args),
- should_setup_logging=True,
- pid_file=monitor_pid_file,
- )
diff --git a/airflow/settings.py b/airflow/settings.py
index 82168685aa7..fb9450ef741 100644
--- a/airflow/settings.py
+++ b/airflow/settings.py
@@ -48,8 +48,6 @@ if TYPE_CHECKING:
from sqlalchemy.engine import Engine
from sqlalchemy.orm import Session as SASession
- from airflow.www.utils import UIAlert
-
log = logging.getLogger(__name__)
try:
@@ -707,21 +705,6 @@ HIDE_SENSITIVE_VAR_CONN_FIELDS = conf.getboolean("core", "hide_sensitive_var_con
# instances
MASK_SECRETS_IN_LOGS = False
-# Display alerts on the dashboard
-# Useful for warning about setup issues or announcing changes to end users
-# List of UIAlerts, which allows for specifying the message, category, and roles the
-# message should be shown to. For example:
-# from airflow.www.utils import UIAlert
-#
-# DASHBOARD_UIALERTS = [
-# UIAlert("Welcome to Airflow"), # All users
-# UIAlert("Airflow update happening next week", roles=["User"]), # Only users with the User role
-# # A flash message with html:
-# UIAlert('Visit <a href="http://airflow.apache.org">airflow.apache.org</a>', html=True),
-# ]
-#
-DASHBOARD_UIALERTS: list[UIAlert] = []
-
# Prefix used to identify tables holding data moved during migration.
AIRFLOW_MOVED_TABLE_PREFIX = "_airflow_moved"
diff --git a/airflow/ui/CONTRIBUTING.md b/airflow/ui/CONTRIBUTING.md
index 73ff3ee00df..44e5a55570e 100644
--- a/airflow/ui/CONTRIBUTING.md
+++ b/airflow/ui/CONTRIBUTING.md
@@ -28,7 +28,7 @@ Manually:
- Have the `dev-mode` environment variable enabled
- Run `pnpm install && pnpm dev`
-- Note: Make sure to access the UI via the Airflow localhost port (8080 or 28080) and not the vite port (5173)
+- Note: Make sure to access the UI via the Airflow localhost port (9091 or 29091) and not the vite port (5173)
## More
diff --git a/airflow/ui/src/layouts/Nav/Nav.tsx b/airflow/ui/src/layouts/Nav/Nav.tsx
index d182ce6f391..7bb520db878 100644
--- a/airflow/ui/src/layouts/Nav/Nav.tsx
+++ b/airflow/ui/src/layouts/Nav/Nav.tsx
@@ -17,7 +17,7 @@
* under the License.
*/
import { Box, Flex, VStack, Link } from "@chakra-ui/react";
-import { FiCornerUpLeft, FiDatabase, FiHome } from "react-icons/fi";
+import { FiDatabase, FiHome } from "react-icons/fi";
import { useVersionServiceGetVersion } from "openapi/queries";
import { AirflowPin } from "src/assets/AirflowPin";
@@ -56,11 +56,6 @@ export const Nav = () => {
<AdminButton />
</Flex>
<Flex flexDir="column">
- <NavButton
- icon={<FiCornerUpLeft size="1.75rem" />}
- title="Legacy UI"
- to={import.meta.env.VITE_LEGACY_API_URL || "http://localhost:28080/home"}
- />
<DocsButton />
<UserSettingsButton />
<Link
diff --git a/airflow/utils/airflow_flask_app.py b/airflow/utils/airflow_flask_app.py
index f18f02a0ecb..498f15ea312 100644
--- a/airflow/utils/airflow_flask_app.py
+++ b/airflow/utils/airflow_flask_app.py
@@ -22,13 +22,11 @@ from flask import Flask
if TYPE_CHECKING:
from airflow.models.dagbag import DagBag
- from airflow.www.extensions.init_appbuilder import AirflowAppBuilder
class AirflowApp(Flask):
"""Airflow Flask Application."""
- appbuilder: AirflowAppBuilder
dag_bag: DagBag
api_auth: list[Any]
diff --git a/airflow/www/.eslintignore b/airflow/www/.eslintignore
deleted file mode 100644
index f70244c96f6..00000000000
--- a/airflow/www/.eslintignore
+++ /dev/null
@@ -1,7 +0,0 @@
-**/*{.,-}min.js
-**/*.sh
-**/*.py
-jqClock.min.js
-coverage/**
-static/dist/*
-static/docs/*
diff --git a/airflow/www/.eslintrc b/airflow/www/.eslintrc
deleted file mode 100644
index 29914c0cd84..00000000000
--- a/airflow/www/.eslintrc
+++ /dev/null
@@ -1,67 +0,0 @@
-{
- "extends": ["airbnb", "airbnb/hooks", "prettier"],
- "parser": "@babel/eslint-parser",
- "parserOptions": {
- "babelOptions": {
- "presets": [
- "@babel/preset-env",
- "@babel/preset-react",
- "@babel/preset-typescript"
- ],
- "plugins": ["@babel/plugin-transform-runtime"]
- }
- },
- "plugins": ["html", "react"],
- "rules": {
- "no-param-reassign": 1,
- "react/prop-types": 0,
- "react/jsx-props-no-spreading": 0,
- "import/extensions": [
- "error",
- "ignorePackages",
- {
- "js": "never",
- "jsx": "never",
- "ts": "never",
- "tsx": "never"
- }
- ],
- "import/no-extraneous-dependencies": [
- "error",
- {
- "devDependencies": true,
- "optionalDependencies": false,
- "peerDependencies": false
- }
- ],
- "react/function-component-definition": [
- 0,
- {
- "namedComponents": "function-declaration"
- }
- ]
- },
- "settings": {
- "import/resolver": {
- "node": {
- "extensions": [".js", ".jsx", ".ts", ".tsx"]
- }
- }
- },
- // eslint that apply only to typescript files
- "overrides": [
- {
- "files": ["*.ts", "*.tsx"],
- "extends": ["airbnb-typescript", "prettier"],
- "parser": "@typescript-eslint/parser",
- "plugins": ["@typescript-eslint"],
- "parserOptions": {
- "project": "./tsconfig.json"
- },
- "rules": {
- "react/require-default-props": 0,
- "@typescript-eslint/no-explicit-any": 1
- }
- }
- ]
-}
diff --git a/airflow/www/.prettierignore b/airflow/www/.prettierignore
deleted file mode 100644
index a2bcd8157d6..00000000000
--- a/airflow/www/.prettierignore
+++ /dev/null
@@ -1,5 +0,0 @@
-.mypy_cache/
-templates/**/*.html
-dist/
-*.md
-*.yaml
diff --git a/airflow/www/.prettierrc b/airflow/www/.prettierrc
deleted file mode 100644
index 3574cf79c5c..00000000000
--- a/airflow/www/.prettierrc
+++ /dev/null
@@ -1,10 +0,0 @@
-{
- "overrides": [
- {
- "files": "*.json",
- "options": {
- "tabWidth": 2
- }
- }
- ]
-}
diff --git a/airflow/www/.stylelintignore b/airflow/www/.stylelintignore
deleted file mode 100644
index 683379b45b2..00000000000
--- a/airflow/www/.stylelintignore
+++ /dev/null
@@ -1,2 +0,0 @@
-node_modules
-static/dist/
diff --git a/airflow/www/.stylelintrc b/airflow/www/.stylelintrc
deleted file mode 100644
index 2e8ff5864a4..00000000000
--- a/airflow/www/.stylelintrc
+++ /dev/null
@@ -1,3 +0,0 @@
-{
- "extends": ["stylelint-config-standard", "stylelint-config-prettier"]
-}
diff --git a/airflow/www/alias-rest-types.js b/airflow/www/alias-rest-types.js
deleted file mode 100644
index cc6e53cd965..00000000000
--- a/airflow/www/alias-rest-types.js
+++ /dev/null
@@ -1,214 +0,0 @@
-/*!
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you 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.
- */
-
-const ts = require("typescript");
-const fs = require("fs");
-
-/* This library does three things to make openapi-typescript generation easier to use.
- * 1. Creates capitalized exports for Paths and Operations
- * 2. Alias Variables based either on the Path name or the Operation ID, such as:
- * export type ListProjectsVariables = operations['listProjects']['parameters']['query'];
- * 3. Aliases the returned data types, such as:
- * export type ConnectionArgument = components['schemas']['ConnectionArgument'];
- */
-
-/* Finds all words, capitalizes them, and removes all other characters. */
-const toPascalCase = (str) =>
- (str.match(/[a-zA-Z0-9]+/g) || [])
- .map((w) => `${w.charAt(0).toUpperCase()}${w.slice(1)}`)
- .join("");
-
-/* Adds a prefix to a type prop as necessary.
- * ('', 'components') => 'components'
- */
-const prefixPath = (rootPrefix, prop) =>
- rootPrefix ? `${rootPrefix}['${prop}']` : prop;
-
-// Recursively find child nodes by name.
-const findNode = (node, ...names) => {
- if (!node || names.length === 0) return node;
-
- const children = node.members || node.type.members;
-
- if (!children) {
- return undefined;
- }
-
- const child = children.find((c) => c.name?.text === names[0]);
-
- return findNode(child, ...names.slice(1));
-};
-
-// Generate Variable Type Aliases for a given path or operation
-const generateVariableAliases = (node, operationPath, operationName) => {
- const variableTypes = [];
- const hasPath = !!findNode(node, "parameters", "path");
- const hasQuery = !!findNode(node, "parameters", "query");
- const hasBody = !!findNode(
- node,
- "requestBody",
- "content",
- "application/json"
- );
-
- if (hasPath) variableTypes.push(`${operationPath}['parameters']['path']`);
- if (hasQuery) variableTypes.push(`${operationPath}['parameters']['query']`);
- if (hasBody)
- variableTypes.push(
- `${operationPath}['requestBody']['content']['application/json']`
- );
-
- if (variableTypes.length === 0) return "";
- const typeName = `${toPascalCase(operationName)}Variables`;
- return [
- typeName,
- `export type ${typeName} = CamelCasedPropertiesDeep<${variableTypes.join(
- " & "
- )}>;`,
- ];
-};
-
-// Generate Type Aliases
-const generateAliases = (rootNode, writeText, prefix = "") => {
- // Loop through the root AST nodes of the file
- ts.forEachChild(rootNode, (node) => {
- // Response Data Types
- if (ts.isInterfaceDeclaration(node) && node.name?.text === "components") {
- const schemaMemberNames = findNode(node, "schemas").type.members.map(
- (n) => n.name?.text
- );
-
- const types = schemaMemberNames.map((n) => [
- `${n}`,
- `export type ${n} = CamelCasedPropertiesDeep<${prefixPath(
- prefix,
- "components"
- )}['schemas']['${n}']>;`,
- ]);
- if (types.length) {
- writeText.push(["comment", `Types for returned data ${prefix}`]);
- writeText.push(...types);
- }
- }
-
- // Paths referencing an operation are skipped
- if (node.name?.text === "paths") {
- if (!prefix) {
- writeText.push(["comment", "Alias paths to PascalCase."]);
- writeText.push(["Paths", "export type Paths = paths;"]);
- }
-
- const types = [];
-
- (node.members || node.type.members).forEach((path) => {
- const methodNames = path.type.members.map((m) => m.name.text);
- const methodTypes = methodNames.map((m) =>
- generateVariableAliases(
- findNode(path, m),
- `${prefixPath(prefix, "paths")}['${path.name?.text}']['${m}']`,
- `${path.name.text}${toPascalCase(m)}`
- )
- );
- types.push(...methodTypes.filter((m) => !!m));
- });
-
- if (types.length) {
- writeText.push([
- "comment",
- `Types for path operation variables ${prefix}`,
- ]);
- writeText.push(...types);
- }
- }
-
- // operationIds are defined
- if (node.name?.text === "operations") {
- if (!prefix) {
- writeText.push(["comment", "Alias operations to PascalCase."]);
- writeText.push(["Operations", "export type Operations = operations;"]);
- }
-
- const types = (node.members || node.type.members).map((operation) =>
- generateVariableAliases(
- operation,
- `${prefixPath(prefix, "operations")}['${operation.name.text}']`,
- operation.name.text
- )
- );
- if (types.length) {
- writeText.push(["comment", `Types for operation variables ${prefix}`]);
- writeText.push(...types);
- writeText.push("\n");
- }
- }
-
- // recursively call this for any externals
- if (ts.isInterfaceDeclaration(node) && node.name?.text === "external") {
- node.members.forEach((external) => {
- generateAliases(
- external.type,
- writeText,
- `external['${external.name.text}']`
- );
- });
- }
- });
-};
-
-function generate(file) {
- // Create a Program to represent the project, then pull out the
- // source file to parse its AST.
- const program = ts.createProgram([file], { allowJs: true });
- const sourceFile = program.getSourceFile(file);
- const writeText = [];
- writeText.push(["comment", "eslint-disable"]);
- // eslint-disable-next-line quotes
- writeText.push([
- "block",
- `import type { CamelCasedPropertiesDeep } from 'type-fest';`,
- ]);
- writeText.push(["block", sourceFile.text]);
- generateAliases(sourceFile, writeText);
-
- const finalText = writeText
- // Deduplicate types
- .map((pair) => {
- // keep all comments and code blocks
- if (pair[0] === "comment" || pair[0] === "block") return pair;
- // return the first instance of this key only
- const firstInstance = writeText.find((p) => p[0] === pair[0]);
- return firstInstance === pair
- ? pair
- : ["comment", `Duplicate removed: ${pair[1]}`];
- })
- // Remove undefined created above
- .filter((p) => !!p)
- // Escape comments and flatten.
- .map((pair) => (pair[0] === "comment" ? `\n/* ${pair[1]} */` : pair[1]))
- .join("\n");
-
- fs.writeFileSync(file, finalText, (err) => {
- if (err) {
- // eslint-disable-next-line no-console
- console.error(err);
- }
- });
-}
-
-generate(process.argv[2]);
diff --git a/airflow/www/app.py b/airflow/www/app.py
deleted file mode 100644
index 06a0e14de04..00000000000
--- a/airflow/www/app.py
+++ /dev/null
@@ -1,183 +0,0 @@
-#
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements. See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership. The ASF licenses this file
-# to you 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.
-from __future__ import annotations
-
-from datetime import timedelta
-from os.path import isabs
-
-from flask import Flask
-from flask_appbuilder import SQLA
-from flask_wtf.csrf import CSRFProtect
-from markupsafe import Markup
-from sqlalchemy.engine.url import make_url
-
-from airflow import settings
-from airflow.configuration import conf
-from airflow.exceptions import AirflowConfigException
-from airflow.logging_config import configure_logging
-from airflow.models import import_all_models
-from airflow.utils.json import AirflowJsonProvider
-from airflow.www.extensions.init_appbuilder import init_appbuilder
-from airflow.www.extensions.init_appbuilder_links import init_appbuilder_links
-from airflow.www.extensions.init_cache import init_cache
-from airflow.www.extensions.init_dagbag import init_dagbag
-from airflow.www.extensions.init_jinja_globals import init_jinja_globals
-from airflow.www.extensions.init_manifest_files import configure_manifest_files
-from airflow.www.extensions.init_robots import init_robots
-from airflow.www.extensions.init_security import (
- init_api_auth,
- init_cache_control,
- init_xframe_protection,
-)
-from airflow.www.extensions.init_session import init_airflow_session_interface
-from airflow.www.extensions.init_views import (
- init_api_auth_provider,
- init_api_connexion,
- init_api_error_handlers,
- init_api_internal,
- init_appbuilder_views,
- init_error_handlers,
- init_flash_views,
- init_plugins,
-)
-from airflow.www.extensions.init_wsgi_middlewares import init_wsgi_middleware
-
-app: Flask | None = None
-
-# Initializes at the module level, so plugins can access it.
-# See: /docs/plugins.rst
-csrf = CSRFProtect()
-
-
-def create_app(config=None, testing=False):
- """Create a new instance of Airflow WWW app."""
- flask_app = Flask(__name__)
- flask_app.secret_key = conf.get("webserver", "SECRET_KEY")
-
- flask_app.config["PERMANENT_SESSION_LIFETIME"] = timedelta(minutes=settings.get_session_lifetime_config())
-
- flask_app.config["MAX_CONTENT_LENGTH"] = conf.getfloat("webserver", "allowed_payload_size") * 1024 * 1024
- flask_app.config["MAX_FORM_PARTS"] = conf.getint("webserver", "max_form_parts")
- flask_app.config["MAX_FORM_MEMORY_SIZE"] = conf.getint("webserver", "max_form_memory_size")
-
- webserver_config = conf.get_mandatory_value("webserver", "config_file")
- # Enable customizations in webserver_config.py to be applied via Flask.current_app.
- with flask_app.app_context():
- flask_app.config.from_pyfile(webserver_config, silent=True)
-
- flask_app.config["TESTING"] = testing
- flask_app.config["SQLALCHEMY_DATABASE_URI"] = conf.get("database", "SQL_ALCHEMY_CONN")
-
- instance_name = conf.get(section="webserver", key="instance_name", fallback="Airflow")
- require_confirmation_dag_change = conf.getboolean(
- section="webserver", key="require_confirmation_dag_change", fallback=False
- )
- instance_name_has_markup = conf.getboolean(
- section="webserver", key="instance_name_has_markup", fallback=False
- )
- if instance_name_has_markup:
- instance_name = Markup(instance_name).striptags()
-
- flask_app.config["APP_NAME"] = instance_name
- flask_app.config["REQUIRE_CONFIRMATION_DAG_CHANGE"] = require_confirmation_dag_change
-
- url = make_url(flask_app.config["SQLALCHEMY_DATABASE_URI"])
- if url.drivername == "sqlite" and url.database and not isabs(url.database):
- raise AirflowConfigException(
- f'Cannot use relative path: `{conf.get("database", "SQL_ALCHEMY_CONN")}` to connect to sqlite. '
- "Please use absolute path such as `sqlite:////tmp/airflow.db`."
- )
-
- flask_app.config["SQLALCHEMY_TRACK_MODIFICATIONS"] = False
-
- flask_app.config["SESSION_COOKIE_HTTPONLY"] = True
- flask_app.config["SESSION_COOKIE_SECURE"] = conf.getboolean("webserver", "COOKIE_SECURE")
-
- # Note: Ensure "Lax" is the default if config not specified
- flask_app.config["SESSION_COOKIE_SAMESITE"] = (
- conf.get("webserver", "COOKIE_SAMESITE", fallback=None) or "Lax"
- )
-
- # Above Flask 2.0.x, default value of SEND_FILE_MAX_AGE_DEFAULT changed 12 hours to None.
- # for static file caching, it needs to set value explicitly.
- flask_app.config["SEND_FILE_MAX_AGE_DEFAULT"] = timedelta(seconds=43200)
-
- if config:
- flask_app.config.from_mapping(config)
-
- if "SQLALCHEMY_ENGINE_OPTIONS" not in flask_app.config:
- flask_app.config["SQLALCHEMY_ENGINE_OPTIONS"] = settings.prepare_engine_args()
-
- # Configure the JSON encoder used by `|tojson` filter from Flask
- flask_app.json_provider_class = AirflowJsonProvider
- flask_app.json = AirflowJsonProvider(flask_app)
-
- csrf.init_app(flask_app)
-
- init_wsgi_middleware(flask_app)
-
- db = SQLA()
- db.session = settings.Session
- db.init_app(flask_app)
-
- init_dagbag(flask_app)
-
- init_api_auth(flask_app)
-
- init_robots(flask_app)
-
- init_cache(flask_app)
-
- init_flash_views(flask_app)
-
- configure_logging()
- configure_manifest_files(flask_app)
-
- import_all_models()
-
- with flask_app.app_context():
- init_appbuilder(flask_app)
-
- init_appbuilder_views(flask_app)
- init_appbuilder_links(flask_app)
- init_plugins(flask_app)
- init_error_handlers(flask_app)
- init_api_connexion(flask_app)
- if conf.getboolean("webserver", "run_internal_api", fallback=False):
- init_api_internal(flask_app)
- init_api_auth_provider(flask_app)
- init_api_error_handlers(flask_app) # needs to be after all api inits to let them add their path first
- init_jinja_globals(flask_app)
- init_xframe_protection(flask_app)
- init_cache_control(flask_app)
- init_airflow_session_interface(flask_app)
- return flask_app
-
-
-def cached_app(config=None, testing=False):
- """Return cached instance of Airflow WWW app."""
- global app
- if not app:
- app = create_app(config=config, testing=testing)
- return app
-
-
-def purge_cached_app():
- """Remove the cached version of the app in global state."""
- global app
- app = None
diff --git a/airflow/www/babel.config.js b/airflow/www/babel.config.js
deleted file mode 100644
index 7f40a9fdf6b..00000000000
--- a/airflow/www/babel.config.js
+++ /dev/null
@@ -1,35 +0,0 @@
-/*!
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you 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.
- */
-
-module.exports = (api) => {
- api.cache(true);
-
- const presets = [
- "@babel/preset-env",
- "@babel/preset-react",
- "@babel/preset-typescript",
- ];
-
- const plugins = ["@babel/plugin-transform-runtime"];
-
- return {
- presets,
- plugins,
- };
-};
diff --git a/airflow/www/constants.py b/airflow/www/constants.py
deleted file mode 100644
index 263caf1576c..00000000000
--- a/airflow/www/constants.py
+++ /dev/null
@@ -1,28 +0,0 @@
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements. See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership. The ASF licenses this file
-# to you 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.
-from __future__ import annotations
-
-from pathlib import Path
-
-from airflow.configuration import conf
-
-WWW = Path(__file__).resolve().parent
-# There is a difference with configuring Swagger in Connexion 2.x and Connexion 3.x
-# Connexion 2: https://connexion.readthedocs.io/en/2.14.2/quickstart.html#the-swagger-ui-console
-# Connexion 3: https://connexion.readthedocs.io/en/stable/swagger_ui.html#configuring-the-swagger-ui
-SWAGGER_ENABLED = conf.getboolean("webserver", "enable_swagger_ui", fallback=True)
-SWAGGER_BUNDLE = WWW.joinpath("static", "dist", "swagger-ui")
diff --git a/airflow/www/decorators.py b/airflow/www/decorators.py
deleted file mode 100644
index 96a7f32e4c1..00000000000
--- a/airflow/www/decorators.py
+++ /dev/null
@@ -1,200 +0,0 @@
-#
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements. See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership. The ASF licenses this file
-# to you 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.
-from __future__ import annotations
-
-import functools
-import gzip
-import itertools
-import json
-import logging
-from io import BytesIO
-from typing import Callable, TypeVar, cast
-
-import pendulum
-from flask import after_this_request, request
-from pendulum.parsing.exceptions import ParserError
-
-from airflow.api_fastapi.app import get_auth_manager
-from airflow.models import Log
-from airflow.sdk.execution_time import secrets_masker
-from airflow.utils.session import create_session
-
-T = TypeVar("T", bound=Callable)
-
-logger = logging.getLogger(__name__)
-
-
-def _mask_variable_fields(extra_fields):
- """
- Mask the 'val_content' field if 'key_content' is in the mask list.
-
- The variable requests values and args comes in this form:
- {'key': 'key_content', 'val': 'val_content', 'description': 'description_content'}
- """
- result = {}
- keyname = None
- for k, v in extra_fields.items():
- if k == "key":
- keyname = v
- result[k] = v
- elif keyname and (k == "val" or k == "value"):
- x = secrets_masker.redact(v, keyname)
- result[k] = x
- keyname = None
- else:
- result[k] = v
- return result
-
-
-def _mask_connection_fields(extra_fields):
- """Mask connection fields."""
- result = {}
- for k, v in extra_fields.items():
- if k == "extra" and v:
- try:
- extra = json.loads(v)
- extra = {k: secrets_masker.redact(v, k) for k, v in extra.items()}
- result[k] = dict(extra)
- except json.JSONDecodeError:
- result[k] = "Encountered non-JSON in `extra` field"
- else:
- result[k] = secrets_masker.redact(v, k)
- return result
-
-
-def action_logging(func: T | None = None, event: str | None = None) -> T | Callable:
- """Log user actions."""
-
- def log_action(f: T) -> T:
- @functools.wraps(f)
- def wrapper(*args, **kwargs):
- __tracebackhide__ = True # Hide from pytest traceback.
-
- with create_session() as session:
- event_name = event or f.__name__
- if not get_auth_manager().is_logged_in():
- user = "anonymous"
- user_display = ""
- else:
- user = get_auth_manager().get_user_name()
- user_display = get_auth_manager().get_user_display_name()
-
- isAPIRequest = request.blueprint == "/api/v1"
- hasJsonBody = "application/json" in request.headers.get("content-type", "") and request.json
-
- fields_skip_logging = {
- "csrf_token",
- "_csrf_token",
- "is_paused",
- "dag_id",
- "task_id",
- "dag_run_id",
- "run_id",
- "logical_date",
- }
- extra_fields = {
- k: secrets_masker.redact(v, k)
- for k, v in itertools.chain(request.values.items(multi=True), request.view_args.items())
- if k not in fields_skip_logging
- }
- if event and event.startswith("variable."):
- extra_fields = _mask_variable_fields(
- request.json if isAPIRequest and hasJsonBody else extra_fields
- )
- elif event and event.startswith("connection."):
- extra_fields = _mask_connection_fields(
- request.json if isAPIRequest and hasJsonBody else extra_fields
- )
- elif hasJsonBody:
- masked_json = {k: secrets_masker.redact(v, k) for k, v in request.json.items()}
- extra_fields = {**extra_fields, **masked_json}
-
- params = {**request.values, **request.view_args}
- if params and "is_paused" in params:
- extra_fields["is_paused"] = params["is_paused"] == "false"
-
- if isAPIRequest:
- if f"{request.origin}/" == request.root_url:
- event_name = f"ui.{event_name}"
- else:
- event_name = f"api.{event_name}"
-
- log = Log(
- event=event_name,
- task_instance=None,
- owner=user,
- owner_display_name=user_display,
- extra=json.dumps(extra_fields),
- task_id=params.get("task_id"),
- dag_id=params.get("dag_id"),
- run_id=params.get("run_id") or params.get("dag_run_id"),
- )
-
- if "logical_date" in request.values:
- logical_date_value = request.values.get("logical_date")
- try:
- log.logical_date = pendulum.parse(logical_date_value, strict=False)
- except ParserError:
- logger.exception(
- "Failed to parse logical_date from the request: %s", logical_date_value
- )
-
- session.add(log)
-
- return f(*args, **kwargs)
-
- return cast(T, wrapper)
-
- if func:
- return log_action(func)
- return log_action
-
-
-def gzipped(f: T) -> T:
- """Make a view compressed."""
-
- @functools.wraps(f)
- def view_func(*args, **kwargs):
- @after_this_request
- def zipper(response):
- accept_encoding = request.headers.get("Accept-Encoding", "")
-
- if "gzip" not in accept_encoding.lower():
- return response
-
- response.direct_passthrough = False
-
- if (
- response.status_code < 200
- or response.status_code >= 300
- or "Content-Encoding" in response.headers
- ):
- return response
- with BytesIO() as gzip_buffer:
- with gzip.GzipFile(mode="wb", fileobj=gzip_buffer) as gzip_file:
- gzip_file.write(response.data)
- response.data = gzip_buffer.getvalue()
- response.headers["Content-Encoding"] = "gzip"
- response.headers["Vary"] = "Accept-Encoding"
- response.headers["Content-Length"] = len(response.data)
-
- return response
-
- return f(*args, **kwargs)
-
- return cast(T, view_func)
diff --git a/airflow/www/extensions/init_appbuilder.py b/airflow/www/extensions/init_appbuilder.py
deleted file mode 100644
index 2238066e6e9..00000000000
--- a/airflow/www/extensions/init_appbuilder.py
+++ /dev/null
@@ -1,685 +0,0 @@
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements. See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership. The ASF licenses this file
-# to you 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.
-
-# mypy: disable-error-code=var-annotated
-from __future__ import annotations
-
-import logging
-from functools import reduce
-from typing import TYPE_CHECKING
-
-from flask import Blueprint, current_app, url_for
-from flask_appbuilder import __version__
-from flask_appbuilder.babel.manager import BabelManager
-from flask_appbuilder.const import (
- LOGMSG_ERR_FAB_ADD_PERMISSION_MENU,
- LOGMSG_ERR_FAB_ADD_PERMISSION_VIEW,
- LOGMSG_ERR_FAB_ADDON_IMPORT,
- LOGMSG_ERR_FAB_ADDON_PROCESS,
- LOGMSG_INF_FAB_ADD_VIEW,
- LOGMSG_INF_FAB_ADDON_ADDED,
- LOGMSG_WAR_FAB_VIEW_EXISTS,
-)
-from flask_appbuilder.filters import TemplateFilters
-from flask_appbuilder.menu import Menu
-from flask_appbuilder.views import IndexView, UtilView
-
-from airflow import settings
-from airflow.api_fastapi.app import create_auth_manager, get_auth_manager
-from airflow.configuration import conf
-from airflow.www.security_manager import AirflowSecurityManagerV2
-
-if TYPE_CHECKING:
- from flask import Flask
- from flask_appbuilder import BaseView
- from flask_appbuilder.security.manager import BaseSecurityManager
- from sqlalchemy.orm import Session
-
-
-# This product contains a modified portion of 'Flask App Builder' developed by Daniel Vaz Gaspar.
-# (https://github.com/dpgaspar/Flask-AppBuilder).
-# Copyright 2013, Daniel Vaz Gaspar
-# This module contains code imported from FlaskAppbuilder, so lets use _its_ logger name
-log = logging.getLogger("flask_appbuilder.base")
-
-
-def dynamic_class_import(class_path):
- """
- Will dynamically import a class from a string path.
-
- :param class_path: string with class path
- :return: class
- """
- # Split first occurrence of path
- try:
- tmp = class_path.split(".")
- module_path = ".".join(tmp[0:-1])
- package = __import__(module_path)
- return reduce(getattr, tmp[1:], package)
- except Exception as e:
- log.exception(e)
- log.error(LOGMSG_ERR_FAB_ADDON_IMPORT, class_path, e)
-
-
-class AirflowAppBuilder:
- """
- This is the base class for all the framework.
-
- This is where you will register all your views
- and create the menu structure.
- Will hold your flask app object, all your views, and security classes.
- Initialize your application like this for SQLAlchemy::
- from flask import Flask
- from flask_appbuilder import SQLA, AppBuilder
- app = Flask(__name__)
- app.config.from_object('config')
- db = SQLA(app)
- appbuilder = AppBuilder(app, db.session)
- When using MongoEngine::
- from flask import Flask
- from flask_appbuilder import AppBuilder
- from flask_appbuilder.security.mongoengine.manager import SecurityManager
- from flask_mongoengine import MongoEngine
- app = Flask(__name__)
- app.config.from_object('config')
- dbmongo = MongoEngine(app)
- appbuilder = AppBuilder(app)
- You can also create everything as an application factory.
- """
-
- baseviews: list[BaseView | Session] = []
- # Flask app
- app = None
- # Database Session
- session = None
- # Security Manager Class
- sm: BaseSecurityManager
- # Babel Manager Class
- bm = None
- # dict with addon name has key and instantiated class has value
- addon_managers: dict
- # temporary list that hold addon_managers config key
- _addon_managers: list
-
- menu = None
- indexview = None
-
- static_folder = None
- static_url_path = None
-
- template_filters = None
-
- def __init__(
- self,
- app=None,
- session: Session | None = None,
- menu=None,
- indexview=None,
- base_template="airflow/main.html",
- static_folder="static/appbuilder",
- static_url_path="/appbuilder",
- ):
- """
- App-builder constructor.
-
- :param app:
- The flask app object
- :param session:
- The SQLAlchemy session object
- :param menu:
- optional, a previous constructed menu
- :param indexview:
- optional, your customized indexview
- :param static_folder:
- optional, your override for the global static folder
- :param static_url_path:
- optional, your override for the global static url path
- """
- from airflow.providers_manager import ProvidersManager
-
- providers_manager = ProvidersManager()
- providers_manager.initialize_providers_configuration()
- self.baseviews = []
- self._addon_managers = []
- self.addon_managers = {}
- self.menu = menu
- self.base_template = base_template
- self.indexview = indexview
- self.static_folder = static_folder
- self.static_url_path = static_url_path
- self.app = app
- self.update_perms = conf.getboolean("fab", "UPDATE_FAB_PERMS")
- self.auth_rate_limited = conf.getboolean("fab", "AUTH_RATE_LIMITED")
- self.auth_rate_limit = conf.get("fab", "AUTH_RATE_LIMIT")
- if app is not None:
- self.init_app(app, session)
-
- def init_app(self, app, session):
- """
- Will initialize the Flask app, supporting the app factory pattern.
-
- :param app:
- :param session: The SQLAlchemy session
- """
- app.config.setdefault("APP_NAME", "F.A.B.")
- app.config.setdefault("APP_THEME", "")
- app.config.setdefault("APP_ICON", "")
- app.config.setdefault("LANGUAGES", {"en": {"flag": "gb", "name": "English"}})
- app.config.setdefault("ADDON_MANAGERS", [])
- app.config.setdefault("RATELIMIT_ENABLED", self.auth_rate_limited)
- app.config.setdefault("FAB_API_MAX_PAGE_SIZE", 100)
- app.config.setdefault("FAB_BASE_TEMPLATE", self.base_template)
- app.config.setdefault("FAB_STATIC_FOLDER", self.static_folder)
- app.config.setdefault("FAB_STATIC_URL_PATH", self.static_url_path)
- app.config.setdefault("AUTH_RATE_LIMITED", self.auth_rate_limited)
- app.config.setdefault("AUTH_RATE_LIMIT", self.auth_rate_limit)
-
- self.app = app
-
- self.base_template = app.config.get("FAB_BASE_TEMPLATE", self.base_template)
- self.static_folder = app.config.get("FAB_STATIC_FOLDER", self.static_folder)
- self.static_url_path = app.config.get("FAB_STATIC_URL_PATH", self.static_url_path)
- _index_view = app.config.get("FAB_INDEX_VIEW", None)
- if _index_view is not None:
- self.indexview = dynamic_class_import(_index_view)
- else:
- self.indexview = self.indexview or IndexView
- _menu = app.config.get("FAB_MENU", None)
- if _menu is not None:
- self.menu = dynamic_class_import(_menu)
- else:
- self.menu = self.menu or Menu()
-
- if self.update_perms: # default is True, if False takes precedence from config
- self.update_perms = app.config.get("FAB_UPDATE_PERMS", True)
-
- self._addon_managers = app.config["ADDON_MANAGERS"]
- self.session = session
- auth_manager = create_auth_manager()
- auth_manager.appbuilder = self
- auth_manager.init()
- # FAB auth manager is used in both the old FAB UI and the new React UI backend by Fastapi.
- # It can behave differently depending on the application. Setting this flag so that it knows this
- # instance is run in FAB.
- auth_manager.is_in_fab = True
- if hasattr(auth_manager, "security_manager"):
- self.sm = auth_manager.security_manager
- else:
- self.sm = AirflowSecurityManagerV2(self)
- self.bm = BabelManager(self)
- self._add_global_static()
- self._add_global_filters()
- app.before_request(self.sm.before_request)
- self._add_admin_views()
- self._add_addon_views()
- if self.app:
- self._add_menu_permissions()
- else:
- self.post_init()
- self._init_extension(app)
- self._swap_url_filter()
-
- def _init_extension(self, app):
- app.appbuilder = self
- if not hasattr(app, "extensions"):
- app.extensions = {}
- app.extensions["appbuilder"] = self
-
- def _swap_url_filter(self):
- """Use our url filtering util function so there is consistency between FAB and Airflow routes."""
- from flask_appbuilder.security import views as fab_sec_views
-
- from airflow.www.views import get_safe_url
-
- fab_sec_views.get_safe_redirect = get_safe_url
-
- def post_init(self):
- for baseview in self.baseviews:
- # instantiate the views and add session
- self._check_and_init(baseview)
- # Register the views has blueprints
- if baseview.__class__.__name__ not in self.get_app.blueprints.keys():
- self.register_blueprint(baseview)
- # Add missing permissions where needed
- self.add_permissions()
-
- @property
- def get_app(self):
- """
- Get current or configured flask app.
-
- :return: Flask App
- """
- if self.app:
- return self.app
- else:
- return current_app
-
- @property
- def get_session(self):
- """
- Get the current sqlalchemy session.
-
- :return: SQLAlchemy Session
- """
- return self.session
-
- @property
- def app_name(self):
- """
- Get the App name.
-
- :return: String with app name
- """
- return self.get_app.config["APP_NAME"]
-
- @property
- def require_confirmation_dag_change(self):
- """
- Get the value of the require_confirmation_dag_change configuration.
-
- The logic is:
- - return True, in page dag.html, when user trigger/pause the dag from UI.
- Once confirmation box will be shown before triggering the dag.
- - Default value is False.
-
- :return: Boolean
- """
- return self.get_app.config["REQUIRE_CONFIRMATION_DAG_CHANGE"]
-
- @property
- def app_theme(self):
- """
- Get the App theme name.
-
- :return: String app theme name
- """
- return self.get_app.config["APP_THEME"]
-
- @property
- def app_icon(self):
- """
- Get the App icon location.
-
- :return: String with relative app icon location
- """
- return self.get_app.config["APP_ICON"]
-
- @property
- def languages(self):
- return self.get_app.config["LANGUAGES"]
-
- @property
- def version(self):
- """
- Get the current F.A.B. version.
-
- :return: String with the current F.A.B. version
- """
- return __version__
-
- def _add_global_filters(self):
- self.template_filters = TemplateFilters(self.get_app, self.sm)
-
- def _add_global_static(self):
- bp = Blueprint(
- "appbuilder",
- "flask_appbuilder.base",
- url_prefix="/static",
- template_folder="templates",
- static_folder=self.static_folder,
- static_url_path=self.static_url_path,
- )
- self.get_app.register_blueprint(bp)
-
- def _add_admin_views(self):
- """Register indexview, utilview (back function), babel views and Security views."""
- self.indexview = self._check_and_init(self.indexview)
- self.add_view_no_menu(self.indexview)
- self.add_view_no_menu(UtilView())
- self.bm.register_views()
-
- try:
- get_auth_manager().register_views()
- except AttributeError:
- # TODO: remove when min airflow version >= 3
- self.sm.register_views()
-
- def _add_addon_views(self):
- """Register declared addons."""
- for addon in self._addon_managers:
- addon_class = dynamic_class_import(addon)
- if addon_class:
- # Instantiate manager with appbuilder (self)
- addon_class = addon_class(self)
- try:
- addon_class.pre_process()
- addon_class.register_views()
- addon_class.post_process()
- self.addon_managers[addon] = addon_class
- log.info(LOGMSG_INF_FAB_ADDON_ADDED, addon)
- except Exception as e:
- log.exception(e)
- log.error(LOGMSG_ERR_FAB_ADDON_PROCESS, addon, e)
-
- def _check_and_init(self, baseview):
- if hasattr(baseview, "datamodel"):
- baseview.datamodel.session = self.session
- if callable(baseview):
- baseview = baseview()
- return baseview
-
- def add_view(
- self,
- baseview,
- name,
- href="",
- icon="",
- label="",
- category="",
- category_icon="",
- category_label="",
- menu_cond=None,
- ):
- """
- Add your views associated with menus using this method.
-
- :param baseview:
- A BaseView type class instantiated or not.
- This method will instantiate the class for you if needed.
- :param name:
- The string name that identifies the menu.
- :param href:
- Override the generated href for the menu.
- You can use an url string or an endpoint name
- if non provided default_view from view will be set as href.
- :param icon:
- Font-Awesome icon name, optional.
- :param label:
- The label that will be displayed on the menu,
- if absent param name will be used
- :param category:
- The menu category where the menu will be included,
- if non provided the view will be accessible as a top menu.
- :param category_icon:
- Font-Awesome icon name for the category, optional.
- :param category_label:
- The label that will be displayed on the menu,
- if absent param name will be used
- :param menu_cond:
- If a callable, :code:`menu_cond` will be invoked when
- constructing the menu items. If it returns :code:`True`,
- then this link will be a part of the menu. Otherwise, it
- will not be included in the menu items. Defaults to
- :code:`None`, meaning the item will always be present.
-
- Examples::
-
- appbuilder = AppBuilder(app, db)
- # Register a view, rendering a top menu without icon.
- appbuilder.add_view(MyModelView(), "My View")
- # or not instantiated
- appbuilder.add_view(MyModelView, "My View")
- # Register a view, a submenu "Other View" from "Other" with a phone icon.
- appbuilder.add_view(MyOtherModelView, "Other View", icon="fa-phone", category="Others")
- # Register a view, with category icon and translation.
- appbuilder.add_view(
- YetOtherModelView,
- "Other View",
- icon="fa-phone",
- label=_("Other View"),
- category="Others",
- category_icon="fa-envelop",
- category_label=_("Other View"),
- )
- # Register a view whose menu item will be conditionally displayed
- appbuilder.add_view(
- YourFeatureView,
- "Your Feature",
- icon="fa-feature",
- label=_("Your Feature"),
- menu_cond=lambda: is_feature_enabled("your-feature"),
- )
- # Add a link
- appbuilder.add_link("google", href="www.google.com", icon="fa-google-plus")
- """
- baseview = self._check_and_init(baseview)
- log.info(LOGMSG_INF_FAB_ADD_VIEW, baseview.__class__.__name__, name)
-
- if not self._view_exists(baseview):
- baseview.appbuilder = self
- self.baseviews.append(baseview)
- self._process_inner_views()
- if self.app:
- self.register_blueprint(baseview)
- self._add_permission(baseview)
- self.add_limits(baseview)
- self.add_link(
- name=name,
- href=href,
- icon=icon,
- label=label,
- category=category,
- category_icon=category_icon,
- category_label=category_label,
- baseview=baseview,
- cond=menu_cond,
- )
- return baseview
-
- def add_link(
- self,
- name,
- href,
- icon="",
- label="",
- category="",
- category_icon="",
- category_label="",
- baseview=None,
- cond=None,
- ):
- """
- Add your own links to menu using this method.
-
- :param name:
- The string name that identifies the menu.
- :param href:
- Override the generated href for the menu.
- You can use an url string or an endpoint name
- :param icon:
- Font-Awesome icon name, optional.
- :param label:
- The label that will be displayed on the menu,
- if absent param name will be used
- :param category:
- The menu category where the menu will be included,
- if non provided the view will be accessible as a top menu.
- :param category_icon:
- Font-Awesome icon name for the category, optional.
- :param category_label:
- The label that will be displayed on the menu,
- if absent param name will be used
- :param baseview:
- A BaseView type class instantiated.
- :param cond:
- If a callable, :code:`cond` will be invoked when
- constructing the menu items. If it returns :code:`True`,
- then this link will be a part of the menu. Otherwise, it
- will not be included in the menu items. Defaults to
- :code:`None`, meaning the item will always be present.
- """
- self.menu.add_link(
- name=name,
- href=href,
- icon=icon,
- label=label,
- category=category,
- category_icon=category_icon,
- category_label=category_label,
- baseview=baseview,
- cond=cond,
- )
- if self.app:
- self._add_permissions_menu(name)
- if category:
- self._add_permissions_menu(category)
-
- def add_separator(self, category, cond=None):
- """
- Add a separator to the menu, you will sequentially create the menu.
-
- :param category:
- The menu category where the separator will be included.
- :param cond:
- If a callable, :code:`cond` will be invoked when
- constructing the menu items. If it returns :code:`True`,
- then this separator will be a part of the menu. Otherwise,
- it will not be included in the menu items. Defaults to
- :code:`None`, meaning the separator will always be present.
- """
- self.menu.add_separator(category, cond=cond)
-
- def add_view_no_menu(self, baseview, endpoint=None, static_folder=None):
- """
- Add your views without creating a menu.
-
- :param baseview: A BaseView type class instantiated.
- """
- baseview = self._check_and_init(baseview)
- log.info(LOGMSG_INF_FAB_ADD_VIEW, baseview.__class__.__name__, "")
-
- if not self._view_exists(baseview):
- baseview.appbuilder = self
- self.baseviews.append(baseview)
- self._process_inner_views()
- if self.app:
- self.register_blueprint(baseview, endpoint=endpoint, static_folder=static_folder)
- self._add_permission(baseview)
- else:
- log.warning(LOGMSG_WAR_FAB_VIEW_EXISTS, baseview.__class__.__name__)
- return baseview
-
- def security_cleanup(self):
- """
- Clean up security.
-
- This method is useful if you have changed the name of your menus or
- classes. Changing them leaves behind permissions that are not associated
- with anything. You can use it always or just sometimes to perform a
- security cleanup.
-
- .. warning::
-
- This deletes any permission that is no longer part of any registered
- view or menu. Only invoke AFTER YOU HAVE REGISTERED ALL VIEWS.
- """
- if not hasattr(self.sm, "security_cleanup"):
- raise NotImplementedError("The auth manager used does not support security_cleanup method.")
- self.sm.security_cleanup(self.baseviews, self.menu)
-
- def security_converge(self, dry=False) -> dict:
- """
- Migrates all permissions to the new names on all the Roles.
-
- This method is useful when you use:
-
- - ``class_permission_name``
- - ``previous_class_permission_name``
- - ``method_permission_name``
- - ``previous_method_permission_name``
-
- :param dry: If True will not change DB
- :return: Dict with all computed necessary operations
- """
- return self.sm.security_converge(self.baseviews, self.menu, dry)
-
- def get_url_for_login_with(self, next_url: str | None = None) -> str:
- return get_auth_manager().get_url_login(next_url=next_url)
-
- @property
- def get_url_for_login(self):
- return get_auth_manager().get_url_login()
-
- @property
- def get_url_for_index(self):
- return url_for(f"{self.indexview.endpoint}.{self.indexview.default_view}")
-
- def get_url_for_locale(self, lang):
- return url_for(
- f"{self.bm.locale_view.endpoint}.{self.bm.locale_view.default_view}",
- locale=lang,
- )
-
- def add_limits(self, baseview) -> None:
- if hasattr(baseview, "limits"):
- self.sm.add_limit_view(baseview)
-
- def add_permissions(self, update_perms=False):
- if self.update_perms or update_perms:
- for baseview in self.baseviews:
- self._add_permission(baseview, update_perms=update_perms)
- self._add_menu_permissions(update_perms=update_perms)
-
- def _add_permission(self, baseview, update_perms=False):
- if self.update_perms or update_perms:
- try:
- self.sm.add_permissions_view(baseview.base_permissions, baseview.class_permission_name)
- except Exception as e:
- log.exception(e)
- log.error(LOGMSG_ERR_FAB_ADD_PERMISSION_VIEW, e)
-
- def _add_permissions_menu(self, name, update_perms=False):
- if self.update_perms or update_perms:
- try:
- self.sm.add_permissions_menu(name)
- except Exception as e:
- log.exception(e)
- log.error(LOGMSG_ERR_FAB_ADD_PERMISSION_MENU, e)
-
- def _add_menu_permissions(self, update_perms=False):
- if self.update_perms or update_perms:
- for category in self.menu.get_list():
- self._add_permissions_menu(category.name, update_perms=update_perms)
- for item in category.childs:
- # don't add permission for menu separator
- if item.name != "-":
- self._add_permissions_menu(item.name, update_perms=update_perms)
-
- def register_blueprint(self, baseview, endpoint=None, static_folder=None):
- self.get_app.register_blueprint(
- baseview.create_blueprint(self, endpoint=endpoint, static_folder=static_folder)
- )
-
- def _view_exists(self, view):
- return any(baseview.__class__ == view.__class__ for baseview in self.baseviews)
-
- def _process_inner_views(self):
- for view in self.baseviews:
- for inner_class in view.get_uninit_inner_views():
- for v in self.baseviews:
- if isinstance(v, inner_class) and v not in view.get_init_inner_views():
- view.get_init_inner_views().append(v)
-
-
-def init_appbuilder(app: Flask) -> AirflowAppBuilder:
- """Init `Flask App Builder <https://flask-appbuilder.readthedocs.io/en/latest/>`__."""
- return AirflowAppBuilder(
- app=app,
- session=settings.Session,
- base_template="airflow/main.html",
- )
diff --git a/airflow/www/extensions/init_views.py b/airflow/www/extensions/init_views.py
deleted file mode 100644
index a45e008d773..00000000000
--- a/airflow/www/extensions/init_views.py
+++ /dev/null
@@ -1,336 +0,0 @@
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements. See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership. The ASF licenses this file
-# to you 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.
-from __future__ import annotations
-
-import logging
-from functools import cached_property
-from pathlib import Path
-from typing import TYPE_CHECKING
-
-from connexion import FlaskApi, ProblemException, Resolver
-from connexion.decorators.validation import RequestBodyValidator
-from connexion.exceptions import BadRequestProblem
-from flask import request
-from werkzeug import Request
-
-from airflow.api_connexion.exceptions import common_error_handler
-from airflow.api_fastapi.app import get_auth_manager
-from airflow.configuration import conf
-from airflow.security import permissions
-from airflow.utils.yaml import safe_load
-from airflow.www.constants import SWAGGER_BUNDLE, SWAGGER_ENABLED
-
-if TYPE_CHECKING:
- from flask import Flask
-
-log = logging.getLogger(__name__)
-
-# airflow/www/extensions/init_views.py => airflow/
-ROOT_APP_DIR = Path(__file__).parents[2].resolve()
-
-
-def init_flash_views(app):
- """Init main app view - redirect to FAB."""
- from airflow.www.blueprints import routes
-
- app.register_blueprint(routes)
-
-
-def init_appbuilder_views(app):
- """Initialize Web UI views."""
- from airflow.models import import_all_models
-
- import_all_models()
-
- from airflow.www import views
-
- appbuilder = app.appbuilder
-
- # Remove the session from scoped_session registry to avoid
- # reusing a session with a disconnected connection
- appbuilder.session.remove()
- appbuilder.add_view_no_menu(views.AutocompleteView())
- appbuilder.add_view_no_menu(views.Airflow())
- appbuilder.add_view(
- views.DagRunModelView,
- permissions.RESOURCE_DAG_RUN,
- category=permissions.RESOURCE_BROWSE_MENU,
- category_icon="fa-globe",
- )
- appbuilder.add_view(
- views.JobModelView, permissions.RESOURCE_JOB, category=permissions.RESOURCE_BROWSE_MENU
- )
- appbuilder.add_view(
- views.LogModelView, permissions.RESOURCE_AUDIT_LOG, category=permissions.RESOURCE_BROWSE_MENU
- )
- appbuilder.add_view(
- views.VariableModelView, permissions.RESOURCE_VARIABLE, category=permissions.RESOURCE_ADMIN_MENU
- )
- appbuilder.add_view(
- views.TaskInstanceModelView,
- permissions.RESOURCE_TASK_INSTANCE,
- category=permissions.RESOURCE_BROWSE_MENU,
- )
- appbuilder.add_view(
- views.TaskRescheduleModelView,
- permissions.RESOURCE_TASK_RESCHEDULE,
- category=permissions.RESOURCE_BROWSE_MENU,
- )
- appbuilder.add_view(
- views.TriggerModelView,
- permissions.RESOURCE_TRIGGER,
- category=permissions.RESOURCE_BROWSE_MENU,
- )
- appbuilder.add_view(
- views.ConfigurationView,
- permissions.RESOURCE_CONFIG,
- category=permissions.RESOURCE_ADMIN_MENU,
- category_icon="fa-user",
- )
- appbuilder.add_view(
- views.ConnectionModelView, permissions.RESOURCE_CONNECTION, category=permissions.RESOURCE_ADMIN_MENU
- )
- appbuilder.add_view(
- views.PluginView, permissions.RESOURCE_PLUGIN, category=permissions.RESOURCE_ADMIN_MENU
- )
- appbuilder.add_view(
- views.ProviderView, permissions.RESOURCE_PROVIDER, category=permissions.RESOURCE_ADMIN_MENU
- )
- appbuilder.add_view(
- views.PoolModelView, permissions.RESOURCE_POOL, category=permissions.RESOURCE_ADMIN_MENU
- )
- appbuilder.add_view(
- views.XComModelView, permissions.RESOURCE_XCOM, category=permissions.RESOURCE_ADMIN_MENU
- )
- appbuilder.add_view(
- views.DagDependenciesView,
- permissions.RESOURCE_DAG_DEPENDENCIES,
- category=permissions.RESOURCE_BROWSE_MENU,
- )
- # add_view_no_menu to change item position.
- # I added link in extensions.init_appbuilder_links.init_appbuilder_links
- appbuilder.add_view_no_menu(views.RedocView)
- # Development views
- appbuilder.add_view_no_menu(views.DevView)
- appbuilder.add_view_no_menu(views.DocsView)
-
-
-def init_plugins(app):
- """Integrate Flask and FAB with plugins."""
- from airflow import plugins_manager
-
- plugins_manager.initialize_flask_plugins()
-
- appbuilder = app.appbuilder
-
- for view in plugins_manager.flask_appbuilder_views:
- name = view.get("name")
- if name:
- filtered_view_kwargs = {k: v for k, v in view.items() if k not in ["view"]}
- log.debug("Adding view %s with menu", name)
- baseview = view.get("view")
- if baseview:
- appbuilder.add_view(baseview, **filtered_view_kwargs)
- else:
- log.error("'view' key is missing for the named view: %s", name)
- else:
- # if 'name' key is missing, intent is to add view without menu
- log.debug("Adding view %s without menu", str(type(view["view"])))
- appbuilder.add_view_no_menu(view["view"])
-
- for menu_link in sorted(
- plugins_manager.flask_appbuilder_menu_links, key=lambda x: (x.get("category", ""), x["name"])
- ):
- log.debug("Adding menu link %s to %s", menu_link["name"], menu_link["href"])
- appbuilder.add_link(**menu_link)
-
- for blue_print in plugins_manager.flask_blueprints:
- log.debug("Adding blueprint %s:%s", blue_print["name"], blue_print["blueprint"].import_name)
- app.register_blueprint(blue_print["blueprint"])
-
-
-def init_error_handlers(app: Flask):
- """Add custom errors handlers."""
- from airflow.www import views
-
- app.register_error_handler(500, views.show_traceback)
- app.register_error_handler(404, views.not_found)
-
-
-def set_cors_headers_on_response(response):
- """Add response headers."""
- allow_headers = conf.get("api", "access_control_allow_headers")
- allow_methods = conf.get("api", "access_control_allow_methods")
- allow_origins = conf.get("api", "access_control_allow_origins")
- if allow_headers:
- response.headers["Access-Control-Allow-Headers"] = allow_headers
- if allow_methods:
- response.headers["Access-Control-Allow-Methods"] = allow_methods
- if allow_origins == "*":
- response.headers["Access-Control-Allow-Origin"] = "*"
- elif allow_origins:
- allowed_origins = allow_origins.split(" ")
- origin = request.environ.get("HTTP_ORIGIN", allowed_origins[0])
- if origin in allowed_origins:
- response.headers["Access-Control-Allow-Origin"] = origin
- return response
-
-
-def init_data_form_parameters():
- """
- Initialize custom values for data form parameters.
-
- This is a workaround for Flask versions prior to 3.1.0.
- In order to allow users customizing form data parameters, we need these two fields to be configurable.
- Starting from Flask 3.1.0 these two parameters can be configured through Flask config, but unfortunately,
- current version of flask supported in Airflow is way older. That's why this workaround was introduced.
- See https://flask.palletsprojects.com/en/stable/api/#flask.Request.max_form_memory_size
- # TODO: remove it when Flask upgraded to version 3.1.0 or higher.
- """
- Request.max_form_parts = conf.getint("webserver", "max_form_parts")
- Request.max_form_memory_size = conf.getint("webserver", "max_form_memory_size")
-
-
-class _LazyResolution:
- """
- OpenAPI endpoint that lazily resolves the function on first use.
-
- This is a stand-in replacement for ``connexion.Resolution`` that implements
- its public attributes ``function`` and ``operation_id``, but the function
- is only resolved when it is first accessed.
- """
-
- def __init__(self, resolve_func, operation_id):
- self._resolve_func = resolve_func
- self.operation_id = operation_id
-
- @cached_property
- def function(self):
- return self._resolve_func(self.operation_id)
-
-
-class _LazyResolver(Resolver):
- """
- OpenAPI endpoint resolver that loads lazily on first use.
-
- This re-implements ``connexion.Resolver.resolve()`` to not eagerly resolve
- the endpoint function (and thus avoid importing it in the process), but only
- return a placeholder that will be actually resolved when the contained
- function is accessed.
- """
-
- def resolve(self, operation):
- operation_id = self.resolve_operation_id(operation)
- return _LazyResolution(self.resolve_function_from_operation_id, operation_id)
-
-
-class _CustomErrorRequestBodyValidator(RequestBodyValidator):
- """
- Custom request body validator that overrides error messages.
-
- By default, Connextion emits a very generic *None is not of type 'object'*
- error when receiving an empty request body (with the view specifying the
- body as non-nullable). We overrides it to provide a more useful message.
- """
-
- def validate_schema(self, data, url):
- if not self.is_null_value_valid and data is None:
- raise BadRequestProblem(detail="Request body must not be empty")
- return super().validate_schema(data, url)
-
-
-base_paths: list[str] = [] # contains the list of base paths that have api endpoints
-
-
-def init_api_error_handlers(app: Flask) -> None:
- """Add error handlers for 404 and 405 errors for existing API paths."""
- from airflow.www import views
-
- @app.errorhandler(404)
- def _handle_api_not_found(ex):
- if any([request.path.startswith(p) for p in base_paths]):
- # 404 errors are never handled on the blueprint level
- # unless raised from a view func so actual 404 errors,
- # i.e. "no route for it" defined, need to be handled
- # here on the application level
- return common_error_handler(ex)
- else:
- return views.not_found(ex)
-
- @app.errorhandler(405)
- def _handle_method_not_allowed(ex):
- if any([request.path.startswith(p) for p in base_paths]):
- return common_error_handler(ex)
- else:
- return views.method_not_allowed(ex)
-
- app.register_error_handler(ProblemException, common_error_handler)
-
-
-def init_api_connexion(app: Flask) -> None:
- """Initialize Stable API."""
- base_path = "/api/v1"
- base_paths.append(base_path)
-
- with ROOT_APP_DIR.joinpath("api_connexion", "openapi", "v1.yaml").open() as f:
- specification = safe_load(f)
- api_bp = FlaskApi(
- specification=specification,
- resolver=_LazyResolver(),
- base_path=base_path,
- options={"swagger_ui": SWAGGER_ENABLED, "swagger_path": SWAGGER_BUNDLE.__fspath__()},
- strict_validation=True,
- validate_responses=True,
- validator_map={"body": _CustomErrorRequestBodyValidator},
- ).blueprint
- api_bp.before_app_request(init_data_form_parameters)
- api_bp.after_request(set_cors_headers_on_response)
-
- app.register_blueprint(api_bp)
- app.extensions["csrf"].exempt(api_bp)
-
-
-def init_api_internal(app: Flask, standalone_api: bool = False) -> None:
- """Initialize Internal API."""
- if not standalone_api and not conf.getboolean("webserver", "run_internal_api", fallback=False):
- return
-
- base_paths.append("/internal_api/v1")
- with ROOT_APP_DIR.joinpath("api_internal", "openapi", "internal_api_v1.yaml").open() as f:
- specification = safe_load(f)
- api_bp = FlaskApi(
- specification=specification,
- base_path="/internal_api/v1",
- options={"swagger_ui": SWAGGER_ENABLED, "swagger_path": SWAGGER_BUNDLE.__fspath__()},
- strict_validation=True,
- validate_responses=True,
- ).blueprint
- api_bp.after_request(set_cors_headers_on_response)
-
- app.register_blueprint(api_bp)
- app.after_request_funcs.setdefault(api_bp.name, []).append(set_cors_headers_on_response)
- app.extensions["csrf"].exempt(api_bp)
-
-
-def init_api_auth_provider(app):
- """Initialize the API offered by the auth manager."""
- auth_mgr = get_auth_manager()
- blueprint = auth_mgr.get_api_endpoints()
- if blueprint:
- base_paths.append(blueprint.url_prefix)
- app.register_blueprint(blueprint)
- app.extensions["csrf"].exempt(blueprint)
diff --git a/airflow/www/forms.py b/airflow/www/forms.py
deleted file mode 100644
index 7028e2026e4..00000000000
--- a/airflow/www/forms.py
+++ /dev/null
@@ -1,231 +0,0 @@
-#
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements. See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership. The ASF licenses this file
-# to you 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.
-from __future__ import annotations
-
-import datetime
-import json
-import operator
-from collections.abc import Iterator
-from functools import cache
-
-import pendulum
-from flask_appbuilder.fieldwidgets import (
- BS3PasswordFieldWidget,
- BS3TextAreaFieldWidget,
- BS3TextFieldWidget,
- Select2Widget,
-)
-from flask_appbuilder.forms import DynamicForm
-from flask_babel import lazy_gettext
-from flask_wtf import FlaskForm
-from wtforms import widgets
-from wtforms.fields import Field, IntegerField, PasswordField, SelectField, StringField, TextAreaField
-from wtforms.validators import InputRequired, Optional
-
-from airflow.configuration import conf
-from airflow.providers_manager import ProvidersManager
-from airflow.utils.types import DagRunType
-from airflow.www.validators import ReadOnly, ValidConnID
-from airflow.www.widgets import (
- AirflowDateTimePickerROWidget,
- AirflowDateTimePickerWidget,
- BS3TextAreaROWidget,
- BS3TextFieldROWidget,
-)
-
-
-class DateTimeWithTimezoneField(Field):
- """A text field which stores a `datetime.datetime` matching a format."""
-
- widget = widgets.TextInput()
-
- def __init__(self, label=None, validators=None, datetime_format="%Y-%m-%d %H:%M:%S%Z", **kwargs):
- super().__init__(label, validators, **kwargs)
- self.format = datetime_format
- self.data = None
-
- def _value(self):
- if self.raw_data:
- return " ".join(self.raw_data)
- if self.data:
- return self.data.strftime(self.format)
- return ""
-
- def process_formdata(self, valuelist):
- if not valuelist:
- return
- date_str = " ".join(valuelist)
- try:
- # Check if the datetime string is in the format without timezone, if so convert it to the
- # default timezone
- if len(date_str) == 19:
- parsed_datetime = datetime.datetime.strptime(date_str, "%Y-%m-%d %H:%M:%S")
- default_timezone = self._get_default_timezone()
- self.data = default_timezone.convert(parsed_datetime)
- else:
- self.data = pendulum.parse(date_str)
- except ValueError:
- self.data = None
- raise ValueError(self.gettext("Not a valid datetime value"))
-
- def _get_default_timezone(self):
- current_timezone = conf.get("core", "default_timezone")
- if current_timezone == "system":
- default_timezone = pendulum.local_timezone()
- else:
- default_timezone = pendulum.timezone(current_timezone)
- return default_timezone
-
-
-class DateTimeForm(FlaskForm):
- """Date filter form needed for task views."""
-
- logical_date = DateTimeWithTimezoneField("Logical date", widget=AirflowDateTimePickerWidget())
-
-
-class DagRunEditForm(DynamicForm):
- """
- Form for editing DAG Run.
-
- Only note field is editable, so everything else is read-only here.
- """
-
- dag_id = StringField(lazy_gettext("Dag Id"), validators=[ReadOnly()], widget=BS3TextFieldROWidget())
- start_date = DateTimeWithTimezoneField(
- lazy_gettext("Start Date"), validators=[ReadOnly()], widget=AirflowDateTimePickerROWidget()
- )
- end_date = DateTimeWithTimezoneField(
- lazy_gettext("End Date"), validators=[ReadOnly()], widget=AirflowDateTimePickerROWidget()
- )
- run_id = StringField(lazy_gettext("Run Id"), validators=[ReadOnly()], widget=BS3TextFieldROWidget())
- state = StringField(lazy_gettext("State"), validators=[ReadOnly()], widget=BS3TextFieldROWidget())
- logical_date = DateTimeWithTimezoneField(
- lazy_gettext("Logical Date"),
- validators=[ReadOnly()],
- widget=AirflowDateTimePickerROWidget(),
- )
- conf = TextAreaField(lazy_gettext("Conf"), validators=[ReadOnly()], widget=BS3TextAreaROWidget())
- note = TextAreaField(lazy_gettext("User Note"), widget=BS3TextAreaFieldWidget())
-
- def populate_obj(self, item):
- """Populate the attributes of the passed obj with data from the form's not-read-only fields."""
- for name, field in self._fields.items():
- if not field.flags.readonly:
- field.populate_obj(item, name)
- item.run_type = DagRunType.from_run_id(item.run_id)
- if item.conf:
- item.conf = json.loads(item.conf)
-
-
-class TaskInstanceEditForm(DynamicForm):
- """
- Form for editing TaskInstance.
-
- Only note and state fields are editable, so everything else is read-only here.
- """
-
- dag_id = StringField(
- lazy_gettext("Dag Id"), validators=[InputRequired(), ReadOnly()], widget=BS3TextFieldROWidget()
- )
- task_id = StringField(
- lazy_gettext("Task Id"), validators=[InputRequired(), ReadOnly()], widget=BS3TextFieldROWidget()
- )
- start_date = DateTimeWithTimezoneField(
- lazy_gettext("Start Date"), validators=[ReadOnly()], widget=AirflowDateTimePickerROWidget()
- )
- end_date = DateTimeWithTimezoneField(
- lazy_gettext("End Date"), validators=[ReadOnly()], widget=AirflowDateTimePickerROWidget()
- )
- state = SelectField(
- lazy_gettext("State"),
- choices=(
- ("success", "success"),
- ("running", "running"),
- ("failed", "failed"),
- ("up_for_retry", "up_for_retry"),
- ),
- widget=Select2Widget(),
- validators=[InputRequired()],
- )
- logical_date = DateTimeWithTimezoneField(
- lazy_gettext("Logical Date"),
- widget=AirflowDateTimePickerROWidget(),
- validators=[InputRequired(), ReadOnly()],
- )
- note = TextAreaField(lazy_gettext("User Note"), widget=BS3TextAreaFieldWidget())
-
- def populate_obj(self, item):
- """Populate the attributes of the passed obj with data from the form's not-read-only fields."""
- for name, field in self._fields.items():
- if not field.flags.readonly:
- field.populate_obj(item, name)
-
-
-@cache
-def create_connection_form_class() -> type[DynamicForm]:
- """
- Create a form class for editing and adding Connection.
-
- This class is created dynamically because it relies heavily on run-time
- provider discovery, which slows down webserver startup a lot.
- By creating the class at runtime, we can delay loading the providers until
- when the connection form is first used, which may as well be never for a
- short-lived server.
- """
- providers_manager = ProvidersManager()
-
- def _iter_connection_types() -> Iterator[tuple[str, str]]:
- """List available connection types."""
- for connection_type, provider_info in providers_manager.hooks.items():
- if provider_info:
- yield (connection_type, provider_info.hook_name)
-
- class ConnectionForm(DynamicForm):
- def process(self, formdata=None, obj=None, **kwargs):
- super().process(formdata=formdata, obj=obj, **kwargs)
- for field in self._fields.values():
- if isinstance(getattr(field, "data", None), str):
- field.data = field.data.strip()
-
- conn_id = StringField(
- lazy_gettext("Connection Id"),
- validators=[InputRequired(), ValidConnID()],
- widget=BS3TextFieldWidget(),
- )
- conn_type = SelectField(
- lazy_gettext("Connection Type"),
- choices=sorted(_iter_connection_types(), key=operator.itemgetter(1)),
- widget=Select2Widget(),
- validators=[InputRequired()],
- description=(
- "Connection Type missing? Make sure you've installed the "
- "corresponding Airflow Provider Package."
- ),
- )
- description = StringField(lazy_gettext("Description"), widget=BS3TextAreaFieldWidget())
- host = StringField(lazy_gettext("Host"), widget=BS3TextFieldWidget())
- schema = StringField(lazy_gettext("Schema"), widget=BS3TextFieldWidget())
- login = StringField(lazy_gettext("Login"), widget=BS3TextFieldWidget())
- password = PasswordField(lazy_gettext("Password"), widget=BS3PasswordFieldWidget())
- port = IntegerField(lazy_gettext("Port"), validators=[Optional()], widget=BS3TextFieldWidget())
- extra = TextAreaField(lazy_gettext("Extra"), widget=BS3TextAreaFieldWidget())
-
- for key, value in providers_manager.connection_form_widgets.items():
- setattr(ConnectionForm, key, value.field)
-
- return ConnectionForm
diff --git a/airflow/www/gunicorn_config.py b/airflow/www/gunicorn_config.py
deleted file mode 100644
index 3268698bd85..00000000000
--- a/airflow/www/gunicorn_config.py
+++ /dev/null
@@ -1,43 +0,0 @@
-#!/usr/bin/env python
-#
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements. See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership. The ASF licenses this file
-# to you 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.
-from __future__ import annotations
-
-import setproctitle
-
-from airflow import settings
-
-
-def post_worker_init(_):
- """
- Set process title.
-
- This is used by airflow.cli.commands.local_commands.webserver_command to track the status of the worker.
- """
- old_title = setproctitle.getproctitle()
- setproctitle.setproctitle(settings.GUNICORN_WORKER_READY_PREFIX + old_title)
-
-
-def on_starting(server):
- from airflow.providers_manager import ProvidersManager
-
- providers_manager = ProvidersManager()
- # Load providers configuration before forking workers
- providers_manager.initialize_providers_configuration()
- # Load providers before forking workers
- providers_manager.connection_form_widgets
diff --git a/airflow/www/jest-setup.js b/airflow/www/jest-setup.js
deleted file mode 100644
index d12185f7135..00000000000
--- a/airflow/www/jest-setup.js
+++ /dev/null
@@ -1,69 +0,0 @@
-// We need this lint rule for now because these are only dev-dependencies
-/* eslint-disable import/no-extraneous-dependencies */
-/*!
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you 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.
- */
-
-import "@testing-library/jest-dom";
-import axios from "axios";
-import { setLogger } from "react-query";
-import "jest-canvas-mock";
-
-// eslint-disable-next-line import/no-extraneous-dependencies
-import moment from "moment-timezone";
-
-axios.defaults.adapter = require("axios/lib/adapters/http");
-
-axios.interceptors.response.use((res) => res.data || res);
-
-setLogger({
- /* eslint-disable no-console */
- log: console.log,
- warn: console.warn,
- /* eslint-enable no-console */
- // ✅ no more errors on the console
- error: () => {},
-});
-
-// Mock global objects we use across the app
-global.stateColors = {
- deferred: "mediumpurple",
- failed: "red",
- queued: "gray",
- removed: "lightgrey",
- restarting: "violet",
- running: "lime",
- scheduled: "tan",
- skipped: "hotpink",
- success: "green",
- up_for_reschedule: "turquoise",
- up_for_retry: "gold",
- upstream_failed: "orange",
-};
-
-global.defaultDagRunDisplayNumber = 245;
-
-global.filtersOptions = {
- // Must stay in sync with airflow/www/static/js/types/index.ts
- dagStates: ["success", "running", "queued", "failed"],
- runTypes: ["manual", "backfill", "scheduled", "asset_triggered"],
-};
-
-global.moment = moment;
-
-global.autoRefreshInterval = undefined;
diff --git a/airflow/www/jest.config.js b/airflow/www/jest.config.js
deleted file mode 100644
index 333a061268c..00000000000
--- a/airflow/www/jest.config.js
+++ /dev/null
@@ -1,64 +0,0 @@
-/*!
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you 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.
- */
-
-const config = {
- verbose: true,
- transform: {
- "^.+\\.[jt]sx?$": "babel-jest",
- },
- testEnvironment: "jsdom",
- setupFilesAfterEnv: ["./jest-setup.js"],
- moduleDirectories: ["node_modules"],
- moduleNameMapper: {
- // Listing all aliases
- "^src/(.*)$": "<rootDir>/static/js/$1",
- },
- transformIgnorePatterns: [
- `node_modules/(?!${[
- // specify modules that needs to be transformed for jest. (esm modules)
- "ansi_up",
- "axios",
- "bail",
- "ccount",
- "character-entities",
- "comma-separated-tokens",
- "decode-named-character-reference",
- "escape-string-regexp",
- "hast",
- "is-plain-obj",
- "markdown-table",
- "mdast",
- "micromark",
- "property-information",
- "react-markdown",
- "remark-gfm",
- "remark-parse",
- "remark-rehype",
- "space-separated-tokens",
- "trim-lines",
- "trough",
- "unified",
- "unist",
- "vfile",
- "vfile-message",
- ].join("|")})`,
- ],
-};
-
-module.exports = config;
diff --git a/airflow/www/package.json b/airflow/www/package.json
deleted file mode 100644
index 638e7821396..00000000000
--- a/airflow/www/package.json
+++ /dev/null
@@ -1,156 +0,0 @@
-{
- "name": "airflow-www",
- "version": "1.0.0",
- "description": "Apache Airflow is a platform to programmatically author, schedule and monitor workflows.",
- "scripts": {
- "test": "jest",
- "dev": "NODE_ENV=development webpack --watch --progress --devtool eval-cheap-source-map --mode development",
- "prod": "NODE_ENV=production node --max_old_space_size=4096 ./node_modules/webpack/bin/webpack.js --mode production --progress",
- "build": "NODE_ENV=production webpack --progress --mode production",
- "lint": "eslint --ignore-path=.eslintignore --max-warnings=0 --ext .js,.jsx,.ts,.tsx . && tsc",
- "lint:fix": "eslint --fix --ignore-path=.eslintignore --ext .js,.jsx,.ts,.tsx . && tsc",
- "format": "yarn prettier --write .",
- "generate-api-types": "npx openapi-typescript \"../api_connexion/openapi/v1.yaml\" --output static/js/types/api-generated.ts && node alias-rest-types.js static/js/types/api-generated.ts"
- },
- "author": "Apache",
- "license": "Apache-2.0",
- "repository": {
- "type": "git",
- "url": "git+https://github.com/apache/airflow.git"
- },
- "homepage": "https://airflow.apache.org/",
- "keywords": [
- "big",
- "data",
- "workflow",
- "airflow",
- "d3",
- "nerds",
- "database",
- "flask"
- ],
- "browserslist": {
- "production": [
- ">0.2%",
- "not dead",
- "not op_mini all"
- ],
- "development": [
- "last 1 chrome version",
- "last 1 firefox version",
- "last 1 safari version"
- ]
- },
- "devDependencies": {
- "@babel/core": "^7.24.7",
- "@babel/eslint-parser": "^7.24.7",
- "@babel/plugin-transform-runtime": "^7.24.7",
- "@babel/preset-env": "^7.24.7",
- "@babel/preset-react": "^7.24.7",
- "@babel/preset-typescript": "^7.24.7",
- "@testing-library/jest-dom": "^5.16.0",
- "@testing-library/react": "^13.0.0",
- "@types/color": "^3.0.3",
- "@types/json-to-pretty-yaml": "^1.2.1",
- "@types/react": "^18.0.12",
- "@types/react-dom": "^18.0.5",
- "@types/react-syntax-highlighter": "^15.5.6",
- "@types/react-table": "^7.7.12",
- "@typescript-eslint/eslint-plugin": "^5.13.0",
- "@typescript-eslint/parser": "^5.0.0",
- "babel-jest": "^27.3.1",
- "babel-loader": "^9.1.0",
- "clean-webpack-plugin": "^3.0.0",
- "copy-webpack-plugin": "^6.0.3",
- "css-loader": "5.2.7",
- "css-minimizer-webpack-plugin": "^4.0.0",
- "eslint": "^8.6.0",
- "eslint-config-airbnb": "^19.0.4",
- "eslint-config-airbnb-typescript": "^17.0.0",
- "eslint-config-prettier": "^8.6.0",
- "eslint-plugin-html": "^6.0.2",
- "eslint-plugin-import": "^2.27.5",
- "eslint-plugin-jsx-a11y": "^6.5.0",
- "eslint-plugin-node": "^11.1.0",
- "eslint-plugin-promise": "^4.2.1",
- "eslint-plugin-react": "^7.30.0",
- "eslint-plugin-react-hooks": "^4.5.0",
- "eslint-plugin-standard": "^4.0.1",
- "file-loader": "^6.0.0",
- "imports-loader": "^1.1.0",
- "jest": "^27.3.1",
- "jest-canvas-mock": "^2.5.1",
- "mini-css-extract-plugin": "^1.6.2",
- "moment": "^2.29.4",
- "moment-locales-webpack-plugin": "^1.2.0",
- "openapi-typescript": "^5.4.1",
- "prettier": "^2.8.4",
- "style-loader": "^1.2.1",
- "stylelint": "^15.10.1",
- "stylelint-config-prettier": "^9.0.5",
- "stylelint-config-standard": "^20.0.0",
- "terser-webpack-plugin": "<5.0.0",
- "typescript": "^4.6.3",
- "url-loader": "4.1.0",
- "web-worker": "^1.2.0",
- "webpack": "^5.94.0",
- "webpack-cli": "^4.0.0",
- "webpack-license-plugin": "^4.2.1",
- "webpack-manifest-plugin": "^4.0.0"
- },
- "dependencies": {
- "@chakra-ui/anatomy": "^2.2.2",
- "@chakra-ui/react": "2.4.2",
- "@emotion/cache": "^11.9.3",
- "@emotion/react": "^11.9.3",
- "@emotion/styled": "^11",
- "@tanstack/react-table": "^8.13.2",
- "@visx/group": "^2.10.0",
- "@visx/shape": "^2.12.2",
- "ansi_up": "^6.0.2",
- "axios": "^1.7.4",
- "camelcase-keys": "^7.0.0",
- "chakra-react-select": "^4.0.0",
- "codemirror": "^5.59.1",
- "color": "^4.2.3",
- "d3": "^3.4.4",
- "d3-selection": "^3.0.0",
- "d3-shape": "^2.1.0",
- "d3-tip": "^0.9.1",
- "dagre-d3": "^0.6.4",
- "echarts": "^5.4.2",
- "elkjs": "^0.7.1",
- "eonasdan-bootstrap-datetimepicker": "^4.17.47",
- "framer-motion": "^6.0.0",
- "jquery": ">=3.5.0",
- "jquery-ui": "1.13.3",
- "jshint": "^2.13.4",
- "json-to-pretty-yaml": "^1.2.2",
- "lodash": "^4.17.21",
- "moment-timezone": "^0.5.43",
- "react": "^18.0.0",
- "react-dom": "^18.0.0",
- "react-icons": "^5.2.1",
- "react-json-view": "^1.21.3",
- "react-markdown": "^8.0.4",
- "react-query": "^3.39.1",
- "react-router-dom": "^6.3.0",
- "react-syntax-highlighter": "^15.5.0",
- "react-table": "^7.8.0",
- "react-textarea-autosize": "^8.3.4",
- "reactflow": "^11.7.4",
- "redoc": "^2.0.0-rc.72",
- "remark-gfm": "^3.0.1",
- "swagger-ui-dist": "4.1.3",
- "tsconfig-paths": "^3.14.2",
- "type-fest": "^2.17.0",
- "url-search-params-polyfill": "^8.1.0",
- "validator": "^13.9.0"
- },
- "resolutions": {
- "d3-color": "^3.1.0",
- "braces": "^3.0.3",
- "json5": ">=2.2.2",
- "moment-timezone": ">=0.5.35"
- }
-}
diff --git a/airflow/www/static/airflow.gif b/airflow/www/static/airflow.gif
deleted file mode 100644
index 1889b868f70..00000000000
Binary files a/airflow/www/static/airflow.gif and /dev/null differ
diff --git a/airflow/www/static/css/bootstrap-theme.css b/airflow/www/static/css/bootstrap-theme.css
deleted file mode 100644
index 94e1ee58877..00000000000
--- a/airflow/www/static/css/bootstrap-theme.css
+++ /dev/null
@@ -1,6215 +0,0 @@
-/*!
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you 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.
- */
-
-/* stylelint-disable no-descending-specificity, rule-empty-line-before, no-duplicate-selectors */
-
-/*!
- * Bootstrap v3.2
- *
- * Copyright 2014 Twitter, Inc
- * Licensed under the Apache License v2.0
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Designed and built with all the love in the world by @mdo and @fat.
- * BootSwatchr built and provided by @DrewStrickland
- */
-
-/*! normalize.css v3.0.2 | MIT License | git.io/normalize */
-html {
- font-family: sans-serif;
- -ms-text-size-adjust: 100%;
- -webkit-text-size-adjust: 100%;
-}
-html[data-color-scheme="dark"] {
- filter: invert(100%) hue-rotate(180deg) saturate(90%) contrast(85%);
-}
-
-/* Default icons to not display until the data-color-scheme has been set */
-#dark_icon,
-#light_icon {
- display: none;
-}
-html[data-color-scheme="dark"] #dark_icon {
- display: block;
-}
-html[data-color-scheme="dark"] #light_icon {
- display: none;
-}
-html[data-color-scheme="light"] #dark_icon {
- display: none;
-}
-html[data-color-scheme="light"] #light_icon {
- display: block;
-}
-body {
- margin: 0;
-}
-article,
-aside,
-details,
-figcaption,
-figure,
-footer,
-header,
-hgroup,
-main,
-menu,
-nav,
-section,
-summary {
- display: block;
-}
-audio,
-canvas,
-progress,
-video {
- display: inline-block;
- vertical-align: baseline;
-}
-audio:not([controls]) {
- display: none;
- height: 0;
-}
-[hidden],
-template {
- display: none;
-}
-a {
- background-color: transparent;
-}
-a:active,
-a:hover {
- outline: 0;
-}
-abbr[title] {
- border-bottom: 1px dotted;
-}
-b,
-strong {
- font-weight: bold;
-}
-dfn {
- font-style: italic;
-}
-h1 {
- font-size: 2em;
- margin: 0.67em 0;
-}
-mark {
- background: #ff0;
- color: #000;
-}
-small {
- font-size: 80%;
-}
-sub,
-sup {
- font-size: 75%;
- line-height: 0;
- position: relative;
- vertical-align: baseline;
-}
-sup {
- top: -0.5em;
-}
-sub {
- bottom: -0.25em;
-}
-img {
- border: 0;
-}
-svg:not(:root) {
- overflow: hidden;
-}
-figure {
- margin: 1em 40px;
-}
-hr {
- -moz-box-sizing: content-box;
- box-sizing: content-box;
- height: 0;
-}
-pre {
- overflow: auto;
-}
-code,
-kbd,
-pre,
-samp {
- font-family: monospace;
- font-size: 1em;
-}
-button,
-input,
-optgroup,
-select,
-textarea {
- color: inherit;
- font: inherit;
- margin: 0;
-}
-button {
- overflow: visible;
-}
-button,
-select {
- text-transform: none;
-}
-button,
-html input[type="button"],
-input[type="reset"],
-input[type="submit"] {
- -webkit-appearance: button;
- cursor: pointer;
-}
-button[disabled],
-html input[disabled] {
- cursor: default;
-}
-button::-moz-focus-inner,
-input::-moz-focus-inner {
- border: 0;
- padding: 0;
-}
-input {
- line-height: normal;
-}
-input[type="checkbox"],
-input[type="radio"] {
- box-sizing: border-box;
- padding: 0;
-}
-input[type="number"]::-webkit-inner-spin-button,
-input[type="number"]::-webkit-outer-spin-button {
- height: auto;
-}
-input[type="search"] {
- box-sizing: border-box;
- -webkit-appearance: none;
-}
-input[type="search"]::-webkit-search-cancel-button,
-input[type="search"]::-webkit-search-decoration {
- -webkit-appearance: none;
-}
-fieldset {
- border: 1px solid #c0c0c0;
- margin: 0 2px;
- padding: 0.35em 0.625em 0.75em;
-}
-legend {
- border: 0;
- padding: 0;
-}
-textarea {
- overflow: auto;
-}
-optgroup {
- font-weight: bold;
-}
-table {
- border-collapse: collapse;
- border-spacing: 0;
-}
-td,
-th {
- padding: 0;
-}
-
-/*! Source: https://github.com/h5bp/html5-boilerplate/blob/master/src/css/main.css */
-@media print {
- *,
- *::before,
- *::after {
- background: transparent !important;
- color: #000 !important;
- box-shadow: none !important;
- text-shadow: none !important;
- }
- a,
- a:visited {
- text-decoration: underline;
- }
- a[href]::after {
- content: " (" attr(href) ")";
- }
- abbr[title]::after {
- content: " (" attr(title) ")";
- }
- a[href^="#"]::after,
- a[href^="javascript:"]::after {
- content: "";
- }
- pre,
- blockquote {
- page-break-inside: avoid;
- }
- thead {
- display: table-header-group;
- }
- tr,
- img {
- page-break-inside: avoid;
- }
- img {
- max-width: 100% !important;
- }
- p,
- h2,
- h3 {
- orphans: 3;
- widows: 3;
- }
- h2,
- h3 {
- page-break-after: avoid;
- }
- select {
- background: #fff !important;
- }
- .navbar {
- display: none;
- }
- .btn > .caret,
- .dropup > .btn > .caret {
- border-top-color: #cbcbcb !important;
- }
- .label {
- border: 1px solid #51504f;
- }
- .table {
- border-collapse: collapse !important;
- }
- .table td,
- .table th {
- background-color: #fff !important;
- }
- .table-bordered th,
- .table-bordered td {
- border: 1px solid #f0f0f0 !important;
- }
-}
-* {
- -webkit-box-sizing: border-box;
- -moz-box-sizing: border-box;
- box-sizing: border-box;
-}
-*::before,
-*::after {
- -webkit-box-sizing: border-box;
- -moz-box-sizing: border-box;
- box-sizing: border-box;
-}
-html {
- font-size: 12px;
- -webkit-tap-highlight-color: rgba(0, 0, 0, 0);
-}
-body {
- font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
- font-size: 14px;
- line-height: 1.428571429;
- color: #51504f;
-}
-input,
-button,
-select,
-textarea {
- font-family: inherit;
- font-size: inherit;
- line-height: inherit;
-}
-a,
-a code {
- color: #365f84;
- text-decoration: none;
-}
-a:hover,
-a:focus {
- color: #017cee;
- text-decoration: none;
-}
-a:focus {
- outline: thin dotted;
- outline: 5px auto -webkit-focus-ring-color;
- outline-offset: -2px;
-}
-figure {
- margin: 0;
-}
-img {
- vertical-align: middle;
-}
-.img-responsive,
-.thumbnail > img,
-.thumbnail a > img,
-.carousel-inner > .item > img,
-.carousel-inner > .item > a > img {
- display: block;
- max-width: 100%;
- height: auto;
-}
-.img-rounded {
- border-radius: 6px;
-}
-.img-thumbnail {
- padding: 4px;
- line-height: 1.428571429;
- background-color: #fafafa;
- border: 1px solid #ddd;
- border-radius: 4px;
- -webkit-transition: all 0.2s ease-in-out;
- -o-transition: all 0.2s ease-in-out;
- transition: all 0.2s ease-in-out;
- display: inline-block;
- max-width: 100%;
- height: auto;
-}
-.img-circle {
- border-radius: 50%;
-}
-hr {
- margin-top: 15px;
- margin-bottom: 15px;
- border: 0;
- border-top: 2px solid #f0f0f0;
-}
-.sr-only {
- position: absolute;
- width: 1px;
- height: 1px;
- margin: -1px;
- padding: 0;
- overflow: hidden;
- clip: rect(0, 0, 0, 0);
- border: 0;
-}
-.sr-only-focusable:active,
-.sr-only-focusable:focus {
- position: static;
- width: auto;
- height: auto;
- margin: 0;
- overflow: visible;
- clip: auto;
-}
-h1,
-h2,
-h3,
-h4,
-h5,
-h6,
-.h1,
-.h2,
-.h3,
-.h4,
-.h5,
-.h6 {
- font-family: inherit;
- font-weight: 500;
- line-height: 1.1;
- color: inherit;
-}
-h1 small,
-h2 small,
-h3 small,
-h4 small,
-h5 small,
-h6 small,
-.h1 small,
-.h2 small,
-.h3 small,
-.h4 small,
-.h5 small,
-.h6 small,
-h1 .small,
-h2 .small,
-h3 .small,
-h4 .small,
-h5 .small,
-h6 .small,
-.h1 .small,
-.h2 .small,
-.h3 .small,
-.h4 .small,
-.h5 .small,
-.h6 .small {
- font-weight: normal;
- line-height: 1;
- color: #777;
-}
-h1,
-.h1,
-h2,
-.h2,
-h3,
-.h3 {
- margin-top: 20px;
- margin-bottom: 10px;
-}
-h1 small,
-.h1 small,
-h2 small,
-.h2 small,
-h3 small,
-.h3 small,
-h1 .small,
-.h1 .small,
-h2 .small,
-.h2 .small,
-h3 .small,
-.h3 .small {
- font-size: 65%;
-}
-h4,
-.h4,
-h5,
-.h5,
-h6,
-.h6 {
- margin-top: 10px;
- margin-bottom: 10px;
-}
-h4 small,
-.h4 small,
-h5 small,
-.h5 small,
-h6 small,
-.h6 small,
-h4 .small,
-.h4 .small,
-h5 .small,
-.h5 .small,
-h6 .small,
-.h6 .small {
- font-size: 75%;
-}
-h1,
-.h1 {
- font-size: 36px;
-}
-h2,
-.h2 {
- font-size: 30px;
-}
-h3,
-.h3 {
- font-size: 24px;
-}
-h4,
-.h4 {
- font-size: 18px;
-}
-h5,
-.h5 {
- font-size: 14px;
-}
-h6,
-.h6 {
- font-size: 12px;
-}
-p {
- margin: 0 0 10px;
-}
-.lead {
- margin-bottom: 20px;
- font-size: 16px;
- font-weight: 300;
- line-height: 1.4;
-}
-
-@media (min-width: 768px) {
- .lead {
- font-size: 21px;
- }
-}
-small,
-.small {
- font-size: 85%;
-}
-mark,
-.mark {
- background-color: #fcf8e3;
- padding: 0.2em;
-}
-.text-left {
- text-align: left;
-}
-.text-right {
- text-align: right;
-}
-.text-center {
- text-align: center;
-}
-.text-justify {
- text-align: justify;
-}
-.text-nowrap {
- white-space: nowrap;
-}
-.text-lowercase {
- text-transform: lowercase;
-}
-.text-uppercase {
- text-transform: uppercase;
-}
-.text-capitalize {
- text-transform: capitalize;
-}
-.text-muted {
- color: #cbcbcb;
-}
-.text-primary {
- color: #017cee;
-}
-a.text-primary:hover {
- color: #002e33;
-}
-.text-success {
- color: #1b8e49;
-}
-a.text-success:hover {
- color: #366f4c;
-}
-.text-info {
- color: #31708f;
-}
-a.text-info:hover {
- color: #245269;
-}
-.text-warning {
- color: #8a6d3b;
-}
-a.text-warning:hover {
- color: #66512c;
-}
-.text-danger {
- color: #e43921;
-}
-a.text-danger:hover {
- color: #843534;
-}
-.bg-primary {
- color: #fff;
- background-color: #017cee;
-}
-a.bg-primary:hover {
- background-color: #002e33;
-}
-.bg-success {
- background-color: #bfebd1;
-}
-a.bg-success:hover {
- background-color: #c1e2b3;
-}
-.bg-info {
- background-color: #d9edf7;
-}
-a.bg-info:hover {
- background-color: #afd9ee;
-}
-.bg-warning {
- background-color: #fcf8e3;
-}
-a.bg-warning:hover {
- background-color: #f7ecb5;
-}
-.bg-danger {
- background-color: #f2dede;
-}
-a.bg-danger:hover {
- background-color: #e4b9b9;
-}
-.page-header {
- padding-bottom: 9px;
- margin: 40px 0 20px;
- border-bottom: 1px solid #eee;
-}
-ul,
-ol {
- margin-top: 0;
- margin-bottom: 10px;
-}
-ul ul,
-ol ul,
-ul ol,
-ol ol {
- margin-bottom: 0;
-}
-.list-unstyled {
- padding-left: 0;
- list-style: none;
-}
-.list-inline {
- padding-left: 0;
- list-style: none;
- margin-left: -5px;
-}
-.list-inline > li {
- display: inline-block;
- padding-left: 5px;
- padding-right: 5px;
-}
-dl {
- margin-top: 0;
- margin-bottom: 20px;
-}
-dt,
-dd {
- line-height: 1.428571429;
-}
-dt {
- font-weight: bold;
-}
-dd {
- margin-left: 0;
-}
-
-@media (min-width: 768px) {
- .dl-horizontal dt {
- float: left;
- width: 160px;
- clear: left;
- text-align: right;
- overflow: hidden;
- text-overflow: ellipsis;
- white-space: nowrap;
- }
- .dl-horizontal dd {
- margin-left: 180px;
- }
-}
-abbr[title],
-abbr[data-original-title] {
- cursor: help;
- border-bottom: 1px dotted #777;
-}
-.initialism {
- font-size: 90%;
- text-transform: uppercase;
-}
-blockquote {
- padding: 10px 20px;
- margin: 0 0 20px;
- font-size: 17.5px;
- border-left: 5px solid #eee;
-}
-blockquote p:last-child,
-blockquote ul:last-child,
-blockquote ol:last-child {
- margin-bottom: 0;
-}
-blockquote footer,
-blockquote small,
-blockquote .small {
- display: block;
- font-size: 80%;
- line-height: 1.428571429;
- color: #777;
-}
-blockquote footer::before,
-blockquote small::before,
-blockquote .small::before {
- content: "\2014 \00A0";
-}
-.blockquote-reverse,
-blockquote.pull-right {
- padding-right: 15px;
- padding-left: 0;
- border-right: 5px solid #eee;
- border-left: 0;
- text-align: right;
-}
-.blockquote-reverse footer::before,
-blockquote.pull-right footer::before,
-.blockquote-reverse small::before,
-blockquote.pull-right small::before,
-.blockquote-reverse .small::before,
-blockquote.pull-right .small::before {
- content: "";
-}
-.blockquote-reverse footer::after,
-blockquote.pull-right footer::after,
-.blockquote-reverse small::after,
-blockquote.pull-right small::after,
-.blockquote-reverse .small::after,
-blockquote.pull-right .small::after {
- content: "\00A0 \2014";
-}
-address {
- margin-bottom: 20px;
- font-style: normal;
- line-height: 1.428571429;
-}
-code,
-kbd,
-pre,
-samp {
- font-family: Menlo, Monaco, Consolas, "Courier New", monospace;
-}
-code {
- padding: 2px 4px;
- font-size: 90%;
- color: #51504f;
- background-color: #e2e2e2;
- border-radius: 4px;
-}
-kbd {
- padding: 2px 4px;
- font-size: 90%;
- color: #fff;
- background-color: #51504f;
- border-radius: 3px;
- box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.25);
-}
-kbd kbd {
- padding: 0;
- font-size: 100%;
- font-weight: bold;
- box-shadow: none;
-}
-pre {
- display: block;
- padding: 9.5px;
- margin: 0 0 10px;
- font-size: 13px;
- line-height: 1.428571429;
- word-break: break-all;
- word-wrap: break-word;
- color: #51504f;
- background-color: #f0f0f0;
- border: 0;
- border-radius: 4px;
-}
-pre code {
- padding: 0;
- font-size: inherit;
- color: inherit;
- white-space: pre-wrap;
- background-color: transparent;
- border-radius: 0;
-}
-.pre-scrollable {
- max-height: 340px;
- overflow-y: auto;
-}
-.container {
- margin-right: auto;
- margin-left: auto;
- padding-left: 15px;
- padding-right: 15px;
-}
-
-@media (min-width: 768px) {
- .container {
- width: 750px;
- }
-}
-
-@media (min-width: 992px) {
- .container {
- width: 970px;
- }
-}
-
-@media (min-width: 1200px) {
- .container {
- width: 1170px;
- }
-}
-.container-fluid {
- margin-right: auto;
- margin-left: auto;
- padding-left: 15px;
- padding-right: 15px;
-}
-.row {
- margin-left: -15px;
- margin-right: -15px;
-}
-.col-xs-1,
-.col-sm-1,
-.col-md-1,
-.col-lg-1,
-.col-xs-2,
-.col-sm-2,
-.col-md-2,
-.col-lg-2,
-.col-xs-3,
-.col-sm-3,
-.col-md-3,
-.col-lg-3,
-.col-xs-4,
-.col-sm-4,
-.col-md-4,
-.col-lg-4,
-.col-xs-5,
-.col-sm-5,
-.col-md-5,
-.col-lg-5,
-.col-xs-6,
-.col-sm-6,
-.col-md-6,
-.col-lg-6,
-.col-xs-7,
-.col-sm-7,
-.col-md-7,
-.col-lg-7,
-.col-xs-8,
-.col-sm-8,
-.col-md-8,
-.col-lg-8,
-.col-xs-9,
-.col-sm-9,
-.col-md-9,
-.col-lg-9,
-.col-xs-10,
-.col-sm-10,
-.col-md-10,
-.col-lg-10,
-.col-xs-11,
-.col-sm-11,
-.col-md-11,
-.col-lg-11,
-.col-xs-12,
-.col-sm-12,
-.col-md-12,
-.col-lg-12 {
- position: relative;
- min-height: 1px;
- padding-left: 15px;
- padding-right: 15px;
-}
-.col-xs-1,
-.col-xs-2,
-.col-xs-3,
-.col-xs-4,
-.col-xs-5,
-.col-xs-6,
-.col-xs-7,
-.col-xs-8,
-.col-xs-9,
-.col-xs-10,
-.col-xs-11,
-.col-xs-12 {
- float: left;
-}
-.col-xs-12 {
- width: 100%;
-}
-.col-xs-11 {
- width: 91.66666666666666%;
-}
-.col-xs-10 {
- width: 83.33333333333334%;
-}
-.col-xs-9 {
- width: 75%;
-}
-.col-xs-8 {
- width: 66.66666666666666%;
-}
-.col-xs-7 {
- width: 58.333333333333336%;
-}
-.col-xs-6 {
- width: 50%;
-}
-.col-xs-5 {
- width: 41.66666666666667%;
-}
-.col-xs-4 {
- width: 33.33333333333333%;
-}
-.col-xs-3 {
- width: 25%;
-}
-.col-xs-2 {
- width: 16.666666666666664%;
-}
-.col-xs-1 {
- width: 8.333333333333332%;
-}
-.col-xs-pull-12 {
- right: 100%;
-}
-.col-xs-pull-11 {
- right: 91.66666666666666%;
-}
-.col-xs-pull-10 {
- right: 83.33333333333334%;
-}
-.col-xs-pull-9 {
- right: 75%;
-}
-.col-xs-pull-8 {
- right: 66.66666666666666%;
-}
-.col-xs-pull-7 {
- right: 58.333333333333336%;
-}
-.col-xs-pull-6 {
- right: 50%;
-}
-.col-xs-pull-5 {
- right: 41.66666666666667%;
-}
-.col-xs-pull-4 {
- right: 33.33333333333333%;
-}
-.col-xs-pull-3 {
- right: 25%;
-}
-.col-xs-pull-2 {
- right: 16.666666666666664%;
-}
-.col-xs-pull-1 {
- right: 8.333333333333332%;
-}
-.col-xs-pull-0 {
- right: auto;
-}
-.col-xs-push-12 {
- left: 100%;
-}
-.col-xs-push-11 {
- left: 91.66666666666666%;
-}
-.col-xs-push-10 {
- left: 83.33333333333334%;
-}
-.col-xs-push-9 {
- left: 75%;
-}
-.col-xs-push-8 {
- left: 66.66666666666666%;
-}
-.col-xs-push-7 {
- left: 58.333333333333336%;
-}
-.col-xs-push-6 {
- left: 50%;
-}
-.col-xs-push-5 {
- left: 41.66666666666667%;
-}
-.col-xs-push-4 {
- left: 33.33333333333333%;
-}
-.col-xs-push-3 {
- left: 25%;
-}
-.col-xs-push-2 {
- left: 16.666666666666664%;
-}
-.col-xs-push-1 {
- left: 8.333333333333332%;
-}
-.col-xs-push-0 {
- left: auto;
-}
-.col-xs-offset-12 {
- margin-left: 100%;
-}
-.col-xs-offset-11 {
- margin-left: 91.66666666666666%;
-}
-.col-xs-offset-10 {
- margin-left: 83.33333333333334%;
-}
-.col-xs-offset-9 {
- margin-left: 75%;
-}
-.col-xs-offset-8 {
- margin-left: 66.66666666666666%;
-}
-.col-xs-offset-7 {
- margin-left: 58.333333333333336%;
-}
-.col-xs-offset-6 {
- margin-left: 50%;
-}
-.col-xs-offset-5 {
- margin-left: 41.66666666666667%;
-}
-.col-xs-offset-4 {
- margin-left: 33.33333333333333%;
-}
-.col-xs-offset-3 {
- margin-left: 25%;
-}
-.col-xs-offset-2 {
- margin-left: 16.666666666666664%;
-}
-.col-xs-offset-1 {
- margin-left: 8.333333333333332%;
-}
-.col-xs-offset-0 {
- margin-left: 0%;
-}
-
-@media (min-width: 768px) {
- .col-sm-1,
- .col-sm-2,
- .col-sm-3,
- .col-sm-4,
- .col-sm-5,
- .col-sm-6,
- .col-sm-7,
- .col-sm-8,
- .col-sm-9,
- .col-sm-10,
- .col-sm-11,
- .col-sm-12 {
- float: left;
- }
- .col-sm-12 {
- width: 100%;
- }
- .col-sm-11 {
- width: 91.66666666666666%;
- }
- .col-sm-10 {
- width: 83.33333333333334%;
- }
- .col-sm-9 {
- width: 75%;
- }
- .col-sm-8 {
- width: 66.66666666666666%;
- }
- .col-sm-7 {
- width: 58.333333333333336%;
- }
- .col-sm-6 {
- width: 50%;
- }
- .col-sm-5 {
- width: 41.66666666666667%;
- }
- .col-sm-4 {
- width: 33.33333333333333%;
- }
- .col-sm-3 {
- width: 25%;
- }
- .col-sm-2 {
- width: 16.666666666666664%;
- }
- .col-sm-1 {
- width: 8.333333333333332%;
- }
- .col-sm-pull-12 {
- right: 100%;
- }
- .col-sm-pull-11 {
- right: 91.66666666666666%;
- }
- .col-sm-pull-10 {
- right: 83.33333333333334%;
- }
- .col-sm-pull-9 {
- right: 75%;
- }
- .col-sm-pull-8 {
- right: 66.66666666666666%;
- }
- .col-sm-pull-7 {
- right: 58.333333333333336%;
- }
- .col-sm-pull-6 {
- right: 50%;
- }
- .col-sm-pull-5 {
- right: 41.66666666666667%;
- }
- .col-sm-pull-4 {
- right: 33.33333333333333%;
- }
- .col-sm-pull-3 {
- right: 25%;
- }
- .col-sm-pull-2 {
- right: 16.666666666666664%;
- }
- .col-sm-pull-1 {
- right: 8.333333333333332%;
- }
- .col-sm-pull-0 {
- right: auto;
- }
- .col-sm-push-12 {
- left: 100%;
- }
- .col-sm-push-11 {
- left: 91.66666666666666%;
- }
- .col-sm-push-10 {
- left: 83.33333333333334%;
- }
- .col-sm-push-9 {
- left: 75%;
- }
- .col-sm-push-8 {
- left: 66.66666666666666%;
- }
- .col-sm-push-7 {
- left: 58.333333333333336%;
- }
- .col-sm-push-6 {
- left: 50%;
- }
- .col-sm-push-5 {
- left: 41.66666666666667%;
- }
- .col-sm-push-4 {
- left: 33.33333333333333%;
- }
- .col-sm-push-3 {
- left: 25%;
- }
- .col-sm-push-2 {
- left: 16.666666666666664%;
- }
- .col-sm-push-1 {
- left: 8.333333333333332%;
- }
- .col-sm-push-0 {
- left: auto;
- }
- .col-sm-offset-12 {
- margin-left: 100%;
- }
- .col-sm-offset-11 {
- margin-left: 91.66666666666666%;
- }
- .col-sm-offset-10 {
- margin-left: 83.33333333333334%;
- }
- .col-sm-offset-9 {
- margin-left: 75%;
- }
- .col-sm-offset-8 {
- margin-left: 66.66666666666666%;
- }
- .col-sm-offset-7 {
- margin-left: 58.333333333333336%;
- }
- .col-sm-offset-6 {
- margin-left: 50%;
- }
- .col-sm-offset-5 {
- margin-left: 41.66666666666667%;
- }
- .col-sm-offset-4 {
- margin-left: 33.33333333333333%;
- }
- .col-sm-offset-3 {
- margin-left: 25%;
- }
- .col-sm-offset-2 {
- margin-left: 16.666666666666664%;
- }
- .col-sm-offset-1 {
- margin-left: 8.333333333333332%;
- }
- .col-sm-offset-0 {
- margin-left: 0%;
- }
-}
-
-@media (min-width: 992px) {
- .col-md-1,
- .col-md-2,
- .col-md-3,
- .col-md-4,
- .col-md-5,
- .col-md-6,
- .col-md-7,
- .col-md-8,
- .col-md-9,
- .col-md-10,
- .col-md-11,
- .col-md-12 {
- float: left;
- }
- .col-md-12 {
- width: 100%;
- }
- .col-md-11 {
- width: 91.66666666666666%;
- }
- .col-md-10 {
- width: 83.33333333333334%;
- }
- .col-md-9 {
- width: 75%;
- }
- .col-md-8 {
- width: 66.66666666666666%;
- }
- .col-md-7 {
- width: 58.333333333333336%;
- }
- .col-md-6 {
- width: 50%;
- }
- .col-md-5 {
- width: 41.66666666666667%;
- }
- .col-md-4 {
- width: 33.33333333333333%;
- }
- .col-md-3 {
- width: 25%;
- }
- .col-md-2 {
- width: 16.666666666666664%;
- }
- .col-md-1 {
- width: 8.333333333333332%;
- }
- .col-md-pull-12 {
- right: 100%;
- }
- .col-md-pull-11 {
- right: 91.66666666666666%;
- }
- .col-md-pull-10 {
- right: 83.33333333333334%;
- }
- .col-md-pull-9 {
- right: 75%;
- }
- .col-md-pull-8 {
- right: 66.66666666666666%;
- }
- .col-md-pull-7 {
- right: 58.333333333333336%;
- }
- .col-md-pull-6 {
- right: 50%;
- }
- .col-md-pull-5 {
- right: 41.66666666666667%;
- }
- .col-md-pull-4 {
- right: 33.33333333333333%;
- }
- .col-md-pull-3 {
- right: 25%;
- }
- .col-md-pull-2 {
- right: 16.666666666666664%;
- }
- .col-md-pull-1 {
- right: 8.333333333333332%;
- }
- .col-md-pull-0 {
- right: auto;
- }
- .col-md-push-12 {
- left: 100%;
- }
- .col-md-push-11 {
- left: 91.66666666666666%;
- }
- .col-md-push-10 {
- left: 83.33333333333334%;
- }
- .col-md-push-9 {
- left: 75%;
- }
- .col-md-push-8 {
- left: 66.66666666666666%;
- }
- .col-md-push-7 {
- left: 58.333333333333336%;
- }
- .col-md-push-6 {
- left: 50%;
- }
- .col-md-push-5 {
- left: 41.66666666666667%;
- }
- .col-md-push-4 {
- left: 33.33333333333333%;
- }
- .col-md-push-3 {
- left: 25%;
- }
- .col-md-push-2 {
- left: 16.666666666666664%;
- }
- .col-md-push-1 {
- left: 8.333333333333332%;
- }
- .col-md-push-0 {
- left: auto;
- }
- .col-md-offset-12 {
- margin-left: 100%;
- }
- .col-md-offset-11 {
- margin-left: 91.66666666666666%;
- }
- .col-md-offset-10 {
- margin-left: 83.33333333333334%;
- }
- .col-md-offset-9 {
- margin-left: 75%;
- }
- .col-md-offset-8 {
- margin-left: 66.66666666666666%;
- }
- .col-md-offset-7 {
- margin-left: 58.333333333333336%;
- }
- .col-md-offset-6 {
- margin-left: 50%;
- }
- .col-md-offset-5 {
- margin-left: 41.66666666666667%;
- }
- .col-md-offset-4 {
- margin-left: 33.33333333333333%;
- }
- .col-md-offset-3 {
- margin-left: 25%;
- }
- .col-md-offset-2 {
- margin-left: 16.666666666666664%;
- }
- .col-md-offset-1 {
- margin-left: 8.333333333333332%;
- }
- .col-md-offset-0 {
- margin-left: 0%;
- }
-}
-
-@media (min-width: 1200px) {
- .col-lg-1,
- .col-lg-2,
- .col-lg-3,
- .col-lg-4,
- .col-lg-5,
- .col-lg-6,
- .col-lg-7,
- .col-lg-8,
- .col-lg-9,
- .col-lg-10,
- .col-lg-11,
- .col-lg-12 {
- float: left;
- }
- .col-lg-12 {
- width: 100%;
- }
- .col-lg-11 {
- width: 91.66666666666666%;
- }
- .col-lg-10 {
- width: 83.33333333333334%;
- }
- .col-lg-9 {
- width: 75%;
- }
- .col-lg-8 {
- width: 66.66666666666666%;
- }
- .col-lg-7 {
- width: 58.333333333333336%;
- }
- .col-lg-6 {
- width: 50%;
- }
- .col-lg-5 {
- width: 41.66666666666667%;
- }
- .col-lg-4 {
- width: 33.33333333333333%;
- }
- .col-lg-3 {
- width: 25%;
- }
- .col-lg-2 {
- width: 16.666666666666664%;
- }
- .col-lg-1 {
- width: 8.333333333333332%;
- }
- .col-lg-pull-12 {
- right: 100%;
- }
- .col-lg-pull-11 {
- right: 91.66666666666666%;
- }
- .col-lg-pull-10 {
- right: 83.33333333333334%;
- }
- .col-lg-pull-9 {
- right: 75%;
- }
- .col-lg-pull-8 {
- right: 66.66666666666666%;
- }
- .col-lg-pull-7 {
- right: 58.333333333333336%;
- }
- .col-lg-pull-6 {
- right: 50%;
- }
- .col-lg-pull-5 {
- right: 41.66666666666667%;
- }
- .col-lg-pull-4 {
- right: 33.33333333333333%;
- }
- .col-lg-pull-3 {
- right: 25%;
- }
- .col-lg-pull-2 {
- right: 16.666666666666664%;
- }
- .col-lg-pull-1 {
- right: 8.333333333333332%;
- }
- .col-lg-pull-0 {
- right: auto;
- }
- .col-lg-push-12 {
- left: 100%;
- }
- .col-lg-push-11 {
- left: 91.66666666666666%;
- }
- .col-lg-push-10 {
- left: 83.33333333333334%;
- }
- .col-lg-push-9 {
- left: 75%;
- }
- .col-lg-push-8 {
- left: 66.66666666666666%;
- }
- .col-lg-push-7 {
- left: 58.333333333333336%;
- }
- .col-lg-push-6 {
- left: 50%;
- }
- .col-lg-push-5 {
- left: 41.66666666666667%;
- }
- .col-lg-push-4 {
- left: 33.33333333333333%;
- }
- .col-lg-push-3 {
- left: 25%;
- }
- .col-lg-push-2 {
- left: 16.666666666666664%;
- }
- .col-lg-push-1 {
- left: 8.333333333333332%;
- }
- .col-lg-push-0 {
- left: auto;
- }
- .col-lg-offset-12 {
- margin-left: 100%;
- }
- .col-lg-offset-11 {
- margin-left: 91.66666666666666%;
- }
- .col-lg-offset-10 {
- margin-left: 83.33333333333334%;
- }
- .col-lg-offset-9 {
- margin-left: 75%;
- }
- .col-lg-offset-8 {
- margin-left: 66.66666666666666%;
- }
- .col-lg-offset-7 {
- margin-left: 58.333333333333336%;
- }
- .col-lg-offset-6 {
- margin-left: 50%;
- }
- .col-lg-offset-5 {
- margin-left: 41.66666666666667%;
- }
- .col-lg-offset-4 {
- margin-left: 33.33333333333333%;
- }
- .col-lg-offset-3 {
- margin-left: 25%;
- }
- .col-lg-offset-2 {
- margin-left: 16.666666666666664%;
- }
- .col-lg-offset-1 {
- margin-left: 8.333333333333332%;
- }
- .col-lg-offset-0 {
- margin-left: 0%;
- }
-}
-table {
- background-color: transparent;
-}
-caption {
- padding-top: 8px;
- padding-bottom: 8px;
- color: #777;
- text-align: left;
-}
-th {
- text-align: left;
-}
-.table {
- width: 100%;
- max-width: 100%;
- margin-bottom: 20px;
-}
-.table > thead > tr > th,
-.table > tbody > tr > th,
-.table > tfoot > tr > th,
-.table > thead > tr > td,
-.table > tbody > tr > td,
-.table > tfoot > tr > td {
- padding: 8px;
- line-height: 1.428571429;
- vertical-align: middle;
- border-top: 1px solid #ddd;
-}
-.table > thead > tr > th {
- vertical-align: bottom;
- background-color: #f0f0f0;
- border-bottom: 1px solid #fff;
- white-space: nowrap;
-}
-.table > caption + thead > tr:first-child > th,
-.table > colgroup + thead > tr:first-child > th,
-.table > thead:first-child > tr:first-child > th,
-.table > caption + thead > tr:first-child > td,
-.table > colgroup + thead > tr:first-child > td,
-.table > thead:first-child > tr:first-child > td {
- border-top: 0;
-}
-.table > tbody + tbody {
- border-top: 0;
-}
-.table .table {
- background-color: #f0f0f0;
-}
-.table-condensed > thead > tr > th,
-.table-condensed > tbody > tr > th,
-.table-condensed > tfoot > tr > th,
-.table-condensed > thead > tr > td,
-.table-condensed > tbody > tr > td,
-.table-condensed > tfoot > tr > td {
- padding: 5px;
-}
-.table-bordered {
- border: 1px solid #f0f0f0;
-}
-.table-bordered > thead > tr > th,
-.table-bordered > tbody > tr > th,
-.table-bordered > tfoot > tr > th {
- border: 0;
-}
-.table-bordered > thead > tr > td,
-.table-bordered > tbody > tr > td,
-.table-bordered > tfoot > tr > td {
- border: 0;
- border-top: 1px solid #f0f0f0;
-}
-.table-bordered > thead > tr > th,
-.table-bordered > thead > tr > td {
- border-bottom-width: 2px;
-}
-.table-striped > tbody > tr:nth-child(odd) {
- background-color: #fff;
-}
-.table-striped > tbody > tr:nth-child(even) {
- background-color: #f9f9f9;
-}
-.table-hover > tbody > tr:hover {
- background-color: #f5f5f5;
-}
-table col[class*="col-"] {
- position: static;
- float: none;
- display: table-column;
-}
-table td[class*="col-"],
-table th[class*="col-"] {
- position: static;
- float: none;
- display: table-cell;
-}
-.table > thead > tr > td.active,
-.table > tbody > tr > td.active,
-.table > tfoot > tr > td.active,
-.table > thead > tr > th.active,
-.table > tbody > tr > th.active,
-.table > tfoot > tr > th.active,
-.table > thead > tr.active > td,
-.table > tbody > tr.active > td,
-.table > tfoot > tr.active > td,
-.table > thead > tr.active > th,
-.table > tbody > tr.active > th,
-.table > tfoot > tr.active > th {
- background-color: #f5f5f5;
-}
-.table-hover > tbody > tr > td.active:hover,
-.table-hover > tbody > tr > th.active:hover,
-.table-hover > tbody > tr.active:hover > td,
-.table-hover > tbody > tr:hover > .active,
-.table-hover > tbody > tr.active:hover > th {
- background-color: #e8e8e8;
-}
-.table > thead > tr > td.success,
-.table > tbody > tr > td.success,
-.table > tfoot > tr > td.success,
-.table > thead > tr > th.success,
-.table > tbody > tr > th.success,
-.table > tfoot > tr > th.success,
-.table > thead > tr.success > td,
-.table > tbody > tr.success > td,
-.table > tfoot > tr.success > td,
-.table > thead > tr.success > th,
-.table > tbody > tr.success > th,
-.table > tfoot > tr.success > th {
- background-color: #bfebd1;
-}
-.table-hover > tbody > tr > td.success:hover,
-.table-hover > tbody > tr > th.success:hover,
-.table-hover > tbody > tr.success:hover > td,
-.table-hover > tbody > tr:hover > .success,
-.table-hover > tbody > tr.success:hover > th {
- background-color: #d0e9c6;
-}
-.table > thead > tr > td.info,
-.table > tbody > tr > td.info,
-.table > tfoot > tr > td.info,
-.table > thead > tr > th.info,
-.table > tbody > tr > th.info,
-.table > tfoot > tr > th.info,
-.table > thead > tr.info > td,
-.table > tbody > tr.info > td,
-.table > tfoot > tr.info > td,
-.table > thead > tr.info > th,
-.table > tbody > tr.info > th,
-.table > tfoot > tr.info > th {
- background-color: #d9edf7;
-}
-.table-hover > tbody > tr > td.info:hover,
-.table-hover > tbody > tr > th.info:hover,
-.table-hover > tbody > tr.info:hover > td,
-.table-hover > tbody > tr:hover > .info,
-.table-hover > tbody > tr.info:hover > th {
- background-color: #c4e3f3;
-}
-.table > thead > tr > td.warning,
-.table > tbody > tr > td.warning,
-.table > tfoot > tr > td.warning,
-.table > thead > tr > th.warning,
-.table > tbody > tr > th.warning,
-.table > tfoot > tr > th.warning,
-.table > thead > tr.warning > td,
-.table > tbody > tr.warning > td,
-.table > tfoot > tr.warning > td,
-.table > thead > tr.warning > th,
-.table > tbody > tr.warning > th,
-.table > tfoot > tr.warning > th {
- background-color: #fcf8e3;
-}
-.table-hover > tbody > tr > td.warning:hover,
-.table-hover > tbody > tr > th.warning:hover,
-.table-hover > tbody > tr.warning:hover > td,
-.table-hover > tbody > tr:hover > .warning,
-.table-hover > tbody > tr.warning:hover > th {
- background-color: #faf2cc;
-}
-.table > thead > tr > td.danger,
-.table > tbody > tr > td.danger,
-.table > tfoot > tr > td.danger,
-.table > thead > tr > th.danger,
-.table > tbody > tr > th.danger,
-.table > tfoot > tr > th.danger,
-.table > thead > tr.danger > td,
-.table > tbody > tr.danger > td,
-.table > tfoot > tr.danger > td,
-.table > thead > tr.danger > th,
-.table > tbody > tr.danger > th,
-.table > tfoot > tr.danger > th {
- background-color: #f2dede;
-}
-.table-hover > tbody > tr > td.danger:hover,
-.table-hover > tbody > tr > th.danger:hover,
-.table-hover > tbody > tr.danger:hover > td,
-.table-hover > tbody > tr:hover > .danger,
-.table-hover > tbody > tr.danger:hover > th {
- background-color: #ebcccc;
-}
-.table-responsive {
- overflow: visible;
- min-height: 0.01%;
-}
-
-@media screen and (max-width: 767px) {
- .table-responsive {
- width: 100%;
- margin-bottom: 15px;
- border: 1px solid #ddd;
- }
- .table-responsive > .table {
- margin-bottom: 0;
- }
- .table-responsive > .table > thead > tr > th,
- .table-responsive > .table > tbody > tr > th,
- .table-responsive > .table > tfoot > tr > th,
- .table-responsive > .table > thead > tr > td,
- .table-responsive > .table > tbody > tr > td,
- .table-responsive > .table > tfoot > tr > td {
- white-space: nowrap;
- }
- .table-responsive > .table-bordered {
- border: 0;
- }
- .table-responsive > .table-bordered > thead > tr > th:first-child,
- .table-responsive > .table-bordered > tbody > tr > th:first-child,
- .table-responsive > .table-bordered > tfoot > tr > th:first-child,
- .table-responsive > .table-bordered > thead > tr > td:first-child,
- .table-responsive > .table-bordered > tbody > tr > td:first-child,
- .table-responsive > .table-bordered > tfoot > tr > td:first-child {
- border-left: 0;
- }
- .table-responsive > .table-bordered > thead > tr > th:last-child,
- .table-responsive > .table-bordered > tbody > tr > th:last-child,
- .table-responsive > .table-bordered > tfoot > tr > th:last-child,
- .table-responsive > .table-bordered > thead > tr > td:last-child,
- .table-responsive > .table-bordered > tbody > tr > td:last-child,
- .table-responsive > .table-bordered > tfoot > tr > td:last-child {
- border-right: 0;
- }
- .table-responsive > .table-bordered > tbody > tr:last-child > th,
- .table-responsive > .table-bordered > tfoot > tr:last-child > th,
- .table-responsive > .table-bordered > tbody > tr:last-child > td,
- .table-responsive > .table-bordered > tfoot > tr:last-child > td {
- border-bottom: 0;
- }
-}
-
-fieldset {
- padding: 0;
- margin: 0;
- border: 0;
- min-width: 0;
-}
-legend {
- display: block;
- width: 100%;
- padding: 0;
- margin-bottom: 20px;
- font-size: 21px;
- line-height: inherit;
- color: #51504f;
- border: 0;
- border-bottom: 1px solid #e5e5e5;
-}
-label {
- display: inline-block;
- max-width: 100%;
- margin-bottom: 5px;
- font-weight: bold;
-}
-input[type="radio"],
-input[type="checkbox"] {
- margin: 4px 0 0;
- margin-top: 1px \9;
- line-height: normal;
-}
-input[type="file"] {
- display: block;
-}
-input[type="range"] {
- display: block;
- width: 100%;
-}
-select[multiple],
-select[size] {
- height: auto;
-}
-input[type="file"]:focus,
-input[type="radio"]:focus,
-input[type="checkbox"]:focus {
- outline: thin dotted;
- outline: 5px auto -webkit-focus-ring-color;
- outline-offset: -2px;
-}
-output {
- display: block;
- padding-top: 7px;
- font-size: 14px;
- line-height: 1.428571429;
- color: #555;
-}
-.form-control {
- display: block;
- width: 100%;
- height: 34px;
- padding: 6px 12px;
- font-size: 14px;
- line-height: 1.428571429;
- color: #555;
- background-color: #fff;
- background-image: none;
- border: 1px solid #ccc;
- border-radius: 4px;
- -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
- box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
- -webkit-transition: border-color ease-in-out 0.15s,
- box-shadow ease-in-out 0.15s;
- -o-transition: border-color ease-in-out 0.15s, box-shadow ease-in-out 0.15s;
- transition: border-color ease-in-out 0.15s, box-shadow ease-in-out 0.15s;
-}
-.form-control:focus {
- border-color: #66afe9;
- outline: 0;
- -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075),
- 0 0 8px rgba(102, 175, 233, 0.6);
- box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075),
- 0 0 8px rgba(102, 175, 233, 0.6);
-}
-.form-control::-moz-placeholder {
- color: #999;
- opacity: 1;
-}
-.form-control:-ms-input-placeholder {
- color: #999;
-}
-.form-control::-webkit-input-placeholder {
- color: #999;
-}
-.form-control[disabled],
-.form-control[readonly],
-fieldset[disabled] .form-control {
- cursor: not-allowed;
- color: #cbcbcb;
- background-color: #eee;
- opacity: 1;
-}
-textarea.form-control {
- height: auto;
-}
-
-@media screen and (-webkit-min-device-pixel-ratio: 0) {
- input[type="date"],
- input[type="time"],
- input[type="datetime-local"],
- input[type="month"] {
- line-height: 34px;
- }
- input[type="date"].input-sm,
- input[type="time"].input-sm,
- input[type="datetime-local"].input-sm,
- input[type="month"].input-sm {
- line-height: 30px;
- }
- input[type="date"].input-lg,
- input[type="time"].input-lg,
- input[type="datetime-local"].input-lg,
- input[type="month"].input-lg {
- line-height: 46px;
- }
-}
-.form-group {
- margin-bottom: 15px;
-}
-.radio,
-.checkbox {
- position: relative;
- display: block;
- margin-top: 10px;
- margin-bottom: 10px;
-}
-.radio label,
-.checkbox label {
- min-height: 20px;
- padding-left: 20px;
- margin-bottom: 0;
- font-weight: normal;
- cursor: pointer;
-}
-.radio input[type="radio"],
-.radio-inline input[type="radio"],
-.checkbox input[type="checkbox"],
-.checkbox-inline input[type="checkbox"] {
- position: absolute;
- margin-left: -20px;
- margin-top: 4px \9;
-}
-.radio + .radio,
-.checkbox + .checkbox {
- margin-top: -5px;
-}
-.radio-inline,
-.checkbox-inline {
- display: inline-block;
- padding-left: 20px;
- margin-bottom: 0;
- vertical-align: middle;
- font-weight: normal;
- cursor: pointer;
-}
-.radio-inline + .radio-inline,
-.checkbox-inline + .checkbox-inline {
- margin-top: 0;
- margin-left: 10px;
-}
-input[type="radio"][disabled],
-input[type="checkbox"][disabled],
-input[type="radio"].disabled,
-input[type="checkbox"].disabled,
-fieldset[disabled] input[type="radio"],
-fieldset[disabled] input[type="checkbox"] {
- cursor: not-allowed;
-}
-.radio-inline.disabled,
-.checkbox-inline.disabled,
-fieldset[disabled] .radio-inline,
-fieldset[disabled] .checkbox-inline {
- cursor: not-allowed;
-}
-.radio.disabled label,
-.checkbox.disabled label,
-fieldset[disabled] .radio label,
-fieldset[disabled] .checkbox label {
- cursor: not-allowed;
-}
-.form-control-static {
- padding-top: 7px;
- padding-bottom: 7px;
- margin-bottom: 0;
-}
-.form-control-static.input-lg,
-.form-control-static.input-sm {
- padding-left: 0;
- padding-right: 0;
-}
-.input-sm {
- height: 30px;
- padding: 5px 10px;
- font-size: 12px;
- line-height: 1.5;
- border-radius: 3px;
-}
-select.input-sm {
- height: 30px;
- line-height: 30px;
-}
-textarea.input-sm,
-select[multiple].input-sm {
- height: auto;
-}
-.form-group-sm .form-control {
- height: 30px;
- padding: 5px 10px;
- font-size: 12px;
- line-height: 1.5;
- border-radius: 3px;
-}
-select.form-group-sm .form-control {
- height: 30px;
- line-height: 30px;
-}
-textarea.form-group-sm .form-control,
-select[multiple].form-group-sm .form-control {
- height: auto;
-}
-.input-lg {
- height: 46px;
- padding: 10px 16px;
- font-size: 18px;
- line-height: 1.33;
- border-radius: 6px;
-}
-select.input-lg {
- height: 46px;
- line-height: 46px;
-}
-textarea.input-lg,
-select[multiple].input-lg {
- height: auto;
-}
-.form-group-lg .form-control {
- height: 46px;
- padding: 10px 16px;
- font-size: 18px;
- line-height: 1.33;
- border-radius: 6px;
-}
-select.form-group-lg .form-control {
- height: 46px;
- line-height: 46px;
-}
-textarea.form-group-lg .form-control,
-select[multiple].form-group-lg .form-control {
- height: auto;
-}
-.has-feedback {
- position: relative;
-}
-.has-feedback .form-control {
- padding-right: 42.5px;
-}
-.form-control-feedback {
- position: absolute;
- top: 0;
- right: 0;
- z-index: 2;
- display: block;
- width: 34px;
- height: 34px;
- line-height: 34px;
- text-align: center;
- pointer-events: none;
-}
-.input-lg + .form-control-feedback {
- width: 46px;
- height: 46px;
- line-height: 46px;
-}
-.input-sm + .form-control-feedback {
- width: 30px;
- height: 30px;
- line-height: 30px;
-}
-.has-success .help-block,
-.has-success .control-label,
-.has-success .radio,
-.has-success .checkbox,
-.has-success .radio-inline,
-.has-success .checkbox-inline,
-.has-success.radio label,
-.has-success.checkbox label,
-.has-success.radio-inline label,
-.has-success.checkbox-inline label {
- color: #1b8e49;
-}
-.has-success .form-control {
- border-color: #1b8e49;
- -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
- box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
-}
-.has-success .form-control:focus {
- border-color: #366f4c;
- -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #67b168;
- box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #67b168;
-}
-.has-success .input-group-addon {
- color: #1b8e49;
- border-color: #1b8e49;
- background-color: #bfebd1;
-}
-.has-success .form-control-feedback {
- color: #1b8e49;
-}
-.has-warning .help-block,
-.has-warning .control-label,
-.has-warning .radio,
-.has-warning .checkbox,
-.has-warning .radio-inline,
-.has-warning .checkbox-inline,
-.has-warning.radio label,
-.has-warning.checkbox label,
-.has-warning.radio-inline label,
-.has-warning.checkbox-inline label {
- color: #8a6d3b;
-}
-.has-warning .form-control {
- border-color: #8a6d3b;
- -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
- box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
-}
-.has-warning .form-control:focus {
- border-color: #66512c;
- -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #c0a16b;
- box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #c0a16b;
-}
-.has-warning .input-group-addon {
- color: #8a6d3b;
- border-color: #8a6d3b;
- background-color: #fcf8e3;
-}
-.has-warning .form-control-feedback {
- color: #8a6d3b;
-}
-.has-error .help-block,
-.has-error .control-label,
-.has-error .radio,
-.has-error .checkbox,
-.has-error .radio-inline,
-.has-error .checkbox-inline,
-.has-error.radio label,
-.has-error.checkbox label,
-.has-error.radio-inline label,
-.has-error.checkbox-inline label {
- color: #e43921;
-}
-.has-error .form-control {
- border-color: #e43921;
- -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
- box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
-}
-.has-error .form-control:focus {
- border-color: #843534;
- -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #ce8483;
- box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #ce8483;
-}
-.has-error .input-group-addon {
- color: #e43921;
- border-color: #e43921;
- background-color: #f2dede;
-}
-.has-error .form-control-feedback {
- color: #e43921;
-}
-.has-feedback label ~ .form-control-feedback {
- top: 25px;
-}
-.has-feedback label.sr-only ~ .form-control-feedback {
- top: 0;
-}
-.help-block {
- display: block;
- margin-top: 5px;
- margin-bottom: 10px;
- color: #737373;
-}
-
-@media (min-width: 768px) {
- .form-inline .form-group {
- display: inline-block;
- margin-bottom: 0;
- vertical-align: middle;
- }
- .form-inline .form-control {
- display: inline-block;
- width: auto;
- vertical-align: middle;
- }
- .form-inline .form-control-static {
- display: inline-block;
- }
- .form-inline .input-group {
- display: inline-table;
- vertical-align: middle;
- }
- .form-inline .input-group .input-group-addon,
- .form-inline .input-group .input-group-btn,
- .form-inline .input-group .form-control {
- width: auto;
- }
- .form-inline .input-group > .form-control {
- width: 100%;
- }
- .form-inline .control-label {
- margin-bottom: 0;
- vertical-align: middle;
- }
- .form-inline .radio,
- .form-inline .checkbox {
- display: inline-block;
- margin-top: 0;
- margin-bottom: 0;
- vertical-align: middle;
- }
- .form-inline .radio label,
- .form-inline .checkbox label {
- padding-left: 0;
- }
- .form-inline .radio input[type="radio"],
- .form-inline .checkbox input[type="checkbox"] {
- position: relative;
- margin-left: 0;
- }
- .form-inline .has-feedback .form-control-feedback {
- top: 0;
- }
-}
-.form-horizontal .radio,
-.form-horizontal .checkbox,
-.form-horizontal .radio-inline,
-.form-horizontal .checkbox-inline {
- margin-top: 0;
- margin-bottom: 0;
- padding-top: 7px;
-}
-.form-horizontal .radio,
-.form-horizontal .checkbox {
- min-height: 27px;
-}
-.form-horizontal .form-group {
- margin-left: -15px;
- margin-right: -15px;
-}
-
-@media (min-width: 768px) {
- .form-horizontal .control-label {
- text-align: right;
- margin-bottom: 0;
- padding-top: 7px;
- }
-}
-.form-horizontal .has-feedback .form-control-feedback {
- right: 15px;
-}
-
-@media (min-width: 768px) {
- .form-horizontal .form-group-lg .control-label {
- padding-top: 14.3px;
- }
-}
-
-@media (min-width: 768px) {
- .form-horizontal .form-group-sm .control-label {
- padding-top: 6px;
- }
-}
-.btn {
- display: inline-block;
- margin-bottom: 0;
- font-weight: normal;
- text-align: center;
- vertical-align: middle;
- touch-action: manipulation;
- cursor: pointer;
- color: #017cee;
- background: #fff;
- border: 1px solid #017cee;
- white-space: nowrap;
- padding: 6px 12px;
- font-size: 14px;
- line-height: 1.428571429;
- border-radius: 4px;
- -webkit-user-select: none;
- -moz-user-select: none;
- -ms-user-select: none;
- user-select: none;
-}
-.btn:focus,
-.btn:active:focus,
-.btn.active:focus,
-.btn.focus,
-.btn:active.focus,
-.btn.active.focus {
- outline: thin dotted;
- outline: 5px auto -webkit-focus-ring-color;
- outline-offset: -2px;
-}
-.btn:hover,
-.btn:focus,
-.btn.focus {
- color: #017cee;
- text-decoration: none;
- background-color: #c0defb;
-}
-.btn:active,
-.btn.active {
- outline: 0;
- background-image: none;
- -webkit-box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);
- box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);
-}
-.btn.disabled,
-.btn[disabled],
-fieldset[disabled] .btn {
- cursor: not-allowed;
- pointer-events: none;
- opacity: 0.5;
- -webkit-box-shadow: none;
- box-shadow: none;
-}
-.btn-icon-only {
- padding: 6px;
-}
-.btn-default {
- color: #365f84;
- background-color: #fff;
- border-color: #ccc;
-}
-.btn-default:hover,
-.btn-default:focus,
-.btn-default.focus,
-.btn-default:active,
-.btn-default.active,
-.open > .dropdown-toggle.btn-default {
- color: #017cee;
- background-color: #f0f0f0;
- border-color: #adadad;
-}
-.btn-default:active,
-.btn-default.active,
-.open > .dropdown-toggle.btn-default {
- background-image: none;
-}
-.btn-default.disabled,
-.btn-default[disabled],
-fieldset[disabled] .btn-default,
-.btn-default.disabled:hover,
-.btn-default[disabled]:hover,
-fieldset[disabled] .btn-default:hover,
-.btn-default.disabled:focus,
-.btn-default[disabled]:focus,
-fieldset[disabled] .btn-default:focus,
-.btn-default.disabled.focus,
-.btn-default[disabled].focus,
-fieldset[disabled] .btn-default.focus,
-.btn-default.disabled:active,
-.btn-default[disabled]:active,
-fieldset[disabled] .btn-default:active,
-.btn-default.disabled.active,
-.btn-default[disabled].active,
-fieldset[disabled] .btn-default.active {
- background-color: #fff;
- border-color: #ccc;
-}
-.btn-default .badge {
- color: #fff;
- background-color: #51504f;
-}
-.btn-primary {
- color: #fff;
- background-color: #017cee;
- border-color: #017cee;
-}
-.btn-primary:hover,
-.btn-primary:focus,
-.btn-primary.focus,
-.btn-primary:active,
-.btn-primary.active,
-.open > .dropdown-toggle.btn-primary {
- color: #fff;
- background-color: #0cb6ff;
- border-color: #0cb6ff;
-}
-.btn-primary:active,
-.btn-primary.active,
-.open > .dropdown-toggle.btn-primary {
- background-image: none;
-}
-.btn-primary.disabled,
-.btn-primary[disabled],
-fieldset[disabled] .btn-primary,
-.btn-primary.disabled:hover,
-.btn-primary[disabled]:hover,
-fieldset[disabled] .btn-primary:hover,
-.btn-primary.disabled:focus,
-.btn-primary[disabled]:focus,
-fieldset[disabled] .btn-primary:focus,
-.btn-primary.disabled.focus,
-.btn-primary[disabled].focus,
-fieldset[disabled] .btn-primary.focus,
-.btn-primary.disabled:active,
-.btn-primary[disabled]:active,
-fieldset[disabled] .btn-primary:active,
-.btn-primary.disabled.active,
-.btn-primary[disabled].active,
-fieldset[disabled] .btn-primary.active {
- background-color: #017cee;
- border-color: #00454c;
-}
-.btn-primary .badge {
- color: #017cee;
- background-color: #fff;
-}
-.btn-primary:hover .badge,
-.btn-primary:focus .badge,
-.btn-primary.focus .badge,
-.btn-primary:active .badge,
-.btn-primary.active .badge,
-.open > .dropdown-toggle.btn-primary .badge {
- color: #0cb6ff;
-}
-.btn-success {
- color: #fff;
- background-color: #00ad46;
- border-color: #38a047;
-}
-.btn-success:hover,
-.btn-success:focus,
-.btn-success.focus,
-.btn-success:active,
-.btn-success.active,
-.open > .dropdown-toggle.btn-success {
- color: #fff;
- background-color: #328d3e;
- border-color: #287333;
-}
-.btn-success:active,
-.btn-success.active,
-.open > .dropdown-toggle.btn-success {
- background-image: none;
-}
-.btn-success.disabled,
-.btn-success[disabled],
-fieldset[disabled] .btn-success,
-.btn-success.disabled:hover,
-.btn-success[disabled]:hover,
-fieldset[disabled] .btn-success:hover,
-.btn-success.disabled:focus,
-.btn-success[disabled]:focus,
-fieldset[disabled] .btn-success:focus,
-.btn-success.disabled.focus,
-.btn-success[disabled].focus,
-fieldset[disabled] .btn-success.focus,
-.btn-success.disabled:active,
-.btn-success[disabled]:active,
-fieldset[disabled] .btn-success:active,
-.btn-success.disabled.active,
-.btn-success[disabled].active,
-fieldset[disabled] .btn-success.active {
- background-color: #00ad46;
- border-color: #00ad46;
-}
-.btn-success .badge {
- color: #00ad46;
- background-color: #fff;
-}
-.btn-info {
- color: #fff;
- background-color: #00d1c1;
- border-color: #00b8a9;
-}
-.btn-info:hover,
-.btn-info:focus,
-.btn-info.focus,
-.btn-info:active,
-.btn-info.active,
-.open > .dropdown-toggle.btn-info {
- color: #fff;
- background-color: #009e92;
- border-color: #007a71;
-}
-.btn-info:active,
-.btn-info.active,
-.open > .dropdown-toggle.btn-info {
- background-image: none;
-}
-.btn-info.disabled,
-.btn-info[disabled],
-fieldset[disabled] .btn-info,
-.btn-info.disabled:hover,
-.btn-info[disabled]:hover,
-fieldset[disabled] .btn-info:hover,
-.btn-info.disabled:focus,
-.btn-info[disabled]:focus,
-fieldset[disabled] .btn-info:focus,
-.btn-info.disabled.focus,
-.btn-info[disabled].focus,
-fieldset[disabled] .btn-info.focus,
-.btn-info.disabled:active,
-.btn-info[disabled]:active,
-fieldset[disabled] .btn-info:active,
-.btn-info.disabled.active,
-.btn-info[disabled].active,
-fieldset[disabled] .btn-info.active {
- background-color: #00d1c1;
- border-color: #00b8a9;
-}
-.btn-info .badge {
- color: #00d1c1;
- background-color: #fff;
-}
-.btn-warning {
- color: #fff;
- background-color: #ffb400;
- border-color: #e6a200;
-}
-.btn-warning:hover,
-.btn-warning:focus,
-.btn-warning.focus,
-.btn-warning:active,
-.btn-warning.active,
-.open > .dropdown-toggle.btn-warning {
- color: #fff;
- background-color: #cc9000;
- border-color: #a87700;
-}
-.btn-warning:active,
-.btn-warning.active,
-.open > .dropdown-toggle.btn-warning {
- background-image: none;
-}
-.btn-warning.disabled,
-.btn-warning[disabled],
-fieldset[disabled] .btn-warning,
-.btn-warning.disabled:hover,
-.btn-warning[disabled]:hover,
-fieldset[disabled] .btn-warning:hover,
-.btn-warning.disabled:focus,
-.btn-warning[disabled]:focus,
-fieldset[disabled] .btn-warning:focus,
-.btn-warning.disabled.focus,
-.btn-warning[disabled].focus,
-fieldset[disabled] .btn-warning.focus,
-.btn-warning.disabled:active,
-.btn-warning[disabled]:active,
-fieldset[disabled] .btn-warning:active,
-.btn-warning.disabled.active,
-.btn-warning[disabled].active,
-fieldset[disabled] .btn-warning.active {
- background-color: #ffb400;
- border-color: #e6a200;
-}
-.btn-warning .badge {
- color: #ffb400;
- background-color: #fff;
-}
-.btn-danger {
- color: #fff;
- background-color: #ff5a5f;
- border-color: #ff4146;
-}
-.btn-danger:hover,
-.btn-danger:focus,
-.btn-danger.focus,
-.btn-danger:active,
-.btn-danger.active,
-.open > .dropdown-toggle.btn-danger {
- color: #fff;
- background-color: #ff272e;
- border-color: #ff030b;
-}
-.btn-danger:active,
-.btn-danger.active,
-.open > .dropdown-toggle.btn-danger {
- background-image: none;
-}
-.btn-danger.disabled,
-.btn-danger[disabled],
-fieldset[disabled] .btn-danger,
-.btn-danger.disabled:hover,
-.btn-danger[disabled]:hover,
-fieldset[disabled] .btn-danger:hover,
-.btn-danger.disabled:focus,
-.btn-danger[disabled]:focus,
-fieldset[disabled] .btn-danger:focus,
-.btn-danger.disabled.focus,
-.btn-danger[disabled].focus,
-fieldset[disabled] .btn-danger.focus,
-.btn-danger.disabled:active,
-.btn-danger[disabled]:active,
-fieldset[disabled] .btn-danger:active,
-.btn-danger.disabled.active,
-.btn-danger[disabled].active,
-fieldset[disabled] .btn-danger.active {
- background-color: #ff5a5f;
- border-color: #ff4146;
-}
-.btn-danger .badge {
- color: #ff5a5f;
- background-color: #fff;
-}
-.btn-link {
- color: #017cee;
- font-weight: normal;
- border-radius: 0;
-}
-.btn-link,
-.btn-link:active,
-.btn-link.active,
-.btn-link[disabled],
-fieldset[disabled] .btn-link {
- background-color: transparent;
- -webkit-box-shadow: none;
- box-shadow: none;
-}
-.btn-link,
-.btn-link:hover,
-.btn-link:focus,
-.btn-link:active {
- border-color: transparent;
-}
-.btn-link:hover,
-.btn-link:focus {
- color: #004c54;
- text-decoration: underline;
- background-color: transparent;
-}
-.btn-link[disabled]:hover,
-fieldset[disabled] .btn-link:hover,
-.btn-link[disabled]:focus,
-fieldset[disabled] .btn-link:focus {
- color: #777;
- text-decoration: none;
-}
-.btn-lg,
-.btn-group-lg > .btn {
- padding: 10px 16px;
- font-size: 18px;
- line-height: 1.33;
- border-radius: 6px;
-}
-.btn-lg.btn-icon-only,
-.btn-group-lg > .btn.btn-icon-only {
- padding: 10px;
-}
-.btn-sm,
-.btn-group-sm > .btn {
- padding: 5px 10px;
- font-size: 12px;
- line-height: 1.5;
- border-radius: 3px;
-}
-.btn-sm.btn-icon-only,
-.btn-group-sm > .btn.btn-icon-only {
- padding: 5px;
-}
-.btn-xs,
-.btn-group-xs > .btn {
- padding: 1px 5px;
- font-size: 12px;
- line-height: 1.5;
- border-radius: 3px;
-}
-.btn-xs.btn-icon-only,
-.btn-group-xs > .btn.btn-icon-only {
- padding: 1px;
-}
-.btn-block {
- display: block;
- width: 100%;
-}
-.btn-block + .btn-block {
- margin-top: 5px;
-}
-input[type="submit"].btn-block,
-input[type="reset"].btn-block,
-input[type="button"].btn-block {
- width: 100%;
-}
-.fade {
- opacity: 0;
- -webkit-transition: opacity 0.15s linear;
- -o-transition: opacity 0.15s linear;
- transition: opacity 0.15s linear;
-}
-.fade.in {
- opacity: 1;
-}
-.collapse {
- display: none;
- visibility: hidden;
-}
-.collapse.in {
- display: block;
- visibility: visible;
-}
-tr.collapse.in {
- display: table-row;
-}
-tbody.collapse.in {
- display: table-row-group;
-}
-.collapsing {
- position: relative;
- height: 0;
- overflow: hidden;
- -webkit-transition-property: height, visibility;
- transition-property: height, visibility;
- -webkit-transition-duration: 0.35s;
- transition-duration: 0.35s;
- -webkit-transition-timing-function: ease;
- transition-timing-function: ease;
-}
-.caret {
- display: inline-block;
- width: 0;
- height: 0;
- margin-left: 2px;
- vertical-align: middle;
- border-top: 4px solid;
- border-right: 4px solid transparent;
- border-left: 4px solid transparent;
-}
-.dropdown {
- position: relative;
-}
-.dropdown-toggle:focus {
- outline: 0;
-}
-.dropdown-menu {
- position: absolute;
- top: 100%;
- left: 0;
- z-index: 1000;
- display: none;
- float: left;
- min-width: 160px;
- padding: 5px 0;
- margin: 2px 0 0;
- list-style: none;
- font-size: 14px;
- text-align: left;
- background-color: #fff;
- border: 1px solid #ddd;
- border-radius: 4px;
- -webkit-box-shadow: 0 6px 12px rgba(0, 0, 0, 0.175);
- box-shadow: 0 6px 12px rgba(0, 0, 0, 0.175);
- background-clip: padding-box;
-}
-.dropdown-menu.pull-right {
- right: 0;
- left: auto;
-}
-.dropdown-menu .divider {
- height: 1px;
- margin: 9px 0;
- overflow: hidden;
- background-color: #e5e5e5;
-}
-
-.dropdown-menu > li > a,
-.dropdown-menu > li > form > button {
- display: block;
- padding: 3px 20px;
- clear: both;
- font-weight: normal;
- line-height: 1.428571429;
- color: #51504f;
- white-space: nowrap;
- width: 100%;
- max-width: 100%;
- text-align: left;
-}
-.dropdown-menu > li > a:hover,
-.dropdown-menu > li > a:focus,
-.dropdown-menu > li > form > button:hover,
-.dropdown-menu > li > form > button:focus {
- text-decoration: none;
- color: #262626;
- background-color: #f5f5f5;
-}
-.dropdown-menu > .active > a,
-.dropdown-menu > .active > a:hover,
-.dropdown-menu > .active > a:focus {
- color: #fff;
- text-decoration: none;
- outline: 0;
- background-color: #017cee;
-}
-.dropdown-menu > .disabled > a,
-.dropdown-menu > .disabled > a:hover,
-.dropdown-menu > .disabled > a:focus {
- color: #777;
-}
-.dropdown-menu > .disabled > a:hover,
-.dropdown-menu > .disabled > a:focus {
- text-decoration: none;
- background-color: transparent;
- background-image: none;
- cursor: not-allowed;
-}
-.open > .dropdown-menu {
- display: block;
-}
-.open > a {
- outline: 0;
-}
-.dropdown-menu-right {
- left: auto;
- right: 0;
-}
-.dropdown-menu-left {
- left: 0;
- right: auto;
-}
-.dropdown-header {
- display: block;
- padding: 3px 20px;
- font-size: 12px;
- line-height: 1.428571429;
- color: #777;
- white-space: nowrap;
-}
-.dropdown-backdrop {
- position: fixed;
- left: 0;
- right: 0;
- bottom: 0;
- top: 0;
- z-index: 990;
-}
-.pull-right > .dropdown-menu {
- right: 0;
- left: auto;
-}
-.dropup .caret,
-.navbar-fixed-bottom .dropdown .caret {
- border-top: 0;
- border-bottom: 4px solid;
- content: "";
-}
-.dropup .dropdown-menu,
-.navbar-fixed-bottom .dropdown .dropdown-menu {
- top: auto;
- bottom: 100%;
- margin-bottom: 2px;
-}
-
-@media (min-width: 768px) {
- .navbar-right .dropdown-menu {
- left: auto;
- right: 0;
- }
- .navbar-right .dropdown-menu-left {
- left: 0;
- right: auto;
- }
-}
-.btn-group,
-.btn-group-vertical {
- position: relative;
- display: inline-block;
- vertical-align: middle;
-}
-.btn-group > .btn,
-.btn-group-vertical > .btn {
- position: relative;
- float: left;
-}
-.btn-group > .btn:hover,
-.btn-group-vertical > .btn:hover,
-.btn-group > .btn:focus,
-.btn-group-vertical > .btn:focus,
-.btn-group > .btn:active,
-.btn-group-vertical > .btn:active,
-.btn-group > .btn.active,
-.btn-group-vertical > .btn.active {
- z-index: 2;
-}
-.btn-group .btn + .btn,
-.btn-group .btn + .btn-group,
-.btn-group .btn-group + .btn,
-.btn-group .btn-group + .btn-group {
- margin-left: -1px;
-}
-.btn-toolbar {
- margin-left: -5px;
-}
-.btn-toolbar .btn-group,
-.btn-toolbar .input-group {
- float: left;
-}
-.btn-toolbar > .btn,
-.btn-toolbar > .btn-group,
-.btn-toolbar > .input-group {
- margin-left: 5px;
-}
-.btn-group > .btn:not(:first-child):not(:last-child):not(.dropdown-toggle) {
- border-radius: 0;
-}
-.btn-group > .btn:first-child {
- margin-left: 0;
-}
-.btn-group > .btn:first-child:not(:last-child):not(.dropdown-toggle) {
- border-bottom-right-radius: 0;
- border-top-right-radius: 0;
-}
-.btn-group > .btn:last-child:not(:first-child),
-.btn-group > .dropdown-toggle:not(:first-child) {
- border-bottom-left-radius: 0;
- border-top-left-radius: 0;
-}
-.btn-group > .btn-group {
- float: left;
-}
-.btn-group > .btn-group:not(:first-child):not(:last-child) > .btn {
- border-radius: 0;
-}
-.btn-group > .btn-group:first-child > .btn:last-child,
-.btn-group > .btn-group:first-child > .dropdown-toggle {
- border-bottom-right-radius: 0;
- border-top-right-radius: 0;
-}
-.btn-group > .btn-group:last-child > .btn:first-child {
- border-bottom-left-radius: 0;
- border-top-left-radius: 0;
-}
-.btn-group .dropdown-toggle:active,
-.btn-group.open .dropdown-toggle {
- outline: 0;
-}
-.btn-group > .btn + .dropdown-toggle {
- padding-left: 8px;
- padding-right: 8px;
-}
-.btn-group > .btn-lg + .dropdown-toggle {
- padding-left: 12px;
- padding-right: 12px;
-}
-.btn-group.open .dropdown-toggle {
- -webkit-box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);
- box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);
-}
-.btn-group.open .dropdown-toggle.btn-link {
- -webkit-box-shadow: none;
- box-shadow: none;
-}
-.btn .caret {
- margin-left: 0;
-}
-.btn-lg .caret {
- border-width: 5px 5px 0;
- border-bottom-width: 0;
-}
-.dropup .btn-lg .caret {
- border-width: 0 5px 5px;
-}
-.btn-group-vertical > .btn,
-.btn-group-vertical > .btn-group,
-.btn-group-vertical > .btn-group > .btn {
- display: block;
- float: none;
- width: 100%;
- max-width: 100%;
-}
-.btn-group-vertical > .btn-group > .btn {
- float: none;
-}
-.btn-group-vertical > .btn + .btn,
-.btn-group-vertical > .btn + .btn-group,
-.btn-group-vertical > .btn-group + .btn,
-.btn-group-vertical > .btn-group + .btn-group {
- margin-top: -1px;
- margin-left: 0;
-}
-.btn-group-vertical > .btn:not(:first-child):not(:last-child) {
- border-radius: 0;
-}
-.btn-group-vertical > .btn:first-child:not(:last-child) {
- border-top-right-radius: 4px;
- border-bottom-right-radius: 0;
- border-bottom-left-radius: 0;
-}
-.btn-group-vertical > .btn:last-child:not(:first-child) {
- border-bottom-left-radius: 4px;
- border-top-right-radius: 0;
- border-top-left-radius: 0;
-}
-.btn-group-vertical > .btn-group:not(:first-child):not(:last-child) > .btn {
- border-radius: 0;
-}
-.btn-group-vertical > .btn-group:first-child:not(:last-child) > .btn:last-child,
-.btn-group-vertical
- > .btn-group:first-child:not(:last-child)
- > .dropdown-toggle {
- border-bottom-right-radius: 0;
- border-bottom-left-radius: 0;
-}
-.btn-group-vertical
- > .btn-group:last-child:not(:first-child)
- > .btn:first-child {
- border-top-right-radius: 0;
- border-top-left-radius: 0;
-}
-.btn-group-justified {
- display: table;
- width: 100%;
- table-layout: fixed;
- border-collapse: separate;
-}
-.btn-group-justified > .btn,
-.btn-group-justified > .btn-group {
- float: none;
- display: table-cell;
- width: 1%;
-}
-.btn-group-justified > .btn-group .btn {
- width: 100%;
-}
-.btn-group-justified > .btn-group .dropdown-menu {
- left: auto;
-}
-[data-toggle="buttons"] > .btn input[type="radio"],
-[data-toggle="buttons"] > .btn-group > .btn input[type="radio"],
-[data-toggle="buttons"] > .btn input[type="checkbox"],
-[data-toggle="buttons"] > .btn-group > .btn input[type="checkbox"] {
- position: absolute;
- clip: rect(0, 0, 0, 0);
- pointer-events: none;
-}
-.input-group {
- position: relative;
- display: table;
- border-collapse: separate;
-}
-.input-group[class*="col-"] {
- float: none;
- padding-left: 0;
- padding-right: 0;
-}
-.input-group .form-control {
- position: relative;
- z-index: 2;
- float: left;
- width: 100%;
- margin-bottom: 0;
-}
-.input-group-lg > .form-control,
-.input-group-lg > .input-group-addon,
-.input-group-lg > .input-group-btn > .btn {
- height: 46px;
- padding: 10px 16px;
- font-size: 18px;
- line-height: 1.33;
- border-radius: 6px;
-}
-select.input-group-lg > .form-control,
-select.input-group-lg > .input-group-addon,
-select.input-group-lg > .input-group-btn > .btn {
- height: 46px;
- line-height: 46px;
-}
-textarea.input-group-lg > .form-control,
-textarea.input-group-lg > .input-group-addon,
-textarea.input-group-lg > .input-group-btn > .btn,
-select[multiple].input-group-lg > .form-control,
-select[multiple].input-group-lg > .input-group-addon,
-select[multiple].input-group-lg > .input-group-btn > .btn {
- height: auto;
-}
-.input-group-sm > .form-control,
-.input-group-sm > .input-group-addon,
-.input-group-sm > .input-group-btn > .btn {
- height: 30px;
- padding: 5px 10px;
- font-size: 12px;
- line-height: 1.5;
- border-radius: 3px;
-}
-select.input-group-sm > .form-control,
-select.input-group-sm > .input-group-addon,
-select.input-group-sm > .input-group-btn > .btn {
- height: 30px;
- line-height: 30px;
-}
-textarea.input-group-sm > .form-control,
-textarea.input-group-sm > .input-group-addon,
-textarea.input-group-sm > .input-group-btn > .btn,
-select[multiple].input-group-sm > .form-control,
-select[multiple].input-group-sm > .input-group-addon,
-select[multiple].input-group-sm > .input-group-btn > .btn {
- height: auto;
-}
-.input-group-addon,
-.input-group-btn,
-.input-group .form-control {
- display: table-cell;
-}
-.input-group-addon:not(:first-child):not(:last-child),
-.input-group-btn:not(:first-child):not(:last-child),
-.input-group .form-control:not(:first-child):not(:last-child) {
- border-radius: 0;
-}
-.input-group-addon,
-.input-group-btn {
- width: 1%;
- white-space: nowrap;
- vertical-align: middle;
-}
-.input-group-addon {
- padding: 6px 12px;
- font-size: 14px;
- font-weight: normal;
- line-height: 1;
- color: #555;
- text-align: center;
- background-color: #eee;
- border: 1px solid #ccc;
- border-radius: 4px;
-}
-.input-group-addon.input-sm {
- padding: 5px 10px;
- font-size: 12px;
- border-radius: 3px;
-}
-.input-group-addon.input-lg {
- padding: 10px 16px;
- font-size: 18px;
- border-radius: 6px;
-}
-.input-group-addon input[type="radio"],
-.input-group-addon input[type="checkbox"] {
- margin-top: 0;
-}
-.input-group .form-control:first-child,
-.input-group-addon:first-child,
-.input-group-btn:first-child > .btn,
-.input-group-btn:first-child > .btn-group > .btn,
-.input-group-btn:first-child > .dropdown-toggle,
-.input-group-btn:last-child > .btn:not(:last-child):not(.dropdown-toggle),
-.input-group-btn:last-child > .btn-group:not(:last-child) > .btn {
- border-bottom-right-radius: 0;
- border-top-right-radius: 0;
-}
-.input-group-addon:first-child {
- border-right: 0;
-}
-.input-group .form-control:last-child,
-.input-group-addon:last-child,
-.input-group-btn:last-child > .btn,
-.input-group-btn:last-child > .btn-group > .btn,
-.input-group-btn:last-child > .dropdown-toggle,
-.input-group-btn:first-child > .btn:not(:first-child),
-.input-group-btn:first-child > .btn-group:not(:first-child) > .btn {
- border-bottom-left-radius: 0;
- border-top-left-radius: 0;
-}
-.input-group-addon:last-child {
- border-left: 0;
-}
-.input-group-btn {
- position: relative;
- font-size: 0;
- white-space: nowrap;
-}
-.input-group-btn > .btn {
- position: relative;
-}
-.input-group-btn > .btn + .btn {
- margin-left: -1px;
-}
-.input-group-btn > .btn:hover,
-.input-group-btn > .btn:focus,
-.input-group-btn > .btn:active {
- z-index: 2;
-}
-.input-group-btn:first-child > .btn,
-.input-group-btn:first-child > .btn-group {
- margin-right: -1px;
-}
-.input-group-btn:last-child > .btn,
-.input-group-btn:last-child > .btn-group {
- margin-left: -1px;
-}
-.nav {
- margin-bottom: 0;
- padding-left: 0;
- list-style: none;
-}
-.nav > li {
- position: relative;
- display: block;
-}
-.nav > li > a {
- position: relative;
- display: block;
- padding: 10px 15px;
-}
-.nav > li > a:hover,
-.nav > li:focus-within > a,
-.nav > li > a:focus {
- text-decoration: none;
- background-color: #eee;
-}
-.nav > li.disabled > a {
- color: #777;
-}
-.nav > li.disabled > a:hover,
-.nav > li.disabled > a:focus {
- color: #777;
- text-decoration: none;
- background-color: transparent;
- cursor: not-allowed;
-}
-.nav .open > a,
-.nav .open > a:hover,
-.nav .open > a:focus {
- background-color: #eee;
- border-color: #017cee;
-}
-.nav .nav-divider {
- height: 1px;
- margin: 9px 0;
- overflow: hidden;
- background-color: #e5e5e5;
-}
-.nav > li > a > img {
- max-width: none;
-}
-.nav-tabs {
- border-bottom: 1px solid #ddd;
-}
-.nav-tabs > li {
- float: left;
- margin-bottom: -1px;
-}
-.nav-tabs > li > a {
- margin-right: 2px;
- line-height: 1.428571429;
- border: 1px solid transparent;
- border-radius: 4px 4px 0 0;
-}
-.nav-tabs > li > a:hover {
- border-color: #eee #eee #ddd;
-}
-.nav-tabs > li.active > a,
-.nav-tabs > li.active > a:hover,
-.nav-tabs > li.active > a:focus {
- color: #555;
- background-color: #fafafa;
- border: 1px solid #ddd;
- border-bottom-color: transparent;
- cursor: default;
-}
-.nav-tabs.nav-justified {
- width: 100%;
- border-bottom: 0;
-}
-.nav-tabs.nav-justified > li {
- float: none;
-}
-.nav-tabs.nav-justified > li > a {
- border-radius: 4px;
- margin-right: 0;
- margin-bottom: 5px;
- text-align: center;
-}
-.nav-tabs.nav-justified > .dropdown .dropdown-menu {
- top: auto;
- left: auto;
-}
-
-@media (min-width: 768px) {
- .nav-tabs.nav-justified > li {
- display: table-cell;
- width: 1%;
- }
- .nav-tabs.nav-justified > li > a {
- margin-bottom: 0;
- }
-}
-.nav-tabs.nav-justified > .active > a,
-.nav-tabs.nav-justified > .active > a:hover,
-.nav-tabs.nav-justified > .active > a:focus {
- border: 1px solid #ddd;
-}
-
-@media (min-width: 768px) {
- .nav-tabs.nav-justified > li > a {
- border-bottom: 1px solid #ddd;
- border-radius: 4px 4px 0 0;
- }
- .nav-tabs.nav-justified > .active > a,
- .nav-tabs.nav-justified > .active > a:hover,
- .nav-tabs.nav-justified > .active > a:focus {
- border-bottom-color: #fafafa;
- }
-}
-.nav-pills > li {
- float: left;
-}
-.nav-pills > li > a {
- border-radius: 4px;
- color: #365f84;
-}
-.nav-pills > li > a:hover,
-.nav-pills > li > a:focus {
- color: #365f84;
- background-color: #c0defb;
-}
-.nav-pills > li + li {
- margin-left: 2px;
-}
-.nav-pills > li.active > a,
-.nav-pills > li.active > a:hover,
-.nav-pills > li.active > a:focus {
- color: #fff;
- background-color: #365f84;
-}
-.nav-stacked > li {
- float: none;
-}
-.nav-stacked > li + li {
- margin-top: 2px;
- margin-left: 0;
-}
-.nav-justified {
- width: 100%;
-}
-.nav-justified > li {
- float: none;
-}
-.nav-justified > li > a {
- text-align: center;
- margin-bottom: 5px;
-}
-.nav-justified > .dropdown .dropdown-menu {
- top: auto;
- left: auto;
-}
-
-@media (min-width: 768px) {
- .nav-justified > li {
- display: table-cell;
- width: 1%;
- }
- .nav-justified > li > a {
- margin-bottom: 0;
- }
-}
-.nav-tabs-justified {
- border-bottom: 0;
-}
-.nav-tabs-justified > li > a {
- margin-right: 0;
- border-radius: 4px;
-}
-.nav-tabs-justified > .active > a,
-.nav-tabs-justified > .active > a:hover,
-.nav-tabs-justified > .active > a:focus {
- border: 1px solid #ddd;
-}
-
-@media (min-width: 768px) {
- .nav-tabs-justified > li > a {
- border-bottom: 1px solid #ddd;
- border-radius: 4px 4px 0 0;
- }
- .nav-tabs-justified > .active > a,
- .nav-tabs-justified > .active > a:hover,
- .nav-tabs-justified > .active > a:focus {
- border-bottom-color: #fafafa;
- }
-}
-.tab-content > .tab-pane {
- display: none;
- visibility: hidden;
-}
-.tab-content > .active {
- display: block;
- visibility: visible;
-}
-.nav-tabs .dropdown-menu {
- margin-top: -1px;
- border-top-right-radius: 0;
- border-top-left-radius: 0;
-}
-.navbar {
- position: relative;
- min-height: 50px;
- background-color: #fff;
- margin: 0;
- border: 0;
- box-shadow: 0 0 16px rgba(29, 32, 25, 0.25);
-}
-
-@media (min-width: 768px) {
- .navbar {
- border-radius: 4px;
- }
-}
-
-@media (min-width: 768px) {
- .navbar-header {
- float: left;
- }
-}
-.navbar-collapse {
- overflow-x: visible;
- padding-right: 15px;
- padding-left: 15px;
- border-top: 1px solid transparent;
- box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1);
- -webkit-overflow-scrolling: touch;
-}
-.navbar-collapse.in {
- overflow-y: visible;
-}
-
-@media (min-width: 768px) {
- .navbar-collapse {
- width: auto;
- border-top: 0;
- box-shadow: none;
- }
- .navbar-collapse.collapse {
- display: block !important;
- visibility: visible !important;
- height: auto !important;
- padding-bottom: 0;
- overflow: visible !important;
- }
- .navbar-collapse.in {
- overflow-y: visible;
- }
- .navbar-fixed-top .navbar-collapse,
- .navbar-static-top .navbar-collapse,
- .navbar-fixed-bottom .navbar-collapse {
- padding-left: 0;
- padding-right: 0;
- }
-}
-.navbar-fixed-top .navbar-collapse,
-.navbar-fixed-bottom .navbar-collapse {
- max-height: none;
-}
-
-@media (max-device-width: 480px) and (orientation: landscape) {
- .navbar-fixed-top .navbar-collapse,
- .navbar-fixed-bottom .navbar-collapse {
- max-height: 200px;
- }
-}
-.container > .navbar-header,
-.container-fluid > .navbar-header,
-.container > .navbar-collapse,
-.container-fluid > .navbar-collapse {
- margin-right: -15px;
- margin-left: -15px;
-}
-
-@media (min-width: 768px) {
- .container > .navbar-header,
- .container-fluid > .navbar-header,
- .container > .navbar-collapse,
- .container-fluid > .navbar-collapse {
- margin-right: 0;
- margin-left: 0;
- }
-}
-.navbar-static-top {
- z-index: 1000;
- border-width: 0 0 1px;
-}
-
-@media (min-width: 768px) {
- .navbar-static-top {
- border-radius: 0;
- }
-}
-.navbar-fixed-top,
-.navbar-fixed-bottom {
- position: fixed;
- right: 0;
- left: 0;
- z-index: 1030;
-}
-
-@media (min-width: 768px) {
- .navbar-fixed-top,
- .navbar-fixed-bottom {
- border-radius: 0;
- }
-}
-.navbar-fixed-top {
- top: 0;
- border-width: 0 0 1px;
-}
-.navbar-fixed-bottom {
- bottom: 0;
- margin-bottom: 0;
- border-width: 1px 0 0;
-}
-.navbar-brand {
- float: left;
- padding: 10px 15px;
- height: 50px;
-}
-
-@media (min-width: 768px) {
- .navbar > .container .navbar-brand,
- .navbar > .container-fluid .navbar-brand {
- margin-left: -15px;
- }
-}
-.navbar-toggle {
- position: relative;
- float: right;
- margin-right: 15px;
- padding: 9px 10px;
- margin-top: 8px;
- margin-bottom: 8px;
- background-color: transparent;
- background-image: none;
- border: 1px solid transparent;
- border-radius: 4px;
-}
-.navbar-toggle:focus {
- outline: 0;
-}
-.navbar-toggle .icon-bar {
- display: block;
- width: 22px;
- height: 2px;
- border-radius: 1px;
- background: currentColor;
-}
-.navbar-toggle .icon-bar + .icon-bar {
- margin-top: 4px;
-}
-
-@media (min-width: 768px) {
- .navbar-toggle {
- display: none;
- }
-}
-.navbar-nav {
- margin: 7.5px -15px;
-}
-.navbar-nav > li > a {
- padding-top: 10px;
- padding-bottom: 10px;
- line-height: 20px;
-}
-
-@media (max-width: 767px) {
- .navbar-nav .dropdown:hover > .dropdown-menu,
- .navbar-nav .dropdown:focus-within > .dropdown-menu {
- position: static;
- float: none;
- width: auto;
- margin-top: 0;
- background-color: transparent;
- border: 0;
- box-shadow: none;
- }
- .navbar-nav .dropdown:hover > .dropdown-menu > li > a,
- .navbar-nav .dropdown:hover > .dropdown-menu > li > form > button,
- .navbar-nav .dropdown:hover > .dropdown-menu .dropdown-header,
- .navbar-nav .dropdown:focus-within > .dropdown-menu > li > a,
- .navbar-nav .dropdown:focus-within > .dropdown-menu > li > form > button,
- .navbar-nav .dropdown:focus-within > .dropdown-menu .dropdown-header {
- padding: 5px 15px 5px 25px;
- }
- .navbar-nav .dropdown:hover > .dropdown-menu > li > a,
- .navbar-nav .dropdown:hover > .dropdown-menu > li > form > button,
- .navbar-nav .dropdown:focus-within > .dropdown-menu > li > a,
- .navbar-nav .dropdown:focus-within > .dropdown-menu > li > form > button {
- line-height: 20px;
- }
- .navbar-nav .dropdown:hover > .dropdown-menu > li > a:hover,
- .navbar-nav .dropdown:hover > .dropdown-menu > li > a:focus,
- .navbar-nav .dropdown:focus-within > .dropdown-menu > li > a:hover,
- .navbar-nav .dropdown:focus-within > .dropdown-menu > li > a:focus {
- background-image: none;
- }
-}
-
-@media (min-width: 768px) {
- .navbar-nav {
- float: left;
- margin: 0;
- }
- .navbar-nav > li {
- float: left;
- }
- .navbar-nav > li > a {
- padding-top: 20px;
- padding-bottom: 20px;
- }
-}
-.navbar-form {
- margin-left: -15px;
- margin-right: -15px;
- padding: 10px 15px;
- border-top: 1px solid transparent;
- border-bottom: 1px solid transparent;
- -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1),
- 0 1px 0 rgba(255, 255, 255, 0.1);
- box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1),
- 0 1px 0 rgba(255, 255, 255, 0.1);
- margin-top: 8px;
- margin-bottom: 8px;
-}
-
-@media (min-width: 768px) {
- .navbar-form .form-group {
- display: inline-block;
- margin-bottom: 0;
- vertical-align: middle;
- }
- .navbar-form .form-control {
- display: inline-block;
- width: auto;
- vertical-align: middle;
- }
- .navbar-form .form-control-static {
- display: inline-block;
- }
- .navbar-form .input-group {
- display: inline-table;
- vertical-align: middle;
- }
- .navbar-form .input-group .input-group-addon,
- .navbar-form .input-group .input-group-btn,
- .navbar-form .input-group .form-control {
- width: auto;
- }
- .navbar-form .input-group > .form-control {
- width: 100%;
- }
- .navbar-form .control-label {
- margin-bottom: 0;
- vertical-align: middle;
- }
- .navbar-form .radio,
- .navbar-form .checkbox {
- display: inline-block;
- margin-top: 0;
- margin-bottom: 0;
- vertical-align: middle;
- }
- .navbar-form .radio label,
- .navbar-form .checkbox label {
- padding-left: 0;
- }
- .navbar-form .radio input[type="radio"],
- .navbar-form .checkbox input[type="checkbox"] {
- position: relative;
- margin-left: 0;
- }
- .navbar-form .has-feedback .form-control-feedback {
- top: 0;
- }
-}
-
-@media (max-width: 767px) {
- .navbar-form .form-group {
- margin-bottom: 5px;
- }
- .navbar-form .form-group:last-child {
- margin-bottom: 0;
- }
-}
-
-@media (min-width: 768px) {
- .navbar-form {
- width: auto;
- border: 0;
- margin-left: 0;
- margin-right: 0;
- padding-top: 0;
- padding-bottom: 0;
- -webkit-box-shadow: none;
- box-shadow: none;
- }
-}
-.navbar-nav > li > .dropdown-menu {
- margin-top: 0;
- border-top-right-radius: 0;
- border-top-left-radius: 0;
-}
-.navbar-fixed-bottom .navbar-nav > li > .dropdown-menu {
- border-radius: 4px 4px 0 0;
-}
-.navbar-btn {
- margin-top: 8px;
- margin-bottom: 8px;
-}
-.navbar-btn.btn-sm {
- margin-top: 10px;
- margin-bottom: 10px;
-}
-.navbar-btn.btn-xs {
- margin-top: 14px;
- margin-bottom: 14px;
-}
-.navbar-text {
- margin-top: 15px;
- margin-bottom: 15px;
-}
-
-@media (min-width: 768px) {
- .navbar-text {
- float: left;
- margin-left: 15px;
- margin-right: 15px;
- }
-}
-
-@media (min-width: 768px) {
- .navbar-left {
- float: left !important;
- }
- .navbar-right {
- float: right !important;
- margin-right: -15px;
- }
- .navbar-right ~ .navbar-right {
- margin-right: 0;
- }
-}
-.navbar-default {
- background-color: #ffb400;
- border-color: #de9d00;
-}
-.navbar-default .navbar-brand {
- color: #007a87;
-}
-.navbar-default .navbar-brand:hover,
-.navbar-default .navbar-brand:focus {
- color: #004c54;
- background-color: transparent;
-}
-.navbar-default .navbar-text {
- color: #777;
-}
-.navbar-default .navbar-nav > li > a {
- color: #007a87;
-}
-.navbar-default .navbar-nav > li > a:hover,
-.navbar-default .navbar-nav > li > a:focus {
- color: #51504f;
- background-color: transparent;
-}
-.navbar-default .navbar-nav > .active > a,
-.navbar-default .navbar-nav > .active > a:hover,
-.navbar-default .navbar-nav > .active > a:focus {
- color: #555;
- background-color: #de9d00;
-}
-.navbar-default .navbar-nav > .disabled > a,
-.navbar-default .navbar-nav > .disabled > a:hover,
-.navbar-default .navbar-nav > .disabled > a:focus {
- color: #ccc;
- background-color: transparent;
-}
-.navbar-default .navbar-toggle {
- border-color: #ddd;
-}
-.navbar-default .navbar-toggle:hover,
-.navbar-default .navbar-toggle:focus {
- background-color: #ddd;
-}
-.navbar-default .navbar-toggle .icon-bar {
- background-color: #888;
-}
-.navbar-default .navbar-collapse,
-.navbar-default .navbar-form {
- border-color: #de9d00;
-}
-.navbar-default .navbar-nav > .open > a,
-.navbar-default .navbar-nav > .open > a:hover,
-.navbar-default .navbar-nav > .open > a:focus {
- background-color: #de9d00;
- color: #555;
-}
-
-@media (max-width: 767px) {
- .navbar-default .navbar-nav .open .dropdown-menu > li > a {
- color: #007a87;
- }
- .navbar-default .navbar-nav .open .dropdown-menu > li > a:hover,
- .navbar-default .navbar-nav .open .dropdown-menu > li > a:focus {
- color: #51504f;
- background-color: transparent;
- }
- .navbar-default .navbar-nav .open .dropdown-menu > .active > a,
- .navbar-default .navbar-nav .open .dropdown-menu > .active > a:hover,
- .navbar-default .navbar-nav .open .dropdown-menu > .active > a:focus {
- color: #555;
- background-color: #de9d00;
- }
- .navbar-default .navbar-nav .open .dropdown-menu > .disabled > a,
- .navbar-default .navbar-nav .open .dropdown-menu > .disabled > a:hover,
- .navbar-default .navbar-nav .open .dropdown-menu > .disabled > a:focus {
- color: #ccc;
- background-color: transparent;
- }
-}
-.navbar-default .navbar-link {
- color: #007a87;
-}
-.navbar-default .navbar-link:hover {
- color: #51504f;
-}
-.navbar-default .btn-link {
- color: #007a87;
-}
-.navbar-default .btn-link:hover,
-.navbar-default .btn-link:focus {
- color: #51504f;
-}
-.navbar-default .btn-link[disabled]:hover,
-fieldset[disabled] .navbar-default .btn-link:hover,
-.navbar-default .btn-link[disabled]:focus,
-fieldset[disabled] .navbar-default .btn-link:focus {
- color: #ccc;
-}
-.navbar-inverse {
- background-color: #007a87;
- border-color: #004c54;
- background-image: none;
-}
-.navbar-inverse .navbar-nav > .active > a,
-.navbar-inverse .navbar-nav > .open > a {
- background-image: none;
-}
-/* stylelint-disable selector-type-no-unknown */
-.dropdown-menu > .active > a,
-ee.dropdown-menu > .active > a:focus,
-.dropdown-menu > .active > a:hover {
- background-image: none;
-}
-/* stylelint-enable selector-type-no-unknown */
-.navbar-inverse .navbar-brand {
- color: #ddd;
-}
-.navbar-inverse .navbar-brand:hover,
-.navbar-inverse .navbar-brand:focus {
- color: #fff;
- background-color: transparent;
-}
-.navbar-inverse .navbar-text {
- color: #ffb400;
-}
-.navbar-inverse .navbar-nav > li > a {
- color: #ddd;
-}
-.navbar-inverse .navbar-nav > li > a:hover,
-.navbar-inverse .navbar-nav > li > a:focus {
- color: #fff;
- background-color: transparent;
-}
-.navbar-inverse .navbar-nav > .active > a,
-.navbar-inverse .navbar-nav > .active > a:hover,
-.navbar-inverse .navbar-nav > .active > a:focus {
- color: #fff;
- background-color: #004c54;
-}
-.navbar-inverse .navbar-nav > .disabled > a,
-.navbar-inverse .navbar-nav > .disabled > a:hover,
-.navbar-inverse .navbar-nav > .disabled > a:focus {
- color: #444;
- background-color: transparent;
-}
-.navbar-inverse .navbar-toggle {
- border-color: #51504f;
-}
-.navbar-inverse .navbar-toggle:hover,
-.navbar-inverse .navbar-toggle:focus {
- background-color: #51504f;
-}
-.navbar-inverse .navbar-toggle .icon-bar {
- background-color: #fff;
-}
-.navbar-inverse .navbar-collapse,
-.navbar-inverse .navbar-form {
- border-color: #005a63;
-}
-.navbar-inverse .navbar-nav > .open > a,
-.navbar-inverse .navbar-nav > .open > a:hover,
-.navbar-inverse .navbar-nav > .open > a:focus {
- background-color: #004c54;
- color: #fff;
-}
-
-@media (max-width: 767px) {
- .navbar-inverse .navbar-nav .open .dropdown-menu > .dropdown-header {
- border-color: #004c54;
- }
- .navbar-inverse .navbar-nav .open .dropdown-menu .divider {
- background-color: #004c54;
- }
- .navbar-inverse .navbar-nav .open .dropdown-menu > li > a {
- color: #ddd;
- }
- .navbar-inverse .navbar-nav .open .dropdown-menu > li > a:hover,
- .navbar-inverse .navbar-nav .open .dropdown-menu > li > a:focus {
- color: #fff;
- background-color: transparent;
- }
- .navbar-inverse .navbar-nav .open .dropdown-menu > .active > a,
- .navbar-inverse .navbar-nav .open .dropdown-menu > .active > a:hover,
- .navbar-inverse .navbar-nav .open .dropdown-menu > .active > a:focus {
- color: #fff;
- background-color: #004c54;
- }
- .navbar-inverse .navbar-nav .open .dropdown-menu > .disabled > a,
- .navbar-inverse .navbar-nav .open .dropdown-menu > .disabled > a:hover,
- .navbar-inverse .navbar-nav .open .dropdown-menu > .disabled > a:focus {
- color: #444;
- background-color: transparent;
- }
-}
-.navbar-inverse .navbar-link {
- color: #ddd;
-}
-.navbar-inverse .navbar-link:hover {
- color: #fff;
-}
-.navbar-inverse .btn-link {
- color: #ddd;
-}
-.navbar-inverse .btn-link:hover,
-.navbar-inverse .btn-link:focus {
- color: #fff;
-}
-.navbar-inverse .btn-link[disabled]:hover,
-fieldset[disabled] .navbar-inverse .btn-link:hover,
-.navbar-inverse .btn-link[disabled]:focus,
-fieldset[disabled] .navbar-inverse .btn-link:focus {
- color: #444;
-}
-.breadcrumb {
- padding: 8px 15px;
- margin-bottom: 20px;
- list-style: none;
- background-color: #f5f5f5;
- border-radius: 4px;
-}
-.breadcrumb > li {
- display: inline-block;
-}
-.breadcrumb > li + li::before {
- content: "/\00a0";
- padding: 0 5px;
- color: #ccc;
-}
-.breadcrumb > .active {
- color: #777;
-}
-.pagination {
- display: inline-block;
- padding-left: 0;
- margin: 20px 0;
- border-radius: 4px;
-}
-.pagination > li {
- display: inline;
-}
-.pagination > li > a,
-.pagination > li > span {
- position: relative;
- float: left;
- padding: 6px 12px;
- line-height: 1.428571429;
- text-decoration: none;
- color: #017cee;
- background-color: #fff;
- border: 1px solid #ddd;
- margin-left: -1px;
-}
-.pagination > li:first-child > a,
-.pagination > li:first-child > span {
- margin-left: 0;
- border-bottom-left-radius: 4px;
- border-top-left-radius: 4px;
-}
-.pagination > li:last-child > a,
-.pagination > li:last-child > span {
- border-bottom-right-radius: 4px;
- border-top-right-radius: 4px;
-}
-.pagination > li > a:hover,
-.pagination > li > span:hover,
-.pagination > li > a:focus,
-.pagination > li > span:focus {
- color: #0cb6ff;
- background-color: #eee;
- border-color: #ddd;
-}
-.pagination > .active > a,
-.pagination > .active > span,
-.pagination > .active > a:hover,
-.pagination > .active > span:hover,
-.pagination > .active > a:focus,
-.pagination > .active > span:focus {
- z-index: 2;
- color: #fff;
- background-color: ##017cee;
- border-color: ##017cee;
- cursor: default;
-}
-.pagination > .disabled > span,
-.pagination > .disabled > span:hover,
-.pagination > .disabled > span:focus,
-.pagination > .disabled > a,
-.pagination > .disabled > a:hover,
-.pagination > .disabled > a:focus {
- color: #777;
- background-color: #fff;
- border-color: #ddd;
- cursor: not-allowed;
-}
-.pagination-lg > li > a,
-.pagination-lg > li > span {
- padding: 10px 16px;
- font-size: 18px;
-}
-.pagination-lg > li:first-child > a,
-.pagination-lg > li:first-child > span {
- border-bottom-left-radius: 6px;
- border-top-left-radius: 6px;
-}
-.pagination-lg > li:last-child > a,
-.pagination-lg > li:last-child > span {
- border-bottom-right-radius: 6px;
- border-top-right-radius: 6px;
-}
-.pagination-sm > li > a,
-.pagination-sm > li > span {
- padding: 5px 10px;
- font-size: 12px;
-}
-.pagination-sm > li:first-child > a,
-.pagination-sm > li:first-child > span {
- border-bottom-left-radius: 3px;
- border-top-left-radius: 3px;
-}
-.pagination-sm > li:last-child > a,
-.pagination-sm > li:last-child > span {
- border-bottom-right-radius: 3px;
- border-top-right-radius: 3px;
-}
-.pager {
- padding-left: 0;
- margin: 20px 0;
- list-style: none;
- text-align: center;
-}
-.pager li {
- display: inline;
-}
-.pager li > a,
-.pager li > span {
- display: inline-block;
- padding: 5px 14px;
- background-color: #fff;
- border: 1px solid #ddd;
- border-radius: 15px;
-}
-.pager li > a:hover,
-.pager li > a:focus {
- text-decoration: none;
- background-color: #eee;
-}
-.pager .next > a,
-.pager .next > span {
- float: right;
-}
-.pager .previous > a,
-.pager .previous > span {
- float: left;
-}
-.pager .disabled > a,
-.pager .disabled > a:hover,
-.pager .disabled > a:focus,
-.pager .disabled > span {
- color: #777;
- background-color: #fff;
- cursor: not-allowed;
-}
-.label {
- display: inline;
- padding: 0.2em 0.6em 0.3em;
- font-size: 75%;
- font-weight: bold;
- line-height: 1;
- color: #51504f;
- text-align: center;
- white-space: nowrap;
- vertical-align: baseline;
- border-radius: 0.25em;
-}
-a.label:hover,
-a.label:focus {
- color: #51504f;
- text-decoration: none;
- cursor: pointer;
-}
-.label:empty {
- display: none;
-}
-.btn .label {
- position: relative;
- top: -1px;
-}
-.label-default {
- background-color: #e2e2e2;
-}
-.label-default[href]:hover,
-.label-default[href]:focus {
- background-color: #cbcbcb;
-}
-.label-primary {
- color: #365f84;
- background-color: #c0defb;
-}
-.label-primary[href]:hover,
-.label-primary[href]:focus {
- color: #365f84;
- background-color: #0cb6ff;
-}
-.label-success {
- color: #366f4c;
- background-color: #bfebd1;
-}
-.label-success[href]:hover,
-.label-success[href]:focus {
- color: #366f4c;
- background-color: #04d659;
-}
-.label-info {
- color: #36787b;
- background-color: #bff1f4;
-}
-.label-info[href]:hover,
-.label-info[href]:focus {
- color: #36787b;
- background-color: #11e1ee;
-}
-.label-warning {
- background-color: #ffb400;
-}
-.label-warning[href]:hover,
-.label-warning[href]:focus {
- background-color: #cc9000;
-}
-.label-danger {
- color: #824840;
- background-color: #f8cec8;
-}
-.label-danger[href]:hover,
-.label-danger[href]:focus {
- color: #824840;
- background-color: #ff7557;
-}
-.badge {
- display: inline-block;
- min-width: 10px;
- padding: 3px 7px;
- font-size: 12px;
- font-weight: bold;
- color: #fff;
- line-height: 1;
- vertical-align: baseline;
- white-space: nowrap;
- text-align: center;
- background-color: #777;
- border-radius: 10px;
-}
-.badge:empty {
- display: none;
-}
-.btn .badge {
- position: relative;
- top: -1px;
-}
-.btn-xs .badge {
- top: 0;
- padding: 1px 5px;
-}
-a.badge:hover,
-a.badge:focus {
- color: #fff;
- text-decoration: none;
- cursor: pointer;
-}
-.list-group-item.active > .badge,
-.nav-pills > .active > a > .badge {
- color: #017cee;
- background-color: #fff;
-}
-.list-group-item > .badge {
- float: right;
-}
-.list-group-item > .badge + .badge {
- margin-right: 5px;
-}
-.nav-pills > li > a > .badge {
- margin-left: 3px;
-}
-.jumbotron {
- padding: 30px 15px;
- margin-bottom: 30px;
- color: inherit;
- background-color: #eee;
-}
-.jumbotron h1,
-.jumbotron .h1 {
- color: inherit;
-}
-.jumbotron p {
- margin-bottom: 15px;
- font-size: 21px;
- font-weight: 200;
-}
-.jumbotron > hr {
- border-top-color: #d5d5d5;
-}
-.container .jumbotron,
-.container-fluid .jumbotron {
- border-radius: 6px;
-}
-.jumbotron .container {
- max-width: 100%;
-}
-
-@media screen and (min-width: 768px) {
- .jumbotron {
- padding: 48px 0;
- }
- .container .jumbotron,
- .container-fluid .jumbotron {
- padding-left: 60px;
- padding-right: 60px;
- }
- .jumbotron h1,
- .jumbotron .h1 {
- font-size: 63px;
- }
-}
-.thumbnail {
- display: block;
- padding: 4px;
- margin-bottom: 20px;
- line-height: 1.428571429;
- background-color: #fafafa;
- border: 1px solid #ddd;
- border-radius: 4px;
- -webkit-transition: border 0.2s ease-in-out;
- -o-transition: border 0.2s ease-in-out;
- transition: border 0.2s ease-in-out;
-}
-.thumbnail > img,
-.thumbnail a > img {
- margin-left: auto;
- margin-right: auto;
-}
-a.thumbnail:hover,
-a.thumbnail:focus,
-a.thumbnail.active {
- border-color: #017cee;
-}
-.thumbnail .caption {
- padding: 9px;
- color: #51504f;
-}
-.alert {
- padding: 16px;
- margin-bottom: 16px;
- border: 0;
- border-radius: 4px;
-}
-.alert h4 {
- margin-top: 0;
- color: inherit;
-}
-.alert .alert-link {
- font-weight: bold;
-}
-.alert > p,
-.alert > ul {
- margin-bottom: 0;
-}
-.alert > p + p {
- margin-top: 5px;
-}
-.alert-dismissable,
-.alert-dismissible {
- padding-right: 35px;
-}
-.alert-dismissable .close,
-.alert-dismissible .close {
- position: relative;
- top: -2px;
- right: -21px;
- color: inherit;
-}
-.alert-success {
- background-color: #bfebd1;
- color: #1b8e49;
-}
-.alert-success hr {
- border-top-color: #bfebd1;
-}
-.alert-success .alert-link {
- color: #366f4c;
-}
-.alert-info {
- background-color: #bff1f4;
- color: #36787b;
-}
-.alert-info hr {
- border-top-color: #a6e1ec;
-}
-.alert-info .alert-link {
- color: #245269;
-}
-.alert-warning {
- background-color: #fcf8e3;
- color: #8a6d3b;
-}
-.alert-warning hr {
- border-top-color: #f7e1b5;
-}
-.alert-warning .alert-link {
- color: #66512c;
-}
-.alert-danger {
- background-color: #f2dede;
- color: #e43921;
-}
-.alert-danger hr {
- border-top-color: #e4b9c0;
-}
-.alert-danger .alert-link {
- color: #843534;
-}
-.alert-message {
- background-color: #d9edf7;
- color: #31708f;
-}
-.alert-message hr {
- border-top-color: #a6e1ec;
-}
-.alert-message .alert-link {
- color: #245269;
-}
-.alert-error {
- background-color: #f2dede;
- color: #e43921;
-}
-.alert-error hr {
- border-top-color: #e4b9c0;
-}
-.alert-error .alert-link {
- color: #843534;
-}
-
-@-webkit-keyframes progress-bar-stripes {
- from {
- background-position: 40px 0;
- }
- to {
- background-position: 0 0;
- }
-}
-
-@keyframes progress-bar-stripes {
- from {
- background-position: 40px 0;
- }
- to {
- background-position: 0 0;
- }
-}
-.progress {
- overflow: hidden;
- height: 20px;
- margin-bottom: 20px;
- background-color: #f5f5f5;
- border-radius: 4px;
- -webkit-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1);
- box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1);
-}
-.progress-bar {
- float: left;
- width: 0%;
- height: 100%;
- font-size: 12px;
- line-height: 20px;
- color: #fff;
- text-align: center;
- background-color: #017cee;
- -webkit-box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.15);
- box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.15);
- -webkit-transition: width 0.6s ease;
- -o-transition: width 0.6s ease;
- transition: width 0.6s ease;
-}
-.progress-striped .progress-bar,
-.progress-bar-striped {
- background-image: -webkit-linear-gradient(
- 45deg,
- rgba(255, 255, 255, 0.15) 25%,
- transparent 25%,
- transparent 50%,
- rgba(255, 255, 255, 0.15) 50%,
- rgba(255, 255, 255, 0.15) 75%,
- transparent 75%,
- transparent
- );
- background-image: -o-linear-gradient(
- 45deg,
- rgba(255, 255, 255, 0.15) 25%,
- transparent 25%,
- transparent 50%,
- rgba(255, 255, 255, 0.15) 50%,
- rgba(255, 255, 255, 0.15) 75%,
- transparent 75%,
- transparent
- );
- background-image: linear-gradient(
- 45deg,
- rgba(255, 255, 255, 0.15) 25%,
- transparent 25%,
- transparent 50%,
- rgba(255, 255, 255, 0.15) 50%,
- rgba(255, 255, 255, 0.15) 75%,
- transparent 75%,
- transparent
- );
- background-size: 40px 40px;
-}
-.progress.active .progress-bar,
-.progress-bar.active {
- -webkit-animation: progress-bar-stripes 2s linear infinite;
- -o-animation: progress-bar-stripes 2s linear infinite;
- animation: progress-bar-stripes 2s linear infinite;
-}
-.progress-bar-success {
- background-color: #00ad46;
-}
-.progress-striped .progress-bar-success {
- background-image: -webkit-linear-gradient(
- 45deg,
- rgba(255, 255, 255, 0.15) 25%,
- transparent 25%,
- transparent 50%,
- rgba(255, 255, 255, 0.15) 50%,
- rgba(255, 255, 255, 0.15) 75%,
- transparent 75%,
- transparent
- );
- background-image: -o-linear-gradient(
- 45deg,
- rgba(255, 255, 255, 0.15) 25%,
- transparent 25%,
- transparent 50%,
- rgba(255, 255, 255, 0.15) 50%,
- rgba(255, 255, 255, 0.15) 75%,
- transparent 75%,
- transparent
- );
- background-image: linear-gradient(
- 45deg,
- rgba(255, 255, 255, 0.15) 25%,
- transparent 25%,
- transparent 50%,
- rgba(255, 255, 255, 0.15) 50%,
- rgba(255, 255, 255, 0.15) 75%,
- transparent 75%,
- transparent
- );
-}
-.progress-bar-info {
- background-color: #00d1c1;
-}
-.progress-striped .progress-bar-info {
- background-image: -webkit-linear-gradient(
- 45deg,
- rgba(255, 255, 255, 0.15) 25%,
- transparent 25%,
- transparent 50%,
- rgba(255, 255, 255, 0.15) 50%,
- rgba(255, 255, 255, 0.15) 75%,
- transparent 75%,
- transparent
- );
- background-image: -o-linear-gradient(
- 45deg,
- rgba(255, 255, 255, 0.15) 25%,
- transparent 25%,
- transparent 50%,
- rgba(255, 255, 255, 0.15) 50%,
- rgba(255, 255, 255, 0.15) 75%,
- transparent 75%,
- transparent
- );
- background-image: linear-gradient(
- 45deg,
- rgba(255, 255, 255, 0.15) 25%,
- transparent 25%,
- transparent 50%,
- rgba(255, 255, 255, 0.15) 50%,
- rgba(255, 255, 255, 0.15) 75%,
- transparent 75%,
- transparent
- );
-}
-.progress-bar-warning {
- background-color: #ffb400;
-}
-.progress-striped .progress-bar-warning {
- background-image: -webkit-linear-gradient(
- 45deg,
- rgba(255, 255, 255, 0.15) 25%,
- transparent 25%,
- transparent 50%,
- rgba(255, 255, 255, 0.15) 50%,
- rgba(255, 255, 255, 0.15) 75%,
- transparent 75%,
- transparent
- );
- background-image: -o-linear-gradient(
- 45deg,
- rgba(255, 255, 255, 0.15) 25%,
- transparent 25%,
- transparent 50%,
- rgba(255, 255, 255, 0.15) 50%,
- rgba(255, 255, 255, 0.15) 75%,
- transparent 75%,
- transparent
- );
- background-image: linear-gradient(
- 45deg,
- rgba(255, 255, 255, 0.15) 25%,
- transparent 25%,
- transparent 50%,
- rgba(255, 255, 255, 0.15) 50%,
- rgba(255, 255, 255, 0.15) 75%,
- transparent 75%,
- transparent
- );
-}
-.progress-bar-danger {
- background-color: #ff5a5f;
-}
-.progress-striped .progress-bar-danger {
- background-image: -webkit-linear-gradient(
- 45deg,
- rgba(255, 255, 255, 0.15) 25%,
- transparent 25%,
- transparent 50%,
- rgba(255, 255, 255, 0.15) 50%,
- rgba(255, 255, 255, 0.15) 75%,
- transparent 75%,
- transparent
- );
- background-image: -o-linear-gradient(
- 45deg,
- rgba(255, 255, 255, 0.15) 25%,
- transparent 25%,
- transparent 50%,
- rgba(255, 255, 255, 0.15) 50%,
- rgba(255, 255, 255, 0.15) 75%,
- transparent 75%,
- transparent
- );
- background-image: linear-gradient(
- 45deg,
- rgba(255, 255, 255, 0.15) 25%,
- transparent 25%,
- transparent 50%,
- rgba(255, 255, 255, 0.15) 50%,
- rgba(255, 255, 255, 0.15) 75%,
- transparent 75%,
- transparent
- );
-}
-.media {
- margin-top: 15px;
-}
-.media:first-child {
- margin-top: 0;
-}
-.media,
-.media-body {
- zoom: 1;
- overflow: hidden;
-}
-.media-object {
- display: block;
-}
-.media-right,
-.media > .pull-right {
- padding-left: 10px;
-}
-.media-left,
-.media > .pull-left {
- padding-right: 10px;
-}
-.media-left,
-.media-right,
-.media-body {
- display: table-cell;
- vertical-align: top;
-}
-.media-middle {
- vertical-align: middle;
-}
-.media-bottom {
- vertical-align: bottom;
-}
-.media-heading {
- margin-top: 0;
- margin-bottom: 5px;
-}
-.media-list {
- padding-left: 0;
- list-style: none;
-}
-.list-group {
- margin-bottom: 20px;
- padding-left: 0;
-}
-.list-group-item {
- position: relative;
- display: block;
- padding: 10px 15px;
- margin-bottom: -1px;
- background-color: #fff;
- border: 1px solid #ddd;
-}
-.list-group-item:first-child {
- border-top-right-radius: 4px;
- border-top-left-radius: 4px;
-}
-.list-group-item:last-child {
- margin-bottom: 0;
- border-bottom-right-radius: 4px;
- border-bottom-left-radius: 4px;
-}
-a.list-group-item {
- color: #555;
-}
-a.list-group-item .list-group-item-heading {
- color: #51504f;
-}
-a.list-group-item:hover,
-a.list-group-item:focus {
- text-decoration: none;
- color: #555;
- background-color: #f5f5f5;
-}
-.list-group-item.disabled,
-.list-group-item.disabled:hover,
-.list-group-item.disabled:focus {
- background-color: #eee;
- color: #777;
- cursor: not-allowed;
-}
-.list-group-item.disabled .list-group-item-heading,
-.list-group-item.disabled:hover .list-group-item-heading,
-.list-group-item.disabled:focus .list-group-item-heading {
- color: inherit;
-}
-.list-group-item.disabled .list-group-item-text,
-.list-group-item.disabled:hover .list-group-item-text,
-.list-group-item.disabled:focus .list-group-item-text {
- color: #777;
-}
-.list-group-item.active,
-.list-group-item.active:hover,
-.list-group-item.active:focus {
- z-index: 2;
- color: #fff;
- background-color: #017cee;
- border-color: #017cee;
-}
-.list-group-item.active .list-group-item-heading,
-.list-group-item.active:hover .list-group-item-heading,
-.list-group-item.active:focus .list-group-item-heading,
-.list-group-item.active .list-group-item-heading > small,
-.list-group-item.active:hover .list-group-item-heading > small,
-.list-group-item.active:focus .list-group-item-heading > small,
-.list-group-item.active .list-group-item-heading > .small,
-.list-group-item.active:hover .list-group-item-heading > .small,
-.list-group-item.active:focus .list-group-item-heading > .small {
- color: inherit;
-}
-.list-group-item.active .list-group-item-text,
-.list-group-item.active:hover .list-group-item-text,
-.list-group-item.active:focus .list-group-item-text {
- color: #33ebff;
-}
-.list-group-item-success {
- color: #1b8e49;
- background-color: #bfebd1;
-}
-a.list-group-item-success {
- color: #1b8e49;
-}
-a.list-group-item-success .list-group-item-heading {
- color: inherit;
-}
-a.list-group-item-success:hover,
-a.list-group-item-success:focus {
- color: #1b8e49;
- background-color: #d0e9c6;
-}
-a.list-group-item-success.active,
-a.list-group-item-success.active:hover,
-a.list-group-item-success.active:focus {
- color: #fff;
- background-color: #1b8e49;
- border-color: #1b8e49;
-}
-.list-group-item-info {
- color: #31708f;
- background-color: #d9edf7;
-}
-a.list-group-item-info {
- color: #31708f;
-}
-a.list-group-item-info .list-group-item-heading {
- color: inherit;
-}
-a.list-group-item-info:hover,
-a.list-group-item-info:focus {
- color: #31708f;
- background-color: #c4e3f3;
-}
-a.list-group-item-info.active,
-a.list-group-item-info.active:hover,
-a.list-group-item-info.active:focus {
- color: #fff;
- background-color: #31708f;
- border-color: #31708f;
-}
-.list-group-item-warning {
- color: #8a6d3b;
- background-color: #fcf8e3;
-}
-a.list-group-item-warning {
- color: #8a6d3b;
-}
-a.list-group-item-warning .list-group-item-heading {
- color: inherit;
-}
-a.list-group-item-warning:hover,
-a.list-group-item-warning:focus {
- color: #8a6d3b;
- background-color: #faf2cc;
-}
-a.list-group-item-warning.active,
-a.list-group-item-warning.active:hover,
-a.list-group-item-warning.active:focus {
- color: #fff;
- background-color: #8a6d3b;
- border-color: #8a6d3b;
-}
-.list-group-item-danger {
- color: #e43921;
- background-color: #f2dede;
-}
-a.list-group-item-danger {
- color: #e43921;
-}
-a.list-group-item-danger .list-group-item-heading {
- color: inherit;
-}
-a.list-group-item-danger:hover,
-a.list-group-item-danger:focus {
- color: #e43921;
- background-color: #ebcccc;
-}
-a.list-group-item-danger.active,
-a.list-group-item-danger.active:hover,
-a.list-group-item-danger.active:focus {
- color: #fff;
- background-color: #e43921;
- border-color: #e43921;
-}
-.list-group-item-heading {
- margin-top: 0;
- margin-bottom: 5px;
-}
-.list-group-item-text {
- margin-bottom: 0;
- line-height: 1.3;
-}
-.panel {
- margin-bottom: 20px;
- background-color: #fff;
- border: 2px solid transparent;
- border-radius: 4px;
- box-shadow: none;
-}
-.panel-body {
- padding: 15px;
-}
-.panel-heading {
- padding: 10px 15px;
- border-bottom: 1px solid transparent;
- border-top-right-radius: 2px;
- border-top-left-radius: 2px;
-}
-.panel-heading > .dropdown .dropdown-toggle {
- color: inherit;
-}
-.panel-title {
- margin-top: 0;
- margin-bottom: 0;
- font-size: 16px;
- color: inherit;
-}
-.panel-title > a {
- color: inherit;
-}
-.panel-footer {
- padding: 10px 15px;
- background-color: #f5f5f5;
- border-top: 1px solid #ddd;
- border-bottom-right-radius: 4px;
- border-bottom-left-radius: 4px;
-}
-.panel > .list-group,
-.panel > .panel-collapse > .list-group {
- margin-bottom: 0;
-}
-.panel > .list-group .list-group-item,
-.panel > .panel-collapse > .list-group .list-group-item {
- border-width: 1px 0;
- border-radius: 0;
-}
-.panel > .list-group:first-child .list-group-item:first-child,
-.panel
- > .panel-collapse
- > .list-group:first-child
- .list-group-item:first-child {
- border-top: 0;
- border-top-right-radius: 3px;
- border-top-left-radius: 3px;
-}
-.panel > .list-group:last-child .list-group-item:last-child,
-.panel > .panel-collapse > .list-group:last-child .list-group-item:last-child {
- border-bottom: 0;
- border-bottom-right-radius: 3px;
- border-bottom-left-radius: 3px;
-}
-.panel-heading + .list-group .list-group-item:first-child {
- border-top-width: 0;
-}
-.list-group + .panel-footer {
- border-top-width: 0;
-}
-.panel > .table,
-.panel > .table-responsive > .table,
-.panel > .panel-collapse > .table {
- margin-bottom: 0;
-}
-.panel > .table caption,
-.panel > .table-responsive > .table caption,
-.panel > .panel-collapse > .table caption {
- padding-left: 15px;
- padding-right: 15px;
-}
-.panel > .table:first-child,
-.panel > .table-responsive:first-child > .table:first-child {
- border-top-right-radius: 3px;
- border-top-left-radius: 3px;
-}
-.panel > .table:first-child > thead:first-child > tr:first-child,
-.panel
- > .table-responsive:first-child
- > .table:first-child
- > thead:first-child
- > tr:first-child,
-.panel > .table:first-child > tbody:first-child > tr:first-child,
-.panel
- > .table-responsive:first-child
- > .table:first-child
- > tbody:first-child
- > tr:first-child {
- border-top-left-radius: 3px;
- border-top-right-radius: 3px;
-}
-.panel > .table:first-child > thead:first-child > tr:first-child td:first-child,
-.panel
- > .table-responsive:first-child
- > .table:first-child
- > thead:first-child
- > tr:first-child
- td:first-child,
-.panel > .table:first-child > tbody:first-child > tr:first-child td:first-child,
-.panel
- > .table-responsive:first-child
- > .table:first-child
- > tbody:first-child
- > tr:first-child
- td:first-child,
-.panel > .table:first-child > thead:first-child > tr:first-child th:first-child,
-.panel
- > .table-responsive:first-child
- > .table:first-child
- > thead:first-child
- > tr:first-child
- th:first-child,
-.panel > .table:first-child > tbody:first-child > tr:first-child th:first-child,
-.panel
- > .table-responsive:first-child
- > .table:first-child
- > tbody:first-child
- > tr:first-child
- th:first-child {
- border-top-left-radius: 3px;
-}
-.panel > .table:first-child > thead:first-child > tr:first-child td:last-child,
-.panel
- > .table-responsive:first-child
- > .table:first-child
- > thead:first-child
- > tr:first-child
- td:last-child,
-.panel > .table:first-child > tbody:first-child > tr:first-child td:last-child,
-.panel
- > .table-responsive:first-child
- > .table:first-child
- > tbody:first-child
- > tr:first-child
- td:last-child,
-.panel > .table:first-child > thead:first-child > tr:first-child th:last-child,
-.panel
- > .table-responsive:first-child
- > .table:first-child
- > thead:first-child
- > tr:first-child
- th:last-child,
-.panel > .table:first-child > tbody:first-child > tr:first-child th:last-child,
-.panel
- > .table-responsive:first-child
- > .table:first-child
- > tbody:first-child
- > tr:first-child
- th:last-child {
- border-top-right-radius: 3px;
-}
-.panel > .table:last-child,
-.panel > .table-responsive:last-child > .table:last-child {
- border-bottom-right-radius: 3px;
- border-bottom-left-radius: 3px;
-}
-.panel > .table:last-child > tbody:last-child > tr:last-child,
-.panel
- > .table-responsive:last-child
- > .table:last-child
- > tbody:last-child
- > tr:last-child,
-.panel > .table:last-child > tfoot:last-child > tr:last-child,
-.panel
- > .table-responsive:last-child
- > .table:last-child
- > tfoot:last-child
- > tr:last-child {
- border-bottom-left-radius: 3px;
- border-bottom-right-radius: 3px;
-}
-.panel > .table:last-child > tbody:last-child > tr:last-child td:first-child,
-.panel
- > .table-responsive:last-child
- > .table:last-child
- > tbody:last-child
- > tr:last-child
- td:first-child,
-.panel > .table:last-child > tfoot:last-child > tr:last-child td:first-child,
-.panel
- > .table-responsive:last-child
- > .table:last-child
- > tfoot:last-child
- > tr:last-child
- td:first-child,
-.panel > .table:last-child > tbody:last-child > tr:last-child th:first-child,
-.panel
- > .table-responsive:last-child
- > .table:last-child
- > tbody:last-child
- > tr:last-child
- th:first-child,
-.panel > .table:last-child > tfoot:last-child > tr:last-child th:first-child,
-.panel
- > .table-responsive:last-child
- > .table:last-child
- > tfoot:last-child
- > tr:last-child
- th:first-child {
- border-bottom-left-radius: 3px;
-}
-.panel > .table:last-child > tbody:last-child > tr:last-child td:last-child,
-.panel
- > .table-responsive:last-child
- > .table:last-child
- > tbody:last-child
- > tr:last-child
- td:last-child,
-.panel > .table:last-child > tfoot:last-child > tr:last-child td:last-child,
-.panel
- > .table-responsive:last-child
- > .table:last-child
- > tfoot:last-child
- > tr:last-child
- td:last-child,
-.panel > .table:last-child > tbody:last-child > tr:last-child th:last-child,
-.panel
- > .table-responsive:last-child
- > .table:last-child
- > tbody:last-child
- > tr:last-child
- th:last-child,
-.panel > .table:last-child > tfoot:last-child > tr:last-child th:last-child,
-.panel
- > .table-responsive:last-child
- > .table:last-child
- > tfoot:last-child
- > tr:last-child
- th:last-child {
- border-bottom-right-radius: 3px;
-}
-.panel > .panel-body + .table,
-.panel > .panel-body + .table-responsive,
-.panel > .table + .panel-body,
-.panel > .table-responsive + .panel-body {
- border-top: 1px solid #ddd;
-}
-.panel > .table > tbody:first-child > tr:first-child th,
-.panel > .table > tbody:first-child > tr:first-child td {
- border-top: 0;
-}
-.panel > .table-bordered,
-.panel > .table-responsive > .table-bordered {
- border: 0;
-}
-.panel > .table-bordered > thead > tr > th:first-child,
-.panel > .table-responsive > .table-bordered > thead > tr > th:first-child,
-.panel > .table-bordered > tbody > tr > th:first-child,
-.panel > .table-responsive > .table-bordered > tbody > tr > th:first-child,
-.panel > .table-bordered > tfoot > tr > th:first-child,
-.panel > .table-responsive > .table-bordered > tfoot > tr > th:first-child,
-.panel > .table-bordered > thead > tr > td:first-child,
-.panel > .table-responsive > .table-bordered > thead > tr > td:first-child,
-.panel > .table-bordered > tbody > tr > td:first-child,
-.panel > .table-responsive > .table-bordered > tbody > tr > td:first-child,
-.panel > .table-bordered > tfoot > tr > td:first-child,
-.panel > .table-responsive > .table-bordered > tfoot > tr > td:first-child {
- border-left: 0;
-}
-.panel > .table-bordered > thead > tr > th:last-child,
-.panel > .table-responsive > .table-bordered > thead > tr > th:last-child,
-.panel > .table-bordered > tbody > tr > th:last-child,
-.panel > .table-responsive > .table-bordered > tbody > tr > th:last-child,
-.panel > .table-bordered > tfoot > tr > th:last-child,
-.panel > .table-responsive > .table-bordered > tfoot > tr > th:last-child,
-.panel > .table-bordered > thead > tr > td:last-child,
-.panel > .table-responsive > .table-bordered > thead > tr > td:last-child,
-.panel > .table-bordered > tbody > tr > td:last-child,
-.panel > .table-responsive > .table-bordered > tbody > tr > td:last-child,
-.panel > .table-bordered > tfoot > tr > td:last-child,
-.panel > .table-responsive > .table-bordered > tfoot > tr > td:last-child {
- border-right: 0;
-}
-.panel > .table-bordered > thead > tr:first-child > td,
-.panel > .table-responsive > .table-bordered > thead > tr:first-child > td,
-.panel > .table-bordered > tbody > tr:first-child > td,
-.panel > .table-responsive > .table-bordered > tbody > tr:first-child > td,
-.panel > .table-bordered > thead > tr:first-child > th,
-.panel > .table-responsive > .table-bordered > thead > tr:first-child > th,
-.panel > .table-bordered > tbody > tr:first-child > th,
-.panel > .table-responsive > .table-bordered > tbody > tr:first-child > th {
- border-bottom: 0;
-}
-.panel > .table-bordered > tbody > tr:last-child > td,
-.panel > .table-responsive > .table-bordered > tbody > tr:last-child > td,
-.panel > .table-bordered > tfoot > tr:last-child > td,
-.panel > .table-responsive > .table-bordered > tfoot > tr:last-child > td,
-.panel > .table-bordered > tbody > tr:last-child > th,
-.panel > .table-responsive > .table-bordered > tbody > tr:last-child > th,
-.panel > .table-bordered > tfoot > tr:last-child > th,
-.panel > .table-responsive > .table-bordered > tfoot > tr:last-child > th {
- border-bottom: 0;
-}
-.panel > .table-responsive {
- border: 0;
- margin-bottom: 0;
-}
-.panel-group {
- margin-bottom: 20px;
-}
-.panel-group .panel {
- margin-bottom: 0;
- border-radius: 4px;
-}
-.panel-group .panel + .panel {
- margin-top: 5px;
-}
-.panel-group .panel-heading {
- border-bottom: 0;
-}
-.panel-group .panel-heading + .panel-collapse > .panel-body,
-.panel-group .panel-heading + .panel-collapse > .list-group {
- border-top: 1px solid #ddd;
-}
-.panel-group .panel-footer {
- border-top: 0;
-}
-.panel-group .panel-footer + .panel-collapse .panel-body {
- border-bottom: 1px solid #ddd;
-}
-.panel-default {
- border-color: #ddd;
-}
-.panel-default > .panel-heading {
- color: #51504f;
- background-color: #f5f5f5;
- border-color: #ddd;
-}
-.panel-default > .panel-heading + .panel-collapse > .panel-body {
- border-top-color: #ddd;
-}
-.panel-default > .panel-heading .badge {
- color: #f5f5f5;
- background-color: #51504f;
-}
-.panel-default > .panel-footer + .panel-collapse > .panel-body {
- border-bottom-color: #ddd;
-}
-.panel-primary {
- border-color: #e2e2e2;
-}
-.panel-primary > .panel-heading {
- color: #51504f;
- background-color: #e2e2e2;
- border-color: #e2e2e2;
-}
-.panel-primary > .panel-heading + .panel-collapse > .panel-body {
- border-top-color: #e2e2e2;
-}
-.panel-primary > .panel-heading .badge {
- color: #e2e2e2;
-}
-.panel-primary > .panel-footer + .panel-collapse > .panel-body {
- border-bottom-color: #e2e2e2;
-}
-.panel-success {
- border-color: #d6e9c6;
-}
-.panel-success > .panel-heading {
- color: #1b8e49;
- background-color: #bfebd1;
- border-color: #d6e9c6;
-}
-.panel-success > .panel-heading + .panel-collapse > .panel-body {
- border-top-color: #d6e9c6;
-}
-.panel-success > .panel-heading .badge {
- color: #bfebd1;
- background-color: #1b8e49;
-}
-.panel-success > .panel-footer + .panel-collapse > .panel-body {
- border-bottom-color: #d6e9c6;
-}
-.panel-info {
- border-color: #bce8f1;
-}
-.panel-info > .panel-heading {
- color: #31708f;
- background-color: #d9edf7;
- border-color: #bce8f1;
-}
-.panel-info > .panel-heading + .panel-collapse > .panel-body {
- border-top-color: #bce8f1;
-}
-.panel-info > .panel-heading .badge {
- color: #d9edf7;
- background-color: #31708f;
-}
-.panel-info > .panel-footer + .panel-collapse > .panel-body {
- border-bottom-color: #bce8f1;
-}
-.panel-warning {
- border-color: #faebcc;
-}
-.panel-warning > .panel-heading {
- color: #8a6d3b;
- background-color: #fcf8e3;
- border-color: #faebcc;
-}
-.panel-warning > .panel-heading + .panel-collapse > .panel-body {
- border-top-color: #faebcc;
-}
-.panel-warning > .panel-heading .badge {
- color: #fcf8e3;
- background-color: #8a6d3b;
-}
-.panel-warning > .panel-footer + .panel-collapse > .panel-body {
- border-bottom-color: #faebcc;
-}
-.panel-danger {
- border-color: #ebccd1;
-}
-.panel-danger > .panel-heading {
- color: #e43921;
- background-color: #f2dede;
- border-color: #ebccd1;
-}
-.panel-danger > .panel-heading + .panel-collapse > .panel-body {
- border-top-color: #ebccd1;
-}
-.panel-danger > .panel-heading .badge {
- color: #f2dede;
- background-color: #e43921;
-}
-.panel-danger > .panel-footer + .panel-collapse > .panel-body {
- border-bottom-color: #ebccd1;
-}
-.well {
- min-height: 20px;
- padding: 19px;
- margin-bottom: 20px;
- background-color: #f5f5f5;
- border: 0;
- border-radius: 4px;
- box-shadow: none;
-}
-.well blockquote {
- border-color: #ddd;
- border-color: rgba(0, 0, 0, 0.15);
-}
-.well-lg {
- padding: 24px;
- border-radius: 6px;
-}
-.well-sm {
- padding: 9px;
-}
-.close {
- float: right;
- font-size: 21px;
- font-weight: bold;
- line-height: 1;
- color: currentColor;
- opacity: 0.4;
-}
-.close:hover,
-.close:focus {
- color: currentColor;
- text-decoration: none;
- cursor: pointer;
- opacity: 0.8;
-}
-button.close {
- padding: 0;
- cursor: pointer;
- background: transparent;
- border: 0;
- -webkit-appearance: none;
-}
-.modal-open {
- overflow: hidden;
-}
-.modal {
- display: none;
- overflow: hidden;
- position: fixed;
- top: 0;
- right: 0;
- bottom: 0;
- left: 0;
- z-index: 1040;
- -webkit-overflow-scrolling: touch;
- outline: 0;
-}
-.modal.fade .modal-dialog {
- -webkit-transform: translate(0, -25%);
- -ms-transform: translate(0, -25%);
- -o-transform: translate(0, -25%);
- transform: translate(0, -25%);
- -webkit-transition: -webkit-transform 0.3s ease-out;
- -moz-transition: -moz-transform 0.3s ease-out;
- -o-transition: -o-transform 0.3s ease-out;
- transition: transform 0.3s ease-out;
-}
-.modal.in .modal-dialog {
- -webkit-transform: translate(0, 0);
- -ms-transform: translate(0, 0);
- -o-transform: translate(0, 0);
- transform: translate(0, 0);
-}
-.modal-open .modal {
- overflow-x: hidden;
- overflow-y: auto;
-}
-.modal-dialog {
- position: relative;
- width: auto;
- margin: 10px;
-}
-.modal-content {
- position: relative;
- background-color: #fff;
- border: 0;
- border-radius: 6px;
- -webkit-box-shadow: 0 3px 9px rgba(0, 0, 0, 0.5);
- box-shadow: 0 3px 9px rgba(0, 0, 0, 0.5);
- background-clip: padding-box;
- outline: 0;
- z-index: 5;
-}
-.modal-backdrop {
- z-index: 2;
- position: fixed;
- top: 0;
- right: 0;
- left: 0;
- background-color: #000;
-}
-.modal-backdrop.fade {
- opacity: 0;
-}
-.modal-backdrop.in {
- opacity: 0.5;
-}
-.modal-header {
- padding: 15px;
- border-bottom: 1px solid #e5e5e5;
- min-height: 16.428571429px;
-}
-.modal-header .close {
- margin-top: -2px;
-}
-.modal-title {
- margin: 0;
- line-height: 1.428571429;
-}
-.modal-body {
- position: relative;
- padding: 15px;
-}
-.modal-footer {
- padding: 15px;
- text-align: right;
- border-top: 1px solid #e5e5e5;
-}
-.modal-footer .btn + .btn {
- margin-left: 5px;
- margin-bottom: 0;
-}
-.modal-footer .btn-group .btn + .btn {
- margin-left: -1px;
-}
-.modal-footer .btn-block + .btn-block {
- margin-left: 0;
-}
-.modal-scrollbar-measure {
- position: absolute;
- top: -9999px;
- width: 50px;
- height: 50px;
- overflow: auto;
-}
-
-@media (min-width: 768px) {
- .modal-dialog {
- width: 600px;
- margin: 30px auto;
- }
- .modal-content {
- -webkit-box-shadow: 0 5px 15px rgba(0, 0, 0, 0.5);
- box-shadow: 0 5px 15px rgba(0, 0, 0, 0.5);
- }
- .modal-sm {
- width: 300px;
- }
-}
-
-@media (min-width: 992px) {
- .modal-lg {
- width: 900px;
- }
-}
-.tooltip {
- position: absolute;
- z-index: 1070;
- display: block;
- visibility: visible;
- font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
- font-size: 12px;
- font-weight: normal;
- line-height: 1.4;
- opacity: 0;
-}
-.tooltip.in {
- opacity: 1;
-}
-.tooltip.top {
- margin-top: -3px;
- padding: 5px 0;
-}
-.tooltip.right {
- margin-left: 3px;
- padding: 0 5px;
-}
-.tooltip.bottom {
- margin-top: 3px;
- padding: 5px 0;
-}
-.tooltip.left {
- margin-left: -3px;
- padding: 0 5px;
-}
-.tooltip-inner {
- max-width: 500px;
- padding: 3px 8px;
- color: #fff;
- text-align: left;
- text-decoration: none;
- background-color: #51504f;
- border-radius: 4px;
- font-size: 13px;
-}
-.tooltip-arrow {
- position: absolute;
- width: 0;
- height: 0;
- border-color: transparent;
- border-style: solid;
-}
-.tooltip.top .tooltip-arrow {
- bottom: 0;
- left: 50%;
- margin-left: -5px;
- border-width: 5px 5px 0;
- border-top-color: #51504f;
-}
-.tooltip.top-left .tooltip-arrow {
- bottom: 0;
- right: 5px;
- margin-bottom: -5px;
- border-width: 5px 5px 0;
- border-top-color: #51504f;
-}
-.tooltip.top-right .tooltip-arrow {
- bottom: 0;
- left: 5px;
- margin-bottom: -5px;
- border-width: 5px 5px 0;
- border-top-color: #51504f;
-}
-.tooltip.right .tooltip-arrow {
- top: 50%;
- left: 0;
- margin-top: -5px;
- border-width: 5px 5px 5px 0;
- border-right-color: #51504f;
-}
-.tooltip.left .tooltip-arrow {
- top: 50%;
- right: 0;
- margin-top: -5px;
- border-width: 5px 0 5px 5px;
- border-left-color: #51504f;
-}
-.tooltip.bottom .tooltip-arrow {
- top: 0;
- left: 50%;
- margin-left: -5px;
- border-width: 0 5px 5px;
- border-bottom-color: #51504f;
-}
-.tooltip.bottom-left .tooltip-arrow {
- top: 0;
- right: 5px;
- margin-top: -5px;
- border-width: 0 5px 5px;
- border-bottom-color: #51504f;
-}
-.tooltip.bottom-right .tooltip-arrow {
- top: 0;
- left: 5px;
- margin-top: -5px;
- border-width: 0 5px 5px;
- border-bottom-color: #51504f;
-}
-.popover {
- position: absolute;
- top: 0;
- left: 0;
- z-index: 1060;
- display: none;
- max-width: 276px;
- padding: 1px;
- font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
- font-size: 14px;
- font-weight: normal;
- line-height: 1.428571429;
- text-align: left;
- background-color: #fff;
- background-clip: padding-box;
- border: 1px solid #ccc;
- border: 1px solid rgba(0, 0, 0, 0.2);
- border-radius: 6px;
- -webkit-box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2);
- box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2);
- white-space: normal;
-}
-.popover.top {
- margin-top: -10px;
-}
-.popover.right {
- margin-left: 10px;
-}
-.popover.bottom {
- margin-top: 10px;
-}
-.popover.left {
- margin-left: -10px;
-}
-.popover-title {
- margin: 0;
- padding: 8px 14px;
- font-size: 14px;
- background-color: #f7f7f7;
- border-bottom: 1px solid #ebebeb;
- border-radius: 5px 5px 0 0;
-}
-.popover-content {
- padding: 9px 14px;
-}
-.popover > .arrow,
-.popover > .arrow::after {
- position: absolute;
- display: block;
- width: 0;
- height: 0;
- border-color: transparent;
- border-style: solid;
-}
-.popover > .arrow {
- border-width: 11px;
-}
-.popover > .arrow::after {
- border-width: 10px;
- content: "";
-}
-.popover.top > .arrow {
- left: 50%;
- margin-left: -11px;
- border-bottom-width: 0;
- border-top-color: #999;
- border-top-color: rgba(0, 0, 0, 0.25);
- bottom: -11px;
-}
-.popover.top > .arrow::after {
- content: " ";
- bottom: 1px;
- margin-left: -10px;
- border-bottom-width: 0;
- border-top-color: #fff;
-}
-.popover.right > .arrow {
- top: 50%;
- left: -11px;
- margin-top: -11px;
- border-left-width: 0;
- border-right-color: #999;
- border-right-color: rgba(0, 0, 0, 0.25);
-}
-.popover.right > .arrow::after {
- content: " ";
- left: 1px;
- bottom: -10px;
- border-left-width: 0;
- border-right-color: #fff;
-}
-.popover.bottom > .arrow {
- left: 50%;
- margin-left: -11px;
- border-top-width: 0;
- border-bottom-color: #999;
- border-bottom-color: rgba(0, 0, 0, 0.25);
- top: -11px;
-}
-.popover.bottom > .arrow::after {
- content: " ";
- top: 1px;
- margin-left: -10px;
- border-top-width: 0;
- border-bottom-color: #fff;
-}
-.popover.left > .arrow {
- top: 50%;
- right: -11px;
- margin-top: -11px;
- border-right-width: 0;
- border-left-color: #999;
- border-left-color: rgba(0, 0, 0, 0.25);
-}
-.popover.left > .arrow::after {
- content: " ";
- right: 1px;
- border-right-width: 0;
- border-left-color: #fff;
- bottom: -10px;
-}
-.carousel {
- position: relative;
-}
-.carousel-inner {
- position: relative;
- overflow: hidden;
- width: 100%;
-}
-.carousel-inner > .item {
- display: none;
- position: relative;
- -webkit-transition: 0.6s ease-in-out left;
- -o-transition: 0.6s ease-in-out left;
- transition: 0.6s ease-in-out left;
-}
-.carousel-inner > .item > img,
-.carousel-inner > .item > a > img {
- line-height: 1;
-}
-
-@supports (transform: translate3d) {
- .carousel-inner > .item {
- -webkit-transition: -webkit-transform 0.6s ease-in-out;
- -moz-transition: -moz-transform 0.6s ease-in-out;
- -o-transition: -o-transform 0.6s ease-in-out;
- transition: transform 0.6s ease-in-out;
- -webkit-backface-visibility: hidden;
- -moz-backface-visibility: hidden;
- backface-visibility: hidden;
- -webkit-perspective: 1000;
- -moz-perspective: 1000;
- perspective: 1000;
- }
- .carousel-inner > .item.next,
- .carousel-inner > .item.active.right {
- -webkit-transform: translate3d(100%, 0, 0);
- transform: translate3d(100%, 0, 0);
- left: 0;
- }
- .carousel-inner > .item.prev,
- .carousel-inner > .item.active.left {
- -webkit-transform: translate3d(-100%, 0, 0);
- transform: translate3d(-100%, 0, 0);
- left: 0;
- }
- .carousel-inner > .item.next.left,
- .carousel-inner > .item.prev.right,
- .carousel-inner > .item.active {
- -webkit-transform: translate3d(0, 0, 0);
- transform: translate3d(0, 0, 0);
- left: 0;
- }
-}
-.carousel-inner > .active,
-.carousel-inner > .next,
-.carousel-inner > .prev {
- display: block;
-}
-.carousel-inner > .active {
- left: 0;
-}
-.carousel-inner > .next,
-.carousel-inner > .prev {
- position: absolute;
- top: 0;
- width: 100%;
-}
-.carousel-inner > .next {
- left: 100%;
-}
-.carousel-inner > .prev {
- left: -100%;
-}
-.carousel-inner > .next.left,
-.carousel-inner > .prev.right {
- left: 0;
-}
-.carousel-inner > .active.left {
- left: -100%;
-}
-.carousel-inner > .active.right {
- left: 100%;
-}
-.carousel-control {
- position: absolute;
- top: 0;
- left: 0;
- bottom: 0;
- width: 15%;
- opacity: 0.5;
- font-size: 20px;
- color: #fff;
- text-align: center;
- text-shadow: 0 1px 2px rgba(0, 0, 0, 0.6);
-}
-.carousel-control.left {
- background-image: -webkit-linear-gradient(
- left,
- rgba(0, 0, 0, 0.5) 0%,
- rgba(0, 0, 0, 0.0001) 100%
- );
- background-image: -o-linear-gradient(
- left,
- rgba(0, 0, 0, 0.5) 0%,
- rgba(0, 0, 0, 0.0001) 100%
- );
- background-image: linear-gradient(
- to right,
- rgba(0, 0, 0, 0.5) 0%,
- rgba(0, 0, 0, 0.0001) 100%
- );
- background-repeat: repeat-x;
-}
-.carousel-control.right {
- left: auto;
- right: 0;
- background-image: -webkit-linear-gradient(
- left,
- rgba(0, 0, 0, 0.0001) 0%,
- rgba(0, 0, 0, 0.5) 100%
- );
- background-image: -o-linear-gradient(
- left,
- rgba(0, 0, 0, 0.0001) 0%,
- rgba(0, 0, 0, 0.5) 100%
- );
- background-image: linear-gradient(
- to right,
- rgba(0, 0, 0, 0.0001) 0%,
- rgba(0, 0, 0, 0.5) 100%
- );
- background-repeat: repeat-x;
-}
-.carousel-control:hover,
-.carousel-control:focus {
- outline: 0;
- color: #fff;
- text-decoration: none;
- opacity: 0.9;
-}
-.carousel-control .icon-prev,
-.carousel-control .icon-next,
-.carousel-control .glyphicon-chevron-left,
-.carousel-control .glyphicon-chevron-right {
- position: absolute;
- top: 50%;
- z-index: 5;
- display: inline-block;
-}
-.carousel-control .icon-prev,
-.carousel-control .glyphicon-chevron-left {
- left: 50%;
- margin-left: -10px;
-}
-.carousel-control .icon-next,
-.carousel-control .glyphicon-chevron-right {
- right: 50%;
- margin-right: -10px;
-}
-.carousel-control .icon-prev,
-.carousel-control .icon-next {
- width: 20px;
- height: 20px;
- margin-top: -10px;
- line-height: 1;
- font-family: serif;
-}
-.carousel-control .icon-prev::before {
- content: "\2039";
-}
-.carousel-control .icon-next::before {
- content: "\203a";
-}
-.carousel-indicators {
- position: absolute;
- bottom: 10px;
- left: 50%;
- z-index: 15;
- width: 60%;
- margin-left: -30%;
- padding-left: 0;
- list-style: none;
- text-align: center;
-}
-.carousel-indicators li {
- display: inline-block;
- width: 10px;
- height: 10px;
- margin: 1px;
- text-indent: -999px;
- border: 1px solid #fff;
- border-radius: 10px;
- cursor: pointer;
- background-color: #000 \9;
- background-color: rgba(0, 0, 0, 0);
-}
-.carousel-indicators .active {
- margin: 0;
- width: 12px;
- height: 12px;
- background-color: #fff;
-}
-.carousel-caption {
- position: absolute;
- left: 15%;
- right: 15%;
- bottom: 20px;
- z-index: 10;
- padding-top: 20px;
- padding-bottom: 20px;
- color: #fff;
- text-align: center;
- text-shadow: 0 1px 2px rgba(0, 0, 0, 0.6);
-}
-.carousel-caption .btn {
- text-shadow: none;
-}
-
-@media screen and (min-width: 768px) {
- .carousel-control .glyphicon-chevron-left,
- .carousel-control .glyphicon-chevron-right,
- .carousel-control .icon-prev,
- .carousel-control .icon-next {
- width: 30px;
- height: 30px;
- margin-top: -15px;
- font-size: 30px;
- }
- .carousel-control .glyphicon-chevron-left,
- .carousel-control .icon-prev {
- margin-left: -15px;
- }
- .carousel-control .glyphicon-chevron-right,
- .carousel-control .icon-next {
- margin-right: -15px;
- }
- .carousel-caption {
- left: 20%;
- right: 20%;
- padding-bottom: 30px;
- }
- .carousel-indicators {
- bottom: 20px;
- }
-}
-.clearfix::before,
-.clearfix::after,
-.dl-horizontal dd::before,
-.dl-horizontal dd::after,
-.container::before,
-.container::after,
-.container-fluid::before,
-.container-fluid::after,
-.row::before,
-.row::after,
-.form-horizontal .form-group::before,
-.form-horizontal .form-group::after,
-.btn-toolbar::before,
-.btn-toolbar::after,
-.btn-group-vertical > .btn-group::before,
-.btn-group-vertical > .btn-group::after,
-.nav::before,
-.nav::after,
-.navbar::before,
-.navbar::after,
-.navbar-header::before,
-.navbar-header::after,
-.navbar-collapse::before,
-.navbar-collapse::after,
-.pager::before,
-.pager::after,
-.panel-body::before,
-.panel-body::after,
-.modal-footer::before,
-.modal-footer::after {
- content: " ";
- display: table;
-}
-.clearfix::after,
-.dl-horizontal dd::after,
-.container::after,
-.container-fluid::after,
-.row::after,
-.form-horizontal .form-group::after,
-.btn-toolbar::after,
-.btn-group-vertical > .btn-group::after,
-.nav::after,
-.navbar::after,
-.navbar-header::after,
-.navbar-collapse::after,
-.pager::after,
-.panel-body::after,
-.modal-footer::after {
- clear: both;
-}
-.center-block {
- display: block;
- margin-left: auto;
- margin-right: auto;
-}
-.pull-right {
- float: right !important;
-}
-.pull-left {
... 69123 lines suppressed ...