@ccp-nc/crystvis-js 0.6.1 → 0.7.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.mocharc.yml +4 -0
- package/.vscode/settings.json +5 -1
- package/CHANGELOG.md +54 -16
- package/README.md +75 -2
- package/audit.txt +12 -25
- package/changes.txt +1 -26
- package/demo/demo.css +15 -10
- package/demo/index.html +32 -5
- package/demo/main.js +149 -42
- package/lib/formats/magres.js +156 -1
- package/lib/model.js +31 -0
- package/lib/modelview.js +24 -0
- package/lib/render.js +97 -2
- package/lib/selbox.js +18 -4
- package/lib/visualizer.js +237 -1
- package/outdated.txt +9 -12
- package/package.json +7 -5
- package/test/data/hf_mu_test.magres +62 -0
- package/test/data/hf_test.magres +61 -0
- package/test/data/optimized_muon_65-hf.magres +1424 -0
- package/test/loader.js +115 -1
- package/test/model.js +118 -0
- package/test/setup-dom.cjs +147 -0
- package/test/visualizer.js +475 -0
- package/docs/data/search.json +0 -1
- package/docs/fonts/Inconsolata-Regular.ttf +0 -0
- package/docs/fonts/OpenSans-Regular.ttf +0 -0
- package/docs/fonts/WorkSans-Bold.ttf +0 -0
- package/docs/index.html +0 -10
- package/docs/lib_model.module_js-AtomImage.html +0 -3
- package/docs/lib_model.module_js-BondImage.html +0 -3
- package/docs/lib_model.module_js-Model.html +0 -3
- package/docs/lib_model.module_js.html +0 -3
- package/docs/lib_modelview.module_js-ModelView.html +0 -12
- package/docs/lib_modelview.module_js.html +0 -3
- package/docs/lib_visualizer.module_js-CrystVis.html +0 -3
- package/docs/lib_visualizer.module_js.html +0 -3
- package/docs/model.js.html +0 -2160
- package/docs/modelview.js.html +0 -449
- package/docs/scripts/core.js +0 -726
- package/docs/scripts/core.min.js +0 -23
- package/docs/scripts/resize.js +0 -90
- package/docs/scripts/search.js +0 -265
- package/docs/scripts/search.min.js +0 -6
- package/docs/scripts/third-party/Apache-License-2.0.txt +0 -202
- package/docs/scripts/third-party/fuse.js +0 -9
- package/docs/scripts/third-party/hljs-line-num-original.js +0 -369
- package/docs/scripts/third-party/hljs-line-num.js +0 -1
- package/docs/scripts/third-party/hljs-original.js +0 -5171
- package/docs/scripts/third-party/hljs.js +0 -1
- package/docs/scripts/third-party/popper.js +0 -5
- package/docs/scripts/third-party/tippy.js +0 -1
- package/docs/scripts/third-party/tocbot.js +0 -672
- package/docs/scripts/third-party/tocbot.min.js +0 -1
- package/docs/styles/clean-jsdoc-theme-base.css +0 -1159
- package/docs/styles/clean-jsdoc-theme-dark.css +0 -412
- package/docs/styles/clean-jsdoc-theme-light.css +0 -482
- package/docs/styles/clean-jsdoc-theme-scrollbar.css +0 -30
- package/docs/styles/clean-jsdoc-theme-without-scrollbar.min.css +0 -1
- package/docs/styles/clean-jsdoc-theme.min.css +0 -1
- package/docs/tutorial-Events.html +0 -13
- package/docs/tutorial-Queries.html +0 -16
- package/docs/tutorial-ThreejsMigration.html +0 -25
- package/docs/visualizer.js.html +0 -574
package/lib/formats/magres.js
CHANGED
|
@@ -120,6 +120,146 @@ const MagresParsers = {
|
|
|
120
120
|
isc: parseTwoAtomLine
|
|
121
121
|
};
|
|
122
122
|
|
|
123
|
+
/**
|
|
124
|
+
* Parse the magres_old block to extract hyperfine (HF) tensors and the
|
|
125
|
+
* magnetogyric ratios table.
|
|
126
|
+
*
|
|
127
|
+
* The magres_old block is free-text output produced by older CASTEP versions.
|
|
128
|
+
* Each atom section looks like:
|
|
129
|
+
*
|
|
130
|
+
* ============
|
|
131
|
+
* Atom: H 1
|
|
132
|
+
* ============
|
|
133
|
+
* H 1 Coordinates ...
|
|
134
|
+
*
|
|
135
|
+
* TOTAL tensor
|
|
136
|
+
*
|
|
137
|
+
* a11 a12 a13
|
|
138
|
+
* a21 a22 a23
|
|
139
|
+
* a31 a32 a33
|
|
140
|
+
*
|
|
141
|
+
* H 1 Eigenvalue A_xx ...
|
|
142
|
+
* ...
|
|
143
|
+
* H 1 Iso: ... (MHz)
|
|
144
|
+
*
|
|
145
|
+
* The tensor values are in MHz.
|
|
146
|
+
*
|
|
147
|
+
* The header table looks like either:
|
|
148
|
+
* H Isotope 1 GAMMA = 2.6752E+08 rad T-1 s-1
|
|
149
|
+
* H:Mu User defined. GAMMA = 8.5162E+08 rad T-1 s-1
|
|
150
|
+
*
|
|
151
|
+
* @param {string} text Contents of the magres_old block
|
|
152
|
+
* @param {Array} mlabels Array of [species, index] pairs for each atom
|
|
153
|
+
* @param {int} N Number of atoms
|
|
154
|
+
* @returns {Object} { hf_data: Array<TensorData|null>,
|
|
155
|
+
* gamma_ratios: Object<string, {isotope, gamma}> }
|
|
156
|
+
*/
|
|
157
|
+
function parseMagresOldBlock(text, mlabels, N) {
|
|
158
|
+
const lines = text.split('\n');
|
|
159
|
+
const hf_data = new Array(N).fill(null);
|
|
160
|
+
const gamma_ratios = {}; // species label → { isotope: int|null, gamma: float }
|
|
161
|
+
|
|
162
|
+
let current_sp = null;
|
|
163
|
+
let current_idx = null;
|
|
164
|
+
// 'idle' | 'reading_rows' | 'waiting_eigenvalue_label'
|
|
165
|
+
let state = 'idle';
|
|
166
|
+
let tensor_rows = [];
|
|
167
|
+
let pending_tensor = null;
|
|
168
|
+
|
|
169
|
+
for (let i = 0; i < lines.length; i++) {
|
|
170
|
+
const line = lines[i].trim();
|
|
171
|
+
|
|
172
|
+
// Parse magnetogyric ratio table lines:
|
|
173
|
+
// "H Isotope 1 GAMMA = 2.6752E+08 rad T-1 s-1"
|
|
174
|
+
// "H:Mu User defined. GAMMA = 8.5162E+08 rad T-1 s-1"
|
|
175
|
+
const gamma_iso_match = line.match(/^(\S+)\s+Isotope\s+(\d+)\s+GAMMA\s*=\s*([+-]?[\d.]+E[+-]?\d+)/i);
|
|
176
|
+
if (gamma_iso_match) {
|
|
177
|
+
gamma_ratios[gamma_iso_match[1]] = {
|
|
178
|
+
isotope: parseInt(gamma_iso_match[2]),
|
|
179
|
+
gamma: parseFloat(gamma_iso_match[3])
|
|
180
|
+
};
|
|
181
|
+
continue;
|
|
182
|
+
}
|
|
183
|
+
const gamma_user_match = line.match(/^(\S+)\s+User defined\.\s+GAMMA\s*=\s*([+-]?[\d.]+E[+-]?\d+)/i);
|
|
184
|
+
if (gamma_user_match) {
|
|
185
|
+
gamma_ratios[gamma_user_match[1]] = {
|
|
186
|
+
isotope: null,
|
|
187
|
+
gamma: parseFloat(gamma_user_match[2])
|
|
188
|
+
};
|
|
189
|
+
continue;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
// Look for "Atom: ELEM INDEX"
|
|
193
|
+
const atom_match = line.match(/^Atom:\s+(\S+)\s+(\d+)$/);
|
|
194
|
+
if (atom_match) {
|
|
195
|
+
current_sp = atom_match[1];
|
|
196
|
+
current_idx = parseInt(atom_match[2]);
|
|
197
|
+
state = 'idle';
|
|
198
|
+
tensor_rows = [];
|
|
199
|
+
pending_tensor = null;
|
|
200
|
+
continue;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
// Look for the "TOTAL tensor" header (exact match – not "TOTAL Shielding Tensor" etc.)
|
|
204
|
+
if (line === 'TOTAL tensor') {
|
|
205
|
+
state = 'reading_rows';
|
|
206
|
+
tensor_rows = [];
|
|
207
|
+
pending_tensor = null;
|
|
208
|
+
continue;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
if (state === 'reading_rows' && current_sp !== null) {
|
|
212
|
+
if (line === '') continue;
|
|
213
|
+
|
|
214
|
+
const parts = line.split(/\s+/).filter(s => s !== '');
|
|
215
|
+
const nums = parts.map(parseFloat);
|
|
216
|
+
|
|
217
|
+
if (nums.length === 3 && nums.every(n => !isNaN(n))) {
|
|
218
|
+
tensor_rows.push(nums);
|
|
219
|
+
if (tensor_rows.length === 3) {
|
|
220
|
+
// Matrix complete – wait for Eigenvalue label to confirm it's HF
|
|
221
|
+
pending_tensor = tensor_rows.slice();
|
|
222
|
+
state = 'waiting_eigenvalue_label';
|
|
223
|
+
tensor_rows = [];
|
|
224
|
+
}
|
|
225
|
+
} else {
|
|
226
|
+
// Unexpected non-numeric line – abort
|
|
227
|
+
state = 'idle';
|
|
228
|
+
tensor_rows = [];
|
|
229
|
+
}
|
|
230
|
+
continue;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
if (state === 'waiting_eigenvalue_label' && current_sp !== null) {
|
|
234
|
+
if (line === '') continue;
|
|
235
|
+
|
|
236
|
+
// Look for "SPECIES INDEX Eigenvalue LABEL VALUE"
|
|
237
|
+
// HF tensors use A_xx / A_yy / A_zz.
|
|
238
|
+
// EFG uses V_xx, shielding uses sigma_xx – we skip those.
|
|
239
|
+
const eval_match = line.match(/Eigenvalue\s+(\S+)/);
|
|
240
|
+
if (eval_match) {
|
|
241
|
+
const label = eval_match[1]; // e.g. A_xx, V_xx, sigma_xx
|
|
242
|
+
if (label.startsWith('A_')) {
|
|
243
|
+
// Confirmed hyperfine – store the tensor
|
|
244
|
+
const idx = _.findIndex(mlabels, x =>
|
|
245
|
+
x[0] === current_sp && x[1] === current_idx
|
|
246
|
+
);
|
|
247
|
+
if (idx >= 0) {
|
|
248
|
+
hf_data[idx] = new TensorData(pending_tensor);
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
// Any other label (V_xx, sigma_xx, …) – silently discard
|
|
252
|
+
state = 'idle';
|
|
253
|
+
pending_tensor = null;
|
|
254
|
+
}
|
|
255
|
+
// Non-eigenvalue lines (could be blank) – keep waiting
|
|
256
|
+
continue;
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
return { hf_data, gamma_ratios };
|
|
261
|
+
}
|
|
262
|
+
|
|
123
263
|
function load(contents, filename='magres') {
|
|
124
264
|
|
|
125
265
|
const known_blocks = ['atoms', 'magres'];
|
|
@@ -275,7 +415,10 @@ function load(contents, filename='magres') {
|
|
|
275
415
|
for (let i = 0; i < ablock.atom.lines.length; ++i) {
|
|
276
416
|
let l = ablock.atom.lines[i];
|
|
277
417
|
|
|
278
|
-
|
|
418
|
+
// CASTEP custom species use the form ELEMENT:LABEL (e.g. H:Mu).
|
|
419
|
+
// Strip the colon-suffix to recover a valid element symbol while
|
|
420
|
+
// keeping the full species identifier in mlabels/labels for lookup.
|
|
421
|
+
elems.push(l[0].split(':')[0]);
|
|
279
422
|
mlabels.push([l[1], parseInt(l[2])]);
|
|
280
423
|
labels.push(l[1]);
|
|
281
424
|
pos.push(_.map(l.slice(3,6), function(x) {
|
|
@@ -360,6 +503,18 @@ function load(contents, filename='magres') {
|
|
|
360
503
|
|
|
361
504
|
}
|
|
362
505
|
|
|
506
|
+
// Parse hyperfine tensors and gamma ratios from magres_old block
|
|
507
|
+
const magres_old_text = blocks['magres_old'];
|
|
508
|
+
if (magres_old_text && typeof magres_old_text === 'string') {
|
|
509
|
+
const { hf_data, gamma_ratios } = parseMagresOldBlock(magres_old_text, mlabels, N);
|
|
510
|
+
if (hf_data.some(d => d !== null)) {
|
|
511
|
+
atoms.set_array('hf', hf_data);
|
|
512
|
+
}
|
|
513
|
+
if (Object.keys(gamma_ratios).length > 0) {
|
|
514
|
+
atoms.info['hf-gyromagnetic-ratios'] = gamma_ratios;
|
|
515
|
+
}
|
|
516
|
+
}
|
|
517
|
+
|
|
363
518
|
let structs = {};
|
|
364
519
|
structs[filename] = atoms;
|
|
365
520
|
|
package/lib/model.js
CHANGED
|
@@ -1776,6 +1776,37 @@ class Model {
|
|
|
1776
1776
|
return new ModelView(this, indices);
|
|
1777
1777
|
}
|
|
1778
1778
|
|
|
1779
|
+
/**
|
|
1780
|
+
* Reconstruct a {@link ModelView} from a plain array of atom-image indices,
|
|
1781
|
+
* as produced by {@link ModelView#toIndices}. This is the stable public API
|
|
1782
|
+
* for deserialising a saved selection.
|
|
1783
|
+
*
|
|
1784
|
+
* @param {number[]} indices
|
|
1785
|
+
* @return {ModelView}
|
|
1786
|
+
*/
|
|
1787
|
+
viewFromIndices(indices = []) {
|
|
1788
|
+
return this.view(indices);
|
|
1789
|
+
}
|
|
1790
|
+
|
|
1791
|
+
/**
|
|
1792
|
+
* Reconstruct a {@link ModelView} from an array of crystallographic site
|
|
1793
|
+
* labels (`crystLabel`), as produced by {@link ModelView#toLabels}. This
|
|
1794
|
+
* is resilient to atom-index reordering across reloads.
|
|
1795
|
+
*
|
|
1796
|
+
* @param {string[]} labels
|
|
1797
|
+
* @return {ModelView}
|
|
1798
|
+
*/
|
|
1799
|
+
viewFromLabels(labels = []) {
|
|
1800
|
+
const wanted = new Set(labels);
|
|
1801
|
+
const indices = [];
|
|
1802
|
+
for (let i = 0; i < this._atom_images.length; ++i) {
|
|
1803
|
+
if (wanted.has(this._atom_images[i].crystLabel)) {
|
|
1804
|
+
indices.push(i);
|
|
1805
|
+
}
|
|
1806
|
+
}
|
|
1807
|
+
return this.view(indices);
|
|
1808
|
+
}
|
|
1809
|
+
|
|
1779
1810
|
/**
|
|
1780
1811
|
* Set a property on a series of atom images
|
|
1781
1812
|
*
|
package/lib/modelview.js
CHANGED
|
@@ -440,6 +440,30 @@ class ModelView {
|
|
|
440
440
|
return this;
|
|
441
441
|
}
|
|
442
442
|
|
|
443
|
+
// ─── Serialisation helpers ───────────────────────────────────────────────────
|
|
444
|
+
|
|
445
|
+
/**
|
|
446
|
+
* Return a plain array of the atom-image indices in this view.
|
|
447
|
+
* Use this to serialise a selection; restore it with
|
|
448
|
+
* {@link Model#viewFromIndices}.
|
|
449
|
+
*
|
|
450
|
+
* @return {number[]}
|
|
451
|
+
*/
|
|
452
|
+
toIndices() {
|
|
453
|
+
return Array.from(this._indices);
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
/**
|
|
457
|
+
* Return the crystallographic site labels (`crystLabel`) of the atoms
|
|
458
|
+
* in this view. Use this to serialise a selection in a way that is
|
|
459
|
+
* robust to atom reordering; restore with {@link Model#viewFromLabels}.
|
|
460
|
+
*
|
|
461
|
+
* @return {string[]}
|
|
462
|
+
*/
|
|
463
|
+
toLabels() {
|
|
464
|
+
return this._images.map(a => a.crystLabel);
|
|
465
|
+
}
|
|
466
|
+
|
|
443
467
|
}
|
|
444
468
|
|
|
445
469
|
export {
|
package/lib/render.js
CHANGED
|
@@ -116,7 +116,8 @@ class Renderer {
|
|
|
116
116
|
|
|
117
117
|
// Raycast for clicks
|
|
118
118
|
this._rcastlist = [];
|
|
119
|
-
this.
|
|
119
|
+
this._boundRaycastClick = this._raycastClick.bind(this);
|
|
120
|
+
this._r.domElement.addEventListener('pointerdown', this._boundRaycastClick);
|
|
120
121
|
|
|
121
122
|
// Groups
|
|
122
123
|
this._groups = {
|
|
@@ -162,7 +163,7 @@ class Renderer {
|
|
|
162
163
|
}
|
|
163
164
|
|
|
164
165
|
_animate() {
|
|
165
|
-
requestAnimationFrame(this._animate.bind(this));
|
|
166
|
+
this._animFrameId = requestAnimationFrame(this._animate.bind(this));
|
|
166
167
|
this._render();
|
|
167
168
|
}
|
|
168
169
|
|
|
@@ -335,6 +336,48 @@ class Renderer {
|
|
|
335
336
|
_.pull(this._sboxlist, sbl);
|
|
336
337
|
}
|
|
337
338
|
|
|
339
|
+
/**
|
|
340
|
+
* Tear down the renderer, cancelling the animation loop, removing all canvas
|
|
341
|
+
* event listeners (orbit controls, raycaster, selection box), disconnecting any
|
|
342
|
+
* ResizeObserver, and releasing the WebGL context. After this call the instance
|
|
343
|
+
* must not be used again.
|
|
344
|
+
*/
|
|
345
|
+
dispose() {
|
|
346
|
+
// 1. Stop the animation loop
|
|
347
|
+
if (this._animFrameId != null) {
|
|
348
|
+
cancelAnimationFrame(this._animFrameId);
|
|
349
|
+
this._animFrameId = null;
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
// 2. Remove the raycaster's own pointerdown listener
|
|
353
|
+
this._r.domElement.removeEventListener('pointerdown', this._boundRaycastClick);
|
|
354
|
+
this._rcastlist = [];
|
|
355
|
+
this._sboxlist = [];
|
|
356
|
+
|
|
357
|
+
// 3. Dispose OrbitControls (removes its own listeners from the canvas)
|
|
358
|
+
if (this._oc) {
|
|
359
|
+
this._oc.dispose();
|
|
360
|
+
this._oc = null;
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
// 4. Dispose the selection box helper (removes its listeners and overlay)
|
|
364
|
+
if (this._sboxhelp) {
|
|
365
|
+
this._sboxhelp.dispose();
|
|
366
|
+
this._sboxhelp = null;
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
// 5. Stop watching for container resize
|
|
370
|
+
if (this._resizeObs) {
|
|
371
|
+
this._resizeObs.disconnect();
|
|
372
|
+
this._resizeObs = null;
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
// 6. Release the WebGL context and GPU resources
|
|
376
|
+
this._r.dispose();
|
|
377
|
+
this._r.domElement.remove();
|
|
378
|
+
this._r = null;
|
|
379
|
+
}
|
|
380
|
+
|
|
338
381
|
/**
|
|
339
382
|
* Set properties of ambient light
|
|
340
383
|
*
|
|
@@ -413,6 +456,58 @@ class Renderer {
|
|
|
413
456
|
return this._oc.target;
|
|
414
457
|
}
|
|
415
458
|
|
|
459
|
+
/**
|
|
460
|
+
* Return a plain serialisable snapshot of the current camera state.
|
|
461
|
+
*
|
|
462
|
+
* @return {{ position: {x,y,z}, target: {x,y,z}, zoom: number }}
|
|
463
|
+
*/
|
|
464
|
+
getCameraState() {
|
|
465
|
+
const pos = this._c.position;
|
|
466
|
+
const tgt = this._oc.target;
|
|
467
|
+
return {
|
|
468
|
+
position: { x: pos.x, y: pos.y, z: pos.z },
|
|
469
|
+
target: { x: tgt.x, y: tgt.y, z: tgt.z },
|
|
470
|
+
zoom: this._c.zoom,
|
|
471
|
+
};
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
/**
|
|
475
|
+
* Restore a camera snapshot produced by {@link Renderer#getCameraState}.
|
|
476
|
+
*
|
|
477
|
+
* @param {{ position?: {x,y,z}, target?: {x,y,z}, zoom?: number }} state
|
|
478
|
+
*/
|
|
479
|
+
setCameraState(state) {
|
|
480
|
+
if (!state) return;
|
|
481
|
+
|
|
482
|
+
if (state.position) {
|
|
483
|
+
this._c.position.set(state.position.x, state.position.y, state.position.z);
|
|
484
|
+
}
|
|
485
|
+
if (state.target) {
|
|
486
|
+
this._oc.target.set(state.target.x, state.target.y, state.target.z);
|
|
487
|
+
}
|
|
488
|
+
if (typeof state.zoom === 'number') {
|
|
489
|
+
this._c.zoom = state.zoom;
|
|
490
|
+
this._c.updateProjectionMatrix();
|
|
491
|
+
}
|
|
492
|
+
this._oc.update();
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
/**
|
|
496
|
+
* Subscribe to camera-change events fired by OrbitControls whenever
|
|
497
|
+
* the user rotates, pans, or zooms. The callback receives a camera
|
|
498
|
+
* state snapshot identical to the one returned by {@link Renderer#getCameraState}.
|
|
499
|
+
*
|
|
500
|
+
* @param {Function} callback `callback(cameraState)` called on each change
|
|
501
|
+
* @return {Function} Unsubscribe function — call it to remove the listener
|
|
502
|
+
*/
|
|
503
|
+
onCameraChange(callback) {
|
|
504
|
+
const handler = () => callback(this.getCameraState());
|
|
505
|
+
this._oc.addEventListener('change', handler);
|
|
506
|
+
return () => {
|
|
507
|
+
if (this._oc) this._oc.removeEventListener('change', handler);
|
|
508
|
+
};
|
|
509
|
+
}
|
|
510
|
+
|
|
416
511
|
|
|
417
512
|
/**
|
|
418
513
|
* Remove all currently rendered objects.
|
package/lib/selbox.js
CHANGED
|
@@ -256,6 +256,10 @@ var SelectionHelper = (function() {
|
|
|
256
256
|
this.isDown = false;
|
|
257
257
|
this.selectOverCallback = null;
|
|
258
258
|
|
|
259
|
+
// AbortController lets us remove all listeners in one call via dispose()
|
|
260
|
+
this._ac = new AbortController();
|
|
261
|
+
const signal = this._ac.signal;
|
|
262
|
+
|
|
259
263
|
this.domElement.addEventListener('pointerdown', function(event) {
|
|
260
264
|
|
|
261
265
|
if (event.shiftKey) {
|
|
@@ -263,7 +267,7 @@ var SelectionHelper = (function() {
|
|
|
263
267
|
this.onSelectStart(event);
|
|
264
268
|
}
|
|
265
269
|
|
|
266
|
-
}.bind(this),
|
|
270
|
+
}.bind(this), { signal });
|
|
267
271
|
|
|
268
272
|
this.domElement.addEventListener('pointermove', function(event) {
|
|
269
273
|
|
|
@@ -273,7 +277,7 @@ var SelectionHelper = (function() {
|
|
|
273
277
|
|
|
274
278
|
}
|
|
275
279
|
|
|
276
|
-
}.bind(this),
|
|
280
|
+
}.bind(this), { signal });
|
|
277
281
|
|
|
278
282
|
this.domElement.addEventListener('pointerup', function(event) {
|
|
279
283
|
|
|
@@ -282,7 +286,7 @@ var SelectionHelper = (function() {
|
|
|
282
286
|
this.onSelectOver(event);
|
|
283
287
|
}
|
|
284
288
|
|
|
285
|
-
}.bind(this),
|
|
289
|
+
}.bind(this), { signal });
|
|
286
290
|
|
|
287
291
|
this.domElement.addEventListener('keyup', function(event) {
|
|
288
292
|
|
|
@@ -291,10 +295,20 @@ var SelectionHelper = (function() {
|
|
|
291
295
|
this.onSelectOver(event);
|
|
292
296
|
}
|
|
293
297
|
|
|
294
|
-
}.bind(this),
|
|
298
|
+
}.bind(this), { signal });
|
|
295
299
|
|
|
296
300
|
}
|
|
297
301
|
|
|
302
|
+
SelectionHelper.prototype.dispose = function() {
|
|
303
|
+
// Remove all canvas event listeners registered by this helper
|
|
304
|
+
this._ac.abort();
|
|
305
|
+
// Remove the overlay element from the DOM
|
|
306
|
+
if (this.element) {
|
|
307
|
+
this.element.remove();
|
|
308
|
+
this.element = null;
|
|
309
|
+
}
|
|
310
|
+
};
|
|
311
|
+
|
|
298
312
|
SelectionHelper.prototype.onSelectStart = function(event) {
|
|
299
313
|
|
|
300
314
|
this.element.css({
|