pts 0.10.9 → 0.10.10

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/dist/pts.d.ts CHANGED
@@ -336,11 +336,17 @@ export class Img {
336
336
  protected _scale: number;
337
337
  protected _loaded: boolean;
338
338
  protected _editable: boolean;
339
- constructor(editable?: boolean, pixelScale?: number, crossOrigin?: boolean);
340
- static load(src: string, editable?: boolean, pixelScale?: number, ready?: (img: any) => {}): Img;
339
+ protected _space: CanvasSpace;
340
+ constructor(editable?: boolean, space?: CanvasSpace, crossOrigin?: boolean);
341
+ static load(src: string, editable?: boolean, space?: CanvasSpace, ready?: (img: any) => {}): Img;
342
+ static loadAsync(src: string, editable?: boolean, space?: CanvasSpace): Promise<Img>;
343
+ static loadPattern(src: string, space: CanvasSpace, repeat?: CanvasPatternRepetition, editable?: boolean): Promise<CanvasPattern>;
344
+ static blank(size: PtLike, space: CanvasSpace, scale?: number): Img;
341
345
  load(src: string): Promise<Img>;
342
346
  protected _drawToScale(canvasScale: number | PtLike, img: CanvasImageSource): void;
347
+ initCanvas(width: number, height: number, canvasScale?: number | PtLike): void;
343
348
  bitmap(size?: PtLike): Promise<ImageBitmap>;
349
+ pattern(reptition?: CanvasPatternRepetition, dynamic?: boolean): CanvasPattern;
344
350
  sync(): void;
345
351
  pixel(p: PtLike, rescale?: boolean | number): Pt;
346
352
  static getPixel(imgData: ImageData, p: PtLike): Pt;
@@ -348,10 +354,11 @@ export class Img {
348
354
  crop(box: Bound): ImageData;
349
355
  filter(css: string): this;
350
356
  cleanup(): void;
351
- static fromBlob(blob: Blob, editable?: boolean, pixelScale?: number): Promise<Img>;
357
+ static fromBlob(blob: Blob, editable?: boolean, space?: CanvasSpace): Promise<Img>;
352
358
  static imageDataToBlob(data: ImageData): Promise<Blob>;
353
359
  toBase64(): string;
354
360
  toBlob(): Promise<Blob>;
361
+ getForm(): CanvasForm;
355
362
  get current(): CanvasImageSource;
356
363
  get image(): HTMLImageElement;
357
364
  get canvas(): HTMLCanvasElement;
@@ -361,6 +368,7 @@ export class Img {
361
368
  get pixelScale(): number;
362
369
  get imageSize(): Pt;
363
370
  get canvasSize(): Pt;
371
+ get scaledMatrix(): Mat;
364
372
  }
365
373
 
366
374
  export class Vec {
@@ -389,11 +397,21 @@ export class Vec {
389
397
  static map(a: PtLike, fn: (n: number, index: number, arr: any) => number): PtLike;
390
398
  }
391
399
  export class Mat {
400
+ protected _33: GroupLike;
401
+ constructor();
402
+ get value(): GroupLike;
403
+ get domMatrix(): DOMMatrix;
404
+ reset(): void;
405
+ scale2D(val: PtLike, at?: PtLike): this;
406
+ rotate2D(ang: number, at?: PtLike): this;
407
+ translate2D(val: PtLike): this;
408
+ shear2D(val: PtLike, at?: PtLike): this;
392
409
  static add(a: GroupLike, b: GroupLike | number[][] | number): Group;
393
410
  static multiply(a: GroupLike, b: GroupLike | number[][] | number, transposed?: boolean, elementwise?: boolean): Group;
394
411
  static zipSlice(g: GroupLike | number[][], index: number, defaultValue?: number | boolean): Pt;
395
412
  static zip(g: GroupLike | number[][], defaultValue?: number | boolean, useLongest?: boolean): Group;
396
413
  static transpose(g: GroupLike | number[][], defaultValue?: number | boolean, useLongest?: boolean): Group;
414
+ static toDOMMatrix(m: GroupLike | number[][]): number[];
397
415
  static transform2D(pt: PtLike, m: GroupLike | number[][]): Pt;
398
416
  static scale2DMatrix(x: number, y: number): GroupLike;
399
417
  static rotate2DMatrix(cosA: number, sinA: number): GroupLike;
@@ -879,6 +897,7 @@ export class Group extends Array<Pt> {
879
897
  $matrixMultiply(g: GroupLike | number, transposed?: boolean, elementwise?: boolean): Group;
880
898
  zipSlice(index: number, defaultValue?: number | boolean): Pt;
881
899
  $zip(defaultValue?: number | boolean, useLongest?: boolean): Group;
900
+ toBound(): Group;
882
901
  toString(): string;
883
902
  }
884
903
  export class Bound extends Group implements IPt {
@@ -928,7 +947,10 @@ export abstract class Space {
928
947
  protected _pointer: Pt;
929
948
  protected _isReady: boolean;
930
949
  protected _playing: boolean;
950
+ protected _keyDownBind: (evt: KeyboardEvent) => boolean;
951
+ protected _keyUpBind: (evt: KeyboardEvent) => boolean;
931
952
  refresh(b: boolean): this;
953
+ minFrameTime(ms?: number): void;
932
954
  add(p: IPlayer | AnimateCallbackFn): this;
933
955
  remove(player: IPlayer): this;
934
956
  removeAll(): this;
@@ -958,12 +980,16 @@ export abstract class MultiTouchSpace extends Space {
958
980
  protected _dragged: boolean;
959
981
  protected _hasMouse: boolean;
960
982
  protected _hasTouch: boolean;
983
+ protected _hasKeyboard: boolean;
961
984
  protected _canvas: EventTarget;
962
985
  get pointer(): Pt;
963
- bindCanvas(evt: string, callback: EventListener, options?: any): void;
964
- unbindCanvas(evt: string, callback: EventListener, options?: any): void;
965
- bindMouse(_bind?: boolean): this;
966
- bindTouch(bind?: boolean, passive?: boolean): this;
986
+ bindCanvas(evt: string, callback: EventListener, options?: any, customTarget?: Element): void;
987
+ unbindCanvas(evt: string, callback: EventListener, options?: any, customTarget?: Element): void;
988
+ bindDoc(evt: string, callback: EventListener, options?: any): void;
989
+ unbindDoc(evt: string, callback: EventListener, options?: any): void;
990
+ bindMouse(bind?: boolean, customTarget?: Element): this;
991
+ bindTouch(bind?: boolean, passive?: boolean, customTarget?: Element): this;
992
+ bindKeyboard(bind?: boolean): this;
967
993
  touchesToPoints(evt: TouchEvent, which?: TouchPointsKey): Pt[];
968
994
  protected _mouseAction(type: string, evt: MouseEvent | TouchEvent): void;
969
995
  protected _mouseDown(evt: MouseEvent | TouchEvent): boolean;
@@ -975,6 +1001,9 @@ export abstract class MultiTouchSpace extends Space {
975
1001
  protected _contextMenu(evt: MouseEvent): boolean;
976
1002
  protected _touchMove(evt: TouchEvent): boolean;
977
1003
  protected _touchStart(evt: TouchEvent): boolean;
1004
+ protected _keyDown(evt: KeyboardEvent): boolean;
1005
+ protected _keyUp(evt: KeyboardEvent): boolean;
1006
+ protected _keyboardAction(type: string, evt: KeyboardEvent): void;
978
1007
  }
979
1008
 
980
1009
  export class SVGSpace extends DOMSpace {
@@ -1067,6 +1096,7 @@ export interface ITimer {
1067
1096
  prev: number;
1068
1097
  diff: number;
1069
1098
  end: number;
1099
+ min: number;
1070
1100
  }
1071
1101
  export type TouchPointsKey = "touches" | "changedTouches" | "targetTouches";
1072
1102
  export interface MultiTouchElement {
@@ -1146,6 +1176,7 @@ export type DefaultFormStyle = {
1146
1176
  lineCap?: string;
1147
1177
  globalAlpha?: number;
1148
1178
  };
1179
+ export type CanvasPatternRepetition = "repeat" | "repeat-x" | "repeat-y" | "no-repeat";
1149
1180
 
1150
1181
  export class Typography {
1151
1182
  static textWidthEstimator(fn: (string: any) => number, samples?: string[], distribution?: number[]): (string: any) => number;
@@ -1178,6 +1209,8 @@ export const UIPointerActions: {
1178
1209
  enter: string;
1179
1210
  leave: string;
1180
1211
  click: string;
1212
+ keydown: string;
1213
+ keyup: string;
1181
1214
  contextmenu: string;
1182
1215
  all: string;
1183
1216
  };
@@ -1286,6 +1319,7 @@ export class Util {
1286
1319
  static stepper(max: number, min?: number, stride?: number, callback?: (n: number) => void): (() => number);
1287
1320
  static forRange(fn: (index: number) => any, range: number, start?: number, step?: number): any[];
1288
1321
  static load(url: string, callback: (response: string, success: boolean) => void): void;
1322
+ static download(space: CanvasSpace, filename?: string, filetype?: ("jpeg" | "jpg" | "png" | "webp"), quality?: number): void;
1289
1323
  static performance(avgFrames?: number): () => number;
1290
1324
  static arrayCheck(pts: PtLikeIterable, minRequired?: number): boolean;
1291
1325
  static iterToArray(it: Iterable<any>): any[];
package/dist/pts.js CHANGED
@@ -1,5 +1,5 @@
1
1
  /*!
2
- * pts.js 0.10.9 - Copyright © 2017-2021 William Ngan and contributors.
2
+ * pts.js 0.10.10 - Copyright © 2017-2022 William Ngan and contributors.
3
3
  * Licensed under Apache 2.0 License.
4
4
  * See https://github.com/williamngan/pts for details.
5
5
  */
@@ -2016,29 +2016,62 @@ exports.Font = Font;
2016
2016
 
2017
2017
  "use strict";
2018
2018
 
2019
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
2020
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
2021
+ return new (P || (P = Promise))(function (resolve, reject) {
2022
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
2023
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
2024
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
2025
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
2026
+ });
2027
+ };
2019
2028
  Object.defineProperty(exports, "__esModule", { value: true });
2020
2029
  exports.Img = void 0;
2030
+ const Canvas_1 = __webpack_require__(/*! ./Canvas */ "./src/Canvas.ts");
2021
2031
  const Pt_1 = __webpack_require__(/*! ./Pt */ "./src/Pt.ts");
2032
+ const LinearAlgebra_1 = __webpack_require__(/*! ./LinearAlgebra */ "./src/LinearAlgebra.ts");
2022
2033
  class Img {
2023
- constructor(editable = false, pixelScale = 1, crossOrigin) {
2034
+ constructor(editable = false, space, crossOrigin) {
2024
2035
  this._scale = 1;
2025
2036
  this._loaded = false;
2026
2037
  this._editable = editable;
2027
- this._scale = pixelScale;
2038
+ this._space = space;
2039
+ this._scale = this._space ? this._space.pixelScale : 1;
2028
2040
  this._img = new Image();
2029
2041
  if (crossOrigin)
2030
2042
  this._img.crossOrigin = "Anonymous";
2031
2043
  }
2032
- static load(src, editable = false, pixelScale = 1, ready) {
2033
- let img = new Img(editable, pixelScale);
2044
+ static load(src, editable = false, space, ready) {
2045
+ const img = new Img(editable, space);
2034
2046
  img.load(src).then(res => {
2035
2047
  if (ready)
2036
2048
  ready(res);
2037
2049
  });
2038
2050
  return img;
2039
2051
  }
2052
+ static loadAsync(src, editable = false, space) {
2053
+ return __awaiter(this, void 0, void 0, function* () {
2054
+ const img = yield new Img(editable, space).load(src);
2055
+ return img;
2056
+ });
2057
+ }
2058
+ static loadPattern(src, space, repeat = 'repeat', editable = false) {
2059
+ return __awaiter(this, void 0, void 0, function* () {
2060
+ const img = yield Img.loadAsync(src, editable, space);
2061
+ return img.pattern(repeat);
2062
+ });
2063
+ }
2064
+ static blank(size, space, scale) {
2065
+ let img = new Img(true, space);
2066
+ const s = scale ? scale : space.pixelScale;
2067
+ img.initCanvas(size[0], size[1], s);
2068
+ return img;
2069
+ }
2040
2070
  load(src) {
2041
2071
  return new Promise((resolve, reject) => {
2072
+ if (this._editable && !document) {
2073
+ reject("Cannot create html canvas element. document not found.");
2074
+ }
2042
2075
  this._img.src = src;
2043
2076
  this._img.onload = () => {
2044
2077
  if (this._editable) {
@@ -2056,20 +2089,35 @@ class Img {
2056
2089
  });
2057
2090
  }
2058
2091
  _drawToScale(canvasScale, img) {
2059
- const cms = (typeof canvasScale === 'number') ? [canvasScale, canvasScale] : canvasScale;
2060
2092
  const nw = img.width;
2061
2093
  const nh = img.height;
2062
- this._cv.width = nw * cms[0];
2063
- this._cv.height = nh * cms[1];
2064
- this._ctx = this._cv.getContext('2d');
2094
+ this.initCanvas(nw, nh, canvasScale);
2065
2095
  if (img)
2066
2096
  this._ctx.drawImage(img, 0, 0, nw, nh, 0, 0, this._cv.width, this._cv.height);
2067
2097
  }
2098
+ initCanvas(width, height, canvasScale = 1) {
2099
+ if (!this._editable) {
2100
+ console.error('Cannot initiate canvas because this Img is not set to be editable');
2101
+ return;
2102
+ }
2103
+ if (!this._cv)
2104
+ this._cv = document.createElement("canvas");
2105
+ const cms = (typeof canvasScale === 'number') ? [canvasScale, canvasScale] : canvasScale;
2106
+ this._cv.width = width * cms[0];
2107
+ this._cv.height = height * cms[1];
2108
+ this._ctx = this._cv.getContext('2d');
2109
+ this._loaded = true;
2110
+ }
2068
2111
  bitmap(size) {
2069
2112
  const w = (size) ? size[0] : this._cv.width;
2070
2113
  const h = (size) ? size[1] : this._cv.height;
2071
2114
  return createImageBitmap(this._cv, 0, 0, w, h);
2072
2115
  }
2116
+ pattern(reptition = 'repeat', dynamic = false) {
2117
+ if (!this._space)
2118
+ throw "Cannot find CanvasSpace ctx to create image pattern";
2119
+ return this._space.ctx.createPattern(dynamic ? this._cv : this._img, reptition);
2120
+ }
2073
2121
  sync() {
2074
2122
  if (this._scale !== 1) {
2075
2123
  this.bitmap().then(b => {
@@ -2119,12 +2167,15 @@ class Img {
2119
2167
  this._img.remove();
2120
2168
  this._data = null;
2121
2169
  }
2122
- static fromBlob(blob, editable = false, pixelScale = 1) {
2170
+ static fromBlob(blob, editable = false, space) {
2123
2171
  let url = URL.createObjectURL(blob);
2124
- return new Img(editable, pixelScale).load(url);
2172
+ return new Img(editable, space).load(url);
2125
2173
  }
2126
2174
  static imageDataToBlob(data) {
2127
- return new Promise(function (resolve) {
2175
+ return new Promise(function (resolve, reject) {
2176
+ if (!document) {
2177
+ reject("Cannot create html canvas element. document not found.");
2178
+ }
2128
2179
  let cv = document.createElement("canvas");
2129
2180
  cv.width = data.width;
2130
2181
  cv.height = data.height;
@@ -2143,6 +2194,12 @@ class Img {
2143
2194
  this._cv.toBlob(blob => resolve(blob));
2144
2195
  });
2145
2196
  }
2197
+ getForm() {
2198
+ if (!this._editable) {
2199
+ console.error("Cannot get a CanvasForm because this Img is not editable");
2200
+ }
2201
+ return this._ctx ? new Canvas_1.CanvasForm(this._ctx) : undefined;
2202
+ }
2146
2203
  get current() {
2147
2204
  return this._editable ? this._cv : this._img;
2148
2205
  }
@@ -2165,11 +2222,20 @@ class Img {
2165
2222
  return this._scale;
2166
2223
  }
2167
2224
  get imageSize() {
2168
- return new Pt_1.Pt(this._img.width, this._img.height);
2225
+ if (!this._img.width || !this._img.height) {
2226
+ return this.canvasSize.$divide(this._scale);
2227
+ }
2228
+ else {
2229
+ return new Pt_1.Pt(this._img.width, this._img.height);
2230
+ }
2169
2231
  }
2170
2232
  get canvasSize() {
2171
2233
  return new Pt_1.Pt(this._cv.width, this._cv.height);
2172
2234
  }
2235
+ get scaledMatrix() {
2236
+ const s = 1 / this._scale;
2237
+ return new LinearAlgebra_1.Mat().scale2D([s, s]);
2238
+ }
2173
2239
  }
2174
2240
  exports.Img = Img;
2175
2241
 
@@ -2314,6 +2380,38 @@ class Vec {
2314
2380
  }
2315
2381
  exports.Vec = Vec;
2316
2382
  class Mat {
2383
+ constructor() {
2384
+ this.reset();
2385
+ }
2386
+ get value() {
2387
+ return this._33;
2388
+ }
2389
+ get domMatrix() {
2390
+ return new DOMMatrix(Mat.toDOMMatrix(this._33));
2391
+ }
2392
+ reset() {
2393
+ this._33 = Mat.scale2DMatrix(1, 1);
2394
+ }
2395
+ scale2D(val, at = [0, 0]) {
2396
+ const m = Mat.scaleAt2DMatrix(val[0] || 1, val[1] || 1, at);
2397
+ this._33 = Mat.multiply(this._33, m);
2398
+ return this;
2399
+ }
2400
+ rotate2D(ang, at = [0, 0]) {
2401
+ const m = Mat.rotateAt2DMatrix(Math.cos(ang), Math.sin(ang), at);
2402
+ this._33 = Mat.multiply(this._33, m);
2403
+ return this;
2404
+ }
2405
+ translate2D(val) {
2406
+ const m = Mat.translate2DMatrix(val[0] || 0, val[1] || 0);
2407
+ this._33 = Mat.multiply(this._33, m);
2408
+ return this;
2409
+ }
2410
+ shear2D(val, at = [0, 0]) {
2411
+ const m = Mat.shearAt2DMatrix(Math.tan(val[0] || 0), Math.tan(val[1] || 1), at);
2412
+ this._33 = Mat.multiply(this._33, m);
2413
+ return this;
2414
+ }
2317
2415
  static add(a, b) {
2318
2416
  if (typeof b != "number") {
2319
2417
  if (a[0].length != b[0].length)
@@ -2381,6 +2479,9 @@ class Mat {
2381
2479
  static transpose(g, defaultValue = false, useLongest = false) {
2382
2480
  return Mat.zip(g, defaultValue, useLongest);
2383
2481
  }
2482
+ static toDOMMatrix(m) {
2483
+ return [m[0][0], m[0][1], m[1][0], m[1][1], m[2][0], m[2][1]];
2484
+ }
2384
2485
  static transform2D(pt, m) {
2385
2486
  let x = pt[0] * m[0][0] + pt[1] * m[1][0] + m[2][0];
2386
2487
  let y = pt[0] * m[0][1] + pt[1] * m[1][1] + m[2][1];
@@ -5051,6 +5152,9 @@ class Group extends Array {
5051
5152
  $zip(defaultValue = undefined, useLongest = false) {
5052
5153
  return LinearAlgebra_1.Mat.zip(this, defaultValue, useLongest);
5053
5154
  }
5155
+ toBound() {
5156
+ return Bound.fromGroup(this);
5157
+ }
5054
5158
  toString() {
5055
5159
  return "Group[ " + this.reduce((p, c) => p + c.toString() + " ", "") + " ]";
5056
5160
  }
@@ -5186,7 +5290,7 @@ class Space {
5186
5290
  constructor() {
5187
5291
  this.id = "space";
5188
5292
  this.bound = new Pt_1.Bound();
5189
- this._time = { prev: 0, diff: 0, end: -1 };
5293
+ this._time = { prev: 0, diff: 0, end: -1, min: 0 };
5190
5294
  this.players = {};
5191
5295
  this.playerCount = 0;
5192
5296
  this._animID = -1;
@@ -5200,6 +5304,9 @@ class Space {
5200
5304
  this._refresh = b;
5201
5305
  return this;
5202
5306
  }
5307
+ minFrameTime(ms = 0) {
5308
+ this._time.min = ms;
5309
+ }
5203
5310
  add(p) {
5204
5311
  let player = (typeof p == "function") ? { animate: p } : p;
5205
5312
  let k = this.playerCount++;
@@ -5228,6 +5335,8 @@ class Space {
5228
5335
  if (this._pause)
5229
5336
  return this;
5230
5337
  this._time.diff = time - this._time.prev;
5338
+ if (this._time.diff < this._time.min)
5339
+ return this;
5231
5340
  this._time.prev = time;
5232
5341
  try {
5233
5342
  this.playItems(time);
@@ -5300,20 +5409,33 @@ class MultiTouchSpace extends Space {
5300
5409
  this._dragged = false;
5301
5410
  this._hasMouse = false;
5302
5411
  this._hasTouch = false;
5412
+ this._hasKeyboard = false;
5303
5413
  }
5304
5414
  get pointer() {
5305
5415
  let p = this._pointer.clone();
5306
5416
  p.id = this._pointer.id;
5307
5417
  return p;
5308
5418
  }
5309
- bindCanvas(evt, callback, options = {}) {
5310
- this._canvas.addEventListener(evt, callback, options);
5419
+ bindCanvas(evt, callback, options = {}, customTarget) {
5420
+ const target = customTarget ? customTarget : this._canvas;
5421
+ target.addEventListener(evt, callback, options);
5422
+ }
5423
+ unbindCanvas(evt, callback, options = {}, customTarget) {
5424
+ const target = customTarget ? customTarget : this._canvas;
5425
+ target.removeEventListener(evt, callback, options);
5311
5426
  }
5312
- unbindCanvas(evt, callback, options = {}) {
5313
- this._canvas.removeEventListener(evt, callback, options);
5427
+ bindDoc(evt, callback, options = {}) {
5428
+ if (document) {
5429
+ document.addEventListener(evt, callback, options);
5430
+ }
5431
+ }
5432
+ unbindDoc(evt, callback, options = {}) {
5433
+ if (document) {
5434
+ document.removeEventListener(evt, callback, options);
5435
+ }
5314
5436
  }
5315
- bindMouse(_bind = true) {
5316
- if (_bind) {
5437
+ bindMouse(bind = true, customTarget) {
5438
+ if (bind) {
5317
5439
  this._mouseDown = this._mouseDown.bind(this);
5318
5440
  this._mouseUp = this._mouseUp.bind(this);
5319
5441
  this._mouseOver = this._mouseOver.bind(this);
@@ -5321,44 +5443,59 @@ class MultiTouchSpace extends Space {
5321
5443
  this._mouseMove = this._mouseMove.bind(this);
5322
5444
  this._mouseClick = this._mouseClick.bind(this);
5323
5445
  this._contextMenu = this._contextMenu.bind(this);
5324
- this.bindCanvas("mousedown", this._mouseDown);
5325
- this.bindCanvas("mouseup", this._mouseUp);
5326
- this.bindCanvas("mouseover", this._mouseOver);
5327
- this.bindCanvas("mouseout", this._mouseOut);
5328
- this.bindCanvas("mousemove", this._mouseMove);
5329
- this.bindCanvas("click", this._mouseClick);
5330
- this.bindCanvas("contextmenu", this._contextMenu);
5446
+ this.bindCanvas("mousedown", this._mouseDown, {}, customTarget);
5447
+ this.bindCanvas("mouseup", this._mouseUp, {}, customTarget);
5448
+ this.bindCanvas("mouseover", this._mouseOver, {}, customTarget);
5449
+ this.bindCanvas("mouseout", this._mouseOut, {}, customTarget);
5450
+ this.bindCanvas("mousemove", this._mouseMove, {}, customTarget);
5451
+ this.bindCanvas("click", this._mouseClick, {}, customTarget);
5452
+ this.bindCanvas("contextmenu", this._contextMenu, {}, customTarget);
5331
5453
  this._hasMouse = true;
5332
5454
  }
5333
5455
  else {
5334
- this.unbindCanvas("mousedown", this._mouseDown);
5335
- this.unbindCanvas("mouseup", this._mouseUp);
5336
- this.unbindCanvas("mouseover", this._mouseOver);
5337
- this.unbindCanvas("mouseout", this._mouseOut);
5338
- this.unbindCanvas("mousemove", this._mouseMove);
5339
- this.unbindCanvas("click", this._mouseClick);
5340
- this.unbindCanvas("contextmenu", this._contextMenu);
5456
+ this.unbindCanvas("mousedown", this._mouseDown, {}, customTarget);
5457
+ this.unbindCanvas("mouseup", this._mouseUp, {}, customTarget);
5458
+ this.unbindCanvas("mouseover", this._mouseOver, {}, customTarget);
5459
+ this.unbindCanvas("mouseout", this._mouseOut, {}, customTarget);
5460
+ this.unbindCanvas("mousemove", this._mouseMove, {}, customTarget);
5461
+ this.unbindCanvas("click", this._mouseClick, {}, customTarget);
5462
+ this.unbindCanvas("contextmenu", this._contextMenu, {}, customTarget);
5341
5463
  this._hasMouse = false;
5342
5464
  }
5343
5465
  return this;
5344
5466
  }
5345
- bindTouch(bind = true, passive = false) {
5467
+ bindTouch(bind = true, passive = false, customTarget) {
5346
5468
  if (bind) {
5347
- this.bindCanvas("touchstart", this._touchStart.bind(this), { passive: passive });
5348
- this.bindCanvas("touchend", this._mouseUp.bind(this));
5349
- this.bindCanvas("touchmove", this._touchMove.bind(this), { passive: passive });
5350
- this.bindCanvas("touchcancel", this._mouseOut.bind(this));
5469
+ this.bindCanvas("touchstart", this._touchStart.bind(this), { passive: passive }, customTarget);
5470
+ this.bindCanvas("touchend", this._mouseUp.bind(this), {}, customTarget);
5471
+ this.bindCanvas("touchmove", this._touchMove.bind(this), { passive: passive }, customTarget);
5472
+ this.bindCanvas("touchcancel", this._mouseOut.bind(this), {}, customTarget);
5351
5473
  this._hasTouch = true;
5352
5474
  }
5353
5475
  else {
5354
- this.unbindCanvas("touchstart", this._touchStart.bind(this), { passive: passive });
5355
- this.unbindCanvas("touchend", this._mouseUp.bind(this));
5356
- this.unbindCanvas("touchmove", this._touchMove.bind(this), { passive: passive });
5357
- this.unbindCanvas("touchcancel", this._mouseOut.bind(this));
5476
+ this.unbindCanvas("touchstart", this._touchStart.bind(this), { passive: passive }, customTarget);
5477
+ this.unbindCanvas("touchend", this._mouseUp.bind(this), {}, customTarget);
5478
+ this.unbindCanvas("touchmove", this._touchMove.bind(this), { passive: passive }, customTarget);
5479
+ this.unbindCanvas("touchcancel", this._mouseOut.bind(this), {}, customTarget);
5358
5480
  this._hasTouch = false;
5359
5481
  }
5360
5482
  return this;
5361
5483
  }
5484
+ bindKeyboard(bind = true) {
5485
+ if (bind) {
5486
+ this._keyDownBind = this._keyDown.bind(this);
5487
+ this._keyUpBind = this._keyUp.bind(this);
5488
+ this.bindDoc("keydown", this._keyDownBind, {});
5489
+ this.bindDoc("keyup", this._keyUpBind, {});
5490
+ this._hasKeyboard = true;
5491
+ }
5492
+ else {
5493
+ this.unbindDoc("keydown", this._keyDownBind, {});
5494
+ this.unbindDoc("keyup", this._keyUpBind, {});
5495
+ this._hasKeyboard = false;
5496
+ }
5497
+ return this;
5498
+ }
5362
5499
  touchesToPoints(evt, which = "touches") {
5363
5500
  if (!evt || !evt[which])
5364
5501
  return [];
@@ -5370,6 +5507,8 @@ class MultiTouchSpace extends Space {
5370
5507
  return ts;
5371
5508
  }
5372
5509
  _mouseAction(type, evt) {
5510
+ if (!this.isPlaying)
5511
+ return;
5373
5512
  let px = 0, py = 0;
5374
5513
  if (evt instanceof MouseEvent) {
5375
5514
  for (let k in this.players) {
@@ -5455,6 +5594,25 @@ class MultiTouchSpace extends Space {
5455
5594
  evt.preventDefault();
5456
5595
  return false;
5457
5596
  }
5597
+ _keyDown(evt) {
5598
+ this._keyboardAction(UI_1.UIPointerActions.keydown, evt);
5599
+ return false;
5600
+ }
5601
+ _keyUp(evt) {
5602
+ this._keyboardAction(UI_1.UIPointerActions.keyup, evt);
5603
+ return false;
5604
+ }
5605
+ _keyboardAction(type, evt) {
5606
+ if (!this.isPlaying)
5607
+ return;
5608
+ for (let k in this.players) {
5609
+ if (this.players.hasOwnProperty(k)) {
5610
+ let v = this.players[k];
5611
+ if (v.action)
5612
+ v.action(type, evt.shiftKey ? 1 : 0, evt.altKey ? 1 : 0, evt);
5613
+ }
5614
+ }
5615
+ }
5458
5616
  }
5459
5617
  exports.MultiTouchSpace = MultiTouchSpace;
5460
5618
 
@@ -5932,7 +6090,11 @@ exports.UIShape = {
5932
6090
  rectangle: "rectangle", circle: "circle", polygon: "polygon", polyline: "polyline", line: "line"
5933
6091
  };
5934
6092
  exports.UIPointerActions = {
5935
- up: "up", down: "down", move: "move", drag: "drag", uidrag: "uidrag", drop: "drop", uidrop: "uidrop", over: "over", out: "out", enter: "enter", leave: "leave", click: "click", contextmenu: "contextmenu", all: "all"
6093
+ up: "up", down: "down", move: "move",
6094
+ drag: "drag", uidrag: "uidrag", drop: "drop", uidrop: "uidrop",
6095
+ over: "over", out: "out", enter: "enter", leave: "leave", click: "click",
6096
+ keydown: 'keydown', keyup: 'keyup',
6097
+ contextmenu: "contextmenu", all: "all"
5936
6098
  };
5937
6099
  class UI {
5938
6100
  constructor(group, shape, states = {}, id) {
@@ -6370,6 +6532,19 @@ class Util {
6370
6532
  };
6371
6533
  request.send();
6372
6534
  }
6535
+ static download(space, filename = 'pts_canvas_image', filetype = "png", quality = 1) {
6536
+ const ftype = filetype === 'jpg' ? 'jpeg' : filetype;
6537
+ space.element.toBlob(function (blob) {
6538
+ const link = document.createElement('a');
6539
+ const url = URL.createObjectURL(blob);
6540
+ link.href = url;
6541
+ link.download = `${filename}.${filetype}`;
6542
+ document.body.appendChild(link);
6543
+ link.click();
6544
+ document.body.removeChild(link);
6545
+ URL.revokeObjectURL(url);
6546
+ }, `image/${ftype}`, quality);
6547
+ }
6373
6548
  static performance(avgFrames = 10) {
6374
6549
  let last = Date.now();
6375
6550
  let avg = [];