maptastic 2.0.2 → 3.1.0

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/src/maptastic.js CHANGED
@@ -1,301 +1,495 @@
1
- import { solve } from 'numeric';
1
+ import solve from './solve';
2
+
3
+ /**
4
+ * @typedef {Object} Config
5
+ * @property {boolean} labels
6
+ * @property {boolean} crosshairs
7
+ * @property {boolean} screenbounds
8
+ * @property {boolean} autoSave
9
+ * @property {Layout[]} layers
10
+ * @property {function(): void} onchange
11
+ */
12
+
13
+ /**
14
+ * @typedef {(string|HTMLElement)} Target
15
+ */
16
+
17
+ /**
18
+ * @typedef {[number, number]} Point
19
+ */
20
+
21
+ /**
22
+ * @typedef {Object} Layout
23
+ * @property {string} id - ID of the layer element
24
+ * @property {Point[]} targetPoints
25
+ * @property {Point[]} sourcePoints
26
+ */
27
+
28
+ /**
29
+ * @typedef {Object} Layer
30
+ * @property {Point[]} targetPoints
31
+ * @property {Point[]} sourcePoints
32
+ * @property {boolean} visible
33
+ * @property {HTMLElement} element
34
+ * @property {number} width
35
+ * @property {number} height
36
+ */
37
+
38
+ const has = Object.prototype.hasOwnProperty;
39
+
40
+ /**
41
+ * Get an object's property or a default if not defined.
42
+ *
43
+ * @template T
44
+ * @param {*} obj
45
+ * @param {string} key
46
+ * @param {T} defaultVal
47
+ * @returns {T | *} either the default value or the object's property
48
+ */
49
+ function getProp(obj, key, defaultVal) {
50
+ if (obj && has.call(obj, key) && (obj[key] !== null)) {
51
+ return obj[key];
52
+ } else {
53
+ return defaultVal;
54
+ }
55
+ }
2
56
 
3
57
 
4
- function Maptastic(config) {
58
+ /**
59
+ * Compute linear distance.
60
+ *
61
+ * @param {number} x1
62
+ * @param {number} y1
63
+ * @param {number} x2
64
+ * @param {number} y2
65
+ * @returns {number}
66
+ */
67
+ function distanceTo(x1, y1, x2, y2) {
68
+ return Math.sqrt(Math.pow(x2 - x1, 2) + Math.pow(y2 - y1, 2));
69
+ }
5
70
 
6
- var getProp = function (cfg, key, defaultVal) {
7
- if (cfg && cfg.hasOwnProperty(key) && (cfg[key] !== null)) {
8
- return cfg[key];
9
- } else {
10
- return defaultVal;
11
- }
12
- };
13
-
14
- var showLayerNames = getProp(config, 'labels', true);
15
- var showCrosshairs = getProp(config, 'crosshairs', false);
16
- var showScreenBounds = getProp(config, 'screenbounds', false);
17
- var autoSave = getProp(config, 'autoSave', true);
18
- var autoLoad = getProp(config, 'autoLoad', true);
19
- var layerList = getProp(config, 'layers', []);
20
- var layoutChangeListener = getProp(config, 'onchange', function () {
21
- });
22
- var localStorageKey = 'maptastic.layers';
23
-
24
- var canvas = null;
25
- var context = null;
26
-
27
- var layers = [];
28
-
29
- var configActive = false;
30
-
31
- var dragging = false;
32
- var dragOffset = [];
33
-
34
- var selectedLayer = null;
35
- var selectedPoint = null;
36
- var selectionRadius = 20;
37
- var hoveringPoint = null;
38
- var hoveringLayer = null;
39
- var dragOperation = 'move';
40
- var isLayerSoloed = false;
41
-
42
- var mousePosition = [];
43
- var mouseDelta = [];
44
- var mouseDownPoint = [];
45
-
46
- // Compute linear distance.
47
- var distanceTo = function (x1, y1, x2, y2) {
48
- return Math.sqrt(Math.pow(x2 - x1, 2) + Math.pow(y2 - y1, 2));
49
- };
50
-
51
- var pointInTriangle = function (point, a, b, c) {
52
- var s = a[1] * c[0] - a[0] * c[1] + (c[1] - a[1]) * point[0] + (a[0] - c[0]) * point[1];
53
- var t = a[0] * b[1] - a[1] * b[0] + (a[1] - b[1]) * point[0] + (b[0] - a[0]) * point[1];
54
-
55
- if ((s < 0) != (t < 0)) {
56
- return false;
71
+ /**
72
+ * Determine whether a {@link Point} falls within a triangle, edges included.
73
+ *
74
+ * @param {Point} point
75
+ * @param {Point} a
76
+ * @param {Point} b
77
+ * @param {Point} c
78
+ * @returns {boolean}
79
+ */
80
+ function pointInTriangle(point, a, b, c) {
81
+ let s = a[1] * c[0] - a[0] * c[1] + (c[1] - a[1]) * point[0] + (a[0] - c[0]) * point[1];
82
+ let t = a[0] * b[1] - a[1] * b[0] + (a[1] - b[1]) * point[0] + (b[0] - a[0]) * point[1];
83
+ let A = -b[1] * c[0] + a[1] * (c[0] - b[0]) + a[0] * (b[1] - c[1]) + b[0] * c[1];
84
+
85
+ if (A == 0) {
86
+ return false; // degenerate triangle
87
+ }
88
+
89
+ // normalize the winding so the bounds below hold for either orientation
90
+ if (A < 0) {
91
+ s = -s;
92
+ t = -t;
93
+ A = -A;
94
+ }
95
+
96
+ // inclusive, so the diagonal shared by a layer's two triangles counts as inside
97
+ return s >= 0 && t >= 0 && (s + t) <= A;
98
+ }
99
+
100
+ /**
101
+ * Swap two points in a given list of points. Mutates `layerPoints`.
102
+ *
103
+ * @param {Point[]} layerPoints
104
+ * @param {number} index1
105
+ * @param {number} index2
106
+ */
107
+ function swapLayerPoints(layerPoints, index1, index2) {
108
+ const tx = layerPoints[index1][0];
109
+ const ty = layerPoints[index1][1];
110
+ layerPoints[index1][0] = layerPoints[index2][0];
111
+ layerPoints[index1][1] = layerPoints[index2][1];
112
+ layerPoints[index2][0] = tx;
113
+ layerPoints[index2][1] = ty;
114
+ }
115
+
116
+ /**
117
+ * Determine if a point is inside a layer quad.
118
+ *
119
+ * @param {Point} point
120
+ * @param {Layer} layer
121
+ */
122
+ function pointInLayer(point, layer) {
123
+ const a = pointInTriangle(point, layer.targetPoints[0], layer.targetPoints[1], layer.targetPoints[2]);
124
+ const b = pointInTriangle(point, layer.targetPoints[3], layer.targetPoints[0], layer.targetPoints[2]);
125
+ return a || b;
126
+ }
127
+
128
+ /**
129
+ * Rotate a {@link Layout}. Mutates `layer`.
130
+ *
131
+ * @param {Layer} layer
132
+ * @param {number} angle
133
+ */
134
+ function rotateLayer(layer, angle) {
135
+ const s = Math.sin(angle);
136
+ const c = Math.cos(angle);
137
+
138
+ const centerPoint = [0, 0];
139
+ for (let p = 0; p < layer.targetPoints.length; p++) {
140
+ centerPoint[0] += layer.targetPoints[p][0];
141
+ centerPoint[1] += layer.targetPoints[p][1];
142
+ }
143
+
144
+ centerPoint[0] /= 4;
145
+ centerPoint[1] /= 4;
146
+
147
+ for (let p = 0; p < layer.targetPoints.length; p++) {
148
+ const px = layer.targetPoints[p][0] - centerPoint[0];
149
+ const py = layer.targetPoints[p][1] - centerPoint[1];
150
+
151
+ layer.targetPoints[p][0] = (px * c) - (py * s) + centerPoint[0];
152
+ layer.targetPoints[p][1] = (px * s) + (py * c) + centerPoint[1];
153
+ }
154
+ }
155
+
156
+ /**
157
+ * Scale a {@link Layer}. Mutates `layer`.
158
+ *
159
+ * @param {Layer} layer
160
+ * @param {number} scale
161
+ */
162
+ function scaleLayer(layer, scale) {
163
+ const centerPoint = [0, 0];
164
+ for (let p = 0; p < layer.targetPoints.length; p++) {
165
+ centerPoint[0] += layer.targetPoints[p][0];
166
+ centerPoint[1] += layer.targetPoints[p][1];
167
+ }
168
+
169
+ centerPoint[0] /= 4;
170
+ centerPoint[1] /= 4;
171
+
172
+ for (let p = 0; p < layer.targetPoints.length; p++) {
173
+ let px = layer.targetPoints[p][0] - centerPoint[0];
174
+ let py = layer.targetPoints[p][1] - centerPoint[1];
175
+
176
+ layer.targetPoints[p][0] = (px * scale) + centerPoint[0];
177
+ layer.targetPoints[p][1] = (py * scale) + centerPoint[1];
178
+ }
179
+ }
180
+
181
+ /**
182
+ * Clone a list of points.
183
+ *
184
+ * @param {Point[]} points
185
+ * @returns {Point[]} a new list.
186
+ */
187
+ function clonePoints(points) {
188
+ const clone = [];
189
+ for (let p = 0; p < points.length; p++) {
190
+ clone.push(points[p].slice(0, 2));
191
+ }
192
+ return clone;
193
+ }
194
+
195
+ class Maptastic {
196
+ /**
197
+ * @param {Config|Target} configOrTarget
198
+ * @param {...Target} [targets]
199
+ */
200
+ constructor(configOrTarget, ...targets) {
201
+ /** @type {HTMLCanvasElement} */
202
+ this.canvas = null;
203
+ /** @type {CanvasRenderingContext2D} */
204
+ this.context = null;
205
+
206
+ /** @type {Layer[]} */
207
+ this.layers = [];
208
+
209
+ this.configActive = false;
210
+
211
+ this.dragging = false;
212
+ /** @type {Point} */
213
+ this.dragOffset = [0, 0];
214
+
215
+ this.selectedLayer = null;
216
+ this.selectedPoint = null;
217
+ this.selectionRadius = 20;
218
+ this.hoveringPoint = null;
219
+ this.hoveringLayer = null;
220
+ this.dragOperation = 'move';
221
+ this.isLayerSoloed = false;
222
+
223
+ /** @type {Point} */
224
+ this.mousePosition = [0, 0];
225
+ /** @type {Point} */
226
+ this.mouseDelta = [0, 0];
227
+ /** @type {Point} */
228
+ this.mouseDownPoint = [];
229
+
230
+ this.showLayerNames = getProp(configOrTarget, 'labels', true);
231
+ this.showCrosshairs = getProp(configOrTarget, 'crosshairs', false);
232
+ this.showScreenBounds = getProp(configOrTarget, 'screenbounds', false);
233
+ this.autoSave = getProp(configOrTarget, 'autoSave', true);
234
+ this.layoutChangeListener = getProp(configOrTarget, 'onchange', () => { });
235
+ this.localStorageKey = 'maptastic.layers';
236
+
237
+ const layerList = getProp(configOrTarget, 'layers', []);
238
+ // if the config was just an element or string, interpret it as a layer to add.
239
+ const potentialLayerTargets = layerList.concat(targets, [configOrTarget]);
240
+
241
+ for (var i = 0; i < potentialLayerTargets.length; i++) {
242
+ if ((potentialLayerTargets[i] instanceof HTMLElement) || (typeof (potentialLayerTargets[i]) === 'string')) {
243
+ this.addLayer(potentialLayerTargets[i]);
244
+ }
57
245
  }
58
246
 
59
- var A = -b[1] * c[0] + a[1] * (c[0] - b[0]) + a[0] * (b[1] - c[1]) + b[0] * c[1];
60
- if (A < 0.0) {
61
- s = -s;
62
- t = -t;
63
- A = -A;
247
+ const autoLoad = getProp(configOrTarget, 'autoLoad', true);
248
+ if (autoLoad) {
249
+ this.loadSettings();
64
250
  }
65
251
 
66
- return s > 0 && t > 0 && (s + t) < A;
67
- };
252
+ this.init();
253
+ }
254
+
255
+ /**
256
+ * Set up event listeners, canvas, and context.
257
+ *
258
+ * @private
259
+ */
260
+ init() {
261
+ const canvas = document.createElement('canvas');
68
262
 
69
- // determine if a point is inside a layer quad.
70
- var pointInLayer = function (point, layer) {
71
- var a = pointInTriangle(point, layer.targetPoints[0], layer.targetPoints[1], layer.targetPoints[2]);
72
- var b = pointInTriangle(point, layer.targetPoints[3], layer.targetPoints[0], layer.targetPoints[2]);
73
- return a || b;
74
- };
263
+ canvas.style.display = 'none';
264
+ canvas.style.position = 'fixed';
265
+ canvas.style.top = '0px';
266
+ canvas.style.left = '0px';
267
+ canvas.style.zIndex = '1000000';
268
+ this.canvas = canvas;
269
+
270
+ this.context = canvas.getContext('2d');
271
+
272
+ document.body.appendChild(canvas);
273
+
274
+ window.addEventListener('resize', (event) => this.resize());
75
275
 
76
- var notifyChangeListener = function () {
77
- layoutChangeListener();
78
- };
276
+ // UI events
277
+ window.addEventListener('mousemove', (event) => this.mouseMove(event));
278
+ window.addEventListener('mouseup', (event) => this.mouseUp(event));
279
+ window.addEventListener('mousedown', (event) => this.mouseDown(event));
280
+ window.addEventListener('keydown', (event) => this.keyDown(event));
281
+
282
+ this.resize();
283
+ }
284
+
285
+ /**
286
+ * @private
287
+ */
288
+ notifyChangeListener() {
289
+ this.layoutChangeListener();
290
+ }
79
291
 
80
- var draw = function () {
81
- if (!configActive) {
292
+ /**
293
+ * @private
294
+ */
295
+ resize() {
296
+ const { innerWidth, innerHeight } = window;
297
+ this.canvas.width = innerWidth;
298
+ this.canvas.height = innerHeight;
299
+
300
+ this.draw();
301
+ }
302
+
303
+ /**
304
+ * @private
305
+ */
306
+ draw() {
307
+ if (!this.configActive) {
82
308
  return;
83
309
  }
84
310
 
85
- context.strokeStyle = 'red';
86
- context.lineWidth = 2;
87
- context.clearRect(0, 0, canvas.width, canvas.height);
311
+ this.context.strokeStyle = 'red';
312
+ this.context.lineWidth = 2;
313
+ this.context.clearRect(0, 0, this.canvas.width, this.canvas.height);
88
314
 
89
- for (var i = 0; i < layers.length; i++) {
315
+ for (let i = 0; i < this.layers.length; i++) {
90
316
 
91
- if (layers[i].visible) {
92
- layers[i].element.style.visibility = 'visible';
317
+ if (this.layers[i].visible) {
318
+ this.layers[i].element.style.visibility = 'visible';
93
319
 
94
320
  // Draw layer rectangles.
95
- context.beginPath();
96
- if (layers[i] === hoveringLayer) {
97
- context.strokeStyle = 'red';
98
- } else if (layers[i] === selectedLayer) {
99
- context.strokeStyle = 'red';
321
+ this.context.beginPath();
322
+ if (this.layers[i] === this.hoveringLayer) {
323
+ this.context.strokeStyle = 'red';
324
+ } else if (this.layers[i] === this.selectedLayer) {
325
+ this.context.strokeStyle = 'red';
100
326
  } else {
101
- context.strokeStyle = 'white';
327
+ this.context.strokeStyle = 'white';
102
328
  }
103
- context.moveTo(layers[i].targetPoints[0][0], layers[i].targetPoints[0][1]);
104
- for (var p = 0; p < layers[i].targetPoints.length; p++) {
105
- context.lineTo(layers[i].targetPoints[p][0], layers[i].targetPoints[p][1]);
329
+ this.context.moveTo(this.layers[i].targetPoints[0][0], this.layers[i].targetPoints[0][1]);
330
+ for (let p = 0; p < this.layers[i].targetPoints.length; p++) {
331
+ this.context.lineTo(this.layers[i].targetPoints[p][0], this.layers[i].targetPoints[p][1]);
106
332
  }
107
- context.lineTo(layers[i].targetPoints[3][0], layers[i].targetPoints[3][1]);
108
- context.closePath();
109
- context.stroke();
333
+ this.context.lineTo(this.layers[i].targetPoints[3][0], this.layers[i].targetPoints[3][1]);
334
+ this.context.closePath();
335
+ this.context.stroke();
110
336
 
111
337
  // Draw corner points.
112
- var centerPoint = [0, 0];
113
- for (var p = 0; p < layers[i].targetPoints.length; p++) {
338
+ const centerPoint = [0, 0];
339
+ for (let p = 0; p < this.layers[i].targetPoints.length; p++) {
114
340
 
115
- if (layers[i].targetPoints[p] === hoveringPoint) {
116
- context.strokeStyle = 'red';
117
- } else if (layers[i].targetPoints[p] === selectedPoint) {
118
- context.strokeStyle = 'red';
341
+ if (this.layers[i].targetPoints[p] === this.hoveringPoint) {
342
+ this.context.strokeStyle = 'red';
343
+ } else if (this.layers[i].targetPoints[p] === this.selectedPoint) {
344
+ this.context.strokeStyle = 'red';
119
345
  } else {
120
- context.strokeStyle = 'white';
346
+ this.context.strokeStyle = 'white';
121
347
  }
122
348
 
123
- centerPoint[0] += layers[i].targetPoints[p][0];
124
- centerPoint[1] += layers[i].targetPoints[p][1];
125
-
126
- context.beginPath();
127
- context.arc(layers[i].targetPoints[p][0], layers[i].targetPoints[p][1],
128
- selectionRadius / 2, 0, 2 * Math.PI, false);
129
- context.stroke();
349
+ centerPoint[0] += this.layers[i].targetPoints[p][0];
350
+ centerPoint[1] += this.layers[i].targetPoints[p][1];
351
+
352
+ this.context.beginPath();
353
+ this.context.arc(
354
+ this.layers[i].targetPoints[p][0],
355
+ this.layers[i].targetPoints[p][1],
356
+ this.selectionRadius / 2,
357
+ 0,
358
+ 2 * Math.PI,
359
+ false
360
+ );
361
+ this.context.stroke();
130
362
  }
131
363
 
132
364
  // Find the average of the corner locations for an approximate center.
133
365
  centerPoint[0] /= 4;
134
366
  centerPoint[1] /= 4;
135
367
 
136
-
137
- if (showLayerNames) {
368
+ if (this.showLayerNames) {
138
369
  // Draw the element ID in the center of the quad for reference.
139
- var label = layers[i].element.id.toUpperCase();
140
- context.font = '16px sans-serif';
141
- context.textAlign = 'center';
142
- var metrics = context.measureText(label);
143
- var size = [metrics.width + 8, 16 + 16];
144
- context.fillStyle = 'white';
145
- context.fillRect(centerPoint[0] - size[0] / 2, centerPoint[1] - size[1] + 8, size[0], size[1]);
146
- context.fillStyle = 'black';
147
- context.fillText(label, centerPoint[0], centerPoint[1]);
370
+ const label = this.layers[i].element.id.toUpperCase();
371
+ this.context.font = '16px sans-serif';
372
+ this.context.textAlign = 'center';
373
+ const metrics = this.context.measureText(label);
374
+ const size = [metrics.width + 8, 16 + 16];
375
+ this.context.fillStyle = 'white';
376
+ this.context.fillRect(centerPoint[0] - size[0] / 2, centerPoint[1] - size[1] + 8, size[0], size[1]);
377
+ this.context.fillStyle = 'black';
378
+ this.context.fillText(label, centerPoint[0], centerPoint[1]);
148
379
  }
149
380
  } else {
150
- layers[i].element.style.visibility = 'hidden';
381
+ this.layers[i].element.style.visibility = 'hidden';
151
382
  }
152
383
  }
153
384
 
154
385
  // Draw mouse crosshairs
155
- if (showCrosshairs) {
156
- context.strokeStyle = 'yellow';
157
- context.lineWidth = 1;
386
+ if (this.showCrosshairs) {
387
+ this.context.strokeStyle = 'yellow';
388
+ this.context.lineWidth = 1;
158
389
 
159
- context.beginPath();
390
+ this.context.beginPath();
160
391
 
161
- context.moveTo(mousePosition[0], 0);
162
- context.lineTo(mousePosition[0], canvas.height);
392
+ this.context.moveTo(this.mousePosition[0], 0);
393
+ this.context.lineTo(this.mousePosition[0], this.canvas.height);
163
394
 
164
- context.moveTo(0, mousePosition[1]);
165
- context.lineTo(canvas.width, mousePosition[1]);
395
+ this.context.moveTo(0, this.mousePosition[1]);
396
+ this.context.lineTo(this.canvas.width, this.mousePosition[1]);
166
397
 
167
- context.stroke();
398
+ this.context.stroke();
168
399
  }
169
400
 
170
- if (showScreenBounds) {
401
+ if (this.showScreenBounds) {
171
402
 
172
- context.fillStyle = 'black';
173
- context.lineWidth = 4;
174
- context.fillRect(0, 0, canvas.width, canvas.height);
403
+ this.context.fillStyle = 'black';
404
+ this.context.lineWidth = 4;
405
+ this.context.fillRect(0, 0, this.canvas.width, this.canvas.height);
175
406
 
176
- context.strokeStyle = '#909090';
177
- context.beginPath();
178
- var stepX = canvas.width / 10;
179
- var stepY = canvas.height / 10;
407
+ this.context.strokeStyle = '#909090';
408
+ this.context.beginPath();
409
+ const stepX = this.canvas.width / 10;
410
+ const stepY = this.canvas.height / 10;
180
411
 
181
- for (var i = 0; i < 10; i++) {
182
- context.moveTo(i * stepX, 0);
183
- context.lineTo(i * stepX, canvas.height);
412
+ for (let i = 0; i < 10; i++) {
413
+ this.context.moveTo(i * stepX, 0);
414
+ this.context.lineTo(i * stepX, this.canvas.height);
184
415
 
185
- context.moveTo(0, i * stepY);
186
- context.lineTo(canvas.width, i * stepY);
416
+ this.context.moveTo(0, i * stepY);
417
+ this.context.lineTo(this.canvas.width, i * stepY);
187
418
  }
188
- context.stroke();
189
-
190
- context.strokeStyle = 'white';
191
- context.strokeRect(2, 2, canvas.width - 4, canvas.height - 4);
192
-
193
- var fontSize = Math.round(stepY * 0.6);
194
- context.font = fontSize + 'px mono,sans-serif';
195
- context.fillRect(stepX * 2 + 2, stepY * 3 + 2, canvas.width - stepX * 4 - 4, canvas.height - stepY * 6 - 4);
196
- context.fillStyle = 'white';
197
- context.fontSize = 20;
198
- context.fillText(canvas.width + ' x ' + canvas.height, canvas.width / 2, canvas.height / 2 + (fontSize * 0.75));
199
- context.fillText('display size', canvas.width / 2, canvas.height / 2 - (fontSize * 0.75));
200
- }
201
- };
202
-
203
- var swapLayerPoints = function (layerPoints, index1, index2) {
204
- var tx = layerPoints[index1][0];
205
- var ty = layerPoints[index1][1];
206
- layerPoints[index1][0] = layerPoints[index2][0];
207
- layerPoints[index1][1] = layerPoints[index2][1];
208
- layerPoints[index2][0] = tx;
209
- layerPoints[index2][1] = ty;
210
- };
211
-
212
- var init = function () {
213
- canvas = document.createElement('canvas');
214
-
215
- canvas.style.display = 'none';
216
- canvas.style.position = 'fixed';
217
- canvas.style.top = '0px';
218
- canvas.style.left = '0px';
219
- canvas.style.zIndex = '1000000';
220
-
221
- context = canvas.getContext('2d');
222
-
223
- document.body.appendChild(canvas);
224
-
225
- window.addEventListener('resize', resize);
226
-
227
- // UI events
228
- window.addEventListener('mousemove', mouseMove);
229
- window.addEventListener('mouseup', mouseUp);
230
- window.addEventListener('mousedown', mouseDown);
231
- window.addEventListener('keydown', keyDown);
232
-
233
- resize();
234
- };
235
-
236
- var rotateLayer = function (layer, angle) {
237
- var s = Math.sin(angle);
238
- var c = Math.cos(angle);
239
-
240
- var centerPoint = [0, 0];
241
- for (var p = 0; p < layer.targetPoints.length; p++) {
242
- centerPoint[0] += layer.targetPoints[p][0];
243
- centerPoint[1] += layer.targetPoints[p][1];
244
- }
419
+ this.context.stroke();
245
420
 
246
- centerPoint[0] /= 4;
247
- centerPoint[1] /= 4;
421
+ this.context.strokeStyle = 'white';
422
+ this.context.strokeRect(2, 2, this.canvas.width - 4, this.canvas.height - 4);
248
423
 
249
- for (var p = 0; p < layer.targetPoints.length; p++) {
250
- var px = layer.targetPoints[p][0] - centerPoint[0];
251
- var py = layer.targetPoints[p][1] - centerPoint[1];
252
-
253
- layer.targetPoints[p][0] = (px * c) - (py * s) + centerPoint[0];
254
- layer.targetPoints[p][1] = (px * s) + (py * c) + centerPoint[1];
424
+ const fontSize = Math.round(stepY * 0.6);
425
+ this.context.font = fontSize + 'px mono,sans-serif';
426
+ this.context.fillRect(stepX * 2 + 2, stepY * 3 + 2, this.canvas.width - stepX * 4 - 4, this.canvas.height - stepY * 6 - 4);
427
+ this.context.fillStyle = 'white';
428
+ this.context.fontSize = 20;
429
+ this.context.fillText(this.canvas.width + ' x ' + this.canvas.height, this.canvas.width / 2, this.canvas.height / 2 + (fontSize * 0.75));
430
+ this.context.fillText('display size', this.canvas.width / 2, this.canvas.height / 2 - (fontSize * 0.75));
255
431
  }
256
- };
257
-
258
- var scaleLayer = function (layer, scale) {
432
+ }
259
433
 
260
- var centerPoint = [0, 0];
261
- for (var p = 0; p < layer.targetPoints.length; p++) {
262
- centerPoint[0] += layer.targetPoints[p][0];
263
- centerPoint[1] += layer.targetPoints[p][1];
264
- }
434
+ updateTransform() {
435
+ // TODO: no reduce
436
+ const transform = ['', '-webkit-', '-moz-', '-ms-', '-o-'].reduce((p, v) => {
437
+ return v + 'transform' in document.body.style ? v : p;
438
+ }) + 'transform';
439
+ for (let l = 0; l < this.layers.length; l++) {
265
440
 
266
- centerPoint[0] /= 4;
267
- centerPoint[1] /= 4;
441
+ let a = [];
442
+ let b = [];
443
+ for (let i = 0, n = this.layers[l].sourcePoints.length; i < n; ++i) {
444
+ const s = this.layers[l].sourcePoints[i], t = this.layers[l].targetPoints[i];
445
+ a.push([s[0], s[1], 1, 0, 0, 0, -s[0] * t[0], -s[1] * t[0]]), b.push(t[0]);
446
+ a.push([0, 0, 0, s[0], s[1], 1, -s[0] * t[1], -s[1] * t[1]]), b.push(t[1]);
447
+ }
268
448
 
269
- for (var p = 0; p < layer.targetPoints.length; p++) {
270
- var px = layer.targetPoints[p][0] - centerPoint[0];
271
- var py = layer.targetPoints[p][1] - centerPoint[1];
449
+ // TODO: remove dependency on numeric
450
+ const X = solve(a, b, true);
451
+ const matrix = [
452
+ X[0], X[3], 0, X[6],
453
+ X[1], X[4], 0, X[7],
454
+ 0, 0, 1, 0,
455
+ X[2], X[5], 0, 1
456
+ ];
272
457
 
273
- layer.targetPoints[p][0] = (px * scale) + centerPoint[0];
274
- layer.targetPoints[p][1] = (py * scale) + centerPoint[1];
458
+ this.layers[l].element.style[transform] = 'matrix3d(' + matrix.join(',') + ')';
459
+ this.layers[l].element.style[transform + '-origin'] = '0px 0px 0px';
275
460
  }
276
- };
461
+ }
277
462
 
278
- var keyDown = function (event) {
279
- if (!configActive) {
463
+ /**
464
+ * @private
465
+ * @param {KeyboardEvent} event
466
+ */
467
+ keyDown(event) {
468
+ if (!this.configActive) {
280
469
  if (event.keyCode == 32 && event.shiftKey) {
281
- setConfigEnabled(true);
470
+ this.setConfigEnabled(true);
282
471
  return;
283
472
  } else {
284
473
  return;
285
474
  }
286
475
  }
287
476
 
288
- var key = event.keyCode;
477
+ // leave Cmd/Ctrl shortcuts (copy, paste, ...) to the page: c and v would otherwise toggle crosshairs / flip
478
+ if (event.metaKey || event.ctrlKey) {
479
+ return;
480
+ }
481
+
482
+ const key = event.keyCode;
289
483
 
290
- var increment = event.shiftKey ? 10 : 1;
291
- var dirty = false;
292
- var delta = [0, 0];
484
+ const increment = event.shiftKey ? 10 : 1;
485
+ let dirty = false;
486
+ const delta = [0, 0];
293
487
 
294
488
  switch (key) {
295
489
 
296
490
  case 32: // spacebar
297
491
  if (event.shiftKey) {
298
- setConfigEnabled(false);
492
+ this.setConfigEnabled(false);
299
493
  return;
300
494
  }
301
495
  break;
@@ -317,78 +511,78 @@ function Maptastic(config) {
317
511
  break;
318
512
 
319
513
  case 67: // c key, toggle crosshairs
320
- showCrosshairs = !showCrosshairs;
514
+ this.showCrosshairs = !this.showCrosshairs;
321
515
  dirty = true;
322
516
  break;
323
517
 
324
518
  case 83: // s key, solo/unsolo quads
325
- if (!isLayerSoloed) {
519
+ if (!this.isLayerSoloed) {
326
520
 
327
- if (selectedLayer != null) {
328
- for (var i = 0; i < layers.length; i++) {
329
- layers[i].visible = false;
521
+ if (this.selectedLayer != null) {
522
+ for (let i = 0; i < this.layers.length; i++) {
523
+ this.layers[i].visible = false;
330
524
  }
331
- selectedLayer.visible = true;
525
+ this.selectedLayer.visible = true;
332
526
  dirty = true;
333
- isLayerSoloed = true;
527
+ this.isLayerSoloed = true;
334
528
  }
335
529
  } else {
336
- for (var i = 0; i < layers.length; i++) {
337
- layers[i].visible = true;
530
+ for (let i = 0; i < this.layers.length; i++) {
531
+ this.layers[i].visible = true;
338
532
  }
339
- isLayerSoloed = false;
533
+ this.isLayerSoloed = false;
340
534
  dirty = true;
341
535
 
342
536
  }
343
537
  break;
344
538
 
345
539
  case 66: // b key, toggle projector bounds rectangle.
346
- showScreenBounds = !showScreenBounds;
347
- draw();
540
+ this.showScreenBounds = !this.showScreenBounds;
541
+ this.draw();
348
542
  break;
349
543
 
350
544
  case 72: // h key, flip horizontal.
351
- if (selectedLayer) {
352
- swapLayerPoints(selectedLayer.sourcePoints, 0, 1);
353
- swapLayerPoints(selectedLayer.sourcePoints, 3, 2);
354
- updateTransform();
355
- draw();
545
+ if (this.selectedLayer) {
546
+ swapLayerPoints(this.selectedLayer.sourcePoints, 0, 1);
547
+ swapLayerPoints(this.selectedLayer.sourcePoints, 3, 2);
548
+ this.updateTransform();
549
+ this.draw();
356
550
  }
357
551
  break;
358
552
 
359
553
  case 86: // v key, flip vertical.
360
- if (selectedLayer) {
361
- swapLayerPoints(selectedLayer.sourcePoints, 0, 3);
362
- swapLayerPoints(selectedLayer.sourcePoints, 1, 2);
363
- updateTransform();
364
- draw();
554
+ if (this.selectedLayer) {
555
+ swapLayerPoints(this.selectedLayer.sourcePoints, 0, 3);
556
+ swapLayerPoints(this.selectedLayer.sourcePoints, 1, 2);
557
+ this.updateTransform();
558
+ this.draw();
365
559
  }
366
560
  break;
367
561
 
368
562
  case 82: // r key, rotate 90 degrees.
369
- if (selectedLayer) {
370
- rotateLayer(selectedLayer, Math.PI / 2);
563
+ if (this.selectedLayer) {
564
+ rotateLayer(this.selectedLayer, Math.PI / 2);
371
565
  //rotateLayer(selectedLayer, 0.002);
372
- updateTransform();
373
- draw();
566
+ this.updateTransform();
567
+ this.draw();
374
568
  }
375
569
  break;
376
570
  }
377
571
 
378
572
  // if a layer or point is selected, add the delta amounts (set above via arrow keys)
379
- if (!showScreenBounds) {
380
- if (selectedPoint) {
381
- selectedPoint[0] += delta[0];
382
- selectedPoint[1] += delta[1];
573
+ if (!this.showScreenBounds) {
574
+ if (this.selectedPoint) {
575
+ this.selectedPoint[0] += delta[0];
576
+ this.selectedPoint[1] += delta[1];
383
577
  dirty = true;
384
- } else if (selectedLayer) {
578
+ } else if (this.selectedLayer) {
385
579
  if (event.altKey == true) {
386
- rotateLayer(selectedLayer, delta[0] * 0.01);
387
- scaleLayer(selectedLayer, (delta[1] * -0.005) + 1.0);
580
+ rotateLayer(this.selectedLayer, delta[0] * 0.01);
581
+ scaleLayer(this.selectedLayer, (delta[1] * -0.005) + 1.0);
388
582
  } else {
389
- for (var i = 0; i < selectedLayer.targetPoints.length; i++) {
390
- selectedLayer.targetPoints[i][0] += delta[0];
391
- selectedLayer.targetPoints[i][1] += delta[1];
583
+ for (let i = 0; i < this.selectedLayer.targetPoints.length; i++) {
584
+ this.selectedLayer.targetPoints[i][0] += delta[0];
585
+ this.selectedLayer.targetPoints[i][1] += delta[1];
392
586
  }
393
587
  }
394
588
  dirty = true;
@@ -397,173 +591,189 @@ function Maptastic(config) {
397
591
 
398
592
  // update the transform and redraw if needed
399
593
  if (dirty) {
400
- updateTransform();
401
- draw();
402
- if (autoSave) {
403
- saveSettings();
594
+ this.updateTransform();
595
+ this.draw();
596
+ if (this.autoSave) {
597
+ this.saveSettings();
404
598
  }
405
- notifyChangeListener();
599
+ this.notifyChangeListener();
406
600
  }
407
- };
601
+ }
408
602
 
409
- var mouseMove = function (event) {
410
- if (!configActive) {
603
+ /**
604
+ * @private
605
+ * @param {MouseEvent} event
606
+ */
607
+ mouseMove(event) {
608
+ if (!this.configActive) {
411
609
  return;
412
610
  }
413
611
 
414
612
  event.preventDefault();
415
613
 
416
- mouseDelta[0] = event.clientX - mousePosition[0];
417
- mouseDelta[1] = event.clientY - mousePosition[1];
614
+ this.mouseDelta[0] = event.clientX - this.mousePosition[0];
615
+ this.mouseDelta[1] = event.clientY - this.mousePosition[1];
418
616
 
419
- mousePosition[0] = event.clientX;
420
- mousePosition[1] = event.clientY;
617
+ this.mousePosition[0] = event.clientX;
618
+ this.mousePosition[1] = event.clientY;
421
619
 
422
- if (dragging) {
620
+ if (this.dragging) {
423
621
 
424
- var scale = event.shiftKey ? 0.1 : 1;
622
+ const scale = event.shiftKey ? 0.1 : 1;
425
623
 
426
- if (selectedPoint) {
427
- selectedPoint[0] += mouseDelta[0] * scale;
428
- selectedPoint[1] += mouseDelta[1] * scale;
429
- } else if (selectedLayer) {
624
+ if (this.selectedPoint) {
625
+ this.selectedPoint[0] += this.mouseDelta[0] * scale;
626
+ this.selectedPoint[1] += this.mouseDelta[1] * scale;
627
+ } else if (this.selectedLayer) {
430
628
 
431
629
  // Alt-drag to rotate and scale
432
630
  if (event.altKey == true) {
433
- rotateLayer(selectedLayer, mouseDelta[0] * (0.01 * scale));
434
- scaleLayer(selectedLayer, (mouseDelta[1] * (-0.005 * scale)) + 1.0);
631
+ rotateLayer(this.selectedLayer, this.mouseDelta[0] * (0.01 * scale));
632
+ scaleLayer(this.selectedLayer, (this.mouseDelta[1] * (-0.005 * scale)) + 1.0);
435
633
  } else {
436
- for (var i = 0; i < selectedLayer.targetPoints.length; i++) {
437
- selectedLayer.targetPoints[i][0] += mouseDelta[0] * scale;
438
- selectedLayer.targetPoints[i][1] += mouseDelta[1] * scale;
634
+ for (let i = 0; i < this.selectedLayer.targetPoints.length; i++) {
635
+ this.selectedLayer.targetPoints[i][0] += this.mouseDelta[0] * scale;
636
+ this.selectedLayer.targetPoints[i][1] += this.mouseDelta[1] * scale;
439
637
  }
440
638
  }
441
639
  }
442
640
 
443
- updateTransform();
444
- if (autoSave) {
445
- saveSettings();
641
+ this.updateTransform();
642
+ if (this.autoSave) {
643
+ this.saveSettings();
446
644
  }
447
- draw();
448
- notifyChangeListener();
449
-
645
+ this.draw();
646
+ this.notifyChangeListener();
450
647
  } else {
451
- var dirty = false;
648
+ let dirty = false;
452
649
 
453
- canvas.style.cursor = 'default';
454
- var mouseX = event.clientX;
455
- var mouseY = event.clientY;
650
+ this.canvas.style.cursor = 'default';
651
+ const mouseX = event.clientX;
652
+ const mouseY = event.clientY;
456
653
 
457
- var previousState = (hoveringPoint != null);
458
- var previousLayer = (hoveringLayer != null);
654
+ const previousState = (this.hoveringPoint != null);
655
+ const previousLayer = (this.hoveringLayer != null);
459
656
 
460
- hoveringPoint = null;
657
+ this.hoveringPoint = null;
461
658
 
462
- for (var i = 0; i < layers.length; i++) {
463
- var layer = layers[i];
659
+ for (let i = 0; i < this.layers.length; i++) {
660
+ let layer = this.layers[i];
464
661
  if (layer.visible) {
465
- for (var p = 0; p < layer.targetPoints.length; p++) {
466
- var point = layer.targetPoints[p];
467
- if (distanceTo(point[0], point[1], mouseX, mouseY) < selectionRadius) {
468
- canvas.style.cursor = 'pointer';
469
- hoveringPoint = point;
662
+ for (let p = 0; p < layer.targetPoints.length; p++) {
663
+ let point = layer.targetPoints[p];
664
+ if (distanceTo(point[0], point[1], mouseX, mouseY) < this.selectionRadius) {
665
+ this.canvas.style.cursor = 'pointer';
666
+ this.hoveringPoint = point;
470
667
  break;
471
668
  }
472
669
  }
473
670
  }
474
671
  }
475
672
 
476
- hoveringLayer = null;
477
- for (var i = 0; i < layers.length; i++) {
478
- if (layers[i].visible && pointInLayer(mousePosition, layers[i])) {
479
- hoveringLayer = layers[i];
673
+ // later layers are drawn on top, so search from the end: overlapping layers select the topmost one
674
+ this.hoveringLayer = null;
675
+ for (let i = this.layers.length - 1; i >= 0; i--) {
676
+ if (this.layers[i].visible && pointInLayer(this.mousePosition, this.layers[i])) {
677
+ this.hoveringLayer = this.layers[i];
480
678
  break;
481
679
  }
482
680
  }
483
681
 
484
- if (showCrosshairs ||
485
- (previousState != (hoveringPoint != null)) ||
486
- (previousLayer != (hoveringLayer != null))
682
+ if (this.showCrosshairs ||
683
+ (previousState != (this.hoveringPoint != null)) ||
684
+ (previousLayer != (this.hoveringLayer != null))
487
685
  ) {
488
- draw();
686
+ this.draw();
489
687
  }
490
688
  }
491
- };
689
+ }
492
690
 
493
- var mouseUp = function (event) {
494
- if (!configActive) {
691
+ /**
692
+ * @private
693
+ * @param {MouseEvent} event
694
+ */
695
+ mouseUp(event) {
696
+ if (!this.configActive) {
495
697
  return;
496
698
  }
497
699
  event.preventDefault();
498
700
 
499
- dragging = false;
500
- };
701
+ this.dragging = false;
702
+ }
501
703
 
502
- var mouseDown = function (event) {
503
- if (!configActive || showScreenBounds) {
704
+ /**
705
+ * @private
706
+ * @param {MouseEvent} event
707
+ */
708
+ mouseDown(event) {
709
+ if (!this.configActive || this.showScreenBounds) {
504
710
  return;
505
711
  }
506
712
  event.preventDefault();
507
713
 
508
- hoveringPoint = null;
714
+ this.hoveringPoint = null;
509
715
 
510
- if (hoveringLayer) {
511
- selectedLayer = hoveringLayer;
512
- dragging = true;
716
+ if (this.hoveringLayer) {
717
+ this.selectedLayer = this.hoveringLayer;
718
+ this.dragging = true;
513
719
  } else {
514
- selectedLayer = null;
720
+ this.selectedLayer = null;
515
721
  }
516
722
 
517
- selectedPoint = null;
518
-
519
- var mouseX = event.clientX;
520
- var mouseY = event.clientY;
521
-
522
- mouseDownPoint[0] = mouseX;
523
- mouseDownPoint[1] = mouseY;
524
-
525
- for (var i = 0; i < layers.length; i++) {
526
- var layer = layers[i];
527
- for (var p = 0; p < layer.targetPoints.length; p++) {
528
- var point = layer.targetPoints[p];
529
- if (distanceTo(point[0], point[1], mouseX, mouseY) < selectionRadius) {
530
- selectedLayer = layer;
531
- selectedPoint = point;
532
- dragging = true;
533
- dragOffset[0] = event.clientX - point[0];
534
- dragOffset[1] = event.clientY - point[1];
535
- //draw();
723
+ this.selectedPoint = null;
724
+
725
+ const mouseX = event.clientX;
726
+ const mouseY = event.clientY;
727
+
728
+ this.mouseDownPoint[0] = mouseX;
729
+ this.mouseDownPoint[1] = mouseY;
730
+
731
+ for (let i = 0; i < this.layers.length; i++) {
732
+ const layer = this.layers[i];
733
+ for (let p = 0; p < layer.targetPoints.length; p++) {
734
+ const point = layer.targetPoints[p];
735
+ if (distanceTo(point[0], point[1], mouseX, mouseY) < this.selectionRadius) {
736
+ this.selectedLayer = layer;
737
+ this.selectedPoint = point;
738
+ this.dragging = true;
739
+ this.dragOffset[0] = event.clientX - point[0];
740
+ this.dragOffset[1] = event.clientY - point[1];
741
+ //this.draw();
536
742
  break;
537
743
  }
538
744
  }
539
745
  }
540
- draw();
746
+ this.draw();
541
747
  return false;
542
- };
543
-
544
- var addLayer = function (target, targetPoints) {
545
-
546
- var element;
748
+ }
547
749
 
750
+ /**
751
+ * Add a {@link Layer} to an HTMLElement, either by id or by reference.
752
+ *
753
+ * @private
754
+ * @param {string|HTMLElement} target
755
+ * @param {Point[]} [targetPoints] initial points for the layer
756
+ */
757
+ addLayer(target, targetPoints) {
758
+ /** @type {HTMLElement} */
759
+ let element;
548
760
  if (typeof (target) == 'string') {
549
761
  element = document.getElementById(target);
550
762
  if (!element) {
551
- throw('Maptastic: No element found with id: ' + target);
763
+ throw new Error('Maptastic: No element found with id: ' + target);
552
764
  }
553
765
  } else if (target instanceof HTMLElement) {
554
766
  element = target;
555
767
  }
556
768
 
557
- var exists = false;
558
- for (var n = 0; n < layers.length; n++) {
559
- if (layers[n].element.id == element.id) {
560
- layers[n].targetPoints = clonePoints(layout[i].targetPoints);
561
- exists = true;
769
+ for (let n = 0; n < this.layers.length; n++) {
770
+ if (this.layers[n].element.id == element.id) {
771
+ throw new Error('Maptastic: Layer already added: ' + element.id);
562
772
  }
563
773
  }
564
774
 
565
- var offsetX = element.offsetLeft;
566
- var offsetY = element.offsetTop;
775
+ const offsetX = element.offsetLeft;
776
+ const offsetY = element.offsetTop;
567
777
 
568
778
  element.style.position = 'fixed';
569
779
  element.style.display = 'block';
@@ -572,7 +782,7 @@ function Maptastic(config) {
572
782
  element.style.padding = '0px';
573
783
  element.style.margin = '0px';
574
784
 
575
- var layer = {
785
+ const layer = {
576
786
  'visible': true,
577
787
  'element': element,
578
788
  'width': element.clientWidth,
@@ -592,113 +802,118 @@ function Maptastic(config) {
592
802
  }
593
803
  }
594
804
 
595
- layers.push(layer);
805
+ this.layers.push(layer);
596
806
 
597
- updateTransform();
598
- };
807
+ this.updateTransform();
808
+ }
599
809
 
600
- var saveSettings = function () {
601
- localStorage.setItem(localStorageKey, JSON.stringify(getLayout(layers)));
602
- };
810
+ // Settings
811
+ /**
812
+ * Stop mapping an element that was added with addLayer. The element itself is left in the page.
813
+ *
814
+ * @param {HTMLElement} element
815
+ */
816
+ removeLayer(element) {
817
+ const index = this.layers.findIndex((layer) => layer.element === element);
818
+ if (index === -1) {
819
+ return;
820
+ }
821
+ this.layers.splice(index, 1);
822
+ if (this.selectedLayer && this.selectedLayer.element === element) {
823
+ this.selectedLayer = null;
824
+ this.selectedPoint = null;
825
+ }
826
+ if (this.hoveringLayer && this.hoveringLayer.element === element) {
827
+ this.hoveringLayer = null;
828
+ }
829
+ this.draw();
830
+ }
603
831
 
604
- var loadSettings = function () {
605
- if (localStorage.getItem(localStorageKey)) {
606
- var data = JSON.parse(localStorage.getItem(localStorageKey));
832
+ /**
833
+ * Save current layout settings to local storage.
834
+ *
835
+ * @private
836
+ */
837
+ saveSettings() {
838
+ localStorage.setItem(this.localStorageKey, JSON.stringify(this.getLayout()));
839
+ }
607
840
 
608
- for (var i = 0; i < data.length; i++) {
609
- for (var n = 0; n < layers.length; n++) {
610
- if (layers[n].element.id == data[i].id) {
611
- layers[n].targetPoints = clonePoints(data[i].targetPoints);
612
- layers[n].sourcePoints = clonePoints(data[i].sourcePoints);
613
- }
614
- }
615
- }
616
- updateTransform();
841
+ /**
842
+ * Load layout settings from local storage.
843
+ *
844
+ * @private
845
+ */
846
+ loadSettings() {
847
+ if (!localStorage.getItem(this.localStorageKey)) {
848
+ return;
617
849
  }
618
- };
850
+ const data = JSON.parse(localStorage.getItem(this.localStorageKey));
619
851
 
620
- var updateTransform = function () {
621
- var transform = ['', '-webkit-', '-moz-', '-ms-', '-o-'].reduce(function (p, v) {
622
- return v + 'transform' in document.body.style ? v : p;
623
- }) + 'transform';
624
- for (var l = 0; l < layers.length; l++) {
625
-
626
- for (var a = [], b = [], i = 0, n = layers[l].sourcePoints.length; i < n; ++i) {
627
- var s = layers[l].sourcePoints[i], t = layers[l].targetPoints[i];
628
- a.push([s[0], s[1], 1, 0, 0, 0, -s[0] * t[0], -s[1] * t[0]]), b.push(t[0]);
629
- a.push([0, 0, 0, s[0], s[1], 1, -s[0] * t[1], -s[1] * t[1]]), b.push(t[1]);
852
+ for (let i = 0; i < data.length; i++) {
853
+ for (let n = 0; n < this.layers.length; n++) {
854
+ if (this.layers[n].element.id == data[i].id) {
855
+ this.layers[n].targetPoints = clonePoints(data[i].targetPoints);
856
+ this.layers[n].sourcePoints = clonePoints(data[i].sourcePoints);
857
+ }
630
858
  }
631
-
632
- var X = solve(a, b, true);
633
- var matrix = [
634
- X[0], X[3], 0, X[6],
635
- X[1], X[4], 0, X[7],
636
- 0, 0, 1, 0,
637
- X[2], X[5], 0, 1
638
- ];
639
-
640
- layers[l].element.style[transform] = 'matrix3d(' + matrix.join(',') + ')';
641
- layers[l].element.style[transform + '-origin'] = '0px 0px 0px';
642
859
  }
643
- };
860
+ this.updateTransform();
861
+ }
644
862
 
645
- var setConfigEnabled = function (enabled) {
646
- configActive = enabled;
647
- canvas.style.display = enabled ? 'block' : 'none';
863
+ /**
864
+ * @private
865
+ * @param {boolean} enabled
866
+ */
867
+ setConfigEnabled(enabled) {
868
+ this.configActive = enabled;
869
+ this.canvas.style.display = enabled ? 'block' : 'none';
648
870
 
649
871
  if (!enabled) {
650
- selectedPoint = null;
651
- selectedLayer = null;
652
- dragging = false;
653
- showScreenBounds = false;
872
+ this.selectedPoint = null;
873
+ this.selectedLayer = null;
874
+ this.dragging = false;
875
+ this.showScreenBounds = false;
654
876
  } else {
655
- draw();
877
+ this.draw();
656
878
  }
657
- };
658
-
659
- var clonePoints = function (points) {
660
- var clone = [];
661
- for (var p = 0; p < points.length; p++) {
662
- clone.push(points[p].slice(0, 2));
663
- }
664
- return clone;
665
- };
666
-
667
- var resize = function () {
668
- canvas.width = window.innerWidth;
669
- canvas.height = window.innerHeight;
670
-
671
- draw();
672
- };
879
+ }
673
880
 
674
- var getLayout = function () {
675
- var layout = [];
676
- for (var i = 0; i < layers.length; i++) {
881
+ /**
882
+ * @public
883
+ * @returns {Layout}
884
+ */
885
+ getLayout() {
886
+ const layout = []; // TODO: change to a map()
887
+ for (let i = 0; i < this.layers.length; i++) {
677
888
  layout.push({
678
- 'id': layers[i].element.id,
679
- 'targetPoints': clonePoints(layers[i].targetPoints),
680
- 'sourcePoints': clonePoints(layers[i].sourcePoints)
889
+ 'id': this.layers[i].element.id,
890
+ 'targetPoints': clonePoints(this.layers[i].targetPoints),
891
+ 'sourcePoints': clonePoints(this.layers[i].sourcePoints)
681
892
  });
682
893
  }
683
894
  return layout;
684
- };
895
+ }
685
896
 
686
- var setLayout = function (layout) {
687
- for (var i = 0; i < layout.length; i++) {
688
- var exists = false;
689
- for (var n = 0; n < layers.length; n++) {
690
- if (layers[n].element.id == layout[i].id) {
897
+ /**
898
+ * @public
899
+ * @param {*} layout
900
+ */
901
+ setLayout(layout) {
902
+ for (let i = 0; i < layout.length; i++) {
903
+ let exists = false;
904
+ for (let n = 0; n < this.layers.length; n++) {
905
+ if (this.layers[n].element.id == layout[i].id) {
691
906
  console.log('Setting points.');
692
- layers[n].targetPoints = clonePoints(layout[i].targetPoints);
693
- layers[n].sourcePoints = clonePoints(layout[i].sourcePoints);
907
+ this.layers[n].targetPoints = clonePoints(layout[i].targetPoints);
908
+ this.layers[n].sourcePoints = clonePoints(layout[i].sourcePoints);
694
909
  exists = true;
695
910
  }
696
911
  }
697
912
 
698
913
  if (!exists) {
699
- var element = document.getElementById(layout[i].id);
914
+ const element = document.getElementById(layout[i].id);
700
915
  if (element) {
701
- addLayer(element, layout[i].targetPoints);
916
+ this.addLayer(element, layout[i].targetPoints);
702
917
  } else {
703
918
  console.log('Maptastic: Can\'t find element: ' + layout[i].id);
704
919
  }
@@ -706,44 +921,9 @@ function Maptastic(config) {
706
921
  console.log('Maptastic: Element \'' + layout[i].id + '\' is already mapped.');
707
922
  }
708
923
  }
709
- updateTransform();
710
- draw();
711
- };
712
-
713
- init();
714
-
715
- // if the config was just an element or string, interpret it as a layer to add.
716
-
717
- for (var i = 0; i < layerList.length; i++) {
718
- if ((layerList[i] instanceof HTMLElement) || (typeof (layerList[i]) === 'string')) {
719
- addLayer(layerList[i]);
720
- }
721
- }
722
-
723
- for (var i = 0; i < arguments.length; i++) {
724
- if ((arguments[i] instanceof HTMLElement) || (typeof (arguments[i]) === 'string')) {
725
- addLayer(arguments[i]);
726
- }
924
+ this.updateTransform();
925
+ this.draw();
727
926
  }
728
-
729
- if (autoLoad) {
730
- loadSettings();
731
- }
732
-
733
- return {
734
- 'getLayout': function () {
735
- return getLayout();
736
- },
737
- 'setLayout': function (layout) {
738
- setLayout(layout);
739
- },
740
- 'setConfigEnabled': function (enabled) {
741
- setConfigEnabled(enabled);
742
- },
743
- 'addLayer': function (target, targetPoints) {
744
- addLayer(target, targetPoints);
745
- }
746
- };
747
927
  }
748
928
 
749
929
  export { Maptastic };