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.6.5/angular-touch.js (2 issues)

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