angularjs-rails-resource 0.1.4 → 0.1.5

Sign up to get free protection for your applications and to get access to all the features.
data/README.md CHANGED
@@ -58,6 +58,7 @@ The following options are available for the config object passed to the factory
58
58
  * **httpConfig** *(optional)* - By default we will add the following headers to ensure that the request is processed as JSON by Rails. You can specify additional http config options or override any of the defaults by setting this property. See the [AngularJS $http API](http://docs.angularjs.org/api/ng.$http) for more information.
59
59
  * **headers**
60
60
  * **Accept** - application/json
61
+ * **Content-Type** - application/json
61
62
  * **defaultParams** *(optional)* - If the resource expects a default set of query params on every call you can specify them here.
62
63
  * **requestTransformers** *(optional) - See [Transformers / Interceptors](#transformers--interceptors)
63
64
  * **responseInterceptors** *(optional)* - See [Transformers / Interceptors](#transformers--interceptors)
@@ -117,9 +118,18 @@ object to keep track of what the field was originally pointing to. The original
117
118
  that if response.data is reassigned that there's still a pointer to the original response.data object.
118
119
 
119
120
 
120
- ## Methods
121
+ ## Resource Methods
121
122
  Resources created using this factory have the following methods available and each one (except the constructor) returns a [Promise](#promises).
122
123
 
124
+ ### $url
125
+ ***
126
+
127
+ Returns the resource URL using the given context.
128
+
129
+ ####Parameters
130
+
131
+ * **context** - The context to use when building the url. See [Resource URLs](#resource-urls) above for more information.
132
+
123
133
  ### constructor
124
134
  ***
125
135
 
@@ -128,10 +138,19 @@ The constructor is the function returned by the railsResourceFactory and can be
128
138
  ####Parameters
129
139
  * **data** *(optional)* - An object containing the data to be stored in the instance.
130
140
 
141
+ ### $get
142
+ ***
143
+
144
+ Executes a GET request against the given URL and returns a promise that will be resolved with a new Resource instance (or instances in the case of an array response).
145
+
146
+ ####Parameters
147
+ * **url** - The url to GET
148
+ * **queryParams** - The set of query parameters to include in the GET request
149
+
131
150
  ### query
132
151
  ***
133
152
 
134
- A "class" method that executes a GET request against the base url with query parameters set via the params option.
153
+ Executes a GET request against the resource's base url and returns a promise that will be resolved with an array of new Resource instances.
135
154
 
136
155
  ####Parameters
137
156
  * **query params** - An map of strings or objects that are passed to $http to be turned into query parameters
@@ -141,16 +160,54 @@ A "class" method that executes a GET request against the base url with query par
141
160
  ### get
142
161
  ***
143
162
 
144
- A "class" method that executes a GET request against the resource url.
163
+ Executs a GET request against the resource's url and returns a promise that will be resolved with a new instance of the Resource.
145
164
 
146
165
  ####Parameters
147
166
  * **context** - A context object that is used during url evaluation to resolve expression variables. If you are using a basic url this can be an id number to append to the url.
148
167
 
149
168
 
169
+ ### $post, $put, $patch
170
+ ***
171
+
172
+ Transforms the given data and submits it using a POST/PUT/PATCH to the given URL and returns a promise that will be resolved with a new Resource instance (or instances in the case of an array response).
173
+
174
+ ####Parameters
175
+ * **url** - The url to POST/PUT/PATCH
176
+ * **data** - The data to transform and submit to the server
177
+
178
+ ### $delete
179
+ ***
180
+
181
+ Executes a DELETE against the given URL and returns a promise that will be resolved with a new Resource instance (if the server returns a body).
182
+
183
+ ####Parameters
184
+ * **url** - The url to POST/PUT/PATCH
185
+
186
+ ## Resource Instance Methods
187
+ The instance methods can be used on any instance (created manually or returned in a promise response) of a resource.
188
+ All of the instance methods will update the instance in-place on response and will resolve the promise with the current instance.
189
+
190
+ ### $url
191
+ ***
192
+
193
+ Returns the url for the instance.
194
+
195
+ ####Parameters
196
+
197
+ None
198
+
199
+ ### $post, $put, $patch
200
+ ***
201
+
202
+ Transforms the instance and submits it using POST/PUT/PATCH to the given URL and returns a promise that will be resolved with a new Resource instance (or instances in the case of an array response).
203
+
204
+ ####Parameters
205
+ * **url** - The url to POST/PUT/PATCH/DELETE
206
+
150
207
  ### create
151
208
  ***
152
209
 
153
- An "instance" method that executes a POST to the base url with the data defined in the instance.
210
+ Transforms and submits the instance using a POST to the resource base URL.
154
211
 
155
212
  ####Parameters
156
213
 
@@ -160,7 +217,7 @@ None
160
217
  ### update
161
218
  ***
162
219
 
163
- An "instance" method that executes a PUT to the resource url with the data defined in the instance.
220
+ Transforms and submits the instance using a PUT to the resource's URL.
164
221
 
165
222
  ####Parameters
166
223
 
@@ -170,7 +227,7 @@ None
170
227
  ### remove / delete
171
228
  ***
172
229
 
173
- Both of these are "instance" methods that execute a DELETE to the resource url.
230
+ Execute a DELETE to the resource's url.
174
231
 
175
232
  ####Parameters
176
233
 
@@ -1,7 +1,7 @@
1
1
  module Angularjs
2
2
  module Rails
3
3
  module Resource
4
- VERSION = "0.1.4"
4
+ VERSION = "0.1.5"
5
5
  end
6
6
  end
7
7
  end
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @license AngularJS v1.0.2
2
+ * @license AngularJS v1.1.4
3
3
  * (c) 2010-2012 Google, Inc. http://angularjs.org
4
4
  * License: MIT
5
5
  */
@@ -10,10 +10,10 @@ var directive = {};
10
10
  var service = { value: {} };
11
11
 
12
12
  var DEPENDENCIES = {
13
- 'angular.js': 'http://code.angularjs.org/angular-' + angular.version.full + '.min.js',
14
- 'angular-resource.js': 'http://code.angularjs.org/angular-resource-' + angular.version.full + '.min.js',
15
- 'angular-sanitize.js': 'http://code.angularjs.org/angular-sanitize-' + angular.version.full + '.min.js',
16
- 'angular-cookies.js': 'http://code.angularjs.org/angular-cookies-' + angular.version.full + '.min.js'
13
+ 'angular.js': 'http://code.angularjs.org/' + angular.version.full + '/angular.min.js',
14
+ 'angular-resource.js': 'http://code.angularjs.org/' + angular.version.full + '/angular-resource.min.js',
15
+ 'angular-sanitize.js': 'http://code.angularjs.org/' + angular.version.full + '/angular-sanitize.min.js',
16
+ 'angular-cookies.js': 'http://code.angularjs.org/' + angular.version.full + '/angular-cookies.min.js'
17
17
  };
18
18
 
19
19
 
@@ -185,7 +185,8 @@ directive.ngEvalJavascript = ['getEmbeddedTemplate', function(getEmbeddedTemplat
185
185
  }];
186
186
 
187
187
 
188
- directive.ngEmbedApp = ['$templateCache', '$browser', '$rootScope', '$location', function($templateCache, $browser, docsRootScope, $location) {
188
+ directive.ngEmbedApp = ['$templateCache', '$browser', '$rootScope', '$location', '$sniffer',
189
+ function($templateCache, $browser, docsRootScope, $location, $sniffer) {
189
190
  return {
190
191
  terminal: true,
191
192
  link: function(scope, element, attrs) {
@@ -195,6 +196,7 @@ directive.ngEmbedApp = ['$templateCache', '$browser', '$rootScope', '$location',
195
196
  $provide.value('$templateCache', $templateCache);
196
197
  $provide.value('$anchorScroll', angular.noop);
197
198
  $provide.value('$browser', $browser);
199
+ $provide.value('$sniffer', $sniffer);
198
200
  $provide.provider('$location', function() {
199
201
  this.$get = ['$rootScope', function($rootScope) {
200
202
  docsRootScope.$on('$locationChangeSuccess', function(event, oldUrl, newUrl) {
@@ -216,7 +218,7 @@ directive.ngEmbedApp = ['$templateCache', '$browser', '$rootScope', '$location',
216
218
  }, $delegate);
217
219
  }]);
218
220
  $provide.decorator('$rootScope', ['$delegate', function(embedRootScope) {
219
- docsRootScope.$watch(function() {
221
+ docsRootScope.$watch(function embedRootScopeDigestWatch() {
220
222
  embedRootScope.$digest();
221
223
  });
222
224
  return embedRootScope;
@@ -229,6 +231,7 @@ directive.ngEmbedApp = ['$templateCache', '$browser', '$rootScope', '$location',
229
231
  event.preventDefault();
230
232
  }
231
233
  });
234
+
232
235
  angular.bootstrap(element, modules);
233
236
  }
234
237
  };
@@ -290,6 +293,7 @@ service.getEmbeddedTemplate = ['reindentCode', function(reindentCode) {
290
293
 
291
294
 
292
295
  angular.module('bootstrapPrettify', []).directive(directive).factory(service);
296
+
293
297
  // Copyright (C) 2006 Google Inc.
294
298
  //
295
299
  // Licensed under the Apache License, Version 2.0 (the "License");
@@ -935,7 +939,7 @@ var REGEXP_PRECEDER_PATTERN = '(?:^^\\.?|[+-]|[!=]=?=?|\\#|%=?|&&?=?|\\(|\\*=?|[
935
939
  * recognized.
936
940
  *
937
941
  * Shortcut is an optional string of characters, any of which, if the first
938
- * character, gurantee that this pattern and only this pattern matches.
942
+ * character, guarantee that this pattern and only this pattern matches.
939
943
  *
940
944
  * @param {Array} shortcutStylePatterns patterns that always start with
941
945
  * a known character. Must have a shortcut string.
@@ -1829,5 +1833,6 @@ var REGEXP_PRECEDER_PATTERN = '(?:^^\\.?|[+-]|[!=]=?=?|\\#|%=?|&&?=?|\\(|\\*=?|[
1829
1833
  }
1830
1834
  })();
1831
1835
 
1836
+
1832
1837
  })(window, window.angular);
1833
1838
  angular.element(document).find('head').append('<style type="text/css">.com{color:#93a1a1;}.lit{color:#195f91;}.pun,.opn,.clo{color:#93a1a1;}.fun{color:#dc322f;}.str,.atv{color:#D14;}.kwd,.linenums .tag{color:#1e347b;}.typ,.atn,.dec,.var{color:teal;}.pln{color:#48484c;}.prettyprint{padding:8px;background-color:#f7f7f9;border:1px solid #e1e1e8;}.prettyprint.linenums{-webkit-box-shadow:inset 40px 0 0 #fbfbfc,inset 41px 0 0 #ececf0;-moz-box-shadow:inset 40px 0 0 #fbfbfc,inset 41px 0 0 #ececf0;box-shadow:inset 40px 0 0 #fbfbfc,inset 41px 0 0 #ececf0;}ol.linenums{margin:0 0 0 33px;}ol.linenums li{padding-left:12px;color:#bebec5;line-height:18px;text-shadow:0 1px 0 #fff;}</style>');
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @license AngularJS v1.0.2
2
+ * @license AngularJS v1.1.4
3
3
  * (c) 2010-2012 Google, Inc. http://angularjs.org
4
4
  * License: MIT
5
5
  */
@@ -15,7 +15,7 @@ directive.dropdownToggle =
15
15
  return {
16
16
  restrict: 'C',
17
17
  link: function(scope, element, attrs) {
18
- scope.$watch(function(){return $location.path();}, function() {
18
+ scope.$watch(function dropdownTogglePathWatch(){return $location.path();}, function dropdownTogglePathWatchAction() {
19
19
  close && close();
20
20
  });
21
21
 
@@ -163,4 +163,5 @@ directive.tabPane = function() {
163
163
 
164
164
  angular.module('bootstrap', []).directive(directive);
165
165
 
166
+
166
167
  })(window, window.angular);
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @license AngularJS v1.0.2
2
+ * @license AngularJS v1.1.4
3
3
  * (c) 2010-2012 Google, Inc. http://angularjs.org
4
4
  * License: MIT
5
5
  */
@@ -25,6 +25,18 @@ angular.module('ngCookies', ['ng']).
25
25
  * this object, new cookies are created/deleted at the end of current $eval.
26
26
  *
27
27
  * @example
28
+ <doc:example>
29
+ <doc:source>
30
+ <script>
31
+ function ExampleController($cookies) {
32
+ // Retrieving a cookie
33
+ var favoriteCookie = $cookies.myFavorite;
34
+ // Setting a cookie
35
+ $cookies.myFavorite = 'oatmeal';
36
+ }
37
+ </script>
38
+ </doc:source>
39
+ </doc:example>
28
40
  */
29
41
  factory('$cookies', ['$rootScope', '$browser', function ($rootScope, $browser) {
30
42
  var cookies = {},
@@ -168,4 +180,5 @@ angular.module('ngCookies', ['ng']).
168
180
 
169
181
  }]);
170
182
 
183
+
171
184
  })(window, window.angular);
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @license AngularJS v1.0.2
2
+ * @license AngularJS v1.1.4
3
3
  * (c) 2010-2012 Google, Inc. http://angularjs.org
4
4
  * License: MIT
5
5
  */
@@ -36,7 +36,7 @@ function setupModuleLoader(window) {
36
36
  *
37
37
  * # Module
38
38
  *
39
- * A module is a collocation of services, directives, filters, and configure information. Module
39
+ * A module is a collocation of services, directives, filters, and configuration information. Module
40
40
  * is used to configure the {@link AUTO.$injector $injector}.
41
41
  *
42
42
  * <pre>
@@ -67,7 +67,7 @@ function setupModuleLoader(window) {
67
67
  * @param {!string} name The name of the module to create or retrieve.
68
68
  * @param {Array.<string>=} requires If specified then new module is being created. If unspecified then the
69
69
  * the module is being retrieved for further configuration.
70
- * @param {Function} configFn Option configuration function for the module. Same as
70
+ * @param {Function} configFn Optional configuration function for the module. Same as
71
71
  * {@link angular.Module#config Module#config()}.
72
72
  * @returns {module} new module with the {@link angular.Module} api.
73
73
  */
@@ -170,6 +170,33 @@ function setupModuleLoader(window) {
170
170
  */
171
171
  constant: invokeLater('$provide', 'constant', 'unshift'),
172
172
 
173
+ /**
174
+ * @ngdoc method
175
+ * @name angular.Module#animation
176
+ * @methodOf angular.Module
177
+ * @param {string} name animation name
178
+ * @param {Function} animationFactory Factory function for creating new instance of an animation.
179
+ * @description
180
+ *
181
+ * Defines an animation hook that can be later used with {@link ng.directive:ngAnimate ngAnimate}
182
+ * alongside {@link ng.directive:ngAnimate#Description common ng directives} as well as custom directives.
183
+ * <pre>
184
+ * module.animation('animation-name', function($inject1, $inject2) {
185
+ * return {
186
+ * //this gets called in preparation to setup an animation
187
+ * setup : function(element) { ... },
188
+ *
189
+ * //this gets called once the animation is run
190
+ * start : function(element, done, memo) { ... }
191
+ * }
192
+ * })
193
+ * </pre>
194
+ *
195
+ * See {@link ng.$animationProvider#register $animationProvider.register()} and
196
+ * {@link ng.directive:ngAnimate ngAnimate} for more information.
197
+ */
198
+ animation: invokeLater('$animationProvider', 'register'),
199
+
173
200
  /**
174
201
  * @ngdoc method
175
202
  * @name angular.Module#filter
@@ -222,8 +249,8 @@ function setupModuleLoader(window) {
222
249
  * @param {Function} initializationFn Execute this function after injector creation.
223
250
  * Useful for application initialization.
224
251
  * @description
225
- * Use this method to register work which needs to be performed when the injector with
226
- * with the current module is finished loading.
252
+ * Use this method to register work which should be performed when the injector is done
253
+ * loading all modules.
227
254
  */
228
255
  run: function(block) {
229
256
  runBlocks.push(block);
@@ -254,6 +281,7 @@ function setupModuleLoader(window) {
254
281
  });
255
282
 
256
283
  }
284
+
257
285
  )(window);
258
286
 
259
287
  /**
@@ -0,0 +1,4 @@
1
+ angular.module("ngLocale", [], ["$provide", function($provide) {
2
+ var PLURAL_CATEGORY = {ZERO: "zero", ONE: "one", TWO: "two", FEW: "few", MANY: "many", OTHER: "other"};
3
+ $provide.value("$locale", {"DATETIME_FORMATS":{"MONTH":["January","February","March","April","May","June","July","August","September","October","November","December"],"SHORTMONTH":["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"],"DAY":["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"],"SHORTDAY":["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],"AMPMS":["AM","PM"],"medium":"MMM d, y h:mm:ss a","short":"M/d/yy h:mm a","fullDate":"EEEE, MMMM d, y","longDate":"MMMM d, y","mediumDate":"MMM d, y","shortDate":"M/d/yy","mediumTime":"h:mm:ss a","shortTime":"h:mm a"},"NUMBER_FORMATS":{"DECIMAL_SEP":".","GROUP_SEP":",","PATTERNS":[{"minInt":1,"minFrac":0,"macFrac":0,"posPre":"","posSuf":"","negPre":"-","negSuf":"","gSize":3,"lgSize":3,"maxFrac":3},{"minInt":1,"minFrac":2,"macFrac":0,"posPre":"\u00A4","posSuf":"","negPre":"(\u00A4","negSuf":")","gSize":3,"lgSize":3,"maxFrac":2}],"CURRENCY_SYM":"$"},"pluralCat":function (n) { if (n == 1) { return PLURAL_CATEGORY.ONE; } return PLURAL_CATEGORY.OTHER;},"id":"en-us"});
4
+ }]);
@@ -0,0 +1,267 @@
1
+ /**
2
+ * @license AngularJS v1.1.4
3
+ * (c) 2010-2012 Google, Inc. http://angularjs.org
4
+ * License: MIT
5
+ */
6
+ (function(window, angular, undefined) {
7
+ 'use strict';
8
+
9
+ /**
10
+ * @ngdoc overview
11
+ * @name ngMobile
12
+ * @description
13
+ */
14
+
15
+ /*
16
+ * Touch events and other mobile helpers by Braden Shepherdson (braden.shepherdson@gmail.com)
17
+ * Based on jQuery Mobile touch event handling (jquerymobile.com)
18
+ */
19
+
20
+ // define ngSanitize module and register $sanitize service
21
+ var ngMobile = angular.module('ngMobile', []);
22
+
23
+ /**
24
+ * @ngdoc directive
25
+ * @name ngMobile.directive:ngTap
26
+ *
27
+ * @description
28
+ * Specify custom behavior when element is tapped on a touchscreen device.
29
+ * A tap is a brief, down-and-up touch without much motion.
30
+ *
31
+ * @element ANY
32
+ * @param {expression} ngClick {@link guide/expression Expression} to evaluate
33
+ * upon tap. (Event object is available as `$event`)
34
+ *
35
+ * @example
36
+ <doc:example>
37
+ <doc:source>
38
+ <button ng-tap="count = count + 1" ng-init="count=0">
39
+ Increment
40
+ </button>
41
+ count: {{ count }}
42
+ </doc:source>
43
+ </doc:example>
44
+ */
45
+
46
+ ngMobile.config(['$provide', function($provide) {
47
+ $provide.decorator('ngClickDirective', ['$delegate', function($delegate) {
48
+ // drop the default ngClick directive
49
+ $delegate.shift();
50
+ return $delegate;
51
+ }]);
52
+ }]);
53
+
54
+ ngMobile.directive('ngClick', ['$parse', '$timeout', '$rootElement',
55
+ function($parse, $timeout, $rootElement) {
56
+ var TAP_DURATION = 750; // Shorter than 750ms is a tap, longer is a taphold or drag.
57
+ var MOVE_TOLERANCE = 12; // 12px seems to work in most mobile browsers.
58
+ var PREVENT_DURATION = 2500; // 2.5 seconds maximum from preventGhostClick call to click
59
+ var CLICKBUSTER_THRESHOLD = 25; // 25 pixels in any dimension is the limit for busting clicks.
60
+ var lastPreventedTime;
61
+ var touchCoordinates;
62
+
63
+
64
+ // TAP EVENTS AND GHOST CLICKS
65
+ //
66
+ // Why tap events?
67
+ // Mobile browsers detect a tap, then wait a moment (usually ~300ms) to see if you're
68
+ // double-tapping, and then fire a click event.
69
+ //
70
+ // This delay sucks and makes mobile apps feel unresponsive.
71
+ // So we detect touchstart, touchmove, touchcancel and touchend ourselves and determine when
72
+ // the user has tapped on something.
73
+ //
74
+ // What happens when the browser then generates a click event?
75
+ // The browser, of course, also detects the tap and fires a click after a delay. This results in
76
+ // tapping/clicking twice. So we do "clickbusting" to prevent it.
77
+ //
78
+ // How does it work?
79
+ // We attach global touchstart and click handlers, that run during the capture (early) phase.
80
+ // So the sequence for a tap is:
81
+ // - global touchstart: Sets an "allowable region" at the point touched.
82
+ // - element's touchstart: Starts a touch
83
+ // (- touchmove or touchcancel ends the touch, no click follows)
84
+ // - element's touchend: Determines if the tap is valid (didn't move too far away, didn't hold
85
+ // too long) and fires the user's tap handler. The touchend also calls preventGhostClick().
86
+ // - preventGhostClick() removes the allowable region the global touchstart created.
87
+ // - The browser generates a click event.
88
+ // - The global click handler catches the click, and checks whether it was in an allowable region.
89
+ // - If preventGhostClick was called, the region will have been removed, the click is busted.
90
+ // - If the region is still there, the click proceeds normally. Therefore clicks on links and
91
+ // other elements without ngTap on them work normally.
92
+ //
93
+ // This is an ugly, terrible hack!
94
+ // Yeah, tell me about it. The alternatives are using the slow click events, or making our users
95
+ // deal with the ghost clicks, so I consider this the least of evils. Fortunately Angular
96
+ // encapsulates this ugly logic away from the user.
97
+ //
98
+ // Why not just put click handlers on the element?
99
+ // We do that too, just to be sure. The problem is that the tap event might have caused the DOM
100
+ // to change, so that the click fires in the same position but something else is there now. So
101
+ // the handlers are global and care only about coordinates and not elements.
102
+
103
+ // Checks if the coordinates are close enough to be within the region.
104
+ function hit(x1, y1, x2, y2) {
105
+ return Math.abs(x1 - x2) < CLICKBUSTER_THRESHOLD && Math.abs(y1 - y2) < CLICKBUSTER_THRESHOLD;
106
+ }
107
+
108
+ // Checks a list of allowable regions against a click location.
109
+ // Returns true if the click should be allowed.
110
+ // Splices out the allowable region from the list after it has been used.
111
+ function checkAllowableRegions(touchCoordinates, x, y) {
112
+ for (var i = 0; i < touchCoordinates.length; i += 2) {
113
+ if (hit(touchCoordinates[i], touchCoordinates[i+1], x, y)) {
114
+ touchCoordinates.splice(i, i + 2);
115
+ return true; // allowable region
116
+ }
117
+ }
118
+ return false; // No allowable region; bust it.
119
+ }
120
+
121
+ // Global click handler that prevents the click if it's in a bustable zone and preventGhostClick
122
+ // was called recently.
123
+ function onClick(event) {
124
+ if (Date.now() - lastPreventedTime > PREVENT_DURATION) {
125
+ return; // Too old.
126
+ }
127
+
128
+ var touches = event.touches && event.touches.length ? event.touches : [event];
129
+ var x = touches[0].clientX;
130
+ var y = touches[0].clientY;
131
+ // Work around desktop Webkit quirk where clicking a label will fire two clicks (on the label
132
+ // and on the input element). Depending on the exact browser, this second click we don't want
133
+ // to bust has either (0,0) or negative coordinates.
134
+ if (x < 1 && y < 1) {
135
+ return; // offscreen
136
+ }
137
+
138
+ // Look for an allowable region containing this click.
139
+ // If we find one, that means it was created by touchstart and not removed by
140
+ // preventGhostClick, so we don't bust it.
141
+ if (checkAllowableRegions(touchCoordinates, x, y)) {
142
+ return;
143
+ }
144
+
145
+ // If we didn't find an allowable region, bust the click.
146
+ event.stopPropagation();
147
+ event.preventDefault();
148
+ }
149
+
150
+
151
+ // Global touchstart handler that creates an allowable region for a click event.
152
+ // This allowable region can be removed by preventGhostClick if we want to bust it.
153
+ function onTouchStart(event) {
154
+ var touches = event.touches && event.touches.length ? event.touches : [event];
155
+ var x = touches[0].clientX;
156
+ var y = touches[0].clientY;
157
+ touchCoordinates.push(x, y);
158
+
159
+ $timeout(function() {
160
+ // Remove the allowable region.
161
+ for (var i = 0; i < touchCoordinates.length; i += 2) {
162
+ if (touchCoordinates[i] == x && touchCoordinates[i+1] == y) {
163
+ touchCoordinates.splice(i, i + 2);
164
+ return;
165
+ }
166
+ }
167
+ }, PREVENT_DURATION, false);
168
+ }
169
+
170
+ // On the first call, attaches some event handlers. Then whenever it gets called, it creates a
171
+ // zone around the touchstart where clicks will get busted.
172
+ function preventGhostClick(x, y) {
173
+ if (!touchCoordinates) {
174
+ $rootElement[0].addEventListener('click', onClick, true);
175
+ $rootElement[0].addEventListener('touchstart', onTouchStart, true);
176
+ touchCoordinates = [];
177
+ }
178
+
179
+ lastPreventedTime = Date.now();
180
+
181
+ checkAllowableRegions(touchCoordinates, x, y);
182
+ }
183
+
184
+ // Actual linking function.
185
+ return function(scope, element, attr) {
186
+ var expressionFn = $parse(attr.ngClick),
187
+ tapping = false,
188
+ tapElement, // Used to blur the element after a tap.
189
+ startTime, // Used to check if the tap was held too long.
190
+ touchStartX,
191
+ touchStartY;
192
+
193
+ function resetState() {
194
+ tapping = false;
195
+ }
196
+
197
+ element.bind('touchstart', function(event) {
198
+ tapping = true;
199
+ tapElement = event.target ? event.target : event.srcElement; // IE uses srcElement.
200
+ // Hack for Safari, which can target text nodes instead of containers.
201
+ if(tapElement.nodeType == 3) {
202
+ tapElement = tapElement.parentNode;
203
+ }
204
+
205
+ startTime = Date.now();
206
+
207
+ var touches = event.touches && event.touches.length ? event.touches : [event];
208
+ var e = touches[0].originalEvent || touches[0];
209
+ touchStartX = e.clientX;
210
+ touchStartY = e.clientY;
211
+ });
212
+
213
+ element.bind('touchmove', function(event) {
214
+ resetState();
215
+ });
216
+
217
+ element.bind('touchcancel', function(event) {
218
+ resetState();
219
+ });
220
+
221
+ element.bind('touchend', function(event) {
222
+ var diff = Date.now() - startTime;
223
+
224
+ var touches = (event.changedTouches && event.changedTouches.length) ? event.changedTouches :
225
+ ((event.touches && event.touches.length) ? event.touches : [event]);
226
+ var e = touches[0].originalEvent || touches[0];
227
+ var x = e.clientX;
228
+ var y = e.clientY;
229
+ var dist = Math.sqrt( Math.pow(x - touchStartX, 2) + Math.pow(y - touchStartY, 2) );
230
+
231
+ if (tapping && diff < TAP_DURATION && dist < MOVE_TOLERANCE) {
232
+ // Call preventGhostClick so the clickbuster will catch the corresponding click.
233
+ preventGhostClick(x, y);
234
+
235
+ // Blur the focused element (the button, probably) before firing the callback.
236
+ // This doesn't work perfectly on Android Chrome, but seems to work elsewhere.
237
+ // I couldn't get anything to work reliably on Android Chrome.
238
+ if (tapElement) {
239
+ tapElement.blur();
240
+ }
241
+
242
+ scope.$apply(function() {
243
+ // TODO(braden): This is sending the touchend, not a tap or click. Is that kosher?
244
+ expressionFn(scope, {$event: event});
245
+ });
246
+ }
247
+ tapping = false;
248
+ });
249
+
250
+ // Hack for iOS Safari's benefit. It goes searching for onclick handlers and is liable to click
251
+ // something else nearby.
252
+ element.onclick = function(event) { };
253
+
254
+ // Fallback click handler.
255
+ // Busted clicks don't get this far, and adding this handler allows ng-tap to be used on
256
+ // desktop as well, to allow more portable sites.
257
+ element.bind('click', function(event) {
258
+ scope.$apply(function() {
259
+ expressionFn(scope, {$event: event});
260
+ });
261
+ });
262
+ };
263
+ }]);
264
+
265
+
266
+
267
+ })(window, window.angular);