diff --git a/web_src/js/features/repo-issue-pull.ts b/web_src/js/features/repo-issue-pull.ts index 77948c4edd..34cc4ed199 100644 --- a/web_src/js/features/repo-issue-pull.ts +++ b/web_src/js/features/repo-issue-pull.ts @@ -52,7 +52,12 @@ function initRepoPullMergeBoxRefresh(el: Element) { const pullLink = el.getAttribute('data-pull-link')!; const resp = await GET(`${pullLink}/merge_box`); if (!resp.ok) return; - const newEl = createElementFromHTML(await resp.text()); + const respText = (await resp.text()).trim(); + if (!respText) { + el.remove(); // merge box might not exist if the PR has changed (e.g.: merged and the head branch has been deleted) + return; + } + const newEl = createElementFromHTML(respText); const scrollTop = el.querySelector('.commit-status-list')?.scrollTop; el.replaceWith(newEl); // don't morph, do full replacement to make sure data-global-init and Vue components are re-initialized if (scrollTop) newEl.querySelector('.commit-status-list')?.scrollTo({top: scrollTop, behavior: 'instant'}); diff --git a/web_src/js/utils/dom.ts b/web_src/js/utils/dom.ts index 3fa865645d..71b6b7506f 100644 --- a/web_src/js/utils/dom.ts +++ b/web_src/js/utils/dom.ts @@ -267,10 +267,10 @@ export function isElemVisible(el: HTMLElement): boolean { export function createElementFromHTML(htmlString: string): T { htmlString = htmlString.trim(); + if (!htmlString.startsWith('<')) throw new Error(`Invalid HTML element string: ${htmlString}`); const isLetter = (code: number) => (code >= 65 && code <= 90) || (code >= 97 && code <= 122); const startsWithTag = (s: string, tag: string) => { - return s.startsWith('<') && - s.substring(1, 1 + tag.length).toLowerCase() === tag.toLowerCase() && + return s.substring(1, 1 + tag.length).toLowerCase() === tag.toLowerCase() && !isLetter(s[1 + tag.length].charCodeAt(0)); }; // There is no way to create some elements without a proper parent, jQuery's approach: https://github.com/jquery/jquery/blob/main/src/manipulation/wrapMap.js