Skip to content

Commit f860fe3

Browse files
authored
Move some JS code from fomantic.js to standalone files (#27994)
To improve maintainability, this PR: 1. Rename `web_src/js/modules/aria` to `web_src/js/modules/fomantic` (the code there are all for aria of fomantic) 2. Move api/transition related code to `web_src/js/modules/fomantic/api.js` and `web_src/js/modules/fomantic/transition.js` No logic is changed.
1 parent 61ff91f commit f860fe3

File tree

8 files changed

+100
-93
lines changed

8 files changed

+100
-93
lines changed

web_src/js/modules/fomantic.js

Lines changed: 6 additions & 93 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
import $ from 'jquery';
2-
import {initAriaCheckboxPatch} from './aria/checkbox.js';
3-
import {initAriaDropdownPatch} from './aria/dropdown.js';
4-
import {initAriaModalPatch} from './aria/modal.js';
2+
import {initFomanticApiPatch} from './fomantic/api.js';
3+
import {initAriaCheckboxPatch} from './fomantic/checkbox.js';
4+
import {initAriaDropdownPatch} from './fomantic/dropdown.js';
5+
import {initAriaModalPatch} from './fomantic/modal.js';
6+
import {initFomanticTransition} from './fomantic/transition.js';
57
import {svg} from '../svg.js';
68

79
export const fomanticMobileScreen = window.matchMedia('only screen and (max-width: 767.98px)');
@@ -22,100 +24,11 @@ export function initGiteaFomantic() {
2224
return escape(text, preserveHTML) + svg('octicon-x', 16, `${className.delete} icon`);
2325
};
2426

25-
const transitionNopBehaviors = new Set([
26-
'clear queue', 'stop', 'stop all', 'destroy',
27-
'force repaint', 'repaint', 'reset',
28-
'looping', 'remove looping', 'disable', 'enable',
29-
'set duration', 'save conditions', 'restore conditions',
30-
]);
31-
// stand-in for removed transition module
32-
$.fn.transition = function (arg0, arg1, arg2) {
33-
if (arg0 === 'is supported') return true;
34-
if (arg0 === 'is animating') return false;
35-
if (arg0 === 'is inward') return false;
36-
if (arg0 === 'is outward') return false;
37-
38-
let argObj;
39-
if (typeof arg0 === 'string') {
40-
// many behaviors are no-op now. https://fomantic-ui.com/modules/transition.html#/usage
41-
if (transitionNopBehaviors.has(arg0)) return this;
42-
// now, the arg0 is an animation name, the syntax: (animation, duration, complete)
43-
argObj = {animation: arg0, ...(arg1 && {duration: arg1}), ...(arg2 && {onComplete: arg2})};
44-
} else if (typeof arg0 === 'object') {
45-
argObj = arg0;
46-
} else {
47-
throw new Error(`invalid argument: ${arg0}`);
48-
}
49-
50-
const isAnimationIn = argObj.animation?.startsWith('show') || argObj.animation?.endsWith(' in');
51-
const isAnimationOut = argObj.animation?.startsWith('hide') || argObj.animation?.endsWith(' out');
52-
this.each((_, el) => {
53-
let toShow = isAnimationIn;
54-
if (!isAnimationIn && !isAnimationOut) {
55-
// If the animation is not in/out, then it must be a toggle animation.
56-
// Fomantic uses computed styles to check "visibility", but to avoid unnecessary arguments, here it only checks the class.
57-
toShow = this.hasClass('hidden'); // maybe it could also check "!this.hasClass('visible')", leave it to the future until there is a real problem.
58-
}
59-
argObj.onStart?.call(el);
60-
if (toShow) {
61-
el.classList.remove('hidden');
62-
el.classList.add('visible', 'transition');
63-
if (argObj.displayType) el.style.setProperty('display', argObj.displayType, 'important');
64-
argObj.onShow?.call(el);
65-
} else {
66-
el.classList.add('hidden');
67-
el.classList.remove('visible'); // don't remove the transition class because the Fomantic animation style is `.hidden.transition`.
68-
el.style.removeProperty('display');
69-
argObj.onHidden?.call(el);
70-
}
71-
argObj.onComplete?.call(el);
72-
});
73-
return this;
74-
};
75-
27+
initFomanticTransition();
7628
initFomanticApiPatch();
7729

7830
// Use the patches to improve accessibility, these patches are designed to be as independent as possible, make it easy to modify or remove in the future.
7931
initAriaCheckboxPatch();
8032
initAriaDropdownPatch();
8133
initAriaModalPatch();
8234
}
83-
84-
function initFomanticApiPatch() {
85-
//
86-
// Fomantic API module has some very buggy behaviors:
87-
//
88-
// If encodeParameters=true, it calls `urlEncodedValue` to encode the parameter.
89-
// However, `urlEncodedValue` just tries to "guess" whether the parameter is already encoded, by decoding the parameter and encoding it again.
90-
//
91-
// There are 2 problems:
92-
// 1. It may guess wrong, and skip encoding a parameter which looks like encoded.
93-
// 2. If the parameter can't be decoded, `decodeURIComponent` will throw an error, and the whole request will fail.
94-
//
95-
// This patch only fixes the second error behavior at the moment.
96-
//
97-
const patchKey = '_giteaFomanticApiPatch';
98-
const oldApi = $.api;
99-
$.api = $.fn.api = function(...args) {
100-
const apiCall = oldApi.bind(this);
101-
const ret = oldApi.apply(this, args);
102-
103-
if (typeof args[0] !== 'string') {
104-
const internalGet = apiCall('internal', 'get');
105-
if (!internalGet.urlEncodedValue[patchKey]) {
106-
const oldUrlEncodedValue = internalGet.urlEncodedValue;
107-
internalGet.urlEncodedValue = function (value) {
108-
try {
109-
return oldUrlEncodedValue(value);
110-
} catch {
111-
// if Fomantic API module's `urlEncodedValue` throws an error, we encode it by ourselves.
112-
return encodeURIComponent(value);
113-
}
114-
};
115-
internalGet.urlEncodedValue[patchKey] = true;
116-
}
117-
}
118-
return ret;
119-
};
120-
$.api.settings = oldApi.settings;
121-
}

web_src/js/modules/fomantic/api.js

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
import $ from 'jquery';
2+
3+
export function initFomanticApiPatch() {
4+
//
5+
// Fomantic API module has some very buggy behaviors:
6+
//
7+
// If encodeParameters=true, it calls `urlEncodedValue` to encode the parameter.
8+
// However, `urlEncodedValue` just tries to "guess" whether the parameter is already encoded, by decoding the parameter and encoding it again.
9+
//
10+
// There are 2 problems:
11+
// 1. It may guess wrong, and skip encoding a parameter which looks like encoded.
12+
// 2. If the parameter can't be decoded, `decodeURIComponent` will throw an error, and the whole request will fail.
13+
//
14+
// This patch only fixes the second error behavior at the moment.
15+
//
16+
const patchKey = '_giteaFomanticApiPatch';
17+
const oldApi = $.api;
18+
$.api = $.fn.api = function(...args) {
19+
const apiCall = oldApi.bind(this);
20+
const ret = oldApi.apply(this, args);
21+
22+
if (typeof args[0] !== 'string') {
23+
const internalGet = apiCall('internal', 'get');
24+
if (!internalGet.urlEncodedValue[patchKey]) {
25+
const oldUrlEncodedValue = internalGet.urlEncodedValue;
26+
internalGet.urlEncodedValue = function (value) {
27+
try {
28+
return oldUrlEncodedValue(value);
29+
} catch {
30+
// if Fomantic API module's `urlEncodedValue` throws an error, we encode it by ourselves.
31+
return encodeURIComponent(value);
32+
}
33+
};
34+
internalGet.urlEncodedValue[patchKey] = true;
35+
}
36+
}
37+
return ret;
38+
};
39+
$.api.settings = oldApi.settings;
40+
}
File renamed without changes.
File renamed without changes.
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
import $ from 'jquery';
2+
3+
export function initFomanticTransition() {
4+
const transitionNopBehaviors = new Set([
5+
'clear queue', 'stop', 'stop all', 'destroy',
6+
'force repaint', 'repaint', 'reset',
7+
'looping', 'remove looping', 'disable', 'enable',
8+
'set duration', 'save conditions', 'restore conditions',
9+
]);
10+
// stand-in for removed transition module
11+
$.fn.transition = function (arg0, arg1, arg2) {
12+
if (arg0 === 'is supported') return true;
13+
if (arg0 === 'is animating') return false;
14+
if (arg0 === 'is inward') return false;
15+
if (arg0 === 'is outward') return false;
16+
17+
let argObj;
18+
if (typeof arg0 === 'string') {
19+
// many behaviors are no-op now. https://fomantic-ui.com/modules/transition.html#/usage
20+
if (transitionNopBehaviors.has(arg0)) return this;
21+
// now, the arg0 is an animation name, the syntax: (animation, duration, complete)
22+
argObj = {animation: arg0, ...(arg1 && {duration: arg1}), ...(arg2 && {onComplete: arg2})};
23+
} else if (typeof arg0 === 'object') {
24+
argObj = arg0;
25+
} else {
26+
throw new Error(`invalid argument: ${arg0}`);
27+
}
28+
29+
const isAnimationIn = argObj.animation?.startsWith('show') || argObj.animation?.endsWith(' in');
30+
const isAnimationOut = argObj.animation?.startsWith('hide') || argObj.animation?.endsWith(' out');
31+
this.each((_, el) => {
32+
let toShow = isAnimationIn;
33+
if (!isAnimationIn && !isAnimationOut) {
34+
// If the animation is not in/out, then it must be a toggle animation.
35+
// Fomantic uses computed styles to check "visibility", but to avoid unnecessary arguments, here it only checks the class.
36+
toShow = this.hasClass('hidden'); // maybe it could also check "!this.hasClass('visible')", leave it to the future until there is a real problem.
37+
}
38+
argObj.onStart?.call(el);
39+
if (toShow) {
40+
el.classList.remove('hidden');
41+
el.classList.add('visible', 'transition');
42+
if (argObj.displayType) el.style.setProperty('display', argObj.displayType, 'important');
43+
argObj.onShow?.call(el);
44+
} else {
45+
el.classList.add('hidden');
46+
el.classList.remove('visible'); // don't remove the transition class because the Fomantic animation style is `.hidden.transition`.
47+
el.style.removeProperty('display');
48+
argObj.onHidden?.call(el);
49+
}
50+
argObj.onComplete?.call(el);
51+
});
52+
return this;
53+
};
54+
}

0 commit comments

Comments
 (0)