GitHub Access Token became invalid

It seems like the GitHub access token used for retrieving details about this repository from GitHub became invalid. This might prevent certain types of inspections from being run (in particular, everything related to pull requests).
Please ask an admin of your repository to re-new the access token on this website.

Issues (3063)

angularjs/angular-1.5.0/angular-touch.js (2 issues)

1
/**
2
 * @license AngularJS v1.5.0
3
 * (c) 2010-2016 Google, Inc. http://angularjs.org
4
 * License: MIT
5
 */
6
(function(window, angular, undefined) {'use strict';
7
8
/* global ngTouchClickDirectiveFactory: false,
9
 */
10
11
/**
12
 * @ngdoc module
13
 * @name ngTouch
14
 * @description
15
 *
16
 * # ngTouch
17
 *
18
 * The `ngTouch` module provides touch events and other helpers for touch-enabled devices.
19
 * The implementation is based on jQuery Mobile touch event handling
20
 * ([jquerymobile.com](http://jquerymobile.com/)).
21
 *
22
 *
23
 * See {@link ngTouch.$swipe `$swipe`} for usage.
24
 *
25
 * <div doc-module-components="ngTouch"></div>
26
 *
27
 */
28
29
// define ngTouch module
30
/* global -ngTouch */
31
var ngTouch = angular.module('ngTouch', []);
32
33
ngTouch.provider('$touch', $TouchProvider);
34
35
function nodeName_(element) {
36
  return angular.lowercase(element.nodeName || (element[0] && element[0].nodeName));
37
}
38
39
/**
40
 * @ngdoc provider
41
 * @name $touchProvider
42
 *
43
 * @description
44
 * The `$touchProvider` allows enabling / disabling {@link ngTouch.ngClick ngTouch's ngClick directive}.
45
 */
46
$TouchProvider.$inject = ['$provide', '$compileProvider'];
47
function $TouchProvider($provide, $compileProvider) {
48
49
  /**
50
   * @ngdoc method
51
   * @name  $touchProvider#ngClickOverrideEnabled
52
   *
53
   * @param {boolean=} enabled update the ngClickOverrideEnabled state if provided, otherwise just return the
54
   * current ngClickOverrideEnabled state
55
   * @returns {*} current value if used as getter or itself (chaining) if used as setter
56
   *
57
   * @kind function
58
   *
59
   * @description
60
   * Call this method to enable/disable {@link ngTouch.ngClick ngTouch's ngClick directive}. If enabled,
61
   * the default ngClick directive will be replaced by a version that eliminates the 300ms delay for
62
   * click events on browser for touch-devices.
63
   *
64
   * The default is `false`.
65
   *
66
   */
67
  var ngClickOverrideEnabled = false;
68
  var ngClickDirectiveAdded = false;
69
  this.ngClickOverrideEnabled = function(enabled) {
70
    if (angular.isDefined(enabled)) {
71
72
      if (enabled && !ngClickDirectiveAdded) {
73
        ngClickDirectiveAdded = true;
74
75
        // Use this to identify the correct directive in the delegate
76
        ngTouchClickDirectiveFactory.$$moduleName = 'ngTouch';
77
        $compileProvider.directive('ngClick', ngTouchClickDirectiveFactory);
78
79
        $provide.decorator('ngClickDirective', ['$delegate', function($delegate) {
80
          if (ngClickOverrideEnabled) {
81
            // drop the default ngClick directive
82
            $delegate.shift();
83
          } else {
84
            // drop the ngTouch ngClick directive if the override has been re-disabled (because
85
            // we cannot de-register added directives)
86
            var i = $delegate.length - 1;
87
            while (i >= 0) {
88
              if ($delegate[i].$$moduleName === 'ngTouch') {
89
                $delegate.splice(i, 1);
90
                break;
91
              }
92
              i--;
93
            }
94
          }
95
96
          return $delegate;
97
        }]);
98
      }
99
100
      ngClickOverrideEnabled = enabled;
101
      return this;
102
    }
103
104
    return ngClickOverrideEnabled;
105
  };
106
107
  /**
108
  * @ngdoc service
109
  * @name $touch
110
  * @kind object
111
  *
112
  * @description
113
  * Provides the {@link ngTouch.$touch#ngClickOverrideEnabled `ngClickOverrideEnabled`} method.
114
  *
115
  */
116
  this.$get = function() {
117
    return {
118
      /**
119
       * @ngdoc method
120
       * @name  $touch#ngClickOverrideEnabled
121
       *
122
       * @returns {*} current value of `ngClickOverrideEnabled` set in the {@link ngTouch.$touchProvider $touchProvider},
123
       * i.e. if {@link ngTouch.ngClick ngTouch's ngClick} directive is enabled.
124
       *
125
       * @kind function
126
       */
127
      ngClickOverrideEnabled: function() {
128
        return ngClickOverrideEnabled;
129
      }
130
    };
131
  };
132
133
}
134
135
/* global ngTouch: false */
136
137
    /**
138
     * @ngdoc service
139
     * @name $swipe
140
     *
141
     * @description
142
     * The `$swipe` service is a service that abstracts the messier details of hold-and-drag swipe
143
     * behavior, to make implementing swipe-related directives more convenient.
144
     *
145
     * Requires the {@link ngTouch `ngTouch`} module to be installed.
146
     *
147
     * `$swipe` is used by the `ngSwipeLeft` and `ngSwipeRight` directives in `ngTouch`.
148
     *
149
     * # Usage
150
     * The `$swipe` service is an object with a single method: `bind`. `bind` takes an element
151
     * which is to be watched for swipes, and an object with four handler functions. See the
152
     * documentation for `bind` below.
153
     */
154
155
ngTouch.factory('$swipe', [function() {
156
  // The total distance in any direction before we make the call on swipe vs. scroll.
157
  var MOVE_BUFFER_RADIUS = 10;
158
159
  var POINTER_EVENTS = {
160
    'mouse': {
161
      start: 'mousedown',
162
      move: 'mousemove',
163
      end: 'mouseup'
164
    },
165
    'touch': {
166
      start: 'touchstart',
167
      move: 'touchmove',
168
      end: 'touchend',
169
      cancel: 'touchcancel'
170
    }
171
  };
172
173
  function getCoordinates(event) {
174
    var originalEvent = event.originalEvent || event;
175
    var touches = originalEvent.touches && originalEvent.touches.length ? originalEvent.touches : [originalEvent];
176
    var e = (originalEvent.changedTouches && originalEvent.changedTouches[0]) || touches[0];
177
178
    return {
179
      x: e.clientX,
180
      y: e.clientY
181
    };
182
  }
183
184
  function getEvents(pointerTypes, eventType) {
185
    var res = [];
186
    angular.forEach(pointerTypes, function(pointerType) {
187
      var eventName = POINTER_EVENTS[pointerType][eventType];
188
      if (eventName) {
189
        res.push(eventName);
190
      }
191
    });
192
    return res.join(' ');
193
  }
194
195
  return {
196
    /**
197
     * @ngdoc method
198
     * @name $swipe#bind
199
     *
200
     * @description
201
     * The main method of `$swipe`. It takes an element to be watched for swipe motions, and an
202
     * object containing event handlers.
203
     * The pointer types that should be used can be specified via the optional
204
     * third argument, which is an array of strings `'mouse'` and `'touch'`. By default,
205
     * `$swipe` will listen for `mouse` and `touch` events.
206
     *
207
     * The four events are `start`, `move`, `end`, and `cancel`. `start`, `move`, and `end`
208
     * receive as a parameter a coordinates object of the form `{ x: 150, y: 310 }` and the raw
209
     * `event`. `cancel` receives the raw `event` as its single parameter.
210
     *
211
     * `start` is called on either `mousedown` or `touchstart`. After this event, `$swipe` is
212
     * watching for `touchmove` or `mousemove` events. These events are ignored until the total
213
     * distance moved in either dimension exceeds a small threshold.
214
     *
215
     * Once this threshold is exceeded, either the horizontal or vertical delta is greater.
216
     * - If the horizontal distance is greater, this is a swipe and `move` and `end` events follow.
217
     * - If the vertical distance is greater, this is a scroll, and we let the browser take over.
218
     *   A `cancel` event is sent.
219
     *
220
     * `move` is called on `mousemove` and `touchmove` after the above logic has determined that
221
     * a swipe is in progress.
222
     *
223
     * `end` is called when a swipe is successfully completed with a `touchend` or `mouseup`.
224
     *
225
     * `cancel` is called either on a `touchcancel` from the browser, or when we begin scrolling
226
     * as described above.
227
     *
228
     */
229
    bind: function(element, eventHandlers, pointerTypes) {
230
      // Absolute total movement, used to control swipe vs. scroll.
231
      var totalX, totalY;
232
      // Coordinates of the start position.
233
      var startCoords;
234
      // Last event's position.
235
      var lastPos;
236
      // Whether a swipe is active.
237
      var active = false;
238
239
      pointerTypes = pointerTypes || ['mouse', 'touch'];
240
      element.on(getEvents(pointerTypes, 'start'), function(event) {
241
        startCoords = getCoordinates(event);
242
        active = true;
243
        totalX = 0;
244
        totalY = 0;
245
        lastPos = startCoords;
246
        eventHandlers['start'] && eventHandlers['start'](startCoords, event);
247
      });
248
      var events = getEvents(pointerTypes, 'cancel');
249
      if (events) {
250
        element.on(events, function(event) {
251
          active = false;
252
          eventHandlers['cancel'] && eventHandlers['cancel'](event);
253
        });
254
      }
255
256
      element.on(getEvents(pointerTypes, 'move'), function(event) {
257
        if (!active) return;
258
259
        // Android will send a touchcancel if it thinks we're starting to scroll.
260
        // So when the total distance (+ or - or both) exceeds 10px in either direction,
261
        // we either:
262
        // - On totalX > totalY, we send preventDefault() and treat this as a swipe.
263
        // - On totalY > totalX, we let the browser handle it as a scroll.
264
265
        if (!startCoords) return;
266
        var coords = getCoordinates(event);
267
268
        totalX += Math.abs(coords.x - lastPos.x);
269
        totalY += Math.abs(coords.y - lastPos.y);
270
271
        lastPos = coords;
272
273
        if (totalX < MOVE_BUFFER_RADIUS && totalY < MOVE_BUFFER_RADIUS) {
274
          return;
275
        }
276
277
        // One of totalX or totalY has exceeded the buffer, so decide on swipe vs. scroll.
278
        if (totalY > totalX) {
279
          // Allow native scrolling to take over.
280
          active = false;
281
          eventHandlers['cancel'] && eventHandlers['cancel'](event);
282
          return;
0 ignored issues
show
This return has no effect and can be removed.
Loading history...
283
        } else {
0 ignored issues
show
Comprehensibility introduced by
else is not necessary here since all if branches return, consider removing it to reduce nesting and make code more readable.
Loading history...
284
          // Prevent the browser from scrolling.
285
          event.preventDefault();
286
          eventHandlers['move'] && eventHandlers['move'](coords, event);
287
        }
288
      });
289
290
      element.on(getEvents(pointerTypes, 'end'), function(event) {
291
        if (!active) return;
292
        active = false;
293
        eventHandlers['end'] && eventHandlers['end'](getCoordinates(event), event);
294
      });
295
    }
296
  };
297
}]);
298
299
/* global ngTouch: false,
300
  nodeName_: false
301
*/
302
303
/**
304
 * @ngdoc directive
305
 * @name ngClick
306
 * @deprecated
307
 *
308
 * @description
309
 * <div class="alert alert-danger">
310
 * **DEPRECATION NOTICE**: Beginning with Angular 1.5, this directive is deprecated and by default **disabled**.
311
 * The directive will receive no further support and might be removed from future releases.
312
 * If you need the directive, you can enable it with the {@link ngTouch.$touchProvider $touchProvider#ngClickOverrideEnabled}
313
 * function. We also recommend that you migrate to [FastClick](https://github.com/ftlabs/fastclick).
314
 * To learn more about the 300ms delay, this [Telerik article](http://developer.telerik.com/featured/300-ms-click-delay-ios-8/)
315
 * gives a good overview.
316
 * </div>
317
 * A more powerful replacement for the default ngClick designed to be used on touchscreen
318
 * devices. Most mobile browsers wait about 300ms after a tap-and-release before sending
319
 * the click event. This version handles them immediately, and then prevents the
320
 * following click event from propagating.
321
 *
322
 * Requires the {@link ngTouch `ngTouch`} module to be installed.
323
 *
324
 * This directive can fall back to using an ordinary click event, and so works on desktop
325
 * browsers as well as mobile.
326
 *
327
 * This directive also sets the CSS class `ng-click-active` while the element is being held
328
 * down (by a mouse click or touch) so you can restyle the depressed element if you wish.
329
 *
330
 * @element ANY
331
 * @param {expression} ngClick {@link guide/expression Expression} to evaluate
332
 * upon tap. (Event object is available as `$event`)
333
 *
334
 * @example
335
    <example module="ngClickExample" deps="angular-touch.js">
336
      <file name="index.html">
337
        <button ng-click="count = count + 1" ng-init="count=0">
338
          Increment
339
        </button>
340
        count: {{ count }}
341
      </file>
342
      <file name="script.js">
343
        angular.module('ngClickExample', ['ngTouch']);
344
      </file>
345
    </example>
346
 */
347
348
var ngTouchClickDirectiveFactory = ['$parse', '$timeout', '$rootElement',
349
    function($parse, $timeout, $rootElement) {
350
  var TAP_DURATION = 750; // Shorter than 750ms is a tap, longer is a taphold or drag.
351
  var MOVE_TOLERANCE = 12; // 12px seems to work in most mobile browsers.
352
  var PREVENT_DURATION = 2500; // 2.5 seconds maximum from preventGhostClick call to click
353
  var CLICKBUSTER_THRESHOLD = 25; // 25 pixels in any dimension is the limit for busting clicks.
354
355
  var ACTIVE_CLASS_NAME = 'ng-click-active';
356
  var lastPreventedTime;
357
  var touchCoordinates;
358
  var lastLabelClickCoordinates;
359
360
361
  // TAP EVENTS AND GHOST CLICKS
362
  //
363
  // Why tap events?
364
  // Mobile browsers detect a tap, then wait a moment (usually ~300ms) to see if you're
365
  // double-tapping, and then fire a click event.
366
  //
367
  // This delay sucks and makes mobile apps feel unresponsive.
368
  // So we detect touchstart, touchcancel and touchend ourselves and determine when
369
  // the user has tapped on something.
370
  //
371
  // What happens when the browser then generates a click event?
372
  // The browser, of course, also detects the tap and fires a click after a delay. This results in
373
  // tapping/clicking twice. We do "clickbusting" to prevent it.
374
  //
375
  // How does it work?
376
  // We attach global touchstart and click handlers, that run during the capture (early) phase.
377
  // So the sequence for a tap is:
378
  // - global touchstart: Sets an "allowable region" at the point touched.
379
  // - element's touchstart: Starts a touch
380
  // (- touchcancel ends the touch, no click follows)
381
  // - element's touchend: Determines if the tap is valid (didn't move too far away, didn't hold
382
  //   too long) and fires the user's tap handler. The touchend also calls preventGhostClick().
383
  // - preventGhostClick() removes the allowable region the global touchstart created.
384
  // - The browser generates a click event.
385
  // - The global click handler catches the click, and checks whether it was in an allowable region.
386
  //     - If preventGhostClick was called, the region will have been removed, the click is busted.
387
  //     - If the region is still there, the click proceeds normally. Therefore clicks on links and
388
  //       other elements without ngTap on them work normally.
389
  //
390
  // This is an ugly, terrible hack!
391
  // Yeah, tell me about it. The alternatives are using the slow click events, or making our users
392
  // deal with the ghost clicks, so I consider this the least of evils. Fortunately Angular
393
  // encapsulates this ugly logic away from the user.
394
  //
395
  // Why not just put click handlers on the element?
396
  // We do that too, just to be sure. If the tap event caused the DOM to change,
397
  // it is possible another element is now in that position. To take account for these possibly
398
  // distinct elements, the handlers are global and care only about coordinates.
399
400
  // Checks if the coordinates are close enough to be within the region.
401
  function hit(x1, y1, x2, y2) {
402
    return Math.abs(x1 - x2) < CLICKBUSTER_THRESHOLD && Math.abs(y1 - y2) < CLICKBUSTER_THRESHOLD;
403
  }
404
405
  // Checks a list of allowable regions against a click location.
406
  // Returns true if the click should be allowed.
407
  // Splices out the allowable region from the list after it has been used.
408
  function checkAllowableRegions(touchCoordinates, x, y) {
409
    for (var i = 0; i < touchCoordinates.length; i += 2) {
410
      if (hit(touchCoordinates[i], touchCoordinates[i + 1], x, y)) {
411
        touchCoordinates.splice(i, i + 2);
412
        return true; // allowable region
413
      }
414
    }
415
    return false; // No allowable region; bust it.
416
  }
417
418
  // Global click handler that prevents the click if it's in a bustable zone and preventGhostClick
419
  // was called recently.
420
  function onClick(event) {
421
    if (Date.now() - lastPreventedTime > PREVENT_DURATION) {
422
      return; // Too old.
423
    }
424
425
    var touches = event.touches && event.touches.length ? event.touches : [event];
426
    var x = touches[0].clientX;
427
    var y = touches[0].clientY;
428
    // Work around desktop Webkit quirk where clicking a label will fire two clicks (on the label
429
    // and on the input element). Depending on the exact browser, this second click we don't want
430
    // to bust has either (0,0), negative coordinates, or coordinates equal to triggering label
431
    // click event
432
    if (x < 1 && y < 1) {
433
      return; // offscreen
434
    }
435
    if (lastLabelClickCoordinates &&
436
        lastLabelClickCoordinates[0] === x && lastLabelClickCoordinates[1] === y) {
437
      return; // input click triggered by label click
438
    }
439
    // reset label click coordinates on first subsequent click
440
    if (lastLabelClickCoordinates) {
441
      lastLabelClickCoordinates = null;
442
    }
443
    // remember label click coordinates to prevent click busting of trigger click event on input
444
    if (nodeName_(event.target) === 'label') {
445
      lastLabelClickCoordinates = [x, y];
446
    }
447
448
    // Look for an allowable region containing this click.
449
    // If we find one, that means it was created by touchstart and not removed by
450
    // preventGhostClick, so we don't bust it.
451
    if (checkAllowableRegions(touchCoordinates, x, y)) {
452
      return;
453
    }
454
455
    // If we didn't find an allowable region, bust the click.
456
    event.stopPropagation();
457
    event.preventDefault();
458
459
    // Blur focused form elements
460
    event.target && event.target.blur && event.target.blur();
461
  }
462
463
464
  // Global touchstart handler that creates an allowable region for a click event.
465
  // This allowable region can be removed by preventGhostClick if we want to bust it.
466
  function onTouchStart(event) {
467
    var touches = event.touches && event.touches.length ? event.touches : [event];
468
    var x = touches[0].clientX;
469
    var y = touches[0].clientY;
470
    touchCoordinates.push(x, y);
471
472
    $timeout(function() {
473
      // Remove the allowable region.
474
      for (var i = 0; i < touchCoordinates.length; i += 2) {
475
        if (touchCoordinates[i] == x && touchCoordinates[i + 1] == y) {
476
          touchCoordinates.splice(i, i + 2);
477
          return;
478
        }
479
      }
480
    }, PREVENT_DURATION, false);
481
  }
482
483
  // On the first call, attaches some event handlers. Then whenever it gets called, it creates a
484
  // zone around the touchstart where clicks will get busted.
485
  function preventGhostClick(x, y) {
486
    if (!touchCoordinates) {
487
      $rootElement[0].addEventListener('click', onClick, true);
488
      $rootElement[0].addEventListener('touchstart', onTouchStart, true);
489
      touchCoordinates = [];
490
    }
491
492
    lastPreventedTime = Date.now();
493
494
    checkAllowableRegions(touchCoordinates, x, y);
495
  }
496
497
  // Actual linking function.
498
  return function(scope, element, attr) {
499
    var clickHandler = $parse(attr.ngClick),
500
        tapping = false,
501
        tapElement,  // Used to blur the element after a tap.
502
        startTime,   // Used to check if the tap was held too long.
503
        touchStartX,
504
        touchStartY;
505
506
    function resetState() {
507
      tapping = false;
508
      element.removeClass(ACTIVE_CLASS_NAME);
509
    }
510
511
    element.on('touchstart', function(event) {
512
      tapping = true;
513
      tapElement = event.target ? event.target : event.srcElement; // IE uses srcElement.
514
      // Hack for Safari, which can target text nodes instead of containers.
515
      if (tapElement.nodeType == 3) {
516
        tapElement = tapElement.parentNode;
517
      }
518
519
      element.addClass(ACTIVE_CLASS_NAME);
520
521
      startTime = Date.now();
522
523
      // Use jQuery originalEvent
524
      var originalEvent = event.originalEvent || event;
525
      var touches = originalEvent.touches && originalEvent.touches.length ? originalEvent.touches : [originalEvent];
526
      var e = touches[0];
527
      touchStartX = e.clientX;
528
      touchStartY = e.clientY;
529
    });
530
531
    element.on('touchcancel', function(event) {
532
      resetState();
533
    });
534
535
    element.on('touchend', function(event) {
536
      var diff = Date.now() - startTime;
537
538
      // Use jQuery originalEvent
539
      var originalEvent = event.originalEvent || event;
540
      var touches = (originalEvent.changedTouches && originalEvent.changedTouches.length) ?
541
          originalEvent.changedTouches :
542
          ((originalEvent.touches && originalEvent.touches.length) ? originalEvent.touches : [originalEvent]);
543
      var e = touches[0];
544
      var x = e.clientX;
545
      var y = e.clientY;
546
      var dist = Math.sqrt(Math.pow(x - touchStartX, 2) + Math.pow(y - touchStartY, 2));
547
548
      if (tapping && diff < TAP_DURATION && dist < MOVE_TOLERANCE) {
549
        // Call preventGhostClick so the clickbuster will catch the corresponding click.
550
        preventGhostClick(x, y);
551
552
        // Blur the focused element (the button, probably) before firing the callback.
553
        // This doesn't work perfectly on Android Chrome, but seems to work elsewhere.
554
        // I couldn't get anything to work reliably on Android Chrome.
555
        if (tapElement) {
556
          tapElement.blur();
557
        }
558
559
        if (!angular.isDefined(attr.disabled) || attr.disabled === false) {
560
          element.triggerHandler('click', [event]);
561
        }
562
      }
563
564
      resetState();
565
    });
566
567
    // Hack for iOS Safari's benefit. It goes searching for onclick handlers and is liable to click
568
    // something else nearby.
569
    element.onclick = function(event) { };
570
571
    // Actual click handler.
572
    // There are three different kinds of clicks, only two of which reach this point.
573
    // - On desktop browsers without touch events, their clicks will always come here.
574
    // - On mobile browsers, the simulated "fast" click will call this.
575
    // - But the browser's follow-up slow click will be "busted" before it reaches this handler.
576
    // Therefore it's safe to use this directive on both mobile and desktop.
577
    element.on('click', function(event, touchend) {
578
      scope.$apply(function() {
579
        clickHandler(scope, {$event: (touchend || event)});
580
      });
581
    });
582
583
    element.on('mousedown', function(event) {
584
      element.addClass(ACTIVE_CLASS_NAME);
585
    });
586
587
    element.on('mousemove mouseup', function(event) {
588
      element.removeClass(ACTIVE_CLASS_NAME);
589
    });
590
591
  };
592
}];
593
594
/* global ngTouch: false */
595
596
/**
597
 * @ngdoc directive
598
 * @name ngSwipeLeft
599
 *
600
 * @description
601
 * Specify custom behavior when an element is swiped to the left on a touchscreen device.
602
 * A leftward swipe is a quick, right-to-left slide of the finger.
603
 * Though ngSwipeLeft is designed for touch-based devices, it will work with a mouse click and drag
604
 * too.
605
 *
606
 * To disable the mouse click and drag functionality, add `ng-swipe-disable-mouse` to
607
 * the `ng-swipe-left` or `ng-swipe-right` DOM Element.
608
 *
609
 * Requires the {@link ngTouch `ngTouch`} module to be installed.
610
 *
611
 * @element ANY
612
 * @param {expression} ngSwipeLeft {@link guide/expression Expression} to evaluate
613
 * upon left swipe. (Event object is available as `$event`)
614
 *
615
 * @example
616
    <example module="ngSwipeLeftExample" deps="angular-touch.js">
617
      <file name="index.html">
618
        <div ng-show="!showActions" ng-swipe-left="showActions = true">
619
          Some list content, like an email in the inbox
620
        </div>
621
        <div ng-show="showActions" ng-swipe-right="showActions = false">
622
          <button ng-click="reply()">Reply</button>
623
          <button ng-click="delete()">Delete</button>
624
        </div>
625
      </file>
626
      <file name="script.js">
627
        angular.module('ngSwipeLeftExample', ['ngTouch']);
628
      </file>
629
    </example>
630
 */
631
632
/**
633
 * @ngdoc directive
634
 * @name ngSwipeRight
635
 *
636
 * @description
637
 * Specify custom behavior when an element is swiped to the right on a touchscreen device.
638
 * A rightward swipe is a quick, left-to-right slide of the finger.
639
 * Though ngSwipeRight is designed for touch-based devices, it will work with a mouse click and drag
640
 * too.
641
 *
642
 * Requires the {@link ngTouch `ngTouch`} module to be installed.
643
 *
644
 * @element ANY
645
 * @param {expression} ngSwipeRight {@link guide/expression Expression} to evaluate
646
 * upon right swipe. (Event object is available as `$event`)
647
 *
648
 * @example
649
    <example module="ngSwipeRightExample" deps="angular-touch.js">
650
      <file name="index.html">
651
        <div ng-show="!showActions" ng-swipe-left="showActions = true">
652
          Some list content, like an email in the inbox
653
        </div>
654
        <div ng-show="showActions" ng-swipe-right="showActions = false">
655
          <button ng-click="reply()">Reply</button>
656
          <button ng-click="delete()">Delete</button>
657
        </div>
658
      </file>
659
      <file name="script.js">
660
        angular.module('ngSwipeRightExample', ['ngTouch']);
661
      </file>
662
    </example>
663
 */
664
665
function makeSwipeDirective(directiveName, direction, eventName) {
666
  ngTouch.directive(directiveName, ['$parse', '$swipe', function($parse, $swipe) {
667
    // The maximum vertical delta for a swipe should be less than 75px.
668
    var MAX_VERTICAL_DISTANCE = 75;
669
    // Vertical distance should not be more than a fraction of the horizontal distance.
670
    var MAX_VERTICAL_RATIO = 0.3;
671
    // At least a 30px lateral motion is necessary for a swipe.
672
    var MIN_HORIZONTAL_DISTANCE = 30;
673
674
    return function(scope, element, attr) {
675
      var swipeHandler = $parse(attr[directiveName]);
676
677
      var startCoords, valid;
678
679
      function validSwipe(coords) {
680
        // Check that it's within the coordinates.
681
        // Absolute vertical distance must be within tolerances.
682
        // Horizontal distance, we take the current X - the starting X.
683
        // This is negative for leftward swipes and positive for rightward swipes.
684
        // After multiplying by the direction (-1 for left, +1 for right), legal swipes
685
        // (ie. same direction as the directive wants) will have a positive delta and
686
        // illegal ones a negative delta.
687
        // Therefore this delta must be positive, and larger than the minimum.
688
        if (!startCoords) return false;
689
        var deltaY = Math.abs(coords.y - startCoords.y);
690
        var deltaX = (coords.x - startCoords.x) * direction;
691
        return valid && // Short circuit for already-invalidated swipes.
692
            deltaY < MAX_VERTICAL_DISTANCE &&
693
            deltaX > 0 &&
694
            deltaX > MIN_HORIZONTAL_DISTANCE &&
695
            deltaY / deltaX < MAX_VERTICAL_RATIO;
696
      }
697
698
      var pointerTypes = ['touch'];
699
      if (!angular.isDefined(attr['ngSwipeDisableMouse'])) {
700
        pointerTypes.push('mouse');
701
      }
702
      $swipe.bind(element, {
703
        'start': function(coords, event) {
704
          startCoords = coords;
705
          valid = true;
706
        },
707
        'cancel': function(event) {
708
          valid = false;
709
        },
710
        'end': function(coords, event) {
711
          if (validSwipe(coords)) {
712
            scope.$apply(function() {
713
              element.triggerHandler(eventName);
714
              swipeHandler(scope, {$event: event});
715
            });
716
          }
717
        }
718
      }, pointerTypes);
719
    };
720
  }]);
721
}
722
723
// Left is negative X-coordinate, right is positive.
724
makeSwipeDirective('ngSwipeLeft', -1, 'swipeleft');
725
makeSwipeDirective('ngSwipeRight', 1, 'swiperight');
726
727
728
729
})(window, window.angular);
730