Angular xstatic for Version 1.3.7

Adding version 1.3.7

1.3.x angular is needed for launch instance work.

Partially Implements: blueprint launch-instance-redesign

Change-Id: Ib45130a624cace9139a5309470f70ed2983b8cb3
This commit is contained in:
Travis Tripp
2014-12-17 10:49:18 -07:00
parent 7300bd88ba
commit dac047df05
17 changed files with 26192 additions and 15994 deletions

View File

@@ -11,7 +11,7 @@ NAME = __name__.split('.')[-1] # package name (e.g. 'foo' or 'foo_bar')
# please use a all-lowercase valid python # please use a all-lowercase valid python
# package name # package name
VERSION = '1.2.16' # version of the packaged files, please use the upstream VERSION = '1.3.7' # version of the packaged files, please use the upstream
# version number # version number
BUILD = '0' # our package build number, so we can release new builds BUILD = '0' # our package build number, so we can release new builds
# with fixes for xstatic stuff. # with fixes for xstatic stuff.

File diff suppressed because it is too large Load Diff

332
xstatic/pkg/angular/data/angular-aria.js vendored Normal file
View File

@@ -0,0 +1,332 @@
/**
* @license AngularJS v1.3.7
* (c) 2010-2014 Google, Inc. http://angularjs.org
* License: MIT
*/
(function(window, angular, undefined) {'use strict';
/**
* @ngdoc module
* @name ngAria
* @description
*
* The `ngAria` module provides support for common
* [<abbr title="Accessible Rich Internet Applications">ARIA</abbr>](http://www.w3.org/TR/wai-aria/)
* attributes that convey state or semantic information about the application for users
* of assistive technologies, such as screen readers.
*
* <div doc-module-components="ngAria"></div>
*
* ## Usage
*
* For ngAria to do its magic, simply include the module as a dependency. The directives supported
* by ngAria are:
* `ngModel`, `ngDisabled`, `ngShow`, `ngHide`, `ngClick`, `ngDblClick`, and `ngMessages`.
*
* Below is a more detailed breakdown of the attributes handled by ngAria:
*
* | Directive | Supported Attributes |
* |---------------------------------------------|----------------------------------------------------------------------------------------|
* | {@link ng.directive:ngModel ngModel} | aria-checked, aria-valuemin, aria-valuemax, aria-valuenow, aria-invalid, aria-required |
* | {@link ng.directive:ngDisabled ngDisabled} | aria-disabled |
* | {@link ng.directive:ngShow ngShow} | aria-hidden |
* | {@link ng.directive:ngHide ngHide} | aria-hidden |
* | {@link ng.directive:ngClick ngClick} | tabindex, keypress event |
* | {@link ng.directive:ngDblclick ngDblclick} | tabindex |
* | {@link module:ngMessages ngMessages} | aria-live |
*
* Find out more information about each directive by reading the
* {@link guide/accessibility ngAria Developer Guide}.
*
* ##Example
* Using ngDisabled with ngAria:
* ```html
* <md-checkbox ng-disabled="disabled">
* ```
* Becomes:
* ```html
* <md-checkbox ng-disabled="disabled" aria-disabled="true">
* ```
*
* ##Disabling Attributes
* It's possible to disable individual attributes added by ngAria with the
* {@link ngAria.$ariaProvider#config config} method. For more details, see the
* {@link guide/accessibility Developer Guide}.
*/
/* global -ngAriaModule */
var ngAriaModule = angular.module('ngAria', ['ng']).
provider('$aria', $AriaProvider);
/**
* @ngdoc provider
* @name $ariaProvider
*
* @description
*
* Used for configuring the ARIA attributes injected and managed by ngAria.
*
* ```js
* angular.module('myApp', ['ngAria'], function config($ariaProvider) {
* $ariaProvider.config({
* ariaValue: true,
* tabindex: false
* });
* });
*```
*
* ## Dependencies
* Requires the {@link ngAria} module to be installed.
*
*/
function $AriaProvider() {
var config = {
ariaHidden: true,
ariaChecked: true,
ariaDisabled: true,
ariaRequired: true,
ariaInvalid: true,
ariaMultiline: true,
ariaValue: true,
tabindex: true,
bindKeypress: true
};
/**
* @ngdoc method
* @name $ariaProvider#config
*
* @param {object} config object to enable/disable specific ARIA attributes
*
* - **ariaHidden** `{boolean}` Enables/disables aria-hidden tags
* - **ariaChecked** `{boolean}` Enables/disables aria-checked tags
* - **ariaDisabled** `{boolean}` Enables/disables aria-disabled tags
* - **ariaRequired** `{boolean}` Enables/disables aria-required tags
* - **ariaInvalid** `{boolean}` Enables/disables aria-invalid tags
* - **ariaMultiline** `{boolean}` Enables/disables aria-multiline tags
* - **ariaValue** `{boolean}` Enables/disables aria-valuemin, aria-valuemax and aria-valuenow tags
* - **tabindex** `{boolean}` Enables/disables tabindex tags
* - **bindKeypress** `{boolean}` Enables/disables keypress event binding on ng-click
*
* @description
* Enables/disables various ARIA attributes
*/
this.config = function(newConfig) {
config = angular.extend(config, newConfig);
};
function watchExpr(attrName, ariaAttr, negate) {
return function(scope, elem, attr) {
var ariaCamelName = attr.$normalize(ariaAttr);
if (config[ariaCamelName] && !attr[ariaCamelName]) {
scope.$watch(attr[attrName], function(boolVal) {
if (negate) {
boolVal = !boolVal;
}
elem.attr(ariaAttr, boolVal);
});
}
};
}
/**
* @ngdoc service
* @name $aria
*
* @description
*
* The $aria service contains helper methods for applying common
* [ARIA](http://www.w3.org/TR/wai-aria/) attributes to HTML directives.
*
* ngAria injects common accessibility attributes that tell assistive technologies when HTML
* elements are enabled, selected, hidden, and more. To see how this is performed with ngAria,
* let's review a code snippet from ngAria itself:
*
*```js
* ngAriaModule.directive('ngDisabled', ['$aria', function($aria) {
* return $aria.$$watchExpr('ngDisabled', 'aria-disabled');
* }])
*```
* Shown above, the ngAria module creates a directive with the same signature as the
* traditional `ng-disabled` directive. But this ngAria version is dedicated to
* solely managing accessibility attributes. The internal `$aria` service is used to watch the
* boolean attribute `ngDisabled`. If it has not been explicitly set by the developer,
* `aria-disabled` is injected as an attribute with its value synchronized to the value in
* `ngDisabled`.
*
* Because ngAria hooks into the `ng-disabled` directive, developers do not have to do
* anything to enable this feature. The `aria-disabled` attribute is automatically managed
* simply as a silent side-effect of using `ng-disabled` with the ngAria module.
*
* The full list of directives that interface with ngAria:
* * **ngModel**
* * **ngShow**
* * **ngHide**
* * **ngClick**
* * **ngDblclick**
* * **ngMessages**
* * **ngDisabled**
*
* Read the {@link guide/accessibility ngAria Developer Guide} for a thorough explanation of each
* directive.
*
*
* ## Dependencies
* Requires the {@link ngAria} module to be installed.
*/
this.$get = function() {
return {
config: function(key) {
return config[key];
},
$$watchExpr: watchExpr
};
};
}
ngAriaModule.directive('ngShow', ['$aria', function($aria) {
return $aria.$$watchExpr('ngShow', 'aria-hidden', true);
}])
.directive('ngHide', ['$aria', function($aria) {
return $aria.$$watchExpr('ngHide', 'aria-hidden', false);
}])
.directive('ngModel', ['$aria', function($aria) {
function shouldAttachAttr(attr, normalizedAttr, elem) {
return $aria.config(normalizedAttr) && !elem.attr(attr);
}
function getShape(attr, elem) {
var type = attr.type,
role = attr.role;
return ((type || role) === 'checkbox' || role === 'menuitemcheckbox') ? 'checkbox' :
((type || role) === 'radio' || role === 'menuitemradio') ? 'radio' :
(type === 'range' || role === 'progressbar' || role === 'slider') ? 'range' :
(type || role) === 'textbox' || elem[0].nodeName === 'TEXTAREA' ? 'multiline' : '';
}
return {
restrict: 'A',
require: '?ngModel',
link: function(scope, elem, attr, ngModel) {
var shape = getShape(attr, elem);
var needsTabIndex = shouldAttachAttr('tabindex', 'tabindex', elem);
function ngAriaWatchModelValue() {
return ngModel.$modelValue;
}
function getRadioReaction() {
if (needsTabIndex) {
needsTabIndex = false;
return function ngAriaRadioReaction(newVal) {
var boolVal = newVal === attr.value;
elem.attr('aria-checked', boolVal);
elem.attr('tabindex', 0 - !boolVal);
};
} else {
return function ngAriaRadioReaction(newVal) {
elem.attr('aria-checked', newVal === attr.value);
};
}
}
function ngAriaCheckboxReaction(newVal) {
elem.attr('aria-checked', !!newVal);
}
switch (shape) {
case 'radio':
case 'checkbox':
if (shouldAttachAttr('aria-checked', 'ariaChecked', elem)) {
scope.$watch(ngAriaWatchModelValue, shape === 'radio' ?
getRadioReaction() : ngAriaCheckboxReaction);
}
break;
case 'range':
if ($aria.config('ariaValue')) {
if (attr.min && !elem.attr('aria-valuemin')) {
elem.attr('aria-valuemin', attr.min);
}
if (attr.max && !elem.attr('aria-valuemax')) {
elem.attr('aria-valuemax', attr.max);
}
if (!elem.attr('aria-valuenow')) {
scope.$watch(ngAriaWatchModelValue, function ngAriaValueNowReaction(newVal) {
elem.attr('aria-valuenow', newVal);
});
}
}
break;
case 'multiline':
if (shouldAttachAttr('aria-multiline', 'ariaMultiline', elem)) {
elem.attr('aria-multiline', true);
}
break;
}
if (needsTabIndex) {
elem.attr('tabindex', 0);
}
if (ngModel.$validators.required && shouldAttachAttr('aria-required', 'ariaRequired', elem)) {
scope.$watch(function ngAriaRequiredWatch() {
return ngModel.$error.required;
}, function ngAriaRequiredReaction(newVal) {
elem.attr('aria-required', !!newVal);
});
}
if (shouldAttachAttr('aria-invalid', 'ariaInvalid', elem)) {
scope.$watch(function ngAriaInvalidWatch() {
return ngModel.$invalid;
}, function ngAriaInvalidReaction(newVal) {
elem.attr('aria-invalid', !!newVal);
});
}
}
};
}])
.directive('ngDisabled', ['$aria', function($aria) {
return $aria.$$watchExpr('ngDisabled', 'aria-disabled');
}])
.directive('ngMessages', function() {
return {
restrict: 'A',
require: '?ngMessages',
link: function(scope, elem, attr, ngMessages) {
if (!elem.attr('aria-live')) {
elem.attr('aria-live', 'assertive');
}
}
};
})
.directive('ngClick',['$aria', function($aria) {
return {
restrict: 'A',
link: function(scope, elem, attr) {
if ($aria.config('tabindex') && !elem.attr('tabindex')) {
elem.attr('tabindex', 0);
}
if ($aria.config('bindKeypress') && !elem.attr('ng-keypress')) {
elem.on('keypress', function(event) {
if (event.keyCode === 32 || event.keyCode === 13) {
scope.$eval(attr.ngClick);
}
});
}
}
};
}])
.directive('ngDblclick', ['$aria', function($aria) {
return function(scope, elem, attr) {
if ($aria.config('tabindex') && !elem.attr('tabindex')) {
elem.attr('tabindex', 0);
}
};
}]);
})(window, window.angular);

View File

@@ -1,5 +1,5 @@
/** /**
* @license AngularJS v1.2.16 * @license AngularJS v1.3.7
* (c) 2010-2014 Google, Inc. http://angularjs.org * (c) 2010-2014 Google, Inc. http://angularjs.org
* License: MIT * License: MIT
*/ */
@@ -37,20 +37,18 @@ angular.module('ngCookies', ['ng']).
* Requires the {@link ngCookies `ngCookies`} module to be installed. * Requires the {@link ngCookies `ngCookies`} module to be installed.
* *
* @example * @example
<example> *
<file name="index.html"> * ```js
<script> * angular.module('cookiesExample', ['ngCookies'])
function ExampleController($cookies) { * .controller('ExampleController', ['$cookies', function($cookies) {
// Retrieving a cookie * // Retrieving a cookie
var favoriteCookie = $cookies.myFavorite; * var favoriteCookie = $cookies.myFavorite;
// Setting a cookie * // Setting a cookie
$cookies.myFavorite = 'oatmeal'; * $cookies.myFavorite = 'oatmeal';
} * }]);
</script> * ```
</file>
</example>
*/ */
factory('$cookies', ['$rootScope', '$browser', function ($rootScope, $browser) { factory('$cookies', ['$rootScope', '$browser', function($rootScope, $browser) {
var cookies = {}, var cookies = {},
lastCookies = {}, lastCookies = {},
lastBrowserCookies, lastBrowserCookies,
@@ -97,7 +95,7 @@ angular.module('ngCookies', ['ng']).
} }
//update all cookies updated in $cookies //update all cookies updated in $cookies
for(name in cookies) { for (name in cookies) {
value = cookies[name]; value = cookies[name];
if (!angular.isString(value)) { if (!angular.isString(value)) {
value = '' + value; value = '' + value;
@@ -110,7 +108,7 @@ angular.module('ngCookies', ['ng']).
} }
//verify what was actually stored //verify what was actually stored
if (updated){ if (updated) {
updated = false; updated = false;
browserCookies = $browser.cookies(); browserCookies = $browser.cookies();
@@ -143,6 +141,18 @@ angular.module('ngCookies', ['ng']).
* Requires the {@link ngCookies `ngCookies`} module to be installed. * Requires the {@link ngCookies `ngCookies`} module to be installed.
* *
* @example * @example
*
* ```js
* angular.module('cookieStoreExample', ['ngCookies'])
* .controller('ExampleController', ['$cookieStore', function($cookieStore) {
* // Put cookie
* $cookieStore.put('myFavorite','oatmeal');
* // Get cookie
* var favoriteCookie = $cookieStore.get('myFavorite');
* // Removing a cookie
* $cookieStore.remove('myFavorite');
* }]);
* ```
*/ */
factory('$cookieStore', ['$cookies', function($cookies) { factory('$cookieStore', ['$cookies', function($cookies) {

View File

@@ -4,15 +4,10 @@
[ng\:cloak], [ng-cloak], [data-ng-cloak], [x-ng-cloak], [ng\:cloak], [ng-cloak], [data-ng-cloak], [x-ng-cloak],
.ng-cloak, .x-ng-cloak, .ng-cloak, .x-ng-cloak,
.ng-hide { .ng-hide:not(.ng-hide-animate) {
display: none !important; display: none !important;
} }
ng\:form { ng\:form {
display: block; display: block;
} }
.ng-animate-block-transitions {
transition:0s all!important;
-webkit-transition:0s all!important;
}

View File

@@ -1,5 +1,5 @@
/** /**
* @license AngularJS v1.2.16 * @license AngularJS v1.3.7
* (c) 2010-2014 Google, Inc. http://angularjs.org * (c) 2010-2014 Google, Inc. http://angularjs.org
* License: MIT * License: MIT
*/ */
@@ -31,52 +31,37 @@
* should all be static strings, not variables or general expressions. * should all be static strings, not variables or general expressions.
* *
* @param {string} module The namespace to use for the new minErr instance. * @param {string} module The namespace to use for the new minErr instance.
* @param {function} ErrorConstructor Custom error constructor to be instantiated when returning
* error from returned function, for cases when a particular type of error is useful.
* @returns {function(code:string, template:string, ...templateArgs): Error} minErr instance * @returns {function(code:string, template:string, ...templateArgs): Error} minErr instance
*/ */
function minErr(module) { function minErr(module, ErrorConstructor) {
return function () { ErrorConstructor = ErrorConstructor || Error;
return function() {
var code = arguments[0], var code = arguments[0],
prefix = '[' + (module ? module + ':' : '') + code + '] ', prefix = '[' + (module ? module + ':' : '') + code + '] ',
template = arguments[1], template = arguments[1],
templateArgs = arguments, templateArgs = arguments,
stringify = function (obj) {
if (typeof obj === 'function') {
return obj.toString().replace(/ \{[\s\S]*$/, '');
} else if (typeof obj === 'undefined') {
return 'undefined';
} else if (typeof obj !== 'string') {
return JSON.stringify(obj);
}
return obj;
},
message, i; message, i;
message = prefix + template.replace(/\{\d+\}/g, function (match) { message = prefix + template.replace(/\{\d+\}/g, function(match) {
var index = +match.slice(1, -1), arg; var index = +match.slice(1, -1), arg;
if (index + 2 < templateArgs.length) { if (index + 2 < templateArgs.length) {
arg = templateArgs[index + 2]; return toDebugString(templateArgs[index + 2]);
if (typeof arg === 'function') {
return arg.toString().replace(/ ?\{[\s\S]*$/, '');
} else if (typeof arg === 'undefined') {
return 'undefined';
} else if (typeof arg !== 'string') {
return toJson(arg);
}
return arg;
} }
return match; return match;
}); });
message = message + '\nhttp://errors.angularjs.org/1.2.16/' + message = message + '\nhttp://errors.angularjs.org/1.3.7/' +
(module ? module + '/' : '') + code; (module ? module + '/' : '') + code;
for (i = 2; i < arguments.length; i++) { for (i = 2; i < arguments.length; i++) {
message = message + (i == 2 ? '?' : '&') + 'p' + (i-2) + '=' + message = message + (i == 2 ? '?' : '&') + 'p' + (i - 2) + '=' +
encodeURIComponent(stringify(arguments[i])); encodeURIComponent(toDebugString(arguments[i]));
} }
return new ErrorConstructor(message);
return new Error(message);
}; };
} }
@@ -124,7 +109,7 @@ function setupModuleLoader(window) {
* *
* # Module * # Module
* *
* A module is a collection of services, directives, filters, and configuration information. * A module is a collection of services, directives, controllers, filters, and configuration information.
* `angular.module` is used to configure the {@link auto.$injector $injector}. * `angular.module` is used to configure the {@link auto.$injector $injector}.
* *
* ```js * ```js
@@ -152,9 +137,9 @@ function setupModuleLoader(window) {
* {@link angular.bootstrap} to simplify this process for you. * {@link angular.bootstrap} to simplify this process for you.
* *
* @param {!string} name The name of the module to create or retrieve. * @param {!string} name The name of the module to create or retrieve.
<<<<<* @param {!Array.<string>=} requires If specified then new module is being created. If * @param {!Array.<string>=} requires If specified then new module is being created. If
>>>>>* unspecified then the module is being retrieved for further configuration. * unspecified then the module is being retrieved for further configuration.
* @param {Function} configFn Optional configuration function for the module. Same as * @param {Function=} configFn Optional configuration function for the module. Same as
* {@link angular.Module#config Module#config()}. * {@link angular.Module#config Module#config()}.
* @returns {module} new module with the {@link angular.Module} api. * @returns {module} new module with the {@link angular.Module} api.
*/ */
@@ -179,22 +164,26 @@ function setupModuleLoader(window) {
/** @type {!Array.<Array.<*>>} */ /** @type {!Array.<Array.<*>>} */
var invokeQueue = []; var invokeQueue = [];
/** @type {!Array.<Function>} */
var configBlocks = [];
/** @type {!Array.<Function>} */ /** @type {!Array.<Function>} */
var runBlocks = []; var runBlocks = [];
var config = invokeLater('$injector', 'invoke'); var config = invokeLater('$injector', 'invoke', 'push', configBlocks);
/** @type {angular.Module} */ /** @type {angular.Module} */
var moduleInstance = { var moduleInstance = {
// Private state // Private state
_invokeQueue: invokeQueue, _invokeQueue: invokeQueue,
_configBlocks: configBlocks,
_runBlocks: runBlocks, _runBlocks: runBlocks,
/** /**
* @ngdoc property * @ngdoc property
* @name angular.Module#requires * @name angular.Module#requires
* @module ng * @module ng
* @returns {Array.<string>} List of module names which must be loaded before this module. *
* @description * @description
* Holds the list of modules which the injector will load before the current module is * Holds the list of modules which the injector will load before the current module is
* loaded. * loaded.
@@ -205,8 +194,9 @@ function setupModuleLoader(window) {
* @ngdoc property * @ngdoc property
* @name angular.Module#name * @name angular.Module#name
* @module ng * @module ng
* @returns {string} Name of the module. *
* @description * @description
* Name of the module.
*/ */
name: name, name: name,
@@ -297,7 +287,7 @@ function setupModuleLoader(window) {
* }) * })
* ``` * ```
* *
* See {@link ngAnimate.$animateProvider#register $animateProvider.register()} and * See {@link ng.$animateProvider#register $animateProvider.register()} and
* {@link ngAnimate ngAnimate module} for more information. * {@link ngAnimate ngAnimate module} for more information.
*/ */
animation: invokeLater('$animateProvider', 'register'), animation: invokeLater('$animateProvider', 'register'),
@@ -346,6 +336,8 @@ function setupModuleLoader(window) {
* configuration. * configuration.
* @description * @description
* Use this method to register work which needs to be performed on module loading. * Use this method to register work which needs to be performed on module loading.
* For more about how to configure services, see
* {@link providers#provider-recipe Provider Recipe}.
*/ */
config: config, config: config,
@@ -369,7 +361,7 @@ function setupModuleLoader(window) {
config(configFn); config(configFn);
} }
return moduleInstance; return moduleInstance;
/** /**
* @param {string} provider * @param {string} provider
@@ -377,9 +369,10 @@ function setupModuleLoader(window) {
* @param {String=} insertMethod * @param {String=} insertMethod
* @returns {angular.Module} * @returns {angular.Module}
*/ */
function invokeLater(provider, method, insertMethod) { function invokeLater(provider, method, insertMethod, queue) {
if (!queue) queue = invokeQueue;
return function() { return function() {
invokeQueue[insertMethod || 'push']([provider, method, arguments]); queue[insertMethod || 'push']([provider, method, arguments]);
return moduleInstance; return moduleInstance;
}; };
} }

View File

@@ -0,0 +1,400 @@
/**
* @license AngularJS v1.3.7
* (c) 2010-2014 Google, Inc. http://angularjs.org
* License: MIT
*/
(function(window, angular, undefined) {'use strict';
/**
* @ngdoc module
* @name ngMessages
* @description
*
* The `ngMessages` module provides enhanced support for displaying messages within templates
* (typically within forms or when rendering message objects that return key/value data).
* Instead of relying on JavaScript code and/or complex ng-if statements within your form template to
* show and hide error messages specific to the state of an input field, the `ngMessages` and
* `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.
*
* # Usage
* The `ngMessages` directive listens on a key/value collection which is set on the ngMessages attribute.
* Since the {@link ngModel ngModel} directive exposes an `$error` object, this error object can be
* used with `ngMessages` to display control error messages in an easier way than with just regular angular
* template directives.
*
* ```html
* <form name="myForm">
* <input type="text" ng-model="field" name="myField" required minlength="5" />
* <div ng-messages="myForm.myField.$error">
* <div ng-message="required">You did not enter a field</div>
* <div ng-message="minlength">The value entered is too short</div>
* </div>
* </form>
* ```
*
* Now whatever key/value entries are present within the provided object (in this case `$error`) then
* the ngMessages directive will render the inner first ngMessage directive (depending if the key values
* match the attribute value present on each ngMessage directive). In other words, if your errors
* object contains the following data:
*
* ```javascript
* <!-- keep in mind that ngModel automatically sets these error flags -->
* myField.$error = { minlength : true, required : false };
* ```
*
* Then the `required` message will be displayed first. When required is false then the `minlength` message
* will be displayed right after (since these messages are ordered this way in the template HTML code).
* The prioritization of each message is determined by what order they're present in the DOM.
* Therefore, instead of having custom JavaScript code determine the priority of what errors are
* present before others, the presentation of the errors are handled within the template.
*
* By default, ngMessages will only display one error at a time. However, if you wish to display all
* messages then the `ng-messages-multiple` attribute flag can be used on the element containing the
* ngMessages directive to make this happen.
*
* ```html
* <div ng-messages="myForm.myField.$error" ng-messages-multiple>...</div>
* ```
*
* ## Reusing and Overriding Messages
* In addition to prioritization, ngMessages also allows for including messages from a remote or an inline
* template. This allows for generic collection of messages to be reused across multiple parts of an
* application.
*
* ```html
* <script type="text/ng-template" id="error-messages">
* <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>
* ```
*
* 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.
*
* ```html
* <!-- a generic template of error messages known as "my-custom-messages" -->
* <script type="text/ng-template" id="my-custom-messages">
* <div ng-message="required">This field is required</div>
* <div ng-message="minlength">This field is too short</div>
* </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">
* <!-- 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>
* </div>
* </form>
* ```
*
* In the example HTML code above the message that is set on required will override the corresponding
* required message defined within the remote template. Therefore, with particular input fields (such
* 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.
*
* ## 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.
*
* 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
* 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-message="alert" class="some-message">...</div>
* <div ng-message="fail" class="some-message">...</div>
* </div>
* ```
*
* Then the CSS animation code for the message container looks like so:
*
* ```css
* .my-messages {
* transition:1s linear all;
* }
* .my-messages.ng-active {
* // messages are visible
* }
* .my-messages.ng-inactive {
* // messages are hidden
* }
* ```
*
* Whenever an inner message is attached (becomes visible) or removed (becomes hidden) then the enter
* and leave animation is triggered for each particular element bound to the `ngMessage` directive.
*
* Therefore, the CSS code for the inner messages looks like so:
*
* ```css
* .some-message {
* transition:1s linear all;
* }
*
* .some-message.ng-enter {}
* .some-message.ng-enter.ng-enter-active {}
*
* .some-message.ng-leave {}
* .some-message.ng-leave.ng-leave-active {}
* ```
*
* {@link ngAnimate Click here} to learn how to use JavaScript animations or to learn more about ngAnimate.
*/
angular.module('ngMessages', [])
/**
* @ngdoc directive
* @module ngMessages
* @name ngMessages
* @restrict AE
*
* @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
* 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.)
*
* A remote template can also be used to promote message reuseability and messages can also be
* overridden.
*
* {@link module:ngMessages Click here} to learn more about `ngMessages` and `ngMessage`.
*
* @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>
*
* <!-- 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>
* ```
*
* @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"
* deps="angular-messages.js"
* 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 />
*
* <pre>myForm.myName.$error = {{ myForm.myName.$error | json }}</pre>
*
* <div ng-messages="myForm.myName.$error" style="color:maroon">
* <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>
* </div>
* </form>
* </file>
* <file name="script.js">
* angular.module('ngMessagesExample', ['ngMessages']);
* </file>
* </example>
*/
.directive('ngMessages', ['$compile', '$animate', '$templateRequest',
function($compile, $animate, $templateRequest) {
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)
};
this.renderMessages = function(values, multiple) {
values = values || {};
var found;
angular.forEach(messages, function(message) {
if ((!found || multiple) && truthyVal(values[message.type])) {
message.attach();
found = true;
} else {
message.detach();
}
});
this.renderElementClasses(found);
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);
};
//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 cachedValues, watchAttr = $attrs.ngMessages || $attrs['for']; //for is a reserved keyword
$scope.$watchCollection(watchAttr, function(values) {
cachedValues = values;
ctrl.renderMessages(values, multiple);
});
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);
});
}
}
};
}])
/**
* @ngdoc directive
* @name ngMessage
* @restrict AE
* @scope
*
* @description
* `ngMessage` is a directive with the purpose to show and hide a particular message.
* For `ngMessage` 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>
*
* <!-- 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>
* ```
*
* {@link module:ngMessages Click here} to learn more about `ngMessages` and `ngMessage`.
*
* @param {string} ngMessage a string value corresponding to the message key.
*/
.directive('ngMessage', ['$animate', function($animate) {
var COMMENT_NODE = 8;
return {
require: '^ngMessages',
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++;
}
}
ngMessages.registerMessage(index, {
type: $attrs.ngMessage || $attrs.when,
attach: function() {
if (!element) {
$transclude($scope, function(clone) {
$animate.enter(clone, null, $element);
element = clone;
});
}
},
detach: function(now) {
if (element) {
$animate.leave(element);
element = null;
}
}
});
}
};
}]);
})(window, window.angular);

File diff suppressed because it is too large Load Diff

View File

@@ -1,5 +1,5 @@
/** /**
* @license AngularJS v1.2.16 * @license AngularJS v1.3.7
* (c) 2010-2014 Google, Inc. http://angularjs.org * (c) 2010-2014 Google, Inc. http://angularjs.org
* License: MIT * License: MIT
*/ */
@@ -35,7 +35,7 @@ function lookupDottedPath(obj, path) {
function shallowClearAndCopy(src, dst) { function shallowClearAndCopy(src, dst) {
dst = dst || {}; dst = dst || {};
angular.forEach(dst, function(value, key){ angular.forEach(dst, function(value, key) {
delete dst[key]; delete dst[key];
}); });
@@ -78,6 +78,18 @@ function shallowClearAndCopy(src, dst) {
* *
* Requires the {@link ngResource `ngResource`} module to be installed. * Requires the {@link ngResource `ngResource`} module to be installed.
* *
* By default, trailing slashes will be stripped from the calculated URLs,
* which can pose problems with server backends that do not expect that
* behavior. This can be disabled by configuring the `$resourceProvider` like
* this:
*
* ```js
app.config(['$resourceProvider', function($resourceProvider) {
// Don't strip trailing slashes from calculated URLs
$resourceProvider.defaults.stripTrailingSlashes = false;
}]);
* ```
*
* @param {string} url A parametrized URL template with parameters prefixed by `:` as in * @param {string} url A parametrized URL template with parameters prefixed by `:` as in
* `/user/:username`. If you are using a URL with a port number (e.g. * `/user/:username`. If you are using a URL with a port number (e.g.
* `http://example.com:8080/api`), it will be respected. * `http://example.com:8080/api`), it will be respected.
@@ -99,12 +111,14 @@ function shallowClearAndCopy(src, dst) {
* Given a template `/path/:verb` and parameter `{verb:'greet', salutation:'Hello'}` results in * Given a template `/path/:verb` and parameter `{verb:'greet', salutation:'Hello'}` results in
* URL `/path/greet?salutation=Hello`. * URL `/path/greet?salutation=Hello`.
* *
* If the parameter value is prefixed with `@` then the value of that parameter is extracted from * If the parameter value is prefixed with `@` then the value for that parameter will be extracted
* the data object (useful for non-GET operations). * from the corresponding property on the `data` object (provided when calling an action method). For
* example, if the `defaultParam` object is `{someParam: '@someProp'}` then the value of `someParam`
* will be `data.someProp`.
* *
* @param {Object.<Object>=} actions Hash with declaration of custom action that should extend * @param {Object.<Object>=} actions Hash with declaration of custom actions that should extend
* the default set of resource actions. The declaration should be created in the format of {@link * the default set of resource actions. The declaration should be created in the format of {@link
* ng.$http#usage_parameters $http.config}: * ng.$http#usage $http.config}:
* *
* {action1: {method:?, params:?, isArray:?, headers:?, ...}, * {action1: {method:?, params:?, isArray:?, headers:?, ...},
* action2: {method:?, params:?, isArray:?, headers:?, ...}, * action2: {method:?, params:?, isArray:?, headers:?, ...},
@@ -114,8 +128,8 @@ function shallowClearAndCopy(src, dst) {
* *
* - **`action`** {string} The name of action. This name becomes the name of the method on * - **`action`** {string} The name of action. This name becomes the name of the method on
* your resource object. * your resource object.
* - **`method`** {string} HTTP request method. Valid methods are: `GET`, `POST`, `PUT`, * - **`method`** {string} Case insensitive HTTP method (e.g. `GET`, `POST`, `PUT`,
* `DELETE`, and `JSONP`. * `DELETE`, `JSONP`, etc).
* - **`params`** {Object=} Optional set of pre-bound parameters for this action. If any of * - **`params`** {Object=} Optional set of pre-bound parameters for this action. If any of
* the parameter value is a function, it will be executed every time when a param value needs to * the parameter value is a function, it will be executed every time when a param value needs to
* be obtained for a request (unless the param was overridden). * be obtained for a request (unless the param was overridden).
@@ -127,10 +141,16 @@ function shallowClearAndCopy(src, dst) {
* `{function(data, headersGetter)|Array.<function(data, headersGetter)>}` * `{function(data, headersGetter)|Array.<function(data, headersGetter)>}`
* transform function or an array of such functions. The transform function takes the http * transform function or an array of such functions. The transform function takes the http
* request body and headers and returns its transformed (typically serialized) version. * request body and headers and returns its transformed (typically serialized) version.
* By default, transformRequest will contain one function that checks if the request data is
* an object and serializes to using `angular.toJson`. To prevent this behavior, set
* `transformRequest` to an empty array: `transformRequest: []`
* - **`transformResponse`** * - **`transformResponse`**
* `{function(data, headersGetter)|Array.<function(data, headersGetter)>}` * `{function(data, headersGetter)|Array.<function(data, headersGetter)>}`
* transform function or an array of such functions. The transform function takes the http * transform function or an array of such functions. The transform function takes the http
* response body and headers and returns its transformed (typically deserialized) version. * response body and headers and returns its transformed (typically deserialized) version.
* By default, transformResponse will contain one function that checks if the response looks like
* a JSON string and deserializes it using `angular.fromJson`. To prevent this behavior, set
* `transformResponse` to an empty array: `transformResponse: []`
* - **`cache`** `{boolean|Cache}` If true, a default $http cache will be used to cache the * - **`cache`** `{boolean|Cache}` If true, a default $http cache will be used to cache the
* GET request, otherwise if a cache instance built with * GET request, otherwise if a cache instance built with
* {@link ng.$cacheFactory $cacheFactory}, this cache will be used for * {@link ng.$cacheFactory $cacheFactory}, this cache will be used for
@@ -147,6 +167,14 @@ function shallowClearAndCopy(src, dst) {
* `response` and `responseError`. Both `response` and `responseError` interceptors get called * `response` and `responseError`. Both `response` and `responseError` interceptors get called
* with `http response` object. See {@link ng.$http $http interceptors}. * with `http response` object. See {@link ng.$http $http interceptors}.
* *
* @param {Object} options Hash with custom settings that should extend the
* default `$resourceProvider` behavior. The only supported option is
*
* Where:
*
* - **`stripTrailingSlashes`** {boolean} If true then the trailing
* slashes from any calculated URL will be stripped. (Defaults to true.)
*
* @returns {Object} A resource "class" object with methods for the default set of resource actions * @returns {Object} A resource "class" object with methods for the default set of resource actions
* optionally extended with custom `actions`. The default set contains these actions: * optionally extended with custom `actions`. The default set contains these actions:
* ```js * ```js
@@ -295,20 +323,20 @@ function shallowClearAndCopy(src, dst) {
* # Creating a custom 'PUT' request * # Creating a custom 'PUT' request
* In this example we create a custom method on our resource to make a PUT request * In this example we create a custom method on our resource to make a PUT request
* ```js * ```js
* var app = angular.module('app', ['ngResource', 'ngRoute']); * var app = angular.module('app', ['ngResource', 'ngRoute']);
* *
* // Some APIs expect a PUT request in the format URL/object/ID * // Some APIs expect a PUT request in the format URL/object/ID
* // Here we are creating an 'update' method * // Here we are creating an 'update' method
* app.factory('Notes', ['$resource', function($resource) { * app.factory('Notes', ['$resource', function($resource) {
* return $resource('/notes/:id', null, * return $resource('/notes/:id', null,
* { * {
* 'update': { method:'PUT' } * 'update': { method:'PUT' }
* }); * });
* }]); * }]);
* *
* // In our controller we get the ID from the URL using ngRoute and $routeParams * // In our controller we get the ID from the URL using ngRoute and $routeParams
* // We pass in $routeParams and our Notes factory along with $scope * // We pass in $routeParams and our Notes factory along with $scope
* app.controller('NotesCtrl', ['$scope', '$routeParams', 'Notes', * app.controller('NotesCtrl', ['$scope', '$routeParams', 'Notes',
function($scope, $routeParams, Notes) { function($scope, $routeParams, Notes) {
* // First get a note object from the factory * // First get a note object from the factory
* var note = Notes.get({ id:$routeParams.id }); * var note = Notes.get({ id:$routeParams.id });
@@ -318,293 +346,322 @@ function shallowClearAndCopy(src, dst) {
* Notes.update({ id:$id }, note); * Notes.update({ id:$id }, note);
* *
* // This will PUT /notes/ID with the note object in the request payload * // This will PUT /notes/ID with the note object in the request payload
* }]); * }]);
* ``` * ```
*/ */
angular.module('ngResource', ['ng']). angular.module('ngResource', ['ng']).
factory('$resource', ['$http', '$q', function($http, $q) { provider('$resource', function() {
var provider = this;
var DEFAULT_ACTIONS = { this.defaults = {
'get': {method:'GET'}, // Strip slashes by default
'save': {method:'POST'}, stripTrailingSlashes: true,
'query': {method:'GET', isArray:true},
'remove': {method:'DELETE'}, // Default actions configuration
'delete': {method:'DELETE'} actions: {
'get': {method: 'GET'},
'save': {method: 'POST'},
'query': {method: 'GET', isArray: true},
'remove': {method: 'DELETE'},
'delete': {method: 'DELETE'}
}
}; };
var noop = angular.noop,
this.$get = ['$http', '$q', function($http, $q) {
var noop = angular.noop,
forEach = angular.forEach, forEach = angular.forEach,
extend = angular.extend, extend = angular.extend,
copy = angular.copy, copy = angular.copy,
isFunction = angular.isFunction; isFunction = angular.isFunction;
/** /**
* We need our custom method because encodeURIComponent is too aggressive and doesn't follow * We need our custom method because encodeURIComponent is too aggressive and doesn't follow
* http://www.ietf.org/rfc/rfc3986.txt with regards to the character set (pchar) allowed in path * http://www.ietf.org/rfc/rfc3986.txt with regards to the character set
* segments: * (pchar) allowed in path segments:
* segment = *pchar * segment = *pchar
* pchar = unreserved / pct-encoded / sub-delims / ":" / "@" * pchar = unreserved / pct-encoded / sub-delims / ":" / "@"
* pct-encoded = "%" HEXDIG HEXDIG * pct-encoded = "%" HEXDIG HEXDIG
* unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~" * unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~"
* sub-delims = "!" / "$" / "&" / "'" / "(" / ")" * sub-delims = "!" / "$" / "&" / "'" / "(" / ")"
* / "*" / "+" / "," / ";" / "=" * / "*" / "+" / "," / ";" / "="
*/ */
function encodeUriSegment(val) { function encodeUriSegment(val) {
return encodeUriQuery(val, true). return encodeUriQuery(val, true).
replace(/%26/gi, '&'). replace(/%26/gi, '&').
replace(/%3D/gi, '='). replace(/%3D/gi, '=').
replace(/%2B/gi, '+'); replace(/%2B/gi, '+');
} }
/** /**
* This method is intended for encoding *key* or *value* parts of query component. We need a * This method is intended for encoding *key* or *value* parts of query component. We need a
* custom method because encodeURIComponent is too aggressive and encodes stuff that doesn't * custom method because encodeURIComponent is too aggressive and encodes stuff that doesn't
* have to be encoded per http://tools.ietf.org/html/rfc3986: * have to be encoded per http://tools.ietf.org/html/rfc3986:
* query = *( pchar / "/" / "?" ) * query = *( pchar / "/" / "?" )
* pchar = unreserved / pct-encoded / sub-delims / ":" / "@" * pchar = unreserved / pct-encoded / sub-delims / ":" / "@"
* unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~" * unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~"
* pct-encoded = "%" HEXDIG HEXDIG * pct-encoded = "%" HEXDIG HEXDIG
* sub-delims = "!" / "$" / "&" / "'" / "(" / ")" * sub-delims = "!" / "$" / "&" / "'" / "(" / ")"
* / "*" / "+" / "," / ";" / "=" * / "*" / "+" / "," / ";" / "="
*/ */
function encodeUriQuery(val, pctEncodeSpaces) { function encodeUriQuery(val, pctEncodeSpaces) {
return encodeURIComponent(val). return encodeURIComponent(val).
replace(/%40/gi, '@'). replace(/%40/gi, '@').
replace(/%3A/gi, ':'). replace(/%3A/gi, ':').
replace(/%24/g, '$'). replace(/%24/g, '$').
replace(/%2C/gi, ','). replace(/%2C/gi, ',').
replace(/%20/g, (pctEncodeSpaces ? '%20' : '+')); replace(/%20/g, (pctEncodeSpaces ? '%20' : '+'));
} }
function Route(template, defaults) { function Route(template, defaults) {
this.template = template; this.template = template;
this.defaults = defaults || {}; this.defaults = extend({}, provider.defaults, defaults);
this.urlParams = {}; this.urlParams = {};
} }
Route.prototype = { Route.prototype = {
setUrlParams: function(config, params, actionUrl) { setUrlParams: function(config, params, actionUrl) {
var self = this, var self = this,
url = actionUrl || self.template, url = actionUrl || self.template,
val, val,
encodedVal; encodedVal;
var urlParams = self.urlParams = {}; var urlParams = self.urlParams = {};
forEach(url.split(/\W/), function(param){ forEach(url.split(/\W/), function(param) {
if (param === 'hasOwnProperty') { if (param === 'hasOwnProperty') {
throw $resourceMinErr('badname', "hasOwnProperty is not a valid parameter name."); throw $resourceMinErr('badname', "hasOwnProperty is not a valid parameter name.");
}
if (!(new RegExp("^\\d+$").test(param)) && param &&
(new RegExp("(^|[^\\\\]):" + param + "(\\W|$)").test(url))) {
urlParams[param] = true;
}
});
url = url.replace(/\\:/g, ':');
params = params || {};
forEach(self.urlParams, function(_, urlParam){
val = params.hasOwnProperty(urlParam) ? params[urlParam] : self.defaults[urlParam];
if (angular.isDefined(val) && val !== null) {
encodedVal = encodeUriSegment(val);
url = url.replace(new RegExp(":" + urlParam + "(\\W|$)", "g"), function(match, p1) {
return encodedVal + p1;
});
} else {
url = url.replace(new RegExp("(\/?):" + urlParam + "(\\W|$)", "g"), function(match,
leadingSlashes, tail) {
if (tail.charAt(0) == '/') {
return tail;
} else {
return leadingSlashes + tail;
}
});
}
});
// strip trailing slashes and set the url
url = url.replace(/\/+$/, '') || '/';
// then replace collapse `/.` if found in the last URL path segment before the query
// E.g. `http://url.com/id./format?q=x` becomes `http://url.com/id.format?q=x`
url = url.replace(/\/\.(?=\w+($|\?))/, '.');
// replace escaped `/\.` with `/.`
config.url = url.replace(/\/\\\./, '/.');
// set params - delegate param encoding to $http
forEach(params, function(value, key){
if (!self.urlParams[key]) {
config.params = config.params || {};
config.params[key] = value;
}
});
}
};
function resourceFactory(url, paramDefaults, actions) {
var route = new Route(url);
actions = extend({}, DEFAULT_ACTIONS, actions);
function extractParams(data, actionParams){
var ids = {};
actionParams = extend({}, paramDefaults, actionParams);
forEach(actionParams, function(value, key){
if (isFunction(value)) { value = value(); }
ids[key] = value && value.charAt && value.charAt(0) == '@' ?
lookupDottedPath(data, value.substr(1)) : value;
});
return ids;
}
function defaultResponseInterceptor(response) {
return response.resource;
}
function Resource(value){
shallowClearAndCopy(value || {}, this);
}
forEach(actions, function(action, name) {
var hasBody = /^(POST|PUT|PATCH)$/i.test(action.method);
Resource[name] = function(a1, a2, a3, a4) {
var params = {}, data, success, error;
/* jshint -W086 */ /* (purposefully fall through case statements) */
switch(arguments.length) {
case 4:
error = a4;
success = a3;
//fallthrough
case 3:
case 2:
if (isFunction(a2)) {
if (isFunction(a1)) {
success = a1;
error = a2;
break;
}
success = a2;
error = a3;
//fallthrough
} else {
params = a1;
data = a2;
success = a3;
break;
} }
case 1: if (!(new RegExp("^\\d+$").test(param)) && param &&
if (isFunction(a1)) success = a1; (new RegExp("(^|[^\\\\]):" + param + "(\\W|$)").test(url))) {
else if (hasBody) data = a1; urlParams[param] = true;
else params = a1; }
break; });
case 0: break; url = url.replace(/\\:/g, ':');
default:
throw $resourceMinErr('badargs',
"Expected up to 4 arguments [params, data, success, error], got {0} arguments",
arguments.length);
}
/* jshint +W086 */ /* (purposefully fall through case statements) */
var isInstanceCall = this instanceof Resource; params = params || {};
var value = isInstanceCall ? data : (action.isArray ? [] : new Resource(data)); forEach(self.urlParams, function(_, urlParam) {
var httpConfig = {}; val = params.hasOwnProperty(urlParam) ? params[urlParam] : self.defaults[urlParam];
var responseInterceptor = action.interceptor && action.interceptor.response || if (angular.isDefined(val) && val !== null) {
defaultResponseInterceptor; encodedVal = encodeUriSegment(val);
var responseErrorInterceptor = action.interceptor && action.interceptor.responseError || url = url.replace(new RegExp(":" + urlParam + "(\\W|$)", "g"), function(match, p1) {
undefined; return encodedVal + p1;
});
forEach(action, function(value, key) { } else {
if (key != 'params' && key != 'isArray' && key != 'interceptor') { url = url.replace(new RegExp("(\/?):" + urlParam + "(\\W|$)", "g"), function(match,
httpConfig[key] = copy(value); leadingSlashes, tail) {
if (tail.charAt(0) == '/') {
return tail;
} else {
return leadingSlashes + tail;
}
});
} }
}); });
if (hasBody) httpConfig.data = data; // strip trailing slashes and set the url (unless this behavior is specifically disabled)
route.setUrlParams(httpConfig, if (self.defaults.stripTrailingSlashes) {
extend({}, extractParams(data, action.params || {}), params), url = url.replace(/\/+$/, '') || '/';
action.url); }
var promise = $http(httpConfig).then(function(response) { // then replace collapse `/.` if found in the last URL path segment before the query
var data = response.data, // E.g. `http://url.com/id./format?q=x` becomes `http://url.com/id.format?q=x`
url = url.replace(/\/\.(?=\w+($|\?))/, '.');
// replace escaped `/\.` with `/.`
config.url = url.replace(/\/\\\./, '/.');
// set params - delegate param encoding to $http
forEach(params, function(value, key) {
if (!self.urlParams[key]) {
config.params = config.params || {};
config.params[key] = value;
}
});
}
};
function resourceFactory(url, paramDefaults, actions, options) {
var route = new Route(url, options);
actions = extend({}, provider.defaults.actions, actions);
function extractParams(data, actionParams) {
var ids = {};
actionParams = extend({}, paramDefaults, actionParams);
forEach(actionParams, function(value, key) {
if (isFunction(value)) { value = value(); }
ids[key] = value && value.charAt && value.charAt(0) == '@' ?
lookupDottedPath(data, value.substr(1)) : value;
});
return ids;
}
function defaultResponseInterceptor(response) {
return response.resource;
}
function Resource(value) {
shallowClearAndCopy(value || {}, this);
}
Resource.prototype.toJSON = function() {
var data = extend({}, this);
delete data.$promise;
delete data.$resolved;
return data;
};
forEach(actions, function(action, name) {
var hasBody = /^(POST|PUT|PATCH)$/i.test(action.method);
Resource[name] = function(a1, a2, a3, a4) {
var params = {}, data, success, error;
/* jshint -W086 */ /* (purposefully fall through case statements) */
switch (arguments.length) {
case 4:
error = a4;
success = a3;
//fallthrough
case 3:
case 2:
if (isFunction(a2)) {
if (isFunction(a1)) {
success = a1;
error = a2;
break;
}
success = a2;
error = a3;
//fallthrough
} else {
params = a1;
data = a2;
success = a3;
break;
}
case 1:
if (isFunction(a1)) success = a1;
else if (hasBody) data = a1;
else params = a1;
break;
case 0: break;
default:
throw $resourceMinErr('badargs',
"Expected up to 4 arguments [params, data, success, error], got {0} arguments",
arguments.length);
}
/* jshint +W086 */ /* (purposefully fall through case statements) */
var isInstanceCall = this instanceof Resource;
var value = isInstanceCall ? data : (action.isArray ? [] : new Resource(data));
var httpConfig = {};
var responseInterceptor = action.interceptor && action.interceptor.response ||
defaultResponseInterceptor;
var responseErrorInterceptor = action.interceptor && action.interceptor.responseError ||
undefined;
forEach(action, function(value, key) {
if (key != 'params' && key != 'isArray' && key != 'interceptor') {
httpConfig[key] = copy(value);
}
});
if (hasBody) httpConfig.data = data;
route.setUrlParams(httpConfig,
extend({}, extractParams(data, action.params || {}), params),
action.url);
var promise = $http(httpConfig).then(function(response) {
var data = response.data,
promise = value.$promise; promise = value.$promise;
if (data) { if (data) {
// Need to convert action.isArray to boolean in case it is undefined // Need to convert action.isArray to boolean in case it is undefined
// jshint -W018 // jshint -W018
if (angular.isArray(data) !== (!!action.isArray)) { if (angular.isArray(data) !== (!!action.isArray)) {
throw $resourceMinErr('badcfg', 'Error in resource configuration. Expected ' + throw $resourceMinErr('badcfg',
'response to contain an {0} but got an {1}', 'Error in resource configuration for action `{0}`. Expected response to ' +
action.isArray?'array':'object', angular.isArray(data)?'array':'object'); 'contain an {1} but got an {2}', name, action.isArray ? 'array' : 'object',
angular.isArray(data) ? 'array' : 'object');
}
// jshint +W018
if (action.isArray) {
value.length = 0;
forEach(data, function(item) {
if (typeof item === "object") {
value.push(new Resource(item));
} else {
// Valid JSON values may be string literals, and these should not be converted
// into objects. These items will not have access to the Resource prototype
// methods, but unfortunately there
value.push(item);
}
});
} else {
shallowClearAndCopy(data, value);
value.$promise = promise;
}
} }
// jshint +W018
if (action.isArray) {
value.length = 0;
forEach(data, function(item) {
value.push(new Resource(item));
});
} else {
shallowClearAndCopy(data, value);
value.$promise = promise;
}
}
value.$resolved = true; value.$resolved = true;
response.resource = value; response.resource = value;
return response; return response;
}, function(response) { }, function(response) {
value.$resolved = true; value.$resolved = true;
(error||noop)(response); (error || noop)(response);
return $q.reject(response); return $q.reject(response);
}); });
promise = promise.then( promise = promise.then(
function(response) { function(response) {
var value = responseInterceptor(response); var value = responseInterceptor(response);
(success||noop)(value, response.headers); (success || noop)(value, response.headers);
return value; return value;
}, },
responseErrorInterceptor); responseErrorInterceptor);
if (!isInstanceCall) { if (!isInstanceCall) {
// we are creating instance / collection // we are creating instance / collection
// - set the initial promise // - set the initial promise
// - return the instance / collection // - return the instance / collection
value.$promise = promise; value.$promise = promise;
value.$resolved = false; value.$resolved = false;
return value; return value;
} }
// instance call // instance call
return promise; return promise;
};
Resource.prototype['$' + name] = function(params, success, error) {
if (isFunction(params)) {
error = success; success = params; params = {};
}
var result = Resource[name].call(this, params, this, success, error);
return result.$promise || result;
};
});
Resource.bind = function(additionalParamDefaults) {
return resourceFactory(url, extend({}, paramDefaults, additionalParamDefaults), actions);
}; };
return Resource;
}
Resource.prototype['$' + name] = function(params, success, error) { return resourceFactory;
if (isFunction(params)) { }];
error = success; success = params; params = {}; });
}
var result = Resource[name].call(this, params, this, success, error);
return result.$promise || result;
};
});
Resource.bind = function(additionalParamDefaults){
return resourceFactory(url, extend({}, paramDefaults, additionalParamDefaults), actions);
};
return Resource;
}
return resourceFactory;
}]);
})(window, window.angular); })(window, window.angular);

View File

@@ -1,5 +1,5 @@
/** /**
* @license AngularJS v1.2.16 * @license AngularJS v1.3.7
* (c) 2010-2014 Google, Inc. http://angularjs.org * (c) 2010-2014 Google, Inc. http://angularjs.org
* License: MIT * License: MIT
*/ */
@@ -22,12 +22,12 @@
*/ */
/* global -ngRouteModule */ /* global -ngRouteModule */
var ngRouteModule = angular.module('ngRoute', ['ng']). var ngRouteModule = angular.module('ngRoute', ['ng']).
provider('$route', $RouteProvider); provider('$route', $RouteProvider),
$routeMinErr = angular.$$minErr('ngRoute');
/** /**
* @ngdoc provider * @ngdoc provider
* @name $routeProvider * @name $routeProvider
* @function
* *
* @description * @description
* *
@@ -39,9 +39,9 @@ var ngRouteModule = angular.module('ngRoute', ['ng']).
* ## Dependencies * ## Dependencies
* Requires the {@link ngRoute `ngRoute`} module to be installed. * Requires the {@link ngRoute `ngRoute`} module to be installed.
*/ */
function $RouteProvider(){ function $RouteProvider() {
function inherit(parent, extra) { function inherit(parent, extra) {
return angular.extend(new (angular.extend(function() {}, {prototype:parent}))(), extra); return angular.extend(Object.create(parent), extra);
} }
var routes = {}; var routes = {};
@@ -146,27 +146,45 @@ function $RouteProvider(){
* Adds a new route definition to the `$route` service. * Adds a new route definition to the `$route` service.
*/ */
this.when = function(path, route) { this.when = function(path, route) {
//copy original route object to preserve params inherited from proto chain
var routeCopy = angular.copy(route);
if (angular.isUndefined(routeCopy.reloadOnSearch)) {
routeCopy.reloadOnSearch = true;
}
if (angular.isUndefined(routeCopy.caseInsensitiveMatch)) {
routeCopy.caseInsensitiveMatch = this.caseInsensitiveMatch;
}
routes[path] = angular.extend( routes[path] = angular.extend(
{reloadOnSearch: true}, routeCopy,
route, path && pathRegExp(path, routeCopy)
path && pathRegExp(path, route)
); );
// create redirection for trailing slashes // create redirection for trailing slashes
if (path) { if (path) {
var redirectPath = (path[path.length-1] == '/') var redirectPath = (path[path.length - 1] == '/')
? path.substr(0, path.length-1) ? path.substr(0, path.length - 1)
: path +'/'; : path + '/';
routes[redirectPath] = angular.extend( routes[redirectPath] = angular.extend(
{redirectTo: path}, {redirectTo: path},
pathRegExp(redirectPath, route) pathRegExp(redirectPath, routeCopy)
); );
} }
return this; return this;
}; };
/**
* @ngdoc property
* @name $routeProvider#caseInsensitiveMatch
* @description
*
* A boolean property indicating if routes defined
* using this provider should be matched using a case insensitive
* algorithm. Defaults to `false`.
*/
this.caseInsensitiveMatch = false;
/** /**
* @param path {string} path * @param path {string} path
* @param opts {Object} options * @param opts {Object} options
@@ -188,7 +206,7 @@ function $RouteProvider(){
path = path path = path
.replace(/([().])/g, '\\$1') .replace(/([().])/g, '\\$1')
.replace(/(\/)?:(\w+)([\?\*])?/g, function(_, slash, key, option){ .replace(/(\/)?:(\w+)([\?\*])?/g, function(_, slash, key, option) {
var optional = option === '?' ? option : null; var optional = option === '?' ? option : null;
var star = option === '*' ? option : null; var star = option === '*' ? option : null;
keys.push({ name: key, optional: !!optional }); keys.push({ name: key, optional: !!optional });
@@ -216,10 +234,14 @@ function $RouteProvider(){
* Sets route definition that will be used on route change when no other route definition * Sets route definition that will be used on route change when no other route definition
* is matched. * is matched.
* *
* @param {Object} params Mapping information to be assigned to `$route.current`. * @param {Object|string} params Mapping information to be assigned to `$route.current`.
* If called with a string, the value maps to `redirectTo`.
* @returns {Object} self * @returns {Object} self
*/ */
this.otherwise = function(params) { this.otherwise = function(params) {
if (typeof params === 'string') {
params = {redirectTo: params};
}
this.when(null, params); this.when(null, params);
return this; return this;
}; };
@@ -230,10 +252,9 @@ function $RouteProvider(){
'$routeParams', '$routeParams',
'$q', '$q',
'$injector', '$injector',
'$http', '$templateRequest',
'$templateCache',
'$sce', '$sce',
function($rootScope, $location, $routeParams, $q, $injector, $http, $templateCache, $sce) { function($rootScope, $location, $routeParams, $q, $injector, $templateRequest, $sce) {
/** /**
* @ngdoc service * @ngdoc service
@@ -270,9 +291,6 @@ function $RouteProvider(){
* This example shows how changing the URL hash causes the `$route` to match a route against the * This example shows how changing the URL hash causes the `$route` to match a route against the
* URL, and the `ngView` pulls in the partial. * URL, and the `ngView` pulls in the partial.
* *
* Note that this example is using {@link ng.directive:script inlined templates}
* to get it working on jsfiddle as well.
*
* <example name="$route-service" module="ngRouteExample" * <example name="$route-service" module="ngRouteExample"
* deps="angular-route.js" fixBase="true"> * deps="angular-route.js" fixBase="true">
* <file name="index.html"> * <file name="index.html">
@@ -380,6 +398,10 @@ function $RouteProvider(){
* defined in `resolve` route property. Once all of the dependencies are resolved * defined in `resolve` route property. Once all of the dependencies are resolved
* `$routeChangeSuccess` is fired. * `$routeChangeSuccess` is fired.
* *
* The route change (and the `$location` change that triggered it) can be prevented
* by calling `preventDefault` method of the event. See {@link ng.$rootScope.Scope#$on}
* for more details about event object.
*
* @param {Object} angularEvent Synthetic event object. * @param {Object} angularEvent Synthetic event object.
* @param {Route} next Future route information. * @param {Route} next Future route information.
* @param {Route} current Current route information. * @param {Route} current Current route information.
@@ -424,6 +446,8 @@ function $RouteProvider(){
*/ */
var forceReload = false, var forceReload = false,
preparedRoute,
preparedRouteIsUpdateOnly,
$route = { $route = {
routes: routes, routes: routes,
@@ -436,15 +460,50 @@ function $RouteProvider(){
* {@link ng.$location $location} hasn't changed. * {@link ng.$location $location} hasn't changed.
* *
* As a result of that, {@link ngRoute.directive:ngView ngView} * As a result of that, {@link ngRoute.directive:ngView ngView}
* creates new scope, reinstantiates the controller. * creates new scope and reinstantiates the controller.
*/ */
reload: function() { reload: function() {
forceReload = true; forceReload = true;
$rootScope.$evalAsync(updateRoute); $rootScope.$evalAsync(function() {
// Don't support cancellation of a reload for now...
prepareRoute();
commitRoute();
});
},
/**
* @ngdoc method
* @name $route#updateParams
*
* @description
* Causes `$route` service to update the current URL, replacing
* current route parameters with those specified in `newParams`.
* Provided property names that match the route's path segment
* definitions will be interpolated into the location's path, while
* remaining properties will be treated as query params.
*
* @param {Object} newParams mapping of URL parameter names to values
*/
updateParams: function(newParams) {
if (this.current && this.current.$$route) {
var searchParams = {}, self=this;
angular.forEach(Object.keys(newParams), function(key) {
if (!self.current.pathParams[key]) searchParams[key] = newParams[key];
});
newParams = angular.extend({}, this.current.params, newParams);
$location.path(interpolate(this.current.$$route.originalPath, newParams));
$location.search(angular.extend({}, $location.search(), searchParams));
}
else {
throw $routeMinErr('norout', 'Tried updating route when with no current route');
}
} }
}; };
$rootScope.$on('$locationChangeSuccess', updateRoute); $rootScope.$on('$locationChangeStart', prepareRoute);
$rootScope.$on('$locationChangeSuccess', commitRoute);
return $route; return $route;
@@ -473,9 +532,7 @@ function $RouteProvider(){
for (var i = 1, len = m.length; i < len; ++i) { for (var i = 1, len = m.length; i < len; ++i) {
var key = keys[i - 1]; var key = keys[i - 1];
var val = 'string' == typeof m[i] var val = m[i];
? decodeURIComponent(m[i])
: m[i];
if (key && val) { if (key && val) {
params[key.name] = val; params[key.name] = val;
@@ -484,56 +541,69 @@ function $RouteProvider(){
return params; return params;
} }
function updateRoute() { function prepareRoute($locationEvent) {
var next = parseRoute(), var lastRoute = $route.current;
last = $route.current;
if (next && last && next.$$route === last.$$route preparedRoute = parseRoute();
&& angular.equals(next.pathParams, last.pathParams) preparedRouteIsUpdateOnly = preparedRoute && lastRoute && preparedRoute.$$route === lastRoute.$$route
&& !next.reloadOnSearch && !forceReload) { && angular.equals(preparedRoute.pathParams, lastRoute.pathParams)
last.params = next.params; && !preparedRoute.reloadOnSearch && !forceReload;
angular.copy(last.params, $routeParams);
$rootScope.$broadcast('$routeUpdate', last); if (!preparedRouteIsUpdateOnly && (lastRoute || preparedRoute)) {
} else if (next || last) { if ($rootScope.$broadcast('$routeChangeStart', preparedRoute, lastRoute).defaultPrevented) {
if ($locationEvent) {
$locationEvent.preventDefault();
}
}
}
}
function commitRoute() {
var lastRoute = $route.current;
var nextRoute = preparedRoute;
if (preparedRouteIsUpdateOnly) {
lastRoute.params = nextRoute.params;
angular.copy(lastRoute.params, $routeParams);
$rootScope.$broadcast('$routeUpdate', lastRoute);
} else if (nextRoute || lastRoute) {
forceReload = false; forceReload = false;
$rootScope.$broadcast('$routeChangeStart', next, last); $route.current = nextRoute;
$route.current = next; if (nextRoute) {
if (next) { if (nextRoute.redirectTo) {
if (next.redirectTo) { if (angular.isString(nextRoute.redirectTo)) {
if (angular.isString(next.redirectTo)) { $location.path(interpolate(nextRoute.redirectTo, nextRoute.params)).search(nextRoute.params)
$location.path(interpolate(next.redirectTo, next.params)).search(next.params)
.replace(); .replace();
} else { } else {
$location.url(next.redirectTo(next.pathParams, $location.path(), $location.search())) $location.url(nextRoute.redirectTo(nextRoute.pathParams, $location.path(), $location.search()))
.replace(); .replace();
} }
} }
} }
$q.when(next). $q.when(nextRoute).
then(function() { then(function() {
if (next) { if (nextRoute) {
var locals = angular.extend({}, next.resolve), var locals = angular.extend({}, nextRoute.resolve),
template, templateUrl; template, templateUrl;
angular.forEach(locals, function(value, key) { angular.forEach(locals, function(value, key) {
locals[key] = angular.isString(value) ? locals[key] = angular.isString(value) ?
$injector.get(value) : $injector.invoke(value); $injector.get(value) : $injector.invoke(value, null, null, key);
}); });
if (angular.isDefined(template = next.template)) { if (angular.isDefined(template = nextRoute.template)) {
if (angular.isFunction(template)) { if (angular.isFunction(template)) {
template = template(next.params); template = template(nextRoute.params);
} }
} else if (angular.isDefined(templateUrl = next.templateUrl)) { } else if (angular.isDefined(templateUrl = nextRoute.templateUrl)) {
if (angular.isFunction(templateUrl)) { if (angular.isFunction(templateUrl)) {
templateUrl = templateUrl(next.params); templateUrl = templateUrl(nextRoute.params);
} }
templateUrl = $sce.getTrustedResourceUrl(templateUrl); templateUrl = $sce.getTrustedResourceUrl(templateUrl);
if (angular.isDefined(templateUrl)) { if (angular.isDefined(templateUrl)) {
next.loadedTemplateUrl = templateUrl; nextRoute.loadedTemplateUrl = templateUrl;
template = $http.get(templateUrl, {cache: $templateCache}). template = $templateRequest(templateUrl);
then(function(response) { return response.data; });
} }
} }
if (angular.isDefined(template)) { if (angular.isDefined(template)) {
@@ -544,16 +614,16 @@ function $RouteProvider(){
}). }).
// after route change // after route change
then(function(locals) { then(function(locals) {
if (next == $route.current) { if (nextRoute == $route.current) {
if (next) { if (nextRoute) {
next.locals = locals; nextRoute.locals = locals;
angular.copy(next.params, $routeParams); angular.copy(nextRoute.params, $routeParams);
} }
$rootScope.$broadcast('$routeChangeSuccess', next, last); $rootScope.$broadcast('$routeChangeSuccess', nextRoute, lastRoute);
} }
}, function(error) { }, function(error) {
if (next == $route.current) { if (nextRoute == $route.current) {
$rootScope.$broadcast('$routeChangeError', next, last, error); $rootScope.$broadcast('$routeChangeError', nextRoute, lastRoute, error);
} }
}); });
} }
@@ -583,11 +653,11 @@ function $RouteProvider(){
*/ */
function interpolate(string, params) { function interpolate(string, params) {
var result = []; var result = [];
angular.forEach((string||'').split(':'), function(segment, i) { angular.forEach((string || '').split(':'), function(segment, i) {
if (i === 0) { if (i === 0) {
result.push(segment); result.push(segment);
} else { } else {
var segmentMatch = segment.match(/(\w+)(.*)/); var segmentMatch = segment.match(/(\w+)(?:[?*])?(.*)/);
var key = segmentMatch[1]; var key = segmentMatch[1];
result.push(params[key]); result.push(params[key]);
result.push(segmentMatch[2] || ''); result.push(segmentMatch[2] || '');
@@ -632,7 +702,7 @@ ngRouteModule.provider('$routeParams', $RouteParamsProvider);
* // Route: /Chapter/:chapterId/Section/:sectionId * // Route: /Chapter/:chapterId/Section/:sectionId
* // * //
* // Then * // Then
* $routeParams ==> {chapterId:1, sectionId:2, search:'moby'} * $routeParams ==> {chapterId:'1', sectionId:'2', search:'moby'}
* ``` * ```
*/ */
function $RouteParamsProvider() { function $RouteParamsProvider() {
@@ -695,7 +765,6 @@ ngRouteModule.directive('ngView', ngViewFillContentFactory);
<pre>$location.path() = {{main.$location.path()}}</pre> <pre>$location.path() = {{main.$location.path()}}</pre>
<pre>$route.current.templateUrl = {{main.$route.current.templateUrl}}</pre> <pre>$route.current.templateUrl = {{main.$route.current.templateUrl}}</pre>
<pre>$route.current.params = {{main.$route.current.params}}</pre> <pre>$route.current.params = {{main.$route.current.params}}</pre>
<pre>$route.current.scope.name = {{main.$route.current.scope.name}}</pre>
<pre>$routeParams = {{main.$routeParams}}</pre> <pre>$routeParams = {{main.$routeParams}}</pre>
</div> </div>
</file> </file>
@@ -719,7 +788,6 @@ ngRouteModule.directive('ngView', ngViewFillContentFactory);
.view-animate-container { .view-animate-container {
position:relative; position:relative;
height:100px!important; height:100px!important;
position:relative;
background:white; background:white;
border:1px solid black; border:1px solid black;
height:40px; height:40px;
@@ -773,7 +841,6 @@ ngRouteModule.directive('ngView', ngViewFillContentFactory);
controllerAs: 'chapter' controllerAs: 'chapter'
}); });
// configure html5 to get links working on jsfiddle
$locationProvider.html5Mode(true); $locationProvider.html5Mode(true);
}]) }])
.controller('MainCtrl', ['$route', '$routeParams', '$location', .controller('MainCtrl', ['$route', '$routeParams', '$location',
@@ -820,7 +887,7 @@ ngRouteModule.directive('ngView', ngViewFillContentFactory);
* Emitted every time the ngView content is reloaded. * Emitted every time the ngView content is reloaded.
*/ */
ngViewFactory.$inject = ['$route', '$anchorScroll', '$animate']; ngViewFactory.$inject = ['$route', '$anchorScroll', '$animate'];
function ngViewFactory( $route, $anchorScroll, $animate) { function ngViewFactory($route, $anchorScroll, $animate) {
return { return {
restrict: 'ECA', restrict: 'ECA',
terminal: true, terminal: true,
@@ -829,7 +896,7 @@ function ngViewFactory( $route, $anchorScroll, $animate) {
link: function(scope, $element, attr, ctrl, $transclude) { link: function(scope, $element, attr, ctrl, $transclude) {
var currentScope, var currentScope,
currentElement, currentElement,
previousElement, previousLeaveAnimation,
autoScrollExp = attr.autoscroll, autoScrollExp = attr.autoscroll,
onloadExp = attr.onload || ''; onloadExp = attr.onload || '';
@@ -837,19 +904,20 @@ function ngViewFactory( $route, $anchorScroll, $animate) {
update(); update();
function cleanupLastView() { function cleanupLastView() {
if(previousElement) { if (previousLeaveAnimation) {
previousElement.remove(); $animate.cancel(previousLeaveAnimation);
previousElement = null; previousLeaveAnimation = null;
} }
if(currentScope) {
if (currentScope) {
currentScope.$destroy(); currentScope.$destroy();
currentScope = null; currentScope = null;
} }
if(currentElement) { if (currentElement) {
$animate.leave(currentElement, function() { previousLeaveAnimation = $animate.leave(currentElement);
previousElement = null; previousLeaveAnimation.then(function() {
previousLeaveAnimation = null;
}); });
previousElement = currentElement;
currentElement = null; currentElement = null;
} }
} }
@@ -869,7 +937,7 @@ function ngViewFactory( $route, $anchorScroll, $animate) {
// function is called before linking the content, which would apply child // function is called before linking the content, which would apply child
// directives to non existing elements. // directives to non existing elements.
var clone = $transclude(newScope, function(clone) { var clone = $transclude(newScope, function(clone) {
$animate.enter(clone, null, currentElement || $element, function onNgViewEnter () { $animate.enter(clone, null, currentElement || $element).then(function onNgViewEnter() {
if (angular.isDefined(autoScrollExp) if (angular.isDefined(autoScrollExp)
&& (!autoScrollExp || scope.$eval(autoScrollExp))) { && (!autoScrollExp || scope.$eval(autoScrollExp))) {
$anchorScroll(); $anchorScroll();

View File

@@ -1,5 +1,5 @@
/** /**
* @license AngularJS v1.2.16 * @license AngularJS v1.3.7
* (c) 2010-2014 Google, Inc. http://angularjs.org * (c) 2010-2014 Google, Inc. http://angularjs.org
* License: MIT * License: MIT
*/ */
@@ -42,35 +42,36 @@ var $sanitizeMinErr = angular.$$minErr('$sanitize');
/** /**
* @ngdoc service * @ngdoc service
* @name $sanitize * @name $sanitize
* @function * @kind function
* *
* @description * @description
* The input is sanitized by parsing the html into tokens. All safe tokens (from a whitelist) are * The input is sanitized by parsing the HTML into tokens. All safe tokens (from a whitelist) are
* then serialized back to properly escaped html string. This means that no unsafe input can make * then serialized back to properly escaped html string. This means that no unsafe input can make
* it into the returned string, however, since our parser is more strict than a typical browser * it into the returned string, however, since our parser is more strict than a typical browser
* parser, it's possible that some obscure input, which would be recognized as valid HTML by a * parser, it's possible that some obscure input, which would be recognized as valid HTML by a
* browser, won't make it through the sanitizer. * browser, won't make it through the sanitizer. The input may also contain SVG markup.
* The whitelist is configured using the functions `aHrefSanitizationWhitelist` and * The whitelist is configured using the functions `aHrefSanitizationWhitelist` and
* `imgSrcSanitizationWhitelist` of {@link ng.$compileProvider `$compileProvider`}. * `imgSrcSanitizationWhitelist` of {@link ng.$compileProvider `$compileProvider`}.
* *
* @param {string} html Html input. * @param {string} html HTML input.
* @returns {string} Sanitized html. * @returns {string} Sanitized HTML.
* *
* @example * @example
<example module="ngSanitize" deps="angular-sanitize.js"> <example module="sanitizeExample" deps="angular-sanitize.js">
<file name="index.html"> <file name="index.html">
<script> <script>
function Ctrl($scope, $sce) { angular.module('sanitizeExample', ['ngSanitize'])
$scope.snippet = .controller('ExampleController', ['$scope', '$sce', function($scope, $sce) {
'<p style="color:blue">an html\n' + $scope.snippet =
'<em onmouseover="this.textContent=\'PWN3D!\'">click here</em>\n' + '<p style="color:blue">an html\n' +
'snippet</p>'; '<em onmouseover="this.textContent=\'PWN3D!\'">click here</em>\n' +
$scope.deliberatelyTrustDangerousSnippet = function() { 'snippet</p>';
return $sce.trustAsHtml($scope.snippet); $scope.deliberatelyTrustDangerousSnippet = function() {
}; return $sce.trustAsHtml($scope.snippet);
} };
}]);
</script> </script>
<div ng-controller="Ctrl"> <div ng-controller="ExampleController">
Snippet: <textarea ng-model="snippet" cols="60" rows="3"></textarea> Snippet: <textarea ng-model="snippet" cols="60" rows="3"></textarea>
<table> <table>
<tr> <tr>
@@ -158,14 +159,15 @@ function sanitizeText(chars) {
// Regular Expressions for parsing tags and attributes // Regular Expressions for parsing tags and attributes
var START_TAG_REGEXP = var START_TAG_REGEXP =
/^<\s*([\w:-]+)((?:\s+[\w:-]+(?:\s*=\s*(?:(?:"[^"]*")|(?:'[^']*')|[^>\s]+))?)*)\s*(\/?)\s*>/, /^<((?:[a-zA-Z])[\w:-]*)((?:\s+[\w:-]+(?:\s*=\s*(?:(?:"[^"]*")|(?:'[^']*')|[^>\s]+))?)*)\s*(\/?)\s*(>?)/,
END_TAG_REGEXP = /^<\s*\/\s*([\w:-]+)[^>]*>/, END_TAG_REGEXP = /^<\/\s*([\w:-]+)[^>]*>/,
ATTR_REGEXP = /([\w:-]+)(?:\s*=\s*(?:(?:"((?:[^"])*)")|(?:'((?:[^'])*)')|([^>\s]+)))?/g, ATTR_REGEXP = /([\w:-]+)(?:\s*=\s*(?:(?:"((?:[^"])*)")|(?:'((?:[^'])*)')|([^>\s]+)))?/g,
BEGIN_TAG_REGEXP = /^</, BEGIN_TAG_REGEXP = /^</,
BEGING_END_TAGE_REGEXP = /^<\s*\//, BEGING_END_TAGE_REGEXP = /^<\//,
COMMENT_REGEXP = /<!--(.*?)-->/g, COMMENT_REGEXP = /<!--(.*?)-->/g,
DOCTYPE_REGEXP = /<!DOCTYPE([^>]*?)>/i, DOCTYPE_REGEXP = /<!DOCTYPE([^>]*?)>/i,
CDATA_REGEXP = /<!\[CDATA\[(.*?)]]>/g, CDATA_REGEXP = /<!\[CDATA\[(.*?)]]>/g,
SURROGATE_PAIR_REGEXP = /[\uD800-\uDBFF][\uDC00-\uDFFF]/g,
// Match everything outside of normal chars and " (quote character) // Match everything outside of normal chars and " (quote character)
NON_ALPHANUMERIC_REGEXP = /([^\#-~| |!])/g; NON_ALPHANUMERIC_REGEXP = /([^\#-~| |!])/g;
@@ -196,6 +198,12 @@ var inlineElements = angular.extend({}, optionalEndTagInlineElements, makeMap("a
"bdi,bdo,big,br,cite,code,del,dfn,em,font,i,img,ins,kbd,label,map,mark,q,ruby,rp,rt,s," + "bdi,bdo,big,br,cite,code,del,dfn,em,font,i,img,ins,kbd,label,map,mark,q,ruby,rp,rt,s," +
"samp,small,span,strike,strong,sub,sup,time,tt,u,var")); "samp,small,span,strike,strong,sub,sup,time,tt,u,var"));
// SVG Elements
// https://wiki.whatwg.org/wiki/Sanitization_rules#svg_Elements
var svgElements = makeMap("animate,animateColor,animateMotion,animateTransform,circle,defs," +
"desc,ellipse,font-face,font-face-name,font-face-src,g,glyph,hkern,image,linearGradient," +
"line,marker,metadata,missing-glyph,mpath,path,polygon,polyline,radialGradient,rect,set," +
"stop,svg,switch,text,title,tspan,use");
// Special Elements (can contain anything) // Special Elements (can contain anything)
var specialElements = makeMap("script,style"); var specialElements = makeMap("script,style");
@@ -204,16 +212,41 @@ var validElements = angular.extend({},
voidElements, voidElements,
blockElements, blockElements,
inlineElements, inlineElements,
optionalEndTagElements); optionalEndTagElements,
svgElements);
//Attributes that have href and hence need to be sanitized //Attributes that have href and hence need to be sanitized
var uriAttrs = makeMap("background,cite,href,longdesc,src,usemap"); var uriAttrs = makeMap("background,cite,href,longdesc,src,usemap,xlink:href");
var validAttrs = angular.extend({}, uriAttrs, makeMap(
'abbr,align,alt,axis,bgcolor,border,cellpadding,cellspacing,class,clear,'+ var htmlAttrs = makeMap('abbr,align,alt,axis,bgcolor,border,cellpadding,cellspacing,class,clear,' +
'color,cols,colspan,compact,coords,dir,face,headers,height,hreflang,hspace,'+ 'color,cols,colspan,compact,coords,dir,face,headers,height,hreflang,hspace,' +
'ismap,lang,language,nohref,nowrap,rel,rev,rows,rowspan,rules,'+ 'ismap,lang,language,nohref,nowrap,rel,rev,rows,rowspan,rules,' +
'scope,scrolling,shape,size,span,start,summary,target,title,type,'+ 'scope,scrolling,shape,size,span,start,summary,target,title,type,' +
'valign,value,vspace,width')); 'valign,value,vspace,width');
// SVG attributes (without "id" and "name" attributes)
// https://wiki.whatwg.org/wiki/Sanitization_rules#svg_Attributes
var svgAttrs = makeMap('accent-height,accumulate,additive,alphabetic,arabic-form,ascent,' +
'attributeName,attributeType,baseProfile,bbox,begin,by,calcMode,cap-height,class,color,' +
'color-rendering,content,cx,cy,d,dx,dy,descent,display,dur,end,fill,fill-rule,font-family,' +
'font-size,font-stretch,font-style,font-variant,font-weight,from,fx,fy,g1,g2,glyph-name,' +
'gradientUnits,hanging,height,horiz-adv-x,horiz-origin-x,ideographic,k,keyPoints,' +
'keySplines,keyTimes,lang,marker-end,marker-mid,marker-start,markerHeight,markerUnits,' +
'markerWidth,mathematical,max,min,offset,opacity,orient,origin,overline-position,' +
'overline-thickness,panose-1,path,pathLength,points,preserveAspectRatio,r,refX,refY,' +
'repeatCount,repeatDur,requiredExtensions,requiredFeatures,restart,rotate,rx,ry,slope,stemh,' +
'stemv,stop-color,stop-opacity,strikethrough-position,strikethrough-thickness,stroke,' +
'stroke-dasharray,stroke-dashoffset,stroke-linecap,stroke-linejoin,stroke-miterlimit,' +
'stroke-opacity,stroke-width,systemLanguage,target,text-anchor,to,transform,type,u1,u2,' +
'underline-position,underline-thickness,unicode,unicode-range,units-per-em,values,version,' +
'viewBox,visibility,width,widths,x,x-height,x1,x2,xlink:actuate,xlink:arcrole,xlink:role,' +
'xlink:show,xlink:title,xlink:type,xml:base,xml:lang,xml:space,xmlns,xmlns:xlink,y,y1,y2,' +
'zoomAndPan');
var validAttrs = angular.extend({},
uriAttrs,
svgAttrs,
htmlAttrs);
function makeMap(str) { function makeMap(str) {
var obj = {}, items = str.split(','), i; var obj = {}, items = str.split(','), i;
@@ -234,78 +267,93 @@ function makeMap(str) {
* @param {string} html string * @param {string} html string
* @param {object} handler * @param {object} handler
*/ */
function htmlParser( html, handler ) { function htmlParser(html, handler) {
var index, chars, match, stack = [], last = html; if (typeof html !== 'string') {
if (html === null || typeof html === 'undefined') {
html = '';
} else {
html = '' + html;
}
}
var index, chars, match, stack = [], last = html, text;
stack.last = function() { return stack[ stack.length - 1 ]; }; stack.last = function() { return stack[ stack.length - 1 ]; };
while ( html ) { while (html) {
text = '';
chars = true; chars = true;
// Make sure we're not in a script or style element // Make sure we're not in a script or style element
if ( !stack.last() || !specialElements[ stack.last() ] ) { if (!stack.last() || !specialElements[ stack.last() ]) {
// Comment // Comment
if ( html.indexOf("<!--") === 0 ) { if (html.indexOf("<!--") === 0) {
// comments containing -- are not allowed unless they terminate the comment // comments containing -- are not allowed unless they terminate the comment
index = html.indexOf("--", 4); index = html.indexOf("--", 4);
if ( index >= 0 && html.lastIndexOf("-->", index) === index) { if (index >= 0 && html.lastIndexOf("-->", index) === index) {
if (handler.comment) handler.comment( html.substring( 4, index ) ); if (handler.comment) handler.comment(html.substring(4, index));
html = html.substring( index + 3 ); html = html.substring(index + 3);
chars = false; chars = false;
} }
// DOCTYPE // DOCTYPE
} else if ( DOCTYPE_REGEXP.test(html) ) { } else if (DOCTYPE_REGEXP.test(html)) {
match = html.match( DOCTYPE_REGEXP ); match = html.match(DOCTYPE_REGEXP);
if ( match ) { if (match) {
html = html.replace( match[0], ''); html = html.replace(match[0], '');
chars = false; chars = false;
} }
// end tag // end tag
} else if ( BEGING_END_TAGE_REGEXP.test(html) ) { } else if (BEGING_END_TAGE_REGEXP.test(html)) {
match = html.match( END_TAG_REGEXP ); match = html.match(END_TAG_REGEXP);
if ( match ) { if (match) {
html = html.substring( match[0].length ); html = html.substring(match[0].length);
match[0].replace( END_TAG_REGEXP, parseEndTag ); match[0].replace(END_TAG_REGEXP, parseEndTag);
chars = false; chars = false;
} }
// start tag // start tag
} else if ( BEGIN_TAG_REGEXP.test(html) ) { } else if (BEGIN_TAG_REGEXP.test(html)) {
match = html.match( START_TAG_REGEXP ); match = html.match(START_TAG_REGEXP);
if ( match ) { if (match) {
html = html.substring( match[0].length ); // We only have a valid start-tag if there is a '>'.
match[0].replace( START_TAG_REGEXP, parseStartTag ); if (match[4]) {
html = html.substring(match[0].length);
match[0].replace(START_TAG_REGEXP, parseStartTag);
}
chars = false; chars = false;
} else {
// no ending tag found --- this piece should be encoded as an entity.
text += '<';
html = html.substring(1);
} }
} }
if ( chars ) { if (chars) {
index = html.indexOf("<"); index = html.indexOf("<");
var text = index < 0 ? html : html.substring( 0, index ); text += index < 0 ? html : html.substring(0, index);
html = index < 0 ? "" : html.substring( index ); html = index < 0 ? "" : html.substring(index);
if (handler.chars) handler.chars( decodeEntities(text) ); if (handler.chars) handler.chars(decodeEntities(text));
} }
} else { } else {
html = html.replace(new RegExp("(.*)<\\s*\\/\\s*" + stack.last() + "[^>]*>", 'i'), html = html.replace(new RegExp("(.*)<\\s*\\/\\s*" + stack.last() + "[^>]*>", 'i'),
function(all, text){ function(all, text) {
text = text.replace(COMMENT_REGEXP, "$1").replace(CDATA_REGEXP, "$1"); text = text.replace(COMMENT_REGEXP, "$1").replace(CDATA_REGEXP, "$1");
if (handler.chars) handler.chars( decodeEntities(text) ); if (handler.chars) handler.chars(decodeEntities(text));
return ""; return "";
}); });
parseEndTag( "", stack.last() ); parseEndTag("", stack.last());
} }
if ( html == last ) { if (html == last) {
throw $sanitizeMinErr('badparse', "The sanitizer was unable to parse the following block " + throw $sanitizeMinErr('badparse', "The sanitizer was unable to parse the following block " +
"of html: {0}", html); "of html: {0}", html);
} }
@@ -315,22 +363,22 @@ function htmlParser( html, handler ) {
// Clean up any remaining tags // Clean up any remaining tags
parseEndTag(); parseEndTag();
function parseStartTag( tag, tagName, rest, unary ) { function parseStartTag(tag, tagName, rest, unary) {
tagName = angular.lowercase(tagName); tagName = angular.lowercase(tagName);
if ( blockElements[ tagName ] ) { if (blockElements[ tagName ]) {
while ( stack.last() && inlineElements[ stack.last() ] ) { while (stack.last() && inlineElements[ stack.last() ]) {
parseEndTag( "", stack.last() ); parseEndTag("", stack.last());
} }
} }
if ( optionalEndTagElements[ tagName ] && stack.last() == tagName ) { if (optionalEndTagElements[ tagName ] && stack.last() == tagName) {
parseEndTag( "", tagName ); parseEndTag("", tagName);
} }
unary = voidElements[ tagName ] || !!unary; unary = voidElements[ tagName ] || !!unary;
if ( !unary ) if (!unary)
stack.push( tagName ); stack.push(tagName);
var attrs = {}; var attrs = {};
@@ -343,22 +391,22 @@ function htmlParser( html, handler ) {
attrs[name] = decodeEntities(value); attrs[name] = decodeEntities(value);
}); });
if (handler.start) handler.start( tagName, attrs, unary ); if (handler.start) handler.start(tagName, attrs, unary);
} }
function parseEndTag( tag, tagName ) { function parseEndTag(tag, tagName) {
var pos = 0, i; var pos = 0, i;
tagName = angular.lowercase(tagName); tagName = angular.lowercase(tagName);
if ( tagName ) if (tagName)
// Find the closest opened tag of the same type // Find the closest opened tag of the same type
for ( pos = stack.length - 1; pos >= 0; pos-- ) for (pos = stack.length - 1; pos >= 0; pos--)
if ( stack[ pos ] == tagName ) if (stack[ pos ] == tagName)
break; break;
if ( pos >= 0 ) { if (pos >= 0) {
// Close all the open elements, up the stack // Close all the open elements, up the stack
for ( i = stack.length - 1; i >= pos; i-- ) for (i = stack.length - 1; i >= pos; i--)
if (handler.end) handler.end( stack[ i ] ); if (handler.end) handler.end(stack[ i ]);
// Remove the open elements from the stack // Remove the open elements from the stack
stack.length = pos; stack.length = pos;
@@ -404,7 +452,12 @@ function decodeEntities(value) {
function encodeEntities(value) { function encodeEntities(value) {
return value. return value.
replace(/&/g, '&amp;'). replace(/&/g, '&amp;').
replace(NON_ALPHANUMERIC_REGEXP, function(value){ replace(SURROGATE_PAIR_REGEXP, function(value) {
var hi = value.charCodeAt(0);
var low = value.charCodeAt(1);
return '&#' + (((hi - 0xD800) * 0x400) + (low - 0xDC00) + 0x10000) + ';';
}).
replace(NON_ALPHANUMERIC_REGEXP, function(value) {
return '&#' + value.charCodeAt(0) + ';'; return '&#' + value.charCodeAt(0) + ';';
}). }).
replace(/</g, '&lt;'). replace(/</g, '&lt;').
@@ -421,11 +474,11 @@ function encodeEntities(value) {
* comment: function(text) {} * comment: function(text) {}
* } * }
*/ */
function htmlSanitizeWriter(buf, uriValidator){ function htmlSanitizeWriter(buf, uriValidator) {
var ignore = false; var ignore = false;
var out = angular.bind(buf, buf.push); var out = angular.bind(buf, buf.push);
return { return {
start: function(tag, attrs, unary){ start: function(tag, attrs, unary) {
tag = angular.lowercase(tag); tag = angular.lowercase(tag);
if (!ignore && specialElements[tag]) { if (!ignore && specialElements[tag]) {
ignore = tag; ignore = tag;
@@ -433,7 +486,7 @@ function htmlSanitizeWriter(buf, uriValidator){
if (!ignore && validElements[tag] === true) { if (!ignore && validElements[tag] === true) {
out('<'); out('<');
out(tag); out(tag);
angular.forEach(attrs, function(value, key){ angular.forEach(attrs, function(value, key) {
var lkey=angular.lowercase(key); var lkey=angular.lowercase(key);
var isImage = (tag === 'img' && lkey === 'src') || (lkey === 'background'); var isImage = (tag === 'img' && lkey === 'src') || (lkey === 'background');
if (validAttrs[lkey] === true && if (validAttrs[lkey] === true &&
@@ -448,7 +501,7 @@ function htmlSanitizeWriter(buf, uriValidator){
out(unary ? '/>' : '>'); out(unary ? '/>' : '>');
} }
}, },
end: function(tag){ end: function(tag) {
tag = angular.lowercase(tag); tag = angular.lowercase(tag);
if (!ignore && validElements[tag] === true) { if (!ignore && validElements[tag] === true) {
out('</'); out('</');
@@ -459,7 +512,7 @@ function htmlSanitizeWriter(buf, uriValidator){
ignore = false; ignore = false;
} }
}, },
chars: function(chars){ chars: function(chars) {
if (!ignore) { if (!ignore) {
out(encodeEntities(chars)); out(encodeEntities(chars));
} }
@@ -476,7 +529,7 @@ angular.module('ngSanitize', []).provider('$sanitize', $SanitizeProvider);
/** /**
* @ngdoc filter * @ngdoc filter
* @name linky * @name linky
* @function * @kind function
* *
* @description * @description
* Finds links in text input and turns them into html links. Supports http/https/ftp/mailto and * Finds links in text input and turns them into html links. Supports http/https/ftp/mailto and
@@ -492,20 +545,21 @@ angular.module('ngSanitize', []).provider('$sanitize', $SanitizeProvider);
<span ng-bind-html="linky_expression | linky"></span> <span ng-bind-html="linky_expression | linky"></span>
* *
* @example * @example
<example module="ngSanitize" deps="angular-sanitize.js"> <example module="linkyExample" deps="angular-sanitize.js">
<file name="index.html"> <file name="index.html">
<script> <script>
function Ctrl($scope) { angular.module('linkyExample', ['ngSanitize'])
$scope.snippet = .controller('ExampleController', ['$scope', function($scope) {
'Pretty text with some links:\n'+ $scope.snippet =
'http://angularjs.org/,\n'+ 'Pretty text with some links:\n'+
'mailto:us@somewhere.org,\n'+ 'http://angularjs.org/,\n'+
'another@somewhere.org,\n'+ 'mailto:us@somewhere.org,\n'+
'and one more: ftp://127.0.0.1/.'; 'another@somewhere.org,\n'+
$scope.snippetWithTarget = 'http://angularjs.org/'; 'and one more: ftp://127.0.0.1/.';
} $scope.snippetWithTarget = 'http://angularjs.org/';
}]);
</script> </script>
<div ng-controller="Ctrl"> <div ng-controller="ExampleController">
Snippet: <textarea ng-model="snippet" cols="60" rows="3"></textarea> Snippet: <textarea ng-model="snippet" cols="60" rows="3"></textarea>
<table> <table>
<tr> <tr>
@@ -574,7 +628,7 @@ angular.module('ngSanitize', []).provider('$sanitize', $SanitizeProvider);
*/ */
angular.module('ngSanitize').filter('linky', ['$sanitize', function($sanitize) { angular.module('ngSanitize').filter('linky', ['$sanitize', function($sanitize) {
var LINKY_URL_REGEXP = var LINKY_URL_REGEXP =
/((ftp|https?):\/\/|(mailto:)?[A-Za-z0-9._%+-]+@)\S*[^\s.;,(){}<>]/, /((ftp|https?):\/\/|(www\.)|(mailto:)?[A-Za-z0-9._%+-]+@)\S*[^\s.;,(){}<>"”’]/,
MAILTO_REGEXP = /^mailto:/; MAILTO_REGEXP = /^mailto:/;
return function(text, target) { return function(text, target) {
@@ -587,8 +641,10 @@ angular.module('ngSanitize').filter('linky', ['$sanitize', function($sanitize) {
while ((match = raw.match(LINKY_URL_REGEXP))) { while ((match = raw.match(LINKY_URL_REGEXP))) {
// We can not end in these as they are sometimes found at the end of the sentence // We can not end in these as they are sometimes found at the end of the sentence
url = match[0]; url = match[0];
// if we did not match ftp/http/mailto then assume mailto // if we did not match ftp/http/www/mailto then assume mailto
if (match[2] == match[3]) url = 'mailto:' + url; if (!match[2] && !match[4]) {
url = (match[3] ? 'http://' : 'mailto:') + url;
}
i = match.index; i = match.index;
addText(raw.substr(0, i)); addText(raw.substr(0, i));
addLink(url, match[0].replace(MAILTO_REGEXP, '')); addLink(url, match[0].replace(MAILTO_REGEXP, ''));
@@ -607,13 +663,13 @@ angular.module('ngSanitize').filter('linky', ['$sanitize', function($sanitize) {
function addLink(url, text) { function addLink(url, text) {
html.push('<a '); html.push('<a ');
if (angular.isDefined(target)) { if (angular.isDefined(target)) {
html.push('target="'); html.push('target="',
html.push(target); target,
html.push('" '); '" ');
} }
html.push('href="'); html.push('href="',
html.push(url); url.replace(/"/g, '&quot;'),
html.push('">'); '">');
addText(text); addText(text);
html.push('</a>'); html.push('</a>');
} }

File diff suppressed because it is too large Load Diff

View File

@@ -1,5 +1,5 @@
/** /**
* @license AngularJS v1.2.16 * @license AngularJS v1.3.7
* (c) 2010-2014 Google, Inc. http://angularjs.org * (c) 2010-2014 Google, Inc. http://angularjs.org
* License: MIT * License: MIT
*/ */
@@ -52,6 +52,20 @@ ngTouch.factory('$swipe', [function() {
// The total distance in any direction before we make the call on swipe vs. scroll. // The total distance in any direction before we make the call on swipe vs. scroll.
var MOVE_BUFFER_RADIUS = 10; var MOVE_BUFFER_RADIUS = 10;
var POINTER_EVENTS = {
'mouse': {
start: 'mousedown',
move: 'mousemove',
end: 'mouseup'
},
'touch': {
start: 'touchstart',
move: 'touchmove',
end: 'touchend',
cancel: 'touchcancel'
}
};
function getCoordinates(event) { function getCoordinates(event) {
var touches = event.touches && event.touches.length ? event.touches : [event]; var touches = event.touches && event.touches.length ? event.touches : [event];
var e = (event.changedTouches && event.changedTouches[0]) || var e = (event.changedTouches && event.changedTouches[0]) ||
@@ -65,6 +79,17 @@ ngTouch.factory('$swipe', [function() {
}; };
} }
function getEvents(pointerTypes, eventType) {
var res = [];
angular.forEach(pointerTypes, function(pointerType) {
var eventName = POINTER_EVENTS[pointerType][eventType];
if (eventName) {
res.push(eventName);
}
});
return res.join(' ');
}
return { return {
/** /**
* @ngdoc method * @ngdoc method
@@ -73,6 +98,9 @@ ngTouch.factory('$swipe', [function() {
* @description * @description
* The main method of `$swipe`. It takes an element to be watched for swipe motions, and an * The main method of `$swipe`. It takes an element to be watched for swipe motions, and an
* object containing event handlers. * object containing event handlers.
* The pointer types that should be used can be specified via the optional
* third argument, which is an array of strings `'mouse'` and `'touch'`. By default,
* `$swipe` will listen for `mouse` and `touch` events.
* *
* The four events are `start`, `move`, `end`, and `cancel`. `start`, `move`, and `end` * The four events are `start`, `move`, `end`, and `cancel`. `start`, `move`, and `end`
* receive as a parameter a coordinates object of the form `{ x: 150, y: 310 }`. * receive as a parameter a coordinates object of the form `{ x: 150, y: 310 }`.
@@ -95,7 +123,7 @@ ngTouch.factory('$swipe', [function() {
* as described above. * as described above.
* *
*/ */
bind: function(element, eventHandlers) { bind: function(element, eventHandlers, pointerTypes) {
// Absolute total movement, used to control swipe vs. scroll. // Absolute total movement, used to control swipe vs. scroll.
var totalX, totalY; var totalX, totalY;
// Coordinates of the start position. // Coordinates of the start position.
@@ -105,7 +133,8 @@ ngTouch.factory('$swipe', [function() {
// Whether a swipe is active. // Whether a swipe is active.
var active = false; var active = false;
element.on('touchstart mousedown', function(event) { pointerTypes = pointerTypes || ['mouse', 'touch'];
element.on(getEvents(pointerTypes, 'start'), function(event) {
startCoords = getCoordinates(event); startCoords = getCoordinates(event);
active = true; active = true;
totalX = 0; totalX = 0;
@@ -113,13 +142,15 @@ ngTouch.factory('$swipe', [function() {
lastPos = startCoords; lastPos = startCoords;
eventHandlers['start'] && eventHandlers['start'](startCoords, event); eventHandlers['start'] && eventHandlers['start'](startCoords, event);
}); });
var events = getEvents(pointerTypes, 'cancel');
if (events) {
element.on(events, function(event) {
active = false;
eventHandlers['cancel'] && eventHandlers['cancel'](event);
});
}
element.on('touchcancel', function(event) { element.on(getEvents(pointerTypes, 'move'), function(event) {
active = false;
eventHandlers['cancel'] && eventHandlers['cancel'](event);
});
element.on('touchmove mousemove', function(event) {
if (!active) return; if (!active) return;
// Android will send a touchcancel if it thinks we're starting to scroll. // Android will send a touchcancel if it thinks we're starting to scroll.
@@ -153,7 +184,7 @@ ngTouch.factory('$swipe', [function() {
} }
}); });
element.on('touchend mouseup', function(event) { element.on(getEvents(pointerTypes, 'end'), function(event) {
if (!active) return; if (!active) return;
active = false; active = false;
eventHandlers['end'] && eventHandlers['end'](getCoordinates(event), event); eventHandlers['end'] && eventHandlers['end'](getCoordinates(event), event);
@@ -187,13 +218,16 @@ ngTouch.factory('$swipe', [function() {
* upon tap. (Event object is available as `$event`) * upon tap. (Event object is available as `$event`)
* *
* @example * @example
<example> <example module="ngClickExample" deps="angular-touch.js">
<file name="index.html"> <file name="index.html">
<button ng-click="count = count + 1" ng-init="count=0"> <button ng-click="count = count + 1" ng-init="count=0">
Increment Increment
</button> </button>
count: {{ count }} count: {{ count }}
</file> </file>
<file name="script.js">
angular.module('ngClickExample', ['ngTouch']);
</file>
</example> </example>
*/ */
@@ -230,7 +264,7 @@ ngTouch.directive('ngClick', ['$parse', '$timeout', '$rootElement',
// //
// What happens when the browser then generates a click event? // What happens when the browser then generates a click event?
// The browser, of course, also detects the tap and fires a click after a delay. This results in // The browser, of course, also detects the tap and fires a click after a delay. This results in
// tapping/clicking twice. So we do "clickbusting" to prevent it. // tapping/clicking twice. We do "clickbusting" to prevent it.
// //
// How does it work? // How does it work?
// We attach global touchstart and click handlers, that run during the capture (early) phase. // We attach global touchstart and click handlers, that run during the capture (early) phase.
@@ -253,9 +287,9 @@ ngTouch.directive('ngClick', ['$parse', '$timeout', '$rootElement',
// encapsulates this ugly logic away from the user. // encapsulates this ugly logic away from the user.
// //
// Why not just put click handlers on the element? // Why not just put click handlers on the element?
// We do that too, just to be sure. The problem is that the tap event might have caused the DOM // We do that too, just to be sure. If the tap event caused the DOM to change,
// to change, so that the click fires in the same position but something else is there now. So // it is possible another element is now in that position. To take account for these possibly
// the handlers are global and care only about coordinates and not elements. // distinct elements, the handlers are global and care only about coordinates.
// Checks if the coordinates are close enough to be within the region. // Checks if the coordinates are close enough to be within the region.
function hit(x1, y1, x2, y2) { function hit(x1, y1, x2, y2) {
@@ -267,7 +301,7 @@ ngTouch.directive('ngClick', ['$parse', '$timeout', '$rootElement',
// Splices out the allowable region from the list after it has been used. // Splices out the allowable region from the list after it has been used.
function checkAllowableRegions(touchCoordinates, x, y) { function checkAllowableRegions(touchCoordinates, x, y) {
for (var i = 0; i < touchCoordinates.length; i += 2) { for (var i = 0; i < touchCoordinates.length; i += 2) {
if (hit(touchCoordinates[i], touchCoordinates[i+1], x, y)) { if (hit(touchCoordinates[i], touchCoordinates[i + 1], x, y)) {
touchCoordinates.splice(i, i + 2); touchCoordinates.splice(i, i + 2);
return true; // allowable region return true; // allowable region
} }
@@ -332,7 +366,7 @@ ngTouch.directive('ngClick', ['$parse', '$timeout', '$rootElement',
$timeout(function() { $timeout(function() {
// Remove the allowable region. // Remove the allowable region.
for (var i = 0; i < touchCoordinates.length; i += 2) { for (var i = 0; i < touchCoordinates.length; i += 2) {
if (touchCoordinates[i] == x && touchCoordinates[i+1] == y) { if (touchCoordinates[i] == x && touchCoordinates[i + 1] == y) {
touchCoordinates.splice(i, i + 2); touchCoordinates.splice(i, i + 2);
return; return;
} }
@@ -372,7 +406,7 @@ ngTouch.directive('ngClick', ['$parse', '$timeout', '$rootElement',
tapping = true; tapping = true;
tapElement = event.target ? event.target : event.srcElement; // IE uses srcElement. tapElement = event.target ? event.target : event.srcElement; // IE uses srcElement.
// Hack for Safari, which can target text nodes instead of containers. // Hack for Safari, which can target text nodes instead of containers.
if(tapElement.nodeType == 3) { if (tapElement.nodeType == 3) {
tapElement = tapElement.parentNode; tapElement = tapElement.parentNode;
} }
@@ -402,7 +436,7 @@ ngTouch.directive('ngClick', ['$parse', '$timeout', '$rootElement',
var e = touches[0].originalEvent || touches[0]; var e = touches[0].originalEvent || touches[0];
var x = e.clientX; var x = e.clientX;
var y = e.clientY; var y = e.clientY;
var dist = Math.sqrt( Math.pow(x - touchStartX, 2) + Math.pow(y - touchStartY, 2) ); var dist = Math.sqrt(Math.pow(x - touchStartX, 2) + Math.pow(y - touchStartY, 2));
if (tapping && diff < TAP_DURATION && dist < MOVE_TOLERANCE) { if (tapping && diff < TAP_DURATION && dist < MOVE_TOLERANCE) {
// Call preventGhostClick so the clickbuster will catch the corresponding click. // Call preventGhostClick so the clickbuster will catch the corresponding click.
@@ -462,6 +496,9 @@ ngTouch.directive('ngClick', ['$parse', '$timeout', '$rootElement',
* Though ngSwipeLeft is designed for touch-based devices, it will work with a mouse click and drag * Though ngSwipeLeft is designed for touch-based devices, it will work with a mouse click and drag
* too. * too.
* *
* To disable the mouse click and drag functionality, add `ng-swipe-disable-mouse` to
* the `ng-swipe-left` or `ng-swipe-right` DOM Element.
*
* Requires the {@link ngTouch `ngTouch`} module to be installed. * Requires the {@link ngTouch `ngTouch`} module to be installed.
* *
* @element ANY * @element ANY
@@ -469,7 +506,7 @@ ngTouch.directive('ngClick', ['$parse', '$timeout', '$rootElement',
* upon left swipe. (Event object is available as `$event`) * upon left swipe. (Event object is available as `$event`)
* *
* @example * @example
<example> <example module="ngSwipeLeftExample" deps="angular-touch.js">
<file name="index.html"> <file name="index.html">
<div ng-show="!showActions" ng-swipe-left="showActions = true"> <div ng-show="!showActions" ng-swipe-left="showActions = true">
Some list content, like an email in the inbox Some list content, like an email in the inbox
@@ -479,6 +516,9 @@ ngTouch.directive('ngClick', ['$parse', '$timeout', '$rootElement',
<button ng-click="delete()">Delete</button> <button ng-click="delete()">Delete</button>
</div> </div>
</file> </file>
<file name="script.js">
angular.module('ngSwipeLeftExample', ['ngTouch']);
</file>
</example> </example>
*/ */
@@ -499,7 +539,7 @@ ngTouch.directive('ngClick', ['$parse', '$timeout', '$rootElement',
* upon right swipe. (Event object is available as `$event`) * upon right swipe. (Event object is available as `$event`)
* *
* @example * @example
<example> <example module="ngSwipeRightExample" deps="angular-touch.js">
<file name="index.html"> <file name="index.html">
<div ng-show="!showActions" ng-swipe-left="showActions = true"> <div ng-show="!showActions" ng-swipe-left="showActions = true">
Some list content, like an email in the inbox Some list content, like an email in the inbox
@@ -509,6 +549,9 @@ ngTouch.directive('ngClick', ['$parse', '$timeout', '$rootElement',
<button ng-click="delete()">Delete</button> <button ng-click="delete()">Delete</button>
</div> </div>
</file> </file>
<file name="script.js">
angular.module('ngSwipeRightExample', ['ngTouch']);
</file>
</example> </example>
*/ */
@@ -545,6 +588,10 @@ function makeSwipeDirective(directiveName, direction, eventName) {
deltaY / deltaX < MAX_VERTICAL_RATIO; deltaY / deltaX < MAX_VERTICAL_RATIO;
} }
var pointerTypes = ['touch'];
if (!angular.isDefined(attr['ngSwipeDisableMouse'])) {
pointerTypes.push('mouse');
}
$swipe.bind(element, { $swipe.bind(element, {
'start': function(coords, event) { 'start': function(coords, event) {
startCoords = coords; startCoords = coords;
@@ -561,7 +608,7 @@ function makeSwipeDirective(directiveName, direction, eventName) {
}); });
} }
} }
}); }, pointerTypes);
}; };
}]); }]);
} }

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

View File

@@ -1 +1 @@
{"raw":"v1.2.16","major":1,"minor":2,"patch":16,"prerelease":[],"build":[],"version":"1.2.16","codeName":"badger-enumeration","full":"1.2.16","cdn":{"raw":"v1.2.15","major":1,"minor":2,"patch":15,"prerelease":[],"build":[],"version":"1.2.15","isStable":true,"docsUrl":"http://code.angularjs.org/1.2.15/docs"}} {"raw":"v1.3.7","major":1,"minor":3,"patch":7,"prerelease":[],"build":[],"version":"1.3.7","codeName":"leaky-obstruction","full":"1.3.7","branch":"v1.3.x","cdn":{"raw":"v1.3.6","major":1,"minor":3,"patch":6,"prerelease":[],"build":[],"version":"1.3.6","docsUrl":"http://code.angularjs.org/1.3.6/docs"}}

View File

@@ -1 +1 @@
1.2.16 1.3.7