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.
- data/lib/rasputin/version.rb +1 -1
- data/lib/rasputin.rb +5 -1
- data/rasputin.gemspec +4 -3
- data/vendor/assets/javascripts/sproutcore-touch.js +1627 -0
- data/vendor/assets/stylesheets/normalize.css +2 -2
- metadata +5 -6
- data/lib/rasputin/engine.rb +0 -4
@@ -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
|
+
})({});
|