Implement DomElementObjectPool with tests

This commit is contained in:
Daniel Imms
2017-01-04 08:50:19 -08:00
parent 1fbd3c8f65
commit 335850380e
2 changed files with 100 additions and 0 deletions
+47
View File
@@ -0,0 +1,47 @@
import { assert } from 'chai';
import { DomElementObjectPool } from './DomElementObjectPool';
class MockDocument {
private _attr: {[key: string]: string} = {};
constructor() {}
public getAttribute(key: string): string { return this._attr[key]; };
public setAttribute(key: string, value: string): void { this._attr[key] = value; }
}
describe('DomElementObjectPool', () => {
let pool: DomElementObjectPool;
beforeEach(() => {
pool = new DomElementObjectPool('span');
(<any>global).document = {
createElement: () => new MockDocument()
};
});
it('should acquire distinct elements', () => {
const element1 = pool.acquire();
const element2 = pool.acquire();
assert.notEqual(element1, element2);
});
it('should acquire released elements', () => {
const element = pool.acquire();
pool.release(element);
assert.equal(pool.acquire(), element);
});
it('should handle a series of acquisitions and releases', () => {
const element1 = pool.acquire();
const element2 = pool.acquire();
pool.release(element1);
assert.equal(pool.acquire(), element1);
pool.release(element1);
pool.release(element2);
assert.equal(pool.acquire(), element2);
assert.equal(pool.acquire(), element1);
});
it('should throw when releasing an element that was not acquired', () => {
assert.throws(() => pool.release(document.createElement('span')));
});
});
+53
View File
@@ -0,0 +1,53 @@
/**
* @module xterm/utils/DomElementObjectPool
* @license MIT
*/
/**
* An object pool that manages acquisition and releasing of DOM elements for
* when reuse is desirable.
*/
export class DomElementObjectPool {
private static readonly OBJECT_ID_ATTRIBUTE = 'data-obj-id';
private static _objectCount = 0;
private _type: string;
private _pool: HTMLElement[];
private _inUse: {[key: string]: HTMLElement};
/**
* @param type The DOM element type (div, span, etc.).
*/
constructor(private type: string) {
this._type = type;
this._pool = [];
this._inUse = {};
}
public acquire(): HTMLElement {
let element: HTMLElement;
if (this._pool.length === 0) {
element = this.createNew();
} else {
element = this._pool.pop();
}
this._inUse[element.getAttribute(DomElementObjectPool.OBJECT_ID_ATTRIBUTE)] = element;
return element;
}
public release(element: HTMLElement) {
if (!this._inUse[element.getAttribute(DomElementObjectPool.OBJECT_ID_ATTRIBUTE)]) {
throw new Error('Could not release an element not yet acquired');
}
delete this._inUse[element.getAttribute(DomElementObjectPool.OBJECT_ID_ATTRIBUTE)];
this._pool.push(element);
}
private createNew(): HTMLElement {
const element = document.createElement(this._type);
const id = DomElementObjectPool._objectCount++;
element.setAttribute(DomElementObjectPool.OBJECT_ID_ATTRIBUTE, id.toString(10));
return element;
}
}