Initial OpenECOMP policy/engine commit
[policy/engine.git] / ecomp-sdk-app / src / main / webapp / app / fusion / external / angular-1.5 / angular-touch.js
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;
283         } else {
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);