]> git.proxmox.com Git - mirror_xterm.js.git/commitdiff
Implement DomElementObjectPool with tests
authorDaniel Imms <daimms@microsoft.com>
Wed, 4 Jan 2017 16:50:19 +0000 (08:50 -0800)
committerDaniel Imms <daimms@microsoft.com>
Wed, 4 Jan 2017 16:50:19 +0000 (08:50 -0800)
src/utils/DomElementObjectPool.test.ts [new file with mode: 0644]
src/utils/DomElementObjectPool.ts [new file with mode: 0644]

diff --git a/src/utils/DomElementObjectPool.test.ts b/src/utils/DomElementObjectPool.test.ts
new file mode 100644 (file)
index 0000000..7298f19
--- /dev/null
@@ -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')));
+  });
+});
diff --git a/src/utils/DomElementObjectPool.ts b/src/utils/DomElementObjectPool.ts
new file mode 100644 (file)
index 0000000..8b84fd6
--- /dev/null
@@ -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;
+  }
+}