angularjs-rails 1.2.14 → 1.2.15

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,25 +1,172 @@
1
1
  /**
2
- * @license AngularJS v1.1.5
3
- * (c) 2010-2012 Google, Inc. http://angularjs.org
2
+ * @license AngularJS v1.3.0-beta.3
3
+ * (c) 2010-2014 Google, Inc. http://angularjs.org
4
4
  * License: MIT
5
5
  */
6
- (function(window, angular, undefined) {
7
- 'use strict';
6
+ (function(window, angular, undefined) {'use strict';
8
7
 
9
8
  /**
10
- * @ngdoc overview
11
- * @name ngMobile
9
+ * @ngdoc module
10
+ * @name ngTouch
12
11
  * @description
13
- * Touch events and other mobile helpers.
14
- * Based on jQuery Mobile touch event handling (jquerymobile.com)
12
+ *
13
+ * # ngTouch
14
+ *
15
+ * The `ngTouch` module provides touch events and other helpers for touch-enabled devices.
16
+ * The implementation is based on jQuery Mobile touch event handling
17
+ * ([jquerymobile.com](http://jquerymobile.com/)).
18
+ *
19
+ *
20
+ * See {@link ngTouch.$swipe `$swipe`} for usage.
21
+ *
22
+ * <div doc-module-components="ngTouch"></div>
23
+ *
15
24
  */
16
25
 
17
- // define ngMobile module
18
- var ngMobile = angular.module('ngMobile', []);
26
+ // define ngTouch module
27
+ /* global -ngTouch */
28
+ var ngTouch = angular.module('ngTouch', []);
29
+
30
+ /* global ngTouch: false */
31
+
32
+ /**
33
+ * @ngdoc service
34
+ * @name $swipe
35
+ *
36
+ * @description
37
+ * The `$swipe` service is a service that abstracts the messier details of hold-and-drag swipe
38
+ * behavior, to make implementing swipe-related directives more convenient.
39
+ *
40
+ * Requires the {@link ngTouch `ngTouch`} module to be installed.
41
+ *
42
+ * `$swipe` is used by the `ngSwipeLeft` and `ngSwipeRight` directives in `ngTouch`, and by
43
+ * `ngCarousel` in a separate component.
44
+ *
45
+ * # Usage
46
+ * The `$swipe` service is an object with a single method: `bind`. `bind` takes an element
47
+ * which is to be watched for swipes, and an object with four handler functions. See the
48
+ * documentation for `bind` below.
49
+ */
50
+
51
+ ngTouch.factory('$swipe', [function() {
52
+ // The total distance in any direction before we make the call on swipe vs. scroll.
53
+ var MOVE_BUFFER_RADIUS = 10;
54
+
55
+ function getCoordinates(event) {
56
+ var touches = event.touches && event.touches.length ? event.touches : [event];
57
+ var e = (event.changedTouches && event.changedTouches[0]) ||
58
+ (event.originalEvent && event.originalEvent.changedTouches &&
59
+ event.originalEvent.changedTouches[0]) ||
60
+ touches[0].originalEvent || touches[0];
61
+
62
+ return {
63
+ x: e.clientX,
64
+ y: e.clientY
65
+ };
66
+ }
67
+
68
+ return {
69
+ /**
70
+ * @ngdoc method
71
+ * @name $swipe#bind
72
+ *
73
+ * @description
74
+ * The main method of `$swipe`. It takes an element to be watched for swipe motions, and an
75
+ * object containing event handlers.
76
+ *
77
+ * The four events are `start`, `move`, `end`, and `cancel`. `start`, `move`, and `end`
78
+ * receive as a parameter a coordinates object of the form `{ x: 150, y: 310 }`.
79
+ *
80
+ * `start` is called on either `mousedown` or `touchstart`. After this event, `$swipe` is
81
+ * watching for `touchmove` or `mousemove` events. These events are ignored until the total
82
+ * distance moved in either dimension exceeds a small threshold.
83
+ *
84
+ * Once this threshold is exceeded, either the horizontal or vertical delta is greater.
85
+ * - If the horizontal distance is greater, this is a swipe and `move` and `end` events follow.
86
+ * - If the vertical distance is greater, this is a scroll, and we let the browser take over.
87
+ * A `cancel` event is sent.
88
+ *
89
+ * `move` is called on `mousemove` and `touchmove` after the above logic has determined that
90
+ * a swipe is in progress.
91
+ *
92
+ * `end` is called when a swipe is successfully completed with a `touchend` or `mouseup`.
93
+ *
94
+ * `cancel` is called either on a `touchcancel` from the browser, or when we begin scrolling
95
+ * as described above.
96
+ *
97
+ */
98
+ bind: function(element, eventHandlers) {
99
+ // Absolute total movement, used to control swipe vs. scroll.
100
+ var totalX, totalY;
101
+ // Coordinates of the start position.
102
+ var startCoords;
103
+ // Last event's position.
104
+ var lastPos;
105
+ // Whether a swipe is active.
106
+ var active = false;
107
+
108
+ element.on('touchstart mousedown', function(event) {
109
+ startCoords = getCoordinates(event);
110
+ active = true;
111
+ totalX = 0;
112
+ totalY = 0;
113
+ lastPos = startCoords;
114
+ eventHandlers['start'] && eventHandlers['start'](startCoords, event);
115
+ });
116
+
117
+ element.on('touchcancel', function(event) {
118
+ active = false;
119
+ eventHandlers['cancel'] && eventHandlers['cancel'](event);
120
+ });
121
+
122
+ element.on('touchmove mousemove', function(event) {
123
+ if (!active) return;
124
+
125
+ // Android will send a touchcancel if it thinks we're starting to scroll.
126
+ // So when the total distance (+ or - or both) exceeds 10px in either direction,
127
+ // we either:
128
+ // - On totalX > totalY, we send preventDefault() and treat this as a swipe.
129
+ // - On totalY > totalX, we let the browser handle it as a scroll.
130
+
131
+ if (!startCoords) return;
132
+ var coords = getCoordinates(event);
133
+
134
+ totalX += Math.abs(coords.x - lastPos.x);
135
+ totalY += Math.abs(coords.y - lastPos.y);
136
+
137
+ lastPos = coords;
138
+
139
+ if (totalX < MOVE_BUFFER_RADIUS && totalY < MOVE_BUFFER_RADIUS) {
140
+ return;
141
+ }
142
+
143
+ // One of totalX or totalY has exceeded the buffer, so decide on swipe vs. scroll.
144
+ if (totalY > totalX) {
145
+ // Allow native scrolling to take over.
146
+ active = false;
147
+ eventHandlers['cancel'] && eventHandlers['cancel'](event);
148
+ return;
149
+ } else {
150
+ // Prevent the browser from scrolling.
151
+ event.preventDefault();
152
+ eventHandlers['move'] && eventHandlers['move'](coords, event);
153
+ }
154
+ });
155
+
156
+ element.on('touchend mouseup', function(event) {
157
+ if (!active) return;
158
+ active = false;
159
+ eventHandlers['end'] && eventHandlers['end'](getCoordinates(event), event);
160
+ });
161
+ }
162
+ };
163
+ }]);
164
+
165
+ /* global ngTouch: false */
19
166
 
20
167
  /**
21
168
  * @ngdoc directive
22
- * @name ngMobile.directive:ngClick
169
+ * @name ngClick
23
170
  *
24
171
  * @description
25
172
  * A more powerful replacement for the default ngClick designed to be used on touchscreen
@@ -27,6 +174,8 @@ var ngMobile = angular.module('ngMobile', []);
27
174
  * the click event. This version handles them immediately, and then prevents the
28
175
  * following click event from propagating.
29
176
  *
177
+ * Requires the {@link ngTouch `ngTouch`} module to be installed.
178
+ *
30
179
  * This directive can fall back to using an ordinary click event, and so works on desktop
31
180
  * browsers as well as mobile.
32
181
  *
@@ -38,17 +187,17 @@ var ngMobile = angular.module('ngMobile', []);
38
187
  * upon tap. (Event object is available as `$event`)
39
188
  *
40
189
  * @example
41
- <doc:example>
42
- <doc:source>
190
+ <example>
191
+ <file name="index.html">
43
192
  <button ng-click="count = count + 1" ng-init="count=0">
44
193
  Increment
45
194
  </button>
46
195
  count: {{ count }}
47
- </doc:source>
48
- </doc:example>
196
+ </file>
197
+ </example>
49
198
  */
50
199
 
51
- ngMobile.config(['$provide', function($provide) {
200
+ ngTouch.config(['$provide', function($provide) {
52
201
  $provide.decorator('ngClickDirective', ['$delegate', function($delegate) {
53
202
  // drop the default ngClick directive
54
203
  $delegate.shift();
@@ -56,7 +205,7 @@ ngMobile.config(['$provide', function($provide) {
56
205
  }]);
57
206
  }]);
58
207
 
59
- ngMobile.directive('ngClick', ['$parse', '$timeout', '$rootElement',
208
+ ngTouch.directive('ngClick', ['$parse', '$timeout', '$rootElement',
60
209
  function($parse, $timeout, $rootElement) {
61
210
  var TAP_DURATION = 750; // Shorter than 750ms is a tap, longer is a taphold or drag.
62
211
  var MOVE_TOLERANCE = 12; // 12px seems to work in most mobile browsers.
@@ -66,6 +215,7 @@ ngMobile.directive('ngClick', ['$parse', '$timeout', '$rootElement',
66
215
  var ACTIVE_CLASS_NAME = 'ng-click-active';
67
216
  var lastPreventedTime;
68
217
  var touchCoordinates;
218
+ var lastLabelClickCoordinates;
69
219
 
70
220
 
71
221
  // TAP EVENTS AND GHOST CLICKS
@@ -137,10 +287,23 @@ ngMobile.directive('ngClick', ['$parse', '$timeout', '$rootElement',
137
287
  var y = touches[0].clientY;
138
288
  // Work around desktop Webkit quirk where clicking a label will fire two clicks (on the label
139
289
  // and on the input element). Depending on the exact browser, this second click we don't want
140
- // to bust has either (0,0) or negative coordinates.
290
+ // to bust has either (0,0), negative coordinates, or coordinates equal to triggering label
291
+ // click event
141
292
  if (x < 1 && y < 1) {
142
293
  return; // offscreen
143
294
  }
295
+ if (lastLabelClickCoordinates &&
296
+ lastLabelClickCoordinates[0] === x && lastLabelClickCoordinates[1] === y) {
297
+ return; // input click triggered by label click
298
+ }
299
+ // reset label click coordinates on first subsequent click
300
+ if (lastLabelClickCoordinates) {
301
+ lastLabelClickCoordinates = null;
302
+ }
303
+ // remember label click coordinates to prevent click busting of trigger click event on input
304
+ if (event.target.tagName.toLowerCase() === 'label') {
305
+ lastLabelClickCoordinates = [x, y];
306
+ }
144
307
 
145
308
  // Look for an allowable region containing this click.
146
309
  // If we find one, that means it was created by touchstart and not removed by
@@ -152,6 +315,9 @@ ngMobile.directive('ngClick', ['$parse', '$timeout', '$rootElement',
152
315
  // If we didn't find an allowable region, bust the click.
153
316
  event.stopPropagation();
154
317
  event.preventDefault();
318
+
319
+ // Blur focused form elements
320
+ event.target && event.target.blur();
155
321
  }
156
322
 
157
323
 
@@ -202,7 +368,7 @@ ngMobile.directive('ngClick', ['$parse', '$timeout', '$rootElement',
202
368
  element.removeClass(ACTIVE_CLASS_NAME);
203
369
  }
204
370
 
205
- element.bind('touchstart', function(event) {
371
+ element.on('touchstart', function(event) {
206
372
  tapping = true;
207
373
  tapElement = event.target ? event.target : event.srcElement; // IE uses srcElement.
208
374
  // Hack for Safari, which can target text nodes instead of containers.
@@ -220,15 +386,15 @@ ngMobile.directive('ngClick', ['$parse', '$timeout', '$rootElement',
220
386
  touchStartY = e.clientY;
221
387
  });
222
388
 
223
- element.bind('touchmove', function(event) {
389
+ element.on('touchmove', function(event) {
224
390
  resetState();
225
391
  });
226
392
 
227
- element.bind('touchcancel', function(event) {
393
+ element.on('touchcancel', function(event) {
228
394
  resetState();
229
395
  });
230
396
 
231
- element.bind('touchend', function(event) {
397
+ element.on('touchend', function(event) {
232
398
  var diff = Date.now() - startTime;
233
399
 
234
400
  var touches = (event.changedTouches && event.changedTouches.length) ? event.changedTouches :
@@ -249,10 +415,9 @@ ngMobile.directive('ngClick', ['$parse', '$timeout', '$rootElement',
249
415
  tapElement.blur();
250
416
  }
251
417
 
252
- scope.$apply(function() {
253
- // TODO(braden): This is sending the touchend, not a tap or click. Is that kosher?
254
- clickHandler(scope, {$event: event});
255
- });
418
+ if (!angular.isDefined(attr.disabled) || attr.disabled === false) {
419
+ element.triggerHandler('click', [event]);
420
+ }
256
421
  }
257
422
 
258
423
  resetState();
@@ -262,42 +427,50 @@ ngMobile.directive('ngClick', ['$parse', '$timeout', '$rootElement',
262
427
  // something else nearby.
263
428
  element.onclick = function(event) { };
264
429
 
265
- // Fallback click handler.
266
- // Busted clicks don't get this far, and adding this handler allows ng-tap to be used on
267
- // desktop as well, to allow more portable sites.
268
- element.bind('click', function(event) {
430
+ // Actual click handler.
431
+ // There are three different kinds of clicks, only two of which reach this point.
432
+ // - On desktop browsers without touch events, their clicks will always come here.
433
+ // - On mobile browsers, the simulated "fast" click will call this.
434
+ // - But the browser's follow-up slow click will be "busted" before it reaches this handler.
435
+ // Therefore it's safe to use this directive on both mobile and desktop.
436
+ element.on('click', function(event, touchend) {
269
437
  scope.$apply(function() {
270
- clickHandler(scope, {$event: event});
438
+ clickHandler(scope, {$event: (touchend || event)});
271
439
  });
272
440
  });
273
441
 
274
- element.bind('mousedown', function(event) {
442
+ element.on('mousedown', function(event) {
275
443
  element.addClass(ACTIVE_CLASS_NAME);
276
444
  });
277
445
 
278
- element.bind('mousemove mouseup', function(event) {
446
+ element.on('mousemove mouseup', function(event) {
279
447
  element.removeClass(ACTIVE_CLASS_NAME);
280
448
  });
281
449
 
282
450
  };
283
451
  }]);
284
452
 
453
+ /* global ngTouch: false */
454
+
285
455
  /**
286
456
  * @ngdoc directive
287
- * @name ngMobile.directive:ngSwipeLeft
457
+ * @name ngSwipeLeft
288
458
  *
289
459
  * @description
290
460
  * Specify custom behavior when an element is swiped to the left on a touchscreen device.
291
461
  * A leftward swipe is a quick, right-to-left slide of the finger.
292
- * Though ngSwipeLeft is designed for touch-based devices, it will work with a mouse click and drag too.
462
+ * Though ngSwipeLeft is designed for touch-based devices, it will work with a mouse click and drag
463
+ * too.
464
+ *
465
+ * Requires the {@link ngTouch `ngTouch`} module to be installed.
293
466
  *
294
467
  * @element ANY
295
468
  * @param {expression} ngSwipeLeft {@link guide/expression Expression} to evaluate
296
469
  * upon left swipe. (Event object is available as `$event`)
297
470
  *
298
471
  * @example
299
- <doc:example>
300
- <doc:source>
472
+ <example>
473
+ <file name="index.html">
301
474
  <div ng-show="!showActions" ng-swipe-left="showActions = true">
302
475
  Some list content, like an email in the inbox
303
476
  </div>
@@ -305,26 +478,29 @@ ngMobile.directive('ngClick', ['$parse', '$timeout', '$rootElement',
305
478
  <button ng-click="reply()">Reply</button>
306
479
  <button ng-click="delete()">Delete</button>
307
480
  </div>
308
- </doc:source>
309
- </doc:example>
481
+ </file>
482
+ </example>
310
483
  */
311
484
 
312
485
  /**
313
486
  * @ngdoc directive
314
- * @name ngMobile.directive:ngSwipeRight
487
+ * @name ngSwipeRight
315
488
  *
316
489
  * @description
317
490
  * Specify custom behavior when an element is swiped to the right on a touchscreen device.
318
491
  * A rightward swipe is a quick, left-to-right slide of the finger.
319
- * Though ngSwipeRight is designed for touch-based devices, it will work with a mouse click and drag too.
492
+ * Though ngSwipeRight is designed for touch-based devices, it will work with a mouse click and drag
493
+ * too.
494
+ *
495
+ * Requires the {@link ngTouch `ngTouch`} module to be installed.
320
496
  *
321
497
  * @element ANY
322
498
  * @param {expression} ngSwipeRight {@link guide/expression Expression} to evaluate
323
499
  * upon right swipe. (Event object is available as `$event`)
324
500
  *
325
501
  * @example
326
- <doc:example>
327
- <doc:source>
502
+ <example>
503
+ <file name="index.html">
328
504
  <div ng-show="!showActions" ng-swipe-left="showActions = true">
329
505
  Some list content, like an email in the inbox
330
506
  </div>
@@ -332,41 +508,25 @@ ngMobile.directive('ngClick', ['$parse', '$timeout', '$rootElement',
332
508
  <button ng-click="reply()">Reply</button>
333
509
  <button ng-click="delete()">Delete</button>
334
510
  </div>
335
- </doc:source>
336
- </doc:example>
511
+ </file>
512
+ </example>
337
513
  */
338
514
 
339
- function makeSwipeDirective(directiveName, direction) {
340
- ngMobile.directive(directiveName, ['$parse', function($parse) {
515
+ function makeSwipeDirective(directiveName, direction, eventName) {
516
+ ngTouch.directive(directiveName, ['$parse', '$swipe', function($parse, $swipe) {
341
517
  // The maximum vertical delta for a swipe should be less than 75px.
342
518
  var MAX_VERTICAL_DISTANCE = 75;
343
519
  // Vertical distance should not be more than a fraction of the horizontal distance.
344
520
  var MAX_VERTICAL_RATIO = 0.3;
345
521
  // At least a 30px lateral motion is necessary for a swipe.
346
522
  var MIN_HORIZONTAL_DISTANCE = 30;
347
- // The total distance in any direction before we make the call on swipe vs. scroll.
348
- var MOVE_BUFFER_RADIUS = 10;
349
-
350
- function getCoordinates(event) {
351
- var touches = event.touches && event.touches.length ? event.touches : [event];
352
- var e = (event.changedTouches && event.changedTouches[0]) ||
353
- (event.originalEvent && event.originalEvent.changedTouches &&
354
- event.originalEvent.changedTouches[0]) ||
355
- touches[0].originalEvent || touches[0];
356
-
357
- return {
358
- x: e.clientX,
359
- y: e.clientY
360
- };
361
- }
362
523
 
363
524
  return function(scope, element, attr) {
364
525
  var swipeHandler = $parse(attr[directiveName]);
526
+
365
527
  var startCoords, valid;
366
- var totalX, totalY;
367
- var lastX, lastY;
368
528
 
369
- function validSwipe(event) {
529
+ function validSwipe(coords) {
370
530
  // Check that it's within the coordinates.
371
531
  // Absolute vertical distance must be within tolerances.
372
532
  // Horizontal distance, we take the current X - the starting X.
@@ -376,7 +536,6 @@ function makeSwipeDirective(directiveName, direction) {
376
536
  // illegal ones a negative delta.
377
537
  // Therefore this delta must be positive, and larger than the minimum.
378
538
  if (!startCoords) return false;
379
- var coords = getCoordinates(event);
380
539
  var deltaY = Math.abs(coords.y - startCoords.y);
381
540
  var deltaX = (coords.x - startCoords.x) * direction;
382
541
  return valid && // Short circuit for already-invalidated swipes.
@@ -386,65 +545,21 @@ function makeSwipeDirective(directiveName, direction) {
386
545
  deltaY / deltaX < MAX_VERTICAL_RATIO;
387
546
  }
388
547
 
389
- element.bind('touchstart mousedown', function(event) {
390
- startCoords = getCoordinates(event);
391
- valid = true;
392
- totalX = 0;
393
- totalY = 0;
394
- lastX = startCoords.x;
395
- lastY = startCoords.y;
396
- });
397
-
398
- element.bind('touchcancel', function(event) {
399
- valid = false;
400
- });
401
-
402
- element.bind('touchmove mousemove', function(event) {
403
- if (!valid) return;
404
-
405
- // Android will send a touchcancel if it thinks we're starting to scroll.
406
- // So when the total distance (+ or - or both) exceeds 10px in either direction,
407
- // we either:
408
- // - On totalX > totalY, we send preventDefault() and treat this as a swipe.
409
- // - On totalY > totalX, we let the browser handle it as a scroll.
410
-
411
- // Invalidate a touch while it's in progress if it strays too far away vertically.
412
- // We don't want a scroll down and back up while drifting sideways to be a swipe just
413
- // because you happened to end up vertically close in the end.
414
- if (!startCoords) return;
415
- var coords = getCoordinates(event);
416
-
417
- if (Math.abs(coords.y - startCoords.y) > MAX_VERTICAL_DISTANCE) {
418
- valid = false;
419
- return;
420
- }
421
-
422
- totalX += Math.abs(coords.x - lastX);
423
- totalY += Math.abs(coords.y - lastY);
424
-
425
- lastX = coords.x;
426
- lastY = coords.y;
427
-
428
- if (totalX < MOVE_BUFFER_RADIUS && totalY < MOVE_BUFFER_RADIUS) {
429
- return;
430
- }
431
-
432
- // One of totalX or totalY has exceeded the buffer, so decide on swipe vs. scroll.
433
- if (totalY > totalX) {
548
+ $swipe.bind(element, {
549
+ 'start': function(coords, event) {
550
+ startCoords = coords;
551
+ valid = true;
552
+ },
553
+ 'cancel': function(event) {
434
554
  valid = false;
435
- return;
436
- } else {
437
- event.preventDefault();
438
- }
439
- });
440
-
441
- element.bind('touchend mouseup', function(event) {
442
- if (validSwipe(event)) {
443
- // Prevent this swipe from bubbling up to any other elements with ngSwipes.
444
- event.stopPropagation();
445
- scope.$apply(function() {
446
- swipeHandler(scope, {$event:event});
447
- });
555
+ },
556
+ 'end': function(coords, event) {
557
+ if (validSwipe(coords)) {
558
+ scope.$apply(function() {
559
+ element.triggerHandler(eventName);
560
+ swipeHandler(scope, {$event: event});
561
+ });
562
+ }
448
563
  }
449
564
  });
450
565
  };
@@ -452,8 +567,8 @@ function makeSwipeDirective(directiveName, direction) {
452
567
  }
453
568
 
454
569
  // Left is negative X-coordinate, right is positive.
455
- makeSwipeDirective('ngSwipeLeft', -1);
456
- makeSwipeDirective('ngSwipeRight', 1);
570
+ makeSwipeDirective('ngSwipeLeft', -1, 'swipeleft');
571
+ makeSwipeDirective('ngSwipeRight', 1, 'swiperight');
457
572
 
458
573
 
459
574