/** * Copyright (c) 2019 The xterm.js authors. All rights reserved. * @license MIT */ import { Terminal, ITerminalAddon, IDisposable } from 'xterm'; import { ILinkProviderOptions, WebLinkProvider } from './WebLinkProvider'; const protocolClause = '(https?:\\/\\/)'; const domainCharacterSet = '[\\da-z\\.-]+'; const negatedDomainCharacterSet = '[^\\da-z\\.-]+'; const domainBodyClause = '(' + domainCharacterSet + ')'; const tldClause = '([a-z\\.]{2,18})'; const ipClause = '((\\d{1,3}\\.){3}\\d{1,3})'; const localHostClause = '(localhost)'; const portClause = '(:\\d{1,5})'; const hostClause = '((' + domainBodyClause + '\\.' + tldClause + ')|' + ipClause + '|' + localHostClause + ')' + portClause + '?'; const pathCharacterSet = '(\\/[\\/\\w\\.\\-%~:+@]*)*([^:"\'\\s])'; const pathClause = '(' + pathCharacterSet + ')?'; const queryStringHashFragmentCharacterSet = '[0-9\\w\\[\\]\\(\\)\\/\\?\\!#@$%&\'*+,:;~\\=\\.\\-]*'; const queryStringClause = '(\\?' + queryStringHashFragmentCharacterSet + ')?'; const hashFragmentClause = '(#' + queryStringHashFragmentCharacterSet + ')?'; const negatedPathCharacterSet = '[^\\/\\w\\.\\-%]+'; const bodyClause = hostClause + pathClause + queryStringClause + hashFragmentClause; const start = '(?:^|' + negatedDomainCharacterSet + ')('; const end = ')($|' + negatedPathCharacterSet + ')'; const strictUrlRegex = new RegExp(start + protocolClause + bodyClause + end); function handleLink(event: MouseEvent, uri: string): void { const newWindow = window.open(); if (newWindow) { try { newWindow.opener = null; } catch { // no-op, Electron can throw } newWindow.location.href = uri; } else { console.warn('Opening link blocked as opener could not be cleared'); } } export class WebLinksAddon implements ITerminalAddon { private _terminal: Terminal | undefined; private _linkProvider: IDisposable | undefined; constructor( private _handler: (event: MouseEvent, uri: string) => void = handleLink, private _options: ILinkProviderOptions = {} ) { } public activate(terminal: Terminal): void { this._terminal = terminal; const options = this._options as ILinkProviderOptions; const regex = options.urlRegex || strictUrlRegex; this._linkProvider = this._terminal.registerLinkProvider(new WebLinkProvider(this._terminal, regex, this._handler, options)); } public dispose(): void { this._linkProvider?.dispose(); } }