rasputin 0.8.1 → 0.8.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.
@@ -1,2 +1,1629 @@
1
1
 
2
+ (function(exports) {
3
+ // Vector and Matrix mathematics modules for JavaScript
4
+ // Copyright (c) 2007 James Coglan
5
+ //
6
+ // Permission is hereby granted, free of charge, to any person obtaining
7
+ // a copy of this software and associated documentation files (the "Software"),
8
+ // to deal in the Software without restriction, including without limitation
9
+ // the rights to use, copy, modify, merge, publish, distribute, sublicense,
10
+ // and/or sell copies of the Software, and to permit persons to whom the
11
+ // Software is furnished to do so, subject to the following conditions:
12
+ //
13
+ // The above copyright notice and this permission notice shall be included
14
+ // in all copies or substantial portions of the Software.
15
+ //
16
+ // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
17
+ // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18
+ // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
19
+ // THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20
+ // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21
+ // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
22
+ // DEALINGS IN THE SOFTWARE.
2
23
 
24
+ var Sylvester = {
25
+ version: '0.1.3',
26
+ precision: 1e-6
27
+ };
28
+
29
+ function Matrix() {}
30
+ Matrix.prototype = {
31
+
32
+ // Returns element (i,j) of the matrix
33
+ e: function(i,j) {
34
+ if (i < 1 || i > this.elements.length || j < 1 || j > this.elements[0].length) { return null; }
35
+ return this.elements[i-1][j-1];
36
+ },
37
+
38
+ // Maps the matrix to another matrix (of the same dimensions) according to the given function
39
+ map: function(fn) {
40
+ var els = [], ni = this.elements.length, ki = ni, i, nj, kj = this.elements[0].length, j;
41
+ do { i = ki - ni;
42
+ nj = kj;
43
+ els[i] = [];
44
+ do { j = kj - nj;
45
+ els[i][j] = fn(this.elements[i][j], i + 1, j + 1);
46
+ } while (--nj);
47
+ } while (--ni);
48
+ return Matrix.create(els);
49
+ },
50
+
51
+ // Returns the result of multiplying the matrix from the right by the argument.
52
+ // If the argument is a scalar then just multiply all the elements. If the argument is
53
+ // a vector, a vector is returned, which saves you having to remember calling
54
+ // col(1) on the result.
55
+ multiply: function(matrix) {
56
+ if (!matrix.elements) {
57
+ return this.map(function(x) { return x * matrix; });
58
+ }
59
+ var returnVector = matrix.modulus ? true : false;
60
+ var M = matrix.elements || matrix;
61
+ if (typeof(M[0][0]) == 'undefined') { M = Matrix.create(M).elements; }
62
+ if (!this.canMultiplyFromLeft(M)) { return null; }
63
+ var ni = this.elements.length, ki = ni, i, nj, kj = M[0].length, j;
64
+ var cols = this.elements[0].length, elements = [], sum, nc, c;
65
+ do { i = ki - ni;
66
+ elements[i] = [];
67
+ nj = kj;
68
+ do { j = kj - nj;
69
+ sum = 0;
70
+ nc = cols;
71
+ do { c = cols - nc;
72
+ sum += this.elements[i][c] * M[c][j];
73
+ } while (--nc);
74
+ elements[i][j] = sum;
75
+ } while (--nj);
76
+ } while (--ni);
77
+ var M = Matrix.create(elements);
78
+ return returnVector ? M.col(1) : M;
79
+ },
80
+
81
+ x: function(matrix) { return this.multiply(matrix); },
82
+
83
+ // Returns true iff the matrix can multiply the argument from the left
84
+ canMultiplyFromLeft: function(matrix) {
85
+ var M = matrix.elements || matrix;
86
+ if (typeof(M[0][0]) == 'undefined') { M = Matrix.create(M).elements; }
87
+ // this.columns should equal matrix.rows
88
+ return (this.elements[0].length == M.length);
89
+ },
90
+
91
+ // Set the matrix's elements from an array. If the argument passed
92
+ // is a vector, the resulting matrix will be a single column.
93
+ setElements: function(els) {
94
+ var i, elements = els.elements || els;
95
+ if (typeof(elements[0][0]) != 'undefined') {
96
+ var ni = elements.length, ki = ni, nj, kj, j;
97
+ this.elements = [];
98
+ do { i = ki - ni;
99
+ nj = elements[i].length; kj = nj;
100
+ this.elements[i] = [];
101
+ do { j = kj - nj;
102
+ this.elements[i][j] = elements[i][j];
103
+ } while (--nj);
104
+ } while(--ni);
105
+ return this;
106
+ }
107
+ var n = elements.length, k = n;
108
+ this.elements = [];
109
+ do { i = k - n;
110
+ this.elements.push([elements[i]]);
111
+ } while (--n);
112
+ return this;
113
+ }
114
+ };
115
+
116
+ // Constructor function
117
+ Matrix.create = function(elements) {
118
+ var M = new Matrix();
119
+ return M.setElements(elements);
120
+ };
121
+
122
+ // Utility functions
123
+ $M = Matrix.create;
124
+
125
+ })({});
126
+
127
+
128
+ (function(exports) {
129
+ // ==========================================================================
130
+ // Project: AcceleratedEffects
131
+ // Copyright: ©2011 Majd Taby
132
+ // License: Licensed under MIT license (see license.js)
133
+ // ==========================================================================
134
+
135
+ (function($) {
136
+ if ( !$.cssHooks ) {
137
+ throw("jQuery 1.4.3+ is needed for this plugin to work");
138
+ return;
139
+ }
140
+
141
+ function styleSupport( prop ) {
142
+ var vendorProp, supportedProp,
143
+
144
+ // capitalize first character of the prop to test vendor prefix
145
+ capProp = prop.charAt(0).toUpperCase() + prop.slice(1),
146
+ prefixes = [ "Moz", "Webkit", "O", "ms" ],
147
+ div = document.createElement( "div" );
148
+
149
+ if ( prop in div.style ) {
150
+
151
+ // browser supports standard CSS property name
152
+ supportedProp = prop;
153
+ } else {
154
+
155
+ // otherwise test support for vendor-prefixed property names
156
+ for ( var i = 0; i < prefixes.length; i++ ) {
157
+ vendorProp = prefixes[i] + capProp;
158
+ if ( vendorProp in div.style ) {
159
+ supportedProp = vendorProp;
160
+ break;
161
+ }
162
+ }
163
+ }
164
+
165
+ // avoid memory leak in IE
166
+ div = null;
167
+
168
+ // add property to $.support so it can be accessed elsewhere
169
+ $.support[ prop ] = supportedProp;
170
+
171
+ return supportedProp;
172
+ }
173
+
174
+ var transformProperty = styleSupport('transform');
175
+ console.log(transformProperty);
176
+
177
+ var properties = {
178
+ rotateX: {
179
+ defaultValue: 0
180
+ },
181
+ rotateY: {
182
+ defaultValue: 0
183
+ },
184
+ rotateZ: {
185
+ defaultValue: 0
186
+ },
187
+ translateX: {
188
+ defaultValue: 0
189
+ },
190
+ translateY: {
191
+ defaultValue: 0
192
+ },
193
+ translateZ: {
194
+ defaultValue: 0
195
+ },
196
+ scale: {
197
+ defaultValue: 1
198
+ }
199
+ };
200
+
201
+ var RotationXMatrix = function(a) {
202
+ return $M([
203
+ [1,0,0,0],
204
+ [0,Math.cos(a), Math.sin(-a), 0],
205
+ [0,Math.sin(a), Math.cos( a), 0],
206
+ [0,0,0,1]
207
+ ]);
208
+ };
209
+
210
+ var RotationYMatrix = function(b) {
211
+ return $M([
212
+ [Math.cos( b), 0, Math.sin(b),0],
213
+ [0,1,0,0],
214
+ [Math.sin(-b), 0, Math.cos(b), 0],
215
+ [0,0,0,1]
216
+ ]);
217
+ };
218
+
219
+ var RotationZMatrix = function(c) {
220
+ return $M([
221
+ [Math.cos(c), Math.sin(-c), 0, 0],
222
+ [Math.sin(c), Math.cos( c), 0, 0],
223
+ [0,0,1,0],
224
+ [0,0,0,1]
225
+ ]);
226
+ };
227
+
228
+ var TranslationMatrix = function(tx,ty,tz) {
229
+ return $M([
230
+ [1,0,0,0],
231
+ [0,1,0,0],
232
+ [0,0,1,0],
233
+ [tx,ty,tz,1]
234
+ ]);
235
+ };
236
+
237
+ var ScaleMatrix = function(s) {
238
+ return $M([
239
+ [s,0,0,0],
240
+ [0,s,0,0],
241
+ [0,0,s,0],
242
+ [0,0,0,1]
243
+ ]);
244
+ };
245
+
246
+ var applyMatrix = function(elem) {
247
+ var transforms = $(elem).data('transforms');
248
+
249
+ var rotX = transforms.rotateX || properties.rotateX.defaultValue,
250
+ rotY = transforms.rotateY || properties.rotateY.defaultValue,
251
+ rotZ = transforms.rotateZ || properties.rotateZ.defaultValue,
252
+ scale = transforms.scale || properties.scale.defaultValue,
253
+ translateX = transforms.translateX || properties.translateX.defaultValue,
254
+ translateY = transforms.translateY || properties.translateY.defaultValue,
255
+ translateZ = transforms.translateZ || properties.translateZ.defaultValue;
256
+
257
+ var tM = RotationXMatrix(rotX)
258
+ .x(RotationYMatrix(rotY))
259
+ .x(RotationZMatrix(rotZ))
260
+ .x(ScaleMatrix(scale))
261
+ .x(TranslationMatrix(translateX,translateY,translateZ));
262
+
263
+ s = "matrix3d(";
264
+ s += tM.e(1,1).toFixed(10) + "," + tM.e(1,2).toFixed(10) + "," + tM.e(1,3).toFixed(10) + "," + tM.e(1,4).toFixed(10) + ",";
265
+ s += tM.e(2,1).toFixed(10) + "," + tM.e(2,2).toFixed(10) + "," + tM.e(2,3).toFixed(10) + "," + tM.e(2,4).toFixed(10) + ",";
266
+ s += tM.e(3,1).toFixed(10) + "," + tM.e(3,2).toFixed(10) + "," + tM.e(3,3).toFixed(10) + "," + tM.e(3,4).toFixed(10) + ",";
267
+ s += tM.e(4,1).toFixed(10) + "," + tM.e(4,2).toFixed(10) + "," + tM.e(4,3).toFixed(10) + "," + tM.e(4,4).toFixed(10);
268
+ s += ")";
269
+
270
+ elem.style[transformProperty] = s;
271
+ }
272
+
273
+ var hookFor = function(name) {
274
+
275
+ $.fx.step[name] = function(fx){
276
+ $.cssHooks[name].set( fx.elem, fx.now + fx.unit );
277
+ };
278
+
279
+ return {
280
+ get: function( elem, computed, extra ) {
281
+ var transforms = $(elem).data('transforms');
282
+ if (transforms === undefined) {
283
+ transforms = {};
284
+ $(elem).data('transforms',transforms);
285
+ }
286
+
287
+ return transforms[name] || properties[name].defaultValue;
288
+ },
289
+ set: function( elem, value) {
290
+ var transforms = $(elem).data('transforms');
291
+ if (transforms === undefined) transforms = {};
292
+ var propInfo = properties[name];
293
+
294
+ if (typeof propInfo.apply === 'function') {
295
+ transforms[name] = propInfo.apply(transforms[name] || propInfo.defaultValue, value);
296
+ } else {
297
+ transforms[name] = value
298
+ }
299
+
300
+ $(elem).data('transforms',transforms);
301
+ applyMatrix(elem);
302
+ }
303
+ }
304
+ }
305
+
306
+ if (transformProperty) {
307
+ for (var name in properties) {
308
+ $.cssHooks[name] = hookFor(name);
309
+ $.cssNumber[name] = true;
310
+ }
311
+ }
312
+
313
+ })(jQuery);
314
+
315
+ })({});
316
+
317
+
318
+ (function(exports) {
319
+ // ==========================================================================
320
+ // Project: AcceleratedEffects
321
+ // Copyright: ©2011 Majd Taby
322
+ // License: Licensed under MIT license (see license.js)
323
+ // ==========================================================================
324
+
325
+ })({});
326
+
327
+ (function(exports) {
328
+ // ==========================================================================
329
+ // Project: SproutCore Runtime
330
+ // Copyright: ©2011 Strobe Inc. and contributors.
331
+ // License: Licensed under MIT license (see license.js)
332
+ // ==========================================================================
333
+
334
+ var get = SC.get;
335
+ var set = SC.set;
336
+
337
+ /**
338
+ @class
339
+
340
+ Registry of known gestures in the system. This is a singleton class, and is
341
+ used by SC.View to analyze instances of SC.View for gesture support.
342
+
343
+ You will not use this class yourself. Rather, gesture recognizers will call
344
+ SC.Gestures.register(name, recognizer) when they want to make the system aware
345
+ of them.
346
+
347
+ @private
348
+ @extends SC.Object
349
+ */
350
+ SC.Gestures = SC.Object.create(
351
+ /** @scope SC.Gestures.prototype */{
352
+
353
+ _registeredGestures: null,
354
+
355
+ init: function() {
356
+ this._registeredGestures = {};
357
+
358
+ return this._super();
359
+ },
360
+
361
+ /**
362
+ Registers a gesture recognizer to the system. The gesture recognizer is
363
+ identified by the name parameter, which must be globally unique.
364
+ */
365
+ register: function(name, /** SC.Gesture */recognizer) {
366
+ var registeredGestures = this._registeredGestures;
367
+
368
+ if (registeredGestures[name] !== undefined) {
369
+ throw new SC.Error(name+" already exists as a registered gesture recognizers. Gesture recognizers must have globally unique names.");
370
+ }
371
+
372
+ registeredGestures[name] = recognizer;
373
+ },
374
+
375
+ unregister: function(name) {
376
+ var registeredGestures = this._registeredGestures;
377
+
378
+ if (registeredGestures[name] !== undefined) {
379
+ registeredGestures[name] = undefined;
380
+ }
381
+ },
382
+
383
+ /**
384
+ Registers a gesture recognizer to the system. The gesture recognizer is
385
+ identified by the name parameter, which must be unique across the system.
386
+ */
387
+ knownGestures: function() {
388
+ var registeredGestures = this._registeredGestures;
389
+
390
+ return (registeredGestures)? registeredGestures : {};
391
+ }
392
+
393
+ });
394
+
395
+
396
+ })({});
397
+
398
+
399
+ (function(exports) {
400
+ // ==========================================================================
401
+ // Project: SproutCore Runtime
402
+ // Copyright: ©2011 Strobe Inc. and contributors.
403
+ // License: Licensed under MIT license (see license.js)
404
+ // ==========================================================================
405
+
406
+ var get = SC.get;
407
+ var set = SC.set;
408
+
409
+ /**
410
+ @class
411
+
412
+ Manages multiplegesture recognizers that are associated with a view.
413
+ This class is instantiated automatically by SC.View and you wouldn't
414
+ interact with it yourself.
415
+
416
+ SC.GestureManager mainly acts as a composite for the multiple gesture
417
+ recognizers associated with a view. Whenever it gets a touch event, it
418
+ relays it to the gestures. The other main resposibility of
419
+ SC.GestureManager is to handle re-dispatching of events to the view.
420
+
421
+ @extends SC.Object
422
+ */
423
+ SC.GestureManager = SC.Object.extend({
424
+
425
+ /**
426
+ An array containing all the gesture recognizers associated with a
427
+ view. This is set automatically by SC.View.
428
+
429
+ @default null
430
+ @type Array
431
+ */
432
+ gestures: null,
433
+
434
+ /**
435
+ Internal hash used to keep a list of the events that need to be
436
+ re-dispatched to the views. It's used so we don't re-dispatch
437
+ the same event multiple times to the same view.
438
+
439
+ @default null
440
+ @type Array
441
+ */
442
+ _redispatchQueue: null,
443
+
444
+ _redispatchToNearestParentViewWaitingForTouches: function(evt, view) {
445
+ var foundManager = null,
446
+ successful = false;
447
+ var view = get(view, 'parentView');
448
+
449
+ while(view) {
450
+ var manager = get(view, 'eventManager');
451
+
452
+ if (manager !== undefined && manager !== null) {
453
+ var gestures = get(manager, 'gestures');
454
+
455
+ for (var i=0, l=gestures.length; i<l; i++) {
456
+ if (get(gestures[i], 'state') === SC.Gesture.WAITING_FOR_TOUCHES) {
457
+ foundManager = manager;
458
+ }
459
+ }
460
+
461
+ if (foundManager) {
462
+ successful = true;
463
+ foundManager.touchStart(evt, view);
464
+ break;
465
+ }
466
+ }
467
+
468
+ view = get(view, 'parentView');
469
+ }
470
+
471
+ return successful;
472
+ },
473
+
474
+ /**
475
+ Relays touchStart events to all the gesture recognizers to the
476
+ specified view
477
+
478
+ @return Boolen
479
+ */
480
+ touchStart: function(evt, view) {
481
+ if (this._redispatchToNearestParentViewWaitingForTouches(evt, view)) {
482
+ return;
483
+ }
484
+
485
+ return this._invokeEvent('touchStart',evt, view);
486
+ },
487
+
488
+ /**
489
+ Relays touchMove events to all the gesture recognizers to the
490
+ specified view
491
+
492
+ @return Boolen
493
+ */
494
+ touchMove: function(evt, view) {
495
+ return this._invokeEvent('touchMove',evt, view);
496
+ },
497
+
498
+ /**
499
+ Relays touchEnd events to all the gesture recognizers to the
500
+ specified view
501
+
502
+ @return Boolen
503
+ */
504
+ touchEnd: function(evt, view) {
505
+ return this._invokeEvent('touchEnd',evt, view);
506
+ },
507
+
508
+ /**
509
+ Relays touchCancel events to all the gesture recognizers to the
510
+ specified view
511
+
512
+ @return Boolen
513
+ */
514
+ touchCancel: function(evt, view) {
515
+ return this._invokeEvent('touchCancel',evt, view);
516
+ },
517
+
518
+ /**
519
+ Relays an event to the gesture recognizers. Used internally
520
+ by the touch event listeners.
521
+
522
+ @private
523
+ @return Boolean
524
+ */
525
+ _invokeEvent: function(eventName, eventObject, view) {
526
+ var gestures = get(this, 'gestures'),
527
+ gesture, result = true;
528
+
529
+ this._redispatchQueue = {};
530
+
531
+ for (var i=0, l=gestures.length; i < l; i++) {
532
+ gesture = gestures[i];
533
+ handler = gesture[eventName];
534
+
535
+ if (SC.typeOf(handler) === 'function') {
536
+ result = handler.call(gesture, eventObject, view, this);
537
+ }
538
+ };
539
+
540
+ this._flushReDispatchQueue();
541
+
542
+ return result;
543
+ },
544
+
545
+ /**
546
+ Similar to _invokeEvent, but instead of invoking the event
547
+ to the gesture recognizers, it re-dispatches the event to the
548
+ view. This method is used by the gesture recognizers when they
549
+ want to let the view respond to the original events.
550
+ */
551
+ redispatchEventToView: function(view, eventName, eventObject) {
552
+ var queue = this._redispatchQueue;
553
+
554
+ if (queue[eventName] === undefined) {
555
+ queue[eventName] = [];
556
+ }
557
+ else {
558
+ var views = queue[eventName];
559
+
560
+ for (var i=0, l=views.length; i<l; i++) {
561
+ if (view === views[i].view) {
562
+ return;
563
+ }
564
+ }
565
+ }
566
+
567
+ var originalEvent = null;
568
+ if (eventObject && eventObject.originalEvent) originalEvent = eventObject.originalEvent;
569
+
570
+ queue[eventName].push({
571
+ view: view,
572
+ originalEvent: originalEvent
573
+ });
574
+ },
575
+
576
+ /**
577
+ This method is used internally by _invokeEvent. It re-dispatches
578
+ events to the view if the gestures decided they want to.
579
+ */
580
+ _flushReDispatchQueue: function() {
581
+ var queue = this._redispatchQueue;
582
+
583
+ for (var eventName in queue) {
584
+ var views = queue[eventName];
585
+
586
+ for (var i=0, l=views.length; i<l; i++) {
587
+ var view = views[i].view;
588
+ var event = jQuery.Event(eventName);
589
+
590
+ event.originalEvent = views[i].originalEvent;
591
+
592
+ // Trigger event so it bubbles up the hierarchy
593
+ view.$().trigger(event, this);
594
+ }
595
+ }
596
+ }
597
+
598
+ });
599
+
600
+ })({});
601
+
602
+
603
+ (function(exports) {
604
+ // ==========================================================================
605
+ // Project: SproutCore Touch
606
+ // Copyright: ©2011 Strobe Inc. and contributors.
607
+ // License: Licensed under MIT license (see license.js)
608
+ // ==========================================================================
609
+
610
+ var get = SC.get;
611
+ var set = SC.set;
612
+
613
+ /**
614
+ @class
615
+ @private
616
+
617
+ Used to manage and maintain a list of active touches related to a gesture
618
+ recognizer.
619
+ */
620
+ SC.TouchList = SC.Object.extend({
621
+ touches: null,
622
+
623
+ timestamp: null,
624
+
625
+ init: function() {
626
+ this._super();
627
+
628
+ set(this, 'touches', []);
629
+ },
630
+
631
+ addTouch: function(touch) {
632
+ var touches = get(this, 'touches');
633
+ touches.push(touch);
634
+ this.notifyPropertyChange('touches');
635
+ },
636
+
637
+ updateTouch: function(touch) {
638
+ var touches = get(this, 'touches');
639
+
640
+ for (var i=0, l=touches.length; i<l; i++) {
641
+ var _t = touches[i];
642
+
643
+ if (_t.identifier === touch.identifier) {
644
+ touches[i] = touch;
645
+ this.notifyPropertyChange('touches');
646
+ break;
647
+ }
648
+ }
649
+ },
650
+
651
+ removeTouch: function(touch) {
652
+ var touches = get(this, 'touches');
653
+
654
+ for (var i=0, l=touches.length; i<l; i++) {
655
+ var _t = touches[i];
656
+
657
+ if (_t.identifier === touch.identifier) {
658
+ touches.splice(i,1);
659
+ this.notifyPropertyChange('touches');
660
+ break;
661
+ }
662
+ }
663
+ },
664
+
665
+ removeAllTouches: function() {
666
+ set(this, 'touches', []);
667
+ },
668
+
669
+ touchWithId: function(id) {
670
+ var ret = null,
671
+ touches = get(this, 'touches');
672
+
673
+ for (var i=0, l=touches.length; i<l; i++) {
674
+ var _t = touches[i];
675
+
676
+ if (_t.identifier === id) {
677
+ ret = _t;
678
+ break;
679
+ }
680
+ }
681
+
682
+ return ret;
683
+ },
684
+
685
+ length: function() {
686
+ var touches = get(this, 'touches');
687
+ return touches.length;
688
+ }.property('touches').cacheable()
689
+ });
690
+
691
+ })({});
692
+
693
+
694
+ (function(exports) {
695
+ // ==========================================================================
696
+ // Project: SproutCore Runtime
697
+ // Copyright: ©2011 Strobe Inc. and contributors.
698
+ // License: Licensed under MIT license (see license.js)
699
+ // ==========================================================================
700
+
701
+
702
+
703
+ var get = SC.get;
704
+ var set = SC.set;
705
+
706
+ var sigFigs = 100;
707
+
708
+ /**
709
+ @class
710
+
711
+ Base class for all gesture recognizers. Handles low-level touch and state
712
+ management, and provides some utility methods and some required methods all
713
+ gesture recognizers are expected to implement.
714
+
715
+ Overview
716
+ =========
717
+
718
+ Gestures coalesce multiple touch events to a single higher-level gesture
719
+ event. For example, a tap gesture recognizer takes information about a
720
+ touchstart event, a few touchmove events, and a touchend event and uses
721
+ some heuristics to decide whether or not that sequence of events qualifies
722
+ as a tap event. If it does, then it will notify the view of the higher-level
723
+ tap events.
724
+
725
+ Gesture events follow the format:
726
+
727
+ * [GESTURE_NAME]Start - Sent when a gesture has gathered enough information
728
+ to begin tracking the gesture
729
+
730
+ * [GESTURE_NAME]Change - Sent when a gesture has already started and has
731
+ received touchmove events that cause its state to change
732
+
733
+ * [GESTURE_NAME]End - Sent when a touchend event is received and the gesture
734
+ recognizer decides that the gesture is finished.
735
+
736
+ * [GESTURE_NAME]Cancel - Sent when a touchcancel event is received.
737
+
738
+ There are two types of gesturess: Discrete and Continuous gestures. In contrast
739
+ to continuous gestures, discrete gestures don't have any change events. Rather,
740
+ the start and end events are the only one that gets sent.
741
+
742
+ Usage
743
+ =======
744
+
745
+ While you wouldn't use SC.Gesture directly, all its subclasses have the same
746
+ API. For example, to implement pinch on a view, you implement pinchChange and
747
+ optionally pinchStart, pinchEnd and pinchCancel.
748
+
749
+ var myView = SC.View.create({
750
+ pinchStart: function(recognizer) {
751
+ this.$().css('background','red');
752
+ },
753
+
754
+ pinchChange: function(recognizer) {
755
+ var scale = recognizer.get('scale');
756
+ this.$().css('-webkit-transform','scale3d('+scale+','+scale+',1)');
757
+ },
758
+
759
+ pinchEnd: function(recognizer) {
760
+ this.$().css('background','blue');
761
+ },
762
+
763
+ pinchCancel: function(recognizer) {
764
+ this.$().css('background','blue');
765
+ }
766
+ });
767
+
768
+ pinchStart(), pinchEnd() and pinchCancel() will only get called once per
769
+ gesture, but pinchChange() will get called repeatedly called every time
770
+ one of the touches moves.
771
+
772
+ Creating Custom Gesture Recognizers
773
+ ======
774
+
775
+ SC.Gesture also defines an API which its subclasses can implement to build
776
+ custom gestures. The methods are:
777
+
778
+ * **didBecomePossible** - Called when a gesture enters a possible state. This
779
+ means the gesture recognizer has accepted enough touches to match
780
+ the number of required touches. You would usually initialize your state
781
+ in this callback.
782
+
783
+ * **eventWasRejected** - Called if a view returns false from a gesture event.
784
+ This callback allows you to reset internal state if the user rejects
785
+ an event.
786
+
787
+ * **shouldBegin** - Allows a gesture to block itself from entering a began state.
788
+ This callback will continuously be called as touches move until it begins.
789
+
790
+ * **shouldEnd** - Allows a gesture to block itself from entering an ended state.
791
+ This callback gets called whenever a tracked touch gets a touchEnd event.
792
+
793
+ * **didBegin** - Called when the gesture enters a began state. Called before the
794
+ view receives the Start event.
795
+
796
+ * **didChange** - Called when the gesture enters a began state, and when one of the
797
+ touches moves. Called before the view receives the Change event.
798
+
799
+ * **didEnd** - Called when the gesture enters an ended state. Called before the
800
+ view receives the End event.
801
+
802
+ * **didCancel** - Called when the gesture enters a cancelled state. Called before the
803
+ view receives the Cancel event.
804
+
805
+ In all the callbacks, you can use the `touches` protected property to access the
806
+ touches hash. The touches hash is keyed on the identifiers of the touches, and the
807
+ values are the jQuery.Event objects.
808
+
809
+ You can also use the numberOfActiveTouches property to inspect how many touches
810
+ are active, this is mostly useful in shouldBegin since every other callback can
811
+ assume that there are as many active touches as specified in the
812
+ numberOfRequiredTouches property.
813
+
814
+ Discrete vs Continuous Gestures
815
+ =======
816
+
817
+ There are two main classes of gesture recognizers: Discrete and Continuous
818
+ gestures. Discrete gestures do not get Change events sent, since they represent
819
+ a single, instantaneous event, rather than a continuous motion. If you are
820
+ implementing your own discrete gesture recognizer, you must set the
821
+ isDiscreteGesture property to yes, and SC.Gesture will adapt its behavior.
822
+
823
+ Discrete gestures use the shouldEnd callback to either accept or decline the gesture
824
+ event. If it is delined, then the gesture will enter a Cancelled state and trigger
825
+ the Cancel event on the view.
826
+
827
+ @extends SC.Object
828
+ */
829
+
830
+ SC.Gesture = SC.Object.extend(
831
+ /** @scope SC.Gesture.prototype */{
832
+
833
+ /**
834
+ The current state of the gesture recognizer. This value can be any one
835
+ of the states defined at the end of this file.
836
+
837
+ @type Number
838
+ */
839
+ state: null,
840
+
841
+ /**
842
+ A string of the gesture recognizer's name. This value is set automatically
843
+ but SC.Gestures when a gesture is registered.
844
+
845
+ @type String
846
+ */
847
+ name: null,
848
+
849
+ /**
850
+ Specifies whether a gesture is discrete or continuous.
851
+
852
+ @type Boolean
853
+ @default false
854
+ */
855
+ gestureIsDiscrete: false,
856
+
857
+ /**
858
+ You can use the `touches` protected property to access the touches hash. The touches
859
+ hash is keyed on the identifiers of the touches, and the values are the jQuery.Event
860
+ objects.
861
+
862
+ @private
863
+ @type Hash
864
+ */
865
+ touches: null,
866
+
867
+ /**
868
+ You can also use the numberOfActiveTouches property to inspect how many touches
869
+ are active, this is mostly useful in shouldBegin since every other callback can
870
+ assume that there are as many active touches as specified in the
871
+ numberOfRequiredTouches property.
872
+
873
+ @private
874
+ @type Number
875
+ */
876
+ numberOfActiveTouches: 0,
877
+
878
+ /**
879
+ Used to specify the number of touches required for the gesture to enter a possible
880
+ state
881
+
882
+ @private
883
+ @type Number
884
+ */
885
+ numberOfRequiredTouches: 1,
886
+
887
+ init: function() {
888
+ this._super();
889
+ this.touches = SC.TouchList.create();
890
+ },
891
+
892
+ //..............................................
893
+ // Gesture Callbacks
894
+
895
+ /** @private */
896
+ didBecomePossible: function() { },
897
+
898
+ /** @private */
899
+ shouldBegin: function() {
900
+ return true;
901
+ },
902
+
903
+ /** @private */
904
+ didBegin: function() { },
905
+
906
+ /** @private */
907
+ didChange: function() { },
908
+
909
+ /** @private */
910
+ eventWasRejected: function() { },
911
+
912
+ /** @private */
913
+ shouldEnd: function() {
914
+ return true;
915
+ },
916
+
917
+ /** @private */
918
+ didEnd: function() { },
919
+
920
+ /** @private */
921
+ didCancel: function() { },
922
+
923
+ //..............................................
924
+ // Utilities
925
+
926
+ /** @private */
927
+ attemptGestureEventDelivery: function(evt, view, eventName) {
928
+ if (this.notifyViewOfGestureEvent(view, eventName) === false) {
929
+ this.eventWasRejected();
930
+ } else {
931
+ evt.preventDefault();
932
+ }
933
+ },
934
+
935
+ /**
936
+ Given two Touch objects, this method returns the distance between them.
937
+
938
+ @return Number
939
+ */
940
+ distance: function(touches) {
941
+
942
+ if (touches.length < 2) {
943
+ return 0;
944
+ }
945
+
946
+ var first = touches[0];
947
+ var second = touches[1];
948
+
949
+ var x = first.pageX;
950
+ var y = first.pageY;
951
+ var x0 = second.pageX;
952
+ var y0 = second.pageY;
953
+
954
+ return Math.sqrt((x -= x0) * x + (y -= y0) * y);
955
+ },
956
+
957
+ /**
958
+ Given two Touch objects, this method returns the midpoint between them.
959
+
960
+ @return Number
961
+ */
962
+ centerPointForTouches: function(touches) {
963
+ var sumX = 0,
964
+ sumY = 0;
965
+
966
+ for (var i=0, l=touches.length; i<l; i++) {
967
+ var touch = touches[i];
968
+ sumX += touch.pageX;
969
+ sumY += touch.pageY;
970
+ }
971
+
972
+ var location = {
973
+ x: sumX / touches.length,
974
+ y: sumY / touches.length
975
+ };
976
+
977
+ return location;
978
+ },
979
+
980
+ /** @private */
981
+ _objectValues: function(object) {
982
+ var ret = [];
983
+
984
+ for (var item in object ) {
985
+ if (object.hasOwnProperty(item)) {
986
+ ret.push(object[item]);
987
+ }
988
+ }
989
+
990
+ return ret;
991
+ },
992
+
993
+ /**
994
+ Allows the gesture to notify the view it's associated with of a gesture
995
+ event.
996
+
997
+ @private
998
+ */
999
+ notifyViewOfGestureEvent: function(view, eventName, data) {
1000
+ var handler = view[eventName];
1001
+ var result = true;
1002
+
1003
+ if (SC.typeOf(handler) === 'function') {
1004
+ result = handler.call(view, this, data);
1005
+ }
1006
+
1007
+ return result;
1008
+ },
1009
+
1010
+ toString: function() {
1011
+ return SC.Gesture+'<'+SC.guidFor(this)+'>';
1012
+ },
1013
+
1014
+ /** @private */
1015
+ _resetState: function() {
1016
+ this.touches.removeAllTouches();
1017
+ },
1018
+
1019
+ //..............................................
1020
+ // Touch event handlers
1021
+
1022
+ /** @private */
1023
+ touchStart: function(evt, view, manager) {
1024
+ var targetTouches = evt.originalEvent.targetTouches;
1025
+ var _touches = this.touches;
1026
+ var state = get(this, 'state');
1027
+
1028
+ set(_touches, 'timestamp', Date.now());
1029
+
1030
+ //Collect touches by their identifiers
1031
+ for (var i=0, l=targetTouches.length; i<l; i++) {
1032
+ var touch = targetTouches[i];
1033
+
1034
+ if(_touches.touchWithId(touch.identifier) === null && _touches.get('length') < get(this, 'numberOfRequiredTouches')) {
1035
+ _touches.addTouch(touch);
1036
+ }
1037
+ }
1038
+
1039
+ if (_touches.get('length') < get(this, 'numberOfRequiredTouches')) {
1040
+ set(this ,'state', SC.Gesture.WAITING_FOR_TOUCHES);
1041
+
1042
+ } else {
1043
+ // Discrete gestures may skip the possible step if they're ready to begin
1044
+ if (get(this, 'gestureIsDiscrete') && this.shouldBegin()) {
1045
+ set(this, 'state', SC.Gesture.BEGAN);
1046
+ this.didBegin();
1047
+ this.attemptGestureEventDelivery(evt, view, get(this, 'name')+'Start');
1048
+ } else {
1049
+ set(this, 'state', SC.Gesture.POSSIBLE);
1050
+ this.didBecomePossible();
1051
+ }
1052
+ }
1053
+
1054
+ manager.redispatchEventToView(view,'touchstart', evt);
1055
+ },
1056
+
1057
+ /** @private */
1058
+ touchMove: function(evt, view, manager) {
1059
+ var state = get(this, 'state');
1060
+
1061
+ if (state === SC.Gesture.WAITING_FOR_TOUCHES || state === SC.Gesture.ENDED || state === SC.Gesture.CANCELLED) {
1062
+ // Nothing to do here
1063
+ manager.redispatchEventToView(view,'touchmove', evt);
1064
+ return;
1065
+ }
1066
+
1067
+ var changedTouches = evt.originalEvent.changedTouches;
1068
+ var _touches = this.touches;
1069
+
1070
+ set(_touches, 'timestamp', Date.now());
1071
+
1072
+ // Update touches hash
1073
+ for (var i=0, l=changedTouches.length; i<l; i++) {
1074
+ var touch = changedTouches[i];
1075
+ _touches.updateTouch(touch);
1076
+ }
1077
+
1078
+ if (state === SC.Gesture.POSSIBLE) {
1079
+ if (this.shouldBegin()) {
1080
+ set(this, 'state', SC.Gesture.BEGAN);
1081
+ this.didBegin();
1082
+
1083
+ // Give the gesture a chance to update its state so the view can get
1084
+ // updated information in the Start event
1085
+ this.didChange();
1086
+
1087
+ this.attemptGestureEventDelivery(evt, view, get(this, 'name')+'Start');
1088
+ }
1089
+
1090
+ // Discrete gestures don't fire changed events
1091
+ } else if ((state === SC.Gesture.BEGAN || state === SC.Gesture.CHANGED) && !get(this, 'gestureIsDiscrete')) {
1092
+ set(this, 'state', SC.Gesture.CHANGED);
1093
+ this.didChange();
1094
+
1095
+ this.attemptGestureEventDelivery(evt, view, get(this, 'name')+'Change');
1096
+
1097
+ } else {
1098
+ manager.redispatchEventToView(view,'touchmove', evt);
1099
+ }
1100
+ },
1101
+
1102
+ /** @private */
1103
+ touchEnd: function(evt, view, manager) {
1104
+ // Discrete gestures need to cancel if they shouldn't end successfully
1105
+ if (get(this, 'gestureIsDiscrete')) {
1106
+
1107
+ // Discrete gestures use shouldEnd to either accept or decline the gesture.
1108
+ if (this.state === SC.Gesture.BEGAN && this.shouldEnd()) {
1109
+ set(this, 'state', SC.Gesture.ENDED);
1110
+ this.didEnd();
1111
+ this.attemptGestureEventDelivery(evt, view, get(this, 'name')+'End');
1112
+ } else {
1113
+ set(this, 'state', SC.Gesture.CANCELLED);
1114
+ this.didCancel();
1115
+ this.attemptGestureEventDelivery(evt, view, get(this, 'name')+'Cancel');
1116
+ }
1117
+ }
1118
+ else {
1119
+ if (this.state !== SC.Gesture.ENDED && this.shouldEnd()) {
1120
+ set(this, 'state', SC.Gesture.ENDED);
1121
+ this.didEnd();
1122
+
1123
+ this.attemptGestureEventDelivery(evt, view, get(this, 'name')+'End');
1124
+ }
1125
+
1126
+ manager.redispatchEventToView(view,'touchend', evt);
1127
+ }
1128
+
1129
+ this._resetState();
1130
+ },
1131
+
1132
+ /** @private */
1133
+ touchCancel: function(evt, view, manager) {
1134
+ if (this.state !== SC.Gesture.CANCELLED) {
1135
+ this._resetState();
1136
+ set(this, 'state', SC.Gesture.CANCELLED);
1137
+ this.notifyViewOfGestureEvent(view,get(this, 'name')+'Cancel');
1138
+ } else {
1139
+ manager.redispatchEventToView(view,'touchcancel', evt);
1140
+ }
1141
+ }
1142
+ });
1143
+
1144
+ SC.Gesture.WAITING_FOR_TOUCHES = 0;
1145
+ SC.Gesture.POSSIBLE = 1;
1146
+ SC.Gesture.BEGAN = 2;
1147
+ SC.Gesture.CHANGED = 3;
1148
+ SC.Gesture.ENDED = 4;
1149
+ SC.Gesture.CANCELLED = 4;
1150
+
1151
+ })({});
1152
+
1153
+
1154
+ (function(exports) {
1155
+ // ==========================================================================
1156
+ // Project: SproutCore Runtime
1157
+ // Copyright: ©2011 Strobe Inc. and contributors.
1158
+ // License: Licensed under MIT license (see license.js)
1159
+ // ==========================================================================
1160
+
1161
+ var get = SC.get;
1162
+ var set = SC.set;
1163
+
1164
+ var sigFigs = 100;
1165
+
1166
+ /**
1167
+ @class
1168
+
1169
+ Recognizes a multi-touch pinch gesture. Pinch gestures require a specified number
1170
+ of fingers to move and will record and update the scale.
1171
+
1172
+ For pinchChange events, the pinch gesture recognizer includes a scale property
1173
+ which can be applied as a CSS transform directly.
1174
+
1175
+ var myview = SC.View.create({
1176
+ elementId: 'gestureTest',
1177
+ pinchChange: function(recognizer) {
1178
+ var scale = recognizer.get('scale');
1179
+ this.$().css('-webkit-transform','scale3d('+scale+','+scale+',1)');
1180
+ }
1181
+ })
1182
+
1183
+ You can specify how many touches the gesture requires to start using the numberOfRequiredTouches
1184
+ property, which you can set in the pinchOptions hash:
1185
+
1186
+ var myview = SC.View.create({
1187
+ pinchOptions: {
1188
+ numberOfRequiredTouches: 3
1189
+ }
1190
+ ...
1191
+ })
1192
+
1193
+
1194
+ @extends SC.Gesture
1195
+ */
1196
+ SC.PinchGestureRecognizer = SC.Gesture.extend({
1197
+
1198
+ /**
1199
+ The scale value which represents the current amount of scaling that has been applied
1200
+ to the view. You would normally apply this value directly to your element as a 3D
1201
+ scale.
1202
+
1203
+ @type Number
1204
+ */
1205
+ scale: 1,
1206
+
1207
+ numberOfRequiredTouches: 2,
1208
+
1209
+ //..................................................
1210
+ // Private Methods and Properties
1211
+
1212
+ /**
1213
+ Track starting distance between touches per gesture.
1214
+
1215
+ @private
1216
+ @type Number
1217
+ */
1218
+ _startingDistanceBetweenTouches: null,
1219
+
1220
+ /**
1221
+ Used for measuring velocity
1222
+
1223
+ @private
1224
+ @type Number
1225
+ */
1226
+ _previousTimestamp: null,
1227
+
1228
+ /**
1229
+ Used for measuring velocity and scale
1230
+
1231
+ @private
1232
+ @type Number
1233
+ */
1234
+ _previousDistance: 0,
1235
+
1236
+ /**
1237
+ The pixel distance that the fingers need to get closer/farther away by before
1238
+ this gesture is recognized.
1239
+
1240
+ @private
1241
+ @type Number
1242
+ */
1243
+ _deltaThreshold: 5,
1244
+
1245
+ /**
1246
+ Used for rejected events
1247
+
1248
+ @private
1249
+ @type Number
1250
+ */
1251
+ _previousScale: 1,
1252
+
1253
+ /**
1254
+ @private
1255
+ */
1256
+ didBecomePossible: function() {
1257
+ this._startingDistanceBetweenTouches = this.distance(get(this.touches,'touches'));
1258
+ this._previousDistance = this._startingDistanceBetweenTouches;
1259
+ this._previousTimestamp = get(this.touches,'timestamp');
1260
+ },
1261
+
1262
+ shouldBegin: function() {
1263
+ var currentDistanceBetweenTouches = this.distance(get(this.touches,'touches'));
1264
+
1265
+ return Math.abs(currentDistanceBetweenTouches - this._startingDistanceBetweenTouches) >= this._deltaThreshold;
1266
+ },
1267
+
1268
+ didChange: function() {
1269
+ var scale = this._previousScale = get(this, 'scale');
1270
+ var timeDifference = this.touches.timestamp - this._previousTimestamp;
1271
+ var currentDistanceBetweenTouches = this.distance(get(this.touches,'touches'));
1272
+ var distanceDifference = (currentDistanceBetweenTouches - this._previousDistance);
1273
+
1274
+ set(this, 'velocity', distanceDifference / timeDifference);
1275
+ set(this, 'scale', currentDistanceBetweenTouches / this._previousDistance);
1276
+
1277
+ this._previousTimestamp = get(this.touches,'timestamp');
1278
+ this._previousDistance = currentDistanceBetweenTouches;
1279
+ },
1280
+
1281
+ eventWasRejected: function() {
1282
+ set(this, 'scale', this._previousScale);
1283
+ }
1284
+ });
1285
+
1286
+ SC.Gestures.register('pinch', SC.PinchGestureRecognizer);
1287
+
1288
+ })({});
1289
+
1290
+
1291
+ (function(exports) {
1292
+ // ==========================================================================
1293
+ // Project: SproutCore Runtime
1294
+ // Copyright: ©2011 Strobe Inc. and contributors.
1295
+ // License: Licensed under MIT license (see license.js)
1296
+ // ==========================================================================
1297
+
1298
+ var get = SC.get;
1299
+ var set = SC.set;
1300
+ var x = 0;
1301
+
1302
+ /**
1303
+ @class
1304
+
1305
+ Recognizes a multi-touch pan gesture. Pan gestures require a specified number
1306
+ of fingers to move and will record and update the center point between the
1307
+ touches.
1308
+
1309
+ For panChange events, the pan gesture recognizer includes a translation property
1310
+ which can be applied as a CSS transform directly. Translation values are hashes
1311
+ which contain an x and a y value.
1312
+
1313
+ var myview = SC.View.create({
1314
+ elementId: 'gestureTest',
1315
+ panChange: function(recognizer) {
1316
+ var translation = recognizer.get('translation');
1317
+ this.$().css('-webkit-transform','translate3d('+translate.x+'px,'+translate.y+'px,0)');
1318
+ }
1319
+ })
1320
+
1321
+ You can specify how many touches the gesture requires to start using the numberOfRequiredTouches
1322
+ property, which you can set in the panOptions hash:
1323
+
1324
+ var myview = SC.View.create({
1325
+ panOptions: {
1326
+ numberOfRequiredTouches: 3
1327
+ }
1328
+ ...
1329
+ })
1330
+
1331
+ @extends SC.Gesture
1332
+ */
1333
+ SC.PanGestureRecognizer = SC.Gesture.extend({
1334
+
1335
+ /**
1336
+ The translation value which represents the current amount of movement that has been applied
1337
+ to the view. You would normally apply this value directly to your element as a 3D
1338
+ transform.
1339
+
1340
+ @type Location
1341
+ */
1342
+ translation: null,
1343
+
1344
+ //..................................................
1345
+ // Private Methods and Properties
1346
+
1347
+ /**
1348
+ Used to measure offsets
1349
+
1350
+ @private
1351
+ @type Number
1352
+ */
1353
+ _previousLocation: null,
1354
+
1355
+ /**
1356
+ Used for rejected events
1357
+
1358
+ @private
1359
+ @type Hash
1360
+ */
1361
+ _previousTranslation: null,
1362
+
1363
+ /**
1364
+ The pixel distance that the fingers need to move before this gesture is recognized.
1365
+
1366
+ @private
1367
+ @type Number
1368
+ */
1369
+ _translationThreshold: 5,
1370
+
1371
+ init: function() {
1372
+ this._super();
1373
+ set(this, 'translation', {x:0,y:0});
1374
+ },
1375
+
1376
+ didBecomePossible: function() {
1377
+ this._previousLocation = this.centerPointForTouches(get(this.touches,'touches'));
1378
+ },
1379
+
1380
+ shouldBegin: function() {
1381
+ var previousLocation = this._previousLocation;
1382
+ var currentLocation = this.centerPointForTouches(get(this.touches,'touches'));
1383
+
1384
+ var x = previousLocation.x;
1385
+ var y = previousLocation.y;
1386
+ var x0 = currentLocation.x;
1387
+ var y0 = currentLocation.y;
1388
+
1389
+ var distance = Math.sqrt((x -= x0) * x + (y -= y0) * y);
1390
+ return distance >= this._translationThreshold;
1391
+ },
1392
+
1393
+ didChange: function() {
1394
+ var previousLocation = this._previousLocation;
1395
+ var currentLocation = this.centerPointForTouches(get(this.touches,'touches'));
1396
+ var translation = {x:currentLocation.x, y:currentLocation.y};
1397
+
1398
+ translation.x = currentLocation.x - previousLocation.x;
1399
+ translation.y = currentLocation.y - previousLocation.y;
1400
+
1401
+ this._previousTranslation = get(this, 'translation');
1402
+ set(this, 'translation', translation);
1403
+ this._previousLocation = currentLocation;
1404
+ },
1405
+
1406
+ eventWasRejected: function() {
1407
+ set(this, 'translation', this._previousTranslation);
1408
+ }
1409
+ });
1410
+
1411
+ SC.Gestures.register('pan', SC.PanGestureRecognizer);
1412
+
1413
+ })({});
1414
+
1415
+
1416
+ (function(exports) {
1417
+ // ==========================================================================
1418
+ // Project: SproutCore Runtime
1419
+ // Copyright: ©2011 Strobe Inc. and contributors.
1420
+ // License: Licensed under MIT license (see license.js)
1421
+ // ==========================================================================
1422
+
1423
+ var get = SC.get;
1424
+ var set = SC.set;
1425
+
1426
+ /**
1427
+ @class
1428
+
1429
+ Recognizes a multi-touch tap gesture. Tap gestures allow for a certain amount
1430
+ of wiggle-room between a start and end of a touch. Taps are discrete gestures
1431
+ so only tapStart() and tapEnd() will get fired on a view.
1432
+
1433
+ var myview = SC.View.create({
1434
+ elementId: 'gestureTest',
1435
+ tapStart: function(recognizer) {
1436
+ $('#gestureTest').css('background','green');
1437
+ },
1438
+
1439
+ tapEnd: function(recognizer) {
1440
+ $('#gestureTest').css('background','yellow');
1441
+ }
1442
+ })
1443
+
1444
+ You can specify how many touches the gesture requires to start using the numberOfRequiredTouches
1445
+ property, which you can set in the panOptions hash:
1446
+
1447
+ var myview = SC.View.create({
1448
+ panOptions: {
1449
+ numberOfRequiredTouches: 3
1450
+ }
1451
+ ...
1452
+ })
1453
+
1454
+ And you can also specify the number of taps required for the gesture to fire using the numberOfTaps
1455
+ property.
1456
+
1457
+ @extends SC.Gesture
1458
+ */
1459
+ SC.TapGestureRecognizer = SC.Gesture.extend({
1460
+
1461
+ /**
1462
+ The translation value which represents the current amount of movement that has been applied
1463
+ to the view. You would normally apply this value directly to your element as a 3D
1464
+ transform.
1465
+
1466
+ @type Location
1467
+ */
1468
+ numberOfTaps: 1,
1469
+
1470
+ //..................................................
1471
+ // Private Methods and Properties
1472
+
1473
+ /** @private */
1474
+ MULTITAP_DELAY: 150,
1475
+
1476
+ /** @private */
1477
+ gestureIsDiscrete: true,
1478
+
1479
+ /** @private */
1480
+ _initialLocation: null,
1481
+
1482
+ /** @private */
1483
+ _waitingInterval: null,
1484
+
1485
+ /** @private */
1486
+ _waitingForMoreTouches: false,
1487
+
1488
+ /** @private */
1489
+ _moveThreshold: 10,
1490
+
1491
+ shouldBegin: function() {
1492
+ return get(this.touches,'length') === get(this, 'numberOfRequiredTouches');
1493
+ },
1494
+
1495
+ didBegin: function() {
1496
+ this._initialLocation = this.centerPointForTouches(get(this.touches,'touches'));
1497
+
1498
+ if (get(this.touches,'length') < get(this, 'numberOfTaps')) {
1499
+ this._waitingForMoreTouches = true;
1500
+ this._waitingInterval = window.setInterval(this._intervalFired,this.MULTITAP_DELAY);
1501
+ }
1502
+ },
1503
+
1504
+ shouldEnd: function() {
1505
+ var currentLocation = this.centerPointForTouches(get(this.touches,'touches'));
1506
+
1507
+ var x = this._initialLocation.x;
1508
+ var y = this._initialLocation.y;
1509
+ var x0 = currentLocation.x;
1510
+ var y0 = currentLocation.y;
1511
+
1512
+ var distance = Math.sqrt((x -= x0) * x + (y -= y0) * y);
1513
+
1514
+ return (Math.abs(distance) < this._moveThreshold) && !this._waitingForMoreTouches;
1515
+ },
1516
+
1517
+ didEnd: function() {
1518
+ this._initialLocation = null;
1519
+ },
1520
+
1521
+ didCancel: function() {
1522
+ this._initialLocation = null;
1523
+ },
1524
+
1525
+ _intervalFired: function() {
1526
+ window.clearInterval(this._waitingInterval);
1527
+ _waitingForMoreTouches = false;
1528
+ }
1529
+ });
1530
+
1531
+ SC.Gestures.register('tap', SC.TapGestureRecognizer);
1532
+
1533
+ })({});
1534
+
1535
+
1536
+ (function(exports) {
1537
+
1538
+
1539
+
1540
+ })({});
1541
+
1542
+
1543
+ (function(exports) {
1544
+ // ==========================================================================
1545
+ // Project: SproutCore Runtime
1546
+ // Copyright: ©2011 Strobe Inc. and contributors.
1547
+ // License: Licensed under MIT license (see license.js)
1548
+ // ==========================================================================
1549
+
1550
+ var get = SC.get;
1551
+ var set = SC.set;
1552
+
1553
+ /**
1554
+ @class
1555
+
1556
+ Extends SC.View by making the init method gesture-aware.
1557
+
1558
+ @extends SC.Object
1559
+ */
1560
+ SC.View.reopen(
1561
+ /** @scope SC.View.prototype */{
1562
+
1563
+ /**
1564
+ The SC.GestureManager instance which will manager the gestures of the view.
1565
+ This object is automatically created and set at init-time.
1566
+
1567
+ @default null
1568
+ @type Array
1569
+ */
1570
+ eventManager: null,
1571
+
1572
+ /**
1573
+ Inspects the properties on the view instance and create gestures if they're
1574
+ used.
1575
+ */
1576
+ init: function() {
1577
+ this._super();
1578
+
1579
+ var knownGestures = SC.Gestures.knownGestures();
1580
+ var eventManager = get(this, 'eventManager');
1581
+
1582
+ if (knownGestures && !eventManager) {
1583
+ var gestures = [];
1584
+
1585
+ for (var gesture in knownGestures) {
1586
+ if (this[gesture+'Start'] || this[gesture+'Change'] || this[gesture+'End']) {
1587
+
1588
+ var optionsHash;
1589
+ if (this[gesture+'Options'] !== undefined && typeof this[gesture+'Options'] === 'object') {
1590
+ optionsHash = this[gesture+'Options'];
1591
+ } else {
1592
+ optionsHash = {};
1593
+ }
1594
+
1595
+ optionsHash.name = gesture;
1596
+ optionsHash.view = this;
1597
+
1598
+ gestures.push(knownGestures[gesture].create(optionsHash));
1599
+ }
1600
+ }
1601
+
1602
+ var manager = SC.GestureManager.create({
1603
+ gestures: gestures
1604
+ });
1605
+
1606
+ set(this, 'eventManager', manager);
1607
+
1608
+ }
1609
+ }
1610
+
1611
+ });
1612
+
1613
+
1614
+ })({});
1615
+
1616
+
1617
+ (function(exports) {
1618
+
1619
+
1620
+
1621
+
1622
+ })({});
1623
+
1624
+
1625
+ (function(exports) {
1626
+
1627
+
1628
+
1629
+ })({});