mirror of
https://github.com/go-gitea/gitea.git
synced 2026-07-31 11:55:13 +02:00
chore: fix violations from the newly-enabled unicorn rules
Apply the code changes required by the rules enabled in the previous commit: String() coercions over template literals, location.assign over href assignment, URL#href over toString, Object.entries/values loop forms, and related autofixes. Disable unicorn/no-non-function-verb-prefix, and disable unicorn/no-error-property-assignment for test files. Assisted-by: claude-code:opus-4.8
This commit is contained in:
+2
-1
@@ -812,7 +812,7 @@ export default defineConfig([
|
||||
'unicorn/no-nested-ternary': [0],
|
||||
'unicorn/no-new-array': [0],
|
||||
'unicorn/no-new-buffer': [0],
|
||||
'unicorn/no-non-function-verb-prefix': [2],
|
||||
'unicorn/no-non-function-verb-prefix': [0],
|
||||
'unicorn/no-null': [0],
|
||||
'unicorn/no-object-as-default-parameter': [0],
|
||||
'unicorn/no-object-methods-with-collections': [2],
|
||||
@@ -1053,6 +1053,7 @@ export default defineConfig([
|
||||
languageOptions: {globals: globals.vitest},
|
||||
rules: {
|
||||
'gitea/unescaped-html-literal': [0],
|
||||
'unicorn/no-error-property-assignment': [0],
|
||||
'vitest/consistent-test-filename': [0],
|
||||
'vitest/consistent-test-it': [0],
|
||||
'vitest/expect-expect': [0],
|
||||
|
||||
+1
-1
@@ -90,7 +90,7 @@ export default {
|
||||
'8xl': '96px',
|
||||
'9xl': '128px',
|
||||
...Object.fromEntries(Array.from({length: 100}, (_, i) => {
|
||||
return [`${i}`, `${i === 0 ? '0' : `${i}px`}`];
|
||||
return [String(i), i === 0 ? '0' : `${i}px`];
|
||||
})),
|
||||
},
|
||||
extend: {
|
||||
|
||||
+1
-1
@@ -83,7 +83,7 @@ async function setPrLabels(): Promise<void> {
|
||||
Accept: 'application/vnd.github+json',
|
||||
Authorization: `Bearer ${env.GITHUB_TOKEN}`,
|
||||
'X-GitHub-Api-Version': '2022-11-28',
|
||||
...(body ? {'Content-Type': 'application/json'} : {}),
|
||||
...(body && {'Content-Type': 'application/json'}),
|
||||
},
|
||||
body: body ? JSON.stringify(body) : undefined,
|
||||
});
|
||||
|
||||
@@ -12,30 +12,28 @@ const rule: JSRuleDefinition<JSRuleDefinitionTypeOptions> = {
|
||||
},
|
||||
},
|
||||
|
||||
create(context) {
|
||||
return {
|
||||
Literal(node) {
|
||||
if (typeof node.value !== 'string' || !htmlOpenTag.test(node.value)) return;
|
||||
create: (context) => ({
|
||||
Literal(node) {
|
||||
if (typeof node.value !== 'string' || !htmlOpenTag.test(node.value)) return;
|
||||
|
||||
context.report({
|
||||
node,
|
||||
messageId: 'unescapedHtmlLiteral',
|
||||
});
|
||||
},
|
||||
TemplateLiteral(node) {
|
||||
const templateStart = node.quasis[0]?.value.raw;
|
||||
if (!templateStart || !htmlOpenTag.test(templateStart)) return;
|
||||
context.report({
|
||||
node,
|
||||
messageId: 'unescapedHtmlLiteral',
|
||||
});
|
||||
},
|
||||
TemplateLiteral(node) {
|
||||
const templateStart = node.quasis[0]?.value.raw;
|
||||
if (!templateStart || !htmlOpenTag.test(templateStart)) return;
|
||||
|
||||
const parent = node.parent;
|
||||
if (parent?.type === 'TaggedTemplateExpression' && parent.tag.type === 'Identifier' && parent.tag.name === 'html') return;
|
||||
const parent = node.parent;
|
||||
if (parent?.type === 'TaggedTemplateExpression' && parent.tag.type === 'Identifier' && parent.tag.name === 'html') return;
|
||||
|
||||
context.report({
|
||||
node,
|
||||
messageId: 'unescapedHtmlLiteral',
|
||||
});
|
||||
},
|
||||
};
|
||||
},
|
||||
context.report({
|
||||
node,
|
||||
messageId: 'unescapedHtmlLiteral',
|
||||
});
|
||||
},
|
||||
}),
|
||||
};
|
||||
|
||||
export default rule;
|
||||
|
||||
@@ -7,7 +7,7 @@ import {argv, exit} from 'node:process';
|
||||
async function generate(svg: string, path: string, {size, bg}: {size: number, bg?: boolean}) {
|
||||
const outputFile = new URL(path, import.meta.url);
|
||||
|
||||
if (String(outputFile).endsWith('.svg')) {
|
||||
if (outputFile.href.endsWith('.svg')) {
|
||||
const {data} = optimize(svg, {
|
||||
plugins: [
|
||||
'preset-default',
|
||||
|
||||
@@ -92,7 +92,7 @@ async function processMaterialFileIcons() {
|
||||
}
|
||||
|
||||
// Use VSCode's "Language ID" mapping from its extensions
|
||||
for (const [_, langIdExtMap] of Object.entries(vscodeExtensions)) {
|
||||
for (const langIdExtMap of Object.values(vscodeExtensions)) {
|
||||
for (const [langId, names] of Object.entries(langIdExtMap)) {
|
||||
for (const name of names) {
|
||||
const nameLower = name.toLowerCase();
|
||||
|
||||
+1
-1
@@ -56,7 +56,7 @@ const commonRolldownOptions: Rolldown.RolldownOptions = {
|
||||
checks: {
|
||||
pluginTimings: false,
|
||||
},
|
||||
...(env.CI ? {plugins: [failOnWarningsPlugin()]} : {}),
|
||||
...(env.CI && {plugins: [failOnWarningsPlugin()]}),
|
||||
};
|
||||
|
||||
function commonViteOpts({build, ...other}: InlineConfig): InlineConfig {
|
||||
|
||||
@@ -47,9 +47,9 @@ export type LogLineCommand = {
|
||||
|
||||
export function parseLogLineCommand(line: LogLine): LogLineCommand | null {
|
||||
// TODO: in the future it can be refactored to be a general parser that can parse arguments, drop the "prefix match"
|
||||
for (const prefix of Object.keys(LogLinePrefixCommandMap)) {
|
||||
for (const [prefix, commandName] of Object.entries(LogLinePrefixCommandMap)) {
|
||||
if (line.message.startsWith(prefix)) {
|
||||
return {name: LogLinePrefixCommandMap[prefix], prefix};
|
||||
return {name: commandName, prefix};
|
||||
}
|
||||
}
|
||||
// Handle ::cmd:: and ::cmd args:: format (runner may pass these through raw)
|
||||
|
||||
@@ -57,9 +57,7 @@ export function createViewFileTreeStore(props: {repoLink: string, treePath: stri
|
||||
await store.loadViewContent(url);
|
||||
},
|
||||
|
||||
buildTreePathWebUrl(treePath: string) {
|
||||
return `${props.repoLink}/src/${props.currentRefNameSubURL}/${pathEscapeSegments(treePath)}`;
|
||||
},
|
||||
buildTreePathWebUrl: (treePath: string) => `${props.repoLink}/src/${props.currentRefNameSubURL}/${pathEscapeSegments(treePath)}`,
|
||||
});
|
||||
return store;
|
||||
}
|
||||
|
||||
@@ -166,7 +166,7 @@ function buildDirectNeedsMap(jobs: ActionsJob[]): Map<string, string[]> {
|
||||
const reducedNeedsByJobId = new Map<string, string[]>();
|
||||
for (const [jobId, needs] of directNeedsByJobId) {
|
||||
reducedNeedsByJobId.set(jobId, needs.filter((need) => {
|
||||
return !needs.some((other) => other !== need && canReach(need, other));
|
||||
return needs.every((other) => other === need || !canReach(need, other));
|
||||
}));
|
||||
}
|
||||
return reducedNeedsByJobId;
|
||||
|
||||
@@ -30,9 +30,9 @@ test('ConfigFormValueMapper', () => {
|
||||
const formData = mapper.collectToFormData();
|
||||
const result: Record<string, string> = {};
|
||||
const keys: string[] = [], values: string[] = [];
|
||||
for (const [key, value] of formData.entries()) {
|
||||
for (const [key, value] of formData) {
|
||||
if (key === 'key') keys.push(value as string);
|
||||
if (key === 'value') values.push(value as string);
|
||||
else if (key === 'value') values.push(value as string);
|
||||
}
|
||||
for (let i = 0; i < keys.length; i++) {
|
||||
result[keys[i]] = values[i];
|
||||
|
||||
@@ -178,7 +178,7 @@ export class ConfigFormValueMapper {
|
||||
|
||||
collectToFormData(): FormData {
|
||||
const namedElems: Array<GeneralFormFieldElement | null> = [];
|
||||
queryElems(this.form, '[name]', (el) => namedElems.push(el as GeneralFormFieldElement));
|
||||
queryElems(this.form, '[name]', (el) => { namedElems.push(el as GeneralFormFieldElement) });
|
||||
|
||||
// first, process the config options with sub values, for example:
|
||||
// merge "foo.bar.Enabled", "foo.bar.Message" to "foo.bar"
|
||||
|
||||
@@ -24,7 +24,7 @@ export function initGlobalDeleteButton(): void {
|
||||
btn.addEventListener('click', (e) => {
|
||||
e.preventDefault();
|
||||
|
||||
// `dataset` is used here because the code below depends on it
|
||||
// eslint-disable-next-line unicorn/dom-node-dataset -- code depends on the camel-casing
|
||||
const dataObj = btn.dataset;
|
||||
|
||||
const modalId = btn.getAttribute('data-modal-id');
|
||||
@@ -70,7 +70,7 @@ export function initGlobalDeleteButton(): void {
|
||||
const response = await POST(btn.getAttribute('data-url')!, {data: postData});
|
||||
if (response.ok) {
|
||||
const data = await response.json();
|
||||
window.location.href = data.redirect;
|
||||
window.location.assign(data.redirect);
|
||||
}
|
||||
})();
|
||||
modal.classList.add('is-loading'); // the request is in progress, so also add loading indicator to the modal
|
||||
@@ -158,8 +158,8 @@ function onShowModalClick(el: HTMLElement, e: MouseEvent) {
|
||||
const attrTarget = elModal.querySelector(`#${attrTargetName}`) ||
|
||||
elModal.querySelector(`[name=${CSS.escape(attrTargetName)}]`) ||
|
||||
elModal.querySelector(`.${attrTargetName}`) ||
|
||||
elModal.querySelector(`${attrTargetName}`) ||
|
||||
(elModal.matches(`${attrTargetName}`) || elModal.matches(`#${attrTargetName}`) || elModal.matches(`.${attrTargetName}`) ? elModal : null);
|
||||
elModal.querySelector(attrTargetName) ||
|
||||
(elModal.matches(attrTargetName) || elModal.matches(`#${attrTargetName}`) || elModal.matches(`.${attrTargetName}`) ? elModal : null);
|
||||
if (!attrTarget) {
|
||||
if (!window.config.runModeIsProd) throw new Error(`attr target "${attrTargetCombo}" not found for modal`);
|
||||
continue;
|
||||
|
||||
@@ -114,7 +114,7 @@ function buildFetchActionUrl(el: HTMLElement, opt: FetchActionOpts) {
|
||||
const u = new URL(url, window.location.href);
|
||||
if (name && !u.searchParams.has(name)) {
|
||||
u.searchParams.set(name, val);
|
||||
url = u.toString();
|
||||
url = u.href;
|
||||
}
|
||||
}
|
||||
return url;
|
||||
|
||||
@@ -35,7 +35,7 @@ export function initCommonIssueListQuickGoto() {
|
||||
const repoLink = elGotoButton.getAttribute('data-repo-link') || '';
|
||||
|
||||
elGotoButton.addEventListener('click', () => {
|
||||
window.location.href = elGotoButton.getAttribute('data-issue-goto-link')!;
|
||||
window.location.assign(elGotoButton.getAttribute('data-issue-goto-link')!);
|
||||
});
|
||||
|
||||
const onInput = async () => {
|
||||
|
||||
@@ -110,8 +110,8 @@ export async function initDropzone(dropzoneEl: HTMLElement) {
|
||||
});
|
||||
|
||||
dzInst.on('submit', () => {
|
||||
for (const fileUuid of Object.keys(fileUuidDict)) {
|
||||
fileUuidDict[fileUuid].submitted = true;
|
||||
for (const value of Object.values(fileUuidDict)) {
|
||||
value.submitted = true;
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -24,8 +24,8 @@ export async function initHeatmap() {
|
||||
heatmap[dateStr] = (heatmap[dateStr] || 0) + contributions;
|
||||
}
|
||||
|
||||
const values = Object.keys(heatmap).map((v) => {
|
||||
return {date: new Date(v), count: heatmap[v]};
|
||||
const values = Object.entries(heatmap).map(([dateStr, count]) => {
|
||||
return {date: new Date(dateStr), count};
|
||||
});
|
||||
|
||||
const totalFormatted = totalContributions.toLocaleString();
|
||||
|
||||
@@ -291,7 +291,7 @@ class ImageDiff {
|
||||
|
||||
function updateOpacity() {
|
||||
if (ctx.imageAfter) {
|
||||
(ctx.imageAfter.parentNode as HTMLElement).style.opacity = `${Number(rangeInput.value) / 100}`;
|
||||
(ctx.imageAfter.parentNode as HTMLElement).style.opacity = String(Number(rangeInput.value) / 100);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -95,7 +95,7 @@ function initPostInstall() {
|
||||
if (tid && resp.status === 200) {
|
||||
clearInterval(tid);
|
||||
tid = null;
|
||||
window.location.href = targetUrl;
|
||||
window.location.assign(targetUrl);
|
||||
}
|
||||
} catch {}
|
||||
}, 1000);
|
||||
|
||||
@@ -10,7 +10,7 @@ async function receiveUpdateCount(event: MessageEvent<{type: string, data: strin
|
||||
const data = JSON.parse(event.data.data);
|
||||
for (const count of document.querySelectorAll('.notification_count')) {
|
||||
count.classList.toggle('tw-hidden', data.Count === 0);
|
||||
count.textContent = `${data.Count}`;
|
||||
count.textContent = String(data.Count);
|
||||
}
|
||||
await updateNotificationTable();
|
||||
} catch (error) {
|
||||
@@ -112,7 +112,7 @@ async function updateNotificationCount(): Promise<number> {
|
||||
toggleElem('.notification_count', data.new !== 0);
|
||||
|
||||
for (const el of document.querySelectorAll('.notification_count')) {
|
||||
el.textContent = `${data.new}`;
|
||||
el.textContent = String(data.new);
|
||||
}
|
||||
|
||||
return data.new as number;
|
||||
|
||||
@@ -31,9 +31,9 @@ function selectRange(range: string): Element | null {
|
||||
const updateViewGitBlameFragment = function (anchor: string) {
|
||||
if (!viewGitBlame) return;
|
||||
let href = viewGitBlame.getAttribute('href')!;
|
||||
href = `${href.replace(/#L\d+$|#L\d+-L\d+$/, '')}`;
|
||||
href = href.replace(/#L\d+$|#L\d+-L\d+$/, '');
|
||||
if (anchor.length !== 0) {
|
||||
href = `${href}#${anchor}`;
|
||||
href += `#${anchor}`;
|
||||
}
|
||||
viewGitBlame.setAttribute('href', href);
|
||||
};
|
||||
|
||||
@@ -45,8 +45,8 @@ export function initCommitFileHistoryFollowRename() {
|
||||
registerGlobalInitFunc('initCommitHistoryFollowRename', (el: HTMLInputElement) => {
|
||||
el.addEventListener('change', () => {
|
||||
const url = new URL(window.location.toString());
|
||||
url.searchParams.set('follow-rename', `${el.checked}`);
|
||||
window.location.assign(url.toString());
|
||||
url.searchParams.set('follow-rename', String(el.checked));
|
||||
window.location.assign(url.href);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@@ -23,7 +23,7 @@ async function onDownloadArchive(e: Event) {
|
||||
if (data.complete) break;
|
||||
await sleep(Math.min((tryCount + 1) * 750, 2000));
|
||||
}
|
||||
window.location.href = el.href; // the archive is ready, start real downloading
|
||||
window.location.assign(el.href); // the archive is ready, start real downloading
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
showErrorToast(`Failed to download the archive: ${errorMessage(e)}`, {duration: 2500});
|
||||
|
||||
@@ -129,7 +129,7 @@ function initRepoDiffConversationNav() {
|
||||
const navIndex = isPrevious ? previousIndex : nextIndex;
|
||||
const elNavConversation = elAllConversations[navIndex];
|
||||
const anchor = elNavConversation.querySelector('.comment')!.id;
|
||||
window.location.href = `#${anchor}`;
|
||||
window.location.assign(`#${anchor}`);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -42,7 +42,7 @@ export function initRepoGraphGit() {
|
||||
|
||||
elGraphBody.classList.add('is-loading');
|
||||
try {
|
||||
const resp = await GET(ajaxUrl.toString());
|
||||
const resp = await GET(ajaxUrl.href);
|
||||
elGraphBody.innerHTML = await resp.text();
|
||||
} finally {
|
||||
elGraphBody.classList.remove('is-loading');
|
||||
@@ -51,7 +51,7 @@ export function initRepoGraphGit() {
|
||||
|
||||
const dropdownSelected = params.getAll('branch');
|
||||
if (params.has('hide-pr-refs') && params.get('hide-pr-refs') === 'true') {
|
||||
dropdownSelected.splice(0, 0, '...flow-hide-pr-refs');
|
||||
dropdownSelected.unshift('...flow-hide-pr-refs');
|
||||
}
|
||||
|
||||
const $dropdown = fomanticQuery('#flow-select-refs-dropdown');
|
||||
|
||||
@@ -96,10 +96,7 @@ export function initRepoTopicBar() {
|
||||
};
|
||||
const query = stripTags(this.urlData.query.trim());
|
||||
let found_query = false;
|
||||
const current_topics = [];
|
||||
for (const el of queryElemChildren(topicDropdown, 'a.ui.label.visible')) {
|
||||
current_topics.push(el.getAttribute('data-value'));
|
||||
}
|
||||
const current_topics = Array.from(queryElemChildren(topicDropdown, 'a.ui.label.visible'), (el) => el.getAttribute('data-value'));
|
||||
|
||||
if (res.topics) {
|
||||
let found = false;
|
||||
|
||||
@@ -148,7 +148,7 @@ export async function initRepoIssueContentHistory() {
|
||||
if (resp.editedHistoryCountMap[0] && elIssueDescription) {
|
||||
showContentHistoryMenu(issueBaseUrl, elIssueDescription, '0');
|
||||
}
|
||||
for (const [commentId, _editedCount] of Object.entries(resp.editedHistoryCountMap)) {
|
||||
for (const commentId of Object.keys(resp.editedHistoryCountMap)) {
|
||||
if (commentId === '0') continue;
|
||||
const elIssueComment = document.querySelector(`#issuecomment-${commentId}`);
|
||||
if (elIssueComment) showContentHistoryMenu(issueBaseUrl, elIssueComment, commentId);
|
||||
|
||||
@@ -58,10 +58,7 @@ function initRepoIssueListCheckboxes() {
|
||||
const url = el.getAttribute('data-url')!;
|
||||
let action = el.getAttribute('data-action')!;
|
||||
let elementId = el.getAttribute('data-element-id')!;
|
||||
const issueIDList: string[] = [];
|
||||
for (const el of document.querySelectorAll('.issue-checkbox:checked')) {
|
||||
issueIDList.push(el.getAttribute('data-issue-id')!);
|
||||
}
|
||||
const issueIDList: string[] = Array.from(document.querySelectorAll('.issue-checkbox:checked'), (el) => (el.getAttribute('data-issue-id')!));
|
||||
const issueIDs = issueIDList.join(',');
|
||||
if (!issueIDs) return;
|
||||
|
||||
@@ -109,7 +106,7 @@ function initDropdownUserRemoteSearch(el: Element) {
|
||||
fullTextSearch: true,
|
||||
selectOnKeydown: false,
|
||||
action: (_text: string, value: string) => {
|
||||
window.location.href = actionJumpUrl.replace('{username}', encodeURIComponent(value));
|
||||
window.location.assign(actionJumpUrl.replace('{username}', encodeURIComponent(value)));
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
@@ -28,7 +28,7 @@ function initRepoIssueLabelFilter(elDropdown: HTMLElement) {
|
||||
const queryLabels = url.searchParams.get('labels') || '';
|
||||
const selectedLabelIds = new Set<string>();
|
||||
for (const id of queryLabels ? queryLabels.split(',') : []) {
|
||||
selectedLabelIds.add(`${Math.abs(parseInt(id))}`); // "labels" contains negative ids, which are excluded
|
||||
selectedLabelIds.add(String(Math.abs(parseInt(id)))); // "labels" contains negative ids, which are excluded
|
||||
}
|
||||
|
||||
const excludeLabel = (e: MouseEvent | KeyboardEvent, item: Element) => {
|
||||
|
||||
@@ -79,7 +79,7 @@ async function loginPasskey() {
|
||||
}
|
||||
const reply = await res.json();
|
||||
|
||||
window.location.href = reply?.redirect ?? `${appSubUrl}/`;
|
||||
window.location.assign(reply?.redirect ?? `${appSubUrl}/`);
|
||||
} catch (err) {
|
||||
webAuthnError('general', errorMessage(err));
|
||||
}
|
||||
@@ -151,7 +151,7 @@ async function verifyAssertion(assertedCredential: any) { // TODO: Credential ty
|
||||
}
|
||||
const reply = await res.json();
|
||||
|
||||
window.location.href = reply?.redirect ?? `${appSubUrl}/`;
|
||||
window.location.assign(reply?.redirect ?? `${appSubUrl}/`);
|
||||
}
|
||||
|
||||
async function webauthnRegistered(newCredential: any) { // TODO: Credential type does not work
|
||||
|
||||
@@ -18,15 +18,9 @@ function prepareProcessors(ctx:ProcessorContext): Processors {
|
||||
const level = parseInt(el.tagName.slice(1));
|
||||
el.textContent = `${'#'.repeat(level)} ${el.textContent.trim()}`;
|
||||
},
|
||||
STRONG(el: HTMLElement) {
|
||||
return `**${el.textContent}**`;
|
||||
},
|
||||
EM(el: HTMLElement) {
|
||||
return `_${el.textContent}_`;
|
||||
},
|
||||
DEL(el: HTMLElement) {
|
||||
return `~~${el.textContent}~~`;
|
||||
},
|
||||
STRONG: (el: HTMLElement) => `**${el.textContent}**`,
|
||||
EM: (el: HTMLElement) => `_${el.textContent}_`,
|
||||
DEL: (el: HTMLElement) => `~~${el.textContent}~~`,
|
||||
A(el: HTMLElement) {
|
||||
const text = el.textContent || 'link';
|
||||
const href = el.getAttribute('href');
|
||||
@@ -62,9 +56,7 @@ function prepareProcessors(ctx:ProcessorContext): Processors {
|
||||
el.textContent = `${' '.repeat(nestingIdentLevel * 4)}${bullet}${el.textContent}${ctx.elementIsLast ? '' : '\n'}`;
|
||||
return el;
|
||||
},
|
||||
INPUT(el: HTMLElement) {
|
||||
return (el as HTMLInputElement).checked ? '[x] ' : '[ ] ';
|
||||
},
|
||||
INPUT: (el: HTMLElement) => (el as HTMLInputElement).checked ? '[x] ' : '[ ] ',
|
||||
CODE(el: HTMLElement) {
|
||||
const text = el.textContent;
|
||||
if (el.parentNode && (el.parentNode as HTMLElement).tagName === 'PRE') {
|
||||
|
||||
@@ -29,7 +29,6 @@ describe('navigateToIframeLink', () => {
|
||||
|
||||
test('unsafe links', () => {
|
||||
const errorSpy = vi.spyOn(console, 'error').mockImplementation(() => undefined);
|
||||
window.location.href = 'http://localhost:3000/';
|
||||
|
||||
// eslint-disable-next-line no-script-url
|
||||
navigateToIframeLink('javascript:void(0);', '_blank');
|
||||
|
||||
@@ -4,7 +4,7 @@ import {isDarkTheme} from '../utils.ts';
|
||||
|
||||
function safeRenderIframeLink(link: any): string | null {
|
||||
try {
|
||||
const url = new URL(`${link}`, window.location.href);
|
||||
const url = new URL(link, window.location.href);
|
||||
if (url.protocol !== 'http:' && url.protocol !== 'https:') {
|
||||
console.error(`Unsupported link protocol: ${link}`);
|
||||
return null;
|
||||
|
||||
@@ -203,9 +203,7 @@ export async function createCodeEditor(textarea: HTMLTextAreaElement, filenameIn
|
||||
},
|
||||
}),
|
||||
cm.language.foldGutter({
|
||||
markerDOM(open: boolean) {
|
||||
return createElementFromHTML(svg(open ? 'octicon-chevron-down' : 'octicon-chevron-right', 13));
|
||||
},
|
||||
markerDOM: (open: boolean) => createElementFromHTML(svg(open ? 'octicon-chevron-down' : 'octicon-chevron-right', 13)),
|
||||
}),
|
||||
cm.view.highlightActiveLineGutter(),
|
||||
cm.view.highlightSpecialChars(),
|
||||
|
||||
@@ -253,22 +253,22 @@ function attachDomEvents(dropdown: HTMLElement, focusable: HTMLElement, menu: HT
|
||||
dropdown.addEventListener('mousedown', () => {
|
||||
ignoreClickPreVisible += isMenuVisible() ? 1 : 0;
|
||||
ignoreClickPreEvents++;
|
||||
}, true);
|
||||
}, {capture: true});
|
||||
dropdown.addEventListener('focus', () => {
|
||||
ignoreClickPreVisible += isMenuVisible() ? 1 : 0;
|
||||
ignoreClickPreEvents++;
|
||||
deferredRefreshAriaActiveItem();
|
||||
}, true);
|
||||
}, {capture: true});
|
||||
dropdown.addEventListener('blur', () => {
|
||||
ignoreClickPreVisible = ignoreClickPreEvents = 0;
|
||||
deferredRefreshAriaActiveItem(100);
|
||||
}, true);
|
||||
}, {capture: true});
|
||||
dropdown.addEventListener('mouseup', () => {
|
||||
setTimeout(() => {
|
||||
ignoreClickPreVisible = ignoreClickPreEvents = 0;
|
||||
deferredRefreshAriaActiveItem(100);
|
||||
}, 0);
|
||||
}, true);
|
||||
}, {capture: true});
|
||||
dropdown.addEventListener('click', (e: MouseEvent) => {
|
||||
if (isMenuVisible() &&
|
||||
ignoreClickPreVisible !== 2 && // dropdown is switch from invisible to visible
|
||||
@@ -277,7 +277,7 @@ function attachDomEvents(dropdown: HTMLElement, focusable: HTMLElement, menu: HT
|
||||
e.stopPropagation(); // if the dropdown menu has been opened by focus, do not trigger the next click event again
|
||||
}
|
||||
ignoreClickPreEvents = ignoreClickPreVisible = 0;
|
||||
}, true);
|
||||
}, {capture: true});
|
||||
}
|
||||
|
||||
// Although Fomantic Dropdown supports "hideDividers", it doesn't really work with our "scoped dividers"
|
||||
|
||||
@@ -89,7 +89,7 @@ function attachTooltip(target: Element, content: Content | null = null): Instanc
|
||||
allowHTML: target.getAttribute('data-tooltip-render') === 'html',
|
||||
placement: target.getAttribute('data-tooltip-placement') as Placement || 'top-start',
|
||||
followCursor: target.getAttribute('data-tooltip-follow-cursor') as Props['followCursor'] || false,
|
||||
...(target.getAttribute('data-tooltip-interactive') === 'true' ? {interactive: true, aria: {content: 'describedby', expanded: false}} : {}),
|
||||
...((target.getAttribute('data-tooltip-interactive') === 'true') && {interactive: true, aria: {content: 'describedby', expanded: false}}),
|
||||
};
|
||||
|
||||
if (!target._tippy) {
|
||||
|
||||
@@ -50,7 +50,7 @@ export class UserEventsSharedWorker {
|
||||
// * in this case, the logout fetch call already completes and has sent the "logout" message to the worker
|
||||
// * there can be a data-race between the fetch call's redirection and the "logout" message from the worker
|
||||
// * the fetch call's logout redirection should always win over the worker message, because it might have a custom location
|
||||
setTimeout(() => { window.location.href = `${appSubUrl}/` }, 1000);
|
||||
setTimeout(() => { window.location.assign(`${appSubUrl}/`) }, 1000);
|
||||
} else if (event.data.type === 'close') {
|
||||
this.sharedWorker.port.postMessage({type: 'close'});
|
||||
this.sharedWorker.port.close();
|
||||
|
||||
@@ -4,9 +4,7 @@ export function newInplacePluginPdfViewer(): InplaceRenderPlugin {
|
||||
return {
|
||||
name: 'pdf-viewer',
|
||||
|
||||
canHandle(filename: string, _mimeType: string): boolean {
|
||||
return filename.toLowerCase().endsWith('.pdf');
|
||||
},
|
||||
canHandle: (filename: string, _mimeType: string): boolean => filename.toLowerCase().endsWith('.pdf'),
|
||||
|
||||
async render(container: HTMLElement, fileUrl: string): Promise<void> {
|
||||
const PDFObject = await import('pdfobject');
|
||||
|
||||
@@ -8,7 +8,7 @@ import type {Issue, Mention} from '../types.ts';
|
||||
const maxMatches = 6;
|
||||
|
||||
function sortAndReduce<T>(map: Map<T, number>): T[] {
|
||||
const sortedMap = new Map(Array.from(map.entries()).sort((a, b) => a[1] - b[1]));
|
||||
const sortedMap = new Map(Array.from(map).sort((a, b) => a[1] - b[1]));
|
||||
return Array.from(sortedMap.keys()).slice(0, maxMatches);
|
||||
}
|
||||
|
||||
|
||||
@@ -10,11 +10,11 @@ export function dedent(str: string) {
|
||||
const match = str.match(/^[ \t]*(?=\S)/gm);
|
||||
if (!match) return str;
|
||||
|
||||
let minIndent = Number.POSITIVE_INFINITY;
|
||||
let minIndent = Infinity;
|
||||
for (const indent of match) {
|
||||
minIndent = Math.min(minIndent, indent.length);
|
||||
}
|
||||
if (minIndent === 0 || minIndent === Number.POSITIVE_INFINITY) {
|
||||
if (minIndent === 0 || minIndent === Infinity) {
|
||||
return str;
|
||||
}
|
||||
|
||||
|
||||
@@ -21,7 +21,7 @@ window.customElements.define('overflow-menu', class extends HTMLElement {
|
||||
this.popup.style.display = '';
|
||||
this.button!.setAttribute('aria-expanded', 'true');
|
||||
setTimeout(() => this.popup.focus(), 0);
|
||||
document.addEventListener('click', this.onClickOutside, true);
|
||||
document.addEventListener('click', this.onClickOutside, {capture: true});
|
||||
}
|
||||
|
||||
hidePopup() {
|
||||
@@ -125,7 +125,7 @@ window.customElements.define('overflow-menu', class extends HTMLElement {
|
||||
const itemRight = item.offsetLeft + item.offsetWidth;
|
||||
if (menuRight - itemRight < 38) { // roughly the width of .overflow-menu-button with some extra space
|
||||
const onlyLastItem = idx === menuItems.length - 1 && this.overflowItems.length === 0;
|
||||
const lastItemFit = onlyLastItem && menuRight - itemRight > 0;
|
||||
const lastItemFit = onlyLastItem && menuRight > itemRight;
|
||||
const moveToPopup = !onlyLastItem || !lastItemFit;
|
||||
if (moveToPopup) this.overflowItems.push(item);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user