zui53 0.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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);