zui53 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
data/build/zui53.js ADDED
@@ -0,0 +1,4014 @@
1
+ // # Sylvester
2
+ // Vector and Matrix mathematics modules for JavaScript
3
+ // Copyright (c) 2007 James Coglan
4
+ //
5
+ // Permission is hereby granted, free of charge, to any person obtaining
6
+ // a copy of this software and associated documentation files (the "Software"),
7
+ // to deal in the Software without restriction, including without limitation
8
+ // the rights to use, copy, modify, merge, publish, distribute, sublicense,
9
+ // and/or sell copies of the Software, and to permit persons to whom the
10
+ // Software is furnished to do so, subject to the following conditions:
11
+ //
12
+ // The above copyright notice and this permission notice shall be included
13
+ // in all copies or substantial portions of the Software.
14
+ //
15
+ // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
16
+ // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
18
+ // THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20
+ // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
21
+ // DEALINGS IN THE SOFTWARE.
22
+
23
+ var Sylvester = {
24
+ version: '0.1.3',
25
+ precision: 1e-6
26
+ };
27
+
28
+ function Vector() {}
29
+ Vector.prototype = {
30
+
31
+ // Returns element i of the vector
32
+ e: function(i) {
33
+ return (i < 1 || i > this.elements.length) ? null : this.elements[i-1];
34
+ },
35
+
36
+ // Returns the number of elements the vector has
37
+ dimensions: function() {
38
+ return this.elements.length;
39
+ },
40
+
41
+ // Returns the modulus ('length') of the vector
42
+ modulus: function() {
43
+ return Math.sqrt(this.dot(this));
44
+ },
45
+
46
+ // Returns true iff the vector is equal to the argument
47
+ eql: function(vector) {
48
+ var n = this.elements.length;
49
+ var V = vector.elements || vector;
50
+ if (n != V.length) { return false; }
51
+ do {
52
+ if (Math.abs(this.elements[n-1] - V[n-1]) > Sylvester.precision) { return false; }
53
+ } while (--n);
54
+ return true;
55
+ },
56
+
57
+ // Returns a copy of the vector
58
+ dup: function() {
59
+ return Vector.create(this.elements);
60
+ },
61
+
62
+ // Maps the vector to another vector according to the given function
63
+ map: function(fn) {
64
+ var elements = [];
65
+ this.each(function(x, i) {
66
+ elements.push(fn(x, i));
67
+ });
68
+ return Vector.create(elements);
69
+ },
70
+
71
+ // Calls the iterator for each element of the vector in turn
72
+ each: function(fn) {
73
+ var n = this.elements.length, k = n, i;
74
+ do { i = k - n;
75
+ fn(this.elements[i], i+1);
76
+ } while (--n);
77
+ },
78
+
79
+ // Returns a new vector created by normalizing the receiver
80
+ toUnitVector: function() {
81
+ var r = this.modulus();
82
+ if (r === 0) { return this.dup(); }
83
+ return this.map(function(x) { return x/r; });
84
+ },
85
+
86
+ // Returns the angle between the vector and the argument (also a vector)
87
+ angleFrom: function(vector) {
88
+ var V = vector.elements || vector;
89
+ var n = this.elements.length, k = n, i;
90
+ if (n != V.length) { return null; }
91
+ var dot = 0, mod1 = 0, mod2 = 0;
92
+ // Work things out in parallel to save time
93
+ this.each(function(x, i) {
94
+ dot += x * V[i-1];
95
+ mod1 += x * x;
96
+ mod2 += V[i-1] * V[i-1];
97
+ });
98
+ mod1 = Math.sqrt(mod1); mod2 = Math.sqrt(mod2);
99
+ if (mod1*mod2 === 0) { return null; }
100
+ var theta = dot / (mod1*mod2);
101
+ if (theta < -1) { theta = -1; }
102
+ if (theta > 1) { theta = 1; }
103
+ return Math.acos(theta);
104
+ },
105
+
106
+ // Returns true iff the vector is parallel to the argument
107
+ isParallelTo: function(vector) {
108
+ var angle = this.angleFrom(vector);
109
+ return (angle === null) ? null : (angle <= Sylvester.precision);
110
+ },
111
+
112
+ // Returns true iff the vector is antiparallel to the argument
113
+ isAntiparallelTo: function(vector) {
114
+ var angle = this.angleFrom(vector);
115
+ return (angle === null) ? null : (Math.abs(angle - Math.PI) <= Sylvester.precision);
116
+ },
117
+
118
+ // Returns true iff the vector is perpendicular to the argument
119
+ isPerpendicularTo: function(vector) {
120
+ var dot = this.dot(vector);
121
+ return (dot === null) ? null : (Math.abs(dot) <= Sylvester.precision);
122
+ },
123
+
124
+ // Returns the result of adding the argument to the vector
125
+ add: function(vector) {
126
+ var V = vector.elements || vector;
127
+ if (this.elements.length != V.length) { return null; }
128
+ return this.map(function(x, i) { return x + V[i-1]; });
129
+ },
130
+
131
+ // Returns the result of subtracting the argument from the vector
132
+ subtract: function(vector) {
133
+ var V = vector.elements || vector;
134
+ if (this.elements.length != V.length) { return null; }
135
+ return this.map(function(x, i) { return x - V[i-1]; });
136
+ },
137
+
138
+ // Returns the result of multiplying the elements of the vector by the argument
139
+ multiply: function(k) {
140
+ return this.map(function(x) { return x*k; });
141
+ },
142
+
143
+ x: function(k) { return this.multiply(k); },
144
+
145
+ // Returns the scalar product of the vector with the argument
146
+ // Both vectors must have equal dimensionality
147
+ dot: function(vector) {
148
+ var V = vector.elements || vector;
149
+ var i, product = 0, n = this.elements.length;
150
+ if (n != V.length) { return null; }
151
+ do { product += this.elements[n-1] * V[n-1]; } while (--n);
152
+ return product;
153
+ },
154
+
155
+ // Returns the vector product of the vector with the argument
156
+ // Both vectors must have dimensionality 3
157
+ cross: function(vector) {
158
+ var B = vector.elements || vector;
159
+ if (this.elements.length != 3 || B.length != 3) { return null; }
160
+ var A = this.elements;
161
+ return Vector.create([
162
+ (A[1] * B[2]) - (A[2] * B[1]),
163
+ (A[2] * B[0]) - (A[0] * B[2]),
164
+ (A[0] * B[1]) - (A[1] * B[0])
165
+ ]);
166
+ },
167
+
168
+ // Returns the (absolute) largest element of the vector
169
+ max: function() {
170
+ var m = 0, n = this.elements.length, k = n, i;
171
+ do { i = k - n;
172
+ if (Math.abs(this.elements[i]) > Math.abs(m)) { m = this.elements[i]; }
173
+ } while (--n);
174
+ return m;
175
+ },
176
+
177
+ // Returns the index of the first match found
178
+ indexOf: function(x) {
179
+ var index = null, n = this.elements.length, k = n, i;
180
+ do { i = k - n;
181
+ if (index === null && this.elements[i] == x) {
182
+ index = i + 1;
183
+ }
184
+ } while (--n);
185
+ return index;
186
+ },
187
+
188
+ // Returns a diagonal matrix with the vector's elements as its diagonal elements
189
+ toDiagonalMatrix: function() {
190
+ return Matrix.Diagonal(this.elements);
191
+ },
192
+
193
+ // Returns the result of rounding the elements of the vector
194
+ round: function() {
195
+ return this.map(function(x) { return Math.round(x); });
196
+ },
197
+
198
+ // Returns a copy of the vector with elements set to the given value if they
199
+ // differ from it by less than Sylvester.precision
200
+ snapTo: function(x) {
201
+ return this.map(function(y) {
202
+ return (Math.abs(y - x) <= Sylvester.precision) ? x : y;
203
+ });
204
+ },
205
+
206
+ // Returns the vector's distance from the argument, when considered as a point in space
207
+ distanceFrom: function(obj) {
208
+ if (obj.anchor) { return obj.distanceFrom(this); }
209
+ var V = obj.elements || obj;
210
+ if (V.length != this.elements.length) { return null; }
211
+ var sum = 0, part;
212
+ this.each(function(x, i) {
213
+ part = x - V[i-1];
214
+ sum += part * part;
215
+ });
216
+ return Math.sqrt(sum);
217
+ },
218
+
219
+ // Returns true if the vector is point on the given line
220
+ liesOn: function(line) {
221
+ return line.contains(this);
222
+ },
223
+
224
+ // Return true iff the vector is a point in the given plane
225
+ liesIn: function(plane) {
226
+ return plane.contains(this);
227
+ },
228
+
229
+ // Rotates the vector about the given object. The object should be a
230
+ // point if the vector is 2D, and a line if it is 3D. Be careful with line directions!
231
+ rotate: function(t, obj) {
232
+ var V, R, x, y, z;
233
+ switch (this.elements.length) {
234
+ case 2:
235
+ V = obj.elements || obj;
236
+ if (V.length != 2) { return null; }
237
+ R = Matrix.Rotation(t).elements;
238
+ x = this.elements[0] - V[0];
239
+ y = this.elements[1] - V[1];
240
+ return Vector.create([
241
+ V[0] + R[0][0] * x + R[0][1] * y,
242
+ V[1] + R[1][0] * x + R[1][1] * y
243
+ ]);
244
+ break;
245
+ case 3:
246
+ if (!obj.direction) { return null; }
247
+ var C = obj.pointClosestTo(this).elements;
248
+ R = Matrix.Rotation(t, obj.direction).elements;
249
+ x = this.elements[0] - C[0];
250
+ y = this.elements[1] - C[1];
251
+ z = this.elements[2] - C[2];
252
+ return Vector.create([
253
+ C[0] + R[0][0] * x + R[0][1] * y + R[0][2] * z,
254
+ C[1] + R[1][0] * x + R[1][1] * y + R[1][2] * z,
255
+ C[2] + R[2][0] * x + R[2][1] * y + R[2][2] * z
256
+ ]);
257
+ break;
258
+ default:
259
+ return null;
260
+ }
261
+ },
262
+
263
+ // Returns the result of reflecting the point in the given point, line or plane
264
+ reflectionIn: function(obj) {
265
+ if (obj.anchor) {
266
+ // obj is a plane or line
267
+ var P = this.elements.slice();
268
+ var C = obj.pointClosestTo(P).elements;
269
+ return Vector.create([C[0] + (C[0] - P[0]), C[1] + (C[1] - P[1]), C[2] + (C[2] - (P[2] || 0))]);
270
+ } else {
271
+ // obj is a point
272
+ var Q = obj.elements || obj;
273
+ if (this.elements.length != Q.length) { return null; }
274
+ return this.map(function(x, i) { return Q[i-1] + (Q[i-1] - x); });
275
+ }
276
+ },
277
+
278
+ // Utility to make sure vectors are 3D. If they are 2D, a zero z-component is added
279
+ to3D: function() {
280
+ var V = this.dup();
281
+ switch (V.elements.length) {
282
+ case 3: break;
283
+ case 2: V.elements.push(0); break;
284
+ default: return null;
285
+ }
286
+ return V;
287
+ },
288
+
289
+ // Returns a string representation of the vector
290
+ inspect: function() {
291
+ return '[' + this.elements.join(', ') + ']';
292
+ },
293
+
294
+ // Set vector's elements from an array
295
+ setElements: function(els) {
296
+ this.elements = (els.elements || els).slice();
297
+ return this;
298
+ }
299
+ };
300
+
301
+ // Constructor function
302
+ Vector.create = function(elements) {
303
+ var V = new Vector();
304
+ return V.setElements(elements);
305
+ };
306
+
307
+ // i, j, k unit vectors
308
+ Vector.i = Vector.create([1,0,0]);
309
+ Vector.j = Vector.create([0,1,0]);
310
+ Vector.k = Vector.create([0,0,1]);
311
+
312
+ // Random vector of size n
313
+ Vector.Random = function(n) {
314
+ var elements = [];
315
+ do { elements.push(Math.random());
316
+ } while (--n);
317
+ return Vector.create(elements);
318
+ };
319
+
320
+ // Vector filled with zeros
321
+ Vector.Zero = function(n) {
322
+ var elements = [];
323
+ do { elements.push(0);
324
+ } while (--n);
325
+ return Vector.create(elements);
326
+ };
327
+
328
+
329
+
330
+ function Matrix() {}
331
+ Matrix.prototype = {
332
+
333
+ // Returns element (i,j) of the matrix
334
+ e: function(i,j) {
335
+ if (i < 1 || i > this.elements.length || j < 1 || j > this.elements[0].length) { return null; }
336
+ return this.elements[i-1][j-1];
337
+ },
338
+
339
+ // Returns row k of the matrix as a vector
340
+ row: function(i) {
341
+ if (i > this.elements.length) { return null; }
342
+ return Vector.create(this.elements[i-1]);
343
+ },
344
+
345
+ // Returns column k of the matrix as a vector
346
+ col: function(j) {
347
+ if (j > this.elements[0].length) { return null; }
348
+ var col = [], n = this.elements.length, k = n, i;
349
+ do { i = k - n;
350
+ col.push(this.elements[i][j-1]);
351
+ } while (--n);
352
+ return Vector.create(col);
353
+ },
354
+
355
+ // Returns the number of rows/columns the matrix has
356
+ dimensions: function() {
357
+ return {rows: this.elements.length, cols: this.elements[0].length};
358
+ },
359
+
360
+ // Returns the number of rows in the matrix
361
+ rows: function() {
362
+ return this.elements.length;
363
+ },
364
+
365
+ // Returns the number of columns in the matrix
366
+ cols: function() {
367
+ return this.elements[0].length;
368
+ },
369
+
370
+ // Returns true iff the matrix is equal to the argument. You can supply
371
+ // a vector as the argument, in which case the receiver must be a
372
+ // one-column matrix equal to the vector.
373
+ eql: function(matrix) {
374
+ var M = matrix.elements || matrix;
375
+ if (typeof(M[0][0]) == 'undefined') { M = Matrix.create(M).elements; }
376
+ if (this.elements.length != M.length ||
377
+ this.elements[0].length != M[0].length) { return false; }
378
+ var ni = this.elements.length, ki = ni, i, nj, kj = this.elements[0].length, j;
379
+ do { i = ki - ni;
380
+ nj = kj;
381
+ do { j = kj - nj;
382
+ if (Math.abs(this.elements[i][j] - M[i][j]) > Sylvester.precision) { return false; }
383
+ } while (--nj);
384
+ } while (--ni);
385
+ return true;
386
+ },
387
+
388
+ // Returns a copy of the matrix
389
+ dup: function() {
390
+ return Matrix.create(this.elements);
391
+ },
392
+
393
+ // Maps the matrix to another matrix (of the same dimensions) according to the given function
394
+ map: function(fn) {
395
+ var els = [], ni = this.elements.length, ki = ni, i, nj, kj = this.elements[0].length, j;
396
+ do { i = ki - ni;
397
+ nj = kj;
398
+ els[i] = [];
399
+ do { j = kj - nj;
400
+ els[i][j] = fn(this.elements[i][j], i + 1, j + 1);
401
+ } while (--nj);
402
+ } while (--ni);
403
+ return Matrix.create(els);
404
+ },
405
+
406
+ // Returns true iff the argument has the same dimensions as the matrix
407
+ isSameSizeAs: function(matrix) {
408
+ var M = matrix.elements || matrix;
409
+ if (typeof(M[0][0]) == 'undefined') { M = Matrix.create(M).elements; }
410
+ return (this.elements.length == M.length &&
411
+ this.elements[0].length == M[0].length);
412
+ },
413
+
414
+ // Returns the result of adding the argument to the matrix
415
+ add: function(matrix) {
416
+ var M = matrix.elements || matrix;
417
+ if (typeof(M[0][0]) == 'undefined') { M = Matrix.create(M).elements; }
418
+ if (!this.isSameSizeAs(M)) { return null; }
419
+ return this.map(function(x, i, j) { return x + M[i-1][j-1]; });
420
+ },
421
+
422
+ // Returns the result of subtracting the argument from the matrix
423
+ subtract: function(matrix) {
424
+ var M = matrix.elements || matrix;
425
+ if (typeof(M[0][0]) == 'undefined') { M = Matrix.create(M).elements; }
426
+ if (!this.isSameSizeAs(M)) { return null; }
427
+ return this.map(function(x, i, j) { return x - M[i-1][j-1]; });
428
+ },
429
+
430
+ // Returns true iff the matrix can multiply the argument from the left
431
+ canMultiplyFromLeft: function(matrix) {
432
+ var M = matrix.elements || matrix;
433
+ if (typeof(M[0][0]) == 'undefined') { M = Matrix.create(M).elements; }
434
+ // this.columns should equal matrix.rows
435
+ return (this.elements[0].length == M.length);
436
+ },
437
+
438
+ // Returns the result of multiplying the matrix from the right by the argument.
439
+ // If the argument is a scalar then just multiply all the elements. If the argument is
440
+ // a vector, a vector is returned, which saves you having to remember calling
441
+ // col(1) on the result.
442
+ multiply: function(matrix) {
443
+ if (!matrix.elements) {
444
+ return this.map(function(x) { return x * matrix; });
445
+ }
446
+ var returnVector = matrix.modulus ? true : false;
447
+ var M = matrix.elements || matrix;
448
+ if (typeof(M[0][0]) == 'undefined') { M = Matrix.create(M).elements; }
449
+ if (!this.canMultiplyFromLeft(M)) { return null; }
450
+ var ni = this.elements.length, ki = ni, i, nj, kj = M[0].length, j;
451
+ var cols = this.elements[0].length, elements = [], sum, nc, c;
452
+ do { i = ki - ni;
453
+ elements[i] = [];
454
+ nj = kj;
455
+ do { j = kj - nj;
456
+ sum = 0;
457
+ nc = cols;
458
+ do { c = cols - nc;
459
+ sum += this.elements[i][c] * M[c][j];
460
+ } while (--nc);
461
+ elements[i][j] = sum;
462
+ } while (--nj);
463
+ } while (--ni);
464
+ var M = Matrix.create(elements);
465
+ return returnVector ? M.col(1) : M;
466
+ },
467
+
468
+ x: function(matrix) { return this.multiply(matrix); },
469
+
470
+ // Returns a submatrix taken from the matrix
471
+ // Argument order is: start row, start col, nrows, ncols
472
+ // Element selection wraps if the required index is outside the matrix's bounds, so you could
473
+ // use this to perform row/column cycling or copy-augmenting.
474
+ minor: function(a, b, c, d) {
475
+ var elements = [], ni = c, i, nj, j;
476
+ var rows = this.elements.length, cols = this.elements[0].length;
477
+ do { i = c - ni;
478
+ elements[i] = [];
479
+ nj = d;
480
+ do { j = d - nj;
481
+ elements[i][j] = this.elements[(a+i-1)%rows][(b+j-1)%cols];
482
+ } while (--nj);
483
+ } while (--ni);
484
+ return Matrix.create(elements);
485
+ },
486
+
487
+ // Returns the transpose of the matrix
488
+ transpose: function() {
489
+ var rows = this.elements.length, cols = this.elements[0].length;
490
+ var elements = [], ni = cols, i, nj, j;
491
+ do { i = cols - ni;
492
+ elements[i] = [];
493
+ nj = rows;
494
+ do { j = rows - nj;
495
+ elements[i][j] = this.elements[j][i];
496
+ } while (--nj);
497
+ } while (--ni);
498
+ return Matrix.create(elements);
499
+ },
500
+
501
+ // Returns true iff the matrix is square
502
+ isSquare: function() {
503
+ return (this.elements.length == this.elements[0].length);
504
+ },
505
+
506
+ // Returns the (absolute) largest element of the matrix
507
+ max: function() {
508
+ var m = 0, ni = this.elements.length, ki = ni, i, nj, kj = this.elements[0].length, j;
509
+ do { i = ki - ni;
510
+ nj = kj;
511
+ do { j = kj - nj;
512
+ if (Math.abs(this.elements[i][j]) > Math.abs(m)) { m = this.elements[i][j]; }
513
+ } while (--nj);
514
+ } while (--ni);
515
+ return m;
516
+ },
517
+
518
+ // Returns the indeces of the first match found by reading row-by-row from left to right
519
+ indexOf: function(x) {
520
+ var index = null, ni = this.elements.length, ki = ni, i, nj, kj = this.elements[0].length, j;
521
+ do { i = ki - ni;
522
+ nj = kj;
523
+ do { j = kj - nj;
524
+ if (this.elements[i][j] == x) { return {i: i+1, j: j+1}; }
525
+ } while (--nj);
526
+ } while (--ni);
527
+ return null;
528
+ },
529
+
530
+ // If the matrix is square, returns the diagonal elements as a vector.
531
+ // Otherwise, returns null.
532
+ diagonal: function() {
533
+ if (!this.isSquare) { return null; }
534
+ var els = [], n = this.elements.length, k = n, i;
535
+ do { i = k - n;
536
+ els.push(this.elements[i][i]);
537
+ } while (--n);
538
+ return Vector.create(els);
539
+ },
540
+
541
+ // Make the matrix upper (right) triangular by Gaussian elimination.
542
+ // This method only adds multiples of rows to other rows. No rows are
543
+ // scaled up or switched, and the determinant is preserved.
544
+ toRightTriangular: function() {
545
+ var M = this.dup(), els;
546
+ var n = this.elements.length, k = n, i, np, kp = this.elements[0].length, p;
547
+ do { i = k - n;
548
+ if (M.elements[i][i] == 0) {
549
+ for (j = i + 1; j < k; j++) {
550
+ if (M.elements[j][i] != 0) {
551
+ els = []; np = kp;
552
+ do { p = kp - np;
553
+ els.push(M.elements[i][p] + M.elements[j][p]);
554
+ } while (--np);
555
+ M.elements[i] = els;
556
+ break;
557
+ }
558
+ }
559
+ }
560
+ if (M.elements[i][i] != 0) {
561
+ for (j = i + 1; j < k; j++) {
562
+ var multiplier = M.elements[j][i] / M.elements[i][i];
563
+ els = []; np = kp;
564
+ do { p = kp - np;
565
+ // Elements with column numbers up to an including the number
566
+ // of the row that we're subtracting can safely be set straight to
567
+ // zero, since that's the point of this routine and it avoids having
568
+ // to loop over and correct rounding errors later
569
+ els.push(p <= i ? 0 : M.elements[j][p] - M.elements[i][p] * multiplier);
570
+ } while (--np);
571
+ M.elements[j] = els;
572
+ }
573
+ }
574
+ } while (--n);
575
+ return M;
576
+ },
577
+
578
+ toUpperTriangular: function() { return this.toRightTriangular(); },
579
+
580
+ // Returns the determinant for square matrices
581
+ determinant: function() {
582
+ if (!this.isSquare()) { return null; }
583
+ var M = this.toRightTriangular();
584
+ var det = M.elements[0][0], n = M.elements.length - 1, k = n, i;
585
+ do { i = k - n + 1;
586
+ det = det * M.elements[i][i];
587
+ } while (--n);
588
+ return det;
589
+ },
590
+
591
+ det: function() { return this.determinant(); },
592
+
593
+ // Returns true iff the matrix is singular
594
+ isSingular: function() {
595
+ return (this.isSquare() && this.determinant() === 0);
596
+ },
597
+
598
+ // Returns the trace for square matrices
599
+ trace: function() {
600
+ if (!this.isSquare()) { return null; }
601
+ var tr = this.elements[0][0], n = this.elements.length - 1, k = n, i;
602
+ do { i = k - n + 1;
603
+ tr += this.elements[i][i];
604
+ } while (--n);
605
+ return tr;
606
+ },
607
+
608
+ tr: function() { return this.trace(); },
609
+
610
+ // Returns the rank of the matrix
611
+ rank: function() {
612
+ var M = this.toRightTriangular(), rank = 0;
613
+ var ni = this.elements.length, ki = ni, i, nj, kj = this.elements[0].length, j;
614
+ do { i = ki - ni;
615
+ nj = kj;
616
+ do { j = kj - nj;
617
+ if (Math.abs(M.elements[i][j]) > Sylvester.precision) { rank++; break; }
618
+ } while (--nj);
619
+ } while (--ni);
620
+ return rank;
621
+ },
622
+
623
+ rk: function() { return this.rank(); },
624
+
625
+ // Returns the result of attaching the given argument to the right-hand side of the matrix
626
+ augment: function(matrix) {
627
+ var M = matrix.elements || matrix;
628
+ if (typeof(M[0][0]) == 'undefined') { M = Matrix.create(M).elements; }
629
+ var T = this.dup(), cols = T.elements[0].length;
630
+ var ni = T.elements.length, ki = ni, i, nj, kj = M[0].length, j;
631
+ if (ni != M.length) { return null; }
632
+ do { i = ki - ni;
633
+ nj = kj;
634
+ do { j = kj - nj;
635
+ T.elements[i][cols + j] = M[i][j];
636
+ } while (--nj);
637
+ } while (--ni);
638
+ return T;
639
+ },
640
+
641
+ // Returns the inverse (if one exists) using Gauss-Jordan
642
+ inverse: function() {
643
+ if (!this.isSquare() || this.isSingular()) { return null; }
644
+ var ni = this.elements.length, ki = ni, i, j;
645
+ var M = this.augment(Matrix.I(ni)).toRightTriangular();
646
+ var np, kp = M.elements[0].length, p, els, divisor;
647
+ var inverse_elements = [], new_element;
648
+ // Matrix is non-singular so there will be no zeros on the diagonal
649
+ // Cycle through rows from last to first
650
+ do { i = ni - 1;
651
+ // First, normalise diagonal elements to 1
652
+ els = []; np = kp;
653
+ inverse_elements[i] = [];
654
+ divisor = M.elements[i][i];
655
+ do { p = kp - np;
656
+ new_element = M.elements[i][p] / divisor;
657
+ els.push(new_element);
658
+ // Shuffle of the current row of the right hand side into the results
659
+ // array as it will not be modified by later runs through this loop
660
+ if (p >= ki) { inverse_elements[i].push(new_element); }
661
+ } while (--np);
662
+ M.elements[i] = els;
663
+ // Then, subtract this row from those above it to
664
+ // give the identity matrix on the left hand side
665
+ for (j = 0; j < i; j++) {
666
+ els = []; np = kp;
667
+ do { p = kp - np;
668
+ els.push(M.elements[j][p] - M.elements[i][p] * M.elements[j][i]);
669
+ } while (--np);
670
+ M.elements[j] = els;
671
+ }
672
+ } while (--ni);
673
+ return Matrix.create(inverse_elements);
674
+ },
675
+
676
+ inv: function() { return this.inverse(); },
677
+
678
+ // Returns the result of rounding all the elements
679
+ round: function() {
680
+ return this.map(function(x) { return Math.round(x); });
681
+ },
682
+
683
+ // Returns a copy of the matrix with elements set to the given value if they
684
+ // differ from it by less than Sylvester.precision
685
+ snapTo: function(x) {
686
+ return this.map(function(p) {
687
+ return (Math.abs(p - x) <= Sylvester.precision) ? x : p;
688
+ });
689
+ },
690
+
691
+ // Returns a string representation of the matrix
692
+ inspect: function() {
693
+ var matrix_rows = [];
694
+ var n = this.elements.length, k = n, i;
695
+ do { i = k - n;
696
+ matrix_rows.push(Vector.create(this.elements[i]).inspect());
697
+ } while (--n);
698
+ return matrix_rows.join('\n');
699
+ },
700
+
701
+ // Set the matrix's elements from an array. If the argument passed
702
+ // is a vector, the resulting matrix will be a single column.
703
+ setElements: function(els) {
704
+ var i, elements = els.elements || els;
705
+ if (typeof(elements[0][0]) != 'undefined') {
706
+ var ni = elements.length, ki = ni, nj, kj, j;
707
+ this.elements = [];
708
+ do { i = ki - ni;
709
+ nj = elements[i].length; kj = nj;
710
+ this.elements[i] = [];
711
+ do { j = kj - nj;
712
+ this.elements[i][j] = elements[i][j];
713
+ } while (--nj);
714
+ } while(--ni);
715
+ return this;
716
+ }
717
+ var n = elements.length, k = n;
718
+ this.elements = [];
719
+ do { i = k - n;
720
+ this.elements.push([elements[i]]);
721
+ } while (--n);
722
+ return this;
723
+ }
724
+ };
725
+
726
+ // Constructor function
727
+ Matrix.create = function(elements) {
728
+ var M = new Matrix();
729
+ return M.setElements(elements);
730
+ };
731
+
732
+ // Identity matrix of size n
733
+ Matrix.I = function(n) {
734
+ var els = [], k = n, i, nj, j;
735
+ do { i = k - n;
736
+ els[i] = []; nj = k;
737
+ do { j = k - nj;
738
+ els[i][j] = (i == j) ? 1 : 0;
739
+ } while (--nj);
740
+ } while (--n);
741
+ return Matrix.create(els);
742
+ };
743
+
744
+ // Diagonal matrix - all off-diagonal elements are zero
745
+ Matrix.Diagonal = function(elements) {
746
+ var n = elements.length, k = n, i;
747
+ var M = Matrix.I(n);
748
+ do { i = k - n;
749
+ M.elements[i][i] = elements[i];
750
+ } while (--n);
751
+ return M;
752
+ };
753
+
754
+ // Rotation matrix about some axis. If no axis is
755
+ // supplied, assume we're after a 2D transform
756
+ Matrix.Rotation = function(theta, a) {
757
+ if (!a) {
758
+ return Matrix.create([
759
+ [Math.cos(theta), -Math.sin(theta)],
760
+ [Math.sin(theta), Math.cos(theta)]
761
+ ]);
762
+ }
763
+ var axis = a.dup();
764
+ if (axis.elements.length != 3) { return null; }
765
+ var mod = axis.modulus();
766
+ var x = axis.elements[0]/mod, y = axis.elements[1]/mod, z = axis.elements[2]/mod;
767
+ var s = Math.sin(theta), c = Math.cos(theta), t = 1 - c;
768
+ // Formula derived here: http://www.gamedev.net/reference/articles/article1199.asp
769
+ // That proof rotates the co-ordinate system so theta
770
+ // becomes -theta and sin becomes -sin here.
771
+ return Matrix.create([
772
+ [ t*x*x + c, t*x*y - s*z, t*x*z + s*y ],
773
+ [ t*x*y + s*z, t*y*y + c, t*y*z - s*x ],
774
+ [ t*x*z - s*y, t*y*z + s*x, t*z*z + c ]
775
+ ]);
776
+ };
777
+
778
+ // Special case rotations
779
+ Matrix.RotationX = function(t) {
780
+ var c = Math.cos(t), s = Math.sin(t);
781
+ return Matrix.create([
782
+ [ 1, 0, 0 ],
783
+ [ 0, c, -s ],
784
+ [ 0, s, c ]
785
+ ]);
786
+ };
787
+ Matrix.RotationY = function(t) {
788
+ var c = Math.cos(t), s = Math.sin(t);
789
+ return Matrix.create([
790
+ [ c, 0, s ],
791
+ [ 0, 1, 0 ],
792
+ [ -s, 0, c ]
793
+ ]);
794
+ };
795
+ Matrix.RotationZ = function(t) {
796
+ var c = Math.cos(t), s = Math.sin(t);
797
+ return Matrix.create([
798
+ [ c, -s, 0 ],
799
+ [ s, c, 0 ],
800
+ [ 0, 0, 1 ]
801
+ ]);
802
+ };
803
+
804
+ // Random matrix of n rows, m columns
805
+ Matrix.Random = function(n, m) {
806
+ return Matrix.Zero(n, m).map(
807
+ function() { return Math.random(); }
808
+ );
809
+ };
810
+
811
+ // Matrix filled with zeros
812
+ Matrix.Zero = function(n, m) {
813
+ var els = [], ni = n, i, nj, j;
814
+ do { i = n - ni;
815
+ els[i] = [];
816
+ nj = m;
817
+ do { j = m - nj;
818
+ els[i][j] = 0;
819
+ } while (--nj);
820
+ } while (--ni);
821
+ return Matrix.create(els);
822
+ };
823
+
824
+
825
+
826
+ function Line() {}
827
+ Line.prototype = {
828
+
829
+ // Returns true if the argument occupies the same space as the line
830
+ eql: function(line) {
831
+ return (this.isParallelTo(line) && this.contains(line.anchor));
832
+ },
833
+
834
+ // Returns a copy of the line
835
+ dup: function() {
836
+ return Line.create(this.anchor, this.direction);
837
+ },
838
+
839
+ // Returns the result of translating the line by the given vector/array
840
+ translate: function(vector) {
841
+ var V = vector.elements || vector;
842
+ return Line.create([
843
+ this.anchor.elements[0] + V[0],
844
+ this.anchor.elements[1] + V[1],
845
+ this.anchor.elements[2] + (V[2] || 0)
846
+ ], this.direction);
847
+ },
848
+
849
+ // Returns true if the line is parallel to the argument. Here, 'parallel to'
850
+ // means that the argument's direction is either parallel or antiparallel to
851
+ // the line's own direction. A line is parallel to a plane if the two do not
852
+ // have a unique intersection.
853
+ isParallelTo: function(obj) {
854
+ if (obj.normal) { return obj.isParallelTo(this); }
855
+ var theta = this.direction.angleFrom(obj.direction);
856
+ return (Math.abs(theta) <= Sylvester.precision || Math.abs(theta - Math.PI) <= Sylvester.precision);
857
+ },
858
+
859
+ // Returns the line's perpendicular distance from the argument,
860
+ // which can be a point, a line or a plane
861
+ distanceFrom: function(obj) {
862
+ if (obj.normal) { return obj.distanceFrom(this); }
863
+ if (obj.direction) {
864
+ // obj is a line
865
+ if (this.isParallelTo(obj)) { return this.distanceFrom(obj.anchor); }
866
+ var N = this.direction.cross(obj.direction).toUnitVector().elements;
867
+ var A = this.anchor.elements, B = obj.anchor.elements;
868
+ return Math.abs((A[0] - B[0]) * N[0] + (A[1] - B[1]) * N[1] + (A[2] - B[2]) * N[2]);
869
+ } else {
870
+ // obj is a point
871
+ var P = obj.elements || obj;
872
+ var A = this.anchor.elements, D = this.direction.elements;
873
+ var PA1 = P[0] - A[0], PA2 = P[1] - A[1], PA3 = (P[2] || 0) - A[2];
874
+ var modPA = Math.sqrt(PA1*PA1 + PA2*PA2 + PA3*PA3);
875
+ if (modPA === 0) return 0;
876
+ // Assumes direction vector is normalized
877
+ var cosTheta = (PA1 * D[0] + PA2 * D[1] + PA3 * D[2]) / modPA;
878
+ var sin2 = 1 - cosTheta*cosTheta;
879
+ return Math.abs(modPA * Math.sqrt(sin2 < 0 ? 0 : sin2));
880
+ }
881
+ },
882
+
883
+ // Returns true iff the argument is a point on the line
884
+ contains: function(point) {
885
+ var dist = this.distanceFrom(point);
886
+ return (dist !== null && dist <= Sylvester.precision);
887
+ },
888
+
889
+ // Returns true iff the line lies in the given plane
890
+ liesIn: function(plane) {
891
+ return plane.contains(this);
892
+ },
893
+
894
+ // Returns true iff the line has a unique point of intersection with the argument
895
+ intersects: function(obj) {
896
+ if (obj.normal) { return obj.intersects(this); }
897
+ return (!this.isParallelTo(obj) && this.distanceFrom(obj) <= Sylvester.precision);
898
+ },
899
+
900
+ // Returns the unique intersection point with the argument, if one exists
901
+ intersectionWith: function(obj) {
902
+ if (obj.normal) { return obj.intersectionWith(this); }
903
+ if (!this.intersects(obj)) { return null; }
904
+ var P = this.anchor.elements, X = this.direction.elements,
905
+ Q = obj.anchor.elements, Y = obj.direction.elements;
906
+ var X1 = X[0], X2 = X[1], X3 = X[2], Y1 = Y[0], Y2 = Y[1], Y3 = Y[2];
907
+ var PsubQ1 = P[0] - Q[0], PsubQ2 = P[1] - Q[1], PsubQ3 = P[2] - Q[2];
908
+ var XdotQsubP = - X1*PsubQ1 - X2*PsubQ2 - X3*PsubQ3;
909
+ var YdotPsubQ = Y1*PsubQ1 + Y2*PsubQ2 + Y3*PsubQ3;
910
+ var XdotX = X1*X1 + X2*X2 + X3*X3;
911
+ var YdotY = Y1*Y1 + Y2*Y2 + Y3*Y3;
912
+ var XdotY = X1*Y1 + X2*Y2 + X3*Y3;
913
+ var k = (XdotQsubP * YdotY / XdotX + XdotY * YdotPsubQ) / (YdotY - XdotY * XdotY);
914
+ return Vector.create([P[0] + k*X1, P[1] + k*X2, P[2] + k*X3]);
915
+ },
916
+
917
+ // Returns the point on the line that is closest to the given point or line
918
+ pointClosestTo: function(obj) {
919
+ if (obj.direction) {
920
+ // obj is a line
921
+ if (this.intersects(obj)) { return this.intersectionWith(obj); }
922
+ if (this.isParallelTo(obj)) { return null; }
923
+ var D = this.direction.elements, E = obj.direction.elements;
924
+ var D1 = D[0], D2 = D[1], D3 = D[2], E1 = E[0], E2 = E[1], E3 = E[2];
925
+ // Create plane containing obj and the shared normal and intersect this with it
926
+ // Thank you: http://www.cgafaq.info/wiki/Line-line_distance
927
+ var x = (D3 * E1 - D1 * E3), y = (D1 * E2 - D2 * E1), z = (D2 * E3 - D3 * E2);
928
+ var N = Vector.create([x * E3 - y * E2, y * E1 - z * E3, z * E2 - x * E1]);
929
+ var P = Plane.create(obj.anchor, N);
930
+ return P.intersectionWith(this);
931
+ } else {
932
+ // obj is a point
933
+ var P = obj.elements || obj;
934
+ if (this.contains(P)) { return Vector.create(P); }
935
+ var A = this.anchor.elements, D = this.direction.elements;
936
+ var D1 = D[0], D2 = D[1], D3 = D[2], A1 = A[0], A2 = A[1], A3 = A[2];
937
+ var x = D1 * (P[1]-A2) - D2 * (P[0]-A1), y = D2 * ((P[2] || 0) - A3) - D3 * (P[1]-A2),
938
+ z = D3 * (P[0]-A1) - D1 * ((P[2] || 0) - A3);
939
+ var V = Vector.create([D2 * x - D3 * z, D3 * y - D1 * x, D1 * z - D2 * y]);
940
+ var k = this.distanceFrom(P) / V.modulus();
941
+ return Vector.create([
942
+ P[0] + V.elements[0] * k,
943
+ P[1] + V.elements[1] * k,
944
+ (P[2] || 0) + V.elements[2] * k
945
+ ]);
946
+ }
947
+ },
948
+
949
+ // Returns a copy of the line rotated by t radians about the given line. Works by
950
+ // finding the argument's closest point to this line's anchor point (call this C) and
951
+ // rotating the anchor about C. Also rotates the line's direction about the argument's.
952
+ // Be careful with this - the rotation axis' direction affects the outcome!
953
+ rotate: function(t, line) {
954
+ // If we're working in 2D
955
+ if (typeof(line.direction) == 'undefined') { line = Line.create(line.to3D(), Vector.k); }
956
+ var R = Matrix.Rotation(t, line.direction).elements;
957
+ var C = line.pointClosestTo(this.anchor).elements;
958
+ var A = this.anchor.elements, D = this.direction.elements;
959
+ var C1 = C[0], C2 = C[1], C3 = C[2], A1 = A[0], A2 = A[1], A3 = A[2];
960
+ var x = A1 - C1, y = A2 - C2, z = A3 - C3;
961
+ return Line.create([
962
+ C1 + R[0][0] * x + R[0][1] * y + R[0][2] * z,
963
+ C2 + R[1][0] * x + R[1][1] * y + R[1][2] * z,
964
+ C3 + R[2][0] * x + R[2][1] * y + R[2][2] * z
965
+ ], [
966
+ R[0][0] * D[0] + R[0][1] * D[1] + R[0][2] * D[2],
967
+ R[1][0] * D[0] + R[1][1] * D[1] + R[1][2] * D[2],
968
+ R[2][0] * D[0] + R[2][1] * D[1] + R[2][2] * D[2]
969
+ ]);
970
+ },
971
+
972
+ // Returns the line's reflection in the given point or line
973
+ reflectionIn: function(obj) {
974
+ if (obj.normal) {
975
+ // obj is a plane
976
+ var A = this.anchor.elements, D = this.direction.elements;
977
+ var A1 = A[0], A2 = A[1], A3 = A[2], D1 = D[0], D2 = D[1], D3 = D[2];
978
+ var newA = this.anchor.reflectionIn(obj).elements;
979
+ // Add the line's direction vector to its anchor, then mirror that in the plane
980
+ var AD1 = A1 + D1, AD2 = A2 + D2, AD3 = A3 + D3;
981
+ var Q = obj.pointClosestTo([AD1, AD2, AD3]).elements;
982
+ var newD = [Q[0] + (Q[0] - AD1) - newA[0], Q[1] + (Q[1] - AD2) - newA[1], Q[2] + (Q[2] - AD3) - newA[2]];
983
+ return Line.create(newA, newD);
984
+ } else if (obj.direction) {
985
+ // obj is a line - reflection obtained by rotating PI radians about obj
986
+ return this.rotate(Math.PI, obj);
987
+ } else {
988
+ // obj is a point - just reflect the line's anchor in it
989
+ var P = obj.elements || obj;
990
+ return Line.create(this.anchor.reflectionIn([P[0], P[1], (P[2] || 0)]), this.direction);
991
+ }
992
+ },
993
+
994
+ // Set the line's anchor point and direction.
995
+ setVectors: function(anchor, direction) {
996
+ // Need to do this so that line's properties are not
997
+ // references to the arguments passed in
998
+ anchor = Vector.create(anchor);
999
+ direction = Vector.create(direction);
1000
+ if (anchor.elements.length == 2) {anchor.elements.push(0); }
1001
+ if (direction.elements.length == 2) { direction.elements.push(0); }
1002
+ if (anchor.elements.length > 3 || direction.elements.length > 3) { return null; }
1003
+ var mod = direction.modulus();
1004
+ if (mod === 0) { return null; }
1005
+ this.anchor = anchor;
1006
+ this.direction = Vector.create([
1007
+ direction.elements[0] / mod,
1008
+ direction.elements[1] / mod,
1009
+ direction.elements[2] / mod
1010
+ ]);
1011
+ return this;
1012
+ }
1013
+ };
1014
+
1015
+
1016
+ // Constructor function
1017
+ Line.create = function(anchor, direction) {
1018
+ var L = new Line();
1019
+ return L.setVectors(anchor, direction);
1020
+ };
1021
+
1022
+ // Axes
1023
+ Line.X = Line.create(Vector.Zero(3), Vector.i);
1024
+ Line.Y = Line.create(Vector.Zero(3), Vector.j);
1025
+ Line.Z = Line.create(Vector.Zero(3), Vector.k);
1026
+
1027
+
1028
+
1029
+ function Plane() {}
1030
+ Plane.prototype = {
1031
+
1032
+ // Returns true iff the plane occupies the same space as the argument
1033
+ eql: function(plane) {
1034
+ return (this.contains(plane.anchor) && this.isParallelTo(plane));
1035
+ },
1036
+
1037
+ // Returns a copy of the plane
1038
+ dup: function() {
1039
+ return Plane.create(this.anchor, this.normal);
1040
+ },
1041
+
1042
+ // Returns the result of translating the plane by the given vector
1043
+ translate: function(vector) {
1044
+ var V = vector.elements || vector;
1045
+ return Plane.create([
1046
+ this.anchor.elements[0] + V[0],
1047
+ this.anchor.elements[1] + V[1],
1048
+ this.anchor.elements[2] + (V[2] || 0)
1049
+ ], this.normal);
1050
+ },
1051
+
1052
+ // Returns true iff the plane is parallel to the argument. Will return true
1053
+ // if the planes are equal, or if you give a line and it lies in the plane.
1054
+ isParallelTo: function(obj) {
1055
+ var theta;
1056
+ if (obj.normal) {
1057
+ // obj is a plane
1058
+ theta = this.normal.angleFrom(obj.normal);
1059
+ return (Math.abs(theta) <= Sylvester.precision || Math.abs(Math.PI - theta) <= Sylvester.precision);
1060
+ } else if (obj.direction) {
1061
+ // obj is a line
1062
+ return this.normal.isPerpendicularTo(obj.direction);
1063
+ }
1064
+ return null;
1065
+ },
1066
+
1067
+ // Returns true iff the receiver is perpendicular to the argument
1068
+ isPerpendicularTo: function(plane) {
1069
+ var theta = this.normal.angleFrom(plane.normal);
1070
+ return (Math.abs(Math.PI/2 - theta) <= Sylvester.precision);
1071
+ },
1072
+
1073
+ // Returns the plane's distance from the given object (point, line or plane)
1074
+ distanceFrom: function(obj) {
1075
+ if (this.intersects(obj) || this.contains(obj)) { return 0; }
1076
+ if (obj.anchor) {
1077
+ // obj is a plane or line
1078
+ var A = this.anchor.elements, B = obj.anchor.elements, N = this.normal.elements;
1079
+ return Math.abs((A[0] - B[0]) * N[0] + (A[1] - B[1]) * N[1] + (A[2] - B[2]) * N[2]);
1080
+ } else {
1081
+ // obj is a point
1082
+ var P = obj.elements || obj;
1083
+ var A = this.anchor.elements, N = this.normal.elements;
1084
+ return Math.abs((A[0] - P[0]) * N[0] + (A[1] - P[1]) * N[1] + (A[2] - (P[2] || 0)) * N[2]);
1085
+ }
1086
+ },
1087
+
1088
+ // Returns true iff the plane contains the given point or line
1089
+ contains: function(obj) {
1090
+ if (obj.normal) { return null; }
1091
+ if (obj.direction) {
1092
+ return (this.contains(obj.anchor) && this.contains(obj.anchor.add(obj.direction)));
1093
+ } else {
1094
+ var P = obj.elements || obj;
1095
+ var A = this.anchor.elements, N = this.normal.elements;
1096
+ var diff = Math.abs(N[0]*(A[0] - P[0]) + N[1]*(A[1] - P[1]) + N[2]*(A[2] - (P[2] || 0)));
1097
+ return (diff <= Sylvester.precision);
1098
+ }
1099
+ },
1100
+
1101
+ // Returns true iff the plane has a unique point/line of intersection with the argument
1102
+ intersects: function(obj) {
1103
+ if (typeof(obj.direction) == 'undefined' && typeof(obj.normal) == 'undefined') { return null; }
1104
+ return !this.isParallelTo(obj);
1105
+ },
1106
+
1107
+ // Returns the unique intersection with the argument, if one exists. The result
1108
+ // will be a vector if a line is supplied, and a line if a plane is supplied.
1109
+ intersectionWith: function(obj) {
1110
+ if (!this.intersects(obj)) { return null; }
1111
+ if (obj.direction) {
1112
+ // obj is a line
1113
+ var A = obj.anchor.elements, D = obj.direction.elements,
1114
+ P = this.anchor.elements, N = this.normal.elements;
1115
+ var multiplier = (N[0]*(P[0]-A[0]) + N[1]*(P[1]-A[1]) + N[2]*(P[2]-A[2])) / (N[0]*D[0] + N[1]*D[1] + N[2]*D[2]);
1116
+ return Vector.create([A[0] + D[0]*multiplier, A[1] + D[1]*multiplier, A[2] + D[2]*multiplier]);
1117
+ } else if (obj.normal) {
1118
+ // obj is a plane
1119
+ var direction = this.normal.cross(obj.normal).toUnitVector();
1120
+ // To find an anchor point, we find one co-ordinate that has a value
1121
+ // of zero somewhere on the intersection, and remember which one we picked
1122
+ var N = this.normal.elements, A = this.anchor.elements,
1123
+ O = obj.normal.elements, B = obj.anchor.elements;
1124
+ var solver = Matrix.Zero(2,2), i = 0;
1125
+ while (solver.isSingular()) {
1126
+ i++;
1127
+ solver = Matrix.create([
1128
+ [ N[i%3], N[(i+1)%3] ],
1129
+ [ O[i%3], O[(i+1)%3] ]
1130
+ ]);
1131
+ }
1132
+ // Then we solve the simultaneous equations in the remaining dimensions
1133
+ var inverse = solver.inverse().elements;
1134
+ var x = N[0]*A[0] + N[1]*A[1] + N[2]*A[2];
1135
+ var y = O[0]*B[0] + O[1]*B[1] + O[2]*B[2];
1136
+ var intersection = [
1137
+ inverse[0][0] * x + inverse[0][1] * y,
1138
+ inverse[1][0] * x + inverse[1][1] * y
1139
+ ];
1140
+ var anchor = [];
1141
+ for (var j = 1; j <= 3; j++) {
1142
+ // This formula picks the right element from intersection by
1143
+ // cycling depending on which element we set to zero above
1144
+ anchor.push((i == j) ? 0 : intersection[(j + (5 - i)%3)%3]);
1145
+ }
1146
+ return Line.create(anchor, direction);
1147
+ }
1148
+ },
1149
+
1150
+ // Returns the point in the plane closest to the given point
1151
+ pointClosestTo: function(point) {
1152
+ var P = point.elements || point;
1153
+ var A = this.anchor.elements, N = this.normal.elements;
1154
+ var dot = (A[0] - P[0]) * N[0] + (A[1] - P[1]) * N[1] + (A[2] - (P[2] || 0)) * N[2];
1155
+ return Vector.create([P[0] + N[0] * dot, P[1] + N[1] * dot, (P[2] || 0) + N[2] * dot]);
1156
+ },
1157
+
1158
+ // Returns a copy of the plane, rotated by t radians about the given line
1159
+ // See notes on Line#rotate.
1160
+ rotate: function(t, line) {
1161
+ var R = Matrix.Rotation(t, line.direction).elements;
1162
+ var C = line.pointClosestTo(this.anchor).elements;
1163
+ var A = this.anchor.elements, N = this.normal.elements;
1164
+ var C1 = C[0], C2 = C[1], C3 = C[2], A1 = A[0], A2 = A[1], A3 = A[2];
1165
+ var x = A1 - C1, y = A2 - C2, z = A3 - C3;
1166
+ return Plane.create([
1167
+ C1 + R[0][0] * x + R[0][1] * y + R[0][2] * z,
1168
+ C2 + R[1][0] * x + R[1][1] * y + R[1][2] * z,
1169
+ C3 + R[2][0] * x + R[2][1] * y + R[2][2] * z
1170
+ ], [
1171
+ R[0][0] * N[0] + R[0][1] * N[1] + R[0][2] * N[2],
1172
+ R[1][0] * N[0] + R[1][1] * N[1] + R[1][2] * N[2],
1173
+ R[2][0] * N[0] + R[2][1] * N[1] + R[2][2] * N[2]
1174
+ ]);
1175
+ },
1176
+
1177
+ // Returns the reflection of the plane in the given point, line or plane.
1178
+ reflectionIn: function(obj) {
1179
+ if (obj.normal) {
1180
+ // obj is a plane
1181
+ var A = this.anchor.elements, N = this.normal.elements;
1182
+ var A1 = A[0], A2 = A[1], A3 = A[2], N1 = N[0], N2 = N[1], N3 = N[2];
1183
+ var newA = this.anchor.reflectionIn(obj).elements;
1184
+ // Add the plane's normal to its anchor, then mirror that in the other plane
1185
+ var AN1 = A1 + N1, AN2 = A2 + N2, AN3 = A3 + N3;
1186
+ var Q = obj.pointClosestTo([AN1, AN2, AN3]).elements;
1187
+ var newN = [Q[0] + (Q[0] - AN1) - newA[0], Q[1] + (Q[1] - AN2) - newA[1], Q[2] + (Q[2] - AN3) - newA[2]];
1188
+ return Plane.create(newA, newN);
1189
+ } else if (obj.direction) {
1190
+ // obj is a line
1191
+ return this.rotate(Math.PI, obj);
1192
+ } else {
1193
+ // obj is a point
1194
+ var P = obj.elements || obj;
1195
+ return Plane.create(this.anchor.reflectionIn([P[0], P[1], (P[2] || 0)]), this.normal);
1196
+ }
1197
+ },
1198
+
1199
+ // Sets the anchor point and normal to the plane. If three arguments are specified,
1200
+ // the normal is calculated by assuming the three points should lie in the same plane.
1201
+ // If only two are sepcified, the second is taken to be the normal. Normal vector is
1202
+ // normalised before storage.
1203
+ setVectors: function(anchor, v1, v2) {
1204
+ anchor = Vector.create(anchor);
1205
+ anchor = anchor.to3D(); if (anchor === null) { return null; }
1206
+ v1 = Vector.create(v1);
1207
+ v1 = v1.to3D(); if (v1 === null) { return null; }
1208
+ if (typeof(v2) == 'undefined') {
1209
+ v2 = null;
1210
+ } else {
1211
+ v2 = Vector.create(v2);
1212
+ v2 = v2.to3D(); if (v2 === null) { return null; }
1213
+ }
1214
+ var A1 = anchor.elements[0], A2 = anchor.elements[1], A3 = anchor.elements[2];
1215
+ var v11 = v1.elements[0], v12 = v1.elements[1], v13 = v1.elements[2];
1216
+ var normal, mod;
1217
+ if (v2 !== null) {
1218
+ var v21 = v2.elements[0], v22 = v2.elements[1], v23 = v2.elements[2];
1219
+ normal = Vector.create([
1220
+ (v12 - A2) * (v23 - A3) - (v13 - A3) * (v22 - A2),
1221
+ (v13 - A3) * (v21 - A1) - (v11 - A1) * (v23 - A3),
1222
+ (v11 - A1) * (v22 - A2) - (v12 - A2) * (v21 - A1)
1223
+ ]);
1224
+ mod = normal.modulus();
1225
+ if (mod === 0) { return null; }
1226
+ normal = Vector.create([normal.elements[0] / mod, normal.elements[1] / mod, normal.elements[2] / mod]);
1227
+ } else {
1228
+ mod = Math.sqrt(v11*v11 + v12*v12 + v13*v13);
1229
+ if (mod === 0) { return null; }
1230
+ normal = Vector.create([v1.elements[0] / mod, v1.elements[1] / mod, v1.elements[2] / mod]);
1231
+ }
1232
+ this.anchor = anchor;
1233
+ this.normal = normal;
1234
+ return this;
1235
+ }
1236
+ };
1237
+
1238
+ // Constructor function
1239
+ Plane.create = function(anchor, v1, v2) {
1240
+ var P = new Plane();
1241
+ return P.setVectors(anchor, v1, v2);
1242
+ };
1243
+
1244
+ // X-Y-Z planes
1245
+ Plane.XY = Plane.create(Vector.Zero(3), Vector.k);
1246
+ Plane.YZ = Plane.create(Vector.Zero(3), Vector.i);
1247
+ Plane.ZX = Plane.create(Vector.Zero(3), Vector.j);
1248
+ Plane.YX = Plane.XY; Plane.ZY = Plane.YZ; Plane.XZ = Plane.ZX;
1249
+
1250
+ // Utility functions
1251
+ var $V = Vector.create;
1252
+ var $M = Matrix.create;
1253
+ var $L = Line.create;
1254
+ var $P = Plane.create;
1255
+ /*!
1256
+ * jQuery 2d Transform v0.9.3
1257
+ * http://wiki.github.com/heygrady/transform/
1258
+ *
1259
+ * Copyright 2010, Grady Kuhnline
1260
+ * Dual licensed under the MIT or GPL Version 2 licenses.
1261
+ * http://jquery.org/license
1262
+ *
1263
+ * Date: Sat Dec 4 15:46:09 2010 -0800
1264
+ */
1265
+ ///////////////////////////////////////////////////////
1266
+ // Transform
1267
+ ///////////////////////////////////////////////////////
1268
+ (function($, window, document, undefined) {
1269
+ /**
1270
+ * @var Regex identify the matrix filter in IE
1271
+ */
1272
+ var rmatrix = /progid:DXImageTransform\.Microsoft\.Matrix\(.*?\)/,
1273
+ rfxnum = /^([\+\-]=)?([\d+.\-]+)(.*)$/,
1274
+ rperc = /%/;
1275
+
1276
+ // Steal some code from Modernizr
1277
+ var m = document.createElement( 'modernizr' ),
1278
+ m_style = m.style;
1279
+
1280
+ function stripUnits(arg) {
1281
+ return parseFloat(arg);
1282
+ }
1283
+
1284
+ /**
1285
+ * Find the prefix that this browser uses
1286
+ */
1287
+ function getVendorPrefix() {
1288
+ var property = {
1289
+ transformProperty : '',
1290
+ MozTransform : '-moz-',
1291
+ WebkitTransform : '-webkit-',
1292
+ OTransform : '-o-',
1293
+ msTransform : '-ms-'
1294
+ };
1295
+ for (var p in property) {
1296
+ if (typeof m_style[p] != 'undefined') {
1297
+ return property[p];
1298
+ }
1299
+ }
1300
+ return null;
1301
+ }
1302
+
1303
+ function supportCssTransforms() {
1304
+ if (typeof(window.Modernizr) !== 'undefined') {
1305
+ return Modernizr.csstransforms;
1306
+ }
1307
+
1308
+ var props = [ 'transformProperty', 'WebkitTransform', 'MozTransform', 'OTransform', 'msTransform' ];
1309
+ for ( var i in props ) {
1310
+ if ( m_style[ props[i] ] !== undefined ) {
1311
+ return true;
1312
+ }
1313
+ }
1314
+ }
1315
+
1316
+ // Capture some basic properties
1317
+ var vendorPrefix = getVendorPrefix(),
1318
+ transformProperty = vendorPrefix !== null ? vendorPrefix + 'transform' : false,
1319
+ transformOriginProperty = vendorPrefix !== null ? vendorPrefix + 'transform-origin' : false;
1320
+
1321
+ // store support in the jQuery Support object
1322
+ $.support.csstransforms = supportCssTransforms();
1323
+
1324
+ // IE9 public preview 6 requires the DOM names
1325
+ if (vendorPrefix == '-ms-') {
1326
+ transformProperty = 'msTransform';
1327
+ transformOriginProperty = 'msTransformOrigin';
1328
+ }
1329
+
1330
+ /**
1331
+ * Class for creating cross-browser transformations
1332
+ * @constructor
1333
+ */
1334
+ $.extend({
1335
+ transform: function(elem) {
1336
+ // Cache the transform object on the element itself
1337
+ elem.transform = this;
1338
+
1339
+ /**
1340
+ * The element we're working with
1341
+ * @var jQueryCollection
1342
+ */
1343
+ this.$elem = $(elem);
1344
+
1345
+ /**
1346
+ * Remember the matrix we're applying to help the safeOuterLength func
1347
+ */
1348
+ this.applyingMatrix = false;
1349
+ this.matrix = null;
1350
+
1351
+ /**
1352
+ * Remember the css height and width to save time
1353
+ * This is only really used in IE
1354
+ * @var Number
1355
+ */
1356
+ this.height = null;
1357
+ this.width = null;
1358
+ this.outerHeight = null;
1359
+ this.outerWidth = null;
1360
+
1361
+ /**
1362
+ * We need to know the box-sizing in IE for building the outerHeight and outerWidth
1363
+ * @var string
1364
+ */
1365
+ this.boxSizingValue = null;
1366
+ this.boxSizingProperty = null;
1367
+
1368
+ this.attr = null;
1369
+ this.transformProperty = transformProperty;
1370
+ this.transformOriginProperty = transformOriginProperty;
1371
+ }
1372
+ });
1373
+
1374
+ $.extend($.transform, {
1375
+ /**
1376
+ * @var Array list of all valid transform functions
1377
+ */
1378
+ funcs: ['matrix', 'origin', 'reflect', 'reflectX', 'reflectXY', 'reflectY', 'rotate', 'scale', 'scaleX', 'scaleY', 'skew', 'skewX', 'skewY', 'translate', 'translateX', 'translateY']
1379
+ });
1380
+
1381
+ /**
1382
+ * Create Transform as a jQuery plugin
1383
+ * @param Object funcs
1384
+ * @param Object options
1385
+ */
1386
+ $.fn.transform = function(funcs, options) {
1387
+ return this.each(function() {
1388
+ var t = this.transform || new $.transform(this);
1389
+ if (funcs) {
1390
+ t.exec(funcs, options);
1391
+ }
1392
+ });
1393
+ };
1394
+
1395
+ $.transform.prototype = {
1396
+ /**
1397
+ * Applies all of the transformations
1398
+ * @param Object funcs
1399
+ * @param Object options
1400
+ * forceMatrix - uses the matrix in all browsers
1401
+ * preserve - tries to preserve the values from previous runs
1402
+ */
1403
+ exec: function(funcs, options) {
1404
+ // extend options
1405
+ options = $.extend(true, {
1406
+ forceMatrix: false,
1407
+ preserve: false
1408
+ }, options);
1409
+
1410
+ // preserve the funcs from the previous run
1411
+ this.attr = null;
1412
+ if (options.preserve) {
1413
+ funcs = $.extend(true, this.getAttrs(true, true), funcs);
1414
+ } else {
1415
+ funcs = $.extend(true, {}, funcs); // copy the object to prevent weirdness
1416
+ }
1417
+
1418
+ // Record the custom attributes on the element itself
1419
+ this.setAttrs(funcs);
1420
+
1421
+ // apply the funcs
1422
+ if ($.support.csstransforms && !options.forceMatrix) {
1423
+ // CSS3 is supported
1424
+ return this.execFuncs(funcs);
1425
+ } else if ($.browser.msie || ($.support.csstransforms && options.forceMatrix)) {
1426
+ // Internet Explorer or Forced matrix
1427
+ return this.execMatrix(funcs);
1428
+ }
1429
+ return false;
1430
+ },
1431
+
1432
+ /**
1433
+ * Applies all of the transformations as functions
1434
+ * @param Object funcs
1435
+ */
1436
+ execFuncs: function(funcs) {
1437
+ var values = [];
1438
+
1439
+ // construct a CSS string
1440
+ for (var func in funcs) {
1441
+ // handle origin separately
1442
+ if (func == 'origin') {
1443
+ this[func].apply(this, $.isArray(funcs[func]) ? funcs[func] : [funcs[func]]);
1444
+ } else if ($.inArray(func, $.transform.funcs) !== -1) {
1445
+ values.push(this.createTransformFunc(func, funcs[func]));
1446
+ }
1447
+ }
1448
+ this.$elem.css(transformProperty, values.join(' '));
1449
+ return true;
1450
+ },
1451
+
1452
+ /**
1453
+ * Applies all of the transformations as a matrix
1454
+ * @param Object funcs
1455
+ */
1456
+ execMatrix: function(funcs) {
1457
+ var matrix,
1458
+ tempMatrix,
1459
+ args;
1460
+
1461
+ var elem = this.$elem[0],
1462
+ _this = this;
1463
+ function normalPixels(val, i) {
1464
+ if (rperc.test(val)) {
1465
+ // this really only applies to translation
1466
+ return parseFloat(val) / 100 * _this['safeOuter' + (i ? 'Height' : 'Width')]();
1467
+ }
1468
+ return toPx(elem, val);
1469
+ }
1470
+
1471
+ var rtranslate = /translate[X|Y]?/,
1472
+ trans = [];
1473
+
1474
+ for (var func in funcs) {
1475
+ switch ($.type(funcs[func])) {
1476
+ case 'array': args = funcs[func]; break;
1477
+ case 'string': args = $.map(funcs[func].split(','), $.trim); break;
1478
+ default: args = [funcs[func]];
1479
+ }
1480
+
1481
+ if ($.matrix[func]) {
1482
+
1483
+ if ($.cssAngle[func]) {
1484
+ // normalize on degrees
1485
+ args = $.map(args, $.angle.toDegree);
1486
+ } else if (!$.cssNumber[func]) {
1487
+ // normalize to pixels
1488
+ args = $.map(args, normalPixels);
1489
+ } else {
1490
+ // strip units
1491
+ args = $.map(args, stripUnits);
1492
+ }
1493
+
1494
+ tempMatrix = $.matrix[func].apply(this, args);
1495
+ if (rtranslate.test(func)) {
1496
+ //defer translation
1497
+ trans.push(tempMatrix);
1498
+ } else {
1499
+ matrix = matrix ? matrix.x(tempMatrix) : tempMatrix;
1500
+ }
1501
+ } else if (func == 'origin') {
1502
+ this[func].apply(this, args);
1503
+ }
1504
+ }
1505
+
1506
+ // check that we have a matrix
1507
+ matrix = matrix || $.matrix.identity();
1508
+
1509
+ // Apply translation
1510
+ $.each(trans, function(i, val) { matrix = matrix.x(val); });
1511
+
1512
+ // pull out the relevant values
1513
+ var a = parseFloat(matrix.e(1,1).toFixed(6)),
1514
+ b = parseFloat(matrix.e(2,1).toFixed(6)),
1515
+ c = parseFloat(matrix.e(1,2).toFixed(6)),
1516
+ d = parseFloat(matrix.e(2,2).toFixed(6)),
1517
+ tx = matrix.rows === 3 ? parseFloat(matrix.e(1,3).toFixed(6)) : 0,
1518
+ ty = matrix.rows === 3 ? parseFloat(matrix.e(2,3).toFixed(6)) : 0;
1519
+
1520
+ //apply the transform to the element
1521
+ if ($.support.csstransforms && vendorPrefix === '-moz-') {
1522
+ // -moz-
1523
+ this.$elem.css(transformProperty, 'matrix(' + a + ', ' + b + ', ' + c + ', ' + d + ', ' + tx + 'px, ' + ty + 'px)');
1524
+ } else if ($.support.csstransforms) {
1525
+ // -webkit, -o-, w3c
1526
+ // NOTE: WebKit and Opera don't allow units on the translate variables
1527
+ this.$elem.css(transformProperty, 'matrix(' + a + ', ' + b + ', ' + c + ', ' + d + ', ' + tx + ', ' + ty + ')');
1528
+ } else if ($.browser.msie) {
1529
+ // IE requires the special transform Filter
1530
+
1531
+ //TODO: Use Nearest Neighbor during animation FilterType=\'nearest neighbor\'
1532
+ var filterType = ', FilterType=\'nearest neighbor\''; //bilinear
1533
+ var style = this.$elem[0].style;
1534
+ var matrixFilter = 'progid:DXImageTransform.Microsoft.Matrix(' +
1535
+ 'M11=' + a + ', M12=' + c + ', M21=' + b + ', M22=' + d +
1536
+ ', sizingMethod=\'auto expand\'' + filterType + ')';
1537
+ var filter = style.filter || $.curCSS( this.$elem[0], "filter" ) || "";
1538
+ style.filter = rmatrix.test(filter) ? filter.replace(rmatrix, matrixFilter) : filter ? filter + ' ' + matrixFilter : matrixFilter;
1539
+
1540
+ // Let's know that we're applying post matrix fixes and the height/width will be static for a bit
1541
+ this.applyingMatrix = true;
1542
+ this.matrix = matrix;
1543
+
1544
+ // IE can't set the origin or translate directly
1545
+ this.fixPosition(matrix, tx, ty);
1546
+
1547
+ this.applyingMatrix = false;
1548
+ this.matrix = null;
1549
+ }
1550
+ return true;
1551
+ },
1552
+
1553
+ /**
1554
+ * Sets the transform-origin
1555
+ * This really needs to be percentages
1556
+ * @param Number x length
1557
+ * @param Number y length
1558
+ */
1559
+ origin: function(x, y) {
1560
+ // use CSS in supported browsers
1561
+ if ($.support.csstransforms) {
1562
+ if (typeof y === 'undefined') {
1563
+ this.$elem.css(transformOriginProperty, x);
1564
+ } else {
1565
+ this.$elem.css(transformOriginProperty, x + ' ' + y);
1566
+ }
1567
+ return true;
1568
+ }
1569
+
1570
+ // correct for keyword lengths
1571
+ switch (x) {
1572
+ case 'left': x = '0'; break;
1573
+ case 'right': x = '100%'; break;
1574
+ case 'center': // no break
1575
+ case undefined: x = '50%';
1576
+ }
1577
+ switch (y) {
1578
+ case 'top': y = '0'; break;
1579
+ case 'bottom': y = '100%'; break;
1580
+ case 'center': // no break
1581
+ case undefined: y = '50%'; //TODO: does this work?
1582
+ }
1583
+
1584
+ // store mixed values with units, assumed pixels
1585
+ this.setAttr('origin', [
1586
+ rperc.test(x) ? x : toPx(this.$elem[0], x) + 'px',
1587
+ rperc.test(y) ? y : toPx(this.$elem[0], y) + 'px'
1588
+ ]);
1589
+ //console.log(this.getAttr('origin'));
1590
+ return true;
1591
+ },
1592
+
1593
+ /**
1594
+ * Create a function suitable for a CSS value
1595
+ * @param string func
1596
+ * @param Mixed value
1597
+ */
1598
+ createTransformFunc: function(func, value) {
1599
+ if (func.substr(0, 7) === 'reflect') {
1600
+ // let's fake reflection, false value
1601
+ // falsey sets an identity matrix
1602
+ var m = value ? $.matrix[func]() : $.matrix.identity();
1603
+ return 'matrix(' + m.e(1,1) + ', ' + m.e(2,1) + ', ' + m.e(1,2) + ', ' + m.e(2,2) + ', 0, 0)';
1604
+ }
1605
+
1606
+ //value = _correctUnits(func, value);
1607
+
1608
+ if (func == 'matrix') {
1609
+ if (vendorPrefix === '-moz-') {
1610
+ value[4] = value[4] ? value[4] + 'px' : 0;
1611
+ value[5] = value[5] ? value[5] + 'px' : 0;
1612
+ }
1613
+ }
1614
+ return func + '(' + ($.isArray(value) ? value.join(', ') : value) + ')';
1615
+ },
1616
+
1617
+ /**
1618
+ * @param Matrix matrix
1619
+ * @param Number tx
1620
+ * @param Number ty
1621
+ * @param Number height
1622
+ * @param Number width
1623
+ */
1624
+ fixPosition: function(matrix, tx, ty, height, width) {
1625
+ // now we need to fix it!
1626
+ var calc = new $.matrix.calc(matrix, this.safeOuterHeight(), this.safeOuterWidth()),
1627
+ origin = this.getAttr('origin'); // mixed percentages and px
1628
+
1629
+ // translate a 0, 0 origin to the current origin
1630
+ var offset = calc.originOffset(new $.matrix.V2(
1631
+ rperc.test(origin[0]) ? parseFloat(origin[0])/100*calc.outerWidth : parseFloat(origin[0]),
1632
+ rperc.test(origin[1]) ? parseFloat(origin[1])/100*calc.outerHeight : parseFloat(origin[1])
1633
+ ));
1634
+
1635
+ // IE glues the top-most and left-most pixels of the transformed object to top/left of the original object
1636
+ //TODO: This seems wrong in the calculations
1637
+ var sides = calc.sides();
1638
+
1639
+ // Protect against an item that is already positioned
1640
+ var cssPosition = this.$elem.css('position');
1641
+ if (cssPosition == 'static') {
1642
+ cssPosition = 'relative';
1643
+ }
1644
+
1645
+ //TODO: if the element is already positioned, we should attempt to respect it (somehow)
1646
+ //NOTE: we could preserve our offset top and left in an attr on the elem
1647
+ var pos = {top: 0, left: 0};
1648
+
1649
+ // Approximates transform-origin, tx, and ty
1650
+ var css = {
1651
+ 'position': cssPosition,
1652
+ 'top': (offset.top + ty + sides.top + pos.top) + 'px',
1653
+ 'left': (offset.left + tx + sides.left + pos.left) + 'px',
1654
+ 'zoom': 1
1655
+ };
1656
+
1657
+ this.$elem.css(css);
1658
+ }
1659
+ };
1660
+
1661
+ /**
1662
+ * Ensure that values have the appropriate units on them
1663
+ * @param string func
1664
+ * @param Mixed value
1665
+ */
1666
+ function toPx(elem, val) {
1667
+ var parts = rfxnum.exec($.trim(val));
1668
+
1669
+ if (parts[3] && parts[3] !== 'px') {
1670
+ var prop = 'paddingBottom',
1671
+ orig = $.style( elem, prop );
1672
+
1673
+ $.style( elem, prop, val );
1674
+ val = cur( elem, prop );
1675
+ $.style( elem, prop, orig );
1676
+ return val;
1677
+ }
1678
+ return parseFloat( val );
1679
+ }
1680
+
1681
+ function cur(elem, prop) {
1682
+ if ( elem[prop] != null && (!elem.style || elem.style[prop] == null) ) {
1683
+ return elem[ prop ];
1684
+ }
1685
+
1686
+ var r = parseFloat( $.css( elem, prop ) );
1687
+ return r && r > -10000 ? r : 0;
1688
+ }
1689
+ })(jQuery, this, this.document);
1690
+
1691
+
1692
+ ///////////////////////////////////////////////////////
1693
+ // Safe Outer Length
1694
+ ///////////////////////////////////////////////////////
1695
+ (function($, window, document, undefined) {
1696
+ $.extend($.transform.prototype, {
1697
+ /**
1698
+ * @param void
1699
+ * @return Number
1700
+ */
1701
+ safeOuterHeight: function() {
1702
+ return this.safeOuterLength('height');
1703
+ },
1704
+
1705
+ /**
1706
+ * @param void
1707
+ * @return Number
1708
+ */
1709
+ safeOuterWidth: function() {
1710
+ return this.safeOuterLength('width');
1711
+ },
1712
+
1713
+ /**
1714
+ * Returns reliable outer dimensions for an object that may have been transformed.
1715
+ * Only use this if the matrix isn't handy
1716
+ * @param String dim height or width
1717
+ * @return Number
1718
+ */
1719
+ safeOuterLength: function(dim) {
1720
+ var funcName = 'outer' + (dim == 'width' ? 'Width' : 'Height');
1721
+
1722
+ if (!$.support.csstransforms && $.browser.msie) {
1723
+ // make the variables more generic
1724
+ dim = dim == 'width' ? 'width' : 'height';
1725
+
1726
+ // if we're transforming and have a matrix; we can shortcut.
1727
+ // the true outerHeight is the transformed outerHeight divided by the ratio.
1728
+ // the ratio is equal to the height of a 1px by 1px box that has been transformed by the same matrix.
1729
+ if (this.applyingMatrix && !this[funcName] && this.matrix) {
1730
+ // calculate and return the correct size
1731
+ var calc = new $.matrix.calc(this.matrix, 1, 1),
1732
+ ratio = calc.offset(),
1733
+ length = this.$elem[funcName]() / ratio[dim];
1734
+ this[funcName] = length;
1735
+
1736
+ return length;
1737
+ } else if (this.applyingMatrix && this[funcName]) {
1738
+ // return the cached calculation
1739
+ return this[funcName];
1740
+ }
1741
+
1742
+ // map dimensions to box sides
1743
+ var side = {
1744
+ height: ['top', 'bottom'],
1745
+ width: ['left', 'right']
1746
+ };
1747
+
1748
+ // setup some variables
1749
+ var elem = this.$elem[0],
1750
+ outerLen = parseFloat($.curCSS(elem, dim, true)), //TODO: this can be cached on animations that do not animate height/width
1751
+ boxSizingProp = this.boxSizingProperty,
1752
+ boxSizingValue = this.boxSizingValue;
1753
+
1754
+ // IE6 && IE7 will never have a box-sizing property, so fake it
1755
+ if (!this.boxSizingProperty) {
1756
+ boxSizingProp = this.boxSizingProperty = _findBoxSizingProperty() || 'box-sizing';
1757
+ boxSizingValue = this.boxSizingValue = this.$elem.css(boxSizingProp) || 'content-box';
1758
+ }
1759
+
1760
+ // return it immediately if we already know it
1761
+ if (this[funcName] && this[dim] == outerLen) {
1762
+ return this[funcName];
1763
+ } else {
1764
+ this[dim] = outerLen;
1765
+ }
1766
+
1767
+ // add in the padding and border
1768
+ if (boxSizingProp && (boxSizingValue == 'padding-box' || boxSizingValue == 'content-box')) {
1769
+ outerLen += parseFloat($.curCSS(elem, 'padding-' + side[dim][0], true)) || 0 +
1770
+ parseFloat($.curCSS(elem, 'padding-' + side[dim][1], true)) || 0;
1771
+ }
1772
+ if (boxSizingProp && boxSizingValue == 'content-box') {
1773
+ outerLen += parseFloat($.curCSS(elem, 'border-' + side[dim][0] + '-width', true)) || 0 +
1774
+ parseFloat($.curCSS(elem, 'border-' + side[dim][1] + '-width', true)) || 0;
1775
+ }
1776
+
1777
+ // remember and return the outerHeight
1778
+ this[funcName] = outerLen;
1779
+ return outerLen;
1780
+ }
1781
+ return this.$elem[funcName]();
1782
+ }
1783
+ });
1784
+
1785
+ /**
1786
+ * Determine the correct property for checking the box-sizing property
1787
+ * @param void
1788
+ * @return string
1789
+ */
1790
+ var _boxSizingProperty = null;
1791
+ function _findBoxSizingProperty () {
1792
+ if (_boxSizingProperty) {
1793
+ return _boxSizingProperty;
1794
+ }
1795
+
1796
+ var property = {
1797
+ boxSizing : 'box-sizing',
1798
+ MozBoxSizing : '-moz-box-sizing',
1799
+ WebkitBoxSizing : '-webkit-box-sizing',
1800
+ OBoxSizing : '-o-box-sizing'
1801
+ },
1802
+ elem = document.body;
1803
+
1804
+ for (var p in property) {
1805
+ if (typeof elem.style[p] != 'undefined') {
1806
+ _boxSizingProperty = property[p];
1807
+ return _boxSizingProperty;
1808
+ }
1809
+ }
1810
+ return null;
1811
+ }
1812
+ })(jQuery, this, this.document);
1813
+
1814
+
1815
+ ///////////////////////////////////////////////////////
1816
+ // Attr
1817
+ ///////////////////////////////////////////////////////
1818
+ (function($, window, document, undefined) {
1819
+ var rfuncvalue = /([\w\-]*?)\((.*?)\)/g, // with values
1820
+ attr = 'data-transform',
1821
+ rspace = /\s/,
1822
+ rcspace = /,\s?/;
1823
+
1824
+ $.extend($.transform.prototype, {
1825
+ /**
1826
+ * This overrides all of the attributes
1827
+ * @param Object funcs a list of transform functions to store on this element
1828
+ * @return void
1829
+ */
1830
+ setAttrs: function(funcs) {
1831
+ var string = '',
1832
+ value;
1833
+ for (var func in funcs) {
1834
+ value = funcs[func];
1835
+ if ($.isArray(value)) {
1836
+ value = value.join(', ');
1837
+ }
1838
+ string += ' ' + func + '(' + value + ')';
1839
+ }
1840
+ this.attr = $.trim(string);
1841
+ this.$elem.attr(attr, this.attr);
1842
+ },
1843
+
1844
+ /**
1845
+ * This sets only a specific atribute
1846
+ * @param string func name of a transform function
1847
+ * @param mixed value with proper units
1848
+ * @return void
1849
+ */
1850
+ setAttr: function(func, value) {
1851
+ // stringify the value
1852
+ if ($.isArray(value)) {
1853
+ value = value.join(', ');
1854
+ }
1855
+
1856
+ // pull from a local variable to look it up
1857
+ var transform = this.attr || this.$elem.attr(attr);
1858
+ if (!transform || transform.indexOf(func) == -1) {
1859
+ // we don't have any existing values, save it
1860
+ // we don't have this function yet, save it
1861
+ this.attr = $.trim(transform + ' ' + func + '(' + value + ')');
1862
+ this.$elem.attr(attr, this.attr);
1863
+ } else {
1864
+ // replace the existing value
1865
+ var funcs = [], parts;
1866
+
1867
+ // regex split
1868
+ rfuncvalue.lastIndex = 0; // reset the regex pointer
1869
+ while (parts = rfuncvalue.exec(transform)) {
1870
+ if (func == parts[1]) {
1871
+ funcs.push(func + '(' + value + ')');
1872
+ } else {
1873
+ funcs.push(parts[0]);
1874
+ }
1875
+ }
1876
+ this.attr = funcs.join(' ');
1877
+ this.$elem.attr(attr, this.attr);
1878
+ }
1879
+ },
1880
+
1881
+ /**
1882
+ * @return Object
1883
+ */
1884
+ getAttrs: function() {
1885
+ var transform = this.attr || this.$elem.attr(attr);
1886
+ if (!transform) {
1887
+ // We don't have any existing values, return empty object
1888
+ return {};
1889
+ }
1890
+
1891
+ // replace the existing value
1892
+ var attrs = {}, parts, value;
1893
+
1894
+ rfuncvalue.lastIndex = 0; // reset the regex pointer
1895
+ while ((parts = rfuncvalue.exec(transform)) !== null) {
1896
+ if (parts) {
1897
+ value = parts[2].split(rcspace);
1898
+ attrs[parts[1]] = value.length == 1 ? value[0] : value;
1899
+ }
1900
+ }
1901
+ return attrs;
1902
+ },
1903
+
1904
+ /**
1905
+ * @param String func
1906
+ * @return mixed
1907
+ */
1908
+ getAttr: function(func) {
1909
+ var attrs = this.getAttrs();
1910
+ if (typeof attrs[func] !== 'undefined') {
1911
+ return attrs[func];
1912
+ }
1913
+
1914
+ //TODO: move the origin to a function
1915
+ if (func === 'origin' && $.support.csstransforms) {
1916
+ // supported browsers return percentages always
1917
+ return this.$elem.css(this.transformOriginProperty).split(rspace);
1918
+ } else if (func === 'origin') {
1919
+ // just force IE to also return a percentage
1920
+ return ['50%', '50%'];
1921
+ }
1922
+
1923
+ return $.cssDefault[func] || 0;
1924
+ }
1925
+ });
1926
+
1927
+ // Define
1928
+ if (typeof($.cssAngle) == 'undefined') {
1929
+ $.cssAngle = {};
1930
+ }
1931
+ $.extend($.cssAngle, {
1932
+ rotate: true,
1933
+ skew: true,
1934
+ skewX: true,
1935
+ skewY: true
1936
+ });
1937
+
1938
+ // Define default values
1939
+ if (typeof($.cssDefault) == 'undefined') {
1940
+ $.cssDefault = {};
1941
+ }
1942
+
1943
+ $.extend($.cssDefault, {
1944
+ scale: [1, 1],
1945
+ scaleX: 1,
1946
+ scaleY: 1,
1947
+ matrix: [1, 0, 0, 1, 0, 0],
1948
+ origin: ['50%', '50%'], // TODO: allow this to be a function, like get
1949
+ reflect: [1, 0, 0, 1, 0, 0],
1950
+ reflectX: [1, 0, 0, 1, 0, 0],
1951
+ reflectXY: [1, 0, 0, 1, 0, 0],
1952
+ reflectY: [1, 0, 0, 1, 0, 0]
1953
+ });
1954
+
1955
+ // Define functons with multiple values
1956
+ if (typeof($.cssMultipleValues) == 'undefined') {
1957
+ $.cssMultipleValues = {};
1958
+ }
1959
+ $.extend($.cssMultipleValues, {
1960
+ matrix: 6,
1961
+ origin: {
1962
+ length: 2,
1963
+ duplicate: true
1964
+ },
1965
+ reflect: 6,
1966
+ reflectX: 6,
1967
+ reflectXY: 6,
1968
+ reflectY: 6,
1969
+ scale: {
1970
+ length: 2,
1971
+ duplicate: true
1972
+ },
1973
+ skew: 2,
1974
+ translate: 2
1975
+ });
1976
+
1977
+ // specify unitless funcs
1978
+ $.extend($.cssNumber, {
1979
+ matrix: true,
1980
+ reflect: true,
1981
+ reflectX: true,
1982
+ reflectXY: true,
1983
+ reflectY: true,
1984
+ scale: true,
1985
+ scaleX: true,
1986
+ scaleY: true
1987
+ });
1988
+
1989
+ // override all of the css functions
1990
+ $.each($.transform.funcs, function(i, func) {
1991
+ $.cssHooks[func] = {
1992
+ set: function(elem, value) {
1993
+ var transform = elem.transform || new $.transform(elem),
1994
+ funcs = {};
1995
+ funcs[func] = value;
1996
+ transform.exec(funcs, {preserve: true});
1997
+ },
1998
+ get: function(elem, computed) {
1999
+ var transform = elem.transform || new $.transform(elem);
2000
+ return transform.getAttr(func);
2001
+ }
2002
+ };
2003
+ });
2004
+
2005
+ // Support Reflection animation better by returning a matrix
2006
+ $.each(['reflect', 'reflectX', 'reflectXY', 'reflectY'], function(i, func) {
2007
+ $.cssHooks[func].get = function(elem, computed) {
2008
+ var transform = elem.transform || new $.transform(elem);
2009
+ return transform.getAttr('matrix') || $.cssDefault[func];
2010
+ };
2011
+ });
2012
+ })(jQuery, this, this.document);
2013
+ ///////////////////////////////////////////////////////
2014
+ // Animation
2015
+ ///////////////////////////////////////////////////////
2016
+ (function($, window, document, undefined) {
2017
+ /**
2018
+ * @var Regex looks for units on a string
2019
+ */
2020
+ var rfxnum = /^([+\-]=)?([\d+.\-]+)(.*)$/;
2021
+
2022
+ /**
2023
+ * Doctors prop values in the event that they contain spaces
2024
+ * @param Object prop
2025
+ * @param String speed
2026
+ * @param String easing
2027
+ * @param Function callback
2028
+ * @return bool
2029
+ */
2030
+ var _animate = $.fn.animate;
2031
+ $.fn.animate = function( prop, speed, easing, callback ) {
2032
+ var optall = $.speed(speed, easing, callback),
2033
+ mv = $.cssMultipleValues;
2034
+
2035
+ // Speed always creates a complete function that must be reset
2036
+ optall.complete = optall.old;
2037
+
2038
+ // Capture multiple values
2039
+ if (!$.isEmptyObject(prop)) {
2040
+ if (typeof optall.original === 'undefined') {
2041
+ optall.original = {};
2042
+ }
2043
+ $.each( prop, function( name, val ) {
2044
+ if (mv[name]
2045
+ || $.cssAngle[name]
2046
+ || (!$.cssNumber[name] && $.inArray(name, $.transform.funcs) !== -1)) {
2047
+
2048
+ // Handle special easing
2049
+ var specialEasing = null;
2050
+ if (jQuery.isArray(prop[name])) {
2051
+ var mvlen = 1, len = val.length;
2052
+ if (mv[name]) {
2053
+ mvlen = (typeof mv[name].length === 'undefined' ? mv[name] : mv[name].length);
2054
+ }
2055
+ if ( len > mvlen
2056
+ || (len < mvlen && len == 2)
2057
+ || (len == 2 && mvlen == 2 && isNaN(parseFloat(val[len - 1])))) {
2058
+
2059
+ specialEasing = val[len - 1];
2060
+ val.splice(len - 1, 1);
2061
+ }
2062
+ }
2063
+
2064
+ // Store the original values onto the optall
2065
+ optall.original[name] = val.toString();
2066
+
2067
+ // reduce to a unitless number (to trick animate)
2068
+ prop[name] = parseFloat(val);
2069
+ }
2070
+ } );
2071
+ }
2072
+
2073
+ //NOTE: we edited prop above to trick animate
2074
+ //NOTE: we pre-convert to an optall so we can doctor it
2075
+ return _animate.apply(this, [arguments[0], optall]);
2076
+ };
2077
+
2078
+ var prop = 'paddingBottom';
2079
+ function cur(elem, prop) {
2080
+ if ( elem[prop] != null && (!elem.style || elem.style[prop] == null) ) {
2081
+ //return elem[ prop ];
2082
+ }
2083
+
2084
+ var r = parseFloat( $.css( elem, prop ) );
2085
+ return r && r > -10000 ? r : 0;
2086
+ }
2087
+
2088
+ var _custom = $.fx.prototype.custom;
2089
+ $.fx.prototype.custom = function(from, to, unit) {
2090
+ var multiple = $.cssMultipleValues[this.prop],
2091
+ angle = $.cssAngle[this.prop];
2092
+
2093
+ //TODO: simply check for the existence of CSS Hooks?
2094
+ if (multiple || (!$.cssNumber[this.prop] && $.inArray(this.prop, $.transform.funcs) !== -1)) {
2095
+ this.values = [];
2096
+
2097
+ if (!multiple) {
2098
+ multiple = 1;
2099
+ }
2100
+
2101
+ // Pull out the known values
2102
+ var values = this.options.original[this.prop],
2103
+ currentValues = $(this.elem).css(this.prop),
2104
+ defaultValues = $.cssDefault[this.prop] || 0;
2105
+
2106
+ // make sure the current css value is an array
2107
+ if (!$.isArray(currentValues)) {
2108
+ currentValues = [currentValues];
2109
+ }
2110
+
2111
+ // make sure the new values are an array
2112
+ if (!$.isArray(values)) {
2113
+ if ($.type(values) === 'string') {
2114
+ values = values.split(',');
2115
+ } else {
2116
+ values = [values];
2117
+ }
2118
+ }
2119
+
2120
+ // make sure we have enough new values
2121
+ var length = multiple.length || multiple, i = 0;
2122
+ while (values.length < length) {
2123
+ values.push(multiple.duplicate ? values[0] : defaultValues[i] || 0);
2124
+ i++;
2125
+ }
2126
+
2127
+ // calculate a start, end and unit for each new value
2128
+ var start, parts, end, //unit,
2129
+ fx = this,
2130
+ transform = fx.elem.transform;
2131
+ orig = $.style(fx.elem, prop);
2132
+
2133
+ $.each(values, function(i, val) {
2134
+ // find a sensible start value
2135
+ if (currentValues[i]) {
2136
+ start = currentValues[i];
2137
+ } else if (defaultValues[i] && !multiple.duplicate) {
2138
+ start = defaultValues[i];
2139
+ } else if (multiple.duplicate) {
2140
+ start = currentValues[0];
2141
+ } else {
2142
+ start = 0;
2143
+ }
2144
+
2145
+ // Force the correct unit on the start
2146
+ if (angle) {
2147
+ start = $.angle.toDegree(start);
2148
+ } else if (!$.cssNumber[fx.prop]) {
2149
+ parts = rfxnum.exec($.trim(start));
2150
+ if (parts[3] && parts[3] !== 'px') {
2151
+ if (parts[3] === '%') {
2152
+ start = parseFloat( parts[2] ) / 100 * transform['safeOuter' + (i ? 'Height' : 'Width')]();
2153
+ } else {
2154
+ $.style( fx.elem, prop, start);
2155
+ start = cur(fx.elem, prop);
2156
+ $.style( fx.elem, prop, orig);
2157
+ }
2158
+ }
2159
+ }
2160
+ start = parseFloat(start);
2161
+
2162
+ // parse the value with a regex
2163
+ parts = rfxnum.exec($.trim(val));
2164
+
2165
+ if (parts) {
2166
+ // we found a sensible value and unit
2167
+ end = parseFloat( parts[2] );
2168
+ unit = parts[3] || "px"; //TODO: change to an appropriate default unit
2169
+
2170
+ if (angle) {
2171
+ end = $.angle.toDegree(end + unit);
2172
+ unit = 'deg';
2173
+ } else if (!$.cssNumber[fx.prop] && unit === '%') {
2174
+ start = (start / transform['safeOuter' + (i ? 'Height' : 'Width')]()) * 100;
2175
+ } else if (!$.cssNumber[fx.prop] && unit !== 'px') {
2176
+ $.style( fx.elem, prop, (end || 1) + unit);
2177
+ start = ((end || 1) / cur(fx.elem, prop)) * start;
2178
+ $.style( fx.elem, prop, orig);
2179
+ }
2180
+
2181
+ // If a +=/-= token was provided, we're doing a relative animation
2182
+ if (parts[1]) {
2183
+ end = ((parts[1] === "-=" ? -1 : 1) * end) + start;
2184
+ }
2185
+ } else {
2186
+ // I don't know when this would happen
2187
+ end = val;
2188
+ unit = '';
2189
+ }
2190
+
2191
+ // Save the values
2192
+ fx.values.push({
2193
+ start: start,
2194
+ end: end,
2195
+ unit: unit
2196
+ });
2197
+ });
2198
+ }
2199
+ return _custom.apply(this, arguments);
2200
+ };
2201
+
2202
+ /**
2203
+ * Animates a multi value attribute
2204
+ * @param Object fx
2205
+ * @return null
2206
+ */
2207
+ $.fx.multipleValueStep = {
2208
+ _default: function(fx) {
2209
+ $.each(fx.values, function(i, val) {
2210
+ fx.values[i].now = val.start + ((val.end - val.start) * fx.pos);
2211
+ });
2212
+ }
2213
+ };
2214
+ $.each(['matrix', 'reflect', 'reflectX', 'reflectXY', 'reflectY'], function(i, func) {
2215
+ $.fx.multipleValueStep[func] = function(fx) {
2216
+ var d = fx.decomposed,
2217
+ $m = $.matrix;
2218
+ m = $m.identity();
2219
+
2220
+ d.now = {};
2221
+
2222
+ // increment each part of the decomposition and recompose it
2223
+ $.each(d.start, function(k) {
2224
+ // calculate the current value
2225
+ d.now[k] = parseFloat(d.start[k]) + ((parseFloat(d.end[k]) - parseFloat(d.start[k])) * fx.pos);
2226
+
2227
+ // skip functions that won't affect the transform
2228
+ if (((k === 'scaleX' || k === 'scaleY') && d.now[k] === 1) ||
2229
+ (k !== 'scaleX' && k !== 'scaleY' && d.now[k] === 0)) {
2230
+ return true;
2231
+ }
2232
+
2233
+ // calculating
2234
+ m = m.x($m[k](d.now[k]));
2235
+ });
2236
+
2237
+ // save the correct matrix values for the value of now
2238
+ var val;
2239
+ $.each(fx.values, function(i) {
2240
+ switch (i) {
2241
+ case 0: val = parseFloat(m.e(1, 1).toFixed(6)); break;
2242
+ case 1: val = parseFloat(m.e(2, 1).toFixed(6)); break;
2243
+ case 2: val = parseFloat(m.e(1, 2).toFixed(6)); break;
2244
+ case 3: val = parseFloat(m.e(2, 2).toFixed(6)); break;
2245
+ case 4: val = parseFloat(m.e(1, 3).toFixed(6)); break;
2246
+ case 5: val = parseFloat(m.e(2, 3).toFixed(6)); break;
2247
+ }
2248
+ fx.values[i].now = val;
2249
+ });
2250
+ };
2251
+ });
2252
+ /**
2253
+ * Step for animating tranformations
2254
+ */
2255
+ $.each($.transform.funcs, function(i, func) {
2256
+ $.fx.step[func] = function(fx) {
2257
+ var transform = fx.elem.transform || new $.transform(fx.elem),
2258
+ funcs = {};
2259
+
2260
+ if ($.cssMultipleValues[func] || (!$.cssNumber[func] && $.inArray(func, $.transform.funcs) !== -1)) {
2261
+ ($.fx.multipleValueStep[fx.prop] || $.fx.multipleValueStep._default)(fx);
2262
+ funcs[fx.prop] = [];
2263
+ $.each(fx.values, function(i, val) {
2264
+ funcs[fx.prop].push(val.now + ($.cssNumber[fx.prop] ? '' : val.unit));
2265
+ });
2266
+ } else {
2267
+ funcs[fx.prop] = fx.now + ($.cssNumber[fx.prop] ? '' : fx.unit);
2268
+ }
2269
+
2270
+ transform.exec(funcs, {preserve: true});
2271
+ };
2272
+ });
2273
+
2274
+ // Support matrix animation
2275
+ $.each(['matrix', 'reflect', 'reflectX', 'reflectXY', 'reflectY'], function(i, func) {
2276
+ $.fx.step[func] = function(fx) {
2277
+ var transform = fx.elem.transform || new $.transform(fx.elem),
2278
+ funcs = {};
2279
+
2280
+ if (!fx.initialized) {
2281
+ fx.initialized = true;
2282
+
2283
+ // Reflections need a sensible end value set
2284
+ if (func !== 'matrix') {
2285
+ var values = $.matrix[func]().elements;
2286
+ var val;
2287
+ $.each(fx.values, function(i) {
2288
+ switch (i) {
2289
+ case 0: val = values[0]; break;
2290
+ case 1: val = values[2]; break;
2291
+ case 2: val = values[1]; break;
2292
+ case 3: val = values[3]; break;
2293
+ default: val = 0;
2294
+ }
2295
+ fx.values[i].end = val;
2296
+ });
2297
+ }
2298
+
2299
+ // Decompose the start and end
2300
+ fx.decomposed = {};
2301
+ var v = fx.values;
2302
+
2303
+ fx.decomposed.start = $.matrix.matrix(v[0].start, v[1].start, v[2].start, v[3].start, v[4].start, v[5].start).decompose();
2304
+ fx.decomposed.end = $.matrix.matrix(v[0].end, v[1].end, v[2].end, v[3].end, v[4].end, v[5].end).decompose();
2305
+ }
2306
+
2307
+ ($.fx.multipleValueStep[fx.prop] || $.fx.multipleValueStep._default)(fx);
2308
+ funcs.matrix = [];
2309
+ $.each(fx.values, function(i, val) {
2310
+ funcs.matrix.push(val.now);
2311
+ });
2312
+
2313
+ transform.exec(funcs, {preserve: true});
2314
+ };
2315
+ });
2316
+ })(jQuery, this, this.document);
2317
+ ///////////////////////////////////////////////////////
2318
+ // Angle
2319
+ ///////////////////////////////////////////////////////
2320
+ (function($, window, document, undefined) {
2321
+ /**
2322
+ * Converting a radian to a degree
2323
+ * @const
2324
+ */
2325
+ var RAD_DEG = 180/Math.PI;
2326
+
2327
+ /**
2328
+ * Converting a radian to a grad
2329
+ * @const
2330
+ */
2331
+ var RAD_GRAD = 200/Math.PI;
2332
+
2333
+ /**
2334
+ * Converting a degree to a radian
2335
+ * @const
2336
+ */
2337
+ var DEG_RAD = Math.PI/180;
2338
+
2339
+ /**
2340
+ * Converting a degree to a grad
2341
+ * @const
2342
+ */
2343
+ var DEG_GRAD = 2/1.8;
2344
+
2345
+ /**
2346
+ * Converting a grad to a degree
2347
+ * @const
2348
+ */
2349
+ var GRAD_DEG = 0.9;
2350
+
2351
+ /**
2352
+ * Converting a grad to a radian
2353
+ * @const
2354
+ */
2355
+ var GRAD_RAD = Math.PI/200;
2356
+
2357
+
2358
+ var rfxnum = /^([+\-]=)?([\d+.\-]+)(.*)$/;
2359
+
2360
+ /**
2361
+ * Functions for converting angles
2362
+ * @var Object
2363
+ */
2364
+ $.extend({
2365
+ angle: {
2366
+ /**
2367
+ * available units for an angle
2368
+ * @var Regex
2369
+ */
2370
+ runit: /(deg|g?rad)/,
2371
+
2372
+ /**
2373
+ * Convert a radian into a degree
2374
+ * @param Number rad
2375
+ * @return Number
2376
+ */
2377
+ radianToDegree: function(rad) {
2378
+ return rad * RAD_DEG;
2379
+ },
2380
+
2381
+ /**
2382
+ * Convert a radian into a degree
2383
+ * @param Number rad
2384
+ * @return Number
2385
+ */
2386
+ radianToGrad: function(rad) {
2387
+ return rad * RAD_GRAD;
2388
+ },
2389
+
2390
+ /**
2391
+ * Convert a degree into a radian
2392
+ * @param Number deg
2393
+ * @return Number
2394
+ */
2395
+ degreeToRadian: function(deg) {
2396
+ return deg * DEG_RAD;
2397
+ },
2398
+
2399
+ /**
2400
+ * Convert a degree into a radian
2401
+ * @param Number deg
2402
+ * @return Number
2403
+ */
2404
+ degreeToGrad: function(deg) {
2405
+ return deg * DEG_GRAD;
2406
+ },
2407
+
2408
+ /**
2409
+ * Convert a grad into a degree
2410
+ * @param Number grad
2411
+ * @return Number
2412
+ */
2413
+ gradToDegree: function(grad) {
2414
+ return grad * GRAD_DEG;
2415
+ },
2416
+
2417
+ /**
2418
+ * Convert a grad into a radian
2419
+ * @param Number grad
2420
+ * @return Number
2421
+ */
2422
+ gradToRadian: function(grad) {
2423
+ return grad * GRAD_RAD;
2424
+ },
2425
+
2426
+ /**
2427
+ * Convert an angle with a unit to a degree
2428
+ * @param String val angle with a unit
2429
+ * @return Number
2430
+ */
2431
+ toDegree: function (val) {
2432
+ var parts = rfxnum.exec(val);
2433
+ if (parts) {
2434
+ val = parseFloat( parts[2] );
2435
+ switch (parts[3] || 'deg') {
2436
+ case 'grad':
2437
+ val = $.angle.gradToDegree(val);
2438
+ break;
2439
+ case 'rad':
2440
+ val = $.angle.radianToDegree(val);
2441
+ break;
2442
+ }
2443
+ return val;
2444
+ }
2445
+ return 0;
2446
+ }
2447
+ }
2448
+ });
2449
+ })(jQuery, this, this.document);
2450
+ ///////////////////////////////////////////////////////
2451
+ // Matrix
2452
+ ///////////////////////////////////////////////////////
2453
+ (function($, window, document, undefined) {
2454
+ /**
2455
+ * Matrix object for creating matrices relevant for 2d Transformations
2456
+ * @var Object
2457
+ */
2458
+ if (typeof($.matrix) == 'undefined') {
2459
+ $.extend({
2460
+ matrix: {}
2461
+ });
2462
+ }
2463
+ var $m = $.matrix;
2464
+
2465
+ $.extend( $m, {
2466
+ /**
2467
+ * A 2-value vector
2468
+ * @param Number x
2469
+ * @param Number y
2470
+ * @constructor
2471
+ */
2472
+ V2: function(x, y){
2473
+ if ($.isArray(arguments[0])) {
2474
+ this.elements = arguments[0].slice(0, 2);
2475
+ } else {
2476
+ this.elements = [x, y];
2477
+ }
2478
+ this.length = 2;
2479
+ },
2480
+
2481
+ /**
2482
+ * A 2-value vector
2483
+ * @param Number x
2484
+ * @param Number y
2485
+ * @param Number z
2486
+ * @constructor
2487
+ */
2488
+ V3: function(x, y, z){
2489
+ if ($.isArray(arguments[0])) {
2490
+ this.elements = arguments[0].slice(0, 3);
2491
+ } else {
2492
+ this.elements = [x, y, z];
2493
+ }
2494
+ this.length = 3;
2495
+ },
2496
+
2497
+ /**
2498
+ * A 2x2 Matrix, useful for 2D-transformations without translations
2499
+ * @param Number mn
2500
+ * @constructor
2501
+ */
2502
+ M2x2: function(m11, m12, m21, m22) {
2503
+ if ($.isArray(arguments[0])) {
2504
+ this.elements = arguments[0].slice(0, 4);
2505
+ } else {
2506
+ this.elements = Array.prototype.slice.call(arguments).slice(0, 4);
2507
+ }
2508
+ this.rows = 2;
2509
+ this.cols = 2;
2510
+ },
2511
+
2512
+ /**
2513
+ * A 3x3 Matrix, useful for 3D-transformations without translations
2514
+ * @param Number mn
2515
+ * @constructor
2516
+ */
2517
+ M3x3: function(m11, m12, m13, m21, m22, m23, m31, m32, m33) {
2518
+ if ($.isArray(arguments[0])) {
2519
+ this.elements = arguments[0].slice(0, 9);
2520
+ } else {
2521
+ this.elements = Array.prototype.slice.call(arguments).slice(0, 9);
2522
+ }
2523
+ this.rows = 3;
2524
+ this.cols = 3;
2525
+ }
2526
+ });
2527
+
2528
+ /** generic matrix prototype */
2529
+ var Matrix = {
2530
+ /**
2531
+ * Return a specific element from the matrix
2532
+ * @param Number row where 1 is the 0th row
2533
+ * @param Number col where 1 is the 0th column
2534
+ * @return Number
2535
+ */
2536
+ e: function(row, col) {
2537
+ var rows = this.rows,
2538
+ cols = this.cols;
2539
+
2540
+ // return 0 on nonsense rows and columns
2541
+ if (row > rows || col > rows || row < 1 || col < 1) {
2542
+ return 0;
2543
+ }
2544
+
2545
+ return this.elements[(row - 1) * cols + col - 1];
2546
+ },
2547
+
2548
+ /**
2549
+ * Taken from Zoomooz
2550
+ * https://github.com/jaukia/zoomooz/blob/c7a37b9a65a06ba730bd66391bbd6fe8e55d3a49/js/jquery.zoomooz.js
2551
+ */
2552
+ decompose: function() {
2553
+ var a = this.e(1, 1),
2554
+ b = this.e(2, 1),
2555
+ c = this.e(1, 2),
2556
+ d = this.e(2, 2),
2557
+ e = this.e(1, 3),
2558
+ f = this.e(2, 3);
2559
+
2560
+ // In case the matrix can't be decomposed
2561
+ if (Math.abs(a * d - b * c) < 0.01) {
2562
+ return {
2563
+ rotate: 0 + 'deg',
2564
+ skewX: 0 + 'deg',
2565
+ scaleX: 1,
2566
+ scaleY: 1,
2567
+ translateX: 0 + 'px',
2568
+ translateY: 0 + 'px'
2569
+ };
2570
+ }
2571
+
2572
+ // Translate is easy
2573
+ var tx = e, ty = f;
2574
+
2575
+ // factor out the X scale
2576
+ var sx = Math.sqrt(a * a + b * b);
2577
+ a = a/sx;
2578
+ b = b/sx;
2579
+
2580
+ // factor out the skew
2581
+ var k = a * c + b * d;
2582
+ c -= a * k;
2583
+ d -= b * k;
2584
+
2585
+ // factor out the Y scale
2586
+ var sy = Math.sqrt(c * c + d * d);
2587
+ c = c / sy;
2588
+ d = d / sy;
2589
+ k = k / sy;
2590
+
2591
+ // account for negative scale
2592
+ if ((a * d - b * c) < 0.0) {
2593
+ a = -a;
2594
+ b = -b;
2595
+ //c = -c; // accomplishes nothing to negate it
2596
+ //d = -d; // accomplishes nothing to negate it
2597
+ sx = -sx;
2598
+ //sy = -sy //Scale Y shouldn't ever be negated
2599
+ }
2600
+
2601
+ // calculate the rotation angle and skew angle
2602
+ var rad2deg = $.angle.radianToDegree;
2603
+ var r = rad2deg(Math.atan2(b, a));
2604
+ k = rad2deg(Math.atan(k));
2605
+
2606
+ return {
2607
+ rotate: r + 'deg',
2608
+ skewX: k + 'deg',
2609
+ scaleX: sx,
2610
+ scaleY: sy,
2611
+ translateX: tx + 'px',
2612
+ translateY: ty + 'px'
2613
+ };
2614
+ }
2615
+ };
2616
+
2617
+ /** Extend all of the matrix types with the same prototype */
2618
+ $.extend($m.M2x2.prototype, Matrix, {
2619
+ toM3x3: function() {
2620
+ var a = this.elements;
2621
+ return new $m.M3x3(
2622
+ a[0], a[1], 0,
2623
+ a[2], a[3], 0,
2624
+ 0, 0, 1
2625
+ );
2626
+ },
2627
+
2628
+ /**
2629
+ * Multiply a 2x2 matrix by a similar matrix or a vector
2630
+ * @param M2x2 | V2 matrix
2631
+ * @return M2x2 | V2
2632
+ */
2633
+ x: function(matrix) {
2634
+ var isVector = typeof(matrix.rows) === 'undefined';
2635
+
2636
+ // Ensure the right-sized matrix
2637
+ if (!isVector && matrix.rows == 3) {
2638
+ return this.toM3x3().x(matrix);
2639
+ }
2640
+
2641
+ var a = this.elements,
2642
+ b = matrix.elements;
2643
+
2644
+ if (isVector && b.length == 2) {
2645
+ // b is actually a vector
2646
+ return new $m.V2(
2647
+ a[0] * b[0] + a[1] * b[1],
2648
+ a[2] * b[0] + a[3] * b[1]
2649
+ );
2650
+ } else if (b.length == a.length) {
2651
+ // b is a 2x2 matrix
2652
+ return new $m.M2x2(
2653
+ a[0] * b[0] + a[1] * b[2],
2654
+ a[0] * b[1] + a[1] * b[3],
2655
+
2656
+ a[2] * b[0] + a[3] * b[2],
2657
+ a[2] * b[1] + a[3] * b[3]
2658
+ );
2659
+ }
2660
+ return false; // fail
2661
+ },
2662
+
2663
+ /**
2664
+ * Generates an inverse of the current matrix
2665
+ * @param void
2666
+ * @return M2x2
2667
+ * @link http://www.dr-lex.be/random/matrix_inv.html
2668
+ */
2669
+ inverse: function() {
2670
+ var d = 1/this.determinant(),
2671
+ a = this.elements;
2672
+ return new $m.M2x2(
2673
+ d * a[3], d * -a[1],
2674
+ d * -a[2], d * a[0]
2675
+ );
2676
+ },
2677
+
2678
+ /**
2679
+ * Calculates the determinant of the current matrix
2680
+ * @param void
2681
+ * @return Number
2682
+ * @link http://www.dr-lex.be/random/matrix_inv.html
2683
+ */
2684
+ determinant: function() {
2685
+ var a = this.elements;
2686
+ return a[0] * a[3] - a[1] * a[2];
2687
+ }
2688
+ });
2689
+
2690
+ $.extend($m.M3x3.prototype, Matrix, {
2691
+ /**
2692
+ * Multiply a 3x3 matrix by a similar matrix or a vector
2693
+ * @param M3x3 | V3 matrix
2694
+ * @return M3x3 | V3
2695
+ */
2696
+ x: function(matrix) {
2697
+ var isVector = typeof(matrix.rows) === 'undefined';
2698
+
2699
+ // Ensure the right-sized matrix
2700
+ if (!isVector && matrix.rows < 3) {
2701
+ matrix = matrix.toM3x3();
2702
+ }
2703
+
2704
+ var a = this.elements,
2705
+ b = matrix.elements;
2706
+
2707
+ if (isVector && b.length == 3) {
2708
+ // b is actually a vector
2709
+ return new $m.V3(
2710
+ a[0] * b[0] + a[1] * b[1] + a[2] * b[2],
2711
+ a[3] * b[0] + a[4] * b[1] + a[5] * b[2],
2712
+ a[6] * b[0] + a[7] * b[1] + a[8] * b[2]
2713
+ );
2714
+ } else if (b.length == a.length) {
2715
+ // b is a 3x3 matrix
2716
+ return new $m.M3x3(
2717
+ a[0] * b[0] + a[1] * b[3] + a[2] * b[6],
2718
+ a[0] * b[1] + a[1] * b[4] + a[2] * b[7],
2719
+ a[0] * b[2] + a[1] * b[5] + a[2] * b[8],
2720
+
2721
+ a[3] * b[0] + a[4] * b[3] + a[5] * b[6],
2722
+ a[3] * b[1] + a[4] * b[4] + a[5] * b[7],
2723
+ a[3] * b[2] + a[4] * b[5] + a[5] * b[8],
2724
+
2725
+ a[6] * b[0] + a[7] * b[3] + a[8] * b[6],
2726
+ a[6] * b[1] + a[7] * b[4] + a[8] * b[7],
2727
+ a[6] * b[2] + a[7] * b[5] + a[8] * b[8]
2728
+ );
2729
+ }
2730
+ return false; // fail
2731
+ },
2732
+
2733
+ /**
2734
+ * Generates an inverse of the current matrix
2735
+ * @param void
2736
+ * @return M3x3
2737
+ * @link http://www.dr-lex.be/random/matrix_inv.html
2738
+ */
2739
+ inverse: function() {
2740
+ var d = 1/this.determinant(),
2741
+ a = this.elements;
2742
+ return new $m.M3x3(
2743
+ d * ( a[8] * a[4] - a[7] * a[5]),
2744
+ d * (-(a[8] * a[1] - a[7] * a[2])),
2745
+ d * ( a[5] * a[1] - a[4] * a[2]),
2746
+
2747
+ d * (-(a[8] * a[3] - a[6] * a[5])),
2748
+ d * ( a[8] * a[0] - a[6] * a[2]),
2749
+ d * (-(a[5] * a[0] - a[3] * a[2])),
2750
+
2751
+ d * ( a[7] * a[3] - a[6] * a[4]),
2752
+ d * (-(a[7] * a[0] - a[6] * a[1])),
2753
+ d * ( a[4] * a[0] - a[3] * a[1])
2754
+ );
2755
+ },
2756
+
2757
+ /**
2758
+ * Calculates the determinant of the current matrix
2759
+ * @param void
2760
+ * @return Number
2761
+ * @link http://www.dr-lex.be/random/matrix_inv.html
2762
+ */
2763
+ determinant: function() {
2764
+ var a = this.elements;
2765
+ return a[0] * (a[8] * a[4] - a[7] * a[5]) - a[3] * (a[8] * a[1] - a[7] * a[2]) + a[6] * (a[5] * a[1] - a[4] * a[2]);
2766
+ }
2767
+ });
2768
+
2769
+ /** generic vector prototype */
2770
+ var Vector = {
2771
+ /**
2772
+ * Return a specific element from the vector
2773
+ * @param Number i where 1 is the 0th value
2774
+ * @return Number
2775
+ */
2776
+ e: function(i) {
2777
+ return this.elements[i - 1];
2778
+ }
2779
+ };
2780
+
2781
+ /** Extend all of the vector types with the same prototype */
2782
+ $.extend($m.V2.prototype, Vector);
2783
+ $.extend($m.V3.prototype, Vector);
2784
+ })(jQuery, this, this.document);
2785
+ ///////////////////////////////////////////////////////
2786
+ // Matrix Calculations
2787
+ ///////////////////////////////////////////////////////
2788
+ (function($, window, document, undefined) {
2789
+ /**
2790
+ * Matrix object for creating matrices relevant for 2d Transformations
2791
+ * @var Object
2792
+ */
2793
+ if (typeof($.matrix) == 'undefined') {
2794
+ $.extend({
2795
+ matrix: {}
2796
+ });
2797
+ }
2798
+
2799
+ $.extend( $.matrix, {
2800
+ /**
2801
+ * Class for calculating coordinates on a matrix
2802
+ * @param Matrix matrix
2803
+ * @param Number outerHeight
2804
+ * @param Number outerWidth
2805
+ * @constructor
2806
+ */
2807
+ calc: function(matrix, outerHeight, outerWidth) {
2808
+ /**
2809
+ * @var Matrix
2810
+ */
2811
+ this.matrix = matrix;
2812
+
2813
+ /**
2814
+ * @var Number
2815
+ */
2816
+ this.outerHeight = outerHeight;
2817
+
2818
+ /**
2819
+ * @var Number
2820
+ */
2821
+ this.outerWidth = outerWidth;
2822
+ }
2823
+ });
2824
+
2825
+ $.matrix.calc.prototype = {
2826
+ /**
2827
+ * Calculate a coord on the new object
2828
+ * @return Object
2829
+ */
2830
+ coord: function(x, y, z) {
2831
+ //default z and w
2832
+ z = typeof(z) !== 'undefined' ? z : 0;
2833
+
2834
+ var matrix = this.matrix,
2835
+ vector;
2836
+
2837
+ switch (matrix.rows) {
2838
+ case 2:
2839
+ vector = matrix.x(new $.matrix.V2(x, y));
2840
+ break;
2841
+ case 3:
2842
+ vector = matrix.x(new $.matrix.V3(x, y, z));
2843
+ break;
2844
+ }
2845
+
2846
+ return vector;
2847
+ },
2848
+
2849
+ /**
2850
+ * Calculate the corners of the new object
2851
+ * @return Object
2852
+ */
2853
+ corners: function(x, y) {
2854
+ // Try to save the corners if this is called a lot
2855
+ var save = !(typeof(x) !=='undefined' || typeof(y) !=='undefined'),
2856
+ c;
2857
+ if (!this.c || !save) {
2858
+ y = y || this.outerHeight;
2859
+ x = x || this.outerWidth;
2860
+
2861
+ c = {
2862
+ tl: this.coord(0, 0),
2863
+ bl: this.coord(0, y),
2864
+ tr: this.coord(x, 0),
2865
+ br: this.coord(x, y)
2866
+ };
2867
+ } else {
2868
+ c = this.c;
2869
+ }
2870
+
2871
+ if (save) {
2872
+ this.c = c;
2873
+ }
2874
+ return c;
2875
+ },
2876
+
2877
+ /**
2878
+ * Calculate the sides of the new object
2879
+ * @return Object
2880
+ */
2881
+ sides: function(corners) {
2882
+ // The corners of the box
2883
+ var c = corners || this.corners();
2884
+
2885
+ return {
2886
+ top: Math.min(c.tl.e(2), c.tr.e(2), c.br.e(2), c.bl.e(2)),
2887
+ bottom: Math.max(c.tl.e(2), c.tr.e(2), c.br.e(2), c.bl.e(2)),
2888
+ left: Math.min(c.tl.e(1), c.tr.e(1), c.br.e(1), c.bl.e(1)),
2889
+ right: Math.max(c.tl.e(1), c.tr.e(1), c.br.e(1), c.bl.e(1))
2890
+ };
2891
+ },
2892
+
2893
+ /**
2894
+ * Calculate the offset of the new object
2895
+ * @return Object
2896
+ */
2897
+ offset: function(corners) {
2898
+ // The corners of the box
2899
+ var s = this.sides(corners);
2900
+
2901
+ // return size
2902
+ return {
2903
+ height: Math.abs(s.bottom - s.top),
2904
+ width: Math.abs(s.right - s.left)
2905
+ };
2906
+ },
2907
+
2908
+ /**
2909
+ * Calculate the area of the new object
2910
+ * @return Number
2911
+ * @link http://en.wikipedia.org/wiki/Quadrilateral#Area_of_a_convex_quadrilateral
2912
+ */
2913
+ area: function(corners) {
2914
+ // The corners of the box
2915
+ var c = corners || this.corners();
2916
+
2917
+ // calculate the two diagonal vectors
2918
+ var v1 = {
2919
+ x: c.tr.e(1) - c.tl.e(1) + c.br.e(1) - c.bl.e(1),
2920
+ y: c.tr.e(2) - c.tl.e(2) + c.br.e(2) - c.bl.e(2)
2921
+ },
2922
+ v2 = {
2923
+ x: c.bl.e(1) - c.tl.e(1) + c.br.e(1) - c.tr.e(1),
2924
+ y: c.bl.e(2) - c.tl.e(2) + c.br.e(2) - c.tr.e(2)
2925
+ };
2926
+
2927
+ return 0.25 * Math.abs(v1.e(1) * v2.e(2) - v1.e(2) * v2.e(1));
2928
+ },
2929
+
2930
+ /**
2931
+ * Calculate the non-affinity of the new object
2932
+ * @return Number
2933
+ */
2934
+ nonAffinity: function() {
2935
+ // The corners of the box
2936
+ var sides = this.sides(),
2937
+ xDiff = sides.top - sides.bottom,
2938
+ yDiff = sides.left - sides.right;
2939
+
2940
+ return parseFloat(parseFloat(Math.abs(
2941
+ (Math.pow(xDiff, 2) + Math.pow(yDiff, 2)) /
2942
+ (sides.top * sides.bottom + sides.left * sides.right)
2943
+ )).toFixed(8));
2944
+ },
2945
+
2946
+ /**
2947
+ * Calculate a proper top and left for IE
2948
+ * @param Object toOrigin
2949
+ * @param Object fromOrigin
2950
+ * @return Object
2951
+ */
2952
+ originOffset: function(toOrigin, fromOrigin) {
2953
+ // the origin to translate to
2954
+ toOrigin = toOrigin ? toOrigin : new $.matrix.V2(
2955
+ this.outerWidth * 0.5,
2956
+ this.outerHeight * 0.5
2957
+ );
2958
+
2959
+ // the origin to translate from (IE has a fixed origin of 0, 0)
2960
+ fromOrigin = fromOrigin ? fromOrigin : new $.matrix.V2(
2961
+ 0,
2962
+ 0
2963
+ );
2964
+
2965
+ // transform the origins
2966
+ var toCenter = this.coord(toOrigin.e(1), toOrigin.e(2));
2967
+ var fromCenter = this.coord(fromOrigin.e(1), fromOrigin.e(2));
2968
+
2969
+ // return the offset
2970
+ return {
2971
+ top: (fromCenter.e(2) - fromOrigin.e(2)) - (toCenter.e(2) - toOrigin.e(2)),
2972
+ left: (fromCenter.e(1) - fromOrigin.e(1)) - (toCenter.e(1) - toOrigin.e(1))
2973
+ };
2974
+ }
2975
+ };
2976
+ })(jQuery, this, this.document);
2977
+ ///////////////////////////////////////////////////////
2978
+ // 2d Matrix Functions
2979
+ ///////////////////////////////////////////////////////
2980
+ (function($, window, document, undefined) {
2981
+ /**
2982
+ * Matrix object for creating matrices relevant for 2d Transformations
2983
+ * @var Object
2984
+ */
2985
+ if (typeof($.matrix) == 'undefined') {
2986
+ $.extend({
2987
+ matrix: {}
2988
+ });
2989
+ }
2990
+ var $m = $.matrix,
2991
+ $m2x2 = $m.M2x2,
2992
+ $m3x3 = $m.M3x3;
2993
+
2994
+ $.extend( $m, {
2995
+ /**
2996
+ * Identity matrix
2997
+ * @param Number size
2998
+ * @return Matrix
2999
+ */
3000
+ identity: function(size) {
3001
+ size = size || 2;
3002
+ var length = size * size,
3003
+ elements = new Array(length),
3004
+ mod = size + 1;
3005
+ for (var i = 0; i < length; i++) {
3006
+ elements[i] = (i % mod) === 0 ? 1 : 0;
3007
+ }
3008
+ return new $m['M'+size+'x'+size](elements);
3009
+ },
3010
+
3011
+ /**
3012
+ * Matrix
3013
+ * @return Matrix
3014
+ */
3015
+ matrix: function() {
3016
+ var args = Array.prototype.slice.call(arguments);
3017
+ // arguments are in column-major order
3018
+ switch (arguments.length) {
3019
+ case 4:
3020
+ return new $m2x2(
3021
+ args[0], args[2],
3022
+ args[1], args[3]
3023
+ );
3024
+ case 6:
3025
+ return new $m3x3(
3026
+ args[0], args[2], args[4],
3027
+ args[1], args[3], args[5],
3028
+ 0, 0, 1
3029
+ );
3030
+ }
3031
+ },
3032
+
3033
+ /**
3034
+ * Reflect (same as rotate(180))
3035
+ * @return Matrix
3036
+ */
3037
+ reflect: function() {
3038
+ return new $m2x2(
3039
+ -1, 0,
3040
+ 0, -1
3041
+ );
3042
+ },
3043
+
3044
+ /**
3045
+ * Reflect across the x-axis (mirrored upside down)
3046
+ * @return Matrix
3047
+ */
3048
+ reflectX: function() {
3049
+ return new $m2x2(
3050
+ 1, 0,
3051
+ 0, -1
3052
+ );
3053
+ },
3054
+
3055
+ /**
3056
+ * Reflect by swapping x an y (same as reflectX + rotate(-90))
3057
+ * @return Matrix
3058
+ */
3059
+ reflectXY: function() {
3060
+ return new $m2x2(
3061
+ 0, 1,
3062
+ 1, 0
3063
+ );
3064
+ },
3065
+
3066
+ /**
3067
+ * Reflect across the y-axis (mirrored)
3068
+ * @return Matrix
3069
+ */
3070
+ reflectY: function() {
3071
+ return new $m2x2(
3072
+ -1, 0,
3073
+ 0, 1
3074
+ );
3075
+ },
3076
+
3077
+ /**
3078
+ * Rotates around the origin
3079
+ * @param Number deg
3080
+ * @return Matrix
3081
+ * @link http://www.w3.org/TR/SVG/coords.html#RotationDefined
3082
+ */
3083
+ rotate: function(deg) {
3084
+ //TODO: detect units
3085
+ var rad = $.angle.degreeToRadian(deg),
3086
+ costheta = Math.cos(rad),
3087
+ sintheta = Math.sin(rad);
3088
+
3089
+ var a = costheta,
3090
+ b = sintheta,
3091
+ c = -sintheta,
3092
+ d = costheta;
3093
+
3094
+ return new $m2x2(
3095
+ a, c,
3096
+ b, d
3097
+ );
3098
+ },
3099
+
3100
+ /**
3101
+ * Scale
3102
+ * @param Number sx
3103
+ * @param Number sy
3104
+ * @return Matrix
3105
+ * @link http://www.w3.org/TR/SVG/coords.html#ScalingDefined
3106
+ */
3107
+ scale: function (sx, sy) {
3108
+ sx = sx || sx === 0 ? sx : 1;
3109
+ sy = sy || sy === 0 ? sy : sx;
3110
+
3111
+ return new $m2x2(
3112
+ sx, 0,
3113
+ 0, sy
3114
+ );
3115
+ },
3116
+
3117
+ /**
3118
+ * Scale on the X-axis
3119
+ * @param Number sx
3120
+ * @return Matrix
3121
+ */
3122
+ scaleX: function (sx) {
3123
+ return $m.scale(sx, 1);
3124
+ },
3125
+
3126
+ /**
3127
+ * Scale on the Y-axis
3128
+ * @param Number sy
3129
+ * @return Matrix
3130
+ */
3131
+ scaleY: function (sy) {
3132
+ return $m.scale(1, sy);
3133
+ },
3134
+
3135
+ /**
3136
+ * Skews on the X-axis and Y-axis
3137
+ * @param Number degX
3138
+ * @param Number degY
3139
+ * @return Matrix
3140
+ */
3141
+ skew: function (degX, degY) {
3142
+ degX = degX || 0;
3143
+ degY = degY || 0;
3144
+
3145
+ //TODO: detect units
3146
+ var radX = $.angle.degreeToRadian(degX),
3147
+ radY = $.angle.degreeToRadian(degY),
3148
+ x = Math.tan(radX),
3149
+ y = Math.tan(radY);
3150
+
3151
+ return new $m2x2(
3152
+ 1, x,
3153
+ y, 1
3154
+ );
3155
+ },
3156
+
3157
+ /**
3158
+ * Skews on the X-axis
3159
+ * @param Number degX
3160
+ * @return Matrix
3161
+ * @link http://www.w3.org/TR/SVG/coords.html#SkewXDefined
3162
+ */
3163
+ skewX: function (degX) {
3164
+ return $m.skew(degX);
3165
+ },
3166
+
3167
+ /**
3168
+ * Skews on the Y-axis
3169
+ * @param Number degY
3170
+ * @return Matrix
3171
+ * @link http://www.w3.org/TR/SVG/coords.html#SkewYDefined
3172
+ */
3173
+ skewY: function (degY) {
3174
+ return $m.skew(0, degY);
3175
+ },
3176
+
3177
+ /**
3178
+ * Translate
3179
+ * @param Number tx
3180
+ * @param Number ty
3181
+ * @return Matrix
3182
+ * @link http://www.w3.org/TR/SVG/coords.html#TranslationDefined
3183
+ */
3184
+ translate: function (tx, ty) {
3185
+ tx = tx || 0;
3186
+ ty = ty || 0;
3187
+
3188
+ return new $m3x3(
3189
+ 1, 0, tx,
3190
+ 0, 1, ty,
3191
+ 0, 0, 1
3192
+ );
3193
+ },
3194
+
3195
+ /**
3196
+ * Translate on the X-axis
3197
+ * @param Number tx
3198
+ * @return Matrix
3199
+ * @link http://www.w3.org/TR/SVG/coords.html#TranslationDefined
3200
+ */
3201
+ translateX: function (tx) {
3202
+ return $m.translate(tx);
3203
+ },
3204
+
3205
+ /**
3206
+ * Translate on the Y-axis
3207
+ * @param Number ty
3208
+ * @return Matrix
3209
+ * @link http://www.w3.org/TR/SVG/coords.html#TranslationDefined
3210
+ */
3211
+ translateY: function (ty) {
3212
+ return $m.translate(0, ty);
3213
+ }
3214
+ });
3215
+ })(jQuery, this, this.document);
3216
+ /*! Copyright (c) 2010 Brandon Aaron (http://brandonaaron.net)
3217
+ * Licensed under the MIT License (LICENSE.txt).
3218
+ *
3219
+ * Thanks to: http://adomas.org/javascript-mouse-wheel/ for some pointers.
3220
+ * Thanks to: Mathias Bank(http://www.mathias-bank.de) for a scope bug fix.
3221
+ * Thanks to: Seamus Leahy for adding deltaX and deltaY
3222
+ *
3223
+ * Version: 3.0.4
3224
+ *
3225
+ * Requires: 1.2.2+
3226
+ */
3227
+
3228
+
3229
+ (function($) {
3230
+
3231
+ var types = ['DOMMouseScroll', 'mousewheel'];
3232
+
3233
+ $.event.special.mousewheel = {
3234
+ setup: function() {
3235
+ if ( this.addEventListener ) {
3236
+ for ( var i=types.length; i; ) {
3237
+ this.addEventListener( types[--i], handler, false );
3238
+ }
3239
+ } else {
3240
+ this.onmousewheel = handler;
3241
+ }
3242
+ },
3243
+
3244
+ teardown: function() {
3245
+ if ( this.removeEventListener ) {
3246
+ for ( var i=types.length; i; ) {
3247
+ this.removeEventListener( types[--i], handler, false );
3248
+ }
3249
+ } else {
3250
+ this.onmousewheel = null;
3251
+ }
3252
+ }
3253
+ };
3254
+
3255
+ $.fn.extend({
3256
+ mousewheel: function(fn) {
3257
+ return fn ? this.bind("mousewheel", fn) : this.trigger("mousewheel");
3258
+ },
3259
+
3260
+ unmousewheel: function(fn) {
3261
+ return this.unbind("mousewheel", fn);
3262
+ }
3263
+ });
3264
+
3265
+
3266
+ function handler(event) {
3267
+ var orgEvent = event || window.event, args = [].slice.call( arguments, 1 ), delta = 0, returnValue = true, deltaX = 0, deltaY = 0;
3268
+ event = $.event.fix(orgEvent);
3269
+ event.type = "mousewheel";
3270
+
3271
+ // Old school scrollwheel delta
3272
+ if ( event.wheelDelta ) { delta = event.wheelDelta/120; }
3273
+ if ( event.detail ) { delta = -event.detail/3; }
3274
+
3275
+ // New school multidimensional scroll (touchpads) deltas
3276
+ deltaY = delta;
3277
+
3278
+ // Gecko
3279
+ if ( orgEvent.axis !== undefined && orgEvent.axis === orgEvent.HORIZONTAL_AXIS ) {
3280
+ deltaY = 0;
3281
+ deltaX = -1*delta;
3282
+ }
3283
+
3284
+ // Webkit
3285
+ if ( orgEvent.wheelDeltaY !== undefined ) { deltaY = orgEvent.wheelDeltaY/120; }
3286
+ if ( orgEvent.wheelDeltaX !== undefined ) { deltaX = -1*orgEvent.wheelDeltaX/120; }
3287
+
3288
+ // Add event and delta to the front of the arguments
3289
+ args.unshift(event, delta, deltaX, deltaY);
3290
+
3291
+ return $.event.handle.apply(this, args);
3292
+ }
3293
+
3294
+ })(jQuery);
3295
+ // # ZUI53
3296
+ //
3297
+ // Copyright (c) 2011 Florian Günther
3298
+ //
3299
+ // Permission is hereby granted, free of charge, to any person obtaining
3300
+ // a copy of this software and associated documentation files (the
3301
+ // "Software"), to deal in the Software without restriction, including
3302
+ // without limitation the rights to use, copy, modify, merge, publish,
3303
+ // distribute, sublicense, and/or sell copies of the Software, and to
3304
+ // permit persons to whom the Software is furnished to do so, subject to
3305
+ // the following conditions:
3306
+ //
3307
+ // The above copyright notice and this permission notice shall be
3308
+ // included in all copies or substantial portions of the Software.
3309
+ //
3310
+ // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
3311
+ // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
3312
+ // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
3313
+ // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
3314
+ // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
3315
+ // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
3316
+ // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
3317
+ //
3318
+
3319
+ function registerNS(ns)
3320
+ {
3321
+ var nsParts = ns.split(".");
3322
+ var root = window;
3323
+
3324
+ for(var i=0; i<nsParts.length; i++)
3325
+ {
3326
+ if(typeof root[nsParts[i]] == "undefined")
3327
+ root[nsParts[i]] = new Object();
3328
+
3329
+ root = root[nsParts[i]];
3330
+ }
3331
+ }
3332
+
3333
+ function namespace(name, callback)
3334
+ {
3335
+ registerNS(name);
3336
+ callback( eval(name) );
3337
+ }
3338
+ ;
3339
+ (function() {
3340
+ var __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; };
3341
+ namespace('ZUI53.Tools', function(exports) {
3342
+ exports.Base = (function() {
3343
+ function Base() {
3344
+ this.stopEvent = __bind(this.stopEvent, this);
3345
+ this.makeUnexclusive = __bind(this.makeUnexclusive, this);
3346
+ this.makeExclusive = __bind(this.makeExclusive, this);
3347
+ this.detach = __bind(this.detach, this);
3348
+ this.attach = __bind(this.attach, this); this.set = null;
3349
+ this.group = null;
3350
+ this.attached = false;
3351
+ }
3352
+ Base.prototype.attach = function() {
3353
+ if (this.group) {
3354
+ this.group.attach(this);
3355
+ }
3356
+ return this.attached = true;
3357
+ };
3358
+ Base.prototype.detach = function() {
3359
+ return this.attached = false;
3360
+ };
3361
+ Base.prototype.makeExclusive = function() {
3362
+ if (this.set) {
3363
+ this.set.exclusive(this);
3364
+ }
3365
+ return this.attach();
3366
+ };
3367
+ Base.prototype.makeUnexclusive = function() {
3368
+ if (this.set) {
3369
+ return this.set.unexclusive();
3370
+ }
3371
+ };
3372
+ Base.prototype.stopEvent = function(e) {
3373
+ e.preventDefault();
3374
+ if (e.stopImmediatePropagation != null) {
3375
+ e.stopImmediatePropagation();
3376
+ }
3377
+ return false;
3378
+ };
3379
+ return Base;
3380
+ })();
3381
+ exports.SetGroup = (function() {
3382
+ function SetGroup() {
3383
+ this.requestUnexclusive = __bind(this.requestUnexclusive, this);
3384
+ this.requestExclusive = __bind(this.requestExclusive, this);
3385
+ this.attach = __bind(this.attach, this);
3386
+ this.add = __bind(this.add, this); this.tools = [];
3387
+ this.current = null;
3388
+ this.beforeExclusive = null;
3389
+ }
3390
+ SetGroup.prototype.add = function(tool) {
3391
+ tool.group = this;
3392
+ this.tools.push(tool);
3393
+ if (this.tools.length === 1) {
3394
+ return tool.attach();
3395
+ }
3396
+ };
3397
+ SetGroup.prototype.attach = function(tool) {
3398
+ var t, _i, _len, _ref, _results;
3399
+ this.current = tool;
3400
+ _ref = this.tools;
3401
+ _results = [];
3402
+ for (_i = 0, _len = _ref.length; _i < _len; _i++) {
3403
+ t = _ref[_i];
3404
+ _results.push(t !== tool ? t.detach() : void 0);
3405
+ }
3406
+ return _results;
3407
+ };
3408
+ SetGroup.prototype.requestExclusive = function(tool) {
3409
+ if (this.current && this.current !== tool) {
3410
+ this.current.detach();
3411
+ }
3412
+ return this.beforeExclusive = this.current;
3413
+ };
3414
+ SetGroup.prototype.requestUnexclusive = function() {
3415
+ this.current = this.beforeExclusive;
3416
+ if (this.current) {
3417
+ return this.current.attach();
3418
+ }
3419
+ };
3420
+ return SetGroup;
3421
+ })();
3422
+ return exports.Set = (function() {
3423
+ function Set(default_tool) {
3424
+ this.default_tool = default_tool;
3425
+ this.unexclusive = __bind(this.unexclusive, this);
3426
+ this.exclusive = __bind(this.exclusive, this);
3427
+ this.add = __bind(this.add, this);
3428
+ this.groups = [new exports.SetGroup()];
3429
+ this.default_tool.set = this;
3430
+ if (this.default_tool) {
3431
+ this.default_tool.attach();
3432
+ }
3433
+ }
3434
+ Set.prototype.add = function(tool) {
3435
+ this.groups[0].add(tool);
3436
+ return tool.set = this;
3437
+ };
3438
+ Set.prototype.exclusive = function(tool) {
3439
+ var g, _i, _len, _ref;
3440
+ _ref = this.groups;
3441
+ for (_i = 0, _len = _ref.length; _i < _len; _i++) {
3442
+ g = _ref[_i];
3443
+ g.requestExclusive(tool);
3444
+ }
3445
+ if (this.default_tool !== tool && this.default_tool) {
3446
+ return this.default_tool.detach();
3447
+ }
3448
+ };
3449
+ Set.prototype.unexclusive = function() {
3450
+ var g, _i, _len, _ref;
3451
+ _ref = this.groups;
3452
+ for (_i = 0, _len = _ref.length; _i < _len; _i++) {
3453
+ g = _ref[_i];
3454
+ g.requestUnexclusive();
3455
+ }
3456
+ if (this.default_tool) {
3457
+ return this.default_tool.attach();
3458
+ }
3459
+ };
3460
+ return Set;
3461
+ })();
3462
+ });
3463
+ }).call(this);
3464
+ (function() {
3465
+ var __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; }, __hasProp = Object.prototype.hasOwnProperty, __extends = function(child, parent) {
3466
+ for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; }
3467
+ function ctor() { this.constructor = child; }
3468
+ ctor.prototype = parent.prototype;
3469
+ child.prototype = new ctor;
3470
+ child.__super__ = parent.prototype;
3471
+ return child;
3472
+ };
3473
+ namespace('ZUI53.Tools', function(exports) {
3474
+ return exports.Pan = (function() {
3475
+ __extends(Pan, exports.Base);
3476
+ function Pan(zui) {
3477
+ this._start_with = __bind(this._start_with, this);
3478
+ this._pan_with = __bind(this._pan_with, this);
3479
+ this.touch_stop = __bind(this.touch_stop, this);
3480
+ this.touch_move = __bind(this.touch_move, this);
3481
+ this.touch_start = __bind(this.touch_start, this);
3482
+ this.stop = __bind(this.stop, this);
3483
+ this.pan = __bind(this.pan, this);
3484
+ this.start = __bind(this.start, this);
3485
+ this.detach = __bind(this.detach, this);
3486
+ this.attach = __bind(this.attach, this); this.vp = zui;
3487
+ this.eventDispatcher = zui.viewport;
3488
+ }
3489
+ Pan.prototype.attach = function() {
3490
+ $('body').addClass('pan');
3491
+ $(this.eventDispatcher).bind('mousedown', this.start);
3492
+ return $(this.eventDispatcher).bind('touchstart', this.touch_start);
3493
+ };
3494
+ Pan.prototype.detach = function() {
3495
+ $('body').removeClass('pan');
3496
+ this.touch_stop(null);
3497
+ $(this.eventDispatcher).unbind('mousedown', this.start);
3498
+ return $(this.eventDispatcher).unbind('touchstart', this.touch_start);
3499
+ };
3500
+ Pan.prototype.start = function(e) {
3501
+ $('body').addClass('panning');
3502
+ this._start_with(e.screenX, e.screenY);
3503
+ window.addEventListener('mousemove', this.pan, true);
3504
+ window.addEventListener('mouseup', this.stop, true);
3505
+ return this.stopEvent(e);
3506
+ };
3507
+ Pan.prototype.pan = function(e) {
3508
+ return this._pan_with(e.screenX, e.screenY);
3509
+ };
3510
+ Pan.prototype.stop = function(e) {
3511
+ $('body').removeClass('panning');
3512
+ window.removeEventListener('mousemove', this.pan, true);
3513
+ window.removeEventListener('mouseup', this.stop, true);
3514
+ window.removeEventListener('touchmove', this.pan, true);
3515
+ window.removeEventListener('touchend', this.stop, true);
3516
+ return this.stopEvent(e);
3517
+ };
3518
+ Pan.prototype.touch_start = function(e) {
3519
+ this._start_with(e.originalEvent.touches[0].clientX, e.originalEvent.touches[0].clientY);
3520
+ this.eventDispatcher.addEventListener('touchmove', this.touch_move, true);
3521
+ this.eventDispatcher.addEventListener('touchend', this.touch_stop, true);
3522
+ return e.originalEvent.preventDefault();
3523
+ };
3524
+ Pan.prototype.touch_move = function(e) {
3525
+ return this._pan_with(e.touches[0].clientX, e.touches[0].clientY);
3526
+ };
3527
+ Pan.prototype.touch_stop = function(e) {
3528
+ this.eventDispatcher.removeEventListener('touchmove', this.touch_move, true);
3529
+ return this.eventDispatcher.removeEventListener('touchend', this.touch_stop, true);
3530
+ };
3531
+ Pan.prototype._pan_with = function(x, y) {
3532
+ var dX, dY;
3533
+ dX = x - this.startX;
3534
+ dY = y - this.startY;
3535
+ this.startX = x;
3536
+ this.startY = y;
3537
+ return this.vp.panBy(dX, dY);
3538
+ };
3539
+ Pan.prototype._start_with = function(x, y) {
3540
+ this.startX = x;
3541
+ return this.startY = y;
3542
+ };
3543
+ return Pan;
3544
+ })();
3545
+ });
3546
+ }).call(this);
3547
+ (function() {
3548
+ var __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; }, __hasProp = Object.prototype.hasOwnProperty, __extends = function(child, parent) {
3549
+ for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; }
3550
+ function ctor() { this.constructor = child; }
3551
+ ctor.prototype = parent.prototype;
3552
+ child.prototype = new ctor;
3553
+ child.__super__ = parent.prototype;
3554
+ return child;
3555
+ };
3556
+ namespace('ZUI53.Tools', function(exports) {
3557
+ return exports.Zoom = (function() {
3558
+ __extends(Zoom, exports.Base);
3559
+ function Zoom(zui) {
3560
+ this.find_distance = __bind(this.find_distance, this);
3561
+ this.find_midpoint = __bind(this.find_midpoint, this);
3562
+ this.touch_move = __bind(this.touch_move, this);
3563
+ this._internal_gesture_end = __bind(this._internal_gesture_end, this);
3564
+ this._internal_gesture_start = __bind(this._internal_gesture_start, this);
3565
+ this.gesture_end = __bind(this.gesture_end, this);
3566
+ this.gesture_move = __bind(this.gesture_move, this);
3567
+ this.gesture_start = __bind(this.gesture_start, this);
3568
+ this.zoom = __bind(this.zoom, this);
3569
+ this.moz_touch_up = __bind(this.moz_touch_up, this);
3570
+ this.moz_touch_move = __bind(this.moz_touch_move, this);
3571
+ this.moz_touch_down = __bind(this.moz_touch_down, this);
3572
+ this.create_touch_index = __bind(this.create_touch_index, this);
3573
+ this.update_moz_touch = __bind(this.update_moz_touch, this);
3574
+ this.fetch_touch = __bind(this.fetch_touch, this);
3575
+ this.detach = __bind(this.detach, this);
3576
+ this.attach = __bind(this.attach, this); this.vp = zui;
3577
+ this.eventDispatcher = zui.viewport;
3578
+ this.use_capture = true;
3579
+ this.t1 = null;
3580
+ this.t2 = null;
3581
+ this.touch = {
3582
+ touches: [],
3583
+ touch_ids: []
3584
+ };
3585
+ }
3586
+ Zoom.prototype.attach = function() {
3587
+ $(this.eventDispatcher).mousewheel(this.zoom);
3588
+ this.eventDispatcher.addEventListener('gesturestart', this.gesture_start, this.use_capture);
3589
+ this.eventDispatcher.addEventListener('MozTouchDown', this.moz_touch_down, this.use_capture);
3590
+ return this.eventDispatcher.addEventListener('MozTouchUp', this.moz_touch_up, this.use_capture);
3591
+ };
3592
+ Zoom.prototype.detach = function() {
3593
+ $(this.eventDispatcher).unmousewheel(this.zoom);
3594
+ this.eventDispatcher.removeEventListener('gesturestart', this.gesture_start, this.use_capture);
3595
+ this.eventDispatcher.removeEventListener('MozTouchDown', this.moz_touch_down, this.use_capture);
3596
+ return this.eventDispatcher.removeEventListener('MozTouchUp', this.moz_touch_up, this.use_capture);
3597
+ };
3598
+ Zoom.prototype.fetch_touch = function(e, value) {
3599
+ if (this.t1 && this.t1.streamId === e.streamId) {
3600
+ this.t1 = value || e;
3601
+ } else {
3602
+ this.t2 = value || e;
3603
+ }
3604
+ return this.update_moz_touch();
3605
+ };
3606
+ Zoom.prototype.update_moz_touch = function() {
3607
+ var mp;
3608
+ if (this.t1 && this.t2) {
3609
+ try {
3610
+ return mp = this.find_midpoint({
3611
+ touches: [this.t1, this.t2]
3612
+ });
3613
+ } catch (e) {
3614
+ return console.log(e);
3615
+ }
3616
+ } else if (this.t1 || this.t2) {
3617
+ return console.log('only one');
3618
+ }
3619
+ };
3620
+ Zoom.prototype.create_touch_index = function(streamId) {
3621
+ var i;
3622
+ i = this.touch.touch_ids.indexOf(streamId);
3623
+ if (i < 0) {
3624
+ i = this.touch.touch_ids.length;
3625
+ this.touch.touch_ids[i] = streamId;
3626
+ }
3627
+ return i;
3628
+ };
3629
+ Zoom.prototype.moz_touch_down = function(e) {
3630
+ var i;
3631
+ this.touch_df = null;
3632
+ try {
3633
+ i = this.create_touch_index(e.streamId);
3634
+ this.touch.touches[i] = e;
3635
+ if (this.touch.touches.length === 2) {
3636
+ this._internal_gesture_start();
3637
+ return this.eventDispatcher.addEventListener('MozTouchMove', this.moz_touch_move, this.use_capture);
3638
+ }
3639
+ } catch (e) {
3640
+ return console.log(e);
3641
+ }
3642
+ };
3643
+ Zoom.prototype.moz_touch_move = function(e) {
3644
+ var d, i, s;
3645
+ i = this.create_touch_index(e.streamId);
3646
+ this.touch.touches[i] = e;
3647
+ this.touch_move(this.touch);
3648
+ d = this.find_distance(this.touch);
3649
+ if (this.touch_df) {
3650
+ s = this.touch_df * d;
3651
+ return this.gesture_move({
3652
+ scale: s
3653
+ });
3654
+ } else {
3655
+ return this.touch_df = 1 / d;
3656
+ }
3657
+ };
3658
+ Zoom.prototype.moz_touch_up = function(e) {
3659
+ var i;
3660
+ i = this.touch.touch_ids.indexOf(e.streamId);
3661
+ if (i > 0) {
3662
+ console.log("Removed: " + i);
3663
+ if (this.touch.touches.length === 2) {
3664
+ this._internal_gesture_end();
3665
+ this.eventDispatcher.removeEventListener('MozTouchMove', this.moz_touch_move, this.use_capture);
3666
+ }
3667
+ this.touch.touches.splice(i, 1);
3668
+ return this.touch.touch_ids.splice(i, 1);
3669
+ }
3670
+ };
3671
+ Zoom.prototype.zoom = function(e) {
3672
+ var delta, f;
3673
+ delta = e.wheelDelta || (e.detail * -1);
3674
+ f = 0.05;
3675
+ if (delta < 0) {
3676
+ f *= -1;
3677
+ }
3678
+ this.vp.zoomBy(f, e.clientX, e.clientY);
3679
+ return this.stopEvent(e);
3680
+ };
3681
+ Zoom.prototype.gesture_start = function(e) {
3682
+ this._internal_gesture_start();
3683
+ this.eventDispatcher.addEventListener('gesturechange', this.gesture_move, this.use_capture);
3684
+ this.eventDispatcher.addEventListener('gestureend', this.gesture_end, this.use_capture);
3685
+ this.eventDispatcher.addEventListener('touchmove', this.touch_move, this.use_capture);
3686
+ return e.preventDefault();
3687
+ };
3688
+ Zoom.prototype.gesture_move = function(e) {
3689
+ if (this.last_touch_p) {
3690
+ return this.vp.zoomSet(this.start_scale * e.scale, this.last_touch_p.e(1), this.last_touch_p.e(2));
3691
+ }
3692
+ };
3693
+ Zoom.prototype.gesture_end = function(e) {
3694
+ this.eventDispatcher.removeEventListener('touchmove', this.touch_move, this.use_capture);
3695
+ this.eventDispatcher.removeEventListener('gesturechange', this.gesture_move, this.use_capture);
3696
+ this.eventDispatcher.removeEventListener('gestureend', this.gesture_end, this.use_capture);
3697
+ return this._internal_gesture_end();
3698
+ };
3699
+ Zoom.prototype._internal_gesture_start = function() {
3700
+ this.makeExclusive();
3701
+ this.last_touch_p = null;
3702
+ return this.start_scale = this.vp.scale;
3703
+ };
3704
+ Zoom.prototype._internal_gesture_end = function() {
3705
+ return this.makeUnexclusive();
3706
+ };
3707
+ Zoom.prototype.touch_move = function(e) {
3708
+ var d, new_touch_p;
3709
+ if (this.last_touch_p) {
3710
+ new_touch_p = this.find_midpoint(e);
3711
+ d = new_touch_p.subtract(this.last_touch_p);
3712
+ this.last_touch_p = new_touch_p;
3713
+ return this.vp.panBy(d.e(1), d.e(2));
3714
+ } else {
3715
+ return this.last_touch_p = this.find_midpoint(e);
3716
+ }
3717
+ };
3718
+ Zoom.prototype.find_midpoint = function(e) {
3719
+ var d, p, p1, p2, t1, t2;
3720
+ t1 = e.touches[0];
3721
+ t2 = e.touches[1];
3722
+ p1 = $V([t1.clientX, t1.clientY, 1]);
3723
+ p2 = $V([t2.clientX, t2.clientY, 1]);
3724
+ d = p2.subtract(p1).multiply(0.5);
3725
+ return p = p1.add(d);
3726
+ };
3727
+ Zoom.prototype.find_distance = function(e) {
3728
+ var p1, p2, t1, t2;
3729
+ t1 = e.touches[0];
3730
+ t2 = e.touches[1];
3731
+ p1 = $V([t1.clientX, t1.clientY, 1]);
3732
+ p2 = $V([t2.clientX, t2.clientY, 1]);
3733
+ return p2.distanceFrom(p1);
3734
+ };
3735
+ return Zoom;
3736
+ })();
3737
+ });
3738
+ }).call(this);
3739
+ (function() {
3740
+ var __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; };
3741
+ namespace('ZUI53.Surfaces', function(exports) {
3742
+ return exports.SVG = (function() {
3743
+ function SVG(node) {
3744
+ this.node = node;
3745
+ this.apply = __bind(this.apply, this);
3746
+ }
3747
+ SVG.prototype.limits = function() {
3748
+ return [0.0001, 20000];
3749
+ };
3750
+ SVG.prototype.apply = function(panX, panY, scale) {
3751
+ var singleSVG;
3752
+ singleSVG = "translate(" + panX + ", " + panY + ") scale(" + scale + ", " + scale + ")";
3753
+ return $(this.node).attr("transform", singleSVG);
3754
+ };
3755
+ return SVG;
3756
+ })();
3757
+ });
3758
+ }).call(this);
3759
+ (function() {
3760
+ var __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; };
3761
+ namespace('ZUI53.Surfaces', function(exports) {
3762
+ return exports.CSS = (function() {
3763
+ function CSS(node) {
3764
+ this.node = node;
3765
+ this.apply = __bind(this.apply, this);
3766
+ $(this.node).transform({
3767
+ origin: ['0', '0']
3768
+ });
3769
+ $(this.node).css({
3770
+ 'position': 'absolute'
3771
+ });
3772
+ }
3773
+ CSS.prototype.limits = function() {
3774
+ return null;
3775
+ };
3776
+ CSS.prototype.apply = function(panX, panY, scale) {
3777
+ return $(this.node).transform({
3778
+ matrix: [scale, 0.0, 0.0, scale, panX, panY]
3779
+ });
3780
+ };
3781
+ return CSS;
3782
+ })();
3783
+ });
3784
+ }).call(this);
3785
+ (function() {
3786
+ var __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; };
3787
+ namespace('ZUI53', function(exports) {
3788
+ return exports.Viewport = (function() {
3789
+ function Viewport(vp) {
3790
+ this.setTransformString = __bind(this.setTransformString, this);
3791
+ this.getTransformString = __bind(this.getTransformString, this);
3792
+ this.setPanAndScale = __bind(this.setPanAndScale, this);
3793
+ this.getPanAndScale = __bind(this.getPanAndScale, this);
3794
+ this.showBounds = __bind(this.showBounds, this);
3795
+ this.avp = __bind(this.avp, this);
3796
+ this.translateSurface = __bind(this.translateSurface, this);
3797
+ this.fitToLimits = __bind(this.fitToLimits, this);
3798
+ this.zoomSet = __bind(this.zoomSet, this);
3799
+ this.zoomBy = __bind(this.zoomBy, this);
3800
+ this.panBy = __bind(this.panBy, this);
3801
+ this.updateSurface = __bind(this.updateSurface, this);
3802
+ this.surfaceToLayer = __bind(this.surfaceToLayer, this);
3803
+ this.surfaceToClient = __bind(this.surfaceToClient, this);
3804
+ this.layerToSurface = __bind(this.layerToSurface, this);
3805
+ this.clientToSurface = __bind(this.clientToSurface, this);
3806
+ this.addLimits = __bind(this.addLimits, this);
3807
+ this.removeSurface = __bind(this.removeSurface, this);
3808
+ this.addSurface = __bind(this.addSurface, this);
3809
+ this.reset = __bind(this.reset, this);
3810
+ this.updateOffset = __bind(this.updateOffset, this); this.min_scale = null;
3811
+ this.max_scale = null;
3812
+ this.viewport = this.styleViewport(vp);
3813
+ this.surfaces = [];
3814
+ this.updateOffset();
3815
+ this.reset();
3816
+ $(vp).scroll(__bind(function(e) {
3817
+ var jVP;
3818
+ jVP = $(this.viewport);
3819
+ this.panBy(-jVP.scrollLeft(), -jVP.scrollTop());
3820
+ return jVP.scrollTop(0).scrollLeft(0);
3821
+ }, this));
3822
+ this.toolset = new ZUI53.Tools.Set(new ZUI53.Tools.Zoom(this));
3823
+ }
3824
+ Viewport.prototype.styleViewport = function(vp) {
3825
+ $(vp).css({
3826
+ 'position': 'relative',
3827
+ 'overflow': 'hidden',
3828
+ 'width': '100%',
3829
+ 'height': '100%'
3830
+ });
3831
+ return vp;
3832
+ };
3833
+ Viewport.prototype.updateOffset = function() {
3834
+ this.vpOffset = $(this.viewport).offset();
3835
+ this.vpOffset.left -= Number($(window.document).scrollLeft());
3836
+ this.vpOffset.top -= Number($(window.document).scrollTop());
3837
+ this.vpOffM = $M([[1, 0, this.vpOffset.left], [0, 1, this.vpOffset.top], [0, 0, 1]]);
3838
+ return this.vpOffM;
3839
+ };
3840
+ Viewport.prototype.reset = function() {
3841
+ this.zoomPos = 0;
3842
+ this.scale = 1.0;
3843
+ this.surfaceM = $M([[1, 0, 0], [0, 1, 0], [0, 0, 1]]);
3844
+ return this.updateSurface();
3845
+ };
3846
+ Viewport.prototype.addSurface = function(surface) {
3847
+ this.surfaces.push(surface);
3848
+ return this.addLimits(surface.limits());
3849
+ };
3850
+ Viewport.prototype.removeSurface = function(surface) {
3851
+ var i;
3852
+ i = this.surfaces.indexOf(surface);
3853
+ if (i >= 0) {
3854
+ return this.surfaces.splice(i, 1);
3855
+ }
3856
+ };
3857
+ Viewport.prototype.addLimits = function(limits) {
3858
+ if (!limits) {
3859
+ return;
3860
+ }
3861
+ if (this.min_scale || this.max_scale) {
3862
+ if (limits[0]) {
3863
+ this.min_scale = Math.max(limits[0], this.min_scale);
3864
+ }
3865
+ if (limits[1]) {
3866
+ return this.max_scale = Math.min(limits[1], this.max_scale);
3867
+ }
3868
+ } else {
3869
+ this.min_scale = limits[0];
3870
+ return this.max_scale = limits[1];
3871
+ }
3872
+ };
3873
+ Viewport.prototype.clientToSurface = function(x, y) {
3874
+ var sV, v;
3875
+ v = $V([x, y, 1]);
3876
+ return sV = this.surfaceM.inverse().multiply(this.updateOffset().inverse().multiply(v));
3877
+ };
3878
+ Viewport.prototype.layerToSurface = function(x, y) {
3879
+ var sV, v;
3880
+ v = $V([x, y, 1]);
3881
+ return sV = this.surfaceM.inverse().multiply(v);
3882
+ };
3883
+ Viewport.prototype.surfaceToClient = function(v) {
3884
+ return this.updateOffset().multiply(this.surfaceM.multiply(v));
3885
+ };
3886
+ Viewport.prototype.surfaceToLayer = function(v) {
3887
+ return this.surfaceM.multiply(v);
3888
+ };
3889
+ Viewport.prototype.updateSurface = function() {
3890
+ var node, v, _i, _len, _ref;
3891
+ v = this.getPanAndScale();
3892
+ _ref = this.surfaces;
3893
+ for (_i = 0, _len = _ref.length; _i < _len; _i++) {
3894
+ node = _ref[_i];
3895
+ node.apply(v[0], v[1], v[2]);
3896
+ }
3897
+ return true;
3898
+ };
3899
+ Viewport.prototype.panBy = function(x, y) {
3900
+ this.translateSurface(x, y);
3901
+ return this.updateSurface();
3902
+ };
3903
+ Viewport.prototype.zoomBy = function(byF, clientX, clientY) {
3904
+ var newScale;
3905
+ newScale = this._pos_to_scale(this.zoomPos + byF);
3906
+ return this.zoomSet(newScale, clientX, clientY);
3907
+ };
3908
+ Viewport.prototype.zoomSet = function(newScale, clientX, clientY) {
3909
+ var c, dX, dY, scaleBy, sf;
3910
+ newScale = this.fitToLimits(newScale);
3911
+ this.zoomPos = this._scale_to_pos(newScale);
3912
+ if (newScale !== this.scale) {
3913
+ sf = this.clientToSurface(clientX, clientY);
3914
+ scaleBy = newScale / this.scale;
3915
+ this.surfaceM = this._scaleMatrix(this.surfaceM, scaleBy);
3916
+ this.scale = newScale;
3917
+ c = this.surfaceToClient(sf);
3918
+ dX = clientX - c.e(1);
3919
+ dY = clientY - c.e(2);
3920
+ this.translateSurface(dX, dY);
3921
+ }
3922
+ return this.updateSurface();
3923
+ };
3924
+ Viewport.prototype.fitToLimits = function(s) {
3925
+ if (this.min_scale && s < this.min_scale) {
3926
+ s = this.min_scale;
3927
+ } else if (this.max_scale && s > this.max_scale) {
3928
+ s = this.max_scale;
3929
+ }
3930
+ return s;
3931
+ };
3932
+ Viewport.prototype.translateSurface = function(x, y) {
3933
+ return this.surfaceM = this._translateMatrix(this.surfaceM, x, y);
3934
+ };
3935
+ Viewport.prototype._translateMatrix = function(m, x, y) {
3936
+ return m.add($M([[0, 0, x], [0, 0, y], [0, 0, 0]]));
3937
+ };
3938
+ Viewport.prototype._scaleMatrix = function(m, s) {
3939
+ return m.multiply($M([[s, 0, 0], [0, s, 0], [0, 0, 1]]));
3940
+ };
3941
+ Viewport.prototype._pos_to_scale = function(pos) {
3942
+ return Math.exp(pos);
3943
+ };
3944
+ Viewport.prototype._scale_to_pos = function(s) {
3945
+ return Math.log(s);
3946
+ };
3947
+ Viewport.prototype.avp = function() {
3948
+ var del, max, min;
3949
+ this.updateOffset();
3950
+ min = this.clientToSurface(this.vpOffset.left, this.vpOffset.top);
3951
+ max = this.clientToSurface(this.vpOffset.left + $(this.viewport).width(), this.vpOffset.top + $(this.viewport).height());
3952
+ del = max.subtract(min);
3953
+ return {
3954
+ x: min.e(1),
3955
+ y: min.e(2),
3956
+ width: del.e(1),
3957
+ height: del.e(2)
3958
+ };
3959
+ };
3960
+ Viewport.prototype._boundsCenter = function(b) {
3961
+ return {
3962
+ x: b.x + b.width / 2,
3963
+ y: b.y + b.height / 2
3964
+ };
3965
+ };
3966
+ Viewport.prototype.showBounds = function(evp) {
3967
+ var aC, avp, eC, exp, s;
3968
+ if (evp.width === 0 || evp.height === 0) {
3969
+ return;
3970
+ }
3971
+ avp = this.avp();
3972
+ s = Math.min(avp.width / evp.width, avp.height / evp.height);
3973
+ exp = 50 / s;
3974
+ evp.x -= exp;
3975
+ evp.y -= exp;
3976
+ evp.width += 2 * exp;
3977
+ evp.height += 2 * exp;
3978
+ s = Math.min(avp.width / evp.width, avp.height / evp.height);
3979
+ s = this.fitToLimits(s);
3980
+ eC = this._boundsCenter(evp);
3981
+ aC = this._boundsCenter(avp);
3982
+ this.setPanAndScale(-eC.x * s, -eC.y * s, s);
3983
+ this.translateSurface($(this.viewport).width() / 2, $(this.viewport).height() / 2);
3984
+ return this.updateSurface();
3985
+ };
3986
+ Viewport.prototype.getPanAndScale = function() {
3987
+ return [this.surfaceM.e(1, 3), this.surfaceM.e(2, 3), this.surfaceM.e(1, 1)];
3988
+ };
3989
+ Viewport.prototype.setPanAndScale = function(panX, panY, scale) {
3990
+ this.surfaceM = $M([[1, 0, 0], [0, 1, 0], [0, 0, 1]]);
3991
+ this.translateSurface(panX, panY);
3992
+ this.surfaceM = this._scaleMatrix(this.surfaceM, scale);
3993
+ this.scale = scale;
3994
+ return this.zoomPos = this._scale_to_pos(scale);
3995
+ };
3996
+ Viewport.prototype.getTransformString = function() {
3997
+ return this.getPanAndScale().join(',');
3998
+ };
3999
+ Viewport.prototype.setTransformString = function(str) {
4000
+ var panX, panY, scale, v;
4001
+ if (!str) {
4002
+ return;
4003
+ }
4004
+ v = str.split(',');
4005
+ panX = Number(v[0]);
4006
+ panY = Number(v[1]);
4007
+ scale = Number(v[2]);
4008
+ this.setPanAndScale(panX, panY, scale);
4009
+ return this.updateSurface();
4010
+ };
4011
+ return Viewport;
4012
+ })();
4013
+ });
4014
+ }).call(this);