You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@struts.apache.org by jo...@apache.org on 2015/07/10 17:37:07 UTC

[20/30] struts git commit: WW-4522 Support latest stable AngularJS version in maven angularjs archetype

http://git-wip-us.apache.org/repos/asf/struts/blob/7928d345/archetypes/struts2-archetype-angularjs/src/main/resources/archetype-resources/src/main/webapp/js/lib/angular/angular-messages.js
----------------------------------------------------------------------
diff --git a/archetypes/struts2-archetype-angularjs/src/main/resources/archetype-resources/src/main/webapp/js/lib/angular/angular-messages.js b/archetypes/struts2-archetype-angularjs/src/main/resources/archetype-resources/src/main/webapp/js/lib/angular/angular-messages.js
index 5c4a2af..b9ca322 100644
--- a/archetypes/struts2-archetype-angularjs/src/main/resources/archetype-resources/src/main/webapp/js/lib/angular/angular-messages.js
+++ b/archetypes/struts2-archetype-angularjs/src/main/resources/archetype-resources/src/main/webapp/js/lib/angular/angular-messages.js
@@ -1,10 +1,18 @@
 /**
- * @license AngularJS v1.3.15
- * (c) 2010-2014 Google, Inc. http://angularjs.org
+ * @license AngularJS v1.4.2
+ * (c) 2010-2015 Google, Inc. http://angularjs.org
  * License: MIT
  */
 (function(window, angular, undefined) {'use strict';
 
+/* jshint ignore:start */
+// this code is in the core, but not in angular-messages.js
+var isArray = angular.isArray;
+var forEach = angular.forEach;
+var isString = angular.isString;
+var jqLite = angular.element;
+/* jshint ignore:end */
+
 /**
  * @ngdoc module
  * @name ngMessages
@@ -17,8 +25,8 @@
  * `ngMessage` directives are designed to handle the complexity, inheritance and priority
  * sequencing based on the order of how the messages are defined in the template.
  *
- * Currently, the ngMessages module only contains the code for the `ngMessages`
- * and `ngMessage` directives.
+ * Currently, the ngMessages module only contains the code for the `ngMessages`, `ngMessagesInclude`
+ * `ngMessage` and `ngMessageExp` directives.
  *
  * # Usage
  * The `ngMessages` directive listens on a key/value collection which is set on the ngMessages attribute.
@@ -28,10 +36,15 @@
  *
  * ```html
  * <form name="myForm">
- *   <input type="text" ng-model="field" name="myField" required minlength="5" />
- *   <div ng-messages="myForm.myField.$error">
+ *   <label>
+ *     Enter text:
+ *     <input type="text" ng-model="field" name="myField" required minlength="5" />
+ *   </label>
+ *   <div ng-messages="myForm.myField.$error" role="alert">
  *     <div ng-message="required">You did not enter a field</div>
- *     <div ng-message="minlength">The value entered is too short</div>
+ *     <div ng-message="minlength, maxlength">
+ *       Your email must be between 5 and 100 characters long
+ *     </div>
  *   </div>
  * </form>
  * ```
@@ -43,7 +56,7 @@
  *
  * ```javascript
  * <!-- keep in mind that ngModel automatically sets these error flags -->
- * myField.$error = { minlength : true, required : false };
+ * myField.$error = { minlength : true, required : true };
  * ```
  *
  * Then the `required` message will be displayed first. When required is false then the `minlength` message
@@ -57,7 +70,11 @@
  * ngMessages directive to make this happen.
  *
  * ```html
+ * <!-- attribute-style usage -->
  * <div ng-messages="myForm.myField.$error" ng-messages-multiple>...</div>
+ *
+ * <!-- element-style usage -->
+ * <ng-messages for="myForm.myField.$error" multiple>...</ng-messages>
  * ```
  *
  * ## Reusing and Overriding Messages
@@ -70,12 +87,15 @@
  *   <div ng-message="required">This field is required</div>
  *   <div ng-message="minlength">This field is too short</div>
  * </script>
- * <div ng-messages="myForm.myField.$error" ng-messages-include="error-messages"></div>
+ *
+ * <div ng-messages="myForm.myField.$error" role="alert">
+ *   <div ng-messages-include="error-messages"></div>
+ * </div>
  * ```
  *
  * However, including generic messages may not be useful enough to match all input fields, therefore,
  * `ngMessages` provides the ability to override messages defined in the remote template by redefining
- * then within the directive container.
+ * them within the directive container.
  *
  * ```html
  * <!-- a generic template of error messages known as "my-custom-messages" -->
@@ -85,19 +105,26 @@
  * </script>
  *
  * <form name="myForm">
- *   <input type="email"
- *          id="email"
- *          name="myEmail"
- *          ng-model="email"
- *          minlength="5"
- *          required />
- *
- *   <div ng-messages="myForm.myEmail.$error" ng-messages-include="my-custom-messages">
+ *   <label>
+ *     Email address
+ *     <input type="email"
+ *            id="email"
+ *            name="myEmail"
+ *            ng-model="email"
+ *            minlength="5"
+ *            required />
+ *   </label>
+ *   <!-- any ng-message elements that appear BEFORE the ng-messages-include will
+ *        override the messages present in the ng-messages-include template -->
+ *   <div ng-messages="myForm.myEmail.$error" role="alert">
  *     <!-- this required message has overridden the template message -->
  *     <div ng-message="required">You did not enter your email address</div>
  *
  *     <!-- this is a brand new message and will appear last in the prioritization -->
  *     <div ng-message="email">Your email address is invalid</div>
+ *
+ *     <!-- and here are the generic error messages -->
+ *     <div ng-messages-include="my-custom-messages"></div>
  *   </div>
  * </form>
  * ```
@@ -107,20 +134,80 @@
  * email addresses, date fields, autocomplete inputs, etc...), specialized error messages can be applied
  * while more generic messages can be used to handle other, more general input errors.
  *
+ * ## Dynamic Messaging
+ * ngMessages also supports using expressions to dynamically change key values. Using arrays and
+ * repeaters to list messages is also supported. This means that the code below will be able to
+ * fully adapt itself and display the appropriate message when any of the expression data changes:
+ *
+ * ```html
+ * <form name="myForm">
+ *   <label>
+ *     Email address
+ *     <input type="email"
+ *            name="myEmail"
+ *            ng-model="email"
+ *            minlength="5"
+ *            required />
+ *   </label>
+ *   <div ng-messages="myForm.myEmail.$error" role="alert">
+ *     <div ng-message="required">You did not enter your email address</div>
+ *     <div ng-repeat="errorMessage in errorMessages">
+ *       <!-- use ng-message-exp for a message whose key is given by an expression -->
+ *       <div ng-message-exp="errorMessage.type">{{ errorMessage.text }}</div>
+ *     </div>
+ *   </div>
+ * </form>
+ * ```
+ *
+ * The `errorMessage.type` expression can be a string value or it can be an array so
+ * that multiple errors can be associated with a single error message:
+ *
+ * ```html
+ *   <label>
+ *     Email address
+ *     <input type="email"
+ *            ng-model="data.email"
+ *            name="myEmail"
+ *            ng-minlength="5"
+ *            ng-maxlength="100"
+ *            required />
+ *   </label>
+ *   <div ng-messages="myForm.myEmail.$error" role="alert">
+ *     <div ng-message-exp="'required'">You did not enter your email address</div>
+ *     <div ng-message-exp="['minlength', 'maxlength']">
+ *       Your email must be between 5 and 100 characters long
+ *     </div>
+ *   </div>
+ * ```
+ *
+ * Feel free to use other structural directives such as ng-if and ng-switch to further control
+ * what messages are active and when. Be careful, if you place ng-message on the same element
+ * as these structural directives, Angular may not be able to determine if a message is active
+ * or not. Therefore it is best to place the ng-message on a child element of the structural
+ * directive.
+ *
+ * ```html
+ * <div ng-messages="myForm.myEmail.$error" role="alert">
+ *   <div ng-if="showRequiredError">
+ *     <div ng-message="required">Please enter something</div>
+ *   </div>
+ * </div>
+ * ```
+ *
  * ## Animations
- * If the `ngAnimate` module is active within the application then both the `ngMessages` and
- * `ngMessage` directives will trigger animations whenever any messages are added and removed
- * from the DOM by the `ngMessages` directive.
+ * If the `ngAnimate` module is active within the application then the `ngMessages`, `ngMessage` and
+ * `ngMessageExp` directives will trigger animations whenever any messages are added and removed from
+ * the DOM by the `ngMessages` directive.
  *
  * Whenever the `ngMessages` directive contains one or more visible messages then the `.ng-active` CSS
  * class will be added to the element. The `.ng-inactive` CSS class will be applied when there are no
- * animations present. Therefore, CSS transitions and keyframes as well as JavaScript animations can
+ * messages present. Therefore, CSS transitions and keyframes as well as JavaScript animations can
  * hook into the animations whenever these classes are added/removed.
  *
  * Let's say that our HTML code for our messages container looks like so:
  *
  * ```html
- * <div ng-messages="myMessages" class="my-messages">
+ * <div ng-messages="myMessages" class="my-messages" role="alert">
  *   <div ng-message="alert" class="some-message">...</div>
  *   <div ng-message="fail" class="some-message">...</div>
  * </div>
@@ -169,14 +256,14 @@ angular.module('ngMessages', [])
     *
     * @description
     * `ngMessages` is a directive that is designed to show and hide messages based on the state
-    * of a key/value object that it listens on. The directive itself compliments error message
+    * of a key/value object that it listens on. The directive itself complements error message
     * reporting with the `ngModel` $error object (which stores a key/value state of validation errors).
     *
     * `ngMessages` manages the state of internal messages within its container element. The internal
     * messages use the `ngMessage` directive and will be inserted/removed from the page depending
     * on if they're present within the key/value object. By default, only one message will be displayed
     * at a time and this depends on the prioritization of the messages within the template. (This can
-    * be changed by using the ng-messages-multiple on the directive container.)
+    * be changed by using the `ng-messages-multiple` or `multiple` attribute on the directive container.)
     *
     * A remote template can also be used to promote message reusability and messages can also be
     * overridden.
@@ -186,24 +273,23 @@ angular.module('ngMessages', [])
     * @usage
     * ```html
     * <!-- using attribute directives -->
-    * <ANY ng-messages="expression">
-    *   <ANY ng-message="keyValue1">...</ANY>
-    *   <ANY ng-message="keyValue2">...</ANY>
-    *   <ANY ng-message="keyValue3">...</ANY>
+    * <ANY ng-messages="expression" role="alert">
+    *   <ANY ng-message="stringValue">...</ANY>
+    *   <ANY ng-message="stringValue1, stringValue2, ...">...</ANY>
+    *   <ANY ng-message-exp="expressionValue">...</ANY>
     * </ANY>
     *
     * <!-- or by using element directives -->
-    * <ng-messages for="expression">
-    *   <ng-message when="keyValue1">...</ng-message>
-    *   <ng-message when="keyValue2">...</ng-message>
-    *   <ng-message when="keyValue3">...</ng-message>
+    * <ng-messages for="expression" role="alert">
+    *   <ng-message when="stringValue">...</ng-message>
+    *   <ng-message when="stringValue1, stringValue2, ...">...</ng-message>
+    *   <ng-message when-exp="expressionValue">...</ng-message>
     * </ng-messages>
     * ```
     *
     * @param {string} ngMessages an angular expression evaluating to a key/value object
     *                 (this is typically the $error object on an ngModel instance).
     * @param {string=} ngMessagesMultiple|multiple when set, all messages will be displayed with true
-    * @param {string=} ngMessagesInclude|include when set, the specified template will be included into the ng-messages container
     *
     * @example
     * <example name="ngMessages-directive" module="ngMessagesExample"
@@ -211,17 +297,18 @@ angular.module('ngMessages', [])
     *          animations="true" fixBase="true">
     *   <file name="index.html">
     *     <form name="myForm">
-    *       <label>Enter your name:</label>
-    *       <input type="text"
-    *              name="myName"
-    *              ng-model="name"
-    *              ng-minlength="5"
-    *              ng-maxlength="20"
-    *              required />
-    *
+    *       <label>
+    *         Enter your name:
+    *         <input type="text"
+    *                name="myName"
+    *                ng-model="name"
+    *                ng-minlength="5"
+    *                ng-maxlength="20"
+    *                required />
+    *       </label>
     *       <pre>myForm.myName.$error = {{ myForm.myName.$error | json }}</pre>
     *
-    *       <div ng-messages="myForm.myName.$error" style="color:maroon">
+    *       <div ng-messages="myForm.myName.$error" style="color:maroon" role="alert">
     *         <div ng-message="required">You did not enter a field</div>
     *         <div ng-message="minlength">Your field is too short</div>
     *         <div ng-message="maxlength">Your field is too long</div>
@@ -233,91 +320,220 @@ angular.module('ngMessages', [])
     *   </file>
     * </example>
     */
-  .directive('ngMessages', ['$compile', '$animate', '$templateRequest',
-                   function($compile,    $animate,   $templateRequest) {
-    var ACTIVE_CLASS = 'ng-active';
-    var INACTIVE_CLASS = 'ng-inactive';
+   .directive('ngMessages', ['$animate', function($animate) {
+     var ACTIVE_CLASS = 'ng-active';
+     var INACTIVE_CLASS = 'ng-inactive';
 
-    return {
-      restrict: 'AE',
-      controller: function() {
-        this.$renderNgMessageClasses = angular.noop;
-
-        var messages = [];
-        this.registerMessage = function(index, message) {
-          for (var i = 0; i < messages.length; i++) {
-            if (messages[i].type == message.type) {
-              if (index != i) {
-                var temp = messages[index];
-                messages[index] = messages[i];
-                if (index < messages.length) {
-                  messages[i] = temp;
-                } else {
-                  messages.splice(0, i); //remove the old one (and shift left)
-                }
-              }
-              return;
-            }
-          }
-          messages.splice(index, 0, message); //add the new one (and shift right)
-        };
+     return {
+       require: 'ngMessages',
+       restrict: 'AE',
+       controller: ['$element', '$scope', '$attrs', function($element, $scope, $attrs) {
+         var ctrl = this;
+         var latestKey = 0;
 
-        this.renderMessages = function(values, multiple) {
-          values = values || {};
+         var messages = this.messages = {};
+         var renderLater, cachedCollection;
 
-          var found;
-          angular.forEach(messages, function(message) {
-            if ((!found || multiple) && truthyVal(values[message.type])) {
-              message.attach();
-              found = true;
-            } else {
-              message.detach();
-            }
-          });
+         this.render = function(collection) {
+           collection = collection || {};
 
-          this.renderElementClasses(found);
+           renderLater = false;
+           cachedCollection = collection;
 
-          function truthyVal(value) {
-            return value !== null && value !== false && value;
-          }
-        };
-      },
-      require: 'ngMessages',
-      link: function($scope, element, $attrs, ctrl) {
-        ctrl.renderElementClasses = function(bool) {
-          bool ? $animate.setClass(element, ACTIVE_CLASS, INACTIVE_CLASS)
-               : $animate.setClass(element, INACTIVE_CLASS, ACTIVE_CLASS);
-        };
+           // this is true if the attribute is empty or if the attribute value is truthy
+           var multiple = isAttrTruthy($scope, $attrs.ngMessagesMultiple) ||
+                          isAttrTruthy($scope, $attrs.multiple);
 
-        //JavaScript treats empty strings as false, but ng-message-multiple by itself is an empty string
-        var multiple = angular.isString($attrs.ngMessagesMultiple) ||
-                       angular.isString($attrs.multiple);
+           var unmatchedMessages = [];
+           var matchedKeys = {};
+           var messageItem = ctrl.head;
+           var messageFound = false;
+           var totalMessages = 0;
 
-        var cachedValues, watchAttr = $attrs.ngMessages || $attrs['for']; //for is a reserved keyword
-        $scope.$watchCollection(watchAttr, function(values) {
-          cachedValues = values;
-          ctrl.renderMessages(values, multiple);
-        });
+           // we use != instead of !== to allow for both undefined and null values
+           while (messageItem != null) {
+             totalMessages++;
+             var messageCtrl = messageItem.message;
 
-        var tpl = $attrs.ngMessagesInclude || $attrs.include;
-        if (tpl) {
-          $templateRequest(tpl)
-            .then(function processTemplate(html) {
-              var after, container = angular.element('<div/>').html(html);
-              angular.forEach(container.children(), function(elm) {
-               elm = angular.element(elm);
-               after ? after.after(elm)
-                     : element.prepend(elm); //start of the container
-               after = elm;
-               $compile(elm)($scope);
-              });
-              ctrl.renderMessages(cachedValues, multiple);
-            });
-        }
-      }
-    };
-  }])
+             var messageUsed = false;
+             if (!messageFound) {
+               forEach(collection, function(value, key) {
+                 if (!messageUsed && truthy(value) && messageCtrl.test(key)) {
+                   // this is to prevent the same error name from showing up twice
+                   if (matchedKeys[key]) return;
+                   matchedKeys[key] = true;
+
+                   messageUsed = true;
+                   messageCtrl.attach();
+                 }
+               });
+             }
+
+             if (messageUsed) {
+               // unless we want to display multiple messages then we should
+               // set a flag here to avoid displaying the next message in the list
+               messageFound = !multiple;
+             } else {
+               unmatchedMessages.push(messageCtrl);
+             }
+
+             messageItem = messageItem.next;
+           }
+
+           forEach(unmatchedMessages, function(messageCtrl) {
+             messageCtrl.detach();
+           });
+
+           unmatchedMessages.length !== totalMessages
+              ? $animate.setClass($element, ACTIVE_CLASS, INACTIVE_CLASS)
+              : $animate.setClass($element, INACTIVE_CLASS, ACTIVE_CLASS);
+         };
+
+         $scope.$watchCollection($attrs.ngMessages || $attrs['for'], ctrl.render);
+
+         this.reRender = function() {
+           if (!renderLater) {
+             renderLater = true;
+             $scope.$evalAsync(function() {
+               if (renderLater) {
+                 cachedCollection && ctrl.render(cachedCollection);
+               }
+             });
+           }
+         };
+
+         this.register = function(comment, messageCtrl) {
+           var nextKey = latestKey.toString();
+           messages[nextKey] = {
+             message: messageCtrl
+           };
+           insertMessageNode($element[0], comment, nextKey);
+           comment.$$ngMessageNode = nextKey;
+           latestKey++;
+
+           ctrl.reRender();
+         };
+
+         this.deregister = function(comment) {
+           var key = comment.$$ngMessageNode;
+           delete comment.$$ngMessageNode;
+           removeMessageNode($element[0], comment, key);
+           delete messages[key];
+           ctrl.reRender();
+         };
+
+         function findPreviousMessage(parent, comment) {
+           var prevNode = comment;
+           var parentLookup = [];
+           while (prevNode && prevNode !== parent) {
+             var prevKey = prevNode.$$ngMessageNode;
+             if (prevKey && prevKey.length) {
+               return messages[prevKey];
+             }
+
+             // dive deeper into the DOM and examine its children for any ngMessage
+             // comments that may be in an element that appears deeper in the list
+             if (prevNode.childNodes.length && parentLookup.indexOf(prevNode) == -1) {
+               parentLookup.push(prevNode);
+               prevNode = prevNode.childNodes[prevNode.childNodes.length - 1];
+             } else {
+               prevNode = prevNode.previousSibling || prevNode.parentNode;
+             }
+           }
+         }
+
+         function insertMessageNode(parent, comment, key) {
+           var messageNode = messages[key];
+           if (!ctrl.head) {
+             ctrl.head = messageNode;
+           } else {
+             var match = findPreviousMessage(parent, comment);
+             if (match) {
+               messageNode.next = match.next;
+               match.next = messageNode;
+             } else {
+               messageNode.next = ctrl.head;
+               ctrl.head = messageNode;
+             }
+           }
+         }
+
+         function removeMessageNode(parent, comment, key) {
+           var messageNode = messages[key];
+
+           var match = findPreviousMessage(parent, comment);
+           if (match) {
+             match.next = messageNode.next;
+           } else {
+             ctrl.head = messageNode.next;
+           }
+         }
+       }]
+     };
+
+     function isAttrTruthy(scope, attr) {
+      return (isString(attr) && attr.length === 0) || //empty attribute
+             truthy(scope.$eval(attr));
+     }
 
+     function truthy(val) {
+       return isString(val) ? val.length : !!val;
+     }
+   }])
+
+   /**
+    * @ngdoc directive
+    * @name ngMessagesInclude
+    * @restrict AE
+    * @scope
+    *
+    * @description
+    * `ngMessagesInclude` is a directive with the purpose to import existing ngMessage template
+    * code from a remote template and place the downloaded template code into the exact spot
+    * that the ngMessagesInclude directive is placed within the ngMessages container. This allows
+    * for a series of pre-defined messages to be reused and also allows for the developer to
+    * determine what messages are overridden due to the placement of the ngMessagesInclude directive.
+    *
+    * @usage
+    * ```html
+    * <!-- using attribute directives -->
+    * <ANY ng-messages="expression" role="alert">
+    *   <ANY ng-messages-include="remoteTplString">...</ANY>
+    * </ANY>
+    *
+    * <!-- or by using element directives -->
+    * <ng-messages for="expression" role="alert">
+    *   <ng-messages-include src="expressionValue1">...</ng-messages-include>
+    * </ng-messages>
+    * ```
+    *
+    * {@link module:ngMessages Click here} to learn more about `ngMessages` and `ngMessage`.
+    *
+    * @param {string} ngMessagesInclude|src a string value corresponding to the remote template.
+    */
+   .directive('ngMessagesInclude',
+     ['$templateRequest', '$document', '$compile', function($templateRequest, $document, $compile) {
+
+     return {
+       restrict: 'AE',
+       require: '^^ngMessages', // we only require this for validation sake
+       link: function($scope, element, attrs) {
+         var src = attrs.ngMessagesInclude || attrs.src;
+         $templateRequest(src).then(function(html) {
+           $compile(html)($scope, function(contents) {
+             element.after(contents);
+
+             // the anchor is placed for debugging purposes
+             var anchor = jqLite($document[0].createComment(' ngMessagesInclude: ' + src + ' '));
+             element.after(anchor);
+
+             // we don't want to pollute the DOM anymore by keeping an empty directive element
+             element.remove();
+           });
+         });
+       }
+     };
+   }])
 
    /**
     * @ngdoc directive
@@ -337,65 +553,126 @@ angular.module('ngMessages', [])
     * @usage
     * ```html
     * <!-- using attribute directives -->
+    * <ANY ng-messages="expression" role="alert">
+    *   <ANY ng-message="stringValue">...</ANY>
+    *   <ANY ng-message="stringValue1, stringValue2, ...">...</ANY>
+    * </ANY>
+    *
+    * <!-- or by using element directives -->
+    * <ng-messages for="expression" role="alert">
+    *   <ng-message when="stringValue">...</ng-message>
+    *   <ng-message when="stringValue1, stringValue2, ...">...</ng-message>
+    * </ng-messages>
+    * ```
+    *
+    * @param {expression} ngMessage|when a string value corresponding to the message key.
+    */
+  .directive('ngMessage', ngMessageDirectiveFactory('AE'))
+
+
+   /**
+    * @ngdoc directive
+    * @name ngMessageExp
+    * @restrict AE
+    * @scope
+    *
+    * @description
+    * `ngMessageExp` is a directive with the purpose to show and hide a particular message.
+    * For `ngMessageExp` to operate, a parent `ngMessages` directive on a parent DOM element
+    * must be situated since it determines which messages are visible based on the state
+    * of the provided key/value map that `ngMessages` listens on.
+    *
+    * @usage
+    * ```html
+    * <!-- using attribute directives -->
     * <ANY ng-messages="expression">
-    *   <ANY ng-message="keyValue1">...</ANY>
-    *   <ANY ng-message="keyValue2">...</ANY>
-    *   <ANY ng-message="keyValue3">...</ANY>
+    *   <ANY ng-message-exp="expressionValue">...</ANY>
     * </ANY>
     *
     * <!-- or by using element directives -->
     * <ng-messages for="expression">
-    *   <ng-message when="keyValue1">...</ng-message>
-    *   <ng-message when="keyValue2">...</ng-message>
-    *   <ng-message when="keyValue3">...</ng-message>
+    *   <ng-message when-exp="expressionValue">...</ng-message>
     * </ng-messages>
     * ```
     *
-    * @param {string} ngMessage a string value corresponding to the message key.
+    * {@link module:ngMessages Click here} to learn more about `ngMessages` and `ngMessage`.
+    *
+    * @param {expression} ngMessageExp|whenExp an expression value corresponding to the message key.
     */
-  .directive('ngMessage', ['$animate', function($animate) {
-    var COMMENT_NODE = 8;
+  .directive('ngMessageExp', ngMessageDirectiveFactory('A'));
+
+function ngMessageDirectiveFactory(restrict) {
+  return ['$animate', function($animate) {
     return {
-      require: '^ngMessages',
+      restrict: 'AE',
       transclude: 'element',
       terminal: true,
-      restrict: 'AE',
-      link: function($scope, $element, $attrs, ngMessages, $transclude) {
-        var index, element;
-
-        var commentNode = $element[0];
-        var parentNode = commentNode.parentNode;
-        for (var i = 0, j = 0; i < parentNode.childNodes.length; i++) {
-          var node = parentNode.childNodes[i];
-          if (node.nodeType == COMMENT_NODE && node.nodeValue.indexOf('ngMessage') >= 0) {
-            if (node === commentNode) {
-              index = j;
-              break;
-            }
-            j++;
-          }
+      require: '^^ngMessages',
+      link: function(scope, element, attrs, ngMessagesCtrl, $transclude) {
+        var commentNode = element[0];
+
+        var records;
+        var staticExp = attrs.ngMessage || attrs.when;
+        var dynamicExp = attrs.ngMessageExp || attrs.whenExp;
+        var assignRecords = function(items) {
+          records = items
+              ? (isArray(items)
+                    ? items
+                    : items.split(/[\s,]+/))
+              : null;
+          ngMessagesCtrl.reRender();
+        };
+
+        if (dynamicExp) {
+          assignRecords(scope.$eval(dynamicExp));
+          scope.$watchCollection(dynamicExp, assignRecords);
+        } else {
+          assignRecords(staticExp);
         }
 
-        ngMessages.registerMessage(index, {
-          type: $attrs.ngMessage || $attrs.when,
+        var currentElement, messageCtrl;
+        ngMessagesCtrl.register(commentNode, messageCtrl = {
+          test: function(name) {
+            return contains(records, name);
+          },
           attach: function() {
-            if (!element) {
-              $transclude($scope, function(clone) {
-                $animate.enter(clone, null, $element);
-                element = clone;
+            if (!currentElement) {
+              $transclude(scope, function(elm) {
+                $animate.enter(elm, null, element);
+                currentElement = elm;
+
+                // in the event that the parent element is destroyed
+                // by any other structural directive then it's time
+                // to deregister the message from the controller
+                currentElement.on('$destroy', function() {
+                  if (currentElement) {
+                    ngMessagesCtrl.deregister(commentNode);
+                    messageCtrl.detach();
+                  }
+                });
               });
             }
           },
-          detach: function(now) {
-            if (element) {
-              $animate.leave(element);
-              element = null;
+          detach: function() {
+            if (currentElement) {
+              var elm = currentElement;
+              currentElement = null;
+              $animate.leave(elm);
             }
           }
         });
       }
     };
-  }]);
+  }];
+
+  function contains(collection, key) {
+    if (collection) {
+      return isArray(collection)
+          ? collection.indexOf(key) >= 0
+          : collection.hasOwnProperty(key);
+    }
+  }
+}
 
 
 })(window, window.angular);

http://git-wip-us.apache.org/repos/asf/struts/blob/7928d345/archetypes/struts2-archetype-angularjs/src/main/resources/archetype-resources/src/main/webapp/js/lib/angular/angular-messages.min.js
----------------------------------------------------------------------
diff --git a/archetypes/struts2-archetype-angularjs/src/main/resources/archetype-resources/src/main/webapp/js/lib/angular/angular-messages.min.js b/archetypes/struts2-archetype-angularjs/src/main/resources/archetype-resources/src/main/webapp/js/lib/angular/angular-messages.min.js
index 9eeae2a..45b2d2c 100644
--- a/archetypes/struts2-archetype-angularjs/src/main/resources/archetype-resources/src/main/webapp/js/lib/angular/angular-messages.min.js
+++ b/archetypes/struts2-archetype-angularjs/src/main/resources/archetype-resources/src/main/webapp/js/lib/angular/angular-messages.min.js
@@ -1,10 +1,11 @@
 /*
- AngularJS v1.3.15
- (c) 2010-2014 Google, Inc. http://angularjs.org
+ AngularJS v1.4.2
+ (c) 2010-2015 Google, Inc. http://angularjs.org
  License: MIT
 */
-(function(r,f,s){'use strict';f.module("ngMessages",[]).directive("ngMessages",["$compile","$animate","$templateRequest",function(q,k,l){return{restrict:"AE",controller:function(){this.$renderNgMessageClasses=f.noop;var b=[];this.registerMessage=function(d,a){for(var c=0;c<b.length;c++)if(b[c].type==a.type){if(d!=c){var g=b[d];b[d]=b[c];d<b.length?b[c]=g:b.splice(0,c)}return}b.splice(d,0,a)};this.renderMessages=function(d,a){d=d||{};var c;f.forEach(b,function(b){var e;if(e=!c||a)e=d[b.type],e=null!==
-e&&!1!==e&&e;e?(b.attach(),c=!0):b.detach()});this.renderElementClasses(c)}},require:"ngMessages",link:function(b,d,a,c){c.renderElementClasses=function(b){b?k.setClass(d,"ng-active","ng-inactive"):k.setClass(d,"ng-inactive","ng-active")};var g=f.isString(a.ngMessagesMultiple)||f.isString(a.multiple),e;b.$watchCollection(a.ngMessages||a["for"],function(b){e=b;c.renderMessages(b,g)});(a=a.ngMessagesInclude||a.include)&&l(a).then(function(a){var h;a=f.element("<div/>").html(a);f.forEach(a.children(),
-function(a){a=f.element(a);h?h.after(a):d.prepend(a);h=a;q(a)(b)});c.renderMessages(e,g)})}}}]).directive("ngMessage",["$animate",function(f){return{require:"^ngMessages",transclude:"element",terminal:!0,restrict:"AE",link:function(k,l,b,d,a){for(var c,g,e=l[0],n=e.parentNode,h=0,p=0;h<n.childNodes.length;h++){var m=n.childNodes[h];if(8==m.nodeType&&0<=m.nodeValue.indexOf("ngMessage")){if(m===e){c=p;break}p++}}d.registerMessage(c,{type:b.ngMessage||b.when,attach:function(){g||a(k,function(a){f.enter(a,
-null,l);g=a})},detach:function(a){g&&(f.leave(g),g=null)}})}}}])})(window,window.angular);
+(function(A,h,B){'use strict';function v(h){return["$animate",function(q){return{restrict:"AE",transclude:"element",terminal:!0,require:"^^ngMessages",link:function(m,e,a,g,k){var c=e[0],n,l=a.ngMessage||a.when;a=a.ngMessageExp||a.whenExp;var p=function(d){n=d?w(d)?d:d.split(/[\s,]+/):null;g.reRender()};a?(p(m.$eval(a)),m.$watchCollection(a,p)):p(l);var f,r;g.register(c,r={test:function(d){var b=n;d=b?w(b)?0<=b.indexOf(d):b.hasOwnProperty(d):void 0;return d},attach:function(){f||k(m,function(d){q.enter(d,
+null,e);f=d;f.on("$destroy",function(){f&&(g.deregister(c),r.detach())})})},detach:function(){if(f){var d=f;f=null;q.leave(d)}}})}}}]}var w=h.isArray,x=h.forEach,y=h.isString,z=h.element;h.module("ngMessages",[]).directive("ngMessages",["$animate",function(h){function q(e,a){return y(a)&&0===a.length||m(e.$eval(a))}function m(e){return y(e)?e.length:!!e}return{require:"ngMessages",restrict:"AE",controller:["$element","$scope","$attrs",function(e,a,g){function k(a,d){for(var b=d,e=[];b&&b!==a;){var c=
+b.$$ngMessageNode;if(c&&c.length)return l[c];b.childNodes.length&&-1==e.indexOf(b)?(e.push(b),b=b.childNodes[b.childNodes.length-1]):b=b.previousSibling||b.parentNode}}var c=this,n=0,l=this.messages={},p,f;this.render=function(r){r=r||{};p=!1;f=r;for(var d=q(a,g.ngMessagesMultiple)||q(a,g.multiple),b=[],n={},s=c.head,k=!1,l=0;null!=s;){l++;var t=s.message,u=!1;k||x(r,function(b,a){!u&&m(b)&&t.test(a)&&!n[a]&&(u=n[a]=!0,t.attach())});u?k=!d:b.push(t);s=s.next}x(b,function(b){b.detach()});b.length!==
+l?h.setClass(e,"ng-active","ng-inactive"):h.setClass(e,"ng-inactive","ng-active")};a.$watchCollection(g.ngMessages||g["for"],c.render);this.reRender=function(){p||(p=!0,a.$evalAsync(function(){p&&f&&c.render(f)}))};this.register=function(a,d){var b=n.toString();l[b]={message:d};var g=e[0],f=l[b];c.head?(g=k(g,a))?(f.next=g.next,g.next=f):(f.next=c.head,c.head=f):c.head=f;a.$$ngMessageNode=b;n++;c.reRender()};this.deregister=function(a){var d=a.$$ngMessageNode;delete a.$$ngMessageNode;var b=l[d];(a=
+k(e[0],a))?a.next=b.next:c.head=b.next;delete l[d];c.reRender()}}]}}]).directive("ngMessagesInclude",["$templateRequest","$document","$compile",function(h,q,m){return{restrict:"AE",require:"^^ngMessages",link:function(e,a,g){var k=g.ngMessagesInclude||g.src;h(k).then(function(c){m(c)(e,function(c){a.after(c);c=z(q[0].createComment(" ngMessagesInclude: "+k+" "));a.after(c);a.remove()})})}}}]).directive("ngMessage",v("AE")).directive("ngMessageExp",v("A"))})(window,window.angular);
 //# sourceMappingURL=angular-messages.min.js.map

http://git-wip-us.apache.org/repos/asf/struts/blob/7928d345/archetypes/struts2-archetype-angularjs/src/main/resources/archetype-resources/src/main/webapp/js/lib/angular/angular-messages.min.js.map
----------------------------------------------------------------------
diff --git a/archetypes/struts2-archetype-angularjs/src/main/resources/archetype-resources/src/main/webapp/js/lib/angular/angular-messages.min.js.map b/archetypes/struts2-archetype-angularjs/src/main/resources/archetype-resources/src/main/webapp/js/lib/angular/angular-messages.min.js.map
index d821988..754ba3f 100644
--- a/archetypes/struts2-archetype-angularjs/src/main/resources/archetype-resources/src/main/webapp/js/lib/angular/angular-messages.min.js.map
+++ b/archetypes/struts2-archetype-angularjs/src/main/resources/archetype-resources/src/main/webapp/js/lib/angular/angular-messages.min.js.map
@@ -1,8 +1,8 @@
 {
 "version":3,
 "file":"angular-messages.min.js",
-"lineCount":9,
-"mappings":"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",
+"lineCount":10,
+"mappings":"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",
 "sources":["angular-messages.js"],
-"names":["window","angular","undefined","module","directive","$compile","$animate","$templateRequest","restrict","controller","$renderNgMessageClasses","noop","messages","registerMessage","this.registerMessage","index","message","i","length","type","temp","splice","renderMessages","this.renderMessages","values","multiple","found","forEach","value","attach","detach","renderElementClasses","require","link","$scope","element","$attrs","ctrl","ctrl.renderElementClasses","bool","setClass","ACTIVE_CLASS","INACTIVE_CLASS","isString","ngMessagesMultiple","cachedValues","$watchCollection","ngMessages","tpl","ngMessagesInclude","include","then","processTemplate","html","after","container","children","elm","prepend","transclude","terminal","$element","$transclude","commentNode","parentNode","j","childNodes","node","COMMENT_NODE","nodeType","nodeValue","indexOf","ngMessage","when","clone","enter","now","leave"]
+"names":["window","angular","undefined","ngMessageDirectiveFactory","restrict","$animate","transclude","terminal","require","link","scope","element","attrs","ngMessagesCtrl","$transclude","commentNode","records","staticExp","ngMessage","when","dynamicExp","ngMessageExp","whenExp","assignRecords","items","isArray","split","reRender","$eval","$watchCollection","currentElement","messageCtrl","register","test","name","collection","indexOf","hasOwnProperty","attach","elm","enter","on","deregister","detach","leave","forEach","isString","jqLite","module","directive","isAttrTruthy","attr","length","truthy","val","controller","$element","$scope","$attrs","findPreviousMessage","parent","comment","prevNode","parentLookup","prevKey","$$ngMessageNode","messages","childNodes","push","previousSibling","parentNode","ctrl","latestKey","renderLater","cachedCollection","render","this.render","multiple","ngMessagesMultiple","unmatchedMessages","matchedKeys","messageItem","head","messageFound","totalMes
 sages","message","messageUsed","value","key","next","setClass","ACTIVE_CLASS","INACTIVE_CLASS","ngMessages","this.reRender","$evalAsync","this.register","nextKey","toString","messageNode","match","this.deregister","$templateRequest","$document","$compile","src","ngMessagesInclude","then","html","contents","after","anchor","createComment","remove"]
 }

http://git-wip-us.apache.org/repos/asf/struts/blob/7928d345/archetypes/struts2-archetype-angularjs/src/main/resources/archetype-resources/src/main/webapp/js/lib/angular/angular-mocks.js
----------------------------------------------------------------------
diff --git a/archetypes/struts2-archetype-angularjs/src/main/resources/archetype-resources/src/main/webapp/js/lib/angular/angular-mocks.js b/archetypes/struts2-archetype-angularjs/src/main/resources/archetype-resources/src/main/webapp/js/lib/angular/angular-mocks.js
index cbf36ff..a2410c8 100644
--- a/archetypes/struts2-archetype-angularjs/src/main/resources/archetype-resources/src/main/webapp/js/lib/angular/angular-mocks.js
+++ b/archetypes/struts2-archetype-angularjs/src/main/resources/archetype-resources/src/main/webapp/js/lib/angular/angular-mocks.js
@@ -1,6 +1,6 @@
 /**
- * @license AngularJS v1.3.15
- * (c) 2010-2014 Google, Inc. http://angularjs.org
+ * @license AngularJS v1.4.2
+ * (c) 2010-2015 Google, Inc. http://angularjs.org
  * License: MIT
  */
 (function(window, angular, undefined) {
@@ -64,10 +64,9 @@ angular.mock.$Browser = function() {
     return listener;
   };
 
+  self.$$applicationDestroyed = angular.noop;
   self.$$checkUrlChange = angular.noop;
 
-  self.cookieHash = {};
-  self.lastCookieHash = {};
   self.deferredFns = [];
   self.deferredNextId = 0;
 
@@ -147,11 +146,6 @@ angular.mock.$Browser.prototype = {
     });
   },
 
-  addPollFn: function(pollFn) {
-    this.pollFns.push(pollFn);
-    return pollFn;
-  },
-
   url: function(url, replace, state) {
     if (angular.isUndefined(state)) {
       state = null;
@@ -170,25 +164,6 @@ angular.mock.$Browser.prototype = {
     return this.$$state;
   },
 
-  cookies:  function(name, value) {
-    if (name) {
-      if (angular.isUndefined(value)) {
-        delete this.cookieHash[name];
-      } else {
-        if (angular.isString(value) &&       //strings only
-            value.length <= 4096) {          //strict cookie storage limits
-          this.cookieHash[name] = value;
-        }
-      }
-    } else {
-      if (!angular.equals(this.cookieHash, this.lastCookieHash)) {
-        this.lastCookieHash = angular.copy(this.cookieHash);
-        this.cookieHash = angular.copy(this.cookieHash);
-      }
-      return this.cookieHash;
-    }
-  },
-
   notifyWhenNoOutstandingRequests: function(fn) {
     fn();
   }
@@ -458,6 +433,7 @@ angular.mock.$LogProvider = function() {
  *   indefinitely.
  * @param {boolean=} [invokeApply=true] If set to `false` skips model dirty checking, otherwise
  *   will invoke `fn` within the {@link ng.$rootScope.Scope#$apply $apply} block.
+ * @param {...*=} Pass additional parameters to the executed function.
  * @returns {promise} A promise which will be notified on each iteration.
  */
 angular.mock.$IntervalProvider = function() {
@@ -468,13 +444,17 @@ angular.mock.$IntervalProvider = function() {
         now = 0;
 
     var $interval = function(fn, delay, count, invokeApply) {
-      var iteration = 0,
+      var hasParams = arguments.length > 4,
+          args = hasParams ? Array.prototype.slice.call(arguments, 4) : [],
+          iteration = 0,
           skipApply = (angular.isDefined(invokeApply) && !invokeApply),
           deferred = (skipApply ? $$q : $q).defer(),
           promise = deferred.promise;
 
       count = (angular.isDefined(count)) ? count : 0;
-      promise.then(null, null, fn);
+      promise.then(null, null, (!hasParams) ? fn : function() {
+        fn.apply(null, args);
+      });
 
       promise.$$intervalId = nextRepeatId;
 
@@ -581,20 +561,20 @@ function jsonStringToDate(string) {
         tzHour = 0,
         tzMin  = 0;
     if (match[9]) {
-      tzHour = int(match[9] + match[10]);
-      tzMin = int(match[9] + match[11]);
+      tzHour = toInt(match[9] + match[10]);
+      tzMin = toInt(match[9] + match[11]);
     }
-    date.setUTCFullYear(int(match[1]), int(match[2]) - 1, int(match[3]));
-    date.setUTCHours(int(match[4] || 0) - tzHour,
-                     int(match[5] || 0) - tzMin,
-                     int(match[6] || 0),
-                     int(match[7] || 0));
+    date.setUTCFullYear(toInt(match[1]), toInt(match[2]) - 1, toInt(match[3]));
+    date.setUTCHours(toInt(match[4] || 0) - tzHour,
+                     toInt(match[5] || 0) - tzMin,
+                     toInt(match[6] || 0),
+                     toInt(match[7] || 0));
     return date;
   }
   return string;
 }
 
-function int(str) {
+function toInt(str) {
   return parseInt(str, 10);
 }
 
@@ -606,8 +586,9 @@ function padNumber(num, digits, trim) {
   }
   num = '' + num;
   while (num.length < digits) num = '0' + num;
-  if (trim)
+  if (trim) {
     num = num.substr(num.length - digits);
+  }
   return neg + num;
 }
 
@@ -657,11 +638,12 @@ angular.mock.TzDate = function(offset, timestamp) {
     self.origDate = jsonStringToDate(timestamp);
 
     timestamp = self.origDate.getTime();
-    if (isNaN(timestamp))
+    if (isNaN(timestamp)) {
       throw {
         name: "Illegal Argument",
         message: "Arg '" + tsStr + "' passed into TzDate constructor is not a valid date string"
       };
+    }
   } else {
     self.origDate = new Date(timestamp);
   }
@@ -789,13 +771,14 @@ angular.mock.animate = angular.module('ngAnimateMock', ['ng'])
       };
     });
 
-    $provide.decorator('$animate', ['$delegate', '$$asyncCallback', '$timeout', '$browser',
-                            function($delegate,   $$asyncCallback,   $timeout,   $browser) {
+    $provide.decorator('$animate', ['$delegate', '$$asyncCallback', '$timeout', '$browser', '$$rAF',
+                            function($delegate,   $$asyncCallback,   $timeout,   $browser,   $$rAF) {
       var animate = {
         queue: [],
         cancel: $delegate.cancel,
         enabled: $delegate.enabled,
         triggerCallbackEvents: function() {
+          $$rAF.flush();
           $$asyncCallback.flush();
         },
         triggerCallbackPromise: function() {
@@ -1107,7 +1090,7 @@ angular.mock.dump = function(object) {
          $httpBackend.flush();
 
          $httpBackend.expectPOST('/add-msg.py', undefined, function(headers) {
-           // check if the header was send, if it wasn't the expectation won't
+           // check if the header was sent, if it wasn't the expectation won't
            // match the request and the test will fail
            return headers['Authorization'] == 'xxx';
          }).respond(201, '');
@@ -1191,14 +1174,16 @@ function createHttpBackendMock($rootScope, $timeout, $delegate, $browser) {
     }
 
     if (expectation && expectation.match(method, url)) {
-      if (!expectation.matchData(data))
+      if (!expectation.matchData(data)) {
         throw new Error('Expected ' + expectation + ' with different data\n' +
             'EXPECTED: ' + prettyPrint(expectation.data) + '\nGOT:      ' + data);
+      }
 
-      if (!expectation.matchHeaders(headers))
+      if (!expectation.matchHeaders(headers)) {
         throw new Error('Expected ' + expectation + ' with different headers\n' +
                         'EXPECTED: ' + prettyPrint(expectation.headers) + '\nGOT:      ' +
                         prettyPrint(headers));
+      }
 
       expectations.shift();
 
@@ -1234,8 +1219,8 @@ function createHttpBackendMock($rootScope, $timeout, $delegate, $browser) {
    * Creates a new backend definition.
    *
    * @param {string} method HTTP method.
-   * @param {string|RegExp|function(string)} url HTTP url or function that receives the url
-   *   and returns true if the url match the current definition.
+   * @param {string|RegExp|function(string)} url HTTP url or function that receives a url
+   *   and returns true if the url matches the current definition.
    * @param {(string|RegExp|function(string))=} data HTTP request body or function that receives
    *   data string and returns true if the data is as expected.
    * @param {(Object|function(Object))=} headers HTTP headers or function that receives http header
@@ -1280,8 +1265,8 @@ function createHttpBackendMock($rootScope, $timeout, $delegate, $browser) {
    * @description
    * Creates a new backend definition for GET requests. For more info see `when()`.
    *
-   * @param {string|RegExp|function(string)} url HTTP url or function that receives the url
-   *   and returns true if the url match the current definition.
+   * @param {string|RegExp|function(string)} url HTTP url or function that receives a url
+   *   and returns true if the url matches the current definition.
    * @param {(Object|function(Object))=} headers HTTP headers.
    * @returns {requestHandler} Returns an object with `respond` method that controls how a matched
    * request is handled. You can save this object for later use and invoke `respond` again in
@@ -1294,8 +1279,8 @@ function createHttpBackendMock($rootScope, $timeout, $delegate, $browser) {
    * @description
    * Creates a new backend definition for HEAD requests. For more info see `when()`.
    *
-   * @param {string|RegExp|function(string)} url HTTP url or function that receives the url
-   *   and returns true if the url match the current definition.
+   * @param {string|RegExp|function(string)} url HTTP url or function that receives a url
+   *   and returns true if the url matches the current definition.
    * @param {(Object|function(Object))=} headers HTTP headers.
    * @returns {requestHandler} Returns an object with `respond` method that controls how a matched
    * request is handled. You can save this object for later use and invoke `respond` again in
@@ -1308,8 +1293,8 @@ function createHttpBackendMock($rootScope, $timeout, $delegate, $browser) {
    * @description
    * Creates a new backend definition for DELETE requests. For more info see `when()`.
    *
-   * @param {string|RegExp|function(string)} url HTTP url or function that receives the url
-   *   and returns true if the url match the current definition.
+   * @param {string|RegExp|function(string)} url HTTP url or function that receives a url
+   *   and returns true if the url matches the current definition.
    * @param {(Object|function(Object))=} headers HTTP headers.
    * @returns {requestHandler} Returns an object with `respond` method that controls how a matched
    * request is handled. You can save this object for later use and invoke `respond` again in
@@ -1322,8 +1307,8 @@ function createHttpBackendMock($rootScope, $timeout, $delegate, $browser) {
    * @description
    * Creates a new backend definition for POST requests. For more info see `when()`.
    *
-   * @param {string|RegExp|function(string)} url HTTP url or function that receives the url
-   *   and returns true if the url match the current definition.
+   * @param {string|RegExp|function(string)} url HTTP url or function that receives a url
+   *   and returns true if the url matches the current definition.
    * @param {(string|RegExp|function(string))=} data HTTP request body or function that receives
    *   data string and returns true if the data is as expected.
    * @param {(Object|function(Object))=} headers HTTP headers.
@@ -1338,8 +1323,8 @@ function createHttpBackendMock($rootScope, $timeout, $delegate, $browser) {
    * @description
    * Creates a new backend definition for PUT requests.  For more info see `when()`.
    *
-   * @param {string|RegExp|function(string)} url HTTP url or function that receives the url
-   *   and returns true if the url match the current definition.
+   * @param {string|RegExp|function(string)} url HTTP url or function that receives a url
+   *   and returns true if the url matches the current definition.
    * @param {(string|RegExp|function(string))=} data HTTP request body or function that receives
    *   data string and returns true if the data is as expected.
    * @param {(Object|function(Object))=} headers HTTP headers.
@@ -1354,8 +1339,8 @@ function createHttpBackendMock($rootScope, $timeout, $delegate, $browser) {
    * @description
    * Creates a new backend definition for JSONP requests. For more info see `when()`.
    *
-   * @param {string|RegExp|function(string)} url HTTP url or function that receives the url
-   *   and returns true if the url match the current definition.
+   * @param {string|RegExp|function(string)} url HTTP url or function that receives a url
+   *   and returns true if the url matches the current definition.
    * @returns {requestHandler} Returns an object with `respond` method that controls how a matched
    * request is handled. You can save this object for later use and invoke `respond` again in
    * order to change how a matched request is handled.
@@ -1370,8 +1355,8 @@ function createHttpBackendMock($rootScope, $timeout, $delegate, $browser) {
    * Creates a new request expectation.
    *
    * @param {string} method HTTP method.
-   * @param {string|RegExp|function(string)} url HTTP url or function that receives the url
-   *   and returns true if the url match the current definition.
+   * @param {string|RegExp|function(string)} url HTTP url or function that receives a url
+   *   and returns true if the url matches the current definition.
    * @param {(string|RegExp|function(string)|Object)=} data HTTP request body or function that
    *  receives data string and returns true if the data is as expected, or Object if request body
    *  is in JSON format.
@@ -1409,8 +1394,8 @@ function createHttpBackendMock($rootScope, $timeout, $delegate, $browser) {
    * @description
    * Creates a new request expectation for GET requests. For more info see `expect()`.
    *
-   * @param {string|RegExp|function(string)} url HTTP url or function that receives the url
-   *   and returns true if the url match the current definition.
+   * @param {string|RegExp|function(string)} url HTTP url or function that receives a url
+   *   and returns true if the url matches the current definition.
    * @param {Object=} headers HTTP headers.
    * @returns {requestHandler} Returns an object with `respond` method that controls how a matched
    * request is handled. You can save this object for later use and invoke `respond` again in
@@ -1423,8 +1408,8 @@ function createHttpBackendMock($rootScope, $timeout, $delegate, $browser) {
    * @description
    * Creates a new request expectation for HEAD requests. For more info see `expect()`.
    *
-   * @param {string|RegExp|function(string)} url HTTP url or function that receives the url
-   *   and returns true if the url match the current definition.
+   * @param {string|RegExp|function(string)} url HTTP url or function that receives a url
+   *   and returns true if the url matches the current definition.
    * @param {Object=} headers HTTP headers.
    * @returns {requestHandler} Returns an object with `respond` method that controls how a matched
    *   request is handled. You can save this object for later use and invoke `respond` again in
@@ -1437,8 +1422,8 @@ function createHttpBackendMock($rootScope, $timeout, $delegate, $browser) {
    * @description
    * Creates a new request expectation for DELETE requests. For more info see `expect()`.
    *
-   * @param {string|RegExp|function(string)} url HTTP url or function that receives the url
-   *   and returns true if the url match the current definition.
+   * @param {string|RegExp|function(string)} url HTTP url or function that receives a url
+   *   and returns true if the url matches the current definition.
    * @param {Object=} headers HTTP headers.
    * @returns {requestHandler} Returns an object with `respond` method that controls how a matched
    *   request is handled. You can save this object for later use and invoke `respond` again in
@@ -1451,8 +1436,8 @@ function createHttpBackendMock($rootScope, $timeout, $delegate, $browser) {
    * @description
    * Creates a new request expectation for POST requests. For more info see `expect()`.
    *
-   * @param {string|RegExp|function(string)} url HTTP url or function that receives the url
-   *   and returns true if the url match the current definition.
+   * @param {string|RegExp|function(string)} url HTTP url or function that receives a url
+   *   and returns true if the url matches the current definition.
    * @param {(string|RegExp|function(string)|Object)=} data HTTP request body or function that
    *  receives data string and returns true if the data is as expected, or Object if request body
    *  is in JSON format.
@@ -1468,8 +1453,8 @@ function createHttpBackendMock($rootScope, $timeout, $delegate, $browser) {
    * @description
    * Creates a new request expectation for PUT requests. For more info see `expect()`.
    *
-   * @param {string|RegExp|function(string)} url HTTP url or function that receives the url
-   *   and returns true if the url match the current definition.
+   * @param {string|RegExp|function(string)} url HTTP url or function that receives a url
+   *   and returns true if the url matches the current definition.
    * @param {(string|RegExp|function(string)|Object)=} data HTTP request body or function that
    *  receives data string and returns true if the data is as expected, or Object if request body
    *  is in JSON format.
@@ -1485,8 +1470,8 @@ function createHttpBackendMock($rootScope, $timeout, $delegate, $browser) {
    * @description
    * Creates a new request expectation for PATCH requests. For more info see `expect()`.
    *
-   * @param {string|RegExp|function(string)} url HTTP url or function that receives the url
-   *   and returns true if the url match the current definition.
+   * @param {string|RegExp|function(string)} url HTTP url or function that receives a url
+   *   and returns true if the url matches the current definition.
    * @param {(string|RegExp|function(string)|Object)=} data HTTP request body or function that
    *  receives data string and returns true if the data is as expected, or Object if request body
    *  is in JSON format.
@@ -1502,8 +1487,8 @@ function createHttpBackendMock($rootScope, $timeout, $delegate, $browser) {
    * @description
    * Creates a new request expectation for JSONP requests. For more info see `expect()`.
    *
-   * @param {string|RegExp|function(string)} url HTTP url or function that receives the url
-   *   and returns true if the url match the current definition.
+   * @param {string|RegExp|function(string)} url HTTP url or function that receives an url
+   *   and returns true if the url matches the current definition.
    * @returns {requestHandler} Returns an object with `respond` method that controls how a matched
    *   request is handled. You can save this object for later use and invoke `respond` again in
    *   order to change how a matched request is handled.
@@ -1780,7 +1765,7 @@ angular.mock.$RAFDecorator = ['$delegate', function($delegate) {
       queue[i]();
     }
 
-    queue = [];
+    queue = queue.slice(i);
   };
 
   return rafFn;
@@ -1985,8 +1970,8 @@ angular.module('ngMockE2E', ['ng']).config(['$provide', function($provide) {
  * Creates a new backend definition.
  *
  * @param {string} method HTTP method.
- * @param {string|RegExp|function(string)} url HTTP url or function that receives the url
- *   and returns true if the url match the current definition.
+ * @param {string|RegExp|function(string)} url HTTP url or function that receives a url
+ *   and returns true if the url matches the current definition.
  * @param {(string|RegExp)=} data HTTP request body.
  * @param {(Object|function(Object))=} headers HTTP headers or function that receives http header
  *   object and returns true if the headers match the current definition.
@@ -2013,8 +1998,8 @@ angular.module('ngMockE2E', ['ng']).config(['$provide', function($provide) {
  * @description
  * Creates a new backend definition for GET requests. For more info see `when()`.
  *
- * @param {string|RegExp|function(string)} url HTTP url or function that receives the url
- *   and returns true if the url match the current definition.
+ * @param {string|RegExp|function(string)} url HTTP url or function that receives a url
+ *   and returns true if the url matches the current definition.
  * @param {(Object|function(Object))=} headers HTTP headers.
  * @returns {requestHandler} Returns an object with `respond` and `passThrough` methods that
  *   control how a matched request is handled. You can save this object for later use and invoke
@@ -2028,8 +2013,8 @@ angular.module('ngMockE2E', ['ng']).config(['$provide', function($provide) {
  * @description
  * Creates a new backend definition for HEAD requests. For more info see `when()`.
  *
- * @param {string|RegExp|function(string)} url HTTP url or function that receives the url
- *   and returns true if the url match the current definition.
+ * @param {string|RegExp|function(string)} url HTTP url or function that receives a url
+ *   and returns true if the url matches the current definition.
  * @param {(Object|function(Object))=} headers HTTP headers.
  * @returns {requestHandler} Returns an object with `respond` and `passThrough` methods that
  *   control how a matched request is handled. You can save this object for later use and invoke
@@ -2043,8 +2028,8 @@ angular.module('ngMockE2E', ['ng']).config(['$provide', function($provide) {
  * @description
  * Creates a new backend definition for DELETE requests. For more info see `when()`.
  *
- * @param {string|RegExp|function(string)} url HTTP url or function that receives the url
- *   and returns true if the url match the current definition.
+ * @param {string|RegExp|function(string)} url HTTP url or function that receives a url
+ *   and returns true if the url matches the current definition.
  * @param {(Object|function(Object))=} headers HTTP headers.
  * @returns {requestHandler} Returns an object with `respond` and `passThrough` methods that
  *   control how a matched request is handled. You can save this object for later use and invoke
@@ -2058,8 +2043,8 @@ angular.module('ngMockE2E', ['ng']).config(['$provide', function($provide) {
  * @description
  * Creates a new backend definition for POST requests. For more info see `when()`.
  *
- * @param {string|RegExp|function(string)} url HTTP url or function that receives the url
- *   and returns true if the url match the current definition.
+ * @param {string|RegExp|function(string)} url HTTP url or function that receives a url
+ *   and returns true if the url matches the current definition.
  * @param {(string|RegExp)=} data HTTP request body.
  * @param {(Object|function(Object))=} headers HTTP headers.
  * @returns {requestHandler} Returns an object with `respond` and `passThrough` methods that
@@ -2074,8 +2059,8 @@ angular.module('ngMockE2E', ['ng']).config(['$provide', function($provide) {
  * @description
  * Creates a new backend definition for PUT requests.  For more info see `when()`.
  *
- * @param {string|RegExp|function(string)} url HTTP url or function that receives the url
- *   and returns true if the url match the current definition.
+ * @param {string|RegExp|function(string)} url HTTP url or function that receives a url
+ *   and returns true if the url matches the current definition.
  * @param {(string|RegExp)=} data HTTP request body.
  * @param {(Object|function(Object))=} headers HTTP headers.
  * @returns {requestHandler} Returns an object with `respond` and `passThrough` methods that
@@ -2090,8 +2075,8 @@ angular.module('ngMockE2E', ['ng']).config(['$provide', function($provide) {
  * @description
  * Creates a new backend definition for PATCH requests.  For more info see `when()`.
  *
- * @param {string|RegExp|function(string)} url HTTP url or function that receives the url
- *   and returns true if the url match the current definition.
+ * @param {string|RegExp|function(string)} url HTTP url or function that receives a url
+ *   and returns true if the url matches the current definition.
  * @param {(string|RegExp)=} data HTTP request body.
  * @param {(Object|function(Object))=} headers HTTP headers.
  * @returns {requestHandler} Returns an object with `respond` and `passThrough` methods that
@@ -2106,8 +2091,8 @@ angular.module('ngMockE2E', ['ng']).config(['$provide', function($provide) {
  * @description
  * Creates a new backend definition for JSONP requests. For more info see `when()`.
  *
- * @param {string|RegExp|function(string)} url HTTP url or function that receives the url
- *   and returns true if the url match the current definition.
+ * @param {string|RegExp|function(string)} url HTTP url or function that receives a url
+ *   and returns true if the url matches the current definition.
  * @returns {requestHandler} Returns an object with `respond` and `passThrough` methods that
  *   control how a matched request is handled. You can save this object for later use and invoke
  *   `respond` or `passThrough` again in order to change how a matched request is handled.
@@ -2244,7 +2229,6 @@ if (window.jasmine || window.mocha) {
 
     if (injector) {
       injector.get('$rootElement').off();
-      injector.get('$browser').pollFns.length = 0;
     }
 
     // clean up jquery's fragment cache

http://git-wip-us.apache.org/repos/asf/struts/blob/7928d345/archetypes/struts2-archetype-angularjs/src/main/resources/archetype-resources/src/main/webapp/js/lib/angular/angular-resource.js
----------------------------------------------------------------------
diff --git a/archetypes/struts2-archetype-angularjs/src/main/resources/archetype-resources/src/main/webapp/js/lib/angular/angular-resource.js b/archetypes/struts2-archetype-angularjs/src/main/resources/archetype-resources/src/main/webapp/js/lib/angular/angular-resource.js
index 19900d3..5d0613f 100644
--- a/archetypes/struts2-archetype-angularjs/src/main/resources/archetype-resources/src/main/webapp/js/lib/angular/angular-resource.js
+++ b/archetypes/struts2-archetype-angularjs/src/main/resources/archetype-resources/src/main/webapp/js/lib/angular/angular-resource.js
@@ -1,6 +1,6 @@
 /**
- * @license AngularJS v1.3.15
- * (c) 2010-2014 Google, Inc. http://angularjs.org
+ * @license AngularJS v1.4.2
+ * (c) 2010-2015 Google, Inc. http://angularjs.org
  * License: MIT
  */
 (function(window, angular, undefined) {'use strict';
@@ -10,7 +10,7 @@ var $resourceMinErr = angular.$$minErr('$resource');
 // Helper functions and regex to lookup a dotted path on an object
 // stopping at undefined/null.  The path must be composed of ASCII
 // identifiers (just like $parse)
-var MEMBER_NAME_REGEX = /^(\.[a-zA-Z_$][0-9a-zA-Z_$]*)+$/;
+var MEMBER_NAME_REGEX = /^(\.[a-zA-Z_$@][0-9a-zA-Z_$@]*)+$/;
 
 function isValidDottedPath(path) {
   return (path != null && path !== '' && path !== 'hasOwnProperty' &&
@@ -90,7 +90,7 @@ function shallowClearAndCopy(src, dst) {
      }]);
  * ```
  *
- * @param {string} url A parametrized URL template with parameters prefixed by `:` as in
+ * @param {string} url A parameterized URL template with parameters prefixed by `:` as in
  *   `/user/:username`. If you are using a URL with a port number (e.g.
  *   `http://example.com:8080/api`), it will be respected.
  *
@@ -214,7 +214,8 @@ function shallowClearAndCopy(src, dst) {
  *   - non-GET instance actions:  `instance.$action([parameters], [success], [error])`
  *
  *
- *   Success callback is called with (value, responseHeaders) arguments. Error callback is called
+ *   Success callback is called with (value, responseHeaders) arguments, where the value is
+ *   the populated resource instance or collection object. The error callback is called
  *   with (httpResponse) argument.
  *
  *   Class actions return empty instance (with additional properties below).
@@ -586,8 +587,8 @@ angular.module('ngResource', ['ng']).
                 if (angular.isArray(data) !== (!!action.isArray)) {
                   throw $resourceMinErr('badcfg',
                       'Error in resource configuration for action `{0}`. Expected response to ' +
-                      'contain an {1} but got an {2}', name, action.isArray ? 'array' : 'object',
-                    angular.isArray(data) ? 'array' : 'object');
+                      'contain an {1} but got an {2} (Request: {3} {4})', name, action.isArray ? 'array' : 'object',
+                    angular.isArray(data) ? 'array' : 'object', httpConfig.method, httpConfig.url);
                 }
                 // jshint +W018
                 if (action.isArray) {

http://git-wip-us.apache.org/repos/asf/struts/blob/7928d345/archetypes/struts2-archetype-angularjs/src/main/resources/archetype-resources/src/main/webapp/js/lib/angular/angular-resource.min.js
----------------------------------------------------------------------
diff --git a/archetypes/struts2-archetype-angularjs/src/main/resources/archetype-resources/src/main/webapp/js/lib/angular/angular-resource.min.js b/archetypes/struts2-archetype-angularjs/src/main/resources/archetype-resources/src/main/webapp/js/lib/angular/angular-resource.min.js
index 02ec4ee..31e6e63 100644
--- a/archetypes/struts2-archetype-angularjs/src/main/resources/archetype-resources/src/main/webapp/js/lib/angular/angular-resource.min.js
+++ b/archetypes/struts2-archetype-angularjs/src/main/resources/archetype-resources/src/main/webapp/js/lib/angular/angular-resource.min.js
@@ -1,13 +1,13 @@
 /*
- AngularJS v1.3.15
- (c) 2010-2014 Google, Inc. http://angularjs.org
+ AngularJS v1.4.2
+ (c) 2010-2015 Google, Inc. http://angularjs.org
  License: MIT
 */
-(function(I,d,B){'use strict';function D(f,q){q=q||{};d.forEach(q,function(d,h){delete q[h]});for(var h in f)!f.hasOwnProperty(h)||"$"===h.charAt(0)&&"$"===h.charAt(1)||(q[h]=f[h]);return q}var w=d.$$minErr("$resource"),C=/^(\.[a-zA-Z_$][0-9a-zA-Z_$]*)+$/;d.module("ngResource",["ng"]).provider("$resource",function(){var f=this;this.defaults={stripTrailingSlashes:!0,actions:{get:{method:"GET"},save:{method:"POST"},query:{method:"GET",isArray:!0},remove:{method:"DELETE"},"delete":{method:"DELETE"}}};
-this.$get=["$http","$q",function(q,h){function t(d,g){this.template=d;this.defaults=s({},f.defaults,g);this.urlParams={}}function v(x,g,l,m){function c(b,k){var c={};k=s({},g,k);r(k,function(a,k){u(a)&&(a=a());var d;if(a&&a.charAt&&"@"==a.charAt(0)){d=b;var e=a.substr(1);if(null==e||""===e||"hasOwnProperty"===e||!C.test("."+e))throw w("badmember",e);for(var e=e.split("."),n=0,g=e.length;n<g&&d!==B;n++){var h=e[n];d=null!==d?d[h]:B}}else d=a;c[k]=d});return c}function F(b){return b.resource}function e(b){D(b||
-{},this)}var G=new t(x,m);l=s({},f.defaults.actions,l);e.prototype.toJSON=function(){var b=s({},this);delete b.$promise;delete b.$resolved;return b};r(l,function(b,k){var g=/^(POST|PUT|PATCH)$/i.test(b.method);e[k]=function(a,y,m,x){var n={},f,l,z;switch(arguments.length){case 4:z=x,l=m;case 3:case 2:if(u(y)){if(u(a)){l=a;z=y;break}l=y;z=m}else{n=a;f=y;l=m;break}case 1:u(a)?l=a:g?f=a:n=a;break;case 0:break;default:throw w("badargs",arguments.length);}var t=this instanceof e,p=t?f:b.isArray?[]:new e(f),
-A={},v=b.interceptor&&b.interceptor.response||F,C=b.interceptor&&b.interceptor.responseError||B;r(b,function(b,a){"params"!=a&&"isArray"!=a&&"interceptor"!=a&&(A[a]=H(b))});g&&(A.data=f);G.setUrlParams(A,s({},c(f,b.params||{}),n),b.url);n=q(A).then(function(a){var c=a.data,g=p.$promise;if(c){if(d.isArray(c)!==!!b.isArray)throw w("badcfg",k,b.isArray?"array":"object",d.isArray(c)?"array":"object");b.isArray?(p.length=0,r(c,function(a){"object"===typeof a?p.push(new e(a)):p.push(a)})):(D(c,p),p.$promise=
-g)}p.$resolved=!0;a.resource=p;return a},function(a){p.$resolved=!0;(z||E)(a);return h.reject(a)});n=n.then(function(a){var b=v(a);(l||E)(b,a.headers);return b},C);return t?n:(p.$promise=n,p.$resolved=!1,p)};e.prototype["$"+k]=function(a,b,c){u(a)&&(c=b,b=a,a={});a=e[k].call(this,a,this,b,c);return a.$promise||a}});e.bind=function(b){return v(x,s({},g,b),l)};return e}var E=d.noop,r=d.forEach,s=d.extend,H=d.copy,u=d.isFunction;t.prototype={setUrlParams:function(f,g,l){var m=this,c=l||m.template,h,
-e,q=m.urlParams={};r(c.split(/\W/),function(b){if("hasOwnProperty"===b)throw w("badname");!/^\d+$/.test(b)&&b&&(new RegExp("(^|[^\\\\]):"+b+"(\\W|$)")).test(c)&&(q[b]=!0)});c=c.replace(/\\:/g,":");g=g||{};r(m.urlParams,function(b,k){h=g.hasOwnProperty(k)?g[k]:m.defaults[k];d.isDefined(h)&&null!==h?(e=encodeURIComponent(h).replace(/%40/gi,"@").replace(/%3A/gi,":").replace(/%24/g,"$").replace(/%2C/gi,",").replace(/%20/g,"%20").replace(/%26/gi,"&").replace(/%3D/gi,"=").replace(/%2B/gi,"+"),c=c.replace(new RegExp(":"+
-k+"(\\W|$)","g"),function(b,a){return e+a})):c=c.replace(new RegExp("(/?):"+k+"(\\W|$)","g"),function(b,a,c){return"/"==c.charAt(0)?c:a+c})});m.defaults.stripTrailingSlashes&&(c=c.replace(/\/+$/,"")||"/");c=c.replace(/\/\.(?=\w+($|\?))/,".");f.url=c.replace(/\/\\\./,"/.");r(g,function(b,c){m.urlParams[c]||(f.params=f.params||{},f.params[c]=b)})}};return v}]})})(window,window.angular);
+(function(I,d,B){'use strict';function D(f,q){q=q||{};d.forEach(q,function(d,h){delete q[h]});for(var h in f)!f.hasOwnProperty(h)||"$"===h.charAt(0)&&"$"===h.charAt(1)||(q[h]=f[h]);return q}var x=d.$$minErr("$resource"),C=/^(\.[a-zA-Z_$@][0-9a-zA-Z_$@]*)+$/;d.module("ngResource",["ng"]).provider("$resource",function(){var f=this;this.defaults={stripTrailingSlashes:!0,actions:{get:{method:"GET"},save:{method:"POST"},query:{method:"GET",isArray:!0},remove:{method:"DELETE"},"delete":{method:"DELETE"}}};
+this.$get=["$http","$q",function(q,h){function u(d,g){this.template=d;this.defaults=s({},f.defaults,g);this.urlParams={}}function w(y,g,l,m){function c(b,k){var c={};k=s({},g,k);r(k,function(a,k){v(a)&&(a=a());var d;if(a&&a.charAt&&"@"==a.charAt(0)){d=b;var e=a.substr(1);if(null==e||""===e||"hasOwnProperty"===e||!C.test("."+e))throw x("badmember",e);for(var e=e.split("."),n=0,g=e.length;n<g&&d!==B;n++){var h=e[n];d=null!==d?d[h]:B}}else d=a;c[k]=d});return c}function F(b){return b.resource}function e(b){D(b||
+{},this)}var G=new u(y,m);l=s({},f.defaults.actions,l);e.prototype.toJSON=function(){var b=s({},this);delete b.$promise;delete b.$resolved;return b};r(l,function(b,k){var g=/^(POST|PUT|PATCH)$/i.test(b.method);e[k]=function(a,z,m,y){var n={},f,l,A;switch(arguments.length){case 4:A=y,l=m;case 3:case 2:if(v(z)){if(v(a)){l=a;A=z;break}l=z;A=m}else{n=a;f=z;l=m;break}case 1:v(a)?l=a:g?f=a:n=a;break;case 0:break;default:throw x("badargs",arguments.length);}var u=this instanceof e,p=u?f:b.isArray?[]:new e(f),
+t={},w=b.interceptor&&b.interceptor.response||F,C=b.interceptor&&b.interceptor.responseError||B;r(b,function(b,a){"params"!=a&&"isArray"!=a&&"interceptor"!=a&&(t[a]=H(b))});g&&(t.data=f);G.setUrlParams(t,s({},c(f,b.params||{}),n),b.url);n=q(t).then(function(a){var c=a.data,g=p.$promise;if(c){if(d.isArray(c)!==!!b.isArray)throw x("badcfg",k,b.isArray?"array":"object",d.isArray(c)?"array":"object",t.method,t.url);b.isArray?(p.length=0,r(c,function(a){"object"===typeof a?p.push(new e(a)):p.push(a)})):
+(D(c,p),p.$promise=g)}p.$resolved=!0;a.resource=p;return a},function(a){p.$resolved=!0;(A||E)(a);return h.reject(a)});n=n.then(function(a){var b=w(a);(l||E)(b,a.headers);return b},C);return u?n:(p.$promise=n,p.$resolved=!1,p)};e.prototype["$"+k]=function(a,b,c){v(a)&&(c=b,b=a,a={});a=e[k].call(this,a,this,b,c);return a.$promise||a}});e.bind=function(b){return w(y,s({},g,b),l)};return e}var E=d.noop,r=d.forEach,s=d.extend,H=d.copy,v=d.isFunction;u.prototype={setUrlParams:function(f,g,l){var m=this,
+c=l||m.template,h,e,q=m.urlParams={};r(c.split(/\W/),function(b){if("hasOwnProperty"===b)throw x("badname");!/^\d+$/.test(b)&&b&&(new RegExp("(^|[^\\\\]):"+b+"(\\W|$)")).test(c)&&(q[b]=!0)});c=c.replace(/\\:/g,":");g=g||{};r(m.urlParams,function(b,k){h=g.hasOwnProperty(k)?g[k]:m.defaults[k];d.isDefined(h)&&null!==h?(e=encodeURIComponent(h).replace(/%40/gi,"@").replace(/%3A/gi,":").replace(/%24/g,"$").replace(/%2C/gi,",").replace(/%20/g,"%20").replace(/%26/gi,"&").replace(/%3D/gi,"=").replace(/%2B/gi,
+"+"),c=c.replace(new RegExp(":"+k+"(\\W|$)","g"),function(b,a){return e+a})):c=c.replace(new RegExp("(/?):"+k+"(\\W|$)","g"),function(b,a,c){return"/"==c.charAt(0)?c:a+c})});m.defaults.stripTrailingSlashes&&(c=c.replace(/\/+$/,"")||"/");c=c.replace(/\/\.(?=\w+($|\?))/,".");f.url=c.replace(/\/\\\./,"/.");r(g,function(b,c){m.urlParams[c]||(f.params=f.params||{},f.params[c]=b)})}};return w}]})})(window,window.angular);
 //# sourceMappingURL=angular-resource.min.js.map