diff --git a/bower.json b/bower.json
index cebdd6f74..8d1ad713f 100644
--- a/bower.json
+++ b/bower.json
@@ -1,8 +1,8 @@
{
"name": "madison",
"dependencies": {
- "jquery-zclip": "~1.1.4",
- "zeroclipboard": "~2.0.1"
+ "zeroclipboard": "~2.0.1",
+ "angular-growl": "~0.4.0"
},
"devDependencies": {
"pagedown": "~1.1.0",
diff --git a/public/bower_components/angular-growl/.bower.json b/public/bower_components/angular-growl/.bower.json
new file mode 100644
index 000000000..22421fa14
--- /dev/null
+++ b/public/bower_components/angular-growl/.bower.json
@@ -0,0 +1,40 @@
+{
+ "author": "Marco Rinck",
+ "name": "angular-growl",
+ "description": "growl like notifications for angularJS projects, using bootstrap alert classes",
+ "version": "0.4.0",
+ "homepage": "https://github.com/marcorinck/angular-growl",
+ "repository": {
+ "type": "git",
+ "url": "https://github.com/marcorinck/angular-growl"
+ },
+ "license": "MIT",
+ "main": "./build/angular-growl.js",
+ "ignore": [
+ "src",
+ "test",
+ ".jshintrc",
+ "package.json",
+ "gruntfile.js",
+ "karma.conf.js",
+ "bower.json",
+ "demo",
+ ".gitignore"
+ ],
+ "dependencies": {
+ "angular": "1.2.1"
+ },
+ "devDependencies": {
+ "angular-mocks": "1.2.1"
+ },
+ "_release": "0.4.0",
+ "_resolution": {
+ "type": "version",
+ "tag": "v0.4.0",
+ "commit": "e0e8b2cda660f28c75eefa7c9703952368547cdd"
+ },
+ "_source": "git://github.com/marcorinck/angular-growl.git",
+ "_target": "~0.4.0",
+ "_originalSource": "angular-growl",
+ "_direct": true
+}
\ No newline at end of file
diff --git a/public/bower_components/angular-growl/LICENSE b/public/bower_components/angular-growl/LICENSE
new file mode 100644
index 000000000..3db10d478
--- /dev/null
+++ b/public/bower_components/angular-growl/LICENSE
@@ -0,0 +1,21 @@
+The MIT License (MIT)
+
+Copyright (c) 2013 Marco Rinck
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
\ No newline at end of file
diff --git a/public/bower_components/angular-growl/README.md b/public/bower_components/angular-growl/README.md
new file mode 100644
index 000000000..095036a6a
--- /dev/null
+++ b/public/bower_components/angular-growl/README.md
@@ -0,0 +1,275 @@
+#angular-growl
+
+> growl like notifications for angularJS projects, using bootstrap alert classes
+
+##Features
+
+![Standard bootstrap 2.x styles](doc/screenshot.jpg)
+
+* growl like notifications like in MacOS X
+* using standard bootstrap classes (alert, alert-info, alert-error, alert-success)
+* global or per message configuration of a timeout when message will be automatically closed
+* automatic translation of messages if [angular-translate](https://github.com/PascalPrecht/angular-translate) filter is
+present, you only have to provide keys as messages, angular-translate will translate them
+* pre-defined $http-Interceptor to automatically handle $http responses for server-sent messages
+* automatic CSS animations when adding/closing notifications (only when using >= angularJS 1.2)
+* < 1 kB after GZIP
+
+##Changelog
+
+**0.4.0** - 19th Nov 2013
+
+* updated dependency to angularJS 1.2.x, angular-growl does not work with 1.0.x anymore (BREAKING CHANGE)
+* new option: only display unique messages, which is the new default, disable to allow same message more than once (BREAKING CHANGE)
+* new option: allow html tags in messages, default is off you need to
+
+**0.3.1** - 1st Oct 2013
+
+* bugfix: translating of messages works again
+* change: also set alert css classes introduced by bootstrap 3
+
+**0.3.0** - 26th Sept 2013
+
+* adding css animations support via ngAnimate (for angularJS >= 1.2)
+* ability to configure server message keys
+
+**0.2.0** - 22nd Sept 2013
+
+* reworking, bugfixing and documenting handling of server sent messages/notifications
+* externalizing css styles of growl class
+* provide minified versions of js and css files in build folder
+
+**0.1.3** - 20th Sept 2013
+
+* introducing ttl config option, fixes #2
+
+##Installation
+
+You can install angular-growl with bower:
+
+> bower install angular-growl
+
+Alternatively you can download the files in the [build folder](build/) manually and include them in your project.
+
+````html
+
+
+
+
+
+
+
+
+
+
+````
+
+As angular-growl is based on its own angularJS module, you have to alter your dependency list when creating your application
+module:
+
+````javascript
+var app = angular.module('myApp', ['angular-growl']);
+````
+
+Finally, you have to include the directive somewhere in your HTML like this:
+
+````html
+
+
+
+````
+
+##Usage
+
+Just let angular inject the growl Factory into your code and call the 4 functions that the factory provides accordingly:
+
+````javascript
+app.controller("demoCtrl", ['$scope', 'growl', function($scope, growl) {
+ $scope.addSpecialWarnMessage = function() {
+ growl.addWarnMessage("This adds a warn message");
+ growl.addInfoMessage("This adds a info message");
+ growl.addSuccessMessage("This adds a success message");
+ growl.addErrorMessage("This adds a error message");
+ }
+}]);
+````
+
+If [angular-translate](https://github.com/PascalPrecht/angular-translate) is present, its filter is automatically called for translating of messages, so you have to provide
+only the key:
+
+````javascript
+app.controller("demoCtrl", ['$scope', 'growl', function($scope, growl) {
+ $scope.addSpecialWarnMessage = function() {
+ growl.addSuccessMessage("SAVE_SUCCESS_MESSAGE");
+ growl.addErrorMessage("VALIDATION_ERROR");
+ }
+}]);
+````
+
+##Configuration
+
+###Only unique messages
+
+* Default: true
+
+Accept only unique messages as a new message. If a message is already displayed (text and severity are the same) then this
+message will not be added to the displayed message list. Set to false, to always display all messages regardless if they
+are already displayed or not:
+
+````javascript
+var app = angular.module('myApp', ['angular-growl']);
+
+app.config(['growlProvider', function(growlProvider) {
+ growlProvider.onlyUniqueMessages(false);
+}]);
+````
+
+###Automatic closing of notifications (timeout, ttl)
+
+* Default: none (all messages need to be closed manually by the user.)
+
+However, you can configure a global timeout (TTL) after which notifications should be automatically closed. To do
+this, you have to configure this during config phase of angular bootstrap like this:
+
+````javascript
+var app = angular.module('myApp', ['angular-growl']);
+
+app.config(['growlProvider', function(growlProvider) {
+ growlProvider.globalTimeToLive(5000);
+}]);
+````
+
+This sets a global timeout of 5 seconds after which every notification will be closed.
+
+You can override TTL generally for every single message if you want:
+
+````javascript
+app.controller("demoCtrl", ['$scope', 'growl', function($scope, growl) {
+ $scope.addSpecialWarnMessage = function() {
+ growl.addWarnMessage("Override global ttl setting", {ttl: 10000});
+ }
+}]);
+````
+
+This sets a 10 second timeout, after which the notification will be automatically closed.
+
+If you have set a global TTL, you can disable automatic closing of single notifications by setting their ttl to -1:
+
+````javascript
+app.controller("demoCtrl", ['$scope', 'growl', function($scope, growl) {
+ $scope.addSpecialWarnMessage = function() {
+ growl.addWarnMessage("this will not be closed automatically even when a global ttl is set", {ttl: -1});
+ }
+}]);
+````
+
+###Allow HTML in messages
+
+* Default: false
+
+Turn this on to be able to display html tags in messages, default behaviour is to NOT display HTML.
+
+For this to work, you have to declare a dependency to "ngSanitize" (and load the extra javascript) in your own application
+module!
+
+````javascript
+var app = angular.module('myApp', ['angular-growl', 'ngSanitize']);
+
+app.config(['growlProvider', function(growlProvider) {
+ growlProvider.globalEnableHtml(true);
+}]);
+````
+
+You can override the global option and allow HTML tags in single messages too:
+
+````javascript
+app.controller("demoCtrl", ['$scope', 'growl', function($scope, growl) {
+ $scope.addSpecialWarnMessage = function() {
+ growl.addWarnMessage("This is a HTML message", {enableHtml: true});
+ }
+}]);
+````
+
+###Animations
+
+Beginning with angularJS 1.2 growl messages can be automatically animated with CSS animations when adding and/or closing
+them. All you have to do is load the angular-animate.js provided by angularJS and add **ngAnimate** to your applications
+dependency list:
+
+````html
+
+
+
+
+
+
+
+
+
+
+````
+
+````javascript
+var app = angular.module('myApp', ['angular-growl', 'ngAnimate']);
+````
+
+That's it. The angular-growl.css comes with a pre-defined animation of 0.5s to opacity.
+
+To configure the animations, just change the _growl-item.*_ classes in the css file to your preference. F.i. to change length
+of animation from 0.5s to 1s do this:
+
+````css
+.growl-item.ng-enter,
+.growl-item.ng-leave {
+ -webkit-transition:1s linear all;
+ -moz-transition:1s linear all;
+ -o-transition:1s linear all;
+ transition:1s linear all;
+}
+````
+
+Basically you can style your animations just as you like if ngAnimate can pick it up automatically. See the [ngAnimate
+docs](http://docs.angularjs.org/api/ngAnimate) for more info.
+
+###Handling of server sent notifications
+
+When doing $http requests, you can configure angular-growl to look automatically for messages in $http responses, so your
+business logic on the server is able to send messages/notifications to the client and you can display them automagically:
+
+````javascript
+var app = angular.module('myApp', ['angular-growl']);
+
+app.config(['growlProvider', '$httpProvider', function(growlProvider, $httpProvider) {
+ $httpProvider.responseInterceptors.push(growlProvider.serverMessagesInterceptor);
+}]);
+````
+
+This adds a pre-defined angularJS HTTP interceptor that is called on every HTTP request and looks if response contains
+messages. Interceptor looks in response for a "messages" array of objects with "text" and "severity" key. This is an example
+response which results in 3 growl messages:
+
+````json
+{
+ "someOtherData": {...},
+ "messages": [
+ {"text":"this is a server message", "severity": "warn"},
+ {"text":"this is another server message", "severity": "info"},
+ {"text":"and another", "severity": "error"}
+ ]
+}
+````
+
+You can configure the keys, the interceptor is looking for like this:
+
+````javascript
+var app = angular.module("demo", ["angular-growl"]);
+
+app.config(["growlProvider", "$httpProvider", function(growlProvider, $httpProvider) {
+ growlProvider.messagesKey("my-messages");
+ growlProvider.messageTextKey("messagetext");
+ growlProvider.messageSeverityKey("severity-level");
+ $httpProvider.responseInterceptors.push(growlProvider.serverMessagesInterceptor);
+}]);
+````
+
+Server messages will be created with default TTL.
diff --git a/public/bower_components/angular-growl/build/angular-growl.js b/public/bower_components/angular-growl/build/angular-growl.js
new file mode 100644
index 000000000..e4612960b
--- /dev/null
+++ b/public/bower_components/angular-growl/build/angular-growl.js
@@ -0,0 +1,183 @@
+/**
+ * angular-growl - v0.4.0 - 2013-11-19
+ * https://github.com/marcorinck/angular-growl
+ * Copyright (c) 2013 Marco Rinck; Licensed MIT
+ */
+angular.module('angular-growl', []);
+angular.module('angular-growl').directive('growl', [
+ '$rootScope',
+ function ($rootScope) {
+ 'use strict';
+ return {
+ restrict: 'A',
+ template: '
' + '\t
' + '\t\t' + '
' + ' ' + ' ' + '
' + '\t
' + '
',
+ replace: false,
+ scope: true,
+ controller: [
+ '$scope',
+ '$timeout',
+ 'growl',
+ function ($scope, $timeout, growl) {
+ var onlyUnique = growl.onlyUnique();
+ $scope.messages = [];
+ function addMessage(message) {
+ $scope.messages.push(message);
+ if (message.ttl && message.ttl !== -1) {
+ $timeout(function () {
+ $scope.deleteMessage(message);
+ }, message.ttl);
+ }
+ }
+ $rootScope.$on('growlMessage', function (event, message) {
+ var found;
+ if (onlyUnique) {
+ angular.forEach($scope.messages, function (msg) {
+ if (message.text === msg.text && message.severity === msg.severity) {
+ found = true;
+ }
+ });
+ if (!found) {
+ addMessage(message);
+ }
+ } else {
+ addMessage(message);
+ }
+ });
+ $scope.deleteMessage = function (message) {
+ var index = $scope.messages.indexOf(message);
+ if (index > -1) {
+ $scope.messages.splice(index, 1);
+ }
+ };
+ $scope.computeClasses = function (message) {
+ return {
+ 'alert-success': message.severity === 'success',
+ 'alert-error': message.severity === 'error',
+ 'alert-danger': message.severity === 'error',
+ 'alert-info': message.severity === 'info',
+ 'alert-warning': message.severity === 'warn'
+ };
+ };
+ }
+ ]
+ };
+ }
+]);
+angular.module('angular-growl').provider('growl', function () {
+ 'use strict';
+ var _ttl = null, _enableHtml = false, _messagesKey = 'messages', _messageTextKey = 'text', _messageSeverityKey = 'severity', _onlyUniqueMessages = true;
+ this.globalTimeToLive = function (ttl) {
+ _ttl = ttl;
+ };
+ this.globalEnableHtml = function (enableHtml) {
+ _enableHtml = enableHtml;
+ };
+ this.messagesKey = function (messagesKey) {
+ _messagesKey = messagesKey;
+ };
+ this.messageTextKey = function (messageTextKey) {
+ _messageTextKey = messageTextKey;
+ };
+ this.messageSeverityKey = function (messageSeverityKey) {
+ _messageSeverityKey = messageSeverityKey;
+ };
+ this.onlyUniqueMessages = function (onlyUniqueMessages) {
+ _onlyUniqueMessages = onlyUniqueMessages;
+ };
+ this.serverMessagesInterceptor = [
+ '$q',
+ 'growl',
+ function ($q, growl) {
+ function checkResponse(response) {
+ if (response.data[_messagesKey] && response.data[_messagesKey].length > 0) {
+ growl.addServerMessages(response.data[_messagesKey]);
+ }
+ }
+ function success(response) {
+ checkResponse(response);
+ return response;
+ }
+ function error(response) {
+ checkResponse(response);
+ return $q.reject(response);
+ }
+ return function (promise) {
+ return promise.then(success, error);
+ };
+ }
+ ];
+ this.$get = [
+ '$rootScope',
+ '$filter',
+ function ($rootScope, $filter) {
+ var translate;
+ try {
+ translate = $filter('translate');
+ } catch (e) {
+ }
+ function broadcastMessage(message) {
+ if (translate) {
+ message.text = translate(message.text);
+ }
+ $rootScope.$broadcast('growlMessage', message);
+ }
+ function sendMessage(text, config, severity) {
+ var _config = config || {}, message;
+ message = {
+ text: text,
+ severity: severity,
+ ttl: _config.ttl || _ttl,
+ enableHtml: _config.enableHtml || _enableHtml
+ };
+ broadcastMessage(message);
+ }
+ function addWarnMessage(text, config) {
+ sendMessage(text, config, 'warn');
+ }
+ function addErrorMessage(text, config) {
+ sendMessage(text, config, 'error');
+ }
+ function addInfoMessage(text, config) {
+ sendMessage(text, config, 'info');
+ }
+ function addSuccessMessage(text, config) {
+ sendMessage(text, config, 'success');
+ }
+ function addServerMessages(messages) {
+ var i, message, severity, length;
+ length = messages.length;
+ for (i = 0; i < length; i++) {
+ message = messages[i];
+ if (message[_messageTextKey] && message[_messageSeverityKey]) {
+ switch (message[_messageSeverityKey]) {
+ case 'warn':
+ severity = 'warn';
+ break;
+ case 'success':
+ severity = 'success';
+ break;
+ case 'info':
+ severity = 'info';
+ break;
+ case 'error':
+ severity = 'error';
+ break;
+ }
+ sendMessage(message[_messageTextKey], undefined, severity);
+ }
+ }
+ }
+ function onlyUnique() {
+ return _onlyUniqueMessages;
+ }
+ return {
+ addWarnMessage: addWarnMessage,
+ addErrorMessage: addErrorMessage,
+ addInfoMessage: addInfoMessage,
+ addSuccessMessage: addSuccessMessage,
+ addServerMessages: addServerMessages,
+ onlyUnique: onlyUnique
+ };
+ }
+ ];
+});
\ No newline at end of file
diff --git a/public/bower_components/angular-growl/build/angular-growl.min.css b/public/bower_components/angular-growl/build/angular-growl.min.css
new file mode 100644
index 000000000..e1ed8423b
--- /dev/null
+++ b/public/bower_components/angular-growl/build/angular-growl.min.css
@@ -0,0 +1,7 @@
+/**
+ * angular-growl - v0.4.0 - 2013-11-19
+ * https://github.com/marcorinck/angular-growl
+ * Copyright (c) 2013 Marco Rinck; Licensed MIT
+ */
+
+.growl{position:fixed;top:10px;right:10px;float:right;width:250px}.growl-item.ng-enter,.growl-item.ng-leave{-webkit-transition:.5s linear all;-moz-transition:.5s linear all;-o-transition:.5s linear all;transition:.5s linear all}.growl-item.ng-enter,.growl-item.ng-leave.ng-leave-active{opacity:0}.growl-item.ng-leave,.growl-item.ng-enter.ng-enter-active{opacity:1}
\ No newline at end of file
diff --git a/public/bower_components/angular-growl/build/angular-growl.min.js b/public/bower_components/angular-growl/build/angular-growl.min.js
new file mode 100644
index 000000000..83798c554
--- /dev/null
+++ b/public/bower_components/angular-growl/build/angular-growl.min.js
@@ -0,0 +1,6 @@
+/**
+ * angular-growl - v0.4.0 - 2013-11-19
+ * https://github.com/marcorinck/angular-growl
+ * Copyright (c) 2013 Marco Rinck; Licensed MIT
+ */
+angular.module("angular-growl",[]),angular.module("angular-growl").directive("growl",["$rootScope",function(a){"use strict";return{restrict:"A",template:'
',replace:!1,scope:!0,controller:["$scope","$timeout","growl",function(b,c,d){function e(a){b.messages.push(a),a.ttl&&-1!==a.ttl&&c(function(){b.deleteMessage(a)},a.ttl)}var f=d.onlyUnique();b.messages=[],a.$on("growlMessage",function(a,c){var d;f?(angular.forEach(b.messages,function(a){c.text===a.text&&c.severity===a.severity&&(d=!0)}),d||e(c)):e(c)}),b.deleteMessage=function(a){var c=b.messages.indexOf(a);c>-1&&b.messages.splice(c,1)},b.computeClasses=function(a){return{"alert-success":"success"===a.severity,"alert-error":"error"===a.severity,"alert-danger":"error"===a.severity,"alert-info":"info"===a.severity,"alert-warning":"warn"===a.severity}}}]}}]),angular.module("angular-growl").provider("growl",function(){"use strict";var a=null,b=!1,c="messages",d="text",e="severity",f=!0;this.globalTimeToLive=function(b){a=b},this.globalEnableHtml=function(a){b=a},this.messagesKey=function(a){c=a},this.messageTextKey=function(a){d=a},this.messageSeverityKey=function(a){e=a},this.onlyUniqueMessages=function(a){f=a},this.serverMessagesInterceptor=["$q","growl",function(a,b){function d(a){a.data[c]&&a.data[c].length>0&&b.addServerMessages(a.data[c])}function e(a){return d(a),a}function f(b){return d(b),a.reject(b)}return function(a){return a.then(e,f)}}],this.$get=["$rootScope","$filter",function(c,g){function h(a){p&&(a.text=p(a.text)),c.$broadcast("growlMessage",a)}function i(c,d,e){var f,g=d||{};f={text:c,severity:e,ttl:g.ttl||a,enableHtml:g.enableHtml||b},h(f)}function j(a,b){i(a,b,"warn")}function k(a,b){i(a,b,"error")}function l(a,b){i(a,b,"info")}function m(a,b){i(a,b,"success")}function n(a){var b,c,f,g;for(g=a.length,b=0;g>b;b++)if(c=a[b],c[d]&&c[e]){switch(c[e]){case"warn":f="warn";break;case"success":f="success";break;case"info":f="info";break;case"error":f="error"}i(c[d],void 0,f)}}function o(){return f}var p;try{p=g("translate")}catch(q){}return{addWarnMessage:j,addErrorMessage:k,addInfoMessage:l,addSuccessMessage:m,addServerMessages:n,onlyUnique:o}}]});
\ No newline at end of file
diff --git a/public/bower_components/angular-growl/doc/screenshot.jpg b/public/bower_components/angular-growl/doc/screenshot.jpg
new file mode 100644
index 000000000..5c21b8e6f
Binary files /dev/null and b/public/bower_components/angular-growl/doc/screenshot.jpg differ
diff --git a/public/bower_components/angular/.bower.json b/public/bower_components/angular/.bower.json
index 8c166490e..39bec0a62 100644
--- a/public/bower_components/angular/.bower.json
+++ b/public/bower_components/angular/.bower.json
@@ -1,16 +1,16 @@
{
"name": "angular",
- "version": "1.2.16",
+ "version": "1.2.17",
"main": "./angular.js",
"dependencies": {},
"homepage": "https://github.com/angular/bower-angular",
- "_release": "1.2.16",
+ "_release": "1.2.17",
"_resolution": {
"type": "version",
- "tag": "v1.2.16",
- "commit": "7ae38b4a0cfced157e3486a0d6e2d299601723bb"
+ "tag": "v1.2.17",
+ "commit": "e161fd5e5e4c74720fb1c4156b89585642882e82"
},
"_source": "git://github.com/angular/bower-angular.git",
- "_target": "1.2.16",
+ "_target": "~1.2.4",
"_originalSource": "angular"
}
\ No newline at end of file
diff --git a/public/bower_components/angular/angular-csp.css b/public/bower_components/angular/angular-csp.css
index 0d3d3a9ae..3abb3a0e6 100644
--- a/public/bower_components/angular/angular-csp.css
+++ b/public/bower_components/angular/angular-csp.css
@@ -16,3 +16,9 @@ ng\:form {
transition:0s all!important;
-webkit-transition:0s all!important;
}
+
+/* show the element during a show/hide animation when the
+ * animation is ongoing, but the .ng-hide class is active */
+.ng-hide-add-active, .ng-hide-remove {
+ display: block!important;
+}
diff --git a/public/bower_components/angular/angular.js b/public/bower_components/angular/angular.js
index 2f26beed6..e3e6ffdf7 100644
--- a/public/bower_components/angular/angular.js
+++ b/public/bower_components/angular/angular.js
@@ -1,5 +1,5 @@
/**
- * @license AngularJS v1.2.16
+ * @license AngularJS v1.2.17
* (c) 2010-2014 Google, Inc. http://angularjs.org
* License: MIT
*/
@@ -68,7 +68,7 @@ function minErr(module) {
return match;
});
- message = message + '\nhttp://errors.angularjs.org/1.2.16/' +
+ message = message + '\nhttp://errors.angularjs.org/1.2.17/' +
(module ? module + '/' : '') + code;
for (i = 2; i < arguments.length; i++) {
message = message + (i == 2 ? '?' : '&') + 'p' + (i-2) + '=' +
@@ -89,7 +89,6 @@ function minErr(module) {
-push,
-toString,
-ngMinErr,
- -_angular,
-angularModule,
-nodeName_,
-uid,
@@ -186,7 +185,7 @@ function minErr(module) {
* @ngdoc function
* @name angular.lowercase
* @module ng
- * @function
+ * @kind function
*
* @description Converts the specified string to lowercase.
* @param {string} string String to be converted to lowercase.
@@ -199,7 +198,7 @@ var hasOwnProperty = Object.prototype.hasOwnProperty;
* @ngdoc function
* @name angular.uppercase
* @module ng
- * @function
+ * @kind function
*
* @description Converts the specified string to uppercase.
* @param {string} string String to be converted to uppercase.
@@ -240,8 +239,6 @@ var /** holds major version number for IE or NaN for real browsers */
toString = Object.prototype.toString,
ngMinErr = minErr('ng'),
-
- _angular = window.angular,
/** @name angular */
angular = window.angular || (window.angular = {}),
angularModule,
@@ -283,7 +280,7 @@ function isArrayLike(obj) {
* @ngdoc function
* @name angular.forEach
* @module ng
- * @function
+ * @kind function
*
* @description
* Invokes the `iterator` function once for each item in `obj` collection, which can be either an
@@ -297,7 +294,7 @@ function isArrayLike(obj) {
```js
var values = {name: 'misko', gender: 'male'};
var log = [];
- angular.forEach(values, function(value, key){
+ angular.forEach(values, function(value, key) {
this.push(key + ': ' + value);
}, log);
expect(log).toEqual(['name: misko', 'gender: male']);
@@ -311,7 +308,7 @@ function isArrayLike(obj) {
function forEach(obj, iterator, context) {
var key;
if (obj) {
- if (isFunction(obj)){
+ if (isFunction(obj)) {
for (key in obj) {
// Need to check if hasOwnProperty exists,
// as on IE8 the result of querySelectorAll is an object without a hasOwnProperty function
@@ -412,7 +409,7 @@ function setHashKey(obj, h) {
* @ngdoc function
* @name angular.extend
* @module ng
- * @function
+ * @kind function
*
* @description
* Extends the destination object `dst` by copying all of the properties from the `src` object(s)
@@ -424,9 +421,9 @@ function setHashKey(obj, h) {
*/
function extend(dst) {
var h = dst.$$hashKey;
- forEach(arguments, function(obj){
+ forEach(arguments, function(obj) {
if (obj !== dst) {
- forEach(obj, function(value, key){
+ forEach(obj, function(value, key) {
dst[key] = value;
});
}
@@ -449,7 +446,7 @@ function inherit(parent, extra) {
* @ngdoc function
* @name angular.noop
* @module ng
- * @function
+ * @kind function
*
* @description
* A function that performs no operations. This function can be useful when writing code in the
@@ -469,7 +466,7 @@ noop.$inject = [];
* @ngdoc function
* @name angular.identity
* @module ng
- * @function
+ * @kind function
*
* @description
* A function that returns its first argument. This function is useful when writing code in the
@@ -491,7 +488,7 @@ function valueFn(value) {return function() {return value;};}
* @ngdoc function
* @name angular.isUndefined
* @module ng
- * @function
+ * @kind function
*
* @description
* Determines if a reference is undefined.
@@ -506,7 +503,7 @@ function isUndefined(value){return typeof value === 'undefined';}
* @ngdoc function
* @name angular.isDefined
* @module ng
- * @function
+ * @kind function
*
* @description
* Determines if a reference is defined.
@@ -521,7 +518,7 @@ function isDefined(value){return typeof value !== 'undefined';}
* @ngdoc function
* @name angular.isObject
* @module ng
- * @function
+ * @kind function
*
* @description
* Determines if a reference is an `Object`. Unlike `typeof` in JavaScript, `null`s are not
@@ -537,7 +534,7 @@ function isObject(value){return value != null && typeof value === 'object';}
* @ngdoc function
* @name angular.isString
* @module ng
- * @function
+ * @kind function
*
* @description
* Determines if a reference is a `String`.
@@ -552,7 +549,7 @@ function isString(value){return typeof value === 'string';}
* @ngdoc function
* @name angular.isNumber
* @module ng
- * @function
+ * @kind function
*
* @description
* Determines if a reference is a `Number`.
@@ -567,7 +564,7 @@ function isNumber(value){return typeof value === 'number';}
* @ngdoc function
* @name angular.isDate
* @module ng
- * @function
+ * @kind function
*
* @description
* Determines if a value is a date.
@@ -575,7 +572,7 @@ function isNumber(value){return typeof value === 'number';}
* @param {*} value Reference to check.
* @returns {boolean} True if `value` is a `Date`.
*/
-function isDate(value){
+function isDate(value) {
return toString.call(value) === '[object Date]';
}
@@ -584,7 +581,7 @@ function isDate(value){
* @ngdoc function
* @name angular.isArray
* @module ng
- * @function
+ * @kind function
*
* @description
* Determines if a reference is an `Array`.
@@ -601,7 +598,7 @@ function isArray(value) {
* @ngdoc function
* @name angular.isFunction
* @module ng
- * @function
+ * @kind function
*
* @description
* Determines if a reference is a `Function`.
@@ -675,7 +672,7 @@ var trim = (function() {
* @ngdoc function
* @name angular.isElement
* @module ng
- * @function
+ * @kind function
*
* @description
* Determines if a reference is a DOM element (or wrapped jQuery element).
@@ -693,7 +690,7 @@ function isElement(node) {
* @param str 'key1,key2,...'
* @returns {object} in the form of {key1:true, key2:true, ...}
*/
-function makeMap(str){
+function makeMap(str) {
var obj = {}, items = str.split(","), i;
for ( i = 0; i < items.length; i++ )
obj[ items[i] ] = true;
@@ -740,7 +737,7 @@ function size(obj, ownPropsOnly) {
if (isArray(obj) || isString(obj)) {
return obj.length;
- } else if (isObject(obj)){
+ } else if (isObject(obj)) {
for (key in obj)
if (!ownPropsOnly || obj.hasOwnProperty(key))
count++;
@@ -786,7 +783,7 @@ function isLeafNode (node) {
* @ngdoc function
* @name angular.copy
* @module ng
- * @function
+ * @kind function
*
* @description
* Creates a deep copy of `source`, which should be an object or an array.
@@ -839,7 +836,7 @@ function isLeafNode (node) {
*/
-function copy(source, destination){
+function copy(source, destination, stackSource, stackDest) {
if (isWindow(source) || isScope(source)) {
throw ngMinErr('cpws',
"Can't copy! Making copies of Window or Scope instances is not supported.");
@@ -849,52 +846,82 @@ function copy(source, destination){
destination = source;
if (source) {
if (isArray(source)) {
- destination = copy(source, []);
+ destination = copy(source, [], stackSource, stackDest);
} else if (isDate(source)) {
destination = new Date(source.getTime());
} else if (isRegExp(source)) {
destination = new RegExp(source.source);
} else if (isObject(source)) {
- destination = copy(source, {});
+ destination = copy(source, {}, stackSource, stackDest);
}
}
} else {
if (source === destination) throw ngMinErr('cpi',
"Can't copy! Source and destination are identical.");
+
+ stackSource = stackSource || [];
+ stackDest = stackDest || [];
+
+ if (isObject(source)) {
+ var index = indexOf(stackSource, source);
+ if (index !== -1) return stackDest[index];
+
+ stackSource.push(source);
+ stackDest.push(destination);
+ }
+
+ var result;
if (isArray(source)) {
destination.length = 0;
for ( var i = 0; i < source.length; i++) {
- destination.push(copy(source[i]));
+ result = copy(source[i], null, stackSource, stackDest);
+ if (isObject(source[i])) {
+ stackSource.push(source[i]);
+ stackDest.push(result);
+ }
+ destination.push(result);
}
} else {
var h = destination.$$hashKey;
- forEach(destination, function(value, key){
+ forEach(destination, function(value, key) {
delete destination[key];
});
for ( var key in source) {
- destination[key] = copy(source[key]);
+ result = copy(source[key], null, stackSource, stackDest);
+ if (isObject(source[key])) {
+ stackSource.push(source[key]);
+ stackDest.push(result);
+ }
+ destination[key] = result;
}
setHashKey(destination,h);
}
+
}
return destination;
}
/**
- * Create a shallow copy of an object
+ * Creates a shallow copy of an object, an array or a primitive
*/
function shallowCopy(src, dst) {
- dst = dst || {};
+ if (isArray(src)) {
+ dst = dst || [];
+
+ for ( var i = 0; i < src.length; i++) {
+ dst[i] = src[i];
+ }
+ } else if (isObject(src)) {
+ dst = dst || {};
- for(var key in src) {
- // shallowCopy is only ever called by $compile nodeLinkFn, which has control over src
- // so we don't need to worry about using our custom hasOwnProperty here
- if (src.hasOwnProperty(key) && !(key.charAt(0) === '$' && key.charAt(1) === '$')) {
- dst[key] = src[key];
+ for (var key in src) {
+ if (hasOwnProperty.call(src, key) && !(key.charAt(0) === '$' && key.charAt(1) === '$')) {
+ dst[key] = src[key];
+ }
}
}
- return dst;
+ return dst || src;
}
@@ -902,7 +929,7 @@ function shallowCopy(src, dst) {
* @ngdoc function
* @name angular.equals
* @module ng
- * @function
+ * @kind function
*
* @description
* Determines if two objects or two values are equivalent. Supports value types, regular
@@ -914,7 +941,7 @@ function shallowCopy(src, dst) {
* * Both objects or values are of the same type and all of their properties are equal by
* comparing them with `angular.equals`.
* * Both values are NaN. (In JavaScript, NaN == NaN => false. But we consider two NaN as equal)
- * * Both values represent the same regular expression (In JavasScript,
+ * * Both values represent the same regular expression (In JavaScript,
* /abc/ == /abc/ => false. But we consider two regular expressions as equal when their textual
* representation matches).
*
@@ -989,7 +1016,7 @@ function sliceArgs(args, startIndex) {
* @ngdoc function
* @name angular.bind
* @module ng
- * @function
+ * @kind function
*
* @description
* Returns a function which calls function `fn` bound to `self` (`self` becomes the `this` for
@@ -1045,7 +1072,7 @@ function toJsonReplacer(key, value) {
* @ngdoc function
* @name angular.toJson
* @module ng
- * @function
+ * @kind function
*
* @description
* Serializes input into a JSON-formatted string. Properties with leading $ characters will be
@@ -1065,7 +1092,7 @@ function toJson(obj, pretty) {
* @ngdoc function
* @name angular.fromJson
* @module ng
- * @function
+ * @kind function
*
* @description
* Deserializes a JSON string.
@@ -1142,7 +1169,7 @@ function tryDecodeURIComponent(value) {
*/
function parseKeyValue(/**string*/keyValue) {
var obj = {}, key_value, key;
- forEach((keyValue || "").split('&'), function(keyValue){
+ forEach((keyValue || "").split('&'), function(keyValue) {
if ( keyValue ) {
key_value = keyValue.split('=');
key = tryDecodeURIComponent(key_value[0]);
@@ -1404,7 +1431,7 @@ function bootstrap(element, modules) {
}
var SNAKE_CASE_REGEXP = /[A-Z]/g;
-function snake_case(name, separator){
+function snake_case(name, separator) {
separator = separator || '_';
return name.replace(SNAKE_CASE_REGEXP, function(letter, pos) {
return (pos ? separator : '') + letter.toLowerCase();
@@ -1414,8 +1441,9 @@ function snake_case(name, separator){
function bindJQuery() {
// bind to jQuery if present;
jQuery = window.jQuery;
- // reset to jQuery or default to us.
- if (jQuery) {
+ // Use jQuery if it exists with proper functionality, otherwise default to us.
+ // Angular 1.2+ requires jQuery 1.7.1+ for on()/off() support.
+ if (jQuery && jQuery.fn.on) {
jqLite = jQuery;
extend(jQuery.fn, {
scope: JQLitePrototype.scope,
@@ -1783,6 +1811,8 @@ function setupModuleLoader(window) {
* configuration.
* @description
* Use this method to register work which needs to be performed on module loading.
+ * For more about how to configure services, see
+ * {@link providers#providers_provider-recipe Provider Recipe}.
*/
config: config,
@@ -1919,11 +1949,11 @@ function setupModuleLoader(window) {
* - `codeName` – `{string}` – Code name of the release, such as "jiggling-armfat".
*/
var version = {
- full: '1.2.16', // all of these placeholder strings will be replaced by grunt's
+ full: '1.2.17', // all of these placeholder strings will be replaced by grunt's
major: 1, // package task
minor: 2,
- dot: 16,
- codeName: 'badger-enumeration'
+ dot: 17,
+ codeName: 'quantum-disentanglement'
};
@@ -2063,7 +2093,7 @@ function publishExternalAPI(angular){
* @ngdoc function
* @name angular.element
* @module ng
- * @function
+ * @kind function
*
* @description
* Wraps a raw DOM element or HTML string as a [jQuery](http://jquery.com) element.
@@ -2146,7 +2176,7 @@ function publishExternalAPI(angular){
*/
var jqCache = JQLite.cache = {},
- jqName = JQLite.expando = 'ng-' + new Date().getTime(),
+ jqName = JQLite.expando = 'ng' + new Date().getTime(),
jqId = 1,
addEventListenerFn = (window.document.addEventListener
? function(element, type, fn) {element.addEventListener(type, fn, false);}
@@ -3102,7 +3132,7 @@ HashMap.prototype = {
* @ngdoc function
* @module ng
* @name angular.injector
- * @function
+ * @kind function
*
* @description
* Creates an injector function that can be used for retrieving services as well as for
@@ -3129,7 +3159,7 @@ HashMap.prototype = {
*
* Sometimes you want to get access to the injector of a currently running Angular app
* from outside Angular. Perhaps, you want to inject and compile some markup after the
- * application has been bootstrapped. You can do this using extra `injector()` added
+ * application has been bootstrapped. You can do this using the extra `injector()` added
* to JQuery/jqLite elements. See {@link angular.element}.
*
* *This is fairly rare but could be the case if a third party library is injecting the
@@ -3199,7 +3229,7 @@ function annotate(fn) {
/**
* @ngdoc service
* @name $injector
- * @function
+ * @kind function
*
* @description
*
@@ -3242,7 +3272,7 @@ function annotate(fn) {
* minification, and obfuscation tools since these tools change the argument names.
*
* ## `$inject` Annotation
- * By adding a `$inject` property onto a function the injection parameters can be specified.
+ * By adding an `$inject` property onto a function the injection parameters can be specified.
*
* ## Inline
* As an array of injection names, where the last item in the array is the function to call.
@@ -3279,7 +3309,7 @@ function annotate(fn) {
* @name $injector#has
*
* @description
- * Allows the user to query if the particular service exist.
+ * Allows the user to query if the particular service exists.
*
* @param {string} Name of the service to query.
* @returns {boolean} returns true if injector has given service.
@@ -3289,8 +3319,8 @@ function annotate(fn) {
* @ngdoc method
* @name $injector#instantiate
* @description
- * Create a new instance of JS type. The method takes a constructor function invokes the new
- * operator and supplies all of the arguments to the constructor function as specified by the
+ * Create a new instance of JS type. The method takes a constructor function, invokes the new
+ * operator, and supplies all of the arguments to the constructor function as specified by the
* constructor annotation.
*
* @param {Function} Type Annotated constructor function.
@@ -3903,7 +3933,7 @@ function createInjector(modulesToLoad) {
* @requires $rootScope
*
* @description
- * When called, it checks current value of `$location.hash()` and scroll to related element,
+ * When called, it checks current value of `$location.hash()` and scrolls to the related element,
* according to rules specified in
* [Html5 spec](http://dev.w3.org/html5/spec/Overview.html#the-indicated-part-of-the-document).
*
@@ -4105,7 +4135,7 @@ var $AnimateProvider = ['$provide', function($provide) {
*
* @ngdoc method
* @name $animate#enter
- * @function
+ * @kind function
* @description Inserts the element into the DOM either after the `after` element or within
* the `parent` element. Once complete, the done() callback will be fired (if provided).
* @param {DOMElement} element the element which will be inserted into the DOM
@@ -4132,7 +4162,7 @@ var $AnimateProvider = ['$provide', function($provide) {
*
* @ngdoc method
* @name $animate#leave
- * @function
+ * @kind function
* @description Removes the element from the DOM. Once complete, the done() callback will be
* fired (if provided).
* @param {DOMElement} element the element which will be removed from the DOM
@@ -4148,7 +4178,7 @@ var $AnimateProvider = ['$provide', function($provide) {
*
* @ngdoc method
* @name $animate#move
- * @function
+ * @kind function
* @description Moves the position of the provided element within the DOM to be placed
* either after the `after` element or inside of the `parent` element. Once complete, the
* done() callback will be fired (if provided).
@@ -4172,7 +4202,7 @@ var $AnimateProvider = ['$provide', function($provide) {
*
* @ngdoc method
* @name $animate#addClass
- * @function
+ * @kind function
* @description Adds the provided className CSS class value to the provided element. Once
* complete, the done() callback will be fired (if provided).
* @param {DOMElement} element the element which will have the className value
@@ -4195,7 +4225,7 @@ var $AnimateProvider = ['$provide', function($provide) {
*
* @ngdoc method
* @name $animate#removeClass
- * @function
+ * @kind function
* @description Removes the provided className CSS class value from the provided element.
* Once complete, the done() callback will be fired (if provided).
* @param {DOMElement} element the element which will have the className value
@@ -4218,10 +4248,10 @@ var $AnimateProvider = ['$provide', function($provide) {
*
* @ngdoc method
* @name $animate#setClass
- * @function
+ * @kind function
* @description Adds and/or removes the given CSS classes to and from the element.
* Once complete, the done() callback will be fired (if provided).
- * @param {DOMElement} element the element which will it's CSS classes changed
+ * @param {DOMElement} element the element which will have its CSS classes changed
* removed from it
* @param {string} add the CSS classes which will be added to the element
* @param {string} remove the CSS class which will be removed from the element
@@ -4775,7 +4805,7 @@ function $CacheFactoryProvider() {
/**
* @ngdoc method
* @name $cacheFactory.Cache#put
- * @function
+ * @kind function
*
* @description
* Inserts a named entry into the {@link $cacheFactory.Cache Cache} object to be
@@ -4811,7 +4841,7 @@ function $CacheFactoryProvider() {
/**
* @ngdoc method
* @name $cacheFactory.Cache#get
- * @function
+ * @kind function
*
* @description
* Retrieves named data stored in the {@link $cacheFactory.Cache Cache} object.
@@ -4835,7 +4865,7 @@ function $CacheFactoryProvider() {
/**
* @ngdoc method
* @name $cacheFactory.Cache#remove
- * @function
+ * @kind function
*
* @description
* Removes an entry from the {@link $cacheFactory.Cache Cache} object.
@@ -4863,7 +4893,7 @@ function $CacheFactoryProvider() {
/**
* @ngdoc method
* @name $cacheFactory.Cache#removeAll
- * @function
+ * @kind function
*
* @description
* Clears the cache object of any entries.
@@ -4879,7 +4909,7 @@ function $CacheFactoryProvider() {
/**
* @ngdoc method
* @name $cacheFactory.Cache#destroy
- * @function
+ * @kind function
*
* @description
* Destroys the {@link $cacheFactory.Cache Cache} object entirely,
@@ -4896,7 +4926,7 @@ function $CacheFactoryProvider() {
/**
* @ngdoc method
* @name $cacheFactory.Cache#info
- * @function
+ * @kind function
*
* @description
* Retrieve information regarding a particular {@link $cacheFactory.Cache Cache}.
@@ -4951,7 +4981,7 @@ function $CacheFactoryProvider() {
* @name $cacheFactory#info
*
* @description
- * Get information about all the of the caches that have been created
+ * Get information about all the caches that have been created
*
* @returns {Object} - key-value map of `cacheId` to the result of calling `cache#info`
*/
@@ -5052,7 +5082,7 @@ function $TemplateCacheProvider() {
/**
* @ngdoc service
* @name $compile
- * @function
+ * @kind function
*
* @description
* Compiles an HTML string or DOM into a template and produces a template function, which
@@ -5090,7 +5120,6 @@ function $TemplateCacheProvider() {
* template: '', // or // function(tElement, tAttrs) { ... },
* // or
* // templateUrl: 'directive.html', // or // function(tElement, tAttrs) { ... },
- * replace: false,
* transclude: false,
* restrict: 'A',
* scope: false,
@@ -5266,7 +5295,7 @@ function $TemplateCacheProvider() {
* api/ng.$sce#getTrustedResourceUrl $sce.getTrustedResourceUrl}.
*
*
- * #### `replace`
+ * #### `replace` ([*DEPRECATED*!], will be removed in next major release)
* specify where the template should be inserted. Defaults to `false`.
*
* * `true` - the template will replace the current element.
@@ -5293,11 +5322,7 @@ function $TemplateCacheProvider() {
* ```
*
* The compile function deals with transforming the template DOM. Since most directives do not do
- * template transformation, it is not used often. Examples that require compile functions are
- * directives that transform template DOM, such as {@link
- * api/ng.directive:ngRepeat ngRepeat}, or load the contents
- * asynchronously, such as {@link ngRoute.directive:ngView ngView}. The
- * compile function takes the following arguments.
+ * template transformation, it is not used often. The compile function takes the following arguments:
*
* * `tElement` - template element - The element where the directive has been declared. It is
* safe to do template transformation on the element and child elements only.
@@ -5535,7 +5560,7 @@ var $compileMinErr = minErr('$compile');
/**
* @ngdoc provider
* @name $compileProvider
- * @function
+ * @kind function
*
* @description
*/
@@ -5543,8 +5568,8 @@ $CompileProvider.$inject = ['$provide', '$$sanitizeUriProvider'];
function $CompileProvider($provide, $$sanitizeUriProvider) {
var hasDirectives = {},
Suffix = 'Directive',
- COMMENT_DIRECTIVE_REGEXP = /^\s*directive\:\s*([\d\w\-_]+)\s+(.*)$/,
- CLASS_DIRECTIVE_REGEXP = /(([\d\w\-_]+)(?:\:([^;]+))?;?)/;
+ COMMENT_DIRECTIVE_REGEXP = /^\s*directive\:\s*([\d\w_\-]+)\s+(.*)$/,
+ CLASS_DIRECTIVE_REGEXP = /(([\d\w_\-]+)(?:\:([^;]+))?;?)/;
// Ref: http://developers.whatwg.org/webappapis.html#event-handler-idl-attributes
// The assumption is that future DOM event attribute names will begin with
@@ -5554,7 +5579,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
/**
* @ngdoc method
* @name $compileProvider#directive
- * @function
+ * @kind function
*
* @description
* Register a new directive with the compiler.
@@ -5607,7 +5632,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
/**
* @ngdoc method
* @name $compileProvider#aHrefSanitizationWhitelist
- * @function
+ * @kind function
*
* @description
* Retrieves or overrides the default regular expression that is used for whitelisting of safe
@@ -5637,7 +5662,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
/**
* @ngdoc method
* @name $compileProvider#imgSrcSanitizationWhitelist
- * @function
+ * @kind function
*
* @description
* Retrieves or overrides the default regular expression that is used for whitelisting of safe
@@ -5681,7 +5706,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
/**
* @ngdoc method
* @name $compile.directive.Attributes#$addClass
- * @function
+ * @kind function
*
* @description
* Adds the CSS class value specified by the classVal parameter to the element. If animations
@@ -5698,7 +5723,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
/**
* @ngdoc method
* @name $compile.directive.Attributes#$removeClass
- * @function
+ * @kind function
*
* @description
* Removes the CSS class value specified by the classVal parameter from the element. If
@@ -5715,7 +5740,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
/**
* @ngdoc method
* @name $compile.directive.Attributes#$updateClass
- * @function
+ * @kind function
*
* @description
* Adds and removes the appropriate CSS class values to the element based on the difference
@@ -5803,7 +5828,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
/**
* @ngdoc method
* @name $compile.directive.Attributes#$observe
- * @function
+ * @kind function
*
* @description
* Observes an interpolated attribute.
@@ -6289,7 +6314,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
if (jqLiteIsTextNode(directiveValue)) {
$template = [];
} else {
- $template = jqLite(directiveValue);
+ $template = jqLite(trim(directiveValue));
}
compileNode = $template[0];
@@ -6372,6 +6397,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
if (pre) {
if (attrStart) pre = groupElementsLinkFnWrapper(pre, attrStart, attrEnd);
pre.require = directive.require;
+ pre.directiveName = directiveName;
if (newIsolateScopeDirective === directive || directive.$$isolateScope) {
pre = cloneAndAnnotateFn(pre, {isolateScope: true});
}
@@ -6380,6 +6406,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
if (post) {
if (attrStart) post = groupElementsLinkFnWrapper(post, attrStart, attrEnd);
post.require = directive.require;
+ post.directiveName = directiveName;
if (newIsolateScopeDirective === directive || directive.$$isolateScope) {
post = cloneAndAnnotateFn(post, {isolateScope: true});
}
@@ -6388,7 +6415,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
}
- function getControllers(require, $element, elementControllers) {
+ function getControllers(directiveName, require, $element, elementControllers) {
var value, retrievalMethod = 'data', optional = false;
if (isString(require)) {
while((value = require.charAt(0)) == '^' || value == '?') {
@@ -6414,7 +6441,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
} else if (isArray(require)) {
value = [];
forEach(require, function(require) {
- value.push(getControllers(require, $element, elementControllers));
+ value.push(getControllers(directiveName, require, $element, elementControllers));
});
}
return value;
@@ -6437,7 +6464,8 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
isolateScope = scope.$new(true);
- if (templateDirective && (templateDirective === newIsolateScopeDirective.$$originalDirective)) {
+ if (templateDirective && (templateDirective === newIsolateScopeDirective ||
+ templateDirective === newIsolateScopeDirective.$$originalDirective)) {
$linkNode.data('$isolateScope', isolateScope) ;
} else {
$linkNode.data('$isolateScopeNoTemplate', isolateScope);
@@ -6557,7 +6585,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
try {
linkFn = preLinkFns[i];
linkFn(linkFn.isolateScope ? isolateScope : scope, $element, attrs,
- linkFn.require && getControllers(linkFn.require, $element, elementControllers), transcludeFn);
+ linkFn.require && getControllers(linkFn.directiveName, linkFn.require, $element, elementControllers), transcludeFn);
} catch (e) {
$exceptionHandler(e, startingTag($element));
}
@@ -6577,7 +6605,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
try {
linkFn = postLinkFns[i];
linkFn(linkFn.isolateScope ? isolateScope : scope, $element, attrs,
- linkFn.require && getControllers(linkFn.require, $element, elementControllers), transcludeFn);
+ linkFn.require && getControllers(linkFn.directiveName, linkFn.require, $element, elementControllers), transcludeFn);
} catch (e) {
$exceptionHandler(e, startingTag($element));
}
@@ -6663,7 +6691,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
// reapply the old attributes to the new element
forEach(dst, function(value, key) {
if (key.charAt(0) != '$') {
- if (src[key]) {
+ if (src[key] && src[key] !== value) {
value += (key === 'style' ? ';' : ' ') + src[key];
}
dst.$set(key, value, true, srcAttr[key]);
@@ -6716,7 +6744,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
if (jqLiteIsTextNode(content)) {
$template = [];
} else {
- $template = jqLite(content);
+ $template = jqLite(trim(content));
}
compileNode = $template[0];
@@ -6997,7 +7025,9 @@ function directiveNormalize(name) {
* element attributes. The values reflect current binding state `{{ }}`. The normalization is
* needed since all of these are treated as equivalent in Angular:
*
+ * ```
*
+ * ```
*/
/**
@@ -7011,7 +7041,7 @@ function directiveNormalize(name) {
/**
* @ngdoc method
* @name $compile.directive.Attributes#$set
- * @function
+ * @kind function
*
* @description
* Set DOM element attribute value.
@@ -7329,9 +7359,9 @@ function $HttpProvider() {
common: {
'Accept': 'application/json, text/plain, */*'
},
- post: copy(CONTENT_TYPE_APPLICATION_JSON),
- put: copy(CONTENT_TYPE_APPLICATION_JSON),
- patch: copy(CONTENT_TYPE_APPLICATION_JSON)
+ post: shallowCopy(CONTENT_TYPE_APPLICATION_JSON),
+ put: shallowCopy(CONTENT_TYPE_APPLICATION_JSON),
+ patch: shallowCopy(CONTENT_TYPE_APPLICATION_JSON)
},
xsrfCookieName: 'XSRF-TOKEN',
@@ -7573,14 +7603,14 @@ function $HttpProvider() {
*
* There are two kinds of interceptors (and two kinds of rejection interceptors):
*
- * * `request`: interceptors get called with http `config` object. The function is free to
- * modify the `config` or create a new one. The function needs to return the `config`
- * directly or as a promise.
+ * * `request`: interceptors get called with a http `config` object. The function is free to
+ * modify the `config` object or create a new one. The function needs to return the `config`
+ * object directly, or a promise containing the `config` or a new `config` object.
* * `requestError`: interceptor gets called when a previous interceptor threw an error or
* resolved with a rejection.
* * `response`: interceptors get called with http `response` object. The function is free to
- * modify the `response` or create a new one. The function needs to return the `response`
- * directly or as a promise.
+ * modify the `response` object or create a new one. The function needs to return the `response`
+ * object directly, or as a promise containing the `response` or a new `response` object.
* * `responseError`: interceptor gets called when a previous interceptor threw an error or
* resolved with a rejection.
*
@@ -7592,7 +7622,7 @@ function $HttpProvider() {
* // optional method
* 'request': function(config) {
* // do something on success
- * return config || $q.when(config);
+ * return config;
* },
*
* // optional method
@@ -7609,7 +7639,7 @@ function $HttpProvider() {
* // optional method
* 'response': function(response) {
* // do something on success
- * return response || $q.when(response);
+ * return response;
* },
*
* // optional method
@@ -7770,7 +7800,7 @@ function $HttpProvider() {
* caching.
* - **timeout** – `{number|Promise}` – timeout in milliseconds, or {@link ng.$q promise}
* that should abort the request when resolved.
- * - **withCredentials** - `{boolean}` - whether to to set the `withCredentials` flag on the
+ * - **withCredentials** - `{boolean}` - whether to set the `withCredentials` flag on the
* XHR object. See [requests with credentials]https://developer.mozilla.org/en/http_access_control#section_5
* for more information.
* - **responseType** - `{string}` - see
@@ -7808,11 +7838,11 @@ function $HttpProvider() {
http status code: {{status}}
@@ -8164,7 +8194,7 @@ function $HttpProvider() {
} else {
// serving from cache
if (isArray(cachedResp)) {
- resolvePromise(cachedResp[1], cachedResp[0], copy(cachedResp[2]), cachedResp[3]);
+ resolvePromise(cachedResp[1], cachedResp[0], shallowCopy(cachedResp[2]), cachedResp[3]);
} else {
resolvePromise(cachedResp, 200, {}, 'OK');
}
@@ -8303,16 +8333,13 @@ function createHttpBackend($browser, createXhr, $browserDefer, callbacks, rawDoc
var callbackId = '_' + (callbacks.counter++).toString(36);
callbacks[callbackId] = function(data) {
callbacks[callbackId].data = data;
+ callbacks[callbackId].called = true;
};
var jsonpDone = jsonpReq(url.replace('JSON_CALLBACK', 'angular.callbacks.' + callbackId),
- function() {
- if (callbacks[callbackId].data) {
- completeRequest(callback, 200, callbacks[callbackId].data);
- } else {
- completeRequest(callback, status || -2);
- }
- callbacks[callbackId] = angular.noop;
+ callbackId, function(status, text) {
+ completeRequest(callback, status, callbacks[callbackId].data, "", text);
+ callbacks[callbackId] = noop;
});
} else {
@@ -8414,34 +8441,52 @@ function createHttpBackend($browser, createXhr, $browserDefer, callbacks, rawDoc
}
};
- function jsonpReq(url, done) {
+ function jsonpReq(url, callbackId, done) {
// we can't use jQuery/jqLite here because jQuery does crazy shit with script elements, e.g.:
// - fetches local scripts via XHR and evals them
// - adds and immediately removes script elements from the document
- var script = rawDocument.createElement('script'),
- doneWrapper = function() {
- script.onreadystatechange = script.onload = script.onerror = null;
- rawDocument.body.removeChild(script);
- if (done) done();
- };
-
- script.type = 'text/javascript';
+ var script = rawDocument.createElement('script'), callback = null;
+ script.type = "text/javascript";
script.src = url;
+ script.async = true;
+
+ callback = function(event) {
+ removeEventListenerFn(script, "load", callback);
+ removeEventListenerFn(script, "error", callback);
+ rawDocument.body.removeChild(script);
+ script = null;
+ var status = -1;
+ var text = "unknown";
+
+ if (event) {
+ if (event.type === "load" && !callbacks[callbackId].called) {
+ event = { type: "error" };
+ }
+ text = event.type;
+ status = event.type === "error" ? 404 : 200;
+ }
+
+ if (done) {
+ done(status, text);
+ }
+ };
+
+ addEventListenerFn(script, "load", callback);
+ addEventListenerFn(script, "error", callback);
- if (msie && msie <= 8) {
+ if (msie <= 8) {
script.onreadystatechange = function() {
- if (/loaded|complete/.test(script.readyState)) {
- doneWrapper();
+ if (isString(script.readyState) && /loaded|complete/.test(script.readyState)) {
+ script.onreadystatechange = null;
+ callback({
+ type: 'load'
+ });
}
};
- } else {
- script.onload = script.onerror = function() {
- doneWrapper();
- };
}
rawDocument.body.appendChild(script);
- return doneWrapper;
+ return callback;
}
}
@@ -8450,7 +8495,7 @@ var $interpolateMinErr = minErr('$interpolate');
/**
* @ngdoc provider
* @name $interpolateProvider
- * @function
+ * @kind function
*
* @description
*
@@ -8468,7 +8513,7 @@ var $interpolateMinErr = minErr('$interpolate');
});
- customInterpolationApp.controller('DemoController', function DemoController() {
+ customInterpolationApp.controller('DemoController', function() {
this.label = "This binding is brought you by // interpolation symbols.";
});
@@ -8531,7 +8576,7 @@ function $InterpolateProvider() {
/**
* @ngdoc service
* @name $interpolate
- * @function
+ * @kind function
*
* @requires $parse
* @requires $sce
@@ -8623,10 +8668,24 @@ function $InterpolateProvider() {
} else {
part = $sce.valueOf(part);
}
- if (part === null || isUndefined(part)) {
+ if (part == null) { // null || undefined
part = '';
- } else if (typeof part != 'string') {
- part = toJson(part);
+ } else {
+ switch (typeof part) {
+ case 'string':
+ {
+ break;
+ }
+ case 'number':
+ {
+ part = '' + part;
+ break;
+ }
+ default:
+ {
+ part = toJson(part);
+ }
+ }
}
}
concat[i] = part;
@@ -9137,7 +9196,7 @@ function LocationHashbangUrl(appBase, hashPrefix) {
Matches paths for file protocol on windows,
such as /C:/foo/bar, and captures only /foo/bar.
*/
- var windowsFilePathExp = /^\/?.*?:(\/.*)/;
+ var windowsFilePathExp = /^\/[A-Z]:(\/.*)/;
var firstPathSegmentMatch;
@@ -9146,10 +9205,7 @@ function LocationHashbangUrl(appBase, hashPrefix) {
url = url.replace(base, '');
}
- /*
- * The input URL intentionally contains a
- * first path segment that ends with a colon.
- */
+ // The input URL intentionally contains a first path segment that ends with a colon.
if (windowsFilePathExp.exec(url)) {
return path;
}
@@ -9205,6 +9261,16 @@ function LocationHashbangInHtml5Url(appBase, hashPrefix) {
return appBaseNoFile;
}
};
+
+ this.$$compose = function() {
+ var search = toKeyValue(this.$$search),
+ hash = this.$$hash ? '#' + encodeUriSegment(this.$$hash) : '';
+
+ this.$$url = encodePath(this.$$path) + (search ? '?' + search : '') + hash;
+ // include hashPrefix in $$absUrl when $$url is empty so IE8 & 9 do not reload page because of removal of '#'
+ this.$$absUrl = appBase + hashPrefix + this.$$url;
+ };
+
}
@@ -9336,15 +9402,37 @@ LocationHashbangInHtml5Url.prototype =
*
* Change search part when called with parameter and return `$location`.
*
+ *
+ * ```js
+ * // given url http://example.com/#/some/path?foo=bar&baz=xoxo
+ * var searchObject = $location.search();
+ * // => {foo: 'bar', baz: 'xoxo'}
+ *
+ *
+ * // set foo to 'yipee'
+ * $location.search('foo', 'yipee');
+ * // => $location
+ * ```
+ *
* @param {string|Object.|Object.>} search New search params - string or
- * hash object. Hash object may contain an array of values, which will be decoded as duplicates in
- * the url.
+ * hash object.
*
- * @param {(string|Array)=} paramValue If `search` is a string, then `paramValue` will override only a
- * single search parameter. If `paramValue` is an array, it will set the parameter as a
- * comma-separated value. If `paramValue` is `null`, the parameter will be deleted.
+ * When called with a single argument the method acts as a setter, setting the `search` component
+ * of `$location` to the specified value.
*
- * @return {string} search
+ * If the argument is a hash object containing an array of values, these values will be encoded
+ * as duplicate search parameters in the url.
+ *
+ * @param {(string|Array)=} paramValue If `search` is a string, then `paramValue` will
+ * override only a single search property.
+ *
+ * If `paramValue` is an array, it will override the property of the `search` component of
+ * `$location` specified via the first argument.
+ *
+ * If `paramValue` is `null`, the property specified via the first argument will be deleted.
+ *
+ * @return {Object} If called with no arguments returns the parsed `search` object. If called with
+ * one or more arguments returns `$location` object itself.
*/
search: function(search, paramValue) {
switch (arguments.length) {
@@ -9557,6 +9645,39 @@ function $LocationProvider(){
absHref = urlResolve(absHref.animVal).href;
}
+ // Make relative links work in HTML5 mode for legacy browsers (or at least IE8 & 9)
+ // The href should be a regular url e.g. /link/somewhere or link/somewhere or ../somewhere or
+ // somewhere#anchor or http://example.com/somewhere
+ if (LocationMode === LocationHashbangInHtml5Url) {
+ // get the actual href attribute - see
+ // http://msdn.microsoft.com/en-us/library/ie/dd347148(v=vs.85).aspx
+ var href = elm.attr('href') || elm.attr('xlink:href');
+
+ if (href.indexOf('://') < 0) { // Ignore absolute URLs
+ var prefix = '#' + hashPrefix;
+ if (href[0] == '/') {
+ // absolute path - replace old path
+ absHref = appBase + prefix + href;
+ } else if (href[0] == '#') {
+ // local anchor
+ absHref = appBase + prefix + ($location.path() || '/') + href;
+ } else {
+ // relative path - join with current path
+ var stack = $location.path().split("/"),
+ parts = href.split("/");
+ for (var i=0; i Note that `$sce.RESOURCE_URL` makes a stronger statement about the URL than `$sce.URL` does and therefore contexts requiring values trusted for `$sce.RESOURCE_URL` can be used anywhere that values trusted for `$sce.URL` are required. |
@@ -13367,7 +13473,7 @@ function $SceDelegateProvider() {
* - `**`: matches zero or more occurrences of *any* character. As such, it's not
* not appropriate to use in for a scheme, domain, etc. as it would match too much. (e.g.
* http://**.example.com/ would match http://evil.com/?ignore=.example.com/ and that might
- * not have been the intention.) It's usage at the very end of the path is ok. (e.g.
+ * not have been the intention.) Its usage at the very end of the path is ok. (e.g.
* http://foo.example.com/templates/**).
* - **RegExp** (*see caveat below*)
* - *Caveat*: While regular expressions are powerful and offer great flexibility, their syntax
@@ -13488,7 +13594,7 @@ function $SceProvider() {
/**
* @ngdoc method
* @name $sceProvider#enabled
- * @function
+ * @kind function
*
* @param {boolean=} value If provided, then enables/disables SCE.
* @return {boolean} true if SCE is enabled, false otherwise.
@@ -13561,12 +13667,12 @@ function $SceProvider() {
'document. See http://docs.angularjs.org/api/ng.$sce for more information.');
}
- var sce = copy(SCE_CONTEXTS);
+ var sce = shallowCopy(SCE_CONTEXTS);
/**
* @ngdoc method
* @name $sce#isEnabled
- * @function
+ * @kind function
*
* @return {Boolean} true if SCE is enabled, false otherwise. If you want to set the value, you
* have to do it at module config time on {@link ng.$sceProvider $sceProvider}.
@@ -14101,7 +14207,7 @@ var originUrl = urlResolve(window.location.href, true);
* https://github.com/angular/angular.js/pull/2902
* http://james.padolsey.com/javascript/parsing-urls-with-the-dom/
*
- * @function
+ * @kind function
* @param {string} url The URL to be parsed.
* @description Normalizes and parses a URL.
* @returns {object} Returns the normalized URL as a dictionary.
@@ -14265,7 +14371,7 @@ function $WindowProvider(){
/**
* @ngdoc service
* @name $filter
- * @function
+ * @kind function
* @description
* Filters are used for formatting data displayed to the user.
*
@@ -14275,7 +14381,24 @@ function $WindowProvider(){
*
* @param {String} name Name of the filter function to retrieve
* @return {Function} the filter function
- */
+ * @example
+
+
+
+
{{ originalText }}
+
{{ filteredText }}
+
+
+
+
+ angular.module('filterExample', [])
+ .controller('MainCtrl', function($scope, $filter) {
+ $scope.originalText = 'hello';
+ $scope.filteredText = $filter('uppercase')($scope.originalText);
+ });
+
+
+ */
$FilterProvider.$inject = ['$provide'];
function $FilterProvider($provide) {
var suffix = 'Filter';
@@ -14335,7 +14458,7 @@ function $FilterProvider($provide) {
/**
* @ngdoc filter
* @name filter
- * @function
+ * @kind function
*
* @description
* Selects a subset of items from `array` and returns it as a new array.
@@ -14367,15 +14490,15 @@ function $FilterProvider($provide) {
*
* Can be one of:
*
- * - `function(actual, expected)`:
- * The function will be given the object value and the predicate value to compare and
- * should return true if the item should be included in filtered result.
+ * - `function(actual, expected)`:
+ * The function will be given the object value and the predicate value to compare and
+ * should return true if the item should be included in filtered result.
*
- * - `true`: A shorthand for `function(actual, expected) { return angular.equals(expected, actual)}`.
- * this is essentially strict comparison of expected and actual.
+ * - `true`: A shorthand for `function(actual, expected) { return angular.equals(expected, actual)}`.
+ * this is essentially strict comparison of expected and actual.
*
- * - `false|undefined`: A short hand for a function which will look for a substring match in case
- * insensitive way.
+ * - `false|undefined`: A short hand for a function which will look for a substring match in case
+ * insensitive way.
*
* @example
@@ -14554,7 +14677,7 @@ function filterFilter() {
/**
* @ngdoc filter
* @name currency
- * @function
+ * @kind function
*
* @description
* Formats a number as a currency (ie $1,234.56). When no currency symbol is provided, default
@@ -14611,7 +14734,7 @@ function currencyFilter($locale) {
/**
* @ngdoc filter
* @name number
- * @function
+ * @kind function
*
* @description
* Formats a number as text.
@@ -14696,8 +14819,8 @@ function formatNumber(number, pattern, groupSep, decimalSep, fractionSize) {
fractionSize = Math.min(Math.max(pattern.minFrac, fractionLen), pattern.maxFrac);
}
- var pow = Math.pow(10, fractionSize);
- number = Math.round(number * pow) / pow;
+ var pow = Math.pow(10, fractionSize + 1);
+ number = Math.floor(number * pow + 5) / pow;
var fraction = ('' + number).split(DECIMAL_SEP);
var whole = fraction[0];
fraction = fraction[1] || '';
@@ -14823,7 +14946,7 @@ var DATE_FORMATS_SPLIT = /((?:[^yMdHhmsaZE']+)|(?:'(?:[^']|'')*')|(?:E+|y+|M+|d+
/**
* @ngdoc filter
* @name date
- * @function
+ * @kind function
*
* @description
* Formats `date` to a string based on the requested `format`.
@@ -14980,7 +15103,7 @@ function dateFilter($locale) {
/**
* @ngdoc filter
* @name json
- * @function
+ * @kind function
*
* @description
* Allows you to convert a JavaScript object into JSON string.
@@ -15015,7 +15138,7 @@ function jsonFilter() {
/**
* @ngdoc filter
* @name lowercase
- * @function
+ * @kind function
* @description
* Converts string to lowercase.
* @see angular.lowercase
@@ -15026,7 +15149,7 @@ var lowercaseFilter = valueFn(lowercase);
/**
* @ngdoc filter
* @name uppercase
- * @function
+ * @kind function
* @description
* Converts string to uppercase.
* @see angular.uppercase
@@ -15036,7 +15159,7 @@ var uppercaseFilter = valueFn(uppercase);
/**
* @ngdoc filter
* @name limitTo
- * @function
+ * @kind function
*
* @description
* Creates a new array or string containing only a specified number of elements. The elements
@@ -15106,7 +15229,11 @@ function limitToFilter(){
return function(input, limit) {
if (!isArray(input) && !isString(input)) return input;
- limit = int(limit);
+ if (Math.abs(Number(limit)) === Infinity) {
+ limit = Number(limit);
+ } else {
+ limit = int(limit);
+ }
if (isString(input)) {
//NaN check on limit
@@ -15145,10 +15272,12 @@ function limitToFilter(){
/**
* @ngdoc filter
* @name orderBy
- * @function
+ * @kind function
*
* @description
- * Orders a specified `array` by the `expression` predicate.
+ * Orders a specified `array` by the `expression` predicate. It is ordered alphabetically
+ * for strings and numerically for numbers. Note: if you notice numbers are not being sorted
+ * correctly, make sure they are actually being saved as numbers and not strings.
*
* @param {Array} array The array to sort.
* @param {function(*)|string|Array.<(function(*)|string)>} expression A predicate to be
@@ -15201,6 +15330,51 @@ function limitToFilter(){
+ *
+ * It's also possible to call the orderBy filter manually, by injecting `$filter`, retrieving the
+ * filter routine with `$filter('orderBy')`, and calling the returned filter routine with the
+ * desired parameters.
+ *
+ * Example:
+ *
+ * @example
+
+
+
+
+
+
+ function Ctrl($scope, $filter) {
+ var orderBy = $filter('orderBy');
+ $scope.friends = [
+ { name: 'John', phone: '555-1212', age: 10 },
+ { name: 'Mary', phone: '555-9876', age: 19 },
+ { name: 'Mike', phone: '555-4321', age: 21 },
+ { name: 'Adam', phone: '555-5678', age: 35 },
+ { name: 'Julie', phone: '555-8765', age: 29 }
+ ];
+
+ $scope.order = function(predicate, reverse) {
+ $scope.friends = orderBy($scope.friends, predicate, reverse);
+ };
+ $scope.order('-age',false);
+ }
+
+
*/
orderByFilter.$inject = ['$parse'];
function orderByFilter($parse){
@@ -15752,7 +15926,7 @@ var nullFormCtrl = {
* - `url`
*
* @description
- * `FormController` keeps track of all its controls and nested forms as well as state of them,
+ * `FormController` keeps track of all its controls and nested forms as well as the state of them,
* such as being valid/invalid or dirty/pristine.
*
* Each {@link ng.directive:form form} directive creates an instance
@@ -16594,6 +16768,8 @@ function addNativeHtml5Validators(ctrl, validatorName, element) {
function textInputType(scope, element, attr, ctrl, $sniffer, $browser) {
var validity = element.prop('validity');
+ var placeholder = element[0].placeholder, noevent = {};
+
// In composition mode, users are still inputing intermediate text buffer,
// hold the listener until composition is done.
// More about composition events: https://developer.mozilla.org/en-US/docs/Web/API/CompositionEvent
@@ -16610,10 +16786,19 @@ function textInputType(scope, element, attr, ctrl, $sniffer, $browser) {
});
}
- var listener = function() {
+ var listener = function(ev) {
if (composing) return;
var value = element.val();
+ // IE (11 and under) seem to emit an 'input' event if the placeholder value changes.
+ // We don't want to dirty the value when this happens, so we abort here. Unfortunately,
+ // IE also sends input events for other non-input-related things, (such as focusing on a
+ // form control), so this change is not entirely enough to solve this.
+ if (msie && (ev || noevent).type === 'input' && element[0].placeholder !== placeholder) {
+ placeholder = element[0].placeholder;
+ return;
+ }
+
// By default we will trim the value
// If the attribute ng-trim exists we will avoid trimming
// e.g.
@@ -17026,14 +17211,14 @@ var VALID_CLASS = 'ng-valid',
* @property {Array.} $formatters Array of functions to execute, as a pipeline, whenever
the model value changes. Each function is called, in turn, passing the value through to the
next. Used to format / convert values for display in the control and validation.
- * ```js
- * function formatter(value) {
- * if (value) {
- * return value.toUpperCase();
- * }
- * }
- * ngModel.$formatters.push(formatter);
- * ```
+ * ```js
+ * function formatter(value) {
+ * if (value) {
+ * return value.toUpperCase();
+ * }
+ * }
+ * ngModel.$formatters.push(formatter);
+ * ```
*
* @property {Array.} $viewChangeListeners Array of functions to execute whenever the
* view value has changed. It is called with no arguments, and its return value is ignored.
@@ -17062,7 +17247,12 @@ var VALID_CLASS = 'ng-valid',
* Note that `contenteditable` is an HTML5 attribute, which tells the browser to let the element
* contents be edited in place by the user. This will not work on older browsers.
*
- *
+ * We are using the {@link ng.service:$sce $sce} service here and include the {@link ngSanitize $sanitize}
+ * module to automatically remove "bad" content like inline event listener (e.g. ``).
+ * However, as we are using `$sce` the model can still decide to to provide unsafe content if it marks
+ * that content using the `$sce` service.
+ *
+ *
[contenteditable] {
border: 1px solid black;
@@ -17076,8 +17266,8 @@ var VALID_CLASS = 'ng-valid',
- angular.module('customControl', []).
- directive('contenteditable', function() {
+ angular.module('customControl', ['ngSanitize']).
+ directive('contenteditable', ['$sce', function($sce) {
return {
restrict: 'A', // only activate on element attribute
require: '?ngModel', // get a hold of NgModelController
@@ -17086,7 +17276,7 @@ var VALID_CLASS = 'ng-valid',
// Specify how UI should be updated
ngModel.$render = function() {
- element.html(ngModel.$viewValue || '');
+ element.html($sce.getTrustedHtml(ngModel.$viewValue || ''));
};
// Listen for change events to enable binding
@@ -17107,7 +17297,7 @@ var VALID_CLASS = 'ng-valid',
}
}
};
- });
+ }]);
\ No newline at end of file
diff --git a/public/templates/activity-sub-comment.html b/public/templates/activity-sub-comment.html
new file mode 100644
index 000000000..525239771
--- /dev/null
+++ b/public/templates/activity-sub-comment.html
@@ -0,0 +1,15 @@
+