angularjs-rails 1.2.14 → 1.2.15

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,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