| /** | 
|  * @license AngularJS v1.5.7 | 
|  * (c) 2010-2016 Google, Inc. http://angularjs.org | 
|  * License: MIT | 
|  */ | 
| (function(window, angular) {'use strict'; | 
|   | 
| /* global ngTouchClickDirectiveFactory: false, | 
|  */ | 
|   | 
| /** | 
|  * @ngdoc module | 
|  * @name ngTouch | 
|  * @description | 
|  * | 
|  * # ngTouch | 
|  * | 
|  * The `ngTouch` module provides touch events and other helpers for touch-enabled devices. | 
|  * The implementation is based on jQuery Mobile touch event handling | 
|  * ([jquerymobile.com](http://jquerymobile.com/)). | 
|  * | 
|  * | 
|  * See {@link ngTouch.$swipe `$swipe`} for usage. | 
|  * | 
|  * <div doc-module-components="ngTouch"></div> | 
|  * | 
|  */ | 
|   | 
| // define ngTouch module | 
| /* global -ngTouch */ | 
| var ngTouch = angular.module('ngTouch', []); | 
|   | 
| ngTouch.provider('$touch', $TouchProvider); | 
|   | 
| function nodeName_(element) { | 
|   return angular.lowercase(element.nodeName || (element[0] && element[0].nodeName)); | 
| } | 
|   | 
| /** | 
|  * @ngdoc provider | 
|  * @name $touchProvider | 
|  * | 
|  * @description | 
|  * The `$touchProvider` allows enabling / disabling {@link ngTouch.ngClick ngTouch's ngClick directive}. | 
|  */ | 
| $TouchProvider.$inject = ['$provide', '$compileProvider']; | 
| function $TouchProvider($provide, $compileProvider) { | 
|   | 
|   /** | 
|    * @ngdoc method | 
|    * @name  $touchProvider#ngClickOverrideEnabled | 
|    * | 
|    * @param {boolean=} enabled update the ngClickOverrideEnabled state if provided, otherwise just return the | 
|    * current ngClickOverrideEnabled state | 
|    * @returns {*} current value if used as getter or itself (chaining) if used as setter | 
|    * | 
|    * @kind function | 
|    * | 
|    * @description | 
|    * Call this method to enable/disable {@link ngTouch.ngClick ngTouch's ngClick directive}. If enabled, | 
|    * the default ngClick directive will be replaced by a version that eliminates the 300ms delay for | 
|    * click events on browser for touch-devices. | 
|    * | 
|    * The default is `false`. | 
|    * | 
|    */ | 
|   var ngClickOverrideEnabled = false; | 
|   var ngClickDirectiveAdded = false; | 
|   this.ngClickOverrideEnabled = function(enabled) { | 
|     if (angular.isDefined(enabled)) { | 
|   | 
|       if (enabled && !ngClickDirectiveAdded) { | 
|         ngClickDirectiveAdded = true; | 
|   | 
|         // Use this to identify the correct directive in the delegate | 
|         ngTouchClickDirectiveFactory.$$moduleName = 'ngTouch'; | 
|         $compileProvider.directive('ngClick', ngTouchClickDirectiveFactory); | 
|   | 
|         $provide.decorator('ngClickDirective', ['$delegate', function($delegate) { | 
|           if (ngClickOverrideEnabled) { | 
|             // drop the default ngClick directive | 
|             $delegate.shift(); | 
|           } else { | 
|             // drop the ngTouch ngClick directive if the override has been re-disabled (because | 
|             // we cannot de-register added directives) | 
|             var i = $delegate.length - 1; | 
|             while (i >= 0) { | 
|               if ($delegate[i].$$moduleName === 'ngTouch') { | 
|                 $delegate.splice(i, 1); | 
|                 break; | 
|               } | 
|               i--; | 
|             } | 
|           } | 
|   | 
|           return $delegate; | 
|         }]); | 
|       } | 
|   | 
|       ngClickOverrideEnabled = enabled; | 
|       return this; | 
|     } | 
|   | 
|     return ngClickOverrideEnabled; | 
|   }; | 
|   | 
|   /** | 
|   * @ngdoc service | 
|   * @name $touch | 
|   * @kind object | 
|   * | 
|   * @description | 
|   * Provides the {@link ngTouch.$touch#ngClickOverrideEnabled `ngClickOverrideEnabled`} method. | 
|   * | 
|   */ | 
|   this.$get = function() { | 
|     return { | 
|       /** | 
|        * @ngdoc method | 
|        * @name  $touch#ngClickOverrideEnabled | 
|        * | 
|        * @returns {*} current value of `ngClickOverrideEnabled` set in the {@link ngTouch.$touchProvider $touchProvider}, | 
|        * i.e. if {@link ngTouch.ngClick ngTouch's ngClick} directive is enabled. | 
|        * | 
|        * @kind function | 
|        */ | 
|       ngClickOverrideEnabled: function() { | 
|         return ngClickOverrideEnabled; | 
|       } | 
|     }; | 
|   }; | 
|   | 
| } | 
|   | 
| /* global ngTouch: false */ | 
|   | 
|     /** | 
|      * @ngdoc service | 
|      * @name $swipe | 
|      * | 
|      * @description | 
|      * The `$swipe` service is a service that abstracts the messier details of hold-and-drag swipe | 
|      * behavior, to make implementing swipe-related directives more convenient. | 
|      * | 
|      * Requires the {@link ngTouch `ngTouch`} module to be installed. | 
|      * | 
|      * `$swipe` is used by the `ngSwipeLeft` and `ngSwipeRight` directives in `ngTouch`. | 
|      * | 
|      * # Usage | 
|      * The `$swipe` service is an object with a single method: `bind`. `bind` takes an element | 
|      * which is to be watched for swipes, and an object with four handler functions. See the | 
|      * documentation for `bind` below. | 
|      */ | 
|   | 
| ngTouch.factory('$swipe', [function() { | 
|   // The total distance in any direction before we make the call on swipe vs. scroll. | 
|   var MOVE_BUFFER_RADIUS = 10; | 
|   | 
|   var POINTER_EVENTS = { | 
|     'mouse': { | 
|       start: 'mousedown', | 
|       move: 'mousemove', | 
|       end: 'mouseup' | 
|     }, | 
|     'touch': { | 
|       start: 'touchstart', | 
|       move: 'touchmove', | 
|       end: 'touchend', | 
|       cancel: 'touchcancel' | 
|     } | 
|   }; | 
|   | 
|   function getCoordinates(event) { | 
|     var originalEvent = event.originalEvent || event; | 
|     var touches = originalEvent.touches && originalEvent.touches.length ? originalEvent.touches : [originalEvent]; | 
|     var e = (originalEvent.changedTouches && originalEvent.changedTouches[0]) || touches[0]; | 
|   | 
|     return { | 
|       x: e.clientX, | 
|       y: e.clientY | 
|     }; | 
|   } | 
|   | 
|   function getEvents(pointerTypes, eventType) { | 
|     var res = []; | 
|     angular.forEach(pointerTypes, function(pointerType) { | 
|       var eventName = POINTER_EVENTS[pointerType][eventType]; | 
|       if (eventName) { | 
|         res.push(eventName); | 
|       } | 
|     }); | 
|     return res.join(' '); | 
|   } | 
|   | 
|   return { | 
|     /** | 
|      * @ngdoc method | 
|      * @name $swipe#bind | 
|      * | 
|      * @description | 
|      * The main method of `$swipe`. It takes an element to be watched for swipe motions, and an | 
|      * object containing event handlers. | 
|      * The pointer types that should be used can be specified via the optional | 
|      * third argument, which is an array of strings `'mouse'` and `'touch'`. By default, | 
|      * `$swipe` will listen for `mouse` and `touch` events. | 
|      * | 
|      * The four events are `start`, `move`, `end`, and `cancel`. `start`, `move`, and `end` | 
|      * receive as a parameter a coordinates object of the form `{ x: 150, y: 310 }` and the raw | 
|      * `event`. `cancel` receives the raw `event` as its single parameter. | 
|      * | 
|      * `start` is called on either `mousedown` or `touchstart`. After this event, `$swipe` is | 
|      * watching for `touchmove` or `mousemove` events. These events are ignored until the total | 
|      * distance moved in either dimension exceeds a small threshold. | 
|      * | 
|      * Once this threshold is exceeded, either the horizontal or vertical delta is greater. | 
|      * - If the horizontal distance is greater, this is a swipe and `move` and `end` events follow. | 
|      * - If the vertical distance is greater, this is a scroll, and we let the browser take over. | 
|      *   A `cancel` event is sent. | 
|      * | 
|      * `move` is called on `mousemove` and `touchmove` after the above logic has determined that | 
|      * a swipe is in progress. | 
|      * | 
|      * `end` is called when a swipe is successfully completed with a `touchend` or `mouseup`. | 
|      * | 
|      * `cancel` is called either on a `touchcancel` from the browser, or when we begin scrolling | 
|      * as described above. | 
|      * | 
|      */ | 
|     bind: function(element, eventHandlers, pointerTypes) { | 
|       // Absolute total movement, used to control swipe vs. scroll. | 
|       var totalX, totalY; | 
|       // Coordinates of the start position. | 
|       var startCoords; | 
|       // Last event's position. | 
|       var lastPos; | 
|       // Whether a swipe is active. | 
|       var active = false; | 
|   | 
|       pointerTypes = pointerTypes || ['mouse', 'touch']; | 
|       element.on(getEvents(pointerTypes, 'start'), function(event) { | 
|         startCoords = getCoordinates(event); | 
|         active = true; | 
|         totalX = 0; | 
|         totalY = 0; | 
|         lastPos = startCoords; | 
|         eventHandlers['start'] && eventHandlers['start'](startCoords, event); | 
|       }); | 
|       var events = getEvents(pointerTypes, 'cancel'); | 
|       if (events) { | 
|         element.on(events, function(event) { | 
|           active = false; | 
|           eventHandlers['cancel'] && eventHandlers['cancel'](event); | 
|         }); | 
|       } | 
|   | 
|       element.on(getEvents(pointerTypes, 'move'), function(event) { | 
|         if (!active) return; | 
|   | 
|         // Android will send a touchcancel if it thinks we're starting to scroll. | 
|         // So when the total distance (+ or - or both) exceeds 10px in either direction, | 
|         // we either: | 
|         // - On totalX > totalY, we send preventDefault() and treat this as a swipe. | 
|         // - On totalY > totalX, we let the browser handle it as a scroll. | 
|   | 
|         if (!startCoords) return; | 
|         var coords = getCoordinates(event); | 
|   | 
|         totalX += Math.abs(coords.x - lastPos.x); | 
|         totalY += Math.abs(coords.y - lastPos.y); | 
|   | 
|         lastPos = coords; | 
|   | 
|         if (totalX < MOVE_BUFFER_RADIUS && totalY < MOVE_BUFFER_RADIUS) { | 
|           return; | 
|         } | 
|   | 
|         // One of totalX or totalY has exceeded the buffer, so decide on swipe vs. scroll. | 
|         if (totalY > totalX) { | 
|           // Allow native scrolling to take over. | 
|           active = false; | 
|           eventHandlers['cancel'] && eventHandlers['cancel'](event); | 
|           return; | 
|         } else { | 
|           // Prevent the browser from scrolling. | 
|           event.preventDefault(); | 
|           eventHandlers['move'] && eventHandlers['move'](coords, event); | 
|         } | 
|       }); | 
|   | 
|       element.on(getEvents(pointerTypes, 'end'), function(event) { | 
|         if (!active) return; | 
|         active = false; | 
|         eventHandlers['end'] && eventHandlers['end'](getCoordinates(event), event); | 
|       }); | 
|     } | 
|   }; | 
| }]); | 
|   | 
| /* global ngTouch: false, | 
|   nodeName_: false | 
| */ | 
|   | 
| /** | 
|  * @ngdoc directive | 
|  * @name ngClick | 
|  * @deprecated | 
|  * | 
|  * @description | 
|  * <div class="alert alert-danger"> | 
|  * **DEPRECATION NOTICE**: Beginning with Angular 1.5, this directive is deprecated and by default **disabled**. | 
|  * The directive will receive no further support and might be removed from future releases. | 
|  * If you need the directive, you can enable it with the {@link ngTouch.$touchProvider $touchProvider#ngClickOverrideEnabled} | 
|  * function. We also recommend that you migrate to [FastClick](https://github.com/ftlabs/fastclick). | 
|  * To learn more about the 300ms delay, this [Telerik article](http://developer.telerik.com/featured/300-ms-click-delay-ios-8/) | 
|  * gives a good overview. | 
|  * </div> | 
|  * A more powerful replacement for the default ngClick designed to be used on touchscreen | 
|  * devices. Most mobile browsers wait about 300ms after a tap-and-release before sending | 
|  * the click event. This version handles them immediately, and then prevents the | 
|  * following click event from propagating. | 
|  * | 
|  * Requires the {@link ngTouch `ngTouch`} module to be installed. | 
|  * | 
|  * This directive can fall back to using an ordinary click event, and so works on desktop | 
|  * browsers as well as mobile. | 
|  * | 
|  * This directive also sets the CSS class `ng-click-active` while the element is being held | 
|  * down (by a mouse click or touch) so you can restyle the depressed element if you wish. | 
|  * | 
|  * @element ANY | 
|  * @param {expression} ngClick {@link guide/expression Expression} to evaluate | 
|  * upon tap. (Event object is available as `$event`) | 
|  * | 
|  * @example | 
|     <example module="ngClickExample" deps="angular-touch.js"> | 
|       <file name="index.html"> | 
|         <button ng-click="count = count + 1" ng-init="count=0"> | 
|           Increment | 
|         </button> | 
|         count: {{ count }} | 
|       </file> | 
|       <file name="script.js"> | 
|         angular.module('ngClickExample', ['ngTouch']); | 
|       </file> | 
|     </example> | 
|  */ | 
|   | 
| var ngTouchClickDirectiveFactory = ['$parse', '$timeout', '$rootElement', | 
|     function($parse, $timeout, $rootElement) { | 
|   var TAP_DURATION = 750; // Shorter than 750ms is a tap, longer is a taphold or drag. | 
|   var MOVE_TOLERANCE = 12; // 12px seems to work in most mobile browsers. | 
|   var PREVENT_DURATION = 2500; // 2.5 seconds maximum from preventGhostClick call to click | 
|   var CLICKBUSTER_THRESHOLD = 25; // 25 pixels in any dimension is the limit for busting clicks. | 
|   | 
|   var ACTIVE_CLASS_NAME = 'ng-click-active'; | 
|   var lastPreventedTime; | 
|   var touchCoordinates; | 
|   var lastLabelClickCoordinates; | 
|   | 
|   | 
|   // TAP EVENTS AND GHOST CLICKS | 
|   // | 
|   // Why tap events? | 
|   // Mobile browsers detect a tap, then wait a moment (usually ~300ms) to see if you're | 
|   // double-tapping, and then fire a click event. | 
|   // | 
|   // This delay sucks and makes mobile apps feel unresponsive. | 
|   // So we detect touchstart, touchcancel and touchend ourselves and determine when | 
|   // the user has tapped on something. | 
|   // | 
|   // What happens when the browser then generates a click event? | 
|   // The browser, of course, also detects the tap and fires a click after a delay. This results in | 
|   // tapping/clicking twice. We do "clickbusting" to prevent it. | 
|   // | 
|   // How does it work? | 
|   // We attach global touchstart and click handlers, that run during the capture (early) phase. | 
|   // So the sequence for a tap is: | 
|   // - global touchstart: Sets an "allowable region" at the point touched. | 
|   // - element's touchstart: Starts a touch | 
|   // (- touchcancel ends the touch, no click follows) | 
|   // - element's touchend: Determines if the tap is valid (didn't move too far away, didn't hold | 
|   //   too long) and fires the user's tap handler. The touchend also calls preventGhostClick(). | 
|   // - preventGhostClick() removes the allowable region the global touchstart created. | 
|   // - The browser generates a click event. | 
|   // - The global click handler catches the click, and checks whether it was in an allowable region. | 
|   //     - If preventGhostClick was called, the region will have been removed, the click is busted. | 
|   //     - If the region is still there, the click proceeds normally. Therefore clicks on links and | 
|   //       other elements without ngTap on them work normally. | 
|   // | 
|   // This is an ugly, terrible hack! | 
|   // Yeah, tell me about it. The alternatives are using the slow click events, or making our users | 
|   // deal with the ghost clicks, so I consider this the least of evils. Fortunately Angular | 
|   // encapsulates this ugly logic away from the user. | 
|   // | 
|   // Why not just put click handlers on the element? | 
|   // We do that too, just to be sure. If the tap event caused the DOM to change, | 
|   // it is possible another element is now in that position. To take account for these possibly | 
|   // distinct elements, the handlers are global and care only about coordinates. | 
|   | 
|   // Checks if the coordinates are close enough to be within the region. | 
|   function hit(x1, y1, x2, y2) { | 
|     return Math.abs(x1 - x2) < CLICKBUSTER_THRESHOLD && Math.abs(y1 - y2) < CLICKBUSTER_THRESHOLD; | 
|   } | 
|   | 
|   // Checks a list of allowable regions against a click location. | 
|   // Returns true if the click should be allowed. | 
|   // Splices out the allowable region from the list after it has been used. | 
|   function checkAllowableRegions(touchCoordinates, x, y) { | 
|     for (var i = 0; i < touchCoordinates.length; i += 2) { | 
|       if (hit(touchCoordinates[i], touchCoordinates[i + 1], x, y)) { | 
|         touchCoordinates.splice(i, i + 2); | 
|         return true; // allowable region | 
|       } | 
|     } | 
|     return false; // No allowable region; bust it. | 
|   } | 
|   | 
|   // Global click handler that prevents the click if it's in a bustable zone and preventGhostClick | 
|   // was called recently. | 
|   function onClick(event) { | 
|     if (Date.now() - lastPreventedTime > PREVENT_DURATION) { | 
|       return; // Too old. | 
|     } | 
|   | 
|     var touches = event.touches && event.touches.length ? event.touches : [event]; | 
|     var x = touches[0].clientX; | 
|     var y = touches[0].clientY; | 
|     // Work around desktop Webkit quirk where clicking a label will fire two clicks (on the label | 
|     // and on the input element). Depending on the exact browser, this second click we don't want | 
|     // to bust has either (0,0), negative coordinates, or coordinates equal to triggering label | 
|     // click event | 
|     if (x < 1 && y < 1) { | 
|       return; // offscreen | 
|     } | 
|     if (lastLabelClickCoordinates && | 
|         lastLabelClickCoordinates[0] === x && lastLabelClickCoordinates[1] === y) { | 
|       return; // input click triggered by label click | 
|     } | 
|     // reset label click coordinates on first subsequent click | 
|     if (lastLabelClickCoordinates) { | 
|       lastLabelClickCoordinates = null; | 
|     } | 
|     // remember label click coordinates to prevent click busting of trigger click event on input | 
|     if (nodeName_(event.target) === 'label') { | 
|       lastLabelClickCoordinates = [x, y]; | 
|     } | 
|   | 
|     // Look for an allowable region containing this click. | 
|     // If we find one, that means it was created by touchstart and not removed by | 
|     // preventGhostClick, so we don't bust it. | 
|     if (checkAllowableRegions(touchCoordinates, x, y)) { | 
|       return; | 
|     } | 
|   | 
|     // If we didn't find an allowable region, bust the click. | 
|     event.stopPropagation(); | 
|     event.preventDefault(); | 
|   | 
|     // Blur focused form elements | 
|     event.target && event.target.blur && event.target.blur(); | 
|   } | 
|   | 
|   | 
|   // Global touchstart handler that creates an allowable region for a click event. | 
|   // This allowable region can be removed by preventGhostClick if we want to bust it. | 
|   function onTouchStart(event) { | 
|     var touches = event.touches && event.touches.length ? event.touches : [event]; | 
|     var x = touches[0].clientX; | 
|     var y = touches[0].clientY; | 
|     touchCoordinates.push(x, y); | 
|   | 
|     $timeout(function() { | 
|       // Remove the allowable region. | 
|       for (var i = 0; i < touchCoordinates.length; i += 2) { | 
|         if (touchCoordinates[i] == x && touchCoordinates[i + 1] == y) { | 
|           touchCoordinates.splice(i, i + 2); | 
|           return; | 
|         } | 
|       } | 
|     }, PREVENT_DURATION, false); | 
|   } | 
|   | 
|   // On the first call, attaches some event handlers. Then whenever it gets called, it creates a | 
|   // zone around the touchstart where clicks will get busted. | 
|   function preventGhostClick(x, y) { | 
|     if (!touchCoordinates) { | 
|       $rootElement[0].addEventListener('click', onClick, true); | 
|       $rootElement[0].addEventListener('touchstart', onTouchStart, true); | 
|       touchCoordinates = []; | 
|     } | 
|   | 
|     lastPreventedTime = Date.now(); | 
|   | 
|     checkAllowableRegions(touchCoordinates, x, y); | 
|   } | 
|   | 
|   // Actual linking function. | 
|   return function(scope, element, attr) { | 
|     var clickHandler = $parse(attr.ngClick), | 
|         tapping = false, | 
|         tapElement,  // Used to blur the element after a tap. | 
|         startTime,   // Used to check if the tap was held too long. | 
|         touchStartX, | 
|         touchStartY; | 
|   | 
|     function resetState() { | 
|       tapping = false; | 
|       element.removeClass(ACTIVE_CLASS_NAME); | 
|     } | 
|   | 
|     element.on('touchstart', function(event) { | 
|       tapping = true; | 
|       tapElement = event.target ? event.target : event.srcElement; // IE uses srcElement. | 
|       // Hack for Safari, which can target text nodes instead of containers. | 
|       if (tapElement.nodeType == 3) { | 
|         tapElement = tapElement.parentNode; | 
|       } | 
|   | 
|       element.addClass(ACTIVE_CLASS_NAME); | 
|   | 
|       startTime = Date.now(); | 
|   | 
|       // Use jQuery originalEvent | 
|       var originalEvent = event.originalEvent || event; | 
|       var touches = originalEvent.touches && originalEvent.touches.length ? originalEvent.touches : [originalEvent]; | 
|       var e = touches[0]; | 
|       touchStartX = e.clientX; | 
|       touchStartY = e.clientY; | 
|     }); | 
|   | 
|     element.on('touchcancel', function(event) { | 
|       resetState(); | 
|     }); | 
|   | 
|     element.on('touchend', function(event) { | 
|       var diff = Date.now() - startTime; | 
|   | 
|       // Use jQuery originalEvent | 
|       var originalEvent = event.originalEvent || event; | 
|       var touches = (originalEvent.changedTouches && originalEvent.changedTouches.length) ? | 
|           originalEvent.changedTouches : | 
|           ((originalEvent.touches && originalEvent.touches.length) ? originalEvent.touches : [originalEvent]); | 
|       var e = touches[0]; | 
|       var x = e.clientX; | 
|       var y = e.clientY; | 
|       var dist = Math.sqrt(Math.pow(x - touchStartX, 2) + Math.pow(y - touchStartY, 2)); | 
|   | 
|       if (tapping && diff < TAP_DURATION && dist < MOVE_TOLERANCE) { | 
|         // Call preventGhostClick so the clickbuster will catch the corresponding click. | 
|         preventGhostClick(x, y); | 
|   | 
|         // Blur the focused element (the button, probably) before firing the callback. | 
|         // This doesn't work perfectly on Android Chrome, but seems to work elsewhere. | 
|         // I couldn't get anything to work reliably on Android Chrome. | 
|         if (tapElement) { | 
|           tapElement.blur(); | 
|         } | 
|   | 
|         if (!angular.isDefined(attr.disabled) || attr.disabled === false) { | 
|           element.triggerHandler('click', [event]); | 
|         } | 
|       } | 
|   | 
|       resetState(); | 
|     }); | 
|   | 
|     // Hack for iOS Safari's benefit. It goes searching for onclick handlers and is liable to click | 
|     // something else nearby. | 
|     element.onclick = function(event) { }; | 
|   | 
|     // Actual click handler. | 
|     // There are three different kinds of clicks, only two of which reach this point. | 
|     // - On desktop browsers without touch events, their clicks will always come here. | 
|     // - On mobile browsers, the simulated "fast" click will call this. | 
|     // - But the browser's follow-up slow click will be "busted" before it reaches this handler. | 
|     // Therefore it's safe to use this directive on both mobile and desktop. | 
|     element.on('click', function(event, touchend) { | 
|       scope.$apply(function() { | 
|         clickHandler(scope, {$event: (touchend || event)}); | 
|       }); | 
|     }); | 
|   | 
|     element.on('mousedown', function(event) { | 
|       element.addClass(ACTIVE_CLASS_NAME); | 
|     }); | 
|   | 
|     element.on('mousemove mouseup', function(event) { | 
|       element.removeClass(ACTIVE_CLASS_NAME); | 
|     }); | 
|   | 
|   }; | 
| }]; | 
|   | 
| /* global ngTouch: false */ | 
|   | 
| /** | 
|  * @ngdoc directive | 
|  * @name ngSwipeLeft | 
|  * | 
|  * @description | 
|  * Specify custom behavior when an element is swiped to the left on a touchscreen device. | 
|  * A leftward swipe is a quick, right-to-left slide of the finger. | 
|  * Though ngSwipeLeft is designed for touch-based devices, it will work with a mouse click and drag | 
|  * too. | 
|  * | 
|  * To disable the mouse click and drag functionality, add `ng-swipe-disable-mouse` to | 
|  * the `ng-swipe-left` or `ng-swipe-right` DOM Element. | 
|  * | 
|  * Requires the {@link ngTouch `ngTouch`} module to be installed. | 
|  * | 
|  * @element ANY | 
|  * @param {expression} ngSwipeLeft {@link guide/expression Expression} to evaluate | 
|  * upon left swipe. (Event object is available as `$event`) | 
|  * | 
|  * @example | 
|     <example module="ngSwipeLeftExample" deps="angular-touch.js"> | 
|       <file name="index.html"> | 
|         <div ng-show="!showActions" ng-swipe-left="showActions = true"> | 
|           Some list content, like an email in the inbox | 
|         </div> | 
|         <div ng-show="showActions" ng-swipe-right="showActions = false"> | 
|           <button ng-click="reply()">Reply</button> | 
|           <button ng-click="delete()">Delete</button> | 
|         </div> | 
|       </file> | 
|       <file name="script.js"> | 
|         angular.module('ngSwipeLeftExample', ['ngTouch']); | 
|       </file> | 
|     </example> | 
|  */ | 
|   | 
| /** | 
|  * @ngdoc directive | 
|  * @name ngSwipeRight | 
|  * | 
|  * @description | 
|  * Specify custom behavior when an element is swiped to the right on a touchscreen device. | 
|  * A rightward swipe is a quick, left-to-right slide of the finger. | 
|  * Though ngSwipeRight is designed for touch-based devices, it will work with a mouse click and drag | 
|  * too. | 
|  * | 
|  * Requires the {@link ngTouch `ngTouch`} module to be installed. | 
|  * | 
|  * @element ANY | 
|  * @param {expression} ngSwipeRight {@link guide/expression Expression} to evaluate | 
|  * upon right swipe. (Event object is available as `$event`) | 
|  * | 
|  * @example | 
|     <example module="ngSwipeRightExample" deps="angular-touch.js"> | 
|       <file name="index.html"> | 
|         <div ng-show="!showActions" ng-swipe-left="showActions = true"> | 
|           Some list content, like an email in the inbox | 
|         </div> | 
|         <div ng-show="showActions" ng-swipe-right="showActions = false"> | 
|           <button ng-click="reply()">Reply</button> | 
|           <button ng-click="delete()">Delete</button> | 
|         </div> | 
|       </file> | 
|       <file name="script.js"> | 
|         angular.module('ngSwipeRightExample', ['ngTouch']); | 
|       </file> | 
|     </example> | 
|  */ | 
|   | 
| function makeSwipeDirective(directiveName, direction, eventName) { | 
|   ngTouch.directive(directiveName, ['$parse', '$swipe', function($parse, $swipe) { | 
|     // The maximum vertical delta for a swipe should be less than 75px. | 
|     var MAX_VERTICAL_DISTANCE = 75; | 
|     // Vertical distance should not be more than a fraction of the horizontal distance. | 
|     var MAX_VERTICAL_RATIO = 0.3; | 
|     // At least a 30px lateral motion is necessary for a swipe. | 
|     var MIN_HORIZONTAL_DISTANCE = 30; | 
|   | 
|     return function(scope, element, attr) { | 
|       var swipeHandler = $parse(attr[directiveName]); | 
|   | 
|       var startCoords, valid; | 
|   | 
|       function validSwipe(coords) { | 
|         // Check that it's within the coordinates. | 
|         // Absolute vertical distance must be within tolerances. | 
|         // Horizontal distance, we take the current X - the starting X. | 
|         // This is negative for leftward swipes and positive for rightward swipes. | 
|         // After multiplying by the direction (-1 for left, +1 for right), legal swipes | 
|         // (ie. same direction as the directive wants) will have a positive delta and | 
|         // illegal ones a negative delta. | 
|         // Therefore this delta must be positive, and larger than the minimum. | 
|         if (!startCoords) return false; | 
|         var deltaY = Math.abs(coords.y - startCoords.y); | 
|         var deltaX = (coords.x - startCoords.x) * direction; | 
|         return valid && // Short circuit for already-invalidated swipes. | 
|             deltaY < MAX_VERTICAL_DISTANCE && | 
|             deltaX > 0 && | 
|             deltaX > MIN_HORIZONTAL_DISTANCE && | 
|             deltaY / deltaX < MAX_VERTICAL_RATIO; | 
|       } | 
|   | 
|       var pointerTypes = ['touch']; | 
|       if (!angular.isDefined(attr['ngSwipeDisableMouse'])) { | 
|         pointerTypes.push('mouse'); | 
|       } | 
|       $swipe.bind(element, { | 
|         'start': function(coords, event) { | 
|           startCoords = coords; | 
|           valid = true; | 
|         }, | 
|         'cancel': function(event) { | 
|           valid = false; | 
|         }, | 
|         'end': function(coords, event) { | 
|           if (validSwipe(coords)) { | 
|             scope.$apply(function() { | 
|               element.triggerHandler(eventName); | 
|               swipeHandler(scope, {$event: event}); | 
|             }); | 
|           } | 
|         } | 
|       }, pointerTypes); | 
|     }; | 
|   }]); | 
| } | 
|   | 
| // Left is negative X-coordinate, right is positive. | 
| makeSwipeDirective('ngSwipeLeft', -1, 'swipeleft'); | 
| makeSwipeDirective('ngSwipeRight', 1, 'swiperight'); | 
|   | 
|   | 
|   | 
| })(window, window.angular); |