mirror of
https://github.com/wavetermdev/xterm.js.git
synced 2026-08-05 13:43:48 -07:00
Merge pull request #619 from Tyriar/618_register_link_before_dom
Allow links to be registered before terminal is attached to DOM
This commit is contained in:
+85
-69
@@ -8,9 +8,9 @@ import { Linkifier } from './Linkifier';
|
||||
import { LinkMatcher } from './Types';
|
||||
|
||||
class TestLinkifier extends Linkifier {
|
||||
constructor(document: Document, rows: HTMLElement[]) {
|
||||
constructor() {
|
||||
Linkifier.TIME_BEFORE_LINKIFY = 0;
|
||||
super(document, rows);
|
||||
super();
|
||||
}
|
||||
|
||||
public get linkMatchers(): LinkMatcher[] { return this._linkMatchers; }
|
||||
@@ -25,89 +25,105 @@ describe('Linkifier', () => {
|
||||
let linkifier: TestLinkifier;
|
||||
|
||||
beforeEach(done => {
|
||||
rows = [];
|
||||
jsdom.env('', (err, w) => {
|
||||
window = w;
|
||||
document = window.document;
|
||||
linkifier = new TestLinkifier(document, rows);
|
||||
container = document.createElement('div');
|
||||
document.body.appendChild(container);
|
||||
linkifier = new TestLinkifier();
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
function addRow(text: string) {
|
||||
const element = document.createElement('div');
|
||||
element.textContent = text;
|
||||
container.appendChild(element);
|
||||
rows.push(element);
|
||||
}
|
||||
|
||||
function clickElement(element: Node) {
|
||||
const event = document.createEvent('MouseEvent');
|
||||
event.initMouseEvent('click', true, true, window, 0, 0, 0, 0, 0, false, false, false, false, 0, null);
|
||||
element.dispatchEvent(event);
|
||||
}
|
||||
|
||||
function assertLinkifiesEntireRow(uri: string, done: MochaDone) {
|
||||
addRow(uri);
|
||||
linkifier.linkifyRow(0);
|
||||
setTimeout(() => {
|
||||
assert.equal((<HTMLElement>rows[0].firstChild).tagName, 'A');
|
||||
assert.equal((<HTMLElement>rows[0].firstChild).textContent, uri);
|
||||
describe('before attachToDom', () => {
|
||||
it('should allow link matcher registration', done => {
|
||||
assert.doesNotThrow(() => {
|
||||
const linkMatcherId = linkifier.registerLinkMatcher(/foo/, () => {});
|
||||
assert.isTrue(linkifier.deregisterLinkMatcher(linkMatcherId));
|
||||
done();
|
||||
}, 0);
|
||||
}
|
||||
|
||||
describe('http links', () => {
|
||||
it('should allow ~ character in URI path', done => assertLinkifiesEntireRow('http://foo.com/a~b#c~d?e~f', done));
|
||||
});
|
||||
|
||||
describe('validationCallback', () => {
|
||||
it('should enable link if true', done => {
|
||||
addRow('test');
|
||||
linkifier.registerLinkMatcher(/test/, () => done(), {
|
||||
validationCallback: (url, cb) => {
|
||||
cb(true);
|
||||
assert.equal((<HTMLElement>rows[0].firstChild).tagName, 'A');
|
||||
setTimeout(() => clickElement(rows[0].firstChild), 0);
|
||||
}
|
||||
});
|
||||
linkifier.linkifyRow(0);
|
||||
});
|
||||
|
||||
it('should disable link if false', done => {
|
||||
addRow('test');
|
||||
linkifier.registerLinkMatcher(/test/, () => assert.fail(), {
|
||||
validationCallback: (url, cb) => {
|
||||
cb(false);
|
||||
assert.equal((<HTMLElement>rows[0].firstChild).tagName, 'A');
|
||||
setTimeout(() => clickElement(rows[0].firstChild), 0);
|
||||
}
|
||||
});
|
||||
linkifier.linkifyRow(0);
|
||||
// Allow time for the click to be performed
|
||||
setTimeout(() => done(), 10);
|
||||
});
|
||||
});
|
||||
|
||||
describe('priority', () => {
|
||||
it('should order the list from highest priority to lowest #1', () => {
|
||||
const aId = linkifier.registerLinkMatcher(/a/, () => {}, { priority: 1 });
|
||||
const bId = linkifier.registerLinkMatcher(/b/, () => {}, { priority: -1 });
|
||||
assert.deepEqual(linkifier.linkMatchers.map(lm => lm.id), [aId, 0, bId]);
|
||||
describe('after attachToDom', () => {
|
||||
beforeEach(() => {
|
||||
rows = [];
|
||||
linkifier.attachToDom(document, rows);
|
||||
container = document.createElement('div');
|
||||
document.body.appendChild(container);
|
||||
});
|
||||
|
||||
it('should order the list from highest priority to lowest #2', () => {
|
||||
const aId = linkifier.registerLinkMatcher(/a/, () => {}, { priority: -1 });
|
||||
const bId = linkifier.registerLinkMatcher(/b/, () => {}, { priority: 1 });
|
||||
assert.deepEqual(linkifier.linkMatchers.map(lm => lm.id), [bId, 0, aId]);
|
||||
function addRow(text: string) {
|
||||
const element = document.createElement('div');
|
||||
element.textContent = text;
|
||||
container.appendChild(element);
|
||||
rows.push(element);
|
||||
}
|
||||
|
||||
function clickElement(element: Node) {
|
||||
const event = document.createEvent('MouseEvent');
|
||||
event.initMouseEvent('click', true, true, window, 0, 0, 0, 0, 0, false, false, false, false, 0, null);
|
||||
element.dispatchEvent(event);
|
||||
}
|
||||
|
||||
function assertLinkifiesEntireRow(uri: string, done: MochaDone) {
|
||||
addRow(uri);
|
||||
linkifier.linkifyRow(0);
|
||||
setTimeout(() => {
|
||||
assert.equal((<HTMLElement>rows[0].firstChild).tagName, 'A');
|
||||
assert.equal((<HTMLElement>rows[0].firstChild).textContent, uri);
|
||||
done();
|
||||
}, 0);
|
||||
}
|
||||
|
||||
describe('http links', () => {
|
||||
it('should allow ~ character in URI path', done => assertLinkifiesEntireRow('http://foo.com/a~b#c~d?e~f', done));
|
||||
});
|
||||
|
||||
it('should order items of equal priority in the order they are added', () => {
|
||||
const aId = linkifier.registerLinkMatcher(/a/, () => {}, { priority: 0 });
|
||||
const bId = linkifier.registerLinkMatcher(/b/, () => {}, { priority: 0 });
|
||||
assert.deepEqual(linkifier.linkMatchers.map(lm => lm.id), [0, aId, bId]);
|
||||
describe('validationCallback', () => {
|
||||
it('should enable link if true', done => {
|
||||
addRow('test');
|
||||
linkifier.registerLinkMatcher(/test/, () => done(), {
|
||||
validationCallback: (url, cb) => {
|
||||
cb(true);
|
||||
assert.equal((<HTMLElement>rows[0].firstChild).tagName, 'A');
|
||||
setTimeout(() => clickElement(rows[0].firstChild), 0);
|
||||
}
|
||||
});
|
||||
linkifier.linkifyRow(0);
|
||||
});
|
||||
|
||||
it('should disable link if false', done => {
|
||||
addRow('test');
|
||||
linkifier.registerLinkMatcher(/test/, () => assert.fail(), {
|
||||
validationCallback: (url, cb) => {
|
||||
cb(false);
|
||||
assert.equal((<HTMLElement>rows[0].firstChild).tagName, 'A');
|
||||
setTimeout(() => clickElement(rows[0].firstChild), 0);
|
||||
}
|
||||
});
|
||||
linkifier.linkifyRow(0);
|
||||
// Allow time for the click to be performed
|
||||
setTimeout(() => done(), 10);
|
||||
});
|
||||
});
|
||||
|
||||
describe('priority', () => {
|
||||
it('should order the list from highest priority to lowest #1', () => {
|
||||
const aId = linkifier.registerLinkMatcher(/a/, () => {}, { priority: 1 });
|
||||
const bId = linkifier.registerLinkMatcher(/b/, () => {}, { priority: -1 });
|
||||
assert.deepEqual(linkifier.linkMatchers.map(lm => lm.id), [aId, 0, bId]);
|
||||
});
|
||||
|
||||
it('should order the list from highest priority to lowest #2', () => {
|
||||
const aId = linkifier.registerLinkMatcher(/a/, () => {}, { priority: -1 });
|
||||
const bId = linkifier.registerLinkMatcher(/b/, () => {}, { priority: 1 });
|
||||
assert.deepEqual(linkifier.linkMatchers.map(lm => lm.id), [bId, 0, aId]);
|
||||
});
|
||||
|
||||
it('should order items of equal priority in the order they are added', () => {
|
||||
const aId = linkifier.registerLinkMatcher(/a/, () => {}, { priority: 0 });
|
||||
const bId = linkifier.registerLinkMatcher(/b/, () => {}, { priority: 0 });
|
||||
assert.deepEqual(linkifier.linkMatchers.map(lm => lm.id), [0, aId, bId]);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
+16
-3
@@ -49,19 +49,32 @@ export class Linkifier {
|
||||
private _rowTimeoutIds: number[];
|
||||
private _nextLinkMatcherId = HYPERTEXT_LINK_MATCHER_ID;
|
||||
|
||||
constructor(document: Document, rows: HTMLElement[]) {
|
||||
this._document = document;
|
||||
this._rows = rows;
|
||||
constructor() {
|
||||
this._rowTimeoutIds = [];
|
||||
this._linkMatchers = [];
|
||||
this.registerLinkMatcher(strictUrlRegex, null, { matchIndex: 1 });
|
||||
}
|
||||
|
||||
/**
|
||||
* Attaches the linkifier to the DOM, enabling linkification.
|
||||
* @param document The document object.
|
||||
* @param rows The array of rows to apply links to.
|
||||
*/
|
||||
public attachToDom(document: Document, rows: HTMLElement[]) {
|
||||
this._document = document;
|
||||
this._rows = rows;
|
||||
}
|
||||
|
||||
/**
|
||||
* Queues a row for linkification.
|
||||
* @param {number} rowIndex The index of the row to linkify.
|
||||
*/
|
||||
public linkifyRow(rowIndex: number): void {
|
||||
// Don't attempt linkify if not yet attached to DOM
|
||||
if (!this._document) {
|
||||
return;
|
||||
}
|
||||
|
||||
const timeoutId = this._rowTimeoutIds[rowIndex];
|
||||
if (timeoutId) {
|
||||
clearTimeout(timeoutId);
|
||||
|
||||
+2
-2
@@ -219,7 +219,7 @@ function Terminal(options) {
|
||||
this.parser = new Parser(this.inputHandler, this);
|
||||
// Reuse renderer if the Terminal is being recreated via a Terminal.reset call.
|
||||
this.renderer = this.renderer || null;
|
||||
this.linkifier = this.linkifier || null;;
|
||||
this.linkifier = this.linkifier || new Linkifier();
|
||||
|
||||
// user input states
|
||||
this.writeBuffer = [];
|
||||
@@ -645,7 +645,7 @@ Terminal.prototype.open = function(parent) {
|
||||
this.rowContainer.classList.add('xterm-rows');
|
||||
this.element.appendChild(this.rowContainer);
|
||||
this.children = [];
|
||||
this.linkifier = new Linkifier(document, this.children);
|
||||
this.linkifier.attachToDom(document, this.children);
|
||||
|
||||
// Create the container that will hold helpers like the textarea for
|
||||
// capturing DOM Events. Then produce the helpers.
|
||||
|
||||
Reference in New Issue
Block a user