diff --git a/Makefile b/Makefile
index ea8ba4b192..17ed23662e 100644
--- a/Makefile
+++ b/Makefile
@@ -940,6 +940,7 @@ fomantic:
cd $(FOMANTIC_WORK_DIR) && npm install --no-save
cp -f $(FOMANTIC_WORK_DIR)/theme.config.less $(FOMANTIC_WORK_DIR)/node_modules/fomantic-ui/src/theme.config
cp -rf $(FOMANTIC_WORK_DIR)/_site $(FOMANTIC_WORK_DIR)/node_modules/fomantic-ui/src/
+ rm -rf $(FOMANTIC_WORK_DIR)/node_modules/fomantic-ui/src/themes/default/modules/dropdown.overrides
$(SED_INPLACE) -e 's/ overrideBrowserslist\r/ overrideBrowserslist: ["defaults"]\r/g' $(FOMANTIC_WORK_DIR)/node_modules/fomantic-ui/tasks/config/tasks.js
cd $(FOMANTIC_WORK_DIR) && npx gulp -f node_modules/fomantic-ui/gulpfile.js build
# fomantic uses "touchstart" as click event for some browsers, it's not ideal, so we force fomantic to always use "click" as click event
diff --git a/renovate.json b/renovate.json
index 135df58c07..e0d7bd5456 100644
--- a/renovate.json
+++ b/renovate.json
@@ -6,7 +6,7 @@
"docker:pinDigests",
"helpers:pinGitHubActionDigests"
],
- "baseBranches": [
+ "baseBranchPatterns": [
"$default",
"/^v11\\.\\d+/forgejo$/",
"/^v12\\.\\d+/forgejo$/"
diff --git a/tests/e2e/image-diff.test.e2e.ts b/tests/e2e/image-diff.test.e2e.ts
new file mode 100644
index 0000000000..f7d4f7bd69
--- /dev/null
+++ b/tests/e2e/image-diff.test.e2e.ts
@@ -0,0 +1,66 @@
+// @watch start
+// templates/repo/diff/**
+// web_src/css/features/imagediff.css
+// web_src/css/modules/tab.css
+// web_src/js/modules/tab.ts
+// @watch end
+
+import {expect} from '@playwright/test';
+import {save_visual, test, dynamic_id} from './utils_e2e.ts';
+
+test.use({user: 'user2'});
+
+test('Repository image diff', async ({page}) => {
+ // Generate a temporary SVG and edit it.
+ let response = await page.goto('/user2/repo1/_new/master', {waitUntil: 'domcontentloaded'});
+ expect(response?.status()).toBe(200);
+
+ const filename = `${dynamic_id()}.svg`;
+
+ await page.getByPlaceholder('Name your fileā¦').fill(filename);
+ await page.locator('.monaco-editor').click();
+ await page.keyboard.type('\n');
+
+ await page.locator('.quick-pull-choice input[value="direct"]').click();
+ await page.getByRole('button', {name: 'Commit changes'}).click();
+
+ response = await page.goto(`/user2/repo1/_edit/master/${filename}`, {waitUntil: 'domcontentloaded'});
+ expect(response?.status()).toBe(200);
+
+ await page.locator('.monaco-editor').click();
+ await page.keyboard.press('Meta+KeyA');
+ await page.keyboard.type('\n');
+
+ await page.locator('.quick-pull-choice input[value="direct"]').click();
+ await page.getByRole('button', {name: 'Commit changes'}).click();
+
+ // Go to the commit page, where a image diff is shown.
+ await page.locator('.commit-summary a.default-link').click();
+
+ // Exhaustively test tabs works as expected
+ await expect(page.locator('.item[data-tab="diff-side-by-side-1"]')).toContainClass('active');
+ await expect(page.locator('.item[data-tab="diff-swipe-1"]')).not.toContainClass('active');
+ await expect(page.locator('.item[data-tab="diff-overlay-1"]')).not.toContainClass('active');
+ await expect(page.locator('.tab[data-tab="diff-side-by-side-1"]')).toBeVisible();
+ await expect(page.locator('.tab[data-tab="diff-swipe-1"]')).toBeHidden();
+ await expect(page.locator('.tab[data-tab="diff-overlay-1"]')).toBeHidden();
+ await save_visual(page);
+
+ await page.getByText('Swipe').click();
+ await expect(page.locator('.item[data-tab="diff-side-by-side-1"]')).not.toContainClass('active');
+ await expect(page.locator('.item[data-tab="diff-swipe-1"]')).toContainClass('active');
+ await expect(page.locator('.item[data-tab="diff-overlay-1"]')).not.toContainClass('active');
+ await expect(page.locator('.tab[data-tab="diff-side-by-side-1"]')).toBeHidden();
+ await expect(page.locator('.tab[data-tab="diff-swipe-1"]')).toBeVisible();
+ await expect(page.locator('.tab[data-tab="diff-overlay-1"]')).toBeHidden();
+ await save_visual(page);
+
+ await page.getByText('Overlay').click();
+ await expect(page.locator('.item[data-tab="diff-side-by-side-1"]')).not.toContainClass('active');
+ await expect(page.locator('.item[data-tab="diff-swipe-1"]')).not.toContainClass('active');
+ await expect(page.locator('.item[data-tab="diff-overlay-1"]')).toContainClass('active');
+ await expect(page.locator('.tab[data-tab="diff-side-by-side-1"]')).toBeHidden();
+ await expect(page.locator('.tab[data-tab="diff-swipe-1"]')).toBeHidden();
+ await expect(page.locator('.tab[data-tab="diff-overlay-1"]')).toBeVisible();
+ await save_visual(page);
+});
diff --git a/tests/e2e/markdown-editor.test.e2e.ts b/tests/e2e/markdown-editor.test.e2e.ts
index 7983907343..d0ab88fe26 100644
--- a/tests/e2e/markdown-editor.test.e2e.ts
+++ b/tests/e2e/markdown-editor.test.e2e.ts
@@ -1,4 +1,6 @@
// @watch start
+// web_src/js/modules/tab.ts
+// web_src/css/modules/tab.css
// web_src/js/features/comp/ComboMarkdownEditor.js
// web_src/css/editor/combomarkdowneditor.css
// templates/shared/combomarkdowneditor.tmpl
diff --git a/web_src/css/index.css b/web_src/css/index.css
index e7e5dda2d5..d86a784bcc 100644
--- a/web_src/css/index.css
+++ b/web_src/css/index.css
@@ -12,6 +12,7 @@
@import "./modules/segment.css";
@import "./modules/grid.css";
@import "./modules/message.css";
+@import "./modules/tab.css";
@import "./modules/table.css";
@import "./modules/card.css";
@import "./modules/checkbox.css";
diff --git a/web_src/css/modules/tab.css b/web_src/css/modules/tab.css
new file mode 100644
index 0000000000..63c83179b2
--- /dev/null
+++ b/web_src/css/modules/tab.css
@@ -0,0 +1,7 @@
+.ui.tab {
+ display: none;
+}
+
+.ui.tab.active {
+ display: block;
+}
diff --git a/web_src/fomantic/_site/globals/site.variables b/web_src/fomantic/_site/globals/site.variables
index 283b9e18eb..19375cf047 100644
--- a/web_src/fomantic/_site/globals/site.variables
+++ b/web_src/fomantic/_site/globals/site.variables
@@ -14,9 +14,14 @@
@variationButtonAnimated: false;
@variationButtonAnimatedFade: false;
@variationButtonAttached: false;
+@variationButtonCircular: false;
+@variationButtonFloated: false;
@variationButtonInverted: false;
+@variationButtonToggle: false;
@variationButtonSocial: false;
@variationButtonTertiary: false;
+@variationButtonOr: false;
+@variationButtonSizes: mini, tiny, small;
@variationCalendarInverted: false;
@variationCardInverted: false;
@variationCheckboxInverted: false;
@@ -24,14 +29,18 @@
@variationDimmerInverted: false;
@variationDividerInverted: false;
@variationDropdownInverted: false;
+@variationDropdownScrollhint: false;
+@variationDropdownSimple: false;
@variationFeedInverted: false;
@variationFlyoutInverted: false;
@variationFormInverted: false;
@variationFormTransparent: false;
+@variationFormSizes: false;
@variationGridDoubling: false;
@variationGridInverted: false;
@variationHeaderInverted: false;
@variationIconInverted: false;
+@variationInputAction: false;
@variationInputInverted: false;
@variationItemInverted: false;
@variationLabelCorner: false;
@@ -40,14 +49,25 @@
@variationLabelRibbon: false;
@variationLabelTag: false;
@variationListInverted: false;
+@variationMenuText: false;
+@variationMenuFitted: false;
+@variationMenuFixed: false;
@variationMenuInverted: false;
@variationMessageInverted: false;
+@variationModalBasic: false;
@variationModalInverted: false;
+@variationModalLegacy: false;
+@variationModalOverlay: false;
+@variationModalSizes: mini, tiny, small;
@variationNagInverted: false;
@variationPlaceholderInverted: false;
@variationPopupInverted: false;
@variationPopupTooltip: false;
@variationProgressInverted: false;
+@variationSearchAligned: false;
+@variationSearchCategory: false;
+@variationSearchShort: false;
+@variationSearchLong: false;
@variationSegmentInverted: false;
@variationSegmentPiled: false;
@variationSegmentStacked: false;
diff --git a/web_src/fomantic/build/semantic.css b/web_src/fomantic/build/semantic.css
index 6e9958faa8..2212ed2dd5 100644
--- a/web_src/fomantic/build/semantic.css
+++ b/web_src/fomantic/build/semantic.css
@@ -287,24 +287,6 @@
Variations
*******************************/
-/*-------------------
- Floated
- --------------------*/
-
-.ui[class*="left floated"].buttons,
-.ui[class*="left floated"].button {
- float: left;
- margin-left: 0;
- margin-right: 0.25em;
-}
-
-.ui[class*="right floated"].buttons,
-.ui[class*="right floated"].button {
- float: right;
- margin-right: 0;
- margin-left: 0.25em;
-}
-
/*-------------------
Compact
--------------------*/
@@ -363,14 +345,6 @@
font-size: 0.92857143rem;
}
-.ui.large.buttons .dropdown,
-.ui.large.buttons .dropdown .menu > .item,
-.ui.large.buttons .button,
-.ui.large.buttons .or,
-.ui.ui.ui.ui.large.button {
- font-size: 1.14285714rem;
-}
-
/*--------------
Icon Only
---------------*/
@@ -571,85 +545,6 @@
animation: loader 2s linear infinite;
}
-/*--------------
- Toggle
- ---------------*/
-
-/* Toggle (Modifies active state to give affordances) */
-
-.ui.toggle.buttons .active.button,
-.ui.buttons .button.toggle.active,
-.ui.button.toggle.active {
- background-color: #21BA45;
- box-shadow: none;
- text-shadow: none;
- color: #FFFFFF;
-}
-
-.ui.button.toggle.active:hover {
- background-color: #16ab39;
- text-shadow: none;
- color: #FFFFFF;
-}
-
-/*--------------
- Circular
- ---------------*/
-
-.ui.circular.button {
- border-radius: 10em;
-}
-
-.ui.circular.button > .icon {
- width: 1em;
- vertical-align: baseline;
-}
-
-/*-------------------
- Or Buttons
- --------------------*/
-
-.ui.buttons .or {
- position: relative;
- width: 0.3em;
- height: 2.57142857em;
- z-index: 3;
-}
-
-.ui.buttons .or:before {
- position: absolute;
- text-align: center;
- border-radius: 500rem;
- content: 'or';
- top: 50%;
- left: 50%;
- background-color: #FFFFFF;
- text-shadow: none;
- margin-top: -0.89285714em;
- margin-left: -0.89285714em;
- width: 1.78571429em;
- height: 1.78571429em;
- line-height: 1.78571429em;
- color: rgba(0, 0, 0, 0.4);
- font-style: normal;
- font-weight: 500;
- box-shadow: 0 0 0 1px transparent inset;
-}
-
-.ui.buttons .or[data-text]:before {
- content: attr(data-text);
-}
-
-/* Fluid Or */
-
-.ui.fluid.buttons .or {
- width: 0 !important;
-}
-
-.ui.fluid.buttons .or:after {
- display: none;
-}
-
/*-------------------
Fluid
--------------------*/
@@ -2944,45 +2839,6 @@ select.ui.dropdown {
box-shadow: none !important;
}
-/* CSS specific to iOS devices or firefox mobile only */
-
-@supports (-webkit-touch-callout: none) or (-webkit-overflow-scrolling: touch) or (-moz-appearance:none) {
-@media (-moz-touch-enabled), (pointer: coarse) {
- .ui.dropdown .scrollhint.menu:not(.hidden):before {
- animation: scrollhint 2s ease 2;
- content: '';
- z-index: 15;
- display: block;
- position: absolute;
- opacity: 0;
- right: 0.25em;
- top: 0;
- height: 100%;
- border-right: 0.25em solid;
- border-left: 0;
- -o-border-image: linear-gradient(to bottom, rgba(0, 0, 0, 0.75), rgba(0, 0, 0, 0)) 1 100%;
- border-image: linear-gradient(to bottom, rgba(0, 0, 0, 0.75), rgba(0, 0, 0, 0)) 1 100%;
- }
-
- .ui.inverted.dropdown .scrollhint.menu:not(.hidden):before {
- -o-border-image: linear-gradient(to bottom, rgba(255, 255, 255, 0.75), rgba(255, 255, 255, 0)) 1 100%;
- border-image: linear-gradient(to bottom, rgba(255, 255, 255, 0.75), rgba(255, 255, 255, 0)) 1 100%;
- }
-
-@keyframes scrollhint {
- 0% {
- opacity: 1;
- top: 100%;
- }
-
- 100% {
- opacity: 0;
- top: 0;
- }
-}
-}
-}
-
/*--------------
Searchable
---------------*/
@@ -3820,85 +3676,6 @@ select.ui.dropdown {
width: 20%;
}
-/*--------------
- Simple
- ---------------*/
-
-/* Displays without javascript */
-
-.ui.simple.dropdown .menu:before,
-.ui.simple.dropdown .menu:after {
- display: none;
-}
-
-.ui.simple.dropdown .menu {
- position: absolute;
- /* IE hack to make dropdown icons appear inline */
- display: -ms-inline-flexbox !important;
- display: block;
- overflow: hidden;
- top: -9999px;
- opacity: 0;
- width: 0;
- height: 0;
- transition: opacity 0.1s ease;
- margin-top: 0 !important;
-}
-
-.ui.simple.active.dropdown,
-.ui.simple.dropdown:hover {
- border-bottom-left-radius: 0 !important;
- border-bottom-right-radius: 0 !important;
-}
-
-.ui.simple.active.dropdown > .menu,
-.ui.simple.dropdown:hover > .menu {
- overflow: visible;
- width: auto;
- height: auto;
- top: 100%;
- opacity: 1;
-}
-
-.ui.simple.dropdown > .menu > .item:active > .menu,
-.ui.simple.dropdown .menu .item:hover > .menu {
- overflow: visible;
- width: auto;
- height: auto;
- top: 0 !important;
- left: 100%;
- opacity: 1;
-}
-
-.ui.simple.dropdown > .menu > .item:active > .left.menu,
-.ui.simple.dropdown .menu .item:hover > .left.menu,
-.right.menu .ui.simple.dropdown > .menu > .item:active > .menu:not(.right),
-.right.menu .ui.simple.dropdown > .menu .item:hover > .menu:not(.right) {
- left: auto;
- right: 100%;
-}
-
-.ui.simple.disabled.dropdown:hover .menu {
- display: none;
- height: 0;
- width: 0;
- overflow: hidden;
-}
-
-/* Visible */
-
-.ui.simple.visible.dropdown > .menu {
- display: block;
-}
-
-/* Scrolling */
-
-.ui.simple.scrolling.active.dropdown > .menu,
-.ui.simple.scrolling.dropdown:hover > .menu {
- overflow-x: hidden;
- overflow-y: auto;
-}
-
/*--------------
Fluid
---------------*/
@@ -4184,70 +3961,6 @@ select.ui.dropdown {
font-size: 1.14285714rem;
}
-/*******************************
- Theme Overrides
-*******************************/
-
-/* Dropdown Carets */
-
-@font-face {
- font-family: 'Dropdown';
- src: url(data:application/x-font-ttf;charset=utf-8;base64,AAEAAAALAIAAAwAwT1MvMggjB5AAAAC8AAAAYGNtYXAPfuIIAAABHAAAAExnYXNwAAAAEAAAAWgAAAAIZ2x5Zjo82LgAAAFwAAABVGhlYWQAQ88bAAACxAAAADZoaGVhAwcB6QAAAvwAAAAkaG10eAS4ABIAAAMgAAAAIGxvY2EBNgDeAAADQAAAABJtYXhwAAoAFgAAA1QAAAAgbmFtZVcZpu4AAAN0AAABRXBvc3QAAwAAAAAEvAAAACAAAwIAAZAABQAAAUwBZgAAAEcBTAFmAAAA9QAZAIQAAAAAAAAAAAAAAAAAAAABEAAAAAAAAAAAAAAAAAAAAABAAADw2gHg/+D/4AHgACAAAAABAAAAAAAAAAAAAAAgAAAAAAACAAAAAwAAABQAAwABAAAAFAAEADgAAAAKAAgAAgACAAEAIPDa//3//wAAAAAAIPDX//3//wAB/+MPLQADAAEAAAAAAAAAAAAAAAEAAf//AA8AAQAAAAAAAAAAAAIAADc5AQAAAAABAAAAAAAAAAAAAgAANzkBAAAAAAEAAAAAAAAAAAACAAA3OQEAAAAAAQAAAIABJQElABMAABM0NzY3BTYXFhUUDwEGJwYvASY1AAUGBwEACAUGBoAFCAcGgAUBEgcGBQEBAQcECQYHfwYBAQZ/BwYAAQAAAG4BJQESABMAADc0PwE2MzIfARYVFAcGIyEiJyY1AAWABgcIBYAGBgUI/wAHBgWABwaABQWABgcHBgUFBgcAAAABABIASQC3AW4AEwAANzQ/ATYXNhcWHQEUBwYnBi8BJjUSBoAFCAcFBgYFBwgFgAbbBwZ/BwEBBwQJ/wgEBwEBB38GBgAAAAABAAAASQClAW4AEwAANxE0NzYzMh8BFhUUDwEGIyInJjUABQYHCAWABgaABQgHBgVbAQAIBQYGgAUIBwWABgYFBwAAAAEAAAABAADZuaKOXw889QALAgAAAAAA0ABHWAAAAADQAEdYAAAAAAElAW4AAAAIAAIAAAAAAAAAAQAAAeD/4AAAAgAAAAAAASUAAQAAAAAAAAAAAAAAAAAAAAgAAAAAAAAAAAAAAAABAAAAASUAAAElAAAAtwASALcAAAAAAAAACgAUAB4AQgBkAIgAqgAAAAEAAAAIABQAAQAAAAAAAgAAAAAAAAAAAAAAAAAAAAAAAAAOAK4AAQAAAAAAAQAOAAAAAQAAAAAAAgAOAEcAAQAAAAAAAwAOACQAAQAAAAAABAAOAFUAAQAAAAAABQAWAA4AAQAAAAAABgAHADIAAQAAAAAACgA0AGMAAwABBAkAAQAOAAAAAwABBAkAAgAOAEcAAwABBAkAAwAOACQAAwABBAkABAAOAFUAAwABBAkABQAWAA4AAwABBAkABgAOADkAAwABBAkACgA0AGMAaQBjAG8AbQBvAG8AbgBWAGUAcgBzAGkAbwBuACAAMQAuADAAaQBjAG8AbQBvAG8Abmljb21vb24AaQBjAG8AbQBvAG8AbgBSAGUAZwB1AGwAYQByAGkAYwBvAG0AbwBvAG4ARgBvAG4AdAAgAGcAZQBuAGUAcgBhAHQAZQBkACAAYgB5ACAASQBjAG8ATQBvAG8AbgAuAAAAAAMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=) format('truetype'), url(data:application/font-woff;charset=utf-8;base64,d09GRk9UVE8AAAVwAAoAAAAABSgAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAABDRkYgAAAA9AAAAdkAAAHZLDXE/09TLzIAAALQAAAAYAAAAGAIIweQY21hcAAAAzAAAABMAAAATA9+4ghnYXNwAAADfAAAAAgAAAAIAAAAEGhlYWQAAAOEAAAANgAAADYAQ88baGhlYQAAA7wAAAAkAAAAJAMHAelobXR4AAAD4AAAACAAAAAgBLgAEm1heHAAAAQAAAAABgAAAAYACFAAbmFtZQAABAgAAAFFAAABRVcZpu5wb3N0AAAFUAAAACAAAAAgAAMAAAEABAQAAQEBCGljb21vb24AAQIAAQA6+BwC+BsD+BgEHgoAGVP/i4seCgAZU/+LiwwHi2v4lPh0BR0AAACIDx0AAACNER0AAAAJHQAAAdASAAkBAQgPERMWGyAlKmljb21vb25pY29tb29udTB1MXUyMHVGMEQ3dUYwRDh1RjBEOXVGMERBAAACAYkABgAIAgABAAQABwAKAA0AVgCfAOgBL/yUDvyUDvyUDvuUDvtvi/emFYuQjZCOjo+Pj42Qiwj3lIsFkIuQiY6Hj4iNhouGi4aJh4eHCPsU+xQFiIiGiYaLhouHjYeOCPsU9xQFiI+Jj4uQCA77b4v3FBWLkI2Pjo8I9xT3FAWPjo+NkIuQi5CJjogI9xT7FAWPh42Hi4aLhomHh4eIiIaJhosI+5SLBYaLh42HjoiPiY+LkAgO+92d928Vi5CNkI+OCPcU9xQFjo+QjZCLkIuPiY6Hj4iNhouGCIv7lAWLhomHh4iIh4eJhouGi4aNiI8I+xT3FAWHjomPi5AIDvvdi+YVi/eUBYuQjZCOjo+Pj42Qi5CLkImOhwj3FPsUBY+IjYaLhouGiYeHiAj7FPsUBYiHhomGi4aLh42Hj4iOiY+LkAgO+JQU+JQViwwKAAAAAAMCAAGQAAUAAAFMAWYAAABHAUwBZgAAAPUAGQCEAAAAAAAAAAAAAAAAAAAAARAAAAAAAAAAAAAAAAAAAAAAQAAA8NoB4P/g/+AB4AAgAAAAAQAAAAAAAAAAAAAAIAAAAAAAAgAAAAMAAAAUAAMAAQAAABQABAA4AAAACgAIAAIAAgABACDw2v/9//8AAAAAACDw1//9//8AAf/jDy0AAwABAAAAAAAAAAAAAAABAAH//wAPAAEAAAABAAA5emozXw889QALAgAAAAAA0ABHWAAAAADQAEdYAAAAAAElAW4AAAAIAAIAAAAAAAAAAQAAAeD/4AAAAgAAAAAAASUAAQAAAAAAAAAAAAAAAAAAAAgAAAAAAAAAAAAAAAABAAAAASUAAAElAAAAtwASALcAAAAAUAAACAAAAAAADgCuAAEAAAAAAAEADgAAAAEAAAAAAAIADgBHAAEAAAAAAAMADgAkAAEAAAAAAAQADgBVAAEAAAAAAAUAFgAOAAEAAAAAAAYABwAyAAEAAAAAAAoANABjAAMAAQQJAAEADgAAAAMAAQQJAAIADgBHAAMAAQQJAAMADgAkAAMAAQQJAAQADgBVAAMAAQQJAAUAFgAOAAMAAQQJAAYADgA5AAMAAQQJAAoANABjAGkAYwBvAG0AbwBvAG4AVgBlAHIAcwBpAG8AbgAgADEALgAwAGkAYwBvAG0AbwBvAG5pY29tb29uAGkAYwBvAG0AbwBvAG4AUgBlAGcAdQBsAGEAcgBpAGMAbwBtAG8AbwBuAEYAbwBuAHQAIABnAGUAbgBlAHIAYQB0AGUAZAAgAGIAeQAgAEkAYwBvAE0AbwBvAG4ALgAAAAADAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA) format('woff');
- font-weight: normal;
- font-style: normal;
-}
-
-.ui.dropdown > .dropdown.icon {
- font-family: 'Dropdown';
- line-height: 1;
- height: 1em;
- width: 1.23em;
- backface-visibility: hidden;
- font-weight: normal;
- font-style: normal;
- text-align: center;
-}
-
-.ui.dropdown > .dropdown.icon {
- width: auto;
-}
-
-.ui.dropdown > .dropdown.icon:before {
- content: '\f0d7';
-}
-
-/* Sub Menu */
-
-.ui.dropdown .menu .item .dropdown.icon:before {
- content: '\f0da' ;
-}
-
-.ui.dropdown .item .left.dropdown.icon:before,
-.ui.dropdown .left.menu .item .dropdown.icon:before {
- content: "\f0d9" ;
-}
-
-/* Vertical Menu Dropdown */
-
-.ui.vertical.menu .dropdown.item > .dropdown.icon:before {
- content: "\f0da" ;
-}
-
-/* Icons for Reference
-.dropdown.down.icon {
- content: "\f0d7";
-}
-.dropdown.up.icon {
- content: "\f0d8";
-}
-.dropdown.left.icon {
- content: "\f0d9";
-}
-.dropdown.icon.icon {
- content: "\f0da";
-}
-*/
-
/*******************************
User Overrides
*******************************/
@@ -4659,38 +4372,6 @@ select.ui.dropdown {
box-shadow: 0 0 0 0 rgba(34, 36, 38, 0.35) inset;
}
-.ui.form .ui.action.input:not([class*="left action"]) input:not([type]):focus,
-.ui.form .ui.action.input:not([class*="left action"]) input[type="date"]:focus,
-.ui.form .ui.action.input:not([class*="left action"]) input[type="datetime-local"]:focus,
-.ui.form .ui.action.input:not([class*="left action"]) input[type="email"]:focus,
-.ui.form .ui.action.input:not([class*="left action"]) input[type="number"]:focus,
-.ui.form .ui.action.input:not([class*="left action"]) input[type="password"]:focus,
-.ui.form .ui.action.input:not([class*="left action"]) input[type="search"]:focus,
-.ui.form .ui.action.input:not([class*="left action"]) input[type="tel"]:focus,
-.ui.form .ui.action.input:not([class*="left action"]) input[type="time"]:focus,
-.ui.form .ui.action.input:not([class*="left action"]) input[type="text"]:focus,
-.ui.form .ui.action.input:not([class*="left action"]) input[type="file"]:focus,
-.ui.form .ui.action.input:not([class*="left action"]) input[type="url"]:focus {
- border-top-right-radius: 0;
- border-bottom-right-radius: 0;
-}
-
-.ui.form .ui[class*="left action"].input input:not([type]),
-.ui.form .ui[class*="left action"].input input[type="date"],
-.ui.form .ui[class*="left action"].input input[type="datetime-local"],
-.ui.form .ui[class*="left action"].input input[type="email"],
-.ui.form .ui[class*="left action"].input input[type="number"],
-.ui.form .ui[class*="left action"].input input[type="password"],
-.ui.form .ui[class*="left action"].input input[type="search"],
-.ui.form .ui[class*="left action"].input input[type="tel"],
-.ui.form .ui[class*="left action"].input input[type="time"],
-.ui.form .ui[class*="left action"].input input[type="text"],
-.ui.form .ui[class*="left action"].input input[type="file"],
-.ui.form .ui[class*="left action"].input input[type="url"] {
- border-bottom-left-radius: 0;
- border-top-left-radius: 0;
-}
-
.ui.form textarea:focus {
color: rgba(0, 0, 0, 0.95);
border-color: #85B7D9;
@@ -6032,30 +5713,6 @@ select.ui.dropdown {
font-size: 1rem;
}
-.ui.mini.form,
-.ui.mini.form .field .dropdown,
-.ui.mini.form .field .dropdown .menu > .item {
- font-size: 0.78571429rem;
-}
-
-.ui.tiny.form,
-.ui.tiny.form .field .dropdown,
-.ui.tiny.form .field .dropdown .menu > .item {
- font-size: 0.85714286rem;
-}
-
-.ui.small.form,
-.ui.small.form .field .dropdown,
-.ui.small.form .field .dropdown .menu > .item {
- font-size: 0.92857143rem;
-}
-
-.ui.large.form,
-.ui.large.form .field .dropdown,
-.ui.large.form .field .dropdown .menu > .item {
- font-size: 1.14285714rem;
-}
-
/*******************************
Theme Overrides
*******************************/
@@ -7147,139 +6804,6 @@ Floated Menu / Item
border-color: currentColor;
}
-/*--------------
- Text Menu
- ---------------*/
-
-.ui.text.menu {
- background: none transparent;
- border-radius: 0;
- box-shadow: none;
- border: none;
- margin: 1em -0.5em;
-}
-
-.ui.text.menu .item {
- border-radius: 0;
- box-shadow: none;
- align-self: center;
- margin: 0 0;
- padding: 0.35714286em 0.5em;
- font-weight: normal;
- color: rgba(0, 0, 0, 0.6);
- transition: opacity 0.1s ease;
-}
-
-/* Border */
-
-.ui.text.menu .item:before,
-.ui.text.menu .menu .item:before {
- display: none !important;
-}
-
-/* Header */
-
-.ui.text.menu .header.item {
- background-color: transparent;
- opacity: 1;
- color: rgba(0, 0, 0, 0.85);
- font-size: 0.92857143em;
- text-transform: uppercase;
- font-weight: 500;
-}
-
-/* Image */
-
-.ui.text.menu .item > img:not(.ui) {
- margin: 0;
-}
-
-/*--- fluid text ---*/
-
-.ui.text.item.menu .item {
- margin: 0;
-}
-
-/*--- vertical text ---*/
-
-.ui.vertical.text.menu {
- margin: 1em 0;
-}
-
-.ui.vertical.text.menu:first-child {
- margin-top: 0;
-}
-
-.ui.vertical.text.menu:last-child {
- margin-bottom: 0;
-}
-
-.ui.vertical.text.menu .item {
- margin: 0.57142857em 0;
- padding-left: 0;
- padding-right: 0;
-}
-
-.ui.vertical.text.menu .item > i.icon {
- float: none;
- margin: 0 0.35714286em 0 0;
-}
-
-.ui.vertical.text.menu .header.item {
- margin: 0.57142857em 0 0.71428571em;
-}
-
-/* Vertical Sub Menu */
-
-.ui.vertical.text.menu .item:not(.dropdown) > .menu {
- margin: 0;
-}
-
-.ui.vertical.text.menu .item:not(.dropdown) > .menu > .item {
- margin: 0;
- padding: 0.5em 0;
-}
-
-/*--- hover ---*/
-
-.ui.text.menu .item:hover {
- opacity: 1;
- background-color: transparent;
-}
-
-/*--- active ---*/
-
-.ui.text.menu .active.item {
- background-color: transparent;
- border: none;
- box-shadow: none;
- font-weight: normal;
- color: rgba(0, 0, 0, 0.95);
-}
-
-/*--- active hover ---*/
-
-.ui.text.menu .active.item:hover {
- background-color: transparent;
-}
-
-/* Disable Bariations */
-
-.ui.text.pointing.menu .active.item:after {
- box-shadow: none;
-}
-
-.ui.text.attached.menu {
- box-shadow: none;
-}
-
-/* Fluid */
-
-.ui.fluid.text.menu {
- margin-left: 0;
- margin-right: 0;
-}
-
/*--------------
Icon Only
---------------*/
@@ -7523,30 +7047,6 @@ Floated Menu / Item
margin: 0 0 0 0.5rem;
}
-/*--------------
- Fitted
- ---------------*/
-
-.ui.fitted.menu .item,
-.ui.fitted.menu .item .menu .item,
-.ui.menu .fitted.item {
- padding: 0;
-}
-
-.ui.horizontally.fitted.menu .item,
-.ui.horizontally.fitted.menu .item .menu .item,
-.ui.menu .horizontally.fitted.item {
- padding-top: 0.92857143em;
- padding-bottom: 0.92857143em;
-}
-
-.ui.vertically.fitted.menu .item,
-.ui.vertically.fitted.menu .item .menu .item,
-.ui.menu .vertically.fitted.item {
- padding-left: 1.14285714em;
- padding-right: 1.14285714em;
-}
-
/*--------------
Borderless
---------------*/
@@ -7665,77 +7165,6 @@ Floated Menu / Item
width: 8.333%;
}
-/*--------------
- Fixed
- ---------------*/
-
-.ui.menu.fixed {
- position: fixed;
- z-index: 101;
- margin: 0;
- width: 100%;
-}
-
-.ui.menu.fixed,
-.ui.menu.fixed .item:first-child,
-.ui.menu.fixed .item:last-child {
- border-radius: 0 !important;
-}
-
-.ui.fixed.menu,
-.ui[class*="top fixed"].menu {
- top: 0;
- left: 0;
- right: auto;
- bottom: auto;
-}
-
-.ui[class*="top fixed"].menu {
- border-top: none;
- border-left: none;
- border-right: none;
-}
-
-.ui[class*="right fixed"].menu {
- border-top: none;
- border-bottom: none;
- border-right: none;
- top: 0;
- right: 0;
- left: auto;
- bottom: auto;
- width: auto;
- height: 100%;
-}
-
-.ui[class*="bottom fixed"].menu {
- border-bottom: none;
- border-left: none;
- border-right: none;
- bottom: 0;
- left: 0;
- top: auto;
- right: auto;
-}
-
-.ui[class*="left fixed"].menu {
- border-top: none;
- border-bottom: none;
- border-left: none;
- top: 0;
- left: 0;
- right: auto;
- bottom: auto;
- width: auto;
- height: 100%;
-}
-
-/* Coupling with Grid */
-
-.ui.fixed.menu + .ui.grid {
- padding-top: 2.75rem;
-}
-
/*-------------------
Pointing
--------------------*/
@@ -8358,62 +7787,6 @@ Floated Menu / Item
Types
*******************************/
-.ui.basic.modal {
- background-color: transparent;
- border: none;
- border-radius: 0;
- box-shadow: none !important;
- color: #FFFFFF;
-}
-
-.ui.basic.modal > .header,
-.ui.basic.modal > .content,
-.ui.basic.modal > .actions {
- background-color: transparent;
-}
-
-.ui.basic.modal > .header {
- color: #FFFFFF;
- border-bottom: none;
-}
-
-.ui.basic.modal > .close {
- top: 1rem;
- right: 1.5rem;
- color: #FFFFFF;
-}
-
-.ui.inverted.dimmer > .basic.modal {
- color: rgba(0, 0, 0, 0.87);
-}
-
-.ui.inverted.dimmer > .ui.basic.modal > .header {
- color: rgba(0, 0, 0, 0.85);
-}
-
-/* Resort to margin positioning if legacy */
-
-.ui.legacy.legacy.modal,
-.ui.legacy.legacy.page.dimmer > .ui.modal {
- left: 50% !important;
-}
-
-.ui.legacy.legacy.modal:not(.aligned),
-.ui.legacy.legacy.page.dimmer > .ui.modal:not(.aligned) {
- top: 50%;
-}
-
-.ui.legacy.legacy.page.dimmer > .ui.scrolling.modal:not(.aligned),
-.ui.page.dimmer > .ui.scrolling.legacy.legacy.modal:not(.aligned),
-.ui.top.aligned.legacy.legacy.page.dimmer > .ui.modal:not(.aligned),
-.ui.top.aligned.dimmer > .ui.legacy.legacy.modal:not(.aligned) {
- top: auto;
-}
-
-.ui.legacy.overlay.fullscreen.modal {
- margin-top: -2rem !important;
-}
-
/*******************************
States
*******************************/
@@ -8509,14 +7882,6 @@ Floated Menu / Item
overflow: auto;
}
-.ui.overlay.fullscreen.modal > .content {
- min-height: calc(100vh - 9.1rem);
-}
-
-.ui.overlay.fullscreen.modal > .scrolling.content {
- max-height: calc(100vh - 9.1rem);
-}
-
/*--------------
Full Screen
---------------*/
@@ -8527,14 +7892,6 @@ Floated Menu / Item
margin: 1em auto;
}
-.ui.overlay.fullscreen.modal {
- width: 100%;
- left: 0;
- margin: 0 auto;
- top: 0;
- border-radius: 0;
-}
-
.ui.modal > .close.inside + .header,
.ui.fullscreen.modal > .header {
padding-right: 2.25rem;
@@ -8547,10 +7904,6 @@ Floated Menu / Item
color: rgba(0, 0, 0, 0.87);
}
-.ui.basic.fullscreen.modal > .close {
- color: #FFFFFF;
-}
-
/*--------------
Size
---------------*/
@@ -8676,45 +8029,6 @@ Floated Menu / Item
}
}
-.ui.large.modal > .header:not(.ui) {
- font-size: 1.6em;
-}
-
-@media only screen and (max-width: 767.98px) {
- .ui.large.modal {
- width: 95%;
- margin: 0 0 0 0;
- }
-}
-
-@media only screen and (min-width: 768px) {
- .ui.large.modal {
- width: 88%;
- margin: 0 0 0 0;
- }
-}
-
-@media only screen and (min-width: 992px) {
- .ui.large.modal {
- width: 1020px;
- margin: 0 0 0 0;
- }
-}
-
-@media only screen and (min-width: 1200px) {
- .ui.large.modal {
- width: 1080px;
- margin: 0 0 0 0;
- }
-}
-
-@media only screen and (min-width: 1920px) {
- .ui.large.modal {
- width: 1140px;
- margin: 0 0 0 0;
- }
-}
-
/*******************************
Theme Overrides
*******************************/
@@ -8957,14 +8271,6 @@ Floated Menu / Item
Active
---------------*/
-.ui.category.search > .results .category.active {
- background: #F3F4F5;
-}
-
-.ui.category.search > .results .category.active > .name {
- color: rgba(0, 0, 0, 0.87);
-}
-
.ui.search > .results .result.active,
.ui.category.search > .results .category .result.active {
position: relative;
@@ -9033,75 +8339,6 @@ Floated Menu / Item
color: #DB2828;
}
-/*--------------
- Category
- ---------------*/
-
-.ui.category.search .results {
- width: 28em;
-}
-
-.ui.category.search .results.animating,
-.ui.category.search .results.visible {
- display: table;
-}
-
-/* Category */
-
-.ui.category.search > .results .category {
- display: table-row;
- background: #F3F4F5;
- box-shadow: none;
- transition: background 0.1s ease, border-color 0.1s ease;
-}
-
-/* Last Category */
-
-.ui.category.search > .results .category:last-child {
- border-bottom: none;
-}
-
-/* First / Last */
-
-.ui.category.search > .results .category:first-child .name + .result {
- border-radius: 0 0.28571429rem 0 0;
-}
-
-.ui.category.search > .results .category:last-child .result:last-child {
- border-radius: 0 0 0.28571429rem 0;
-}
-
-/* Category Result Name */
-
-.ui.category.search > .results .category > .name {
- display: table-cell;
- text-overflow: ellipsis;
- width: 100px;
- white-space: nowrap;
- background: transparent;
- font-family: var(--fonts-regular);
- font-size: 1em;
- padding: 0.4em 1em;
- font-weight: 500;
- color: rgba(0, 0, 0, 0.4);
- border-bottom: 1px solid rgba(34, 36, 38, 0.1);
-}
-
-/* Category Result */
-
-.ui.category.search > .results .category .results {
- display: table-cell;
- background: #FFFFFF;
- border-left: 1px solid rgba(34, 36, 38, 0.15);
- border-bottom: 1px solid rgba(34, 36, 38, 0.1);
-}
-
-.ui.category.search > .results .category .result {
- border-bottom: 1px solid rgba(34, 36, 38, 0.1);
- transition: background 0.1s ease, border-color 0.1s ease;
- padding: 0.85714286em 1.14285714em;
-}
-
/*******************************
Variations
*******************************/
@@ -9143,92 +8380,6 @@ Floated Menu / Item
}
}
-@media only screen and (max-width: 767.98px) {
- .ui.search.short > .results {
- max-height: 12.17714286em;
- }
-
- .ui.search[class*="very short"] > .results {
- max-height: 9.13285714em;
- }
-
- .ui.search.long > .results {
- max-height: 24.35428571em;
- }
-
- .ui.search[class*="very long"] > .results {
- max-height: 36.53142857em;
- }
-}
-
-@media only screen and (min-width: 768px) {
- .ui.search.short > .results {
- max-height: 18.26571429em;
- }
-
- .ui.search[class*="very short"] > .results {
- max-height: 13.69928571em;
- }
-
- .ui.search.long > .results {
- max-height: 36.53142857em;
- }
-
- .ui.search[class*="very long"] > .results {
- max-height: 54.79714286em;
- }
-}
-
-@media only screen and (min-width: 992px) {
- .ui.search.short > .results {
- max-height: 24.35428571em;
- }
-
- .ui.search[class*="very short"] > .results {
- max-height: 18.26571429em;
- }
-
- .ui.search.long > .results {
- max-height: 48.70857143em;
- }
-
- .ui.search[class*="very long"] > .results {
- max-height: 73.06285714em;
- }
-}
-
-@media only screen and (min-width: 1920px) {
- .ui.search.short > .results {
- max-height: 36.53142857em;
- }
-
- .ui.search[class*="very short"] > .results {
- max-height: 27.39857143em;
- }
-
- .ui.search.long > .results {
- max-height: 73.06285714em;
- }
-
- .ui.search[class*="very long"] > .results {
- max-height: 109.59428571em;
- }
-}
-
-/*-------------------
- Left / Right
- --------------------*/
-
-.ui[class*="left aligned"].search > .results {
- right: auto;
- left: 0;
-}
-
-.ui[class*="right aligned"].search > .results {
- right: 0;
- left: auto;
-}
-
/*--------------
Fluid
---------------*/
@@ -9277,86 +8428,4 @@ Floated Menu / Item
/*******************************
Site Overrides
-*******************************/
-/*!
- * # Fomantic-UI - Tab
- * http://github.com/fomantic/Fomantic-UI/
- *
- *
- * Released under the MIT license
- * http://opensource.org/licenses/MIT
- *
- */
-
-/*******************************
- UI Tabs
-*******************************/
-
-.ui.tab {
- display: none;
-}
-
-/*******************************
- States
-*******************************/
-
-/*--------------------
- Active
----------------------*/
-
-.ui.tab.active,
-.ui.tab.open {
- display: block;
-}
-
-/*--------------------
- Loading
- ---------------------*/
-
-.ui.tab.loading {
- position: relative;
- overflow: hidden;
- display: block;
- min-height: 250px;
-}
-
-.ui.tab.loading * {
- position: relative !important;
- left: -10000px !important;
-}
-
-.ui.tab.loading:before,
-.ui.tab.loading.segment:before {
- position: absolute;
- content: '';
- top: 50%;
- left: 50%;
- margin: -1.25em 0 0 -1.25em;
- width: 2.5em;
- height: 2.5em;
- border-radius: 500rem;
- border: 0.2em solid rgba(0, 0, 0, 0.1);
-}
-
-.ui.tab.loading:after,
-.ui.tab.loading.segment:after {
- position: absolute;
- content: '';
- top: 50%;
- left: 50%;
- margin: -1.25em 0 0 -1.25em;
- width: 2.5em;
- height: 2.5em;
- animation: loader 0.6s infinite linear;
- border: 0.2em solid #767676;
- border-radius: 500rem;
- box-shadow: 0 0 0 1px transparent;
-}
-
-/*******************************
- Tab Overrides
-*******************************/
-
-/*******************************
- User Overrides
*******************************/
\ No newline at end of file
diff --git a/web_src/fomantic/build/semantic.js b/web_src/fomantic/build/semantic.js
index 393d9d8142..80ddb88eda 100644
--- a/web_src/fomantic/build/semantic.js
+++ b/web_src/fomantic/build/semantic.js
@@ -10234,1005 +10234,3 @@ $.fn.search.settings = {
};
})( jQuery, window, document );
-
-/*!
- * # Fomantic-UI - Tab
- * http://github.com/fomantic/Fomantic-UI/
- *
- *
- * Released under the MIT license
- * http://opensource.org/licenses/MIT
- *
- */
-
-;(function ($, window, document, undefined) {
-
-'use strict';
-
-$.isWindow = $.isWindow || function(obj) {
- return obj != null && obj === obj.window;
-};
-$.isFunction = $.isFunction || function(obj) {
- return typeof obj === "function" && typeof obj.nodeType !== "number";
-};
-
-window = (typeof window != 'undefined' && window.Math == Math)
- ? window
- : (typeof self != 'undefined' && self.Math == Math)
- ? self
- : Function('return this')()
-;
-
-$.fn.tab = function(parameters) {
-
- var
- // use window context if none specified
- $allModules = $.isFunction(this)
- ? $(window)
- : $(this),
-
- moduleSelector = $allModules.selector || '',
- time = new Date().getTime(),
- performance = [],
-
- query = arguments[0],
- methodInvoked = (typeof query == 'string'),
- queryArguments = [].slice.call(arguments, 1),
-
- initializedHistory = false,
- returnedValue
- ;
-
- $allModules
- .each(function() {
- var
-
- settings = ( $.isPlainObject(parameters) )
- ? $.extend(true, {}, $.fn.tab.settings, parameters)
- : $.extend({}, $.fn.tab.settings),
-
- className = settings.className,
- metadata = settings.metadata,
- selector = settings.selector,
- error = settings.error,
- regExp = settings.regExp,
-
- eventNamespace = '.' + settings.namespace,
- moduleNamespace = 'module-' + settings.namespace,
-
- $module = $(this),
- $context,
- $tabs,
-
- cache = {},
- firstLoad = true,
- recursionDepth = 0,
- element = this,
- instance = $module.data(moduleNamespace),
-
- activeTabPath,
- parameterArray,
- module,
-
- historyEvent
-
- ;
-
- module = {
-
- initialize: function() {
- module.debug('Initializing tab menu item', $module);
- module.fix.callbacks();
- module.determineTabs();
-
- module.debug('Determining tabs', settings.context, $tabs);
- // set up automatic routing
- if(settings.auto) {
- module.set.auto();
- }
- module.bind.events();
-
- if(settings.history && !initializedHistory) {
- module.initializeHistory();
- initializedHistory = true;
- }
-
- if(settings.autoTabActivation && instance === undefined && module.determine.activeTab() == null) {
- module.debug('No active tab detected, setting first tab active', module.get.initialPath());
- module.changeTab(settings.autoTabActivation === true ? module.get.initialPath() : settings.autoTabActivation);
- };
-
- module.instantiate();
- },
-
- instantiate: function () {
- module.verbose('Storing instance of module', module);
- instance = module;
- $module
- .data(moduleNamespace, module)
- ;
- },
-
- destroy: function() {
- module.debug('Destroying tabs', $module);
- $module
- .removeData(moduleNamespace)
- .off(eventNamespace)
- ;
- },
-
- bind: {
- events: function() {
- // if using $.tab don't add events
- if( !$.isWindow( element ) ) {
- module.debug('Attaching tab activation events to element', $module);
- $module
- .on('click' + eventNamespace, module.event.click)
- ;
- }
- }
- },
-
- determineTabs: function() {
- var
- $reference
- ;
-
- // determine tab context
- if(settings.context === 'parent') {
- if($module.closest(selector.ui).length > 0) {
- $reference = $module.closest(selector.ui);
- module.verbose('Using closest UI element as parent', $reference);
- }
- else {
- $reference = $module;
- }
- $context = $reference.parent();
- module.verbose('Determined parent element for creating context', $context);
- }
- else if(settings.context) {
- $context = $(settings.context);
- module.verbose('Using selector for tab context', settings.context, $context);
- }
- else {
- $context = $('body');
- }
- // find tabs
- if(settings.childrenOnly) {
- $tabs = $context.children(selector.tabs);
- module.debug('Searching tab context children for tabs', $context, $tabs);
- }
- else {
- $tabs = $context.find(selector.tabs);
- module.debug('Searching tab context for tabs', $context, $tabs);
- }
- },
-
- fix: {
- callbacks: function() {
- if( $.isPlainObject(parameters) && (parameters.onTabLoad || parameters.onTabInit) ) {
- if(parameters.onTabLoad) {
- parameters.onLoad = parameters.onTabLoad;
- delete parameters.onTabLoad;
- module.error(error.legacyLoad, parameters.onLoad);
- }
- if(parameters.onTabInit) {
- parameters.onFirstLoad = parameters.onTabInit;
- delete parameters.onTabInit;
- module.error(error.legacyInit, parameters.onFirstLoad);
- }
- settings = $.extend(true, {}, $.fn.tab.settings, parameters);
- }
- }
- },
-
- initializeHistory: function() {
- module.debug('Initializing page state');
- if( $.address === undefined ) {
- module.error(error.state);
- return false;
- }
- else {
- if(settings.historyType == 'state') {
- module.debug('Using HTML5 to manage state');
- if(settings.path !== false) {
- $.address
- .history(true)
- .state(settings.path)
- ;
- }
- else {
- module.error(error.path);
- return false;
- }
- }
- $.address
- .bind('change', module.event.history.change)
- ;
- }
- },
-
- event: {
- click: function(event) {
- var
- tabPath = $(this).data(metadata.tab)
- ;
- if(tabPath !== undefined) {
- if(settings.history) {
- module.verbose('Updating page state', event);
- $.address.value(tabPath);
- }
- else {
- module.verbose('Changing tab', event);
- module.changeTab(tabPath);
- }
- event.preventDefault();
- }
- else {
- module.debug('No tab specified');
- }
- },
- history: {
- change: function(event) {
- var
- tabPath = event.pathNames.join('/') || module.get.initialPath(),
- pageTitle = settings.templates.determineTitle(tabPath) || false
- ;
- module.performance.display();
- module.debug('History change event', tabPath, event);
- historyEvent = event;
- if(tabPath !== undefined) {
- module.changeTab(tabPath);
- }
- if(pageTitle) {
- $.address.title(pageTitle);
- }
- }
- }
- },
-
- refresh: function() {
- if(activeTabPath) {
- module.debug('Refreshing tab', activeTabPath);
- module.changeTab(activeTabPath);
- }
- },
-
- cache: {
-
- read: function(cacheKey) {
- return (cacheKey !== undefined)
- ? cache[cacheKey]
- : false
- ;
- },
- add: function(cacheKey, content) {
- cacheKey = cacheKey || activeTabPath;
- module.debug('Adding cached content for', cacheKey);
- cache[cacheKey] = content;
- },
- remove: function(cacheKey) {
- cacheKey = cacheKey || activeTabPath;
- module.debug('Removing cached content for', cacheKey);
- delete cache[cacheKey];
- }
- },
-
- escape: {
- string: function(text) {
- text = String(text);
- return text.replace(regExp.escape, '\\$&');
- }
- },
-
- set: {
- auto: function() {
- var
- url = (typeof settings.path == 'string')
- ? settings.path.replace(/\/$/, '') + '/{$tab}'
- : '/{$tab}'
- ;
- module.verbose('Setting up automatic tab retrieval from server', url);
- if($.isPlainObject(settings.apiSettings)) {
- settings.apiSettings.url = url;
- }
- else {
- settings.apiSettings = {
- url: url
- };
- }
- },
- loading: function(tabPath) {
- var
- $tab = module.get.tabElement(tabPath),
- isLoading = $tab.hasClass(className.loading)
- ;
- if(!isLoading) {
- module.verbose('Setting loading state for', $tab);
- $tab
- .addClass(className.loading)
- .siblings($tabs)
- .removeClass(className.active + ' ' + className.loading)
- ;
- if($tab.length > 0) {
- settings.onRequest.call($tab[0], tabPath);
- }
- }
- },
- state: function(state) {
- $.address.value(state);
- }
- },
-
- changeTab: function(tabPath) {
- var
- pushStateAvailable = (window.history && window.history.pushState),
- shouldIgnoreLoad = (pushStateAvailable && settings.ignoreFirstLoad && firstLoad),
- remoteContent = (settings.auto || $.isPlainObject(settings.apiSettings) ),
- // only add default path if not remote content
- pathArray = (remoteContent && !shouldIgnoreLoad)
- ? module.utilities.pathToArray(tabPath)
- : module.get.defaultPathArray(tabPath)
- ;
- tabPath = module.utilities.arrayToPath(pathArray);
- $.each(pathArray, function(index, tab) {
- var
- currentPathArray = pathArray.slice(0, index + 1),
- currentPath = module.utilities.arrayToPath(currentPathArray),
-
- isTab = module.is.tab(currentPath),
- isLastIndex = (index + 1 == pathArray.length),
-
- $tab = module.get.tabElement(currentPath),
- $anchor,
- nextPathArray,
- nextPath,
- isLastTab
- ;
- module.verbose('Looking for tab', tab);
- if(isTab) {
- module.verbose('Tab was found', tab);
- // scope up
- activeTabPath = currentPath;
- parameterArray = module.utilities.filterArray(pathArray, currentPathArray);
-
- if(isLastIndex) {
- isLastTab = true;
- }
- else {
- nextPathArray = pathArray.slice(0, index + 2);
- nextPath = module.utilities.arrayToPath(nextPathArray);
- isLastTab = ( !module.is.tab(nextPath) );
- if(isLastTab) {
- module.verbose('Tab parameters found', nextPathArray);
- }
- }
- if(isLastTab && remoteContent) {
- if(!shouldIgnoreLoad) {
- module.activate.navigation(currentPath);
- module.fetch.content(currentPath, tabPath);
- }
- else {
- module.debug('Ignoring remote content on first tab load', currentPath);
- firstLoad = false;
- module.cache.add(tabPath, $tab.html());
- module.activate.all(currentPath);
- settings.onFirstLoad.call($tab[0], currentPath, parameterArray, historyEvent);
- settings.onLoad.call($tab[0], currentPath, parameterArray, historyEvent);
- }
- return false;
- }
- else {
- module.debug('Opened local tab', currentPath);
- module.activate.all(currentPath);
- if( !module.cache.read(currentPath) ) {
- module.cache.add(currentPath, true);
- module.debug('First time tab loaded calling tab init');
- settings.onFirstLoad.call($tab[0], currentPath, parameterArray, historyEvent);
- }
- settings.onLoad.call($tab[0], currentPath, parameterArray, historyEvent);
- }
-
- }
- else if(tabPath.search('/') == -1 && tabPath !== '') {
- // look for in page anchor
- tabPath = module.escape.string(tabPath);
- $anchor = $('#' + tabPath + ', a[name="' + tabPath + '"]');
- currentPath = $anchor.closest('[data-tab]').data(metadata.tab);
- $tab = module.get.tabElement(currentPath);
- // if anchor exists use parent tab
- if($anchor && $anchor.length > 0 && currentPath) {
- module.debug('Anchor link used, opening parent tab', $tab, $anchor);
- if( !$tab.hasClass(className.active) ) {
- setTimeout(function() {
- module.scrollTo($anchor);
- }, 0);
- }
- module.activate.all(currentPath);
- if( !module.cache.read(currentPath) ) {
- module.cache.add(currentPath, true);
- module.debug('First time tab loaded calling tab init');
- settings.onFirstLoad.call($tab[0], currentPath, parameterArray, historyEvent);
- }
- settings.onLoad.call($tab[0], currentPath, parameterArray, historyEvent);
- return false;
- }
- }
- else {
- module.error(error.missingTab, $module, $context, currentPath);
- return false;
- }
- });
- },
-
- scrollTo: function($element) {
- var
- scrollOffset = ($element && $element.length > 0)
- ? $element.offset().top
- : false
- ;
- if(scrollOffset !== false) {
- module.debug('Forcing scroll to an in-page link in a hidden tab', scrollOffset, $element);
- $(document).scrollTop(scrollOffset);
- }
- },
-
- update: {
- content: function(tabPath, html, evaluateScripts) {
- var
- $tab = module.get.tabElement(tabPath),
- tab = $tab[0]
- ;
- evaluateScripts = (evaluateScripts !== undefined)
- ? evaluateScripts
- : settings.evaluateScripts
- ;
- if(typeof settings.cacheType == 'string' && settings.cacheType.toLowerCase() == 'dom' && typeof html !== 'string') {
- $tab
- .empty()
- .append($(html).clone(true))
- ;
- }
- else {
- if(evaluateScripts) {
- module.debug('Updating HTML and evaluating inline scripts', tabPath, html);
- $tab.html(html);
- }
- else {
- module.debug('Updating HTML', tabPath, html);
- tab.innerHTML = html;
- }
- }
- }
- },
-
- fetch: {
-
- content: function(tabPath, fullTabPath) {
- var
- $tab = module.get.tabElement(tabPath),
- apiSettings = {
- dataType : 'html',
- encodeParameters : false,
- on : 'now',
- cache : settings.alwaysRefresh,
- headers : {
- 'X-Remote': true
- },
- onSuccess : function(response) {
- if(settings.cacheType == 'response') {
- module.cache.add(fullTabPath, response);
- }
- module.update.content(tabPath, response);
- if(tabPath == activeTabPath) {
- module.debug('Content loaded', tabPath);
- module.activate.tab(tabPath);
- }
- else {
- module.debug('Content loaded in background', tabPath);
- }
- settings.onFirstLoad.call($tab[0], tabPath, parameterArray, historyEvent);
- settings.onLoad.call($tab[0], tabPath, parameterArray, historyEvent);
-
- if(settings.loadOnce) {
- module.cache.add(fullTabPath, true);
- }
- else if(typeof settings.cacheType == 'string' && settings.cacheType.toLowerCase() == 'dom' && $tab.children().length > 0) {
- setTimeout(function() {
- var
- $clone = $tab.children().clone(true)
- ;
- $clone = $clone.not('script');
- module.cache.add(fullTabPath, $clone);
- }, 0);
- }
- else {
- module.cache.add(fullTabPath, $tab.html());
- }
- },
- urlData: {
- tab: fullTabPath
- }
- },
- request = $tab.api('get request') || false,
- existingRequest = ( request && request.state() === 'pending' ),
- requestSettings,
- cachedContent
- ;
-
- fullTabPath = fullTabPath || tabPath;
- cachedContent = module.cache.read(fullTabPath);
-
-
- if(settings.cache && cachedContent) {
- module.activate.tab(tabPath);
- module.debug('Adding cached content', fullTabPath);
- if(!settings.loadOnce) {
- if(settings.evaluateScripts == 'once') {
- module.update.content(tabPath, cachedContent, false);
- }
- else {
- module.update.content(tabPath, cachedContent);
- }
- }
- settings.onLoad.call($tab[0], tabPath, parameterArray, historyEvent);
- }
- else if(existingRequest) {
- module.set.loading(tabPath);
- module.debug('Content is already loading', fullTabPath);
- }
- else if($.api !== undefined) {
- requestSettings = $.extend(true, {}, settings.apiSettings, apiSettings);
- module.debug('Retrieving remote content', fullTabPath, requestSettings);
- module.set.loading(tabPath);
- $tab.api(requestSettings);
- }
- else {
- module.error(error.api);
- }
- }
- },
-
- activate: {
- all: function(tabPath) {
- module.activate.tab(tabPath);
- module.activate.navigation(tabPath);
- },
- tab: function(tabPath) {
- var
- $tab = module.get.tabElement(tabPath),
- $deactiveTabs = (settings.deactivate == 'siblings')
- ? $tab.siblings($tabs)
- : $tabs.not($tab),
- isActive = $tab.hasClass(className.active)
- ;
- module.verbose('Showing tab content for', $tab);
- if(!isActive) {
- $tab
- .addClass(className.active)
- ;
- $deactiveTabs
- .removeClass(className.active + ' ' + className.loading)
- ;
- if($tab.length > 0) {
- settings.onVisible.call($tab[0], tabPath);
- }
- }
- },
- navigation: function(tabPath) {
- var
- $navigation = module.get.navElement(tabPath),
- $deactiveNavigation = (settings.deactivate == 'siblings')
- ? $navigation.siblings($allModules)
- : $allModules.not($navigation),
- isActive = $navigation.hasClass(className.active)
- ;
- module.verbose('Activating tab navigation for', $navigation, tabPath);
- if(!isActive) {
- $navigation
- .addClass(className.active)
- ;
- $deactiveNavigation
- .removeClass(className.active + ' ' + className.loading)
- ;
- }
- }
- },
-
- deactivate: {
- all: function() {
- module.deactivate.navigation();
- module.deactivate.tabs();
- },
- navigation: function() {
- $allModules
- .removeClass(className.active)
- ;
- },
- tabs: function() {
- $tabs
- .removeClass(className.active + ' ' + className.loading)
- ;
- }
- },
-
- is: {
- tab: function(tabName) {
- return (tabName !== undefined)
- ? ( module.get.tabElement(tabName).length > 0 )
- : false
- ;
- }
- },
-
- get: {
- initialPath: function() {
- return $allModules.eq(0).data(metadata.tab) || $tabs.eq(0).data(metadata.tab);
- },
- path: function() {
- return $.address.value();
- },
- // adds default tabs to tab path
- defaultPathArray: function(tabPath) {
- return module.utilities.pathToArray( module.get.defaultPath(tabPath) );
- },
- defaultPath: function(tabPath) {
- var
- $defaultNav = $allModules.filter('[data-' + metadata.tab + '^="' + module.escape.string(tabPath) + '/"]').eq(0),
- defaultTab = $defaultNav.data(metadata.tab) || false
- ;
- if( defaultTab ) {
- module.debug('Found default tab', defaultTab);
- if(recursionDepth < settings.maxDepth) {
- recursionDepth++;
- return module.get.defaultPath(defaultTab);
- }
- module.error(error.recursion);
- }
- else {
- module.debug('No default tabs found for', tabPath, $tabs);
- }
- recursionDepth = 0;
- return tabPath;
- },
- navElement: function(tabPath) {
- tabPath = tabPath || activeTabPath;
- return $allModules.filter('[data-' + metadata.tab + '="' + module.escape.string(tabPath) + '"]');
- },
- tabElement: function(tabPath) {
- var
- $fullPathTab,
- $simplePathTab,
- tabPathArray,
- lastTab
- ;
- tabPath = tabPath || activeTabPath;
- tabPathArray = module.utilities.pathToArray(tabPath);
- lastTab = module.utilities.last(tabPathArray);
- $fullPathTab = $tabs.filter('[data-' + metadata.tab + '="' + module.escape.string(tabPath) + '"]');
- $simplePathTab = $tabs.filter('[data-' + metadata.tab + '="' + module.escape.string(lastTab) + '"]');
- return ($fullPathTab.length > 0)
- ? $fullPathTab
- : $simplePathTab
- ;
- },
- tab: function() {
- return activeTabPath;
- }
- },
-
- determine: {
- activeTab: function() {
- var activeTab = null;
-
- $tabs.each(function(_index, tab) {
- var $tab = $(tab);
-
- if( $tab.hasClass(className.active) ) {
- var
- tabPath = $(this).data(metadata.tab),
- $anchor = $allModules.filter('[data-' + metadata.tab + '="' + module.escape.string(tabPath) + '"]')
- ;
-
- if( $anchor.hasClass(className.active) ) {
- activeTab = tabPath;
- }
- }
- });
-
- return activeTab;
- }
- },
-
- utilities: {
- filterArray: function(keepArray, removeArray) {
- return $.grep(keepArray, function(keepValue) {
- return ( $.inArray(keepValue, removeArray) == -1);
- });
- },
- last: function(array) {
- return Array.isArray(array)
- ? array[ array.length - 1]
- : false
- ;
- },
- pathToArray: function(pathName) {
- if(pathName === undefined) {
- pathName = activeTabPath;
- }
- return typeof pathName == 'string'
- ? pathName.split('/')
- : [pathName]
- ;
- },
- arrayToPath: function(pathArray) {
- return Array.isArray(pathArray)
- ? pathArray.join('/')
- : false
- ;
- }
- },
-
- setting: function(name, value) {
- module.debug('Changing setting', name, value);
- if( $.isPlainObject(name) ) {
- $.extend(true, settings, name);
- }
- else if(value !== undefined) {
- if($.isPlainObject(settings[name])) {
- $.extend(true, settings[name], value);
- }
- else {
- settings[name] = value;
- }
- }
- else {
- return settings[name];
- }
- },
- internal: function(name, value) {
- if( $.isPlainObject(name) ) {
- $.extend(true, module, name);
- }
- else if(value !== undefined) {
- module[name] = value;
- }
- else {
- return module[name];
- }
- },
- debug: function() {
- if(!settings.silent && settings.debug) {
- if(settings.performance) {
- module.performance.log(arguments);
- }
- else {
- module.debug = Function.prototype.bind.call(console.info, console, settings.name + ':');
- module.debug.apply(console, arguments);
- }
- }
- },
- verbose: function() {
- if(!settings.silent && settings.verbose && settings.debug) {
- if(settings.performance) {
- module.performance.log(arguments);
- }
- else {
- module.verbose = Function.prototype.bind.call(console.info, console, settings.name + ':');
- module.verbose.apply(console, arguments);
- }
- }
- },
- error: function() {
- if(!settings.silent) {
- module.error = Function.prototype.bind.call(console.error, console, settings.name + ':');
- module.error.apply(console, arguments);
- }
- },
- performance: {
- log: function(message) {
- var
- currentTime,
- executionTime,
- previousTime
- ;
- if(settings.performance) {
- currentTime = new Date().getTime();
- previousTime = time || currentTime;
- executionTime = currentTime - previousTime;
- time = currentTime;
- performance.push({
- 'Name' : message[0],
- 'Arguments' : [].slice.call(message, 1) || '',
- 'Element' : element,
- 'Execution Time' : executionTime
- });
- }
- clearTimeout(module.performance.timer);
- module.performance.timer = setTimeout(module.performance.display, 500);
- },
- display: function() {
- var
- title = settings.name + ':',
- totalTime = 0
- ;
- time = false;
- clearTimeout(module.performance.timer);
- $.each(performance, function(index, data) {
- totalTime += data['Execution Time'];
- });
- title += ' ' + totalTime + 'ms';
- if(moduleSelector) {
- title += ' \'' + moduleSelector + '\'';
- }
- if( (console.group !== undefined || console.table !== undefined) && performance.length > 0) {
- console.groupCollapsed(title);
- if(console.table) {
- console.table(performance);
- }
- else {
- $.each(performance, function(index, data) {
- console.log(data['Name'] + ': ' + data['Execution Time']+'ms');
- });
- }
- console.groupEnd();
- }
- performance = [];
- }
- },
- invoke: function(query, passedArguments, context) {
- var
- object = instance,
- maxDepth,
- found,
- response
- ;
- passedArguments = passedArguments || queryArguments;
- context = element || context;
- if(typeof query == 'string' && object !== undefined) {
- query = query.split(/[\. ]/);
- maxDepth = query.length - 1;
- $.each(query, function(depth, value) {
- var camelCaseValue = (depth != maxDepth)
- ? value + query[depth + 1].charAt(0).toUpperCase() + query[depth + 1].slice(1)
- : query
- ;
- if( $.isPlainObject( object[camelCaseValue] ) && (depth != maxDepth) ) {
- object = object[camelCaseValue];
- }
- else if( object[camelCaseValue] !== undefined ) {
- found = object[camelCaseValue];
- return false;
- }
- else if( $.isPlainObject( object[value] ) && (depth != maxDepth) ) {
- object = object[value];
- }
- else if( object[value] !== undefined ) {
- found = object[value];
- return false;
- }
- else {
- module.error(error.method, query);
- return false;
- }
- });
- }
- if ( $.isFunction( found ) ) {
- response = found.apply(context, passedArguments);
- }
- else if(found !== undefined) {
- response = found;
- }
- if(Array.isArray(returnedValue)) {
- returnedValue.push(response);
- }
- else if(returnedValue !== undefined) {
- returnedValue = [returnedValue, response];
- }
- else if(response !== undefined) {
- returnedValue = response;
- }
- return found;
- }
- };
- if(methodInvoked) {
- if(instance === undefined) {
- module.initialize();
- }
- module.invoke(query);
- }
- else {
- if(instance !== undefined) {
- instance.invoke('destroy');
- }
- module.initialize();
- }
- })
- ;
- return (returnedValue !== undefined)
- ? returnedValue
- : this
- ;
-
-};
-
-// shortcut for tabbed content with no defined navigation
-$.tab = function() {
- $(window).tab.apply(this, arguments);
-};
-
-$.fn.tab.settings = {
-
- name : 'Tab',
- namespace : 'tab',
-
- silent : false,
- debug : false,
- verbose : false,
- performance : true,
-
- auto : false, // uses pjax style endpoints fetching content from same url with remote-content headers
- history : false, // use browser history
- historyType : 'hash', // #/ or html5 state
- path : false, // base path of url
-
- context : false, // specify a context that tabs must appear inside
- childrenOnly : false, // use only tabs that are children of context
- maxDepth : 25, // max depth a tab can be nested
-
- deactivate : 'siblings', // whether tabs should deactivate sibling menu elements or all elements initialized together
-
- alwaysRefresh : false, // load tab content new every tab click
- cache : true, // cache the content requests to pull locally
- loadOnce : false, // Whether tab data should only be loaded once when using remote content
- cacheType : 'response', // Whether to cache exact response, or to html cache contents after scripts execute
- ignoreFirstLoad : false, // don't load remote content on first load
-
- apiSettings : false, // settings for api call
- evaluateScripts : 'once', // whether inline scripts should be parsed (true/false/once). Once will not re-evaluate on cached content
- autoTabActivation: true, // whether a non existing active tab will auto activate the first available tab
-
- onFirstLoad : function(tabPath, parameterArray, historyEvent) {}, // called first time loaded
- onLoad : function(tabPath, parameterArray, historyEvent) {}, // called on every load
- onVisible : function(tabPath, parameterArray, historyEvent) {}, // called every time tab visible
- onRequest : function(tabPath, parameterArray, historyEvent) {}, // called ever time a tab beings loading remote content
-
- templates : {
- determineTitle: function(tabArray) {} // returns page title for path
- },
-
- error: {
- api : 'You attempted to load content without API module',
- method : 'The method you called is not defined',
- missingTab : 'Activated tab cannot be found. Tabs are case-sensitive.',
- noContent : 'The tab you specified is missing a content url.',
- path : 'History enabled, but no path was specified',
- recursion : 'Max recursive depth reached',
- legacyInit : 'onTabInit has been renamed to onFirstLoad in 2.0, please adjust your code.',
- legacyLoad : 'onTabLoad has been renamed to onLoad in 2.0. Please adjust your code',
- state : 'History requires Asual\'s Address library '
- },
-
- regExp : {
- escape : /[-[\]{}()*+?.,\\^$|#\s:=@]/g
- },
-
- metadata : {
- tab : 'tab',
- loaded : 'loaded',
- promise: 'promise'
- },
-
- className : {
- loading : 'loading',
- active : 'active'
- },
-
- selector : {
- tabs : '.ui.tab',
- ui : '.ui'
- }
-
-};
-
-})( jQuery, window, document );
diff --git a/web_src/fomantic/semantic.json b/web_src/fomantic/semantic.json
index 69474b7539..14afba2193 100644
--- a/web_src/fomantic/semantic.json
+++ b/web_src/fomantic/semantic.json
@@ -27,7 +27,6 @@
"form",
"menu",
"modal",
- "search",
- "tab"
+ "search"
]
}
diff --git a/web_src/js/features/common-global.js b/web_src/js/features/common-global.js
index e652825226..03ad5cea05 100644
--- a/web_src/js/features/common-global.js
+++ b/web_src/js/features/common-global.js
@@ -12,6 +12,7 @@ import {confirmModal} from './comp/ConfirmModal.js';
import {showErrorToast} from '../modules/toast.js';
import {request, POST, GET} from '../modules/fetch.js';
import '../htmx.js';
+import {initTab} from '../modules/tab.ts';
const {appUrl, appSubUrl, csrfToken, i18n} = window.config;
@@ -195,7 +196,9 @@ export function initGlobalCommon() {
$uiDropdowns.filter('.upward').dropdown('setting', 'direction', 'upward');
$uiDropdowns.filter('.downward').dropdown('setting', 'direction', 'downward');
- $('.tabular.menu .item').tab();
+ for (const el of document.querySelectorAll('.tabular.menu')) {
+ initTab(el);
+ }
initSubmitEventPolyfill();
document.addEventListener('submit', formFetchAction);
diff --git a/web_src/js/features/comp/ComboMarkdownEditor.js b/web_src/js/features/comp/ComboMarkdownEditor.js
index 4edd13bfc2..d96628c644 100644
--- a/web_src/js/features/comp/ComboMarkdownEditor.js
+++ b/web_src/js/features/comp/ComboMarkdownEditor.js
@@ -10,6 +10,7 @@ import {easyMDEToolbarActions} from './EasyMDEToolbarActions.js';
import {initTextExpander} from './TextExpander.js';
import {showErrorToast, showHintToast} from '../../modules/toast.js';
import {POST} from '../../modules/fetch.js';
+import {initTab} from '../../modules/tab.ts';
/**
* validate if the given textarea is non-empty.
@@ -200,7 +201,8 @@ class ComboMarkdownEditor {
setupTab() {
const $container = $(this.container);
- const tabs = $container[0].querySelectorAll('.switch > .item');
+ const switchEl = $container[0].querySelector('.switch');
+ const tabs = switchEl.querySelectorAll('.item');
// Fomantic Tab requires the "data-tab" to be globally unique.
// So here it uses our defined "data-tab-for" and "data-tab-panel" to generate the "data-tab" attribute for Fomantic.
@@ -221,7 +223,7 @@ class ComboMarkdownEditor {
});
});
- $(tabs).tab();
+ initTab(switchEl);
this.previewUrl = tabPreviewer.getAttribute('data-preview-url');
this.previewContext = tabPreviewer.getAttribute('data-preview-context');
diff --git a/web_src/js/features/repo-editor.js b/web_src/js/features/repo-editor.js
index d3f5f2f8a9..ac4fc8a75e 100644
--- a/web_src/js/features/repo-editor.js
+++ b/web_src/js/features/repo-editor.js
@@ -5,10 +5,11 @@ import {hideElem, showElem, createElementFromHTML} from '../utils/dom.js';
import {initMarkupContent} from '../markup/content.js';
import {attachRefIssueContextPopup} from './contextpopup.js';
import {POST} from '../modules/fetch.js';
+import {initTab} from '../modules/tab.ts';
function initEditPreviewTab($form) {
const $tabMenu = $form.find('.tabular.menu');
- $tabMenu.find('.item').tab();
+ initTab($tabMenu[0]);
const $previewTab = $tabMenu.find(
`.item[data-tab="${$tabMenu.data('preview')}"]`,
);
diff --git a/web_src/js/modules/fomantic.js b/web_src/js/modules/fomantic.js
index c736026c6c..9d27a9bb47 100644
--- a/web_src/js/modules/fomantic.js
+++ b/web_src/js/modules/fomantic.js
@@ -11,9 +11,6 @@ import {initDimmer} from './dimmer.ts';
export const fomanticMobileScreen = window.matchMedia('only screen and (max-width: 767.98px)');
export function initGiteaFomantic() {
- // Silence fomantic's error logging when tabs are used without a target content element
- $.fn.tab.settings.silent = true;
-
// By default, use "exact match" for full text search
$.fn.dropdown.settings.fullTextSearch = 'exact';
// Do not use "cursor: pointer" for dropdown labels
diff --git a/web_src/js/modules/tab.ts b/web_src/js/modules/tab.ts
new file mode 100644
index 0000000000..a19281793f
--- /dev/null
+++ b/web_src/js/modules/tab.ts
@@ -0,0 +1,36 @@
+export function initTab(parentEl: Element) {
+ if (!parentEl) {
+ return;
+ }
+
+ // Keep track of which tab is active for this element.
+ let activeTabPath = parentEl.querySelector('.item.active')?.getAttribute('data-tab');
+ if (!activeTabPath) {
+ return;
+ }
+
+ for (const el of parentEl.querySelectorAll('.item')) {
+ el.addEventListener('click', (ev) => {
+ // There's no data-tab attribute we can't do anything, ignore.
+ const tabPath = el.getAttribute('data-tab');
+ if (!tabPath) {
+ return;
+ }
+
+ // The item is already active, ignore.
+ if (el.classList.contains('active')) {
+ return;
+ }
+
+ // Make the current item active and the previous item inactive.
+ parentEl.querySelector('.item.active').classList.remove('active');
+ document.querySelector(`.tab.active[data-tab=${activeTabPath}]`).classList.remove('active');
+ el.classList.add('active');
+ document.querySelector(`.tab[data-tab=${tabPath}]`).classList.add('active');
+ activeTabPath = tabPath;
+
+ // Not really sure if this is useful, it is kept from how Fomantic did it.
+ ev.preventDefault();
+ }, {passive: false});
+ }
+}