mirror of
https://github.com/Hopiu/angular.js.git
synced 2026-03-16 23:30:23 +00:00
refactor(services): migrate angular.service -> module
This commit is contained in:
parent
ed36b9da3b
commit
bd04316a89
43 changed files with 2250 additions and 2220 deletions
2
angularFiles.js
vendored
2
angularFiles.js
vendored
|
|
@ -70,7 +70,6 @@ angularFiles = {
|
|||
'src/jstd-scenario-adapter/*.js',
|
||||
'src/scenario/*.js',
|
||||
'src/angular-mocks.js',
|
||||
'test/mocks.js',
|
||||
'test/scenario/*.js',
|
||||
'test/scenario/output/*.js',
|
||||
'test/jstd-scenario-adapter/*.js',
|
||||
|
|
@ -133,7 +132,6 @@ angularFiles = {
|
|||
'src/jstd-scenario-adapter/*.js',
|
||||
'src/scenario/*.js',
|
||||
'src/angular-mocks.js',
|
||||
'test/mocks.js',
|
||||
'test/scenario/*.js',
|
||||
'test/scenario/output/*.js',
|
||||
'test/jstd-scenario-adapter/*.js',
|
||||
|
|
|
|||
|
|
@ -148,9 +148,12 @@ function TutorialInstructionsCtrl($cookieStore) {
|
|||
};
|
||||
}
|
||||
|
||||
angular.service('$locationConfig', function() {
|
||||
return {
|
||||
window.angular = window.angular || {};
|
||||
angular.module = angular.module || {};
|
||||
|
||||
angular.module.ngdocs = function($provide) {
|
||||
$provide.value('$locationConfig', {
|
||||
html5Mode: true,
|
||||
hashPrefix: '!'
|
||||
};
|
||||
});
|
||||
});
|
||||
};
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ describe('example.personalLog.LogCtrl', function() {
|
|||
var logCtrl;
|
||||
|
||||
function createNotesCtrl() {
|
||||
var injector = angular.injector('NG');
|
||||
var injector = angular.injector('NG', 'NG_MOCK');
|
||||
var scope = injector('$rootScope');
|
||||
scope.$cookies = injector('$cookies');
|
||||
return scope.$new(example.personalLog.LogCtrl);
|
||||
|
|
|
|||
|
|
@ -99,8 +99,8 @@ var _undefined = undefined,
|
|||
: noop,
|
||||
|
||||
/** @name angular */
|
||||
angular = window[$angular] || (window[$angular] = {}),
|
||||
angularModules = angular.modules || (angular.modules = {}),
|
||||
angular = window.angular || (window.angular = {}),
|
||||
angularModule = angular.module || (angular.module = {}),
|
||||
/** @name angular.markup */
|
||||
angularTextMarkup = extensionMap(angular, 'markup'),
|
||||
/** @name angular.attrMarkup */
|
||||
|
|
@ -114,7 +114,6 @@ var _undefined = undefined,
|
|||
/** @name angular.service */
|
||||
angularInputType = extensionMap(angular, 'inputType', lowercase),
|
||||
/** @name angular.service */
|
||||
angularService = extensionMap(angular, 'service'),
|
||||
angularCallbacks = extensionMap(angular, 'callbacks'),
|
||||
nodeName_,
|
||||
uid = ['0', '0', '0'],
|
||||
|
|
@ -188,18 +187,6 @@ function forEachSorted(obj, iterator, context) {
|
|||
}
|
||||
|
||||
|
||||
function formatError(arg) {
|
||||
if (arg instanceof Error) {
|
||||
if (arg.stack) {
|
||||
arg = (arg.message && arg.stack.indexOf(arg.message) === -1) ?
|
||||
'Error: ' + arg.message + '\n' + arg.stack : arg.stack;
|
||||
} else if (arg.sourceURL) {
|
||||
arg = arg.message + '\n' + arg.sourceURL + ':' + arg.line;
|
||||
}
|
||||
}
|
||||
return arg;
|
||||
}
|
||||
|
||||
/**
|
||||
* A consistent way of creating unique IDs in angular. The ID is a sequence of alpha numeric
|
||||
* characters such as '012ABC'. The reason why we are not using simply a number counter is that
|
||||
|
|
@ -957,7 +944,7 @@ function angularInit(config, document){
|
|||
modules.push(module);
|
||||
}
|
||||
});
|
||||
createInjector(modules, angularModules)(['$rootScope', '$compile', function(scope, compile){
|
||||
createInjector(modules, angularModule)(['$rootScope', '$compile', '$injector', function(scope, compile, injector){
|
||||
scope.$apply(function(){
|
||||
compile(isString(autobind) ? document.getElementById(autobind) : document)(scope);
|
||||
});
|
||||
|
|
@ -1030,7 +1017,7 @@ function publishExternalAPI(angular){
|
|||
'extend': extend,
|
||||
'equals': equals,
|
||||
'forEach': forEach,
|
||||
'injector': function(){ return createInjector(arguments, angularModules); },
|
||||
'injector': function(){ return createInjector(arguments, angularModule); },
|
||||
'noop':noop,
|
||||
'bind':bind,
|
||||
'toJson': toJson,
|
||||
|
|
@ -1049,14 +1036,39 @@ function publishExternalAPI(angular){
|
|||
'uppercase': uppercase
|
||||
});
|
||||
|
||||
angularModules.NG = ngModule;
|
||||
angularModule.NG = ngModule;
|
||||
}
|
||||
|
||||
ngModule.$inject = ['$provide'];
|
||||
function ngModule($provide) {
|
||||
forEach(angularService, function(factory, name){
|
||||
$provide.factory(name, factory);
|
||||
});
|
||||
ngModule.$inject = ['$provide', '$injector'];
|
||||
function ngModule($provide, $injector) {
|
||||
// TODO(misko): temporary services to get the compiler working;
|
||||
$provide.value('$textMarkup', angularTextMarkup);
|
||||
$provide.value('$attrMarkup', angularAttrMarkup);
|
||||
$provide.value('$directive', angularDirective);
|
||||
$provide.value('$widget', angularWidget);
|
||||
|
||||
$provide.service('$browser', $BrowserProvider);
|
||||
$provide.service('$compile', $CompileProvider);
|
||||
$provide.service('$cookies', $CookiesProvider);
|
||||
$provide.service('$cookieStore', $CookieStoreProvider);
|
||||
$provide.service('$defer', $DeferProvider);
|
||||
$provide.service('$document', $DocumentProvider);
|
||||
$provide.service('$exceptionHandler', $ExceptionHandlerProvider);
|
||||
$provide.service('$formFactory', $FormFactoryProvider);
|
||||
$provide.service('$locale', $LocaleProvider);
|
||||
$provide.service('$location', $LocationProvider);
|
||||
$provide.service('$locationConfig', $LocationConfigProvider);
|
||||
$provide.service('$log', $LogProvider);
|
||||
$provide.service('$resource', $ResourceProvider);
|
||||
$provide.service('$route', $RouteProvider);
|
||||
$provide.service('$routeParams', $RouteParamsProvider);
|
||||
$provide.service('$rootScope', $RootScopeProvider);
|
||||
$provide.service('$sniffer', $SnifferProvider);
|
||||
$provide.service('$window', $WindowProvider);
|
||||
$provide.service('$xhr.bulk', $XhrBulkProvider);
|
||||
$provide.service('$xhr.cache', $XhrCacheProvider);
|
||||
$provide.service('$xhr.error', $XhrErrorProvider);
|
||||
$provide.service('$xhr', $XhrProvider);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -1,16 +1,5 @@
|
|||
'use strict';
|
||||
|
||||
var browserSingleton;
|
||||
|
||||
angularService('$browser', function($log, $sniffer) {
|
||||
if (!browserSingleton) {
|
||||
browserSingleton = new Browser(window, jqLite(window.document), jqLite(window.document.body),
|
||||
XHR, $log, $sniffer);
|
||||
}
|
||||
return browserSingleton;
|
||||
}, {$inject: ['$log', '$sniffer']});
|
||||
|
||||
|
||||
publishExternalAPI(angular);
|
||||
|
||||
//try to bind to jquery now so that one can write angular.element().read()
|
||||
|
|
|
|||
|
|
@ -473,3 +473,10 @@ function Browser(window, document, body, XHR, $log, $sniffer) {
|
|||
return href ? href.replace(/^https?\:\/\/[^\/]*/, '') : href;
|
||||
};
|
||||
}
|
||||
|
||||
function $BrowserProvider(){
|
||||
this.$get = ['$window', '$log', '$sniffer', '$document',
|
||||
function( $window, $log, $sniffer, $document){
|
||||
return new Browser($window, $document, $document.find('body'), XHR, $log, $sniffer);
|
||||
}];
|
||||
}
|
||||
|
|
|
|||
|
|
@ -33,10 +33,6 @@
|
|||
* `injector.eager()`
|
||||
*/
|
||||
|
||||
function angularServiceInject(name, fn, inject, eager) {
|
||||
angularService(name, fn, {$inject:inject, $eager:eager});
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @returns the $inject property of function. If not found the
|
||||
|
|
@ -177,7 +173,11 @@ function createInjector(modulesToLoad, moduleRegistry) {
|
|||
|
||||
forEach(modulesToLoad, function(module){
|
||||
if (isString(module)) {
|
||||
module = moduleRegistry[module];
|
||||
if (moduleRegistry[module]) {
|
||||
module = moduleRegistry[module];
|
||||
} else {
|
||||
throw Error("Module '" + module + "' is not defined!");
|
||||
}
|
||||
}
|
||||
if (isFunction(module) || isArray(module)) {
|
||||
$injector(module);
|
||||
|
|
|
|||
127
src/angular-mocks.js
vendored
127
src/angular-mocks.js
vendored
|
|
@ -1,4 +1,3 @@
|
|||
'use strict';
|
||||
|
||||
/**
|
||||
* @license AngularJS v"NG_VERSION_FULL"
|
||||
|
|
@ -8,30 +7,6 @@
|
|||
|
||||
|
||||
/*
|
||||
|
||||
NUGGGGGH MUST TONGUE WANGS
|
||||
\
|
||||
.....
|
||||
C C /
|
||||
/< /
|
||||
___ __________/_#__=o
|
||||
/(- /(\_\________ \
|
||||
\ ) \ )_ \o \
|
||||
/|\ /|\ |' |
|
||||
| _|
|
||||
/o __\
|
||||
/ ' |
|
||||
/ / |
|
||||
/_/\______|
|
||||
( _( <
|
||||
\ \ \
|
||||
\ \ |
|
||||
\____\____\
|
||||
____\_\__\_\
|
||||
/` /` o\
|
||||
|___ |_______|.. . b'ger
|
||||
|
||||
|
||||
IN THE FINAL BUILD THIS FILE DOESN'T HAVE DIRECT ACCESS TO GLOBAL FUNCTIONS
|
||||
DEFINED IN Angular.js YOU *MUST* REFER TO THEM VIA angular OBJECT
|
||||
(e.g. angular.forEach(...)) AND MAKE SURE THAT THE GIVEN FUNCTION IS EXPORTED
|
||||
|
|
@ -56,8 +31,15 @@
|
|||
* the angular service exception handler.
|
||||
* * {@link angular.mock.service.$log $log } - A mock implementation of the angular service log.
|
||||
*/
|
||||
angular.mock = {};
|
||||
window.angular = window.angular || {};
|
||||
angular.module = angular.module || {};
|
||||
angular.mock = angular.mock || {};
|
||||
|
||||
angular.module.NG_MOCK = ['$provide', function($provide){
|
||||
$provide.service('$browser', angular.mock.$BrowserProvider);
|
||||
$provide.service('$exceptionHandler', angular.mock.$ExceptionHandlerProvider);
|
||||
$provide.service('$log', angular.mock.$LogProvider);
|
||||
}];
|
||||
|
||||
/**
|
||||
* @ngdoc service
|
||||
|
|
@ -81,7 +63,12 @@ angular.mock = {};
|
|||
* - $browser.defer — enables testing of code that uses
|
||||
* {@link angular.service.$defer $defer service} for executing functions via the `setTimeout` api.
|
||||
*/
|
||||
function MockBrowser() {
|
||||
angular.mock.$BrowserProvider = function(){
|
||||
this.$get = function(){
|
||||
return new angular.mock.$Browser();
|
||||
};
|
||||
};
|
||||
angular.mock.$Browser = function() {
|
||||
var self = this,
|
||||
expectations = {},
|
||||
requests = [];
|
||||
|
|
@ -309,7 +296,7 @@ function MockBrowser() {
|
|||
return this.$$baseHref;
|
||||
};
|
||||
}
|
||||
MockBrowser.prototype = {
|
||||
angular.mock.$Browser.prototype = {
|
||||
|
||||
/**
|
||||
* @name angular.mock.service.$browser#poll
|
||||
|
|
@ -360,10 +347,6 @@ MockBrowser.prototype = {
|
|||
addJs: function() {}
|
||||
};
|
||||
|
||||
angular.service('$browser', function() {
|
||||
return new MockBrowser();
|
||||
});
|
||||
|
||||
|
||||
/**
|
||||
* @ngdoc service
|
||||
|
|
@ -376,9 +359,29 @@ angular.service('$browser', function() {
|
|||
*
|
||||
* See {@link angular.mock} for more info on angular mocks.
|
||||
*/
|
||||
angular.service('$exceptionHandler', function() {
|
||||
return function(e) { throw e; };
|
||||
});
|
||||
angular.mock.$ExceptionHandlerProvider = function(){
|
||||
var handler;
|
||||
|
||||
this.mode = function(mode){
|
||||
handler = {
|
||||
rethrow: function(e) {
|
||||
throw e;
|
||||
},
|
||||
log: angular.extend(function log(e) {
|
||||
log.errors.push(e);
|
||||
}, {errors:[]})
|
||||
}[mode];
|
||||
if (!handler) {
|
||||
throw Error("Unknown mode '" + mode + "', only 'log'/'rethrow' modes are allowed!");
|
||||
}
|
||||
};
|
||||
|
||||
this.$get = function(){
|
||||
return handler;
|
||||
};
|
||||
|
||||
this.mode('rethrow');
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
|
|
@ -392,23 +395,43 @@ angular.service('$exceptionHandler', function() {
|
|||
*
|
||||
* See {@link angular.mock} for more info on angular mocks.
|
||||
*/
|
||||
angular.service('$log', MockLogFactory);
|
||||
angular.mock.$LogProvider = function(){
|
||||
this.$get = function () {
|
||||
var $log = {
|
||||
log: function() { $log.log.logs.push(concat([], arguments, 0)); },
|
||||
warn: function() { $log.warn.logs.push(concat([], arguments, 0)); },
|
||||
info: function() { $log.info.logs.push(concat([], arguments, 0)); },
|
||||
error: function() { $log.error.logs.push(concat([], arguments, 0)); }
|
||||
};
|
||||
|
||||
function MockLogFactory() {
|
||||
var $log = {
|
||||
log: function() { $log.log.logs.push(arguments); },
|
||||
warn: function() { $log.warn.logs.push(arguments); },
|
||||
info: function() { $log.info.logs.push(arguments); },
|
||||
error: function() { $log.error.logs.push(arguments); }
|
||||
$log.reset = function (){
|
||||
$log.log.logs = [];
|
||||
$log.warn.logs = [];
|
||||
$log.info.logs = [];
|
||||
$log.error.logs = [];
|
||||
};
|
||||
|
||||
$log.assertEmpty = function(){
|
||||
var errors = [];
|
||||
angular.forEach(['error', 'warn', 'info', 'log'], function(logLevel) {
|
||||
angular.forEach($log[logLevel].logs, function(log) {
|
||||
angular.forEach(log, function (logItem) {
|
||||
errors.push('MOCK $log (' + logLevel + '): ' + (logItem.stack || logItem));
|
||||
});
|
||||
});
|
||||
});
|
||||
if (errors.length) {
|
||||
errors.unshift("Expected $log to be empty! Either a message was logged unexpectedly, or an expected " +
|
||||
"log message was not checked and removed:");
|
||||
errors.push('')
|
||||
throw new Error(errors.join('\n---------\n'));
|
||||
}
|
||||
};
|
||||
|
||||
$log.reset();
|
||||
return $log;
|
||||
};
|
||||
|
||||
$log.log.logs = [];
|
||||
$log.warn.logs = [];
|
||||
$log.info.logs = [];
|
||||
$log.error.logs = [];
|
||||
|
||||
return $log;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
|
|
@ -441,7 +464,7 @@ function MockLogFactory() {
|
|||
* </pre>
|
||||
*
|
||||
*/
|
||||
function TzDate(offset, timestamp) {
|
||||
angular.mock.TzDate = function (offset, timestamp) {
|
||||
if (angular.isString(timestamp)) {
|
||||
var tsStr = timestamp;
|
||||
|
||||
|
|
@ -545,4 +568,4 @@ function TzDate(offset, timestamp) {
|
|||
}
|
||||
|
||||
//make "tzDateInstance instanceof Date" return true
|
||||
TzDate.prototype = Date.prototype;
|
||||
angular.mock.TzDate.prototype = Date.prototype;
|
||||
|
|
|
|||
|
|
@ -1,325 +1,322 @@
|
|||
'use strict';
|
||||
|
||||
// TODO(misko): temporary services to get the compiler working;
|
||||
angularService('$textMarkup', valueFn(angularTextMarkup));
|
||||
angularService('$attrMarkup', valueFn(angularAttrMarkup));
|
||||
angularService('$directive', valueFn(angularDirective));
|
||||
angularService('$widget', valueFn(angularWidget));
|
||||
|
||||
angularServiceInject('$compile', function($injector, $exceptionHandler, $textMarkup, $attrMarkup, $directive, $widget){
|
||||
/**
|
||||
* Template provides directions an how to bind to a given element.
|
||||
* It contains a list of init functions which need to be called to
|
||||
* bind to a new instance of elements. It also provides a list
|
||||
* of child paths which contain child templates
|
||||
*/
|
||||
function Template() {
|
||||
this.paths = [];
|
||||
this.children = [];
|
||||
this.linkFns = [];
|
||||
this.newScope = false;
|
||||
}
|
||||
|
||||
Template.prototype = {
|
||||
link: function(element, scope) {
|
||||
var childScope = scope,
|
||||
locals = {$element: element};
|
||||
if (this.newScope) {
|
||||
childScope = isFunction(this.newScope) ? scope.$new(this.newScope(scope)) : scope.$new();
|
||||
element.data($$scope, childScope);
|
||||
function $CompileProvider(){
|
||||
this.$get = ['$injector', '$exceptionHandler', '$textMarkup', '$attrMarkup', '$directive', '$widget',
|
||||
function( $injector, $exceptionHandler, $textMarkup, $attrMarkup, $directive, $widget){
|
||||
/**
|
||||
* Template provides directions an how to bind to a given element.
|
||||
* It contains a list of init functions which need to be called to
|
||||
* bind to a new instance of elements. It also provides a list
|
||||
* of child paths which contain child templates
|
||||
*/
|
||||
function Template() {
|
||||
this.paths = [];
|
||||
this.children = [];
|
||||
this.linkFns = [];
|
||||
this.newScope = false;
|
||||
}
|
||||
forEach(this.linkFns, function(fn) {
|
||||
try {
|
||||
$injector.invoke(childScope, fn, locals);
|
||||
} catch (e) {
|
||||
$exceptionHandler(e);
|
||||
}
|
||||
});
|
||||
var i,
|
||||
childNodes = element[0].childNodes,
|
||||
children = this.children,
|
||||
paths = this.paths,
|
||||
length = paths.length;
|
||||
for (i = 0; i < length; i++) {
|
||||
// sometimes `element` can be modified by one of the linker functions in `this.linkFns`
|
||||
// and childNodes may be added or removed
|
||||
// TODO: element structure needs to be re-evaluated if new children added
|
||||
// if the childNode still exists
|
||||
if (childNodes[paths[i]])
|
||||
children[i].link(jqLite(childNodes[paths[i]]), childScope);
|
||||
else
|
||||
delete paths[i]; // if child no longer available, delete path
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
addLinkFn:function(linkingFn) {
|
||||
if (linkingFn) {
|
||||
//TODO(misko): temporary hack.
|
||||
if (isFunction(linkingFn) && !linkingFn.$inject) {
|
||||
linkingFn.$inject = ['$element'];
|
||||
}
|
||||
this.linkFns.push(linkingFn);
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
addChild: function(index, template) {
|
||||
if (template) {
|
||||
this.paths.push(index);
|
||||
this.children.push(template);
|
||||
}
|
||||
},
|
||||
|
||||
empty: function() {
|
||||
return this.linkFns.length === 0 && this.paths.length === 0;
|
||||
}
|
||||
};
|
||||
|
||||
///////////////////////////////////
|
||||
//Compiler
|
||||
//////////////////////////////////
|
||||
|
||||
/**
|
||||
* @ngdoc function
|
||||
* @name angular.compile
|
||||
* @function
|
||||
*
|
||||
* @description
|
||||
* Compiles a piece of HTML string or DOM into a template and produces a template function, which
|
||||
* can then be used to link {@link angular.scope scope} and the template together.
|
||||
*
|
||||
* The compilation is a process of walking the DOM tree and trying to match DOM elements to
|
||||
* {@link angular.markup markup}, {@link angular.attrMarkup attrMarkup},
|
||||
* {@link angular.widget widgets}, and {@link angular.directive directives}. For each match it
|
||||
* executes corresponding markup, attrMarkup, widget or directive template function and collects the
|
||||
* instance functions into a single template function which is then returned.
|
||||
*
|
||||
* The template function can then be used once to produce the view or as it is the case with
|
||||
* {@link angular.widget.@ng:repeat repeater} many-times, in which case each call results in a view
|
||||
* that is a DOM clone of the original template.
|
||||
*
|
||||
<pre>
|
||||
// compile the entire window.document and give me the scope bound to this template.
|
||||
var rootScope = angular.compile(window.document)();
|
||||
|
||||
// compile a piece of html
|
||||
var rootScope2 = angular.compile('<div ng:click="clicked = true">click me</div>')();
|
||||
|
||||
|
||||
// compile a piece of html and retain reference to both the dom and scope
|
||||
var template = angular.element('<div ng:click="clicked = true">click me</div>'),
|
||||
scope = angular.compile(template)();
|
||||
// at this point template was transformed into a view
|
||||
</pre>
|
||||
*
|
||||
*
|
||||
* @param {string|DOMElement} element Element or HTML to compile into a template function.
|
||||
* @returns {function(scope[, cloneAttachFn])} a template function which is used to bind template
|
||||
* (a DOM element/tree) to a scope. Where:
|
||||
*
|
||||
* * `scope` - A {@link angular.scope Scope} to bind to.
|
||||
* * `cloneAttachFn` - If `cloneAttachFn` is provided, then the link function will clone the
|
||||
* `template` and call the `cloneAttachFn` function allowing the caller to attach the
|
||||
* cloned elements to the DOM document at the appropriate place. The `cloneAttachFn` is
|
||||
* called as: <br/> `cloneAttachFn(clonedElement, scope)` where:
|
||||
*
|
||||
* * `clonedElement` - is a clone of the original `element` passed into the compiler.
|
||||
* * `scope` - is the current scope with which the linking function is working with.
|
||||
*
|
||||
* Calling the template function returns the element of the template. It is either the original element
|
||||
* passed in, or the clone of the element if the `cloneAttachFn` is provided.
|
||||
*
|
||||
* It is important to understand that the returned scope is "linked" to the view DOM, but no linking
|
||||
* (instance) functions registered by {@link angular.directive directives} or
|
||||
* {@link angular.widget widgets} found in the template have been executed yet. This means that the
|
||||
* view is likely empty and doesn't contain any values that result from evaluation on the scope. To
|
||||
* bring the view to life, the scope needs to run through a $digest phase which typically is done by
|
||||
* Angular automatically, except for the case when an application is being
|
||||
* {@link guide/dev_guide.bootstrap.manual_bootstrap} manually bootstrapped, in which case the
|
||||
* $digest phase must be invoked by calling {@link angular.scope.$apply}.
|
||||
*
|
||||
* If you need access to the bound view, there are two ways to do it:
|
||||
*
|
||||
* - If you are not asking the linking function to clone the template, create the DOM element(s)
|
||||
* before you send them to the compiler and keep this reference around.
|
||||
* <pre>
|
||||
* var scope = angular.injector()('$rootScope');
|
||||
* var element = angular.compile('<p>{{total}}</p>')(scope);
|
||||
* </pre>
|
||||
*
|
||||
* - if on the other hand, you need the element to be cloned, the view reference from the original
|
||||
* example would not point to the clone, but rather to the original template that was cloned. In
|
||||
* this case, you can access the clone via the cloneAttachFn:
|
||||
* <pre>
|
||||
* var original = angular.element('<p>{{total}}</p>'),
|
||||
* scope = someParentScope.$new(),
|
||||
* clone;
|
||||
*
|
||||
* angular.compile(original)(scope, function(clonedElement, scope) {
|
||||
* clone = clonedElement;
|
||||
* //attach the clone to DOM document at the right place
|
||||
* });
|
||||
*
|
||||
* //now we have reference to the cloned DOM via `clone`
|
||||
* </pre>
|
||||
*
|
||||
*
|
||||
* Compiler Methods For Widgets and Directives:
|
||||
*
|
||||
* The following methods are available for use when you write your own widgets, directives,
|
||||
* and markup. (Recall that the compile function's this is a reference to the compiler.)
|
||||
*
|
||||
* `compile(element)` - returns linker -
|
||||
* Invoke a new instance of the compiler to compile a DOM element and return a linker function.
|
||||
* You can apply the linker function to the original element or a clone of the original element.
|
||||
* The linker function returns a scope.
|
||||
*
|
||||
* * `comment(commentText)` - returns element - Create a comment element.
|
||||
*
|
||||
* * `element(elementName)` - returns element - Create an element by name.
|
||||
*
|
||||
* * `text(text)` - returns element - Create a text element.
|
||||
*
|
||||
* * `descend([set])` - returns descend state (true or false). Get or set the current descend
|
||||
* state. If true the compiler will descend to children elements.
|
||||
*
|
||||
* * `directives([set])` - returns directive state (true or false). Get or set the current
|
||||
* directives processing state. The compiler will process directives only when directives set to
|
||||
* true.
|
||||
*
|
||||
* For information on how the compiler works, see the
|
||||
* {@link guide/dev_guide.compiler Angular HTML Compiler} section of the Developer Guide.
|
||||
*/
|
||||
function Compiler(markup, attrMarkup, directives, widgets){
|
||||
this.markup = markup;
|
||||
this.attrMarkup = attrMarkup;
|
||||
this.directives = directives;
|
||||
this.widgets = widgets;
|
||||
}
|
||||
|
||||
Compiler.prototype = {
|
||||
compile: function(templateElement) {
|
||||
templateElement = jqLite(templateElement);
|
||||
var index = 0,
|
||||
template,
|
||||
parent = templateElement.parent();
|
||||
if (templateElement.length > 1) {
|
||||
// https://github.com/angular/angular.js/issues/338
|
||||
throw Error("Cannot compile multiple element roots: " +
|
||||
jqLite('<div>').append(templateElement.clone()).html());
|
||||
}
|
||||
if (parent && parent[0]) {
|
||||
parent = parent[0];
|
||||
for(var i = 0; i < parent.childNodes.length; i++) {
|
||||
if (parent.childNodes[i] == templateElement[0]) {
|
||||
index = i;
|
||||
Template.prototype = {
|
||||
link: function(element, scope) {
|
||||
var childScope = scope,
|
||||
locals = {$element: element};
|
||||
if (this.newScope) {
|
||||
childScope = isFunction(this.newScope) ? scope.$new(this.newScope(scope)) : scope.$new();
|
||||
element.data($$scope, childScope);
|
||||
}
|
||||
forEach(this.linkFns, function(fn) {
|
||||
try {
|
||||
$injector.invoke(childScope, fn, locals);
|
||||
} catch (e) {
|
||||
$exceptionHandler(e);
|
||||
}
|
||||
});
|
||||
var i,
|
||||
childNodes = element[0].childNodes,
|
||||
children = this.children,
|
||||
paths = this.paths,
|
||||
length = paths.length;
|
||||
for (i = 0; i < length; i++) {
|
||||
// sometimes `element` can be modified by one of the linker functions in `this.linkFns`
|
||||
// and childNodes may be added or removed
|
||||
// TODO: element structure needs to be re-evaluated if new children added
|
||||
// if the childNode still exists
|
||||
if (childNodes[paths[i]])
|
||||
children[i].link(jqLite(childNodes[paths[i]]), childScope);
|
||||
else
|
||||
delete paths[i]; // if child no longer available, delete path
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
addLinkFn:function(linkingFn) {
|
||||
if (linkingFn) {
|
||||
//TODO(misko): temporary hack.
|
||||
if (isFunction(linkingFn) && !linkingFn.$inject) {
|
||||
linkingFn.$inject = ['$element'];
|
||||
}
|
||||
this.linkFns.push(linkingFn);
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
addChild: function(index, template) {
|
||||
if (template) {
|
||||
this.paths.push(index);
|
||||
this.children.push(template);
|
||||
}
|
||||
},
|
||||
|
||||
empty: function() {
|
||||
return this.linkFns.length === 0 && this.paths.length === 0;
|
||||
}
|
||||
}
|
||||
template = this.templatize(templateElement, index) || new Template();
|
||||
return function(scope, cloneConnectFn){
|
||||
assertArg(scope, 'scope');
|
||||
// important!!: we must call our jqLite.clone() since the jQuery one is trying to be smart
|
||||
// and sometimes changes the structure of the DOM.
|
||||
var element = cloneConnectFn
|
||||
? JQLitePrototype.clone.call(templateElement) // IMPORTANT!!!
|
||||
: templateElement;
|
||||
element.data($$scope, scope);
|
||||
scope.$element = element;
|
||||
(cloneConnectFn||noop)(element, scope);
|
||||
template.link(element, scope);
|
||||
return element;
|
||||
};
|
||||
},
|
||||
|
||||
templatize: function(element, elementIndex){
|
||||
var self = this,
|
||||
widget,
|
||||
fn,
|
||||
directiveFns = self.directives,
|
||||
descend = true,
|
||||
directives = true,
|
||||
elementName = nodeName_(element),
|
||||
elementNamespace = elementName.indexOf(':') > 0 ? lowercase(elementName).replace(':', '-') : '',
|
||||
template,
|
||||
selfApi = {
|
||||
compile: bind(self, self.compile),
|
||||
descend: function(value){ if(isDefined(value)) descend = value; return descend;},
|
||||
directives: function(value){ if(isDefined(value)) directives = value; return directives;},
|
||||
scope: function(value){ if(isDefined(value)) template.newScope = template.newScope || value; return template.newScope;}
|
||||
};
|
||||
element.addClass(elementNamespace);
|
||||
template = new Template();
|
||||
eachAttribute(element, function(value, name){
|
||||
if (!widget) {
|
||||
if ((widget = self.widgets('@' + name))) {
|
||||
element.addClass('ng-attr-widget');
|
||||
widget = bind(selfApi, widget, value, element);
|
||||
///////////////////////////////////
|
||||
//Compiler
|
||||
//////////////////////////////////
|
||||
|
||||
/**
|
||||
* @ngdoc function
|
||||
* @name angular.compile
|
||||
* @function
|
||||
*
|
||||
* @description
|
||||
* Compiles a piece of HTML string or DOM into a template and produces a template function, which
|
||||
* can then be used to link {@link angular.scope scope} and the template together.
|
||||
*
|
||||
* The compilation is a process of walking the DOM tree and trying to match DOM elements to
|
||||
* {@link angular.markup markup}, {@link angular.attrMarkup attrMarkup},
|
||||
* {@link angular.widget widgets}, and {@link angular.directive directives}. For each match it
|
||||
* executes corresponding markup, attrMarkup, widget or directive template function and collects the
|
||||
* instance functions into a single template function which is then returned.
|
||||
*
|
||||
* The template function can then be used once to produce the view or as it is the case with
|
||||
* {@link angular.widget.@ng:repeat repeater} many-times, in which case each call results in a view
|
||||
* that is a DOM clone of the original template.
|
||||
*
|
||||
<pre>
|
||||
// compile the entire window.document and give me the scope bound to this template.
|
||||
var rootScope = angular.compile(window.document)();
|
||||
|
||||
// compile a piece of html
|
||||
var rootScope2 = angular.compile('<div ng:click="clicked = true">click me</div>')();
|
||||
|
||||
// compile a piece of html and retain reference to both the dom and scope
|
||||
var template = angular.element('<div ng:click="clicked = true">click me</div>'),
|
||||
scope = angular.compile(template)();
|
||||
// at this point template was transformed into a view
|
||||
</pre>
|
||||
*
|
||||
*
|
||||
* @param {string|DOMElement} element Element or HTML to compile into a template function.
|
||||
* @returns {function(scope[, cloneAttachFn])} a template function which is used to bind template
|
||||
* (a DOM element/tree) to a scope. Where:
|
||||
*
|
||||
* * `scope` - A {@link angular.scope Scope} to bind to.
|
||||
* * `cloneAttachFn` - If `cloneAttachFn` is provided, then the link function will clone the
|
||||
* `template` and call the `cloneAttachFn` function allowing the caller to attach the
|
||||
* cloned elements to the DOM document at the appropriate place. The `cloneAttachFn` is
|
||||
* called as: <br/> `cloneAttachFn(clonedElement, scope)` where:
|
||||
*
|
||||
* * `clonedElement` - is a clone of the original `element` passed into the compiler.
|
||||
* * `scope` - is the current scope with which the linking function is working with.
|
||||
*
|
||||
* Calling the template function returns the element of the template. It is either the original element
|
||||
* passed in, or the clone of the element if the `cloneAttachFn` is provided.
|
||||
*
|
||||
* It is important to understand that the returned scope is "linked" to the view DOM, but no linking
|
||||
* (instance) functions registered by {@link angular.directive directives} or
|
||||
* {@link angular.widget widgets} found in the template have been executed yet. This means that the
|
||||
* view is likely empty and doesn't contain any values that result from evaluation on the scope. To
|
||||
* bring the view to life, the scope needs to run through a $digest phase which typically is done by
|
||||
* Angular automatically, except for the case when an application is being
|
||||
* {@link guide/dev_guide.bootstrap.manual_bootstrap} manually bootstrapped, in which case the
|
||||
* $digest phase must be invoked by calling {@link angular.scope.$apply}.
|
||||
*
|
||||
* If you need access to the bound view, there are two ways to do it:
|
||||
*
|
||||
* - If you are not asking the linking function to clone the template, create the DOM element(s)
|
||||
* before you send them to the compiler and keep this reference around.
|
||||
* <pre>
|
||||
* var scope = angular.injector()('$rootScope');
|
||||
* var element = angular.compile('<p>{{total}}</p>')(scope);
|
||||
* </pre>
|
||||
*
|
||||
* - if on the other hand, you need the element to be cloned, the view reference from the original
|
||||
* example would not point to the clone, but rather to the original template that was cloned. In
|
||||
* this case, you can access the clone via the cloneAttachFn:
|
||||
* <pre>
|
||||
* var original = angular.element('<p>{{total}}</p>'),
|
||||
* scope = someParentScope.$new(),
|
||||
* clone;
|
||||
*
|
||||
* angular.compile(original)(scope, function(clonedElement, scope) {
|
||||
* clone = clonedElement;
|
||||
* //attach the clone to DOM document at the right place
|
||||
* });
|
||||
*
|
||||
* //now we have reference to the cloned DOM via `clone`
|
||||
* </pre>
|
||||
*
|
||||
*
|
||||
* Compiler Methods For Widgets and Directives:
|
||||
*
|
||||
* The following methods are available for use when you write your own widgets, directives,
|
||||
* and markup. (Recall that the compile function's this is a reference to the compiler.)
|
||||
*
|
||||
* `compile(element)` - returns linker -
|
||||
* Invoke a new instance of the compiler to compile a DOM element and return a linker function.
|
||||
* You can apply the linker function to the original element or a clone of the original element.
|
||||
* The linker function returns a scope.
|
||||
*
|
||||
* * `comment(commentText)` - returns element - Create a comment element.
|
||||
*
|
||||
* * `element(elementName)` - returns element - Create an element by name.
|
||||
*
|
||||
* * `text(text)` - returns element - Create a text element.
|
||||
*
|
||||
* * `descend([set])` - returns descend state (true or false). Get or set the current descend
|
||||
* state. If true the compiler will descend to children elements.
|
||||
*
|
||||
* * `directives([set])` - returns directive state (true or false). Get or set the current
|
||||
* directives processing state. The compiler will process directives only when directives set to
|
||||
* true.
|
||||
*
|
||||
* For information on how the compiler works, see the
|
||||
* {@link guide/dev_guide.compiler Angular HTML Compiler} section of the Developer Guide.
|
||||
*/
|
||||
function Compiler(markup, attrMarkup, directives, widgets){
|
||||
this.markup = markup;
|
||||
this.attrMarkup = attrMarkup;
|
||||
this.directives = directives;
|
||||
this.widgets = widgets;
|
||||
}
|
||||
|
||||
Compiler.prototype = {
|
||||
compile: function(templateElement) {
|
||||
templateElement = jqLite(templateElement);
|
||||
var index = 0,
|
||||
template,
|
||||
parent = templateElement.parent();
|
||||
if (templateElement.length > 1) {
|
||||
// https://github.com/angular/angular.js/issues/338
|
||||
throw Error("Cannot compile multiple element roots: " +
|
||||
jqLite('<div>').append(templateElement.clone()).html());
|
||||
}
|
||||
}
|
||||
});
|
||||
if (!widget) {
|
||||
if ((widget = self.widgets(elementName))) {
|
||||
if (elementNamespace)
|
||||
element.addClass('ng-widget');
|
||||
widget = bind(selfApi, widget, element);
|
||||
}
|
||||
}
|
||||
if (widget) {
|
||||
descend = false;
|
||||
directives = false;
|
||||
var parent = element.parent();
|
||||
template.addLinkFn(widget.call(selfApi, element));
|
||||
if (parent && parent[0]) {
|
||||
element = jqLite(parent[0].childNodes[elementIndex]);
|
||||
}
|
||||
}
|
||||
if (descend){
|
||||
// process markup for text nodes only
|
||||
for(var i=0, child=element[0].childNodes;
|
||||
i<child.length; i++) {
|
||||
if (isTextNode(child[i])) {
|
||||
forEach(self.markup, function(markup){
|
||||
if (i<child.length) {
|
||||
var textNode = jqLite(child[i]);
|
||||
markup.call(selfApi, textNode.text(), textNode, element);
|
||||
if (parent && parent[0]) {
|
||||
parent = parent[0];
|
||||
for(var i = 0; i < parent.childNodes.length; i++) {
|
||||
if (parent.childNodes[i] == templateElement[0]) {
|
||||
index = i;
|
||||
}
|
||||
}
|
||||
}
|
||||
template = this.templatize(templateElement, index) || new Template();
|
||||
return function(scope, cloneConnectFn){
|
||||
assertArg(scope, 'scope');
|
||||
// important!!: we must call our jqLite.clone() since the jQuery one is trying to be smart
|
||||
// and sometimes changes the structure of the DOM.
|
||||
var element = cloneConnectFn
|
||||
? JQLitePrototype.clone.call(templateElement) // IMPORTANT!!!
|
||||
: templateElement;
|
||||
element.data($$scope, scope);
|
||||
scope.$element = element;
|
||||
(cloneConnectFn||noop)(element, scope);
|
||||
template.link(element, scope);
|
||||
return element;
|
||||
};
|
||||
},
|
||||
|
||||
templatize: function(element, elementIndex){
|
||||
var self = this,
|
||||
widget,
|
||||
fn,
|
||||
directiveFns = self.directives,
|
||||
descend = true,
|
||||
directives = true,
|
||||
elementName = nodeName_(element),
|
||||
elementNamespace = elementName.indexOf(':') > 0 ? lowercase(elementName).replace(':', '-') : '',
|
||||
template,
|
||||
selfApi = {
|
||||
compile: bind(self, self.compile),
|
||||
descend: function(value){ if(isDefined(value)) descend = value; return descend;},
|
||||
directives: function(value){ if(isDefined(value)) directives = value; return directives;},
|
||||
scope: function(value){ if(isDefined(value)) template.newScope = template.newScope || value; return template.newScope;}
|
||||
};
|
||||
element.addClass(elementNamespace);
|
||||
template = new Template();
|
||||
eachAttribute(element, function(value, name){
|
||||
if (!widget) {
|
||||
if ((widget = self.widgets('@' + name))) {
|
||||
element.addClass('ng-attr-widget');
|
||||
widget = bind(selfApi, widget, value, element);
|
||||
}
|
||||
}
|
||||
});
|
||||
if (!widget) {
|
||||
if ((widget = self.widgets(elementName))) {
|
||||
if (elementNamespace)
|
||||
element.addClass('ng-widget');
|
||||
widget = bind(selfApi, widget, element);
|
||||
}
|
||||
}
|
||||
if (widget) {
|
||||
descend = false;
|
||||
directives = false;
|
||||
var parent = element.parent();
|
||||
template.addLinkFn(widget.call(selfApi, element));
|
||||
if (parent && parent[0]) {
|
||||
element = jqLite(parent[0].childNodes[elementIndex]);
|
||||
}
|
||||
}
|
||||
if (descend){
|
||||
// process markup for text nodes only
|
||||
for(var i=0, child=element[0].childNodes;
|
||||
i<child.length; i++) {
|
||||
if (isTextNode(child[i])) {
|
||||
forEach(self.markup, function(markup){
|
||||
if (i<child.length) {
|
||||
var textNode = jqLite(child[i]);
|
||||
markup.call(selfApi, textNode.text(), textNode, element);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (directives) {
|
||||
// Process attributes/directives
|
||||
eachAttribute(element, function(value, name){
|
||||
forEach(self.attrMarkup, function(markup){
|
||||
markup.call(selfApi, value, name, element);
|
||||
});
|
||||
});
|
||||
eachAttribute(element, function(value, name){
|
||||
name = lowercase(name);
|
||||
fn = directiveFns[name];
|
||||
if (fn) {
|
||||
element.addClass('ng-directive');
|
||||
template.addLinkFn((directiveFns[name]).call(selfApi, value, element));
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (directives) {
|
||||
// Process attributes/directives
|
||||
eachAttribute(element, function(value, name){
|
||||
forEach(self.attrMarkup, function(markup){
|
||||
markup.call(selfApi, value, name, element);
|
||||
});
|
||||
});
|
||||
eachAttribute(element, function(value, name){
|
||||
name = lowercase(name);
|
||||
fn = directiveFns[name];
|
||||
if (fn) {
|
||||
element.addClass('ng-directive');
|
||||
template.addLinkFn((directiveFns[name]).call(selfApi, value, element));
|
||||
// Process non text child nodes
|
||||
if (descend) {
|
||||
eachNode(element, function(child, i){
|
||||
template.addChild(i, self.templatize(child, i));
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
// Process non text child nodes
|
||||
if (descend) {
|
||||
eachNode(element, function(child, i){
|
||||
template.addChild(i, self.templatize(child, i));
|
||||
});
|
||||
}
|
||||
return template.empty() ? null : template;
|
||||
}
|
||||
};
|
||||
return template.empty() ? null : template;
|
||||
}
|
||||
};
|
||||
|
||||
/////////////////////////////////////////////////////////////////////
|
||||
var compiler = new Compiler($textMarkup, $attrMarkup, $directive, $widget);
|
||||
return bind(compiler, compiler.compile);
|
||||
}, ['$injector', '$exceptionHandler', '$textMarkup', '$attrMarkup', '$directive', '$widget']);
|
||||
/////////////////////////////////////////////////////////////////////
|
||||
var compiler = new Compiler($textMarkup, $attrMarkup, $directive, $widget);
|
||||
return bind(compiler, compiler.compile);
|
||||
}];
|
||||
};
|
||||
|
||||
|
||||
function eachNode(element, fn){
|
||||
|
|
|
|||
|
|
@ -11,52 +11,54 @@
|
|||
* deserialized by angular's toJson/fromJson.
|
||||
* @example
|
||||
*/
|
||||
angularServiceInject('$cookieStore', function($store) {
|
||||
function $CookieStoreProvider(){
|
||||
this.$get = ['$cookies', function($cookies) {
|
||||
|
||||
return {
|
||||
/**
|
||||
* @ngdoc method
|
||||
* @name angular.service.$cookieStore#get
|
||||
* @methodOf angular.service.$cookieStore
|
||||
*
|
||||
* @description
|
||||
* Returns the value of given cookie key
|
||||
*
|
||||
* @param {string} key Id to use for lookup.
|
||||
* @returns {Object} Deserialized cookie value.
|
||||
*/
|
||||
get: function(key) {
|
||||
return fromJson($store[key]);
|
||||
},
|
||||
return {
|
||||
/**
|
||||
* @ngdoc method
|
||||
* @name angular.service.$cookieStore#get
|
||||
* @methodOf angular.service.$cookieStore
|
||||
*
|
||||
* @description
|
||||
* Returns the value of given cookie key
|
||||
*
|
||||
* @param {string} key Id to use for lookup.
|
||||
* @returns {Object} Deserialized cookie value.
|
||||
*/
|
||||
get: function(key) {
|
||||
return fromJson($cookies[key]);
|
||||
},
|
||||
|
||||
/**
|
||||
* @ngdoc method
|
||||
* @name angular.service.$cookieStore#put
|
||||
* @methodOf angular.service.$cookieStore
|
||||
*
|
||||
* @description
|
||||
* Sets a value for given cookie key
|
||||
*
|
||||
* @param {string} key Id for the `value`.
|
||||
* @param {Object} value Value to be stored.
|
||||
*/
|
||||
put: function(key, value) {
|
||||
$store[key] = toJson(value);
|
||||
},
|
||||
/**
|
||||
* @ngdoc method
|
||||
* @name angular.service.$cookieStore#put
|
||||
* @methodOf angular.service.$cookieStore
|
||||
*
|
||||
* @description
|
||||
* Sets a value for given cookie key
|
||||
*
|
||||
* @param {string} key Id for the `value`.
|
||||
* @param {Object} value Value to be stored.
|
||||
*/
|
||||
put: function(key, value) {
|
||||
$cookies[key] = toJson(value);
|
||||
},
|
||||
|
||||
/**
|
||||
* @ngdoc method
|
||||
* @name angular.service.$cookieStore#remove
|
||||
* @methodOf angular.service.$cookieStore
|
||||
*
|
||||
* @description
|
||||
* Remove given cookie
|
||||
*
|
||||
* @param {string} key Id of the key-value pair to delete.
|
||||
*/
|
||||
remove: function(key) {
|
||||
delete $store[key];
|
||||
}
|
||||
};
|
||||
/**
|
||||
* @ngdoc method
|
||||
* @name angular.service.$cookieStore#remove
|
||||
* @methodOf angular.service.$cookieStore
|
||||
*
|
||||
* @description
|
||||
* Remove given cookie
|
||||
*
|
||||
* @param {string} key Id of the key-value pair to delete.
|
||||
*/
|
||||
remove: function(key) {
|
||||
delete $cookies[key];
|
||||
}
|
||||
};
|
||||
|
||||
}, ['$cookies']);
|
||||
}];
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,80 +13,82 @@
|
|||
*
|
||||
* @example
|
||||
*/
|
||||
angularServiceInject('$cookies', function($rootScope, $browser) {
|
||||
var cookies = {},
|
||||
lastCookies = {},
|
||||
lastBrowserCookies,
|
||||
runEval = false;
|
||||
function $CookiesProvider() {
|
||||
this.$get = ['$rootScope', '$browser', function ($rootScope, $browser) {
|
||||
var cookies = {},
|
||||
lastCookies = {},
|
||||
lastBrowserCookies,
|
||||
runEval = false;
|
||||
|
||||
//creates a poller fn that copies all cookies from the $browser to service & inits the service
|
||||
$browser.addPollFn(function() {
|
||||
var currentCookies = $browser.cookies();
|
||||
if (lastBrowserCookies != currentCookies) { //relies on browser.cookies() impl
|
||||
lastBrowserCookies = currentCookies;
|
||||
copy(currentCookies, lastCookies);
|
||||
copy(currentCookies, cookies);
|
||||
if (runEval) $rootScope.$apply();
|
||||
}
|
||||
})();
|
||||
|
||||
runEval = true;
|
||||
|
||||
//at the end of each eval, push cookies
|
||||
//TODO: this should happen before the "delayed" watches fire, because if some cookies are not
|
||||
// strings or browser refuses to store some cookies, we update the model in the push fn.
|
||||
$rootScope.$watch(push);
|
||||
|
||||
return cookies;
|
||||
|
||||
|
||||
/**
|
||||
* Pushes all the cookies from the service to the browser and verifies if all cookies were stored.
|
||||
*/
|
||||
function push() {
|
||||
var name,
|
||||
value,
|
||||
browserCookies,
|
||||
updated;
|
||||
|
||||
//delete any cookies deleted in $cookies
|
||||
for (name in lastCookies) {
|
||||
if (isUndefined(cookies[name])) {
|
||||
$browser.cookies(name, undefined);
|
||||
//creates a poller fn that copies all cookies from the $browser to service & inits the service
|
||||
$browser.addPollFn(function() {
|
||||
var currentCookies = $browser.cookies();
|
||||
if (lastBrowserCookies != currentCookies) { //relies on browser.cookies() impl
|
||||
lastBrowserCookies = currentCookies;
|
||||
copy(currentCookies, lastCookies);
|
||||
copy(currentCookies, cookies);
|
||||
if (runEval) $rootScope.$apply();
|
||||
}
|
||||
}
|
||||
})();
|
||||
|
||||
//update all cookies updated in $cookies
|
||||
for(name in cookies) {
|
||||
value = cookies[name];
|
||||
if (!isString(value)) {
|
||||
if (isDefined(lastCookies[name])) {
|
||||
cookies[name] = lastCookies[name];
|
||||
} else {
|
||||
delete cookies[name];
|
||||
runEval = true;
|
||||
|
||||
//at the end of each eval, push cookies
|
||||
//TODO: this should happen before the "delayed" watches fire, because if some cookies are not
|
||||
// strings or browser refuses to store some cookies, we update the model in the push fn.
|
||||
$rootScope.$watch(push);
|
||||
|
||||
return cookies;
|
||||
|
||||
|
||||
/**
|
||||
* Pushes all the cookies from the service to the browser and verifies if all cookies were stored.
|
||||
*/
|
||||
function push() {
|
||||
var name,
|
||||
value,
|
||||
browserCookies,
|
||||
updated;
|
||||
|
||||
//delete any cookies deleted in $cookies
|
||||
for (name in lastCookies) {
|
||||
if (isUndefined(cookies[name])) {
|
||||
$browser.cookies(name, undefined);
|
||||
}
|
||||
} else if (value !== lastCookies[name]) {
|
||||
$browser.cookies(name, value);
|
||||
updated = true;
|
||||
}
|
||||
}
|
||||
|
||||
//verify what was actually stored
|
||||
if (updated){
|
||||
updated = false;
|
||||
browserCookies = $browser.cookies();
|
||||
|
||||
for (name in cookies) {
|
||||
if (cookies[name] !== browserCookies[name]) {
|
||||
//delete or reset all cookies that the browser dropped from $cookies
|
||||
if (isUndefined(browserCookies[name])) {
|
||||
delete cookies[name];
|
||||
//update all cookies updated in $cookies
|
||||
for(name in cookies) {
|
||||
value = cookies[name];
|
||||
if (!isString(value)) {
|
||||
if (isDefined(lastCookies[name])) {
|
||||
cookies[name] = lastCookies[name];
|
||||
} else {
|
||||
cookies[name] = browserCookies[name];
|
||||
delete cookies[name];
|
||||
}
|
||||
} else if (value !== lastCookies[name]) {
|
||||
$browser.cookies(name, value);
|
||||
updated = true;
|
||||
}
|
||||
}
|
||||
|
||||
//verify what was actually stored
|
||||
if (updated){
|
||||
updated = false;
|
||||
browserCookies = $browser.cookies();
|
||||
|
||||
for (name in cookies) {
|
||||
if (cookies[name] !== browserCookies[name]) {
|
||||
//delete or reset all cookies that the browser dropped from $cookies
|
||||
if (isUndefined(browserCookies[name])) {
|
||||
delete cookies[name];
|
||||
} else {
|
||||
cookies[name] = browserCookies[name];
|
||||
}
|
||||
updated = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}, ['$rootScope', '$browser']);
|
||||
}];
|
||||
}
|
||||
|
|
|
|||
|
|
@ -28,16 +28,18 @@
|
|||
* @param {*} deferId Token returned by the `$defer` function.
|
||||
* @returns {boolean} Returns `true` if the task hasn't executed yet and was successfuly canceled.
|
||||
*/
|
||||
angularServiceInject('$defer', function($rootScope, $browser) {
|
||||
function defer(fn, delay) {
|
||||
return $browser.defer(function() {
|
||||
$rootScope.$apply(fn);
|
||||
}, delay);
|
||||
}
|
||||
function $DeferProvider(){
|
||||
this.$get = ['$rootScope', '$browser', function($rootScope, $browser) {
|
||||
function defer(fn, delay) {
|
||||
return $browser.defer(function() {
|
||||
$rootScope.$apply(fn);
|
||||
}, delay);
|
||||
}
|
||||
|
||||
defer.cancel = function(deferId) {
|
||||
return $browser.defer.cancel(deferId);
|
||||
};
|
||||
defer.cancel = function(deferId) {
|
||||
return $browser.defer.cancel(deferId);
|
||||
};
|
||||
|
||||
return defer;
|
||||
}, ['$rootScope', '$browser']);
|
||||
return defer;
|
||||
}];
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,6 +9,8 @@
|
|||
* A {@link angular.element jQuery (lite)}-wrapped reference to the browser's `window.document`
|
||||
* element.
|
||||
*/
|
||||
angularServiceInject("$document", function(window){
|
||||
return jqLite(window.document);
|
||||
}, ['$window']);
|
||||
function $DocumentProvider(){
|
||||
this.$get = ['$window', function(window){
|
||||
return jqLite(window.document);
|
||||
}];
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,9 +15,10 @@
|
|||
*
|
||||
* @example
|
||||
*/
|
||||
var $exceptionHandlerFactory; //reference to be used only in tests
|
||||
angularServiceInject('$exceptionHandler', $exceptionHandlerFactory = function($log){
|
||||
return function(e) {
|
||||
$log.error(e);
|
||||
};
|
||||
}, ['$log']);
|
||||
function $ExceptionHandlerProvider(){
|
||||
this.$get = ['$log', function($log){
|
||||
return function(e) {
|
||||
$log.error(e);
|
||||
};
|
||||
}];
|
||||
}
|
||||
|
|
|
|||
|
|
@ -96,297 +96,299 @@
|
|||
</doc:scenario>
|
||||
</doc:example>
|
||||
*/
|
||||
angularServiceInject('$formFactory', function($rootScope) {
|
||||
|
||||
function $FormFactoryProvider() {
|
||||
this.$get = ['$rootScope', function($rootScope) {
|
||||
|
||||
/**
|
||||
* @ngdoc proprety
|
||||
* @name rootForm
|
||||
* @propertyOf angular.service.$formFactory
|
||||
* @description
|
||||
* Static property on `$formFactory`
|
||||
*
|
||||
* Each application ({@link guide/dev_guide.scopes.internals root scope}) gets a root form which
|
||||
* is the top-level parent of all forms.
|
||||
*/
|
||||
formFactory.rootForm = formFactory($rootScope);
|
||||
|
||||
|
||||
/**
|
||||
* @ngdoc proprety
|
||||
* @name rootForm
|
||||
* @propertyOf angular.service.$formFactory
|
||||
* @description
|
||||
* Static property on `$formFactory`
|
||||
*
|
||||
* Each application ({@link guide/dev_guide.scopes.internals root scope}) gets a root form which
|
||||
* is the top-level parent of all forms.
|
||||
*/
|
||||
formFactory.rootForm = formFactory($rootScope);
|
||||
/**
|
||||
* @ngdoc method
|
||||
* @name forElement
|
||||
* @methodOf angular.service.$formFactory
|
||||
* @description
|
||||
* Static method on `$formFactory` service.
|
||||
*
|
||||
* Retrieve the closest form for a given element or defaults to the `root` form. Used by the
|
||||
* {@link angular.widget.form form} element.
|
||||
* @param {Element} element The element where the search for form should initiate.
|
||||
*/
|
||||
formFactory.forElement = function(element) {
|
||||
return element.inheritedData('$form') || formFactory.rootForm;
|
||||
};
|
||||
return formFactory;
|
||||
|
||||
function formFactory(parent) {
|
||||
return (parent || formFactory.rootForm).$new(FormController);
|
||||
}
|
||||
|
||||
/**
|
||||
* @ngdoc method
|
||||
* @name forElement
|
||||
* @methodOf angular.service.$formFactory
|
||||
* @description
|
||||
* Static method on `$formFactory` service.
|
||||
*
|
||||
* Retrieve the closest form for a given element or defaults to the `root` form. Used by the
|
||||
* {@link angular.widget.form form} element.
|
||||
* @param {Element} element The element where the search for form should initiate.
|
||||
*/
|
||||
formFactory.forElement = function(element) {
|
||||
return element.inheritedData('$form') || formFactory.rootForm;
|
||||
};
|
||||
return formFactory;
|
||||
}];
|
||||
|
||||
function formFactory(parent) {
|
||||
return (parent || formFactory.rootForm).$new(FormController);
|
||||
function propertiesUpdate(widget) {
|
||||
widget.$valid = !(widget.$invalid =
|
||||
!(widget.$readonly || widget.$disabled || equals(widget.$error, {})));
|
||||
}
|
||||
|
||||
}, ['$rootScope']);
|
||||
/**
|
||||
* @ngdoc property
|
||||
* @name $error
|
||||
* @propertyOf angular.service.$formFactory
|
||||
* @description
|
||||
* Property of the form and widget instance.
|
||||
*
|
||||
* Summary of all of the errors on the page. If a widget emits `$invalid` with `REQUIRED` key,
|
||||
* then the `$error` object will have a `REQUIRED` key with an array of widgets which have
|
||||
* emitted this key. `form.$error.REQUIRED == [ widget ]`.
|
||||
*/
|
||||
|
||||
function propertiesUpdate(widget) {
|
||||
widget.$valid = !(widget.$invalid =
|
||||
!(widget.$readonly || widget.$disabled || equals(widget.$error, {})));
|
||||
}
|
||||
/**
|
||||
* @ngdoc property
|
||||
* @name $invalid
|
||||
* @propertyOf angular.service.$formFactory
|
||||
* @description
|
||||
* Property of the form and widget instance.
|
||||
*
|
||||
* True if any of the widgets of the form are invalid.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @ngdoc property
|
||||
* @name $error
|
||||
* @propertyOf angular.service.$formFactory
|
||||
* @description
|
||||
* Property of the form and widget instance.
|
||||
*
|
||||
* Summary of all of the errors on the page. If a widget emits `$invalid` with `REQUIRED` key,
|
||||
* then the `$error` object will have a `REQUIRED` key with an array of widgets which have
|
||||
* emitted this key. `form.$error.REQUIRED == [ widget ]`.
|
||||
*/
|
||||
/**
|
||||
* @ngdoc property
|
||||
* @name $valid
|
||||
* @propertyOf angular.service.$formFactory
|
||||
* @description
|
||||
* Property of the form and widget instance.
|
||||
*
|
||||
* True if all of the widgets of the form are valid.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @ngdoc property
|
||||
* @name $invalid
|
||||
* @propertyOf angular.service.$formFactory
|
||||
* @description
|
||||
* Property of the form and widget instance.
|
||||
*
|
||||
* True if any of the widgets of the form are invalid.
|
||||
*/
|
||||
/**
|
||||
* @ngdoc event
|
||||
* @name angular.service.$formFactory#$valid
|
||||
* @eventOf angular.service.$formFactory
|
||||
* @eventType listen on form
|
||||
* @description
|
||||
* Upon receiving the `$valid` event from the widget update the `$error`, `$valid` and `$invalid`
|
||||
* properties of both the widget as well as the from.
|
||||
*
|
||||
* @param {String} validationKey The validation key to be used when updating the `$error` object.
|
||||
* The validation key is what will allow the template to bind to a specific validation error
|
||||
* such as `<div ng:show="form.$error.KEY">error for key</div>`.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @ngdoc property
|
||||
* @name $valid
|
||||
* @propertyOf angular.service.$formFactory
|
||||
* @description
|
||||
* Property of the form and widget instance.
|
||||
*
|
||||
* True if all of the widgets of the form are valid.
|
||||
*/
|
||||
/**
|
||||
* @ngdoc event
|
||||
* @name angular.service.$formFactory#$invalid
|
||||
* @eventOf angular.service.$formFactory
|
||||
* @eventType listen on form
|
||||
* @description
|
||||
* Upon receiving the `$invalid` event from the widget update the `$error`, `$valid` and `$invalid`
|
||||
* properties of both the widget as well as the from.
|
||||
*
|
||||
* @param {String} validationKey The validation key to be used when updating the `$error` object.
|
||||
* The validation key is what will allow the template to bind to a specific validation error
|
||||
* such as `<div ng:show="form.$error.KEY">error for key</div>`.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @ngdoc event
|
||||
* @name angular.service.$formFactory#$valid
|
||||
* @eventOf angular.service.$formFactory
|
||||
* @eventType listen on form
|
||||
* @description
|
||||
* Upon receiving the `$valid` event from the widget update the `$error`, `$valid` and `$invalid`
|
||||
* properties of both the widget as well as the from.
|
||||
*
|
||||
* @param {String} validationKey The validation key to be used when updating the `$error` object.
|
||||
* The validation key is what will allow the template to bind to a specific validation error
|
||||
* such as `<div ng:show="form.$error.KEY">error for key</div>`.
|
||||
*/
|
||||
/**
|
||||
* @ngdoc event
|
||||
* @name angular.service.$formFactory#$validate
|
||||
* @eventOf angular.service.$formFactory
|
||||
* @eventType emit on widget
|
||||
* @description
|
||||
* Emit the `$validate` event on the widget, giving a widget a chance to emit a
|
||||
* `$valid` / `$invalid` event base on its state. The `$validate` event is triggered when the
|
||||
* model or the view changes.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @ngdoc event
|
||||
* @name angular.service.$formFactory#$invalid
|
||||
* @eventOf angular.service.$formFactory
|
||||
* @eventType listen on form
|
||||
* @description
|
||||
* Upon receiving the `$invalid` event from the widget update the `$error`, `$valid` and `$invalid`
|
||||
* properties of both the widget as well as the from.
|
||||
*
|
||||
* @param {String} validationKey The validation key to be used when updating the `$error` object.
|
||||
* The validation key is what will allow the template to bind to a specific validation error
|
||||
* such as `<div ng:show="form.$error.KEY">error for key</div>`.
|
||||
*/
|
||||
/**
|
||||
* @ngdoc event
|
||||
* @name angular.service.$formFactory#$viewChange
|
||||
* @eventOf angular.service.$formFactory
|
||||
* @eventType listen on widget
|
||||
* @description
|
||||
* A widget is responsible for emitting this event whenever the view changes do to user interaction.
|
||||
* The event takes a `$viewValue` parameter, which is the new value of the view. This
|
||||
* event triggers a call to `$parseView()` as well as `$validate` event on widget.
|
||||
*
|
||||
* @param {*} viewValue The new value for the view which will be assigned to `widget.$viewValue`.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @ngdoc event
|
||||
* @name angular.service.$formFactory#$validate
|
||||
* @eventOf angular.service.$formFactory
|
||||
* @eventType emit on widget
|
||||
* @description
|
||||
* Emit the `$validate` event on the widget, giving a widget a chance to emit a
|
||||
* `$valid` / `$invalid` event base on its state. The `$validate` event is triggered when the
|
||||
* model or the view changes.
|
||||
*/
|
||||
function FormController() {
|
||||
var form = this,
|
||||
$error = form.$error = {};
|
||||
|
||||
/**
|
||||
* @ngdoc event
|
||||
* @name angular.service.$formFactory#$viewChange
|
||||
* @eventOf angular.service.$formFactory
|
||||
* @eventType listen on widget
|
||||
* @description
|
||||
* A widget is responsible for emitting this event whenever the view changes do to user interaction.
|
||||
* The event takes a `$viewValue` parameter, which is the new value of the view. This
|
||||
* event triggers a call to `$parseView()` as well as `$validate` event on widget.
|
||||
*
|
||||
* @param {*} viewValue The new value for the view which will be assigned to `widget.$viewValue`.
|
||||
*/
|
||||
form.$on('$destroy', function(event){
|
||||
var widget = event.targetScope;
|
||||
if (widget.$widgetId) {
|
||||
delete form[widget.$widgetId];
|
||||
}
|
||||
forEach($error, removeWidget, widget);
|
||||
});
|
||||
|
||||
function FormController() {
|
||||
var form = this,
|
||||
$error = form.$error = {};
|
||||
form.$on('$valid', function(event, error){
|
||||
var widget = event.targetScope;
|
||||
delete widget.$error[error];
|
||||
propertiesUpdate(widget);
|
||||
removeWidget($error[error], error, widget);
|
||||
});
|
||||
|
||||
form.$on('$destroy', function(event){
|
||||
var widget = event.targetScope;
|
||||
if (widget.$widgetId) {
|
||||
delete form[widget.$widgetId];
|
||||
}
|
||||
forEach($error, removeWidget, widget);
|
||||
});
|
||||
form.$on('$invalid', function(event, error){
|
||||
var widget = event.targetScope;
|
||||
addWidget(error, widget);
|
||||
widget.$error[error] = true;
|
||||
propertiesUpdate(widget);
|
||||
});
|
||||
|
||||
form.$on('$valid', function(event, error){
|
||||
var widget = event.targetScope;
|
||||
delete widget.$error[error];
|
||||
propertiesUpdate(widget);
|
||||
removeWidget($error[error], error, widget);
|
||||
});
|
||||
propertiesUpdate(form);
|
||||
|
||||
form.$on('$invalid', function(event, error){
|
||||
var widget = event.targetScope;
|
||||
addWidget(error, widget);
|
||||
widget.$error[error] = true;
|
||||
propertiesUpdate(widget);
|
||||
});
|
||||
|
||||
propertiesUpdate(form);
|
||||
|
||||
function removeWidget(queue, errorKey, widget) {
|
||||
if (queue) {
|
||||
widget = widget || this; // so that we can be used in forEach;
|
||||
for (var i = 0, length = queue.length; i < length; i++) {
|
||||
if (queue[i] === widget) {
|
||||
queue.splice(i, 1);
|
||||
if (!queue.length) {
|
||||
delete $error[errorKey];
|
||||
function removeWidget(queue, errorKey, widget) {
|
||||
if (queue) {
|
||||
widget = widget || this; // so that we can be used in forEach;
|
||||
for (var i = 0, length = queue.length; i < length; i++) {
|
||||
if (queue[i] === widget) {
|
||||
queue.splice(i, 1);
|
||||
if (!queue.length) {
|
||||
delete $error[errorKey];
|
||||
}
|
||||
}
|
||||
}
|
||||
propertiesUpdate(form);
|
||||
}
|
||||
}
|
||||
|
||||
function addWidget(errorKey, widget) {
|
||||
var queue = $error[errorKey];
|
||||
if (queue) {
|
||||
for (var i = 0, length = queue.length; i < length; i++) {
|
||||
if (queue[i] === widget) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$error[errorKey] = queue = [];
|
||||
}
|
||||
queue.push(widget);
|
||||
propertiesUpdate(form);
|
||||
}
|
||||
}
|
||||
|
||||
function addWidget(errorKey, widget) {
|
||||
var queue = $error[errorKey];
|
||||
if (queue) {
|
||||
for (var i = 0, length = queue.length; i < length; i++) {
|
||||
if (queue[i] === widget) {
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* @ngdoc method
|
||||
* @name $createWidget
|
||||
* @methodOf angular.service.$formFactory
|
||||
* @description
|
||||
*
|
||||
* Use form's `$createWidget` instance method to create new widgets. The widgets can be created
|
||||
* using an alias which makes the accessible from the form and available for data-binding,
|
||||
* useful for displaying validation error messages.
|
||||
*
|
||||
* The creation of a widget sets up:
|
||||
*
|
||||
* - `$watch` of `expression` on `model` scope. This code path syncs the model to the view.
|
||||
* The `$watch` listener will:
|
||||
*
|
||||
* - assign the new model value of `expression` to `widget.$modelValue`.
|
||||
* - call `widget.$parseModel` method if present. The `$parseModel` is responsible for copying
|
||||
* the `widget.$modelValue` to `widget.$viewValue` and optionally converting the data.
|
||||
* (For example to convert a number into string)
|
||||
* - emits `$validate` event on widget giving a widget a chance to emit `$valid` / `$invalid`
|
||||
* event.
|
||||
* - call `widget.$render()` method on widget. The `$render` method is responsible for
|
||||
* reading the `widget.$viewValue` and updating the DOM.
|
||||
*
|
||||
* - Listen on `$viewChange` event from the `widget`. This code path syncs the view to the model.
|
||||
* The `$viewChange` listener will:
|
||||
*
|
||||
* - assign the value to `widget.$viewValue`.
|
||||
* - call `widget.$parseView` method if present. The `$parseView` is responsible for copying
|
||||
* the `widget.$viewValue` to `widget.$modelValue` and optionally converting the data.
|
||||
* (For example to convert a string into number)
|
||||
* - emits `$validate` event on widget giving a widget a chance to emit `$valid` / `$invalid`
|
||||
* event.
|
||||
* - Assign the `widget.$modelValue` to the `expression` on the `model` scope.
|
||||
*
|
||||
* - Creates these set of properties on the `widget` which are updated as a response to the
|
||||
* `$valid` / `$invalid` events:
|
||||
*
|
||||
* - `$error` - object - validation errors will be published as keys on this object.
|
||||
* Data-binding to this property is useful for displaying the validation errors.
|
||||
* - `$valid` - boolean - true if there are no validation errors
|
||||
* - `$invalid` - boolean - opposite of `$valid`.
|
||||
* @param {Object} params Named parameters:
|
||||
*
|
||||
* - `scope` - `{Scope}` - The scope to which the model for this widget is attached.
|
||||
* - `model` - `{string}` - The name of the model property on model scope.
|
||||
* - `controller` - {WidgetController} - The controller constructor function.
|
||||
* The controller constructor should create these instance methods.
|
||||
* - `$parseView()`: optional method responsible for copying `$viewVale` to `$modelValue`.
|
||||
* The method may fire `$valid`/`$invalid` events.
|
||||
* - `$parseModel()`: optional method responsible for copying `$modelVale` to `$viewValue`.
|
||||
* The method may fire `$valid`/`$invalid` events.
|
||||
* - `$render()`: required method which needs to update the DOM of the widget to match the
|
||||
* `$viewValue`.
|
||||
*
|
||||
* - `controllerArgs` - `{Array}` (Optional) - Any extra arguments will be curried to the
|
||||
* WidgetController constructor.
|
||||
* - `onChange` - `{(string|function())}` (Optional) - Expression to execute when user changes the
|
||||
* value.
|
||||
* - `alias` - `{string}` (Optional) - The name of the form property under which the widget
|
||||
* instance should be published. The name should be unique for each form.
|
||||
* @returns {Widget} Instance of a widget scope.
|
||||
*/
|
||||
FormController.prototype.$createWidget = function(params) {
|
||||
var form = this,
|
||||
modelScope = params.scope,
|
||||
onChange = params.onChange,
|
||||
alias = params.alias,
|
||||
scopeGet = parser(params.model).assignable(),
|
||||
scopeSet = scopeGet.assign,
|
||||
widget = this.$new(params.controller, params.controllerArgs);
|
||||
|
||||
widget.$error = {};
|
||||
// Set the state to something we know will change to get the process going.
|
||||
widget.$modelValue = Number.NaN;
|
||||
// watch for scope changes and update the view appropriately
|
||||
modelScope.$watch(scopeGet, function(scope, value) {
|
||||
if (!equals(widget.$modelValue, value)) {
|
||||
widget.$modelValue = value;
|
||||
widget.$parseModel ? widget.$parseModel() : (widget.$viewValue = value);
|
||||
widget.$emit('$validate');
|
||||
widget.$render && widget.$render();
|
||||
}
|
||||
});
|
||||
|
||||
widget.$on('$viewChange', function(event, viewValue){
|
||||
if (!equals(widget.$viewValue, viewValue)) {
|
||||
widget.$viewValue = viewValue;
|
||||
widget.$parseView ? widget.$parseView() : (widget.$modelValue = widget.$viewValue);
|
||||
scopeSet(modelScope, widget.$modelValue);
|
||||
if (onChange) modelScope.$eval(onChange);
|
||||
widget.$emit('$validate');
|
||||
}
|
||||
});
|
||||
|
||||
propertiesUpdate(widget);
|
||||
|
||||
// assign the widgetModel to the form
|
||||
if (alias && !form.hasOwnProperty(alias)) {
|
||||
form[alias] = widget;
|
||||
widget.$widgetId = alias;
|
||||
} else {
|
||||
$error[errorKey] = queue = [];
|
||||
alias = null;
|
||||
}
|
||||
queue.push(widget);
|
||||
propertiesUpdate(form);
|
||||
}
|
||||
|
||||
return widget;
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @ngdoc method
|
||||
* @name $createWidget
|
||||
* @methodOf angular.service.$formFactory
|
||||
* @description
|
||||
*
|
||||
* Use form's `$createWidget` instance method to create new widgets. The widgets can be created
|
||||
* using an alias which makes the accessible from the form and available for data-binding,
|
||||
* useful for displaying validation error messages.
|
||||
*
|
||||
* The creation of a widget sets up:
|
||||
*
|
||||
* - `$watch` of `expression` on `model` scope. This code path syncs the model to the view.
|
||||
* The `$watch` listener will:
|
||||
*
|
||||
* - assign the new model value of `expression` to `widget.$modelValue`.
|
||||
* - call `widget.$parseModel` method if present. The `$parseModel` is responsible for copying
|
||||
* the `widget.$modelValue` to `widget.$viewValue` and optionally converting the data.
|
||||
* (For example to convert a number into string)
|
||||
* - emits `$validate` event on widget giving a widget a chance to emit `$valid` / `$invalid`
|
||||
* event.
|
||||
* - call `widget.$render()` method on widget. The `$render` method is responsible for
|
||||
* reading the `widget.$viewValue` and updating the DOM.
|
||||
*
|
||||
* - Listen on `$viewChange` event from the `widget`. This code path syncs the view to the model.
|
||||
* The `$viewChange` listener will:
|
||||
*
|
||||
* - assign the value to `widget.$viewValue`.
|
||||
* - call `widget.$parseView` method if present. The `$parseView` is responsible for copying
|
||||
* the `widget.$viewValue` to `widget.$modelValue` and optionally converting the data.
|
||||
* (For example to convert a string into number)
|
||||
* - emits `$validate` event on widget giving a widget a chance to emit `$valid` / `$invalid`
|
||||
* event.
|
||||
* - Assign the `widget.$modelValue` to the `expression` on the `model` scope.
|
||||
*
|
||||
* - Creates these set of properties on the `widget` which are updated as a response to the
|
||||
* `$valid` / `$invalid` events:
|
||||
*
|
||||
* - `$error` - object - validation errors will be published as keys on this object.
|
||||
* Data-binding to this property is useful for displaying the validation errors.
|
||||
* - `$valid` - boolean - true if there are no validation errors
|
||||
* - `$invalid` - boolean - opposite of `$valid`.
|
||||
* @param {Object} params Named parameters:
|
||||
*
|
||||
* - `scope` - `{Scope}` - The scope to which the model for this widget is attached.
|
||||
* - `model` - `{string}` - The name of the model property on model scope.
|
||||
* - `controller` - {WidgetController} - The controller constructor function.
|
||||
* The controller constructor should create these instance methods.
|
||||
* - `$parseView()`: optional method responsible for copying `$viewVale` to `$modelValue`.
|
||||
* The method may fire `$valid`/`$invalid` events.
|
||||
* - `$parseModel()`: optional method responsible for copying `$modelVale` to `$viewValue`.
|
||||
* The method may fire `$valid`/`$invalid` events.
|
||||
* - `$render()`: required method which needs to update the DOM of the widget to match the
|
||||
* `$viewValue`.
|
||||
*
|
||||
* - `controllerArgs` - `{Array}` (Optional) - Any extra arguments will be curried to the
|
||||
* WidgetController constructor.
|
||||
* - `onChange` - `{(string|function())}` (Optional) - Expression to execute when user changes the
|
||||
* value.
|
||||
* - `alias` - `{string}` (Optional) - The name of the form property under which the widget
|
||||
* instance should be published. The name should be unique for each form.
|
||||
* @returns {Widget} Instance of a widget scope.
|
||||
*/
|
||||
FormController.prototype.$createWidget = function(params) {
|
||||
var form = this,
|
||||
modelScope = params.scope,
|
||||
onChange = params.onChange,
|
||||
alias = params.alias,
|
||||
scopeGet = parser(params.model).assignable(),
|
||||
scopeSet = scopeGet.assign,
|
||||
widget = this.$new(params.controller, params.controllerArgs);
|
||||
|
||||
widget.$error = {};
|
||||
// Set the state to something we know will change to get the process going.
|
||||
widget.$modelValue = Number.NaN;
|
||||
// watch for scope changes and update the view appropriately
|
||||
modelScope.$watch(scopeGet, function(scope, value) {
|
||||
if (!equals(widget.$modelValue, value)) {
|
||||
widget.$modelValue = value;
|
||||
widget.$parseModel ? widget.$parseModel() : (widget.$viewValue = value);
|
||||
widget.$emit('$validate');
|
||||
widget.$render && widget.$render();
|
||||
}
|
||||
});
|
||||
|
||||
widget.$on('$viewChange', function(event, viewValue){
|
||||
if (!equals(widget.$viewValue, viewValue)) {
|
||||
widget.$viewValue = viewValue;
|
||||
widget.$parseView ? widget.$parseView() : (widget.$modelValue = widget.$viewValue);
|
||||
scopeSet(modelScope, widget.$modelValue);
|
||||
if (onChange) modelScope.$eval(onChange);
|
||||
widget.$emit('$validate');
|
||||
}
|
||||
});
|
||||
|
||||
propertiesUpdate(widget);
|
||||
|
||||
// assign the widgetModel to the form
|
||||
if (alias && !form.hasOwnProperty(alias)) {
|
||||
form[alias] = widget;
|
||||
widget.$widgetId = alias;
|
||||
} else {
|
||||
alias = null;
|
||||
}
|
||||
|
||||
return widget;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -10,61 +10,63 @@
|
|||
*
|
||||
* * `id` – `{string}` – locale id formatted as `languageId-countryId` (e.g. `en-us`)
|
||||
*/
|
||||
angularServiceInject('$locale', function() {
|
||||
return {
|
||||
id: 'en-us',
|
||||
function $LocaleProvider(){
|
||||
this.$get = function() {
|
||||
return {
|
||||
id: 'en-us',
|
||||
|
||||
NUMBER_FORMATS: {
|
||||
DECIMAL_SEP: '.',
|
||||
GROUP_SEP: ',',
|
||||
PATTERNS: [
|
||||
{ // Decimal Pattern
|
||||
minInt: 1,
|
||||
minFrac: 0,
|
||||
maxFrac: 3,
|
||||
posPre: '',
|
||||
posSuf: '',
|
||||
negPre: '-',
|
||||
negSuf: '',
|
||||
gSize: 3,
|
||||
lgSize: 3
|
||||
},{ //Currency Pattern
|
||||
minInt: 1,
|
||||
minFrac: 2,
|
||||
maxFrac: 2,
|
||||
posPre: '\u00A4',
|
||||
posSuf: '',
|
||||
negPre: '(\u00A4',
|
||||
negSuf: ')',
|
||||
gSize: 3,
|
||||
lgSize: 3
|
||||
NUMBER_FORMATS: {
|
||||
DECIMAL_SEP: '.',
|
||||
GROUP_SEP: ',',
|
||||
PATTERNS: [
|
||||
{ // Decimal Pattern
|
||||
minInt: 1,
|
||||
minFrac: 0,
|
||||
maxFrac: 3,
|
||||
posPre: '',
|
||||
posSuf: '',
|
||||
negPre: '-',
|
||||
negSuf: '',
|
||||
gSize: 3,
|
||||
lgSize: 3
|
||||
},{ //Currency Pattern
|
||||
minInt: 1,
|
||||
minFrac: 2,
|
||||
maxFrac: 2,
|
||||
posPre: '\u00A4',
|
||||
posSuf: '',
|
||||
negPre: '(\u00A4',
|
||||
negSuf: ')',
|
||||
gSize: 3,
|
||||
lgSize: 3
|
||||
}
|
||||
],
|
||||
CURRENCY_SYM: '$'
|
||||
},
|
||||
|
||||
DATETIME_FORMATS: {
|
||||
MONTH: 'January,February,March,April,May,June,July,August,September,October,November,December'
|
||||
.split(','),
|
||||
SHORTMONTH: 'Jan,Feb,Mar,Apr,May,Jun,Jul,Aug,Sep,Oct,Nov,Dec'.split(','),
|
||||
DAY: 'Sunday,Monday,Tuesday,Wednesday,Thursday,Friday,Saturday'.split(','),
|
||||
SHORTDAY: 'Sun,Mon,Tue,Wed,Thu,Fri,Sat'.split(','),
|
||||
AMPMS: ['AM','PM'],
|
||||
medium: 'MMM d, y h:mm:ss a',
|
||||
short: 'M/d/yy h:mm a',
|
||||
fullDate: 'EEEE, MMMM d, y',
|
||||
longDate: 'MMMM d, y',
|
||||
mediumDate: 'MMM d, y',
|
||||
shortDate: 'M/d/yy',
|
||||
mediumTime: 'h:mm:ss a',
|
||||
shortTime: 'h:mm a'
|
||||
},
|
||||
|
||||
pluralCat: function(num) {
|
||||
if (num === 1) {
|
||||
return 'one';
|
||||
}
|
||||
],
|
||||
CURRENCY_SYM: '$'
|
||||
},
|
||||
|
||||
DATETIME_FORMATS: {
|
||||
MONTH: 'January,February,March,April,May,June,July,August,September,October,November,December'
|
||||
.split(','),
|
||||
SHORTMONTH: 'Jan,Feb,Mar,Apr,May,Jun,Jul,Aug,Sep,Oct,Nov,Dec'.split(','),
|
||||
DAY: 'Sunday,Monday,Tuesday,Wednesday,Thursday,Friday,Saturday'.split(','),
|
||||
SHORTDAY: 'Sun,Mon,Tue,Wed,Thu,Fri,Sat'.split(','),
|
||||
AMPMS: ['AM','PM'],
|
||||
medium: 'MMM d, y h:mm:ss a',
|
||||
short: 'M/d/yy h:mm a',
|
||||
fullDate: 'EEEE, MMMM d, y',
|
||||
longDate: 'MMMM d, y',
|
||||
mediumDate: 'MMM d, y',
|
||||
shortDate: 'M/d/yy',
|
||||
mediumTime: 'h:mm:ss a',
|
||||
shortTime: 'h:mm a'
|
||||
},
|
||||
|
||||
pluralCat: function(num) {
|
||||
if (num === 1) {
|
||||
return 'one';
|
||||
return 'other';
|
||||
}
|
||||
return 'other';
|
||||
}
|
||||
};
|
||||
};
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -419,94 +419,99 @@ function locationGetterSetter(property, preprocess) {
|
|||
*
|
||||
* For more information see {@link guide/dev_guide.services.$location Developer Guide: Angular Services: Using $location}
|
||||
*/
|
||||
angularServiceInject('$location', function($rootScope, $browser, $sniffer, $locationConfig, $document) {
|
||||
var currentUrl,
|
||||
basePath = $browser.baseHref() || '/',
|
||||
pathPrefix = pathPrefixFromBase(basePath),
|
||||
hashPrefix = $locationConfig.hashPrefix || '',
|
||||
initUrl = $browser.url();
|
||||
function $LocationProvider(){
|
||||
this.$get = ['$rootScope', '$browser', '$sniffer', '$locationConfig', '$document',
|
||||
function( $rootScope, $browser, $sniffer, $locationConfig, $document) {
|
||||
var currentUrl,
|
||||
basePath = $browser.baseHref() || '/',
|
||||
pathPrefix = pathPrefixFromBase(basePath),
|
||||
hashPrefix = $locationConfig.hashPrefix || '',
|
||||
initUrl = $browser.url();
|
||||
|
||||
if ($locationConfig.html5Mode) {
|
||||
if ($sniffer.history) {
|
||||
currentUrl = new LocationUrl(convertToHtml5Url(initUrl, basePath, hashPrefix), pathPrefix);
|
||||
} else {
|
||||
currentUrl = new LocationHashbangUrl(convertToHashbangUrl(initUrl, basePath, hashPrefix),
|
||||
hashPrefix);
|
||||
}
|
||||
|
||||
// link rewriting
|
||||
var u = currentUrl,
|
||||
absUrlPrefix = composeProtocolHostPort(u.protocol(), u.host(), u.port()) + pathPrefix;
|
||||
|
||||
$document.bind('click', function(event) {
|
||||
// TODO(vojta): rewrite link when opening in new tab/window (in legacy browser)
|
||||
// currently we open nice url link and redirect then
|
||||
|
||||
if (event.ctrlKey || event.metaKey || event.which == 2) return;
|
||||
|
||||
var elm = jqLite(event.target);
|
||||
|
||||
// traverse the DOM up to find first A tag
|
||||
while (elm.length && lowercase(elm[0].nodeName) !== 'a') {
|
||||
elm = elm.parent();
|
||||
if ($locationConfig.html5Mode) {
|
||||
if ($sniffer.history) {
|
||||
currentUrl = new LocationUrl(convertToHtml5Url(initUrl, basePath, hashPrefix), pathPrefix);
|
||||
} else {
|
||||
currentUrl = new LocationHashbangUrl(convertToHashbangUrl(initUrl, basePath, hashPrefix),
|
||||
hashPrefix);
|
||||
}
|
||||
|
||||
var href = elm.attr('href');
|
||||
if (!href || isDefined(elm.attr('ng:ext-link')) || elm.attr('target')) return;
|
||||
// link rewriting
|
||||
var u = currentUrl,
|
||||
absUrlPrefix = composeProtocolHostPort(u.protocol(), u.host(), u.port()) + pathPrefix;
|
||||
|
||||
// remove same domain from full url links (IE7 always returns full hrefs)
|
||||
href = href.replace(absUrlPrefix, '');
|
||||
$document.bind('click', function(event) {
|
||||
// TODO(vojta): rewrite link when opening in new tab/window (in legacy browser)
|
||||
// currently we open nice url link and redirect then
|
||||
|
||||
// link to different domain (or base path)
|
||||
if (href.substr(0, 4) == 'http') return;
|
||||
if (event.ctrlKey || event.metaKey || event.which == 2) return;
|
||||
|
||||
// remove pathPrefix from absolute links
|
||||
href = href.indexOf(pathPrefix) === 0 ? href.substr(pathPrefix.length) : href;
|
||||
var elm = jqLite(event.target);
|
||||
|
||||
currentUrl.url(href);
|
||||
$rootScope.$apply();
|
||||
event.preventDefault();
|
||||
// hack to work around FF6 bug 684208 when scenario runner clicks on links
|
||||
window.angular['ff-684208-preventDefault'] = true;
|
||||
});
|
||||
} else {
|
||||
currentUrl = new LocationHashbangUrl(initUrl, hashPrefix);
|
||||
}
|
||||
// traverse the DOM up to find first A tag
|
||||
while (elm.length && lowercase(elm[0].nodeName) !== 'a') {
|
||||
elm = elm.parent();
|
||||
}
|
||||
|
||||
// rewrite hashbang url <> html5 url
|
||||
if (currentUrl.absUrl() != initUrl) {
|
||||
$browser.url(currentUrl.absUrl(), true);
|
||||
}
|
||||
var href = elm.attr('href');
|
||||
if (!href || isDefined(elm.attr('ng:ext-link')) || elm.attr('target')) return;
|
||||
|
||||
// update $location when $browser url changes
|
||||
$browser.onUrlChange(function(newUrl) {
|
||||
if (currentUrl.absUrl() != newUrl) {
|
||||
currentUrl.$$parse(newUrl);
|
||||
$rootScope.$apply();
|
||||
}
|
||||
});
|
||||
// remove same domain from full url links (IE7 always returns full hrefs)
|
||||
href = href.replace(absUrlPrefix, '');
|
||||
|
||||
// update browser
|
||||
var changeCounter = 0;
|
||||
$rootScope.$watch(function() {
|
||||
if ($browser.url() != currentUrl.absUrl()) {
|
||||
changeCounter++;
|
||||
$rootScope.$evalAsync(function() {
|
||||
$browser.url(currentUrl.absUrl(), currentUrl.$$replace);
|
||||
currentUrl.$$replace = false;
|
||||
// link to different domain (or base path)
|
||||
if (href.substr(0, 4) == 'http') return;
|
||||
|
||||
// remove pathPrefix from absolute links
|
||||
href = href.indexOf(pathPrefix) === 0 ? href.substr(pathPrefix.length) : href;
|
||||
|
||||
currentUrl.url(href);
|
||||
$rootScope.$apply();
|
||||
event.preventDefault();
|
||||
// hack to work around FF6 bug 684208 when scenario runner clicks on links
|
||||
window.angular['ff-684208-preventDefault'] = true;
|
||||
});
|
||||
} else {
|
||||
currentUrl = new LocationHashbangUrl(initUrl, hashPrefix);
|
||||
}
|
||||
|
||||
return changeCounter;
|
||||
});
|
||||
// rewrite hashbang url <> html5 url
|
||||
if (currentUrl.absUrl() != initUrl) {
|
||||
$browser.url(currentUrl.absUrl(), true);
|
||||
}
|
||||
|
||||
return currentUrl;
|
||||
}, ['$rootScope', '$browser', '$sniffer', '$locationConfig', '$document']);
|
||||
// update $location when $browser url changes
|
||||
$browser.onUrlChange(function(newUrl) {
|
||||
if (currentUrl.absUrl() != newUrl) {
|
||||
currentUrl.$$parse(newUrl);
|
||||
$rootScope.$apply();
|
||||
}
|
||||
});
|
||||
|
||||
// update browser
|
||||
var changeCounter = 0;
|
||||
$rootScope.$watch(function() {
|
||||
if ($browser.url() != currentUrl.absUrl()) {
|
||||
changeCounter++;
|
||||
$rootScope.$evalAsync(function() {
|
||||
$browser.url(currentUrl.absUrl(), currentUrl.$$replace);
|
||||
currentUrl.$$replace = false;
|
||||
});
|
||||
}
|
||||
|
||||
angular.service('$locationConfig', function() {
|
||||
return {
|
||||
html5Mode: false,
|
||||
hashPrefix: ''
|
||||
return changeCounter;
|
||||
});
|
||||
|
||||
return currentUrl;
|
||||
}];
|
||||
}
|
||||
|
||||
//TODO(misko): refactor to service
|
||||
function $LocationConfigProvider(){
|
||||
this.$get = function() {
|
||||
return {
|
||||
html5Mode: false,
|
||||
hashPrefix: ''
|
||||
};
|
||||
};
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -34,64 +34,78 @@
|
|||
</doc:scenario>
|
||||
</doc:example>
|
||||
*/
|
||||
var $logFactory; //reference to be used only in tests
|
||||
angularServiceInject("$log", $logFactory = function($window){
|
||||
return {
|
||||
/**
|
||||
* @ngdoc method
|
||||
* @name angular.service.$log#log
|
||||
* @methodOf angular.service.$log
|
||||
*
|
||||
* @description
|
||||
* Write a log message
|
||||
*/
|
||||
log: consoleLog('log'),
|
||||
|
||||
/**
|
||||
* @ngdoc method
|
||||
* @name angular.service.$log#warn
|
||||
* @methodOf angular.service.$log
|
||||
*
|
||||
* @description
|
||||
* Write a warning message
|
||||
*/
|
||||
warn: consoleLog('warn'),
|
||||
function $LogProvider(){
|
||||
this.$get = ['$window', function($window){
|
||||
return {
|
||||
/**
|
||||
* @ngdoc method
|
||||
* @name angular.service.$log#log
|
||||
* @methodOf angular.service.$log
|
||||
*
|
||||
* @description
|
||||
* Write a log message
|
||||
*/
|
||||
log: consoleLog('log'),
|
||||
|
||||
/**
|
||||
* @ngdoc method
|
||||
* @name angular.service.$log#info
|
||||
* @methodOf angular.service.$log
|
||||
*
|
||||
* @description
|
||||
* Write an information message
|
||||
*/
|
||||
info: consoleLog('info'),
|
||||
/**
|
||||
* @ngdoc method
|
||||
* @name angular.service.$log#warn
|
||||
* @methodOf angular.service.$log
|
||||
*
|
||||
* @description
|
||||
* Write a warning message
|
||||
*/
|
||||
warn: consoleLog('warn'),
|
||||
|
||||
/**
|
||||
* @ngdoc method
|
||||
* @name angular.service.$log#error
|
||||
* @methodOf angular.service.$log
|
||||
*
|
||||
* @description
|
||||
* Write an error message
|
||||
*/
|
||||
error: consoleLog('error')
|
||||
};
|
||||
/**
|
||||
* @ngdoc method
|
||||
* @name angular.service.$log#info
|
||||
* @methodOf angular.service.$log
|
||||
*
|
||||
* @description
|
||||
* Write an information message
|
||||
*/
|
||||
info: consoleLog('info'),
|
||||
|
||||
function consoleLog(type) {
|
||||
var console = $window.console || {};
|
||||
var logFn = console[type] || console.log || noop;
|
||||
if (logFn.apply) {
|
||||
return function() {
|
||||
var args = [];
|
||||
forEach(arguments, function(arg){
|
||||
args.push(formatError(arg));
|
||||
});
|
||||
return logFn.apply(console, args);
|
||||
};
|
||||
} else {
|
||||
// we are IE, in which case there is nothing we can do
|
||||
return logFn;
|
||||
/**
|
||||
* @ngdoc method
|
||||
* @name angular.service.$log#error
|
||||
* @methodOf angular.service.$log
|
||||
*
|
||||
* @description
|
||||
* Write an error message
|
||||
*/
|
||||
error: consoleLog('error')
|
||||
};
|
||||
|
||||
function formatError(arg) {
|
||||
if (arg instanceof Error) {
|
||||
if (arg.stack) {
|
||||
arg = (arg.message && arg.stack.indexOf(arg.message) === -1) ?
|
||||
'Error: ' + arg.message + '\n' + arg.stack : arg.stack;
|
||||
} else if (arg.sourceURL) {
|
||||
arg = arg.message + '\n' + arg.sourceURL + ':' + arg.line;
|
||||
}
|
||||
}
|
||||
return arg;
|
||||
}
|
||||
}
|
||||
}, ['$window']);
|
||||
|
||||
function consoleLog(type) {
|
||||
var console = $window.console || {};
|
||||
var logFn = console[type] || console.log || noop;
|
||||
if (logFn.apply) {
|
||||
return function() {
|
||||
var args = [];
|
||||
forEach(arguments, function(arg){
|
||||
args.push(formatError(arg));
|
||||
});
|
||||
return logFn.apply(console, args);
|
||||
};
|
||||
} else {
|
||||
// we are IE, in which case there is nothing we can do
|
||||
return logFn;
|
||||
}
|
||||
}
|
||||
}];
|
||||
}
|
||||
|
|
|
|||
|
|
@ -200,7 +200,9 @@
|
|||
</doc:scenario>
|
||||
</doc:example>
|
||||
*/
|
||||
angularServiceInject('$resource', function($xhr){
|
||||
var resource = new ResourceFactory($xhr);
|
||||
return bind(resource, resource.route);
|
||||
}, ['$xhr.cache']);
|
||||
function $ResourceProvider() {
|
||||
this.$get = ['$xhr.cache', function($xhr){
|
||||
var resource = new ResourceFactory($xhr);
|
||||
return bind(resource, resource.route);
|
||||
}];
|
||||
}
|
||||
|
|
|
|||
|
|
@ -62,264 +62,267 @@
|
|||
</doc:scenario>
|
||||
</doc:example>
|
||||
*/
|
||||
angularServiceInject('$route', function($rootScope, $location, $routeParams) {
|
||||
/**
|
||||
* @ngdoc event
|
||||
* @name angular.service.$route#$beforeRouteChange
|
||||
* @eventOf angular.service.$route
|
||||
* @eventType broadcast on root scope
|
||||
* @description
|
||||
* Broadcasted before a route change.
|
||||
*
|
||||
* @param {Route} next Future route information.
|
||||
* @param {Route} current Current route information.
|
||||
*
|
||||
* The `Route` object extends the route definition with the following properties.
|
||||
*
|
||||
* * `scope` - The instance of the route controller.
|
||||
* * `params` - The current {@link angular.service.$routeParams params}.
|
||||
*
|
||||
*/
|
||||
function $RouteProvider(){
|
||||
this.$get = ['$rootScope', '$location', '$routeParams',
|
||||
function( $rootScope, $location, $routeParams) {
|
||||
/**
|
||||
* @ngdoc event
|
||||
* @name angular.service.$route#$beforeRouteChange
|
||||
* @eventOf angular.service.$route
|
||||
* @eventType broadcast on root scope
|
||||
* @description
|
||||
* Broadcasted before a route change.
|
||||
*
|
||||
* @param {Route} next Future route information.
|
||||
* @param {Route} current Current route information.
|
||||
*
|
||||
* The `Route` object extends the route definition with the following properties.
|
||||
*
|
||||
* * `scope` - The instance of the route controller.
|
||||
* * `params` - The current {@link angular.service.$routeParams params}.
|
||||
*
|
||||
*/
|
||||
|
||||
/**
|
||||
* @ngdoc event
|
||||
* @name angular.service.$route#$afterRouteChange
|
||||
* @eventOf angular.service.$route
|
||||
* @eventType broadcast on root scope
|
||||
* @description
|
||||
* Broadcasted after a route change.
|
||||
*
|
||||
* @param {Route} current Current route information.
|
||||
* @param {Route} previous Previous route information.
|
||||
*
|
||||
* The `Route` object extends the route definition with the following properties.
|
||||
*
|
||||
* * `scope` - The instance of the route controller.
|
||||
* * `params` - The current {@link angular.service.$routeParams params}.
|
||||
*
|
||||
*/
|
||||
/**
|
||||
* @ngdoc event
|
||||
* @name angular.service.$route#$afterRouteChange
|
||||
* @eventOf angular.service.$route
|
||||
* @eventType broadcast on root scope
|
||||
* @description
|
||||
* Broadcasted after a route change.
|
||||
*
|
||||
* @param {Route} current Current route information.
|
||||
* @param {Route} previous Previous route information.
|
||||
*
|
||||
* The `Route` object extends the route definition with the following properties.
|
||||
*
|
||||
* * `scope` - The instance of the route controller.
|
||||
* * `params` - The current {@link angular.service.$routeParams params}.
|
||||
*
|
||||
*/
|
||||
|
||||
/**
|
||||
* @ngdoc event
|
||||
* @name angular.service.$route#$routeUpdate
|
||||
* @eventOf angular.service.$route
|
||||
* @eventType emit on the current route scope
|
||||
* @description
|
||||
*
|
||||
* The `reloadOnSearch` property has been set to false, and we are reusing the same
|
||||
* instance of the Controller.
|
||||
*/
|
||||
/**
|
||||
* @ngdoc event
|
||||
* @name angular.service.$route#$routeUpdate
|
||||
* @eventOf angular.service.$route
|
||||
* @eventType emit on the current route scope
|
||||
* @description
|
||||
*
|
||||
* The `reloadOnSearch` property has been set to false, and we are reusing the same
|
||||
* instance of the Controller.
|
||||
*/
|
||||
|
||||
var routes = {},
|
||||
matcher = switchRouteMatcher,
|
||||
parentScope = $rootScope,
|
||||
dirty = 0,
|
||||
forceReload = false,
|
||||
$route = {
|
||||
routes: routes,
|
||||
var routes = {},
|
||||
matcher = switchRouteMatcher,
|
||||
parentScope = $rootScope,
|
||||
dirty = 0,
|
||||
forceReload = false,
|
||||
$route = {
|
||||
routes: routes,
|
||||
|
||||
/**
|
||||
* @ngdoc method
|
||||
* @name angular.service.$route#parent
|
||||
* @methodOf angular.service.$route
|
||||
*
|
||||
* @param {Scope} [scope=rootScope] Scope to be used as parent for newly created
|
||||
* `$route.current.scope` scopes.
|
||||
*
|
||||
* @description
|
||||
* Sets a scope to be used as the parent scope for scopes created on route change. If not
|
||||
* set, defaults to the root scope.
|
||||
*/
|
||||
parent: function(scope) {
|
||||
if (scope) parentScope = scope;
|
||||
},
|
||||
/**
|
||||
* @ngdoc method
|
||||
* @name angular.service.$route#parent
|
||||
* @methodOf angular.service.$route
|
||||
*
|
||||
* @param {Scope} [scope=rootScope] Scope to be used as parent for newly created
|
||||
* `$route.current.scope` scopes.
|
||||
*
|
||||
* @description
|
||||
* Sets a scope to be used as the parent scope for scopes created on route change. If not
|
||||
* set, defaults to the root scope.
|
||||
*/
|
||||
parent: function(scope) {
|
||||
if (scope) parentScope = scope;
|
||||
},
|
||||
|
||||
/**
|
||||
* @ngdoc method
|
||||
* @name angular.service.$route#when
|
||||
* @methodOf angular.service.$route
|
||||
*
|
||||
* @param {string} path Route path (matched against `$location.hash`)
|
||||
* @param {Object} route Mapping information to be assigned to `$route.current` on route
|
||||
* match.
|
||||
*
|
||||
* Object properties:
|
||||
*
|
||||
* - `controller` – `{function()=}` – Controller fn that should be associated with newly
|
||||
* created scope.
|
||||
* - `template` – `{string=}` – path to an html template that should be used by
|
||||
* {@link angular.widget.ng:view ng:view} or
|
||||
* {@link angular.widget.ng:include ng:include} widgets.
|
||||
* - `redirectTo` – {(string|function())=} – value to update
|
||||
* {@link angular.service.$location $location} path with and trigger route redirection.
|
||||
*
|
||||
* If `redirectTo` is a function, it will be called with the following parameters:
|
||||
*
|
||||
* - `{Object.<string>}` - route parameters extracted from the current
|
||||
* `$location.path()` by applying the current route template.
|
||||
* - `{string}` - current `$location.path()`
|
||||
* - `{Object}` - current `$location.search()`
|
||||
*
|
||||
* The custom `redirectTo` function is expected to return a string which will be used
|
||||
* to update `$location.path()` and `$location.search()`.
|
||||
*
|
||||
* - `[reloadOnSearch=true]` - {boolean=} - reload route when only $location.search()
|
||||
* changes.
|
||||
*
|
||||
* If the option is set to false and url in the browser changes, then
|
||||
* $routeUpdate event is emited on the current route scope. You can use this event to
|
||||
* react to {@link angular.service.$routeParams} changes:
|
||||
*
|
||||
* function MyCtrl($route, $routeParams) {
|
||||
* this.$on('$routeUpdate', function() {
|
||||
* // do stuff with $routeParams
|
||||
* });
|
||||
* }
|
||||
*
|
||||
* @returns {Object} route object
|
||||
*
|
||||
* @description
|
||||
* Adds a new route definition to the `$route` service.
|
||||
*/
|
||||
when: function(path, route) {
|
||||
var routeDef = routes[path];
|
||||
if (!routeDef) routeDef = routes[path] = {reloadOnSearch: true};
|
||||
if (route) extend(routeDef, route); // TODO(im): what the heck? merge two route definitions?
|
||||
dirty++;
|
||||
return routeDef;
|
||||
},
|
||||
/**
|
||||
* @ngdoc method
|
||||
* @name angular.service.$route#when
|
||||
* @methodOf angular.service.$route
|
||||
*
|
||||
* @param {string} path Route path (matched against `$location.hash`)
|
||||
* @param {Object} route Mapping information to be assigned to `$route.current` on route
|
||||
* match.
|
||||
*
|
||||
* Object properties:
|
||||
*
|
||||
* - `controller` – `{function()=}` – Controller fn that should be associated with newly
|
||||
* created scope.
|
||||
* - `template` – `{string=}` – path to an html template that should be used by
|
||||
* {@link angular.widget.ng:view ng:view} or
|
||||
* {@link angular.widget.ng:include ng:include} widgets.
|
||||
* - `redirectTo` – {(string|function())=} – value to update
|
||||
* {@link angular.service.$location $location} path with and trigger route redirection.
|
||||
*
|
||||
* If `redirectTo` is a function, it will be called with the following parameters:
|
||||
*
|
||||
* - `{Object.<string>}` - route parameters extracted from the current
|
||||
* `$location.path()` by applying the current route template.
|
||||
* - `{string}` - current `$location.path()`
|
||||
* - `{Object}` - current `$location.search()`
|
||||
*
|
||||
* The custom `redirectTo` function is expected to return a string which will be used
|
||||
* to update `$location.path()` and `$location.search()`.
|
||||
*
|
||||
* - `[reloadOnSearch=true]` - {boolean=} - reload route when only $location.search()
|
||||
* changes.
|
||||
*
|
||||
* If the option is set to false and url in the browser changes, then
|
||||
* $routeUpdate event is emited on the current route scope. You can use this event to
|
||||
* react to {@link angular.service.$routeParams} changes:
|
||||
*
|
||||
* function MyCtrl($route, $routeParams) {
|
||||
* this.$on('$routeUpdate', function() {
|
||||
* // do stuff with $routeParams
|
||||
* });
|
||||
* }
|
||||
*
|
||||
* @returns {Object} route object
|
||||
*
|
||||
* @description
|
||||
* Adds a new route definition to the `$route` service.
|
||||
*/
|
||||
when: function(path, route) {
|
||||
var routeDef = routes[path];
|
||||
if (!routeDef) routeDef = routes[path] = {reloadOnSearch: true};
|
||||
if (route) extend(routeDef, route); // TODO(im): what the heck? merge two route definitions?
|
||||
dirty++;
|
||||
return routeDef;
|
||||
},
|
||||
|
||||
/**
|
||||
* @ngdoc method
|
||||
* @name angular.service.$route#otherwise
|
||||
* @methodOf angular.service.$route
|
||||
*
|
||||
* @description
|
||||
* Sets route definition that will be used on route change when no other route definition
|
||||
* is matched.
|
||||
*
|
||||
* @param {Object} params Mapping information to be assigned to `$route.current`.
|
||||
*/
|
||||
otherwise: function(params) {
|
||||
$route.when(null, params);
|
||||
},
|
||||
/**
|
||||
* @ngdoc method
|
||||
* @name angular.service.$route#otherwise
|
||||
* @methodOf angular.service.$route
|
||||
*
|
||||
* @description
|
||||
* Sets route definition that will be used on route change when no other route definition
|
||||
* is matched.
|
||||
*
|
||||
* @param {Object} params Mapping information to be assigned to `$route.current`.
|
||||
*/
|
||||
otherwise: function(params) {
|
||||
$route.when(null, params);
|
||||
},
|
||||
|
||||
/**
|
||||
* @ngdoc method
|
||||
* @name angular.service.$route#reload
|
||||
* @methodOf angular.service.$route
|
||||
*
|
||||
* @description
|
||||
* Causes `$route` service to reload (and recreate the `$route.current` scope) upon the next
|
||||
* eval even if {@link angular.service.$location $location} hasn't changed.
|
||||
*/
|
||||
reload: function() {
|
||||
dirty++;
|
||||
forceReload = true;
|
||||
}
|
||||
};
|
||||
|
||||
$rootScope.$watch(function() { return dirty + $location.url(); }, updateRoute);
|
||||
|
||||
return $route;
|
||||
|
||||
/////////////////////////////////////////////////////
|
||||
|
||||
function switchRouteMatcher(on, when) {
|
||||
// TODO(i): this code is convoluted and inefficient, we should construct the route matching
|
||||
// regex only once and then reuse it
|
||||
var regex = '^' + when.replace(/([\.\\\(\)\^\$])/g, "\\$1") + '$',
|
||||
params = [],
|
||||
dst = {};
|
||||
forEach(when.split(/\W/), function(param) {
|
||||
if (param) {
|
||||
var paramRegExp = new RegExp(":" + param + "([\\W])");
|
||||
if (regex.match(paramRegExp)) {
|
||||
regex = regex.replace(paramRegExp, "([^\\/]*)$1");
|
||||
params.push(param);
|
||||
}
|
||||
}
|
||||
});
|
||||
var match = on.match(new RegExp(regex));
|
||||
if (match) {
|
||||
forEach(params, function(name, index) {
|
||||
dst[name] = match[index + 1];
|
||||
});
|
||||
}
|
||||
return match ? dst : null;
|
||||
}
|
||||
|
||||
function updateRoute() {
|
||||
var next = parseRoute(),
|
||||
last = $route.current,
|
||||
Controller;
|
||||
|
||||
if (next && last && next.$route === last.$route
|
||||
&& equals(next.pathParams, last.pathParams) && !next.reloadOnSearch && !forceReload) {
|
||||
$route.current = next;
|
||||
copy(next.params, $routeParams);
|
||||
last.scope && last.scope.$emit('$routeUpdate');
|
||||
} else {
|
||||
forceReload = false;
|
||||
$rootScope.$broadcast('$beforeRouteChange', next, last);
|
||||
last && last.scope && last.scope.$destroy();
|
||||
$route.current = next;
|
||||
if (next) {
|
||||
if (next.redirectTo) {
|
||||
if (isString(next.redirectTo)) {
|
||||
$location.path(interpolate(next.redirectTo, next.params)).search(next.params)
|
||||
.replace();
|
||||
} else {
|
||||
$location.url(next.redirectTo(next.pathParams, $location.path(), $location.search()))
|
||||
.replace();
|
||||
/**
|
||||
* @ngdoc method
|
||||
* @name angular.service.$route#reload
|
||||
* @methodOf angular.service.$route
|
||||
*
|
||||
* @description
|
||||
* Causes `$route` service to reload (and recreate the `$route.current` scope) upon the next
|
||||
* eval even if {@link angular.service.$location $location} hasn't changed.
|
||||
*/
|
||||
reload: function() {
|
||||
dirty++;
|
||||
forceReload = true;
|
||||
}
|
||||
};
|
||||
|
||||
$rootScope.$watch(function() { return dirty + $location.url(); }, updateRoute);
|
||||
|
||||
return $route;
|
||||
|
||||
/////////////////////////////////////////////////////
|
||||
|
||||
function switchRouteMatcher(on, when) {
|
||||
// TODO(i): this code is convoluted and inefficient, we should construct the route matching
|
||||
// regex only once and then reuse it
|
||||
var regex = '^' + when.replace(/([\.\\\(\)\^\$])/g, "\\$1") + '$',
|
||||
params = [],
|
||||
dst = {};
|
||||
forEach(when.split(/\W/), function(param) {
|
||||
if (param) {
|
||||
var paramRegExp = new RegExp(":" + param + "([\\W])");
|
||||
if (regex.match(paramRegExp)) {
|
||||
regex = regex.replace(paramRegExp, "([^\\/]*)$1");
|
||||
params.push(param);
|
||||
}
|
||||
} else {
|
||||
copy(next.params, $routeParams);
|
||||
(Controller = next.controller) && inferInjectionArgs(Controller);
|
||||
next.scope = parentScope.$new(Controller);
|
||||
}
|
||||
});
|
||||
var match = on.match(new RegExp(regex));
|
||||
if (match) {
|
||||
forEach(params, function(name, index) {
|
||||
dst[name] = match[index + 1];
|
||||
});
|
||||
}
|
||||
$rootScope.$broadcast('$afterRouteChange', next, last);
|
||||
return match ? dst : null;
|
||||
}
|
||||
}
|
||||
|
||||
function updateRoute() {
|
||||
var next = parseRoute(),
|
||||
last = $route.current,
|
||||
Controller;
|
||||
|
||||
/**
|
||||
* @returns the current active route, by matching it against the URL
|
||||
*/
|
||||
function parseRoute() {
|
||||
// Match a route
|
||||
var params, match;
|
||||
forEach(routes, function(route, path) {
|
||||
if (!match && (params = matcher($location.path(), path))) {
|
||||
match = inherit(route, {
|
||||
params: extend({}, $location.search(), params),
|
||||
pathParams: params});
|
||||
match.$route = route;
|
||||
}
|
||||
});
|
||||
// No route matched; fallback to "otherwise" route
|
||||
return match || routes[null] && inherit(routes[null], {params: {}, pathParams:{}});
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns interpolation of the redirect path with the parametrs
|
||||
*/
|
||||
function interpolate(string, params) {
|
||||
var result = [];
|
||||
forEach((string||'').split(':'), function(segment, i) {
|
||||
if (i == 0) {
|
||||
result.push(segment);
|
||||
if (next && last && next.$route === last.$route
|
||||
&& equals(next.pathParams, last.pathParams) && !next.reloadOnSearch && !forceReload) {
|
||||
$route.current = next;
|
||||
copy(next.params, $routeParams);
|
||||
last.scope && last.scope.$emit('$routeUpdate');
|
||||
} else {
|
||||
var segmentMatch = segment.match(/(\w+)(.*)/);
|
||||
var key = segmentMatch[1];
|
||||
result.push(params[key]);
|
||||
result.push(segmentMatch[2] || '');
|
||||
delete params[key];
|
||||
forceReload = false;
|
||||
$rootScope.$broadcast('$beforeRouteChange', next, last);
|
||||
last && last.scope && last.scope.$destroy();
|
||||
$route.current = next;
|
||||
if (next) {
|
||||
if (next.redirectTo) {
|
||||
if (isString(next.redirectTo)) {
|
||||
$location.path(interpolate(next.redirectTo, next.params)).search(next.params)
|
||||
.replace();
|
||||
} else {
|
||||
$location.url(next.redirectTo(next.pathParams, $location.path(), $location.search()))
|
||||
.replace();
|
||||
}
|
||||
} else {
|
||||
copy(next.params, $routeParams);
|
||||
(Controller = next.controller) && inferInjectionArgs(Controller);
|
||||
next.scope = parentScope.$new(Controller);
|
||||
}
|
||||
}
|
||||
$rootScope.$broadcast('$afterRouteChange', next, last);
|
||||
}
|
||||
});
|
||||
return result.join('');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}, ['$rootScope', '$location', '$routeParams']);
|
||||
/**
|
||||
* @returns the current active route, by matching it against the URL
|
||||
*/
|
||||
function parseRoute() {
|
||||
// Match a route
|
||||
var params, match;
|
||||
forEach(routes, function(route, path) {
|
||||
if (!match && (params = matcher($location.path(), path))) {
|
||||
match = inherit(route, {
|
||||
params: extend({}, $location.search(), params),
|
||||
pathParams: params});
|
||||
match.$route = route;
|
||||
}
|
||||
});
|
||||
// No route matched; fallback to "otherwise" route
|
||||
return match || routes[null] && inherit(routes[null], {params: {}, pathParams:{}});
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns interpolation of the redirect path with the parametrs
|
||||
*/
|
||||
function interpolate(string, params) {
|
||||
var result = [];
|
||||
forEach((string||'').split(':'), function(segment, i) {
|
||||
if (i == 0) {
|
||||
result.push(segment);
|
||||
} else {
|
||||
var segmentMatch = segment.match(/(\w+)(.*)/);
|
||||
var key = segmentMatch[1];
|
||||
result.push(params[key]);
|
||||
result.push(segmentMatch[2] || '');
|
||||
delete params[key];
|
||||
}
|
||||
});
|
||||
return result.join('');
|
||||
}
|
||||
|
||||
|
||||
}];
|
||||
}
|
||||
|
|
|
|||
|
|
@ -25,6 +25,6 @@
|
|||
* $routeParams ==> {chapterId:1, sectionId:2, search:'moby'}
|
||||
* </pre>
|
||||
*/
|
||||
angularService('$routeParams', function() {
|
||||
return {};
|
||||
});
|
||||
function $RouteParamsProvider() {
|
||||
this.$get = valueFn({});
|
||||
}
|
||||
|
|
|
|||
1198
src/service/scope.js
1198
src/service/scope.js
File diff suppressed because it is too large
Load diff
|
|
@ -11,13 +11,15 @@
|
|||
* @description
|
||||
* This is very simple implementation of testing browser's features.
|
||||
*/
|
||||
angularServiceInject('$sniffer', function($window) {
|
||||
if ($window.Modernizr) return $window.Modernizr;
|
||||
function $SnifferProvider(){
|
||||
this.$get = ['$window', function($window){
|
||||
if ($window.Modernizr) return $window.Modernizr;
|
||||
|
||||
return {
|
||||
history: !!($window.history && $window.history.pushState),
|
||||
hashchange: 'onhashchange' in $window &&
|
||||
// IE8 compatible mode lies
|
||||
(!$window.document.documentMode || $window.document.documentMode > 7)
|
||||
};
|
||||
}, ['$window']);
|
||||
return {
|
||||
history: !!($window.history && $window.history.pushState),
|
||||
hashchange: 'onhashchange' in $window &&
|
||||
// IE8 compatible mode lies
|
||||
(!$window.document.documentMode || $window.document.documentMode > 7)
|
||||
};
|
||||
}];
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,4 +23,6 @@
|
|||
</doc:scenario>
|
||||
</doc:example>
|
||||
*/
|
||||
angularServiceInject("$window", bind(window, identity, window));
|
||||
function $WindowProvider(){
|
||||
this.$get = valueFn(window);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,76 +11,79 @@
|
|||
*
|
||||
* @example
|
||||
*/
|
||||
angularServiceInject('$xhr.bulk', function($rootScope, $xhr, $error, $log){
|
||||
var requests = [];
|
||||
function bulkXHR(method, url, post, success, error) {
|
||||
if (isFunction(post)) {
|
||||
error = success;
|
||||
success = post;
|
||||
post = null;
|
||||
}
|
||||
var currentQueue;
|
||||
forEach(bulkXHR.urls, function(queue){
|
||||
if (isFunction(queue.match) ? queue.match(url) : queue.match.exec(url)) {
|
||||
currentQueue = queue;
|
||||
function $XhrBulkProvider() {
|
||||
this.$get = ['$rootScope', '$xhr', '$xhr.error', '$log',
|
||||
function( $rootScope, $xhr, $error, $log) {
|
||||
var requests = [];
|
||||
function bulkXHR(method, url, post, success, error) {
|
||||
if (isFunction(post)) {
|
||||
error = success;
|
||||
success = post;
|
||||
post = null;
|
||||
}
|
||||
});
|
||||
if (currentQueue) {
|
||||
if (!currentQueue.requests) currentQueue.requests = [];
|
||||
var request = {
|
||||
method: method,
|
||||
url: url,
|
||||
data: post,
|
||||
success: success};
|
||||
if (error) request.error = error;
|
||||
currentQueue.requests.push(request);
|
||||
} else {
|
||||
$xhr(method, url, post, success, error);
|
||||
}
|
||||
}
|
||||
bulkXHR.urls = {};
|
||||
bulkXHR.flush = function(success, errorback) {
|
||||
assertArgFn(success = success || noop, 0);
|
||||
assertArgFn(errorback = errorback || noop, 1);
|
||||
forEach(bulkXHR.urls, function(queue, url) {
|
||||
var currentRequests = queue.requests;
|
||||
if (currentRequests && currentRequests.length) {
|
||||
queue.requests = [];
|
||||
queue.callbacks = [];
|
||||
$xhr('POST', url, {requests: currentRequests},
|
||||
function(code, response) {
|
||||
forEach(response, function(response, i) {
|
||||
try {
|
||||
if (response.status == 200) {
|
||||
(currentRequests[i].success || noop)(response.status, response.response);
|
||||
} else if (isFunction(currentRequests[i].error)) {
|
||||
currentRequests[i].error(response.status, response.response);
|
||||
} else {
|
||||
$error(currentRequests[i], response);
|
||||
}
|
||||
} catch(e) {
|
||||
$log.error(e);
|
||||
}
|
||||
});
|
||||
success();
|
||||
},
|
||||
function(code, response) {
|
||||
forEach(currentRequests, function(request, i) {
|
||||
try {
|
||||
if (isFunction(request.error)) {
|
||||
request.error(code, response);
|
||||
} else {
|
||||
$error(request, response);
|
||||
}
|
||||
} catch(e) {
|
||||
$log.error(e);
|
||||
}
|
||||
});
|
||||
noop();
|
||||
});
|
||||
var currentQueue;
|
||||
forEach(bulkXHR.urls, function(queue){
|
||||
if (isFunction(queue.match) ? queue.match(url) : queue.match.exec(url)) {
|
||||
currentQueue = queue;
|
||||
}
|
||||
});
|
||||
if (currentQueue) {
|
||||
if (!currentQueue.requests) currentQueue.requests = [];
|
||||
var request = {
|
||||
method: method,
|
||||
url: url,
|
||||
data: post,
|
||||
success: success};
|
||||
if (error) request.error = error;
|
||||
currentQueue.requests.push(request);
|
||||
} else {
|
||||
$xhr(method, url, post, success, error);
|
||||
}
|
||||
});
|
||||
};
|
||||
$rootScope.$watch(function() { bulkXHR.flush(); });
|
||||
return bulkXHR;
|
||||
}, ['$rootScope', '$xhr', '$xhr.error', '$log']);
|
||||
}
|
||||
bulkXHR.urls = {};
|
||||
bulkXHR.flush = function(success, errorback) {
|
||||
assertArgFn(success = success || noop, 0);
|
||||
assertArgFn(errorback = errorback || noop, 1);
|
||||
forEach(bulkXHR.urls, function(queue, url) {
|
||||
var currentRequests = queue.requests;
|
||||
if (currentRequests && currentRequests.length) {
|
||||
queue.requests = [];
|
||||
queue.callbacks = [];
|
||||
$xhr('POST', url, {requests: currentRequests},
|
||||
function(code, response) {
|
||||
forEach(response, function(response, i) {
|
||||
try {
|
||||
if (response.status == 200) {
|
||||
(currentRequests[i].success || noop)(response.status, response.response);
|
||||
} else if (isFunction(currentRequests[i].error)) {
|
||||
currentRequests[i].error(response.status, response.response);
|
||||
} else {
|
||||
$error(currentRequests[i], response);
|
||||
}
|
||||
} catch(e) {
|
||||
$log.error(e);
|
||||
}
|
||||
});
|
||||
success();
|
||||
},
|
||||
function(code, response) {
|
||||
forEach(currentRequests, function(request, i) {
|
||||
try {
|
||||
if (isFunction(request.error)) {
|
||||
request.error(code, response);
|
||||
} else {
|
||||
$error(request, response);
|
||||
}
|
||||
} catch(e) {
|
||||
$log.error(e);
|
||||
}
|
||||
});
|
||||
noop();
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
||||
$rootScope.$watch(function() { bulkXHR.flush(); });
|
||||
return bulkXHR;
|
||||
}];
|
||||
}
|
||||
|
|
|
|||
|
|
@ -28,87 +28,91 @@
|
|||
* cached entry. The `success` function will be called when the response is received.
|
||||
* @param {boolean=} [sync=false] in case of cache hit execute `success` synchronously.
|
||||
*/
|
||||
angularServiceInject('$xhr.cache', function($xhr, $defer, $error, $log) {
|
||||
var inflight = {};
|
||||
function cache(method, url, post, success, error, verifyCache, sync) {
|
||||
if (isFunction(post)) {
|
||||
if (!isFunction(success)) {
|
||||
verifyCache = success;
|
||||
sync = error;
|
||||
error = null;
|
||||
} else {
|
||||
function $XhrCacheProvider() {
|
||||
this.$get = ['$xhr.bulk', '$defer', '$xhr.error', '$log',
|
||||
function($xhr, $defer, $error, $log) {
|
||||
var inflight = {};
|
||||
function cache(method, url, post, success, error, verifyCache, sync) {
|
||||
if (isFunction(post)) {
|
||||
if (!isFunction(success)) {
|
||||
verifyCache = success;
|
||||
sync = error;
|
||||
error = null;
|
||||
} else {
|
||||
sync = verifyCache;
|
||||
verifyCache = error;
|
||||
error = success;
|
||||
}
|
||||
success = post;
|
||||
post = null;
|
||||
} else if (!isFunction(error)) {
|
||||
sync = verifyCache;
|
||||
verifyCache = error;
|
||||
error = success;
|
||||
error = null;
|
||||
}
|
||||
success = post;
|
||||
post = null;
|
||||
} else if (!isFunction(error)) {
|
||||
sync = verifyCache;
|
||||
verifyCache = error;
|
||||
error = null;
|
||||
}
|
||||
|
||||
if (method == 'GET') {
|
||||
var data, dataCached;
|
||||
if ((dataCached = cache.data[url])) {
|
||||
if (method == 'GET') {
|
||||
var data, dataCached;
|
||||
if ((dataCached = cache.data[url])) {
|
||||
|
||||
if (sync) {
|
||||
success(200, copy(dataCached.value));
|
||||
} else {
|
||||
$defer(function() { success(200, copy(dataCached.value)); });
|
||||
if (sync) {
|
||||
success(200, copy(dataCached.value));
|
||||
} else {
|
||||
$defer(function() { success(200, copy(dataCached.value)); });
|
||||
}
|
||||
|
||||
if (!verifyCache)
|
||||
return;
|
||||
}
|
||||
|
||||
if (!verifyCache)
|
||||
return;
|
||||
}
|
||||
|
||||
if ((data = inflight[url])) {
|
||||
data.successes.push(success);
|
||||
data.errors.push(error);
|
||||
} else {
|
||||
inflight[url] = {successes: [success], errors: [error]};
|
||||
cache.delegate(method, url, post,
|
||||
function(status, response) {
|
||||
if (status == 200)
|
||||
cache.data[url] = {value: response};
|
||||
var successes = inflight[url].successes;
|
||||
delete inflight[url];
|
||||
forEach(successes, function(success) {
|
||||
try {
|
||||
(success||noop)(status, copy(response));
|
||||
} catch(e) {
|
||||
$log.error(e);
|
||||
}
|
||||
});
|
||||
},
|
||||
function(status, response) {
|
||||
var errors = inflight[url].errors,
|
||||
successes = inflight[url].successes;
|
||||
delete inflight[url];
|
||||
|
||||
forEach(errors, function(error, i) {
|
||||
try {
|
||||
if (isFunction(error)) {
|
||||
error(status, copy(response));
|
||||
} else {
|
||||
$error(
|
||||
{method: method, url: url, data: post, success: successes[i]},
|
||||
{status: status, body: response});
|
||||
if ((data = inflight[url])) {
|
||||
data.successes.push(success);
|
||||
data.errors.push(error);
|
||||
} else {
|
||||
inflight[url] = {successes: [success], errors: [error]};
|
||||
cache.delegate(method, url, post,
|
||||
function(status, response) {
|
||||
if (status == 200)
|
||||
cache.data[url] = {value: response};
|
||||
var successes = inflight[url].successes;
|
||||
delete inflight[url];
|
||||
forEach(successes, function(success) {
|
||||
try {
|
||||
(success||noop)(status, copy(response));
|
||||
} catch(e) {
|
||||
$log.error(e);
|
||||
}
|
||||
} catch(e) {
|
||||
$log.error(e);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
},
|
||||
function(status, response) {
|
||||
var errors = inflight[url].errors,
|
||||
successes = inflight[url].successes;
|
||||
delete inflight[url];
|
||||
|
||||
} else {
|
||||
cache.data = {};
|
||||
cache.delegate(method, url, post, success, error);
|
||||
forEach(errors, function(error, i) {
|
||||
try {
|
||||
if (isFunction(error)) {
|
||||
error(status, copy(response));
|
||||
} else {
|
||||
$error(
|
||||
{method: method, url: url, data: post, success: successes[i]},
|
||||
{status: status, body: response});
|
||||
}
|
||||
} catch(e) {
|
||||
$log.error(e);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
} else {
|
||||
cache.data = {};
|
||||
cache.delegate(method, url, post, success, error);
|
||||
}
|
||||
}
|
||||
}
|
||||
cache.data = {};
|
||||
cache.delegate = $xhr;
|
||||
return cache;
|
||||
}, ['$xhr.bulk', '$defer', '$xhr.error', '$log']);
|
||||
cache.data = {};
|
||||
cache.delegate = $xhr;
|
||||
return cache;
|
||||
}];
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -35,8 +35,10 @@
|
|||
</doc:source>
|
||||
</doc:example>
|
||||
*/
|
||||
angularServiceInject('$xhr.error', function($log){
|
||||
return function(request, response){
|
||||
$log.error('ERROR: XHR: ' + request.url, request, response);
|
||||
};
|
||||
}, ['$log']);
|
||||
function $XhrErrorProvider() {
|
||||
this.$get = ['$log', function($log) {
|
||||
return function(request, response){
|
||||
$log.error('ERROR: XHR: ' + request.url, request, response);
|
||||
};
|
||||
}];
|
||||
}
|
||||
|
|
|
|||
|
|
@ -171,58 +171,61 @@
|
|||
</doc:scenario>
|
||||
</doc:example>
|
||||
*/
|
||||
angularServiceInject('$xhr', function($rootScope, $browser, $error, $log){
|
||||
var xhrHeaderDefaults = {
|
||||
common: {
|
||||
"Accept": "application/json, text/plain, */*",
|
||||
"X-Requested-With": "XMLHttpRequest"
|
||||
},
|
||||
post: {'Content-Type': 'application/x-www-form-urlencoded'},
|
||||
get: {}, // all these empty properties are needed so that client apps can just do:
|
||||
head: {}, // $xhr.defaults.headers.head.foo="bar" without having to create head object
|
||||
put: {}, // it also means that if we add a header for these methods in the future, it
|
||||
'delete': {}, // won't be easily silently lost due to an object assignment.
|
||||
patch: {}
|
||||
};
|
||||
function $XhrProvider() {
|
||||
this.$get = ['$rootScope', '$browser', '$xhr.error', '$log',
|
||||
function( $rootScope, $browser, $error, $log){
|
||||
var xhrHeaderDefaults = {
|
||||
common: {
|
||||
"Accept": "application/json, text/plain, */*",
|
||||
"X-Requested-With": "XMLHttpRequest"
|
||||
},
|
||||
post: {'Content-Type': 'application/x-www-form-urlencoded'},
|
||||
get: {}, // all these empty properties are needed so that client apps can just do:
|
||||
head: {}, // $xhr.defaults.headers.head.foo="bar" without having to create head object
|
||||
put: {}, // it also means that if we add a header for these methods in the future, it
|
||||
'delete': {}, // won't be easily silently lost due to an object assignment.
|
||||
patch: {}
|
||||
};
|
||||
|
||||
function xhr(method, url, post, success, error) {
|
||||
if (isFunction(post)) {
|
||||
error = success;
|
||||
success = post;
|
||||
post = null;
|
||||
}
|
||||
if (post && isObject(post)) {
|
||||
post = toJson(post);
|
||||
}
|
||||
|
||||
$browser.xhr(method, url, post, function(code, response){
|
||||
try {
|
||||
if (isString(response)) {
|
||||
if (response.match(/^\)\]\}',\n/)) response=response.substr(6);
|
||||
if (/^\s*[\[\{]/.exec(response) && /[\}\]]\s*$/.exec(response)) {
|
||||
response = fromJson(response, true);
|
||||
}
|
||||
}
|
||||
$rootScope.$apply(function() {
|
||||
if (200 <= code && code < 300) {
|
||||
success(code, response);
|
||||
} else if (isFunction(error)) {
|
||||
error(code, response);
|
||||
} else {
|
||||
$error(
|
||||
{method: method, url: url, data: post, success: success},
|
||||
{status: code, body: response});
|
||||
}
|
||||
});
|
||||
} catch (e) {
|
||||
$log.error(e);
|
||||
function xhr(method, url, post, success, error) {
|
||||
if (isFunction(post)) {
|
||||
error = success;
|
||||
success = post;
|
||||
post = null;
|
||||
}
|
||||
if (post && isObject(post)) {
|
||||
post = toJson(post);
|
||||
}
|
||||
}, extend({'X-XSRF-TOKEN': $browser.cookies()['XSRF-TOKEN']},
|
||||
xhrHeaderDefaults.common,
|
||||
xhrHeaderDefaults[lowercase(method)]));
|
||||
}
|
||||
|
||||
xhr.defaults = {headers: xhrHeaderDefaults};
|
||||
$browser.xhr(method, url, post, function(code, response){
|
||||
try {
|
||||
if (isString(response)) {
|
||||
if (response.match(/^\)\]\}',\n/)) response=response.substr(6);
|
||||
if (/^\s*[\[\{]/.exec(response) && /[\}\]]\s*$/.exec(response)) {
|
||||
response = fromJson(response, true);
|
||||
}
|
||||
}
|
||||
$rootScope.$apply(function() {
|
||||
if (200 <= code && code < 300) {
|
||||
success(code, response);
|
||||
} else if (isFunction(error)) {
|
||||
error(code, response);
|
||||
} else {
|
||||
$error(
|
||||
{method: method, url: url, data: post, success: success},
|
||||
{status: code, body: response});
|
||||
}
|
||||
});
|
||||
} catch (e) {
|
||||
$log.error(e);
|
||||
}
|
||||
}, extend({'X-XSRF-TOKEN': $browser.cookies()['XSRF-TOKEN']},
|
||||
xhrHeaderDefaults.common,
|
||||
xhrHeaderDefaults[lowercase(method)]));
|
||||
}
|
||||
|
||||
return xhr;
|
||||
}, ['$rootScope', '$browser', '$xhr.error', '$log']);
|
||||
xhr.defaults = {headers: xhrHeaderDefaults};
|
||||
|
||||
return xhr;
|
||||
}];
|
||||
}
|
||||
|
|
|
|||
|
|
@ -382,25 +382,6 @@ describe('angular', function() {
|
|||
expect(fake).toEqual('new');
|
||||
}));
|
||||
|
||||
it('should not preserve properties on override', function() {
|
||||
angular.service('fake', {$one: true}, {$two: true}, {three: true});
|
||||
var result = angular.service('fake', {$four: true});
|
||||
|
||||
expect(result.$one).toBeUndefined();
|
||||
expect(result.$two).toBeUndefined();
|
||||
expect(result.three).toBeUndefined();
|
||||
expect(result.$four).toBe(true);
|
||||
});
|
||||
|
||||
it('should not preserve non-angular properties on override', function() {
|
||||
angular.service('fake', {one: true}, {two: true});
|
||||
var result = angular.service('fake', {third: true});
|
||||
|
||||
expect(result.one).not.toBeDefined();
|
||||
expect(result.two).not.toBeDefined();
|
||||
expect(result.third).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should inject dependencies specified by $inject and ignore function argument name', function() {
|
||||
expect(angular.injector(function($provide){
|
||||
$provide.factory('svc1', function() { return 'svc1'; });
|
||||
|
|
|
|||
|
|
@ -222,8 +222,8 @@ describe('Binder', function() {
|
|||
}));
|
||||
|
||||
it('IfTextBindingThrowsErrorDecorateTheSpan', inject(
|
||||
function($provide){
|
||||
$provide.factory('$exceptionHandler', $exceptionHandlerMockFactory);
|
||||
function($exceptionHandlerProvider){
|
||||
$exceptionHandlerProvider.mode('log');
|
||||
},
|
||||
function($rootScope, $exceptionHandler, $compile) {
|
||||
$compile('<div>{{error.throw()}}</div>', null, true)($rootScope);
|
||||
|
|
@ -245,8 +245,8 @@ describe('Binder', function() {
|
|||
})
|
||||
);
|
||||
|
||||
it('IfAttrBindingThrowsErrorDecorateTheAttribute', inject(function($provide){
|
||||
$provide.factory('$exceptionHandler', $exceptionHandlerMockFactory);
|
||||
it('IfAttrBindingThrowsErrorDecorateTheAttribute', inject(function($exceptionHandlerProvider){
|
||||
$exceptionHandlerProvider.mode('log');
|
||||
}, function($rootScope, $exceptionHandler, $compile) {
|
||||
$compile('<div attr="before {{error.throw()}} after"></div>', null, true)($rootScope);
|
||||
var errorLogs = $exceptionHandler.errors;
|
||||
|
|
@ -387,8 +387,8 @@ describe('Binder', function() {
|
|||
}));
|
||||
|
||||
it('ActionOnAHrefThrowsError', inject(
|
||||
function($provide){
|
||||
$provide.factory('$exceptionHandler', $exceptionHandlerMockFactory);
|
||||
function($exceptionHandlerProvider){
|
||||
$exceptionHandlerProvider.mode('log');
|
||||
},
|
||||
function($rootScope, $exceptionHandler, $compile) {
|
||||
var input = $compile('<a ng:click="action()">Add Phone</a>')($rootScope);
|
||||
|
|
@ -471,8 +471,8 @@ describe('Binder', function() {
|
|||
}));
|
||||
|
||||
it('ItShouldDisplayErrorWhenActionIsSyntacticlyIncorrect', inject(
|
||||
function($provide){
|
||||
$provide.factory('$exceptionHandler', $exceptionHandlerMockFactory);
|
||||
function($exceptionHandlerProvider){
|
||||
$exceptionHandlerProvider.mode('log');
|
||||
},
|
||||
function($rootScope, $exceptionHandler, $log, $compile) {
|
||||
var element = $compile(
|
||||
|
|
|
|||
|
|
@ -200,10 +200,10 @@ describe('filter', function() {
|
|||
|
||||
describe('date', function() {
|
||||
|
||||
var morning = new TzDate(+5, '2010-09-03T12:05:08.000Z'); //7am
|
||||
var noon = new TzDate(+5, '2010-09-03T17:05:08.000Z'); //12pm
|
||||
var midnight = new TzDate(+5, '2010-09-03T05:05:08.000Z'); //12am
|
||||
var earlyDate = new TzDate(+5, '0001-09-03T05:05:08.000Z');
|
||||
var morning = new angular.mock.TzDate(+5, '2010-09-03T12:05:08.000Z'); //7am
|
||||
var noon = new angular.mock.TzDate(+5, '2010-09-03T17:05:08.000Z'); //12pm
|
||||
var midnight = new angular.mock.TzDate(+5, '2010-09-03T05:05:08.000Z'); //12am
|
||||
var earlyDate = new angular.mock.TzDate(+5, '0001-09-03T05:05:08.000Z');
|
||||
|
||||
var context, date;
|
||||
|
||||
|
|
|
|||
|
|
@ -239,6 +239,12 @@ describe('injector', function() {
|
|||
expect($injector('a')).toEqual('abc');
|
||||
});
|
||||
|
||||
it('should error on invalid madule name', function(){
|
||||
expect(function(){
|
||||
createInjector(['IDontExist'], {});
|
||||
}).toThrow("Module 'IDontExist' is not defined!");
|
||||
});
|
||||
|
||||
|
||||
describe('$provide', function() {
|
||||
describe('value', function(){
|
||||
|
|
|
|||
113
test/angular-mocksSpec.js
vendored
113
test/angular-mocksSpec.js
vendored
|
|
@ -8,90 +8,90 @@ describe('mocks', function() {
|
|||
}
|
||||
|
||||
it('should take millis as constructor argument', function() {
|
||||
expect(new TzDate(0, 0).getTime()).toBe(0);
|
||||
expect(new TzDate(0, 1283555108000).getTime()).toBe(1283555108000);
|
||||
expect(new angular.mock.TzDate(0, 0).getTime()).toBe(0);
|
||||
expect(new angular.mock.TzDate(0, 1283555108000).getTime()).toBe(1283555108000);
|
||||
});
|
||||
|
||||
it('should take dateString as constructor argument', function() {
|
||||
expect(new TzDate(0, '1970-01-01T00:00:00.000Z').getTime()).toBe(0);
|
||||
expect(new TzDate(0, '2010-09-03T23:05:08.023Z').getTime()).toBe(1283555108023);
|
||||
expect(new angular.mock.TzDate(0, '1970-01-01T00:00:00.000Z').getTime()).toBe(0);
|
||||
expect(new angular.mock.TzDate(0, '2010-09-03T23:05:08.023Z').getTime()).toBe(1283555108023);
|
||||
});
|
||||
|
||||
|
||||
it('should fake getLocalDateString method', function() {
|
||||
//0 in -3h
|
||||
var t0 = new TzDate(-3, 0);
|
||||
var t0 = new angular.mock.TzDate(-3, 0);
|
||||
expect(t0.toLocaleDateString()).toMatch('1970');
|
||||
|
||||
//0 in +0h
|
||||
var t1 = new TzDate(0, 0);
|
||||
var t1 = new angular.mock.TzDate(0, 0);
|
||||
expect(t1.toLocaleDateString()).toMatch('1970');
|
||||
|
||||
//0 in +3h
|
||||
var t2 = new TzDate(3, 0);
|
||||
var t2 = new angular.mock.TzDate(3, 0);
|
||||
expect(t2.toLocaleDateString()).toMatch('1969');
|
||||
});
|
||||
|
||||
|
||||
it('should fake getHours method', function() {
|
||||
//0 in -3h
|
||||
var t0 = new TzDate(-3, 0);
|
||||
var t0 = new angular.mock.TzDate(-3, 0);
|
||||
expect(t0.getHours()).toBe(3);
|
||||
|
||||
//0 in +0h
|
||||
var t1 = new TzDate(0, 0);
|
||||
var t1 = new angular.mock.TzDate(0, 0);
|
||||
expect(t1.getHours()).toBe(0);
|
||||
|
||||
//0 in +3h
|
||||
var t2 = new TzDate(3, 0);
|
||||
var t2 = new angular.mock.TzDate(3, 0);
|
||||
expect(t2.getHours()).toMatch(21);
|
||||
});
|
||||
|
||||
|
||||
it('should fake getMinutes method', function() {
|
||||
//0:15 in -3h
|
||||
var t0 = new TzDate(-3, minutes(15));
|
||||
var t0 = new angular.mock.TzDate(-3, minutes(15));
|
||||
expect(t0.getMinutes()).toBe(15);
|
||||
|
||||
//0:15 in -3.25h
|
||||
var t0a = new TzDate(-3.25, minutes(15));
|
||||
var t0a = new angular.mock.TzDate(-3.25, minutes(15));
|
||||
expect(t0a.getMinutes()).toBe(30);
|
||||
|
||||
//0 in +0h
|
||||
var t1 = new TzDate(0, minutes(0));
|
||||
var t1 = new angular.mock.TzDate(0, minutes(0));
|
||||
expect(t1.getMinutes()).toBe(0);
|
||||
|
||||
//0:15 in +0h
|
||||
var t1a = new TzDate(0, minutes(15));
|
||||
var t1a = new angular.mock.TzDate(0, minutes(15));
|
||||
expect(t1a.getMinutes()).toBe(15);
|
||||
|
||||
//0:15 in +3h
|
||||
var t2 = new TzDate(3, minutes(15));
|
||||
var t2 = new angular.mock.TzDate(3, minutes(15));
|
||||
expect(t2.getMinutes()).toMatch(15);
|
||||
|
||||
//0:15 in +3.25h
|
||||
var t2a = new TzDate(3.25, minutes(15));
|
||||
var t2a = new angular.mock.TzDate(3.25, minutes(15));
|
||||
expect(t2a.getMinutes()).toMatch(0);
|
||||
});
|
||||
|
||||
|
||||
it('should fake getSeconds method', function() {
|
||||
//0 in -3h
|
||||
var t0 = new TzDate(-3, 0);
|
||||
var t0 = new angular.mock.TzDate(-3, 0);
|
||||
expect(t0.getSeconds()).toBe(0);
|
||||
|
||||
//0 in +0h
|
||||
var t1 = new TzDate(0, 0);
|
||||
var t1 = new angular.mock.TzDate(0, 0);
|
||||
expect(t1.getSeconds()).toBe(0);
|
||||
|
||||
//0 in +3h
|
||||
var t2 = new TzDate(3, 0);
|
||||
var t2 = new angular.mock.TzDate(3, 0);
|
||||
expect(t2.getSeconds()).toMatch(0);
|
||||
});
|
||||
|
||||
|
||||
it('should create a date representing new year in Bratislava', function() {
|
||||
var newYearInBratislava = new TzDate(-1, '2009-12-31T23:00:00.000Z');
|
||||
var newYearInBratislava = new angular.mock.TzDate(-1, '2009-12-31T23:00:00.000Z');
|
||||
expect(newYearInBratislava.getTimezoneOffset()).toBe(-60);
|
||||
expect(newYearInBratislava.getFullYear()).toBe(2010);
|
||||
expect(newYearInBratislava.getMonth()).toBe(0);
|
||||
|
|
@ -103,7 +103,7 @@ describe('mocks', function() {
|
|||
|
||||
it('should delegate all the UTC methods to the original UTC Date object', function() {
|
||||
//from when created from string
|
||||
var date1 = new TzDate(-1, '2009-12-31T23:00:00.000Z');
|
||||
var date1 = new angular.mock.TzDate(-1, '2009-12-31T23:00:00.000Z');
|
||||
expect(date1.getUTCFullYear()).toBe(2009);
|
||||
expect(date1.getUTCMonth()).toBe(11);
|
||||
expect(date1.getUTCDate()).toBe(31);
|
||||
|
|
@ -113,7 +113,7 @@ describe('mocks', function() {
|
|||
|
||||
|
||||
//from when created from millis
|
||||
var date2 = new TzDate(-1, angular.String.toDate('2009-12-31T23:00:00.000Z').getTime());
|
||||
var date2 = new angular.mock.TzDate(-1, angular.String.toDate('2009-12-31T23:00:00.000Z').getTime());
|
||||
expect(date2.getUTCFullYear()).toBe(2009);
|
||||
expect(date2.getUTCMonth()).toBe(11);
|
||||
expect(date2.getUTCDate()).toBe(31);
|
||||
|
|
@ -124,16 +124,20 @@ describe('mocks', function() {
|
|||
|
||||
|
||||
it('should throw error when no third param but toString called', function() {
|
||||
expect(function() { new TzDate(0,0).toString(); }).
|
||||
expect(function() { new angular.mock.TzDate(0,0).toString(); }).
|
||||
toThrow('Method \'toString\' is not implemented in the TzDate mock');
|
||||
});
|
||||
});
|
||||
|
||||
describe('$log mock', function() {
|
||||
describe('$log', function() {
|
||||
var $log;
|
||||
beforeEach(function() {
|
||||
$log = MockLogFactory();
|
||||
});
|
||||
beforeEach(inject(['$log', function(log) {
|
||||
$log = log;
|
||||
}]));
|
||||
|
||||
afterEach(inject(function($log){
|
||||
$log.reset();
|
||||
}));
|
||||
|
||||
it('should provide log method', function() {
|
||||
expect(function() { $log.log(''); }).not.toThrow();
|
||||
|
|
@ -170,14 +174,48 @@ describe('mocks', function() {
|
|||
$log.error('fake log');
|
||||
expect($log.error.logs).toContain(['fake log']);
|
||||
});
|
||||
|
||||
it('should assertEmpty', function(){
|
||||
try {
|
||||
$log.error(Error('MyError'));
|
||||
$log.warn(Error('MyWarn'));
|
||||
$log.info(Error('MyInfo'));
|
||||
$log.log(Error('MyLog'));
|
||||
$log.assertEmpty();
|
||||
} catch (error) {
|
||||
error = error.message || error;
|
||||
expect(error).toMatch(/Error: MyError/m);
|
||||
expect(error).toMatch(/Error: MyWarn/m);
|
||||
expect(error).toMatch(/Error: MyInfo/m);
|
||||
expect(error).toMatch(/Error: MyLog/m);
|
||||
} finally {
|
||||
$log.reset();
|
||||
}
|
||||
});
|
||||
|
||||
it('should reset state', function(){
|
||||
$log.error(Error('MyError'));
|
||||
$log.warn(Error('MyWarn'));
|
||||
$log.info(Error('MyInfo'));
|
||||
$log.log(Error('MyLog'));
|
||||
$log.reset();
|
||||
var passed = false;
|
||||
try {
|
||||
$log.assertEmpty(); // should not throw error!
|
||||
passed = true;
|
||||
} catch (e) {
|
||||
passed = e;
|
||||
}
|
||||
expect(passed).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('defer', function() {
|
||||
var browser, log;
|
||||
beforeEach(function() {
|
||||
browser = new MockBrowser();
|
||||
beforeEach(inject(function($browser) {
|
||||
browser = $browser;
|
||||
log = '';
|
||||
});
|
||||
}));
|
||||
|
||||
function logFn(text){ return function() {
|
||||
log += text +';';
|
||||
|
|
@ -229,5 +267,20 @@ describe('mocks', function() {
|
|||
it('should rethrow exceptions', inject(function($exceptionHandler) {
|
||||
expect(function() { $exceptionHandler('myException'); }).toThrow('myException');
|
||||
}));
|
||||
|
||||
|
||||
it('should log exceptions', inject(function($exceptionHandlerProvider){
|
||||
$exceptionHandlerProvider.mode('log');
|
||||
var $exceptionHandler = $exceptionHandlerProvider.$get();
|
||||
$exceptionHandler('MyError');
|
||||
expect($exceptionHandler.errors).toEqual(['MyError']);
|
||||
}));
|
||||
|
||||
|
||||
it('should thorw on wrong argument', inject(function($exceptionHandlerProvider) {
|
||||
expect(function() {
|
||||
$exceptionHandlerProvider.mode('XXX');
|
||||
}).toThrow("Unknown mode 'XXX', only 'log'/'rethrow' modes are allowed!");
|
||||
}));
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,69 +0,0 @@
|
|||
'use strict';
|
||||
|
||||
/**
|
||||
* Mock implementation of {@link angular.service.$log} that gathers all logged messages in arrays
|
||||
* (one array per logging level). These arrays are exposed as `logs` property of each of the
|
||||
* level-specific log function, e.g. for level `error` the array is exposed as
|
||||
* `$logMock.error.logs`
|
||||
*
|
||||
* Please note that this is not a factory function, but rather the actual mock instance. This is
|
||||
* important because it allows `beforeEach` and `afterEach` test hooks to clean up or check the
|
||||
* state of `logs` arrays in between tests.
|
||||
*
|
||||
* Exposing the instance in this way makes this mock a singleton, which means that the instance
|
||||
* becomes global state for tests. To mitigate the issue, each time the `$log` mock is registered
|
||||
* with the injector, a check is performed to ensure that there are no pending logs in `logs`
|
||||
* arrays. This means that if a message is logged via $log during a test, the `logs` array must be
|
||||
* emptied before the test is finished. `Array#shift` method can be used for this purpose as
|
||||
* follows:
|
||||
*
|
||||
* <pre>
|
||||
* it('should do some good', function() {
|
||||
* var scope = angular.scope(),
|
||||
* $log = scope.$service('$log');
|
||||
*
|
||||
* //do something that triggers a message to be logged
|
||||
* expect($log.error.logs.shift()).toEqual(['message', 'arg1', 'arg2']);
|
||||
* });
|
||||
* </pre>
|
||||
*
|
||||
* See {@link angular.mock} for more info on angular mocks.
|
||||
*/
|
||||
var $logMock = {
|
||||
log: function() { $logMock.log.logs.push(concat([], arguments, 0)); },
|
||||
warn: function() { $logMock.warn.logs.push(concat([], arguments, 0)); },
|
||||
info: function() { $logMock.info.logs.push(concat([], arguments, 0)); },
|
||||
error: function() { $logMock.error.logs.push(concat([], arguments, 0)); }
|
||||
};
|
||||
$logMock.log.logs = [];
|
||||
$logMock.warn.logs = [];
|
||||
$logMock.info.logs = [];
|
||||
$logMock.error.logs = [];
|
||||
|
||||
angular.service('$log', function() {
|
||||
return $logMock;
|
||||
});
|
||||
|
||||
|
||||
/**
|
||||
* Factory that returns mock implementation of {@link angular.service.$exceptionHandler} that
|
||||
* gathers all errors in an array. This array is exposed as `errors` property of the mock and can be
|
||||
* accessed as `$exceptionHandler.errors`.
|
||||
*
|
||||
* Note that this factory is not registered with angular's injector by default (as opposed to
|
||||
* `$logMock`). It is your responsibility to register this factory when you need it. Typically like
|
||||
* this:
|
||||
*
|
||||
* <pre>
|
||||
* var scope = angular.scope(null, {'$exceptionHandler': $exceptionHandlerMockFactory()});
|
||||
* </pre>
|
||||
*
|
||||
*/
|
||||
function $exceptionHandlerMockFactory() {
|
||||
var mockHandler = function(e) {
|
||||
mockHandler.errors.push(e);
|
||||
};
|
||||
mockHandler.errors = [];
|
||||
|
||||
return mockHandler;
|
||||
}
|
||||
|
|
@ -3,7 +3,7 @@
|
|||
describe('$cookies', function() {
|
||||
beforeEach(inject(function($provide) {
|
||||
$provide.factory('$browser', function(){
|
||||
return angular.extend(new MockBrowser(), {cookieHash: {preexisting:'oldCookie'}});
|
||||
return angular.extend(new angular.mock.$Browser(), {cookieHash: {preexisting:'oldCookie'}});
|
||||
});
|
||||
}));
|
||||
|
||||
|
|
|
|||
|
|
@ -5,11 +5,9 @@ describe('$exceptionHandler', function() {
|
|||
|
||||
it('should log errors', inject(
|
||||
function($provide){
|
||||
$provide.factory('$exceptionHandler', $exceptionHandlerFactory);
|
||||
$provide.value('$log', $logMock);
|
||||
$provide.service('$exceptionHandler', $ExceptionHandlerProvider);
|
||||
},
|
||||
function($log, $exceptionHandler) {
|
||||
$log.error.rethrow = false;
|
||||
$exceptionHandler('myError');
|
||||
expect($log.error.logs.shift()).toEqual(['myError']);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
describe('$locale', function() {
|
||||
|
||||
var $locale = angular.service('$locale')();
|
||||
var $locale = new $LocaleProvider().$get();
|
||||
|
||||
it('should have locale id set to en-us', function() {
|
||||
expect($locale.id).toBe('en-us');
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ describe('$log', function() {
|
|||
beforeEach(inject(function($provide){
|
||||
$window = {};
|
||||
logger = '';
|
||||
$provide.factory('$log', $logFactory);
|
||||
$provide.service('$log', $LogProvider);
|
||||
$provide.value('$exceptionHandler', rethrow);
|
||||
$provide.value('$window', $window);
|
||||
}));
|
||||
|
|
@ -68,7 +68,7 @@ describe('$log', function() {
|
|||
e.line = undefined;
|
||||
e.stack = undefined;
|
||||
|
||||
$log = $logFactory({console:{error:function() {
|
||||
$log = new $LogProvider().$get[1]({console:{error:function() {
|
||||
errorArgs = arguments;
|
||||
}}});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -2,8 +2,8 @@
|
|||
|
||||
describe('Scope', function() {
|
||||
|
||||
beforeEach(inject(function($provide) {
|
||||
$provide.factory('$exceptionHandler', $exceptionHandlerMockFactory);
|
||||
beforeEach(inject(function($exceptionHandlerProvider) {
|
||||
$exceptionHandlerProvider.mode('log');
|
||||
}));
|
||||
|
||||
|
||||
|
|
@ -121,12 +121,12 @@ describe('Scope', function() {
|
|||
expect(spy).wasCalled();
|
||||
}));
|
||||
|
||||
it('should delegate exceptions', inject(function($rootScope, $exceptionHandler) {
|
||||
it('should delegate exceptions', inject(function($rootScope, $exceptionHandler, $log) {
|
||||
$rootScope.$watch('a', function() {throw new Error('abc');});
|
||||
$rootScope.a = 1;
|
||||
$rootScope.$digest();
|
||||
expect($exceptionHandler.errors[0].message).toEqual('abc');
|
||||
$logMock.error.logs.length = 0;
|
||||
$log.assertEmpty();
|
||||
}));
|
||||
|
||||
|
||||
|
|
@ -422,7 +422,7 @@ describe('Scope', function() {
|
|||
}));
|
||||
|
||||
|
||||
it('should catch exceptions', inject(function($rootScope, $exceptionHandler) {
|
||||
it('should catch exceptions', inject(function($rootScope, $exceptionHandler, $log) {
|
||||
var log = '';
|
||||
var child = $rootScope.$new();
|
||||
$rootScope.$watch('a', function(scope, a){ log += '1'; });
|
||||
|
|
@ -430,7 +430,7 @@ describe('Scope', function() {
|
|||
child.$apply(function() { throw new Error('MyError'); });
|
||||
expect(log).toEqual('1');
|
||||
expect($exceptionHandler.errors[0].message).toEqual('MyError');
|
||||
$logMock.error.logs.shift();
|
||||
$log.error.logs.shift();
|
||||
}));
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
describe('$sniffer', function() {
|
||||
|
||||
function sniffer($window) {
|
||||
return angular.service('$sniffer')($window);
|
||||
return new $SnifferProvider().$get[1]($window);
|
||||
}
|
||||
|
||||
describe('history', function() {
|
||||
|
|
|
|||
|
|
@ -77,18 +77,13 @@ beforeEach(function() {
|
|||
}
|
||||
});
|
||||
|
||||
$logMock.log.logs = [];
|
||||
$logMock.warn.logs = [];
|
||||
$logMock.info.logs = [];
|
||||
$logMock.error.logs = [];
|
||||
|
||||
});
|
||||
|
||||
function inject(){
|
||||
var blockFns = sliceArgs(arguments);
|
||||
return function(){
|
||||
var spec = this;
|
||||
spec.$injector = spec.$injector || angular.injector('NG');
|
||||
spec.$injector = spec.$injector || angular.injector('NG', 'NG_MOCK');
|
||||
angular.forEach(blockFns, function(fn){
|
||||
spec.$injector.invoke(spec, fn);
|
||||
});
|
||||
|
|
@ -96,30 +91,12 @@ function inject(){
|
|||
}
|
||||
|
||||
|
||||
afterEach(inject(function($rootScope) {
|
||||
afterEach(inject(function($rootScope, $log) {
|
||||
// release the injector
|
||||
dealoc($rootScope);
|
||||
|
||||
// check $log mock
|
||||
forEach(['error', 'warn', 'info', 'log'], function(logLevel) {
|
||||
if ($logMock[logLevel].logs.length) {
|
||||
forEach($logMock[logLevel].logs, function(log) {
|
||||
forEach(log, function deleteStack(logItem) {
|
||||
if (logItem instanceof Error) {
|
||||
dump(logItem.stack);
|
||||
delete logItem.stack;
|
||||
delete logItem.arguments;
|
||||
} else {
|
||||
dump(logItem);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
throw new Error("Exprected $log." + logLevel + ".logs array to be empty. " +
|
||||
"Either a message was logged unexpectedly, or an expected log message was not checked " +
|
||||
"and removed. Array contents: " + toJson($logMock[logLevel].logs));
|
||||
}
|
||||
});
|
||||
$log.assertEmpty && $log.assertEmpty();
|
||||
|
||||
clearJqCache();
|
||||
}));
|
||||
|
|
|
|||
|
|
@ -448,12 +448,12 @@ describe('widget: input', function() {
|
|||
});
|
||||
|
||||
|
||||
it('should report error on assignment error', function() {
|
||||
it('should report error on assignment error', inject(function($log) {
|
||||
expect(function() {
|
||||
compile('<input type="text" ng:model="throw \'\'">');
|
||||
}).toThrow("Syntax Error: Token '''' is an unexpected token at column 7 of the expression [throw ''] starting at [''].");
|
||||
$logMock.error.logs.shift();
|
||||
});
|
||||
$log.error.logs.shift();
|
||||
}));
|
||||
});
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -223,12 +223,12 @@ describe("widget", function() {
|
|||
expect(element.text()).toEqual('name:value;');
|
||||
}));
|
||||
|
||||
it('should error on wrong parsing of ng:repeat', inject(function($rootScope, $compile) {
|
||||
it('should error on wrong parsing of ng:repeat', inject(function($rootScope, $compile, $log) {
|
||||
expect(function() {
|
||||
var element = $compile('<ul><li ng:repeat="i dont parse"></li></ul>')($rootScope);
|
||||
}).toThrow("Expected ng:repeat in form of '_item_ in _collection_' but got 'i dont parse'.");
|
||||
|
||||
$logMock.error.logs.shift();
|
||||
$log.error.logs.shift();
|
||||
}));
|
||||
|
||||
it('should expose iterator offset as $index when iterating over arrays', inject(function($rootScope, $compile) {
|
||||
|
|
@ -487,9 +487,9 @@ describe("widget", function() {
|
|||
}));
|
||||
|
||||
it('should be possible to nest ng:view in ng:include', inject(function() {
|
||||
var injector = angular.injector('NG');
|
||||
var injector = angular.injector('NG', 'NG_MOCK');
|
||||
var myApp = injector('$rootScope');
|
||||
var $browser = myApp.$service('$browser');
|
||||
var $browser = injector('$browser');
|
||||
$browser.xhr.expectGET('includePartial.html').respond('view: <ng:view></ng:view>');
|
||||
injector('$location').path('/foo');
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue