go.chromium.org/luci@v0.0.0-20240309015107-7cdc2e660f33/milo/ui/src/common/tools/markdown/plugins/default_target.ts (about) 1 // Copyright 2020 The LUCI Authors. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 import MarkdownIt from 'markdown-it'; 16 17 const CLOSING_ANCHOR_TAG = '</a>'; 18 19 /** 20 * Set default target for link elements. 21 */ 22 export function defaultTarget(md: MarkdownIt, defaultTarget: string) { 23 const renderToken = md.renderer.renderToken.bind(md.renderer); 24 25 // Set default target for link_open tokens. 26 const existingLinkOpenRule = md.renderer.rules['link_open'] || renderToken; 27 md.renderer.rules['link_open'] = (tokens, i, ...params) => { 28 const target = tokens[i].attrGet('target') || defaultTarget; 29 tokens[i].attrSet('target', target); 30 return existingLinkOpenRule(tokens, i, ...params); 31 }; 32 33 // Set default target for anchors in html_inline tokens. 34 const existingHTMLInlineRule = 35 md.renderer.rules['html_inline'] || renderToken; 36 md.renderer.rules['html_inline'] = (tokens, i, ...params) => { 37 const token = tokens[i]; 38 if (/^<a .*>$/i.test(token.content)) { 39 const template = document.createElement('template'); 40 template.innerHTML = token.content + CLOSING_ANCHOR_TAG; 41 const anchor = template.content.firstElementChild!; 42 const target = anchor.getAttribute('target') || defaultTarget; 43 anchor.setAttribute('target', target); 44 token.content = anchor.outerHTML.substring( 45 0, 46 anchor.outerHTML.length - CLOSING_ANCHOR_TAG.length, 47 ); 48 } 49 return existingHTMLInlineRule(tokens, i, ...params); 50 }; 51 52 // Set default target for anchors in html_block tokens. 53 const existingHTMLBlockRule = md.renderer.rules['html_block'] || renderToken; 54 md.renderer.rules['html_block'] = (tokens, i, ...params) => { 55 const token = tokens[i]; 56 const template = document.createElement('template'); 57 template.innerHTML = token.content; 58 const anchors = template.content.querySelectorAll('a'); 59 anchors.forEach((anchor) => { 60 const target = anchor.getAttribute('target') || defaultTarget; 61 anchor.setAttribute('target', target); 62 }); 63 token.content = template.innerHTML; 64 return existingHTMLBlockRule(tokens, i, ...params); 65 }; 66 }