mirror of
https://github.com/Hopiu/angular.js.git
synced 2026-05-17 11:11:05 +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/jstd-scenario-adapter/*.js',
|
||||||
'src/scenario/*.js',
|
'src/scenario/*.js',
|
||||||
'src/angular-mocks.js',
|
'src/angular-mocks.js',
|
||||||
'test/mocks.js',
|
|
||||||
'test/scenario/*.js',
|
'test/scenario/*.js',
|
||||||
'test/scenario/output/*.js',
|
'test/scenario/output/*.js',
|
||||||
'test/jstd-scenario-adapter/*.js',
|
'test/jstd-scenario-adapter/*.js',
|
||||||
|
|
@ -133,7 +132,6 @@ angularFiles = {
|
||||||
'src/jstd-scenario-adapter/*.js',
|
'src/jstd-scenario-adapter/*.js',
|
||||||
'src/scenario/*.js',
|
'src/scenario/*.js',
|
||||||
'src/angular-mocks.js',
|
'src/angular-mocks.js',
|
||||||
'test/mocks.js',
|
|
||||||
'test/scenario/*.js',
|
'test/scenario/*.js',
|
||||||
'test/scenario/output/*.js',
|
'test/scenario/output/*.js',
|
||||||
'test/jstd-scenario-adapter/*.js',
|
'test/jstd-scenario-adapter/*.js',
|
||||||
|
|
|
||||||
|
|
@ -148,9 +148,12 @@ function TutorialInstructionsCtrl($cookieStore) {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
angular.service('$locationConfig', function() {
|
window.angular = window.angular || {};
|
||||||
return {
|
angular.module = angular.module || {};
|
||||||
|
|
||||||
|
angular.module.ngdocs = function($provide) {
|
||||||
|
$provide.value('$locationConfig', {
|
||||||
html5Mode: true,
|
html5Mode: true,
|
||||||
hashPrefix: '!'
|
hashPrefix: '!'
|
||||||
};
|
});
|
||||||
});
|
};
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ describe('example.personalLog.LogCtrl', function() {
|
||||||
var logCtrl;
|
var logCtrl;
|
||||||
|
|
||||||
function createNotesCtrl() {
|
function createNotesCtrl() {
|
||||||
var injector = angular.injector('NG');
|
var injector = angular.injector('NG', 'NG_MOCK');
|
||||||
var scope = injector('$rootScope');
|
var scope = injector('$rootScope');
|
||||||
scope.$cookies = injector('$cookies');
|
scope.$cookies = injector('$cookies');
|
||||||
return scope.$new(example.personalLog.LogCtrl);
|
return scope.$new(example.personalLog.LogCtrl);
|
||||||
|
|
|
||||||
|
|
@ -99,8 +99,8 @@ var _undefined = undefined,
|
||||||
: noop,
|
: noop,
|
||||||
|
|
||||||
/** @name angular */
|
/** @name angular */
|
||||||
angular = window[$angular] || (window[$angular] = {}),
|
angular = window.angular || (window.angular = {}),
|
||||||
angularModules = angular.modules || (angular.modules = {}),
|
angularModule = angular.module || (angular.module = {}),
|
||||||
/** @name angular.markup */
|
/** @name angular.markup */
|
||||||
angularTextMarkup = extensionMap(angular, 'markup'),
|
angularTextMarkup = extensionMap(angular, 'markup'),
|
||||||
/** @name angular.attrMarkup */
|
/** @name angular.attrMarkup */
|
||||||
|
|
@ -114,7 +114,6 @@ var _undefined = undefined,
|
||||||
/** @name angular.service */
|
/** @name angular.service */
|
||||||
angularInputType = extensionMap(angular, 'inputType', lowercase),
|
angularInputType = extensionMap(angular, 'inputType', lowercase),
|
||||||
/** @name angular.service */
|
/** @name angular.service */
|
||||||
angularService = extensionMap(angular, 'service'),
|
|
||||||
angularCallbacks = extensionMap(angular, 'callbacks'),
|
angularCallbacks = extensionMap(angular, 'callbacks'),
|
||||||
nodeName_,
|
nodeName_,
|
||||||
uid = ['0', '0', '0'],
|
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
|
* 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
|
* 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);
|
modules.push(module);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
createInjector(modules, angularModules)(['$rootScope', '$compile', function(scope, compile){
|
createInjector(modules, angularModule)(['$rootScope', '$compile', '$injector', function(scope, compile, injector){
|
||||||
scope.$apply(function(){
|
scope.$apply(function(){
|
||||||
compile(isString(autobind) ? document.getElementById(autobind) : document)(scope);
|
compile(isString(autobind) ? document.getElementById(autobind) : document)(scope);
|
||||||
});
|
});
|
||||||
|
|
@ -1030,7 +1017,7 @@ function publishExternalAPI(angular){
|
||||||
'extend': extend,
|
'extend': extend,
|
||||||
'equals': equals,
|
'equals': equals,
|
||||||
'forEach': forEach,
|
'forEach': forEach,
|
||||||
'injector': function(){ return createInjector(arguments, angularModules); },
|
'injector': function(){ return createInjector(arguments, angularModule); },
|
||||||
'noop':noop,
|
'noop':noop,
|
||||||
'bind':bind,
|
'bind':bind,
|
||||||
'toJson': toJson,
|
'toJson': toJson,
|
||||||
|
|
@ -1049,14 +1036,39 @@ function publishExternalAPI(angular){
|
||||||
'uppercase': uppercase
|
'uppercase': uppercase
|
||||||
});
|
});
|
||||||
|
|
||||||
angularModules.NG = ngModule;
|
angularModule.NG = ngModule;
|
||||||
}
|
}
|
||||||
|
|
||||||
ngModule.$inject = ['$provide'];
|
ngModule.$inject = ['$provide', '$injector'];
|
||||||
function ngModule($provide) {
|
function ngModule($provide, $injector) {
|
||||||
forEach(angularService, function(factory, name){
|
// TODO(misko): temporary services to get the compiler working;
|
||||||
$provide.factory(name, factory);
|
$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';
|
'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);
|
publishExternalAPI(angular);
|
||||||
|
|
||||||
//try to bind to jquery now so that one can write angular.element().read()
|
//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;
|
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()`
|
* `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
|
* @returns the $inject property of function. If not found the
|
||||||
|
|
@ -177,7 +173,11 @@ function createInjector(modulesToLoad, moduleRegistry) {
|
||||||
|
|
||||||
forEach(modulesToLoad, function(module){
|
forEach(modulesToLoad, function(module){
|
||||||
if (isString(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)) {
|
if (isFunction(module) || isArray(module)) {
|
||||||
$injector(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"
|
* @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
|
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
|
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
|
(e.g. angular.forEach(...)) AND MAKE SURE THAT THE GIVEN FUNCTION IS EXPORTED
|
||||||
|
|
@ -56,8 +31,15 @@
|
||||||
* the angular service exception handler.
|
* the angular service exception handler.
|
||||||
* * {@link angular.mock.service.$log $log } - A mock implementation of the angular service log.
|
* * {@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
|
* @ngdoc service
|
||||||
|
|
@ -81,7 +63,12 @@ angular.mock = {};
|
||||||
* - $browser.defer — enables testing of code that uses
|
* - $browser.defer — enables testing of code that uses
|
||||||
* {@link angular.service.$defer $defer service} for executing functions via the `setTimeout` api.
|
* {@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,
|
var self = this,
|
||||||
expectations = {},
|
expectations = {},
|
||||||
requests = [];
|
requests = [];
|
||||||
|
|
@ -309,7 +296,7 @@ function MockBrowser() {
|
||||||
return this.$$baseHref;
|
return this.$$baseHref;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
MockBrowser.prototype = {
|
angular.mock.$Browser.prototype = {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @name angular.mock.service.$browser#poll
|
* @name angular.mock.service.$browser#poll
|
||||||
|
|
@ -360,10 +347,6 @@ MockBrowser.prototype = {
|
||||||
addJs: function() {}
|
addJs: function() {}
|
||||||
};
|
};
|
||||||
|
|
||||||
angular.service('$browser', function() {
|
|
||||||
return new MockBrowser();
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @ngdoc service
|
* @ngdoc service
|
||||||
|
|
@ -376,9 +359,29 @@ angular.service('$browser', function() {
|
||||||
*
|
*
|
||||||
* See {@link angular.mock} for more info on angular mocks.
|
* See {@link angular.mock} for more info on angular mocks.
|
||||||
*/
|
*/
|
||||||
angular.service('$exceptionHandler', function() {
|
angular.mock.$ExceptionHandlerProvider = function(){
|
||||||
return function(e) { throw e; };
|
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.
|
* 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() {
|
$log.reset = function (){
|
||||||
var $log = {
|
$log.log.logs = [];
|
||||||
log: function() { $log.log.logs.push(arguments); },
|
$log.warn.logs = [];
|
||||||
warn: function() { $log.warn.logs.push(arguments); },
|
$log.info.logs = [];
|
||||||
info: function() { $log.info.logs.push(arguments); },
|
$log.error.logs = [];
|
||||||
error: function() { $log.error.logs.push(arguments); }
|
};
|
||||||
|
|
||||||
|
$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>
|
* </pre>
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
function TzDate(offset, timestamp) {
|
angular.mock.TzDate = function (offset, timestamp) {
|
||||||
if (angular.isString(timestamp)) {
|
if (angular.isString(timestamp)) {
|
||||||
var tsStr = timestamp;
|
var tsStr = timestamp;
|
||||||
|
|
||||||
|
|
@ -545,4 +568,4 @@ function TzDate(offset, timestamp) {
|
||||||
}
|
}
|
||||||
|
|
||||||
//make "tzDateInstance instanceof Date" return true
|
//make "tzDateInstance instanceof Date" return true
|
||||||
TzDate.prototype = Date.prototype;
|
angular.mock.TzDate.prototype = Date.prototype;
|
||||||
|
|
|
||||||
|
|
@ -1,325 +1,322 @@
|
||||||
'use strict';
|
'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){
|
function $CompileProvider(){
|
||||||
/**
|
this.$get = ['$injector', '$exceptionHandler', '$textMarkup', '$attrMarkup', '$directive', '$widget',
|
||||||
* Template provides directions an how to bind to a given element.
|
function( $injector, $exceptionHandler, $textMarkup, $attrMarkup, $directive, $widget){
|
||||||
* 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
|
* Template provides directions an how to bind to a given element.
|
||||||
* of child paths which contain child templates
|
* 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
|
||||||
function Template() {
|
* of child paths which contain child templates
|
||||||
this.paths = [];
|
*/
|
||||||
this.children = [];
|
function Template() {
|
||||||
this.linkFns = [];
|
this.paths = [];
|
||||||
this.newScope = false;
|
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);
|
|
||||||
}
|
}
|
||||||
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
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
|
Template.prototype = {
|
||||||
addLinkFn:function(linkingFn) {
|
link: function(element, scope) {
|
||||||
if (linkingFn) {
|
var childScope = scope,
|
||||||
//TODO(misko): temporary hack.
|
locals = {$element: element};
|
||||||
if (isFunction(linkingFn) && !linkingFn.$inject) {
|
if (this.newScope) {
|
||||||
linkingFn.$inject = ['$element'];
|
childScope = isFunction(this.newScope) ? scope.$new(this.newScope(scope)) : scope.$new();
|
||||||
}
|
element.data($$scope, childScope);
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
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,
|
//Compiler
|
||||||
widget,
|
//////////////////////////////////
|
||||||
fn,
|
|
||||||
directiveFns = self.directives,
|
/**
|
||||||
descend = true,
|
* @ngdoc function
|
||||||
directives = true,
|
* @name angular.compile
|
||||||
elementName = nodeName_(element),
|
* @function
|
||||||
elementNamespace = elementName.indexOf(':') > 0 ? lowercase(elementName).replace(':', '-') : '',
|
*
|
||||||
template,
|
* @description
|
||||||
selfApi = {
|
* Compiles a piece of HTML string or DOM into a template and produces a template function, which
|
||||||
compile: bind(self, self.compile),
|
* can then be used to link {@link angular.scope scope} and the template together.
|
||||||
descend: function(value){ if(isDefined(value)) descend = value; return descend;},
|
*
|
||||||
directives: function(value){ if(isDefined(value)) directives = value; return directives;},
|
* The compilation is a process of walking the DOM tree and trying to match DOM elements to
|
||||||
scope: function(value){ if(isDefined(value)) template.newScope = template.newScope || value; return template.newScope;}
|
* {@link angular.markup markup}, {@link angular.attrMarkup attrMarkup},
|
||||||
};
|
* {@link angular.widget widgets}, and {@link angular.directive directives}. For each match it
|
||||||
element.addClass(elementNamespace);
|
* executes corresponding markup, attrMarkup, widget or directive template function and collects the
|
||||||
template = new Template();
|
* instance functions into a single template function which is then returned.
|
||||||
eachAttribute(element, function(value, name){
|
*
|
||||||
if (!widget) {
|
* The template function can then be used once to produce the view or as it is the case with
|
||||||
if ((widget = self.widgets('@' + name))) {
|
* {@link angular.widget.@ng:repeat repeater} many-times, in which case each call results in a view
|
||||||
element.addClass('ng-attr-widget');
|
* that is a DOM clone of the original template.
|
||||||
widget = bind(selfApi, widget, value, element);
|
*
|
||||||
|
<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];
|
||||||
if (!widget) {
|
for(var i = 0; i < parent.childNodes.length; i++) {
|
||||||
if ((widget = self.widgets(elementName))) {
|
if (parent.childNodes[i] == templateElement[0]) {
|
||||||
if (elementNamespace)
|
index = i;
|
||||||
element.addClass('ng-widget');
|
}
|
||||||
widget = bind(selfApi, widget, element);
|
}
|
||||||
}
|
}
|
||||||
}
|
template = this.templatize(templateElement, index) || new Template();
|
||||||
if (widget) {
|
return function(scope, cloneConnectFn){
|
||||||
descend = false;
|
assertArg(scope, 'scope');
|
||||||
directives = false;
|
// important!!: we must call our jqLite.clone() since the jQuery one is trying to be smart
|
||||||
var parent = element.parent();
|
// and sometimes changes the structure of the DOM.
|
||||||
template.addLinkFn(widget.call(selfApi, element));
|
var element = cloneConnectFn
|
||||||
if (parent && parent[0]) {
|
? JQLitePrototype.clone.call(templateElement) // IMPORTANT!!!
|
||||||
element = jqLite(parent[0].childNodes[elementIndex]);
|
: templateElement;
|
||||||
}
|
element.data($$scope, scope);
|
||||||
}
|
scope.$element = element;
|
||||||
if (descend){
|
(cloneConnectFn||noop)(element, scope);
|
||||||
// process markup for text nodes only
|
template.link(element, scope);
|
||||||
for(var i=0, child=element[0].childNodes;
|
return element;
|
||||||
i<child.length; i++) {
|
};
|
||||||
if (isTextNode(child[i])) {
|
},
|
||||||
forEach(self.markup, function(markup){
|
|
||||||
if (i<child.length) {
|
templatize: function(element, elementIndex){
|
||||||
var textNode = jqLite(child[i]);
|
var self = this,
|
||||||
markup.call(selfApi, textNode.text(), textNode, element);
|
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));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
// Process non text child nodes
|
||||||
}
|
if (descend) {
|
||||||
|
eachNode(element, function(child, i){
|
||||||
if (directives) {
|
template.addChild(i, self.templatize(child, i));
|
||||||
// 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));
|
|
||||||
}
|
}
|
||||||
});
|
return template.empty() ? null : template;
|
||||||
}
|
}
|
||||||
// Process non text child nodes
|
};
|
||||||
if (descend) {
|
|
||||||
eachNode(element, function(child, i){
|
|
||||||
template.addChild(i, self.templatize(child, i));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
return template.empty() ? null : template;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/////////////////////////////////////////////////////////////////////
|
/////////////////////////////////////////////////////////////////////
|
||||||
var compiler = new Compiler($textMarkup, $attrMarkup, $directive, $widget);
|
var compiler = new Compiler($textMarkup, $attrMarkup, $directive, $widget);
|
||||||
return bind(compiler, compiler.compile);
|
return bind(compiler, compiler.compile);
|
||||||
}, ['$injector', '$exceptionHandler', '$textMarkup', '$attrMarkup', '$directive', '$widget']);
|
}];
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
function eachNode(element, fn){
|
function eachNode(element, fn){
|
||||||
|
|
|
||||||
|
|
@ -11,52 +11,54 @@
|
||||||
* deserialized by angular's toJson/fromJson.
|
* deserialized by angular's toJson/fromJson.
|
||||||
* @example
|
* @example
|
||||||
*/
|
*/
|
||||||
angularServiceInject('$cookieStore', function($store) {
|
function $CookieStoreProvider(){
|
||||||
|
this.$get = ['$cookies', function($cookies) {
|
||||||
|
|
||||||
return {
|
return {
|
||||||
/**
|
/**
|
||||||
* @ngdoc method
|
* @ngdoc method
|
||||||
* @name angular.service.$cookieStore#get
|
* @name angular.service.$cookieStore#get
|
||||||
* @methodOf angular.service.$cookieStore
|
* @methodOf angular.service.$cookieStore
|
||||||
*
|
*
|
||||||
* @description
|
* @description
|
||||||
* Returns the value of given cookie key
|
* Returns the value of given cookie key
|
||||||
*
|
*
|
||||||
* @param {string} key Id to use for lookup.
|
* @param {string} key Id to use for lookup.
|
||||||
* @returns {Object} Deserialized cookie value.
|
* @returns {Object} Deserialized cookie value.
|
||||||
*/
|
*/
|
||||||
get: function(key) {
|
get: function(key) {
|
||||||
return fromJson($store[key]);
|
return fromJson($cookies[key]);
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @ngdoc method
|
* @ngdoc method
|
||||||
* @name angular.service.$cookieStore#put
|
* @name angular.service.$cookieStore#put
|
||||||
* @methodOf angular.service.$cookieStore
|
* @methodOf angular.service.$cookieStore
|
||||||
*
|
*
|
||||||
* @description
|
* @description
|
||||||
* Sets a value for given cookie key
|
* Sets a value for given cookie key
|
||||||
*
|
*
|
||||||
* @param {string} key Id for the `value`.
|
* @param {string} key Id for the `value`.
|
||||||
* @param {Object} value Value to be stored.
|
* @param {Object} value Value to be stored.
|
||||||
*/
|
*/
|
||||||
put: function(key, value) {
|
put: function(key, value) {
|
||||||
$store[key] = toJson(value);
|
$cookies[key] = toJson(value);
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @ngdoc method
|
* @ngdoc method
|
||||||
* @name angular.service.$cookieStore#remove
|
* @name angular.service.$cookieStore#remove
|
||||||
* @methodOf angular.service.$cookieStore
|
* @methodOf angular.service.$cookieStore
|
||||||
*
|
*
|
||||||
* @description
|
* @description
|
||||||
* Remove given cookie
|
* Remove given cookie
|
||||||
*
|
*
|
||||||
* @param {string} key Id of the key-value pair to delete.
|
* @param {string} key Id of the key-value pair to delete.
|
||||||
*/
|
*/
|
||||||
remove: function(key) {
|
remove: function(key) {
|
||||||
delete $store[key];
|
delete $cookies[key];
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
}, ['$cookies']);
|
}];
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -13,80 +13,82 @@
|
||||||
*
|
*
|
||||||
* @example
|
* @example
|
||||||
*/
|
*/
|
||||||
angularServiceInject('$cookies', function($rootScope, $browser) {
|
function $CookiesProvider() {
|
||||||
var cookies = {},
|
this.$get = ['$rootScope', '$browser', function ($rootScope, $browser) {
|
||||||
lastCookies = {},
|
var cookies = {},
|
||||||
lastBrowserCookies,
|
lastCookies = {},
|
||||||
runEval = false;
|
lastBrowserCookies,
|
||||||
|
runEval = false;
|
||||||
|
|
||||||
//creates a poller fn that copies all cookies from the $browser to service & inits the service
|
//creates a poller fn that copies all cookies from the $browser to service & inits the service
|
||||||
$browser.addPollFn(function() {
|
$browser.addPollFn(function() {
|
||||||
var currentCookies = $browser.cookies();
|
var currentCookies = $browser.cookies();
|
||||||
if (lastBrowserCookies != currentCookies) { //relies on browser.cookies() impl
|
if (lastBrowserCookies != currentCookies) { //relies on browser.cookies() impl
|
||||||
lastBrowserCookies = currentCookies;
|
lastBrowserCookies = currentCookies;
|
||||||
copy(currentCookies, lastCookies);
|
copy(currentCookies, lastCookies);
|
||||||
copy(currentCookies, cookies);
|
copy(currentCookies, cookies);
|
||||||
if (runEval) $rootScope.$apply();
|
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);
|
|
||||||
}
|
}
|
||||||
}
|
})();
|
||||||
|
|
||||||
//update all cookies updated in $cookies
|
runEval = true;
|
||||||
for(name in cookies) {
|
|
||||||
value = cookies[name];
|
//at the end of each eval, push cookies
|
||||||
if (!isString(value)) {
|
//TODO: this should happen before the "delayed" watches fire, because if some cookies are not
|
||||||
if (isDefined(lastCookies[name])) {
|
// strings or browser refuses to store some cookies, we update the model in the push fn.
|
||||||
cookies[name] = lastCookies[name];
|
$rootScope.$watch(push);
|
||||||
} else {
|
|
||||||
delete cookies[name];
|
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
|
//update all cookies updated in $cookies
|
||||||
if (updated){
|
for(name in cookies) {
|
||||||
updated = false;
|
value = cookies[name];
|
||||||
browserCookies = $browser.cookies();
|
if (!isString(value)) {
|
||||||
|
if (isDefined(lastCookies[name])) {
|
||||||
for (name in cookies) {
|
cookies[name] = lastCookies[name];
|
||||||
if (cookies[name] !== browserCookies[name]) {
|
|
||||||
//delete or reset all cookies that the browser dropped from $cookies
|
|
||||||
if (isUndefined(browserCookies[name])) {
|
|
||||||
delete cookies[name];
|
|
||||||
} else {
|
} else {
|
||||||
cookies[name] = browserCookies[name];
|
delete cookies[name];
|
||||||
}
|
}
|
||||||
|
} else if (value !== lastCookies[name]) {
|
||||||
|
$browser.cookies(name, value);
|
||||||
updated = true;
|
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.
|
* @param {*} deferId Token returned by the `$defer` function.
|
||||||
* @returns {boolean} Returns `true` if the task hasn't executed yet and was successfuly canceled.
|
* @returns {boolean} Returns `true` if the task hasn't executed yet and was successfuly canceled.
|
||||||
*/
|
*/
|
||||||
angularServiceInject('$defer', function($rootScope, $browser) {
|
function $DeferProvider(){
|
||||||
function defer(fn, delay) {
|
this.$get = ['$rootScope', '$browser', function($rootScope, $browser) {
|
||||||
return $browser.defer(function() {
|
function defer(fn, delay) {
|
||||||
$rootScope.$apply(fn);
|
return $browser.defer(function() {
|
||||||
}, delay);
|
$rootScope.$apply(fn);
|
||||||
}
|
}, delay);
|
||||||
|
}
|
||||||
|
|
||||||
defer.cancel = function(deferId) {
|
defer.cancel = function(deferId) {
|
||||||
return $browser.defer.cancel(deferId);
|
return $browser.defer.cancel(deferId);
|
||||||
};
|
};
|
||||||
|
|
||||||
return defer;
|
return defer;
|
||||||
}, ['$rootScope', '$browser']);
|
}];
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,8 @@
|
||||||
* A {@link angular.element jQuery (lite)}-wrapped reference to the browser's `window.document`
|
* A {@link angular.element jQuery (lite)}-wrapped reference to the browser's `window.document`
|
||||||
* element.
|
* element.
|
||||||
*/
|
*/
|
||||||
angularServiceInject("$document", function(window){
|
function $DocumentProvider(){
|
||||||
return jqLite(window.document);
|
this.$get = ['$window', function(window){
|
||||||
}, ['$window']);
|
return jqLite(window.document);
|
||||||
|
}];
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -15,9 +15,10 @@
|
||||||
*
|
*
|
||||||
* @example
|
* @example
|
||||||
*/
|
*/
|
||||||
var $exceptionHandlerFactory; //reference to be used only in tests
|
function $ExceptionHandlerProvider(){
|
||||||
angularServiceInject('$exceptionHandler', $exceptionHandlerFactory = function($log){
|
this.$get = ['$log', function($log){
|
||||||
return function(e) {
|
return function(e) {
|
||||||
$log.error(e);
|
$log.error(e);
|
||||||
};
|
};
|
||||||
}, ['$log']);
|
}];
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -96,297 +96,299 @@
|
||||||
</doc:scenario>
|
</doc:scenario>
|
||||||
</doc:example>
|
</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
|
* @ngdoc method
|
||||||
* @name rootForm
|
* @name forElement
|
||||||
* @propertyOf angular.service.$formFactory
|
* @methodOf angular.service.$formFactory
|
||||||
* @description
|
* @description
|
||||||
* Static property on `$formFactory`
|
* Static method on `$formFactory` service.
|
||||||
*
|
*
|
||||||
* Each application ({@link guide/dev_guide.scopes.internals root scope}) gets a root form which
|
* Retrieve the closest form for a given element or defaults to the `root` form. Used by the
|
||||||
* is the top-level parent of all forms.
|
* {@link angular.widget.form form} element.
|
||||||
*/
|
* @param {Element} element The element where the search for form should initiate.
|
||||||
formFactory.rootForm = formFactory($rootScope);
|
*/
|
||||||
|
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) {
|
function propertiesUpdate(widget) {
|
||||||
return (parent || formFactory.rootForm).$new(FormController);
|
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 =
|
* @ngdoc property
|
||||||
!(widget.$readonly || widget.$disabled || equals(widget.$error, {})));
|
* @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
|
* @ngdoc property
|
||||||
* @name $error
|
* @name $valid
|
||||||
* @propertyOf angular.service.$formFactory
|
* @propertyOf angular.service.$formFactory
|
||||||
* @description
|
* @description
|
||||||
* Property of the form and widget instance.
|
* Property of the form and widget instance.
|
||||||
*
|
*
|
||||||
* Summary of all of the errors on the page. If a widget emits `$invalid` with `REQUIRED` key,
|
* True if all of the widgets of the form are valid.
|
||||||
* 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
|
* @ngdoc event
|
||||||
* @name $invalid
|
* @name angular.service.$formFactory#$valid
|
||||||
* @propertyOf angular.service.$formFactory
|
* @eventOf angular.service.$formFactory
|
||||||
* @description
|
* @eventType listen on form
|
||||||
* Property of the form and widget instance.
|
* @description
|
||||||
*
|
* Upon receiving the `$valid` event from the widget update the `$error`, `$valid` and `$invalid`
|
||||||
* True if any of the widgets of the form are 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
|
* @ngdoc event
|
||||||
* @name $valid
|
* @name angular.service.$formFactory#$invalid
|
||||||
* @propertyOf angular.service.$formFactory
|
* @eventOf angular.service.$formFactory
|
||||||
* @description
|
* @eventType listen on form
|
||||||
* Property of the form and widget instance.
|
* @description
|
||||||
*
|
* Upon receiving the `$invalid` event from the widget update the `$error`, `$valid` and `$invalid`
|
||||||
* True if all of the widgets of the form are valid.
|
* 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
|
* @ngdoc event
|
||||||
* @name angular.service.$formFactory#$valid
|
* @name angular.service.$formFactory#$validate
|
||||||
* @eventOf angular.service.$formFactory
|
* @eventOf angular.service.$formFactory
|
||||||
* @eventType listen on form
|
* @eventType emit on widget
|
||||||
* @description
|
* @description
|
||||||
* Upon receiving the `$valid` event from the widget update the `$error`, `$valid` and `$invalid`
|
* Emit the `$validate` event on the widget, giving a widget a chance to emit a
|
||||||
* properties of both the widget as well as the from.
|
* `$valid` / `$invalid` event base on its state. The `$validate` event is triggered when the
|
||||||
*
|
* model or the view changes.
|
||||||
* @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
|
* @ngdoc event
|
||||||
* @name angular.service.$formFactory#$invalid
|
* @name angular.service.$formFactory#$viewChange
|
||||||
* @eventOf angular.service.$formFactory
|
* @eventOf angular.service.$formFactory
|
||||||
* @eventType listen on form
|
* @eventType listen on widget
|
||||||
* @description
|
* @description
|
||||||
* Upon receiving the `$invalid` event from the widget update the `$error`, `$valid` and `$invalid`
|
* A widget is responsible for emitting this event whenever the view changes do to user interaction.
|
||||||
* properties of both the widget as well as the from.
|
* 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 {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
|
* @param {*} viewValue The new value for the view which will be assigned to `widget.$viewValue`.
|
||||||
* such as `<div ng:show="form.$error.KEY">error for key</div>`.
|
*/
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
function FormController() {
|
||||||
* @ngdoc event
|
var form = this,
|
||||||
* @name angular.service.$formFactory#$validate
|
$error = form.$error = {};
|
||||||
* @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.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
form.$on('$destroy', function(event){
|
||||||
* @ngdoc event
|
var widget = event.targetScope;
|
||||||
* @name angular.service.$formFactory#$viewChange
|
if (widget.$widgetId) {
|
||||||
* @eventOf angular.service.$formFactory
|
delete form[widget.$widgetId];
|
||||||
* @eventType listen on widget
|
}
|
||||||
* @description
|
forEach($error, removeWidget, widget);
|
||||||
* 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`.
|
|
||||||
*/
|
|
||||||
|
|
||||||
function FormController() {
|
form.$on('$valid', function(event, error){
|
||||||
var form = this,
|
var widget = event.targetScope;
|
||||||
$error = form.$error = {};
|
delete widget.$error[error];
|
||||||
|
propertiesUpdate(widget);
|
||||||
|
removeWidget($error[error], error, widget);
|
||||||
|
});
|
||||||
|
|
||||||
form.$on('$destroy', function(event){
|
form.$on('$invalid', function(event, error){
|
||||||
var widget = event.targetScope;
|
var widget = event.targetScope;
|
||||||
if (widget.$widgetId) {
|
addWidget(error, widget);
|
||||||
delete form[widget.$widgetId];
|
widget.$error[error] = true;
|
||||||
}
|
propertiesUpdate(widget);
|
||||||
forEach($error, removeWidget, widget);
|
});
|
||||||
});
|
|
||||||
|
|
||||||
form.$on('$valid', function(event, error){
|
propertiesUpdate(form);
|
||||||
var widget = event.targetScope;
|
|
||||||
delete widget.$error[error];
|
|
||||||
propertiesUpdate(widget);
|
|
||||||
removeWidget($error[error], error, widget);
|
|
||||||
});
|
|
||||||
|
|
||||||
form.$on('$invalid', function(event, error){
|
function removeWidget(queue, errorKey, widget) {
|
||||||
var widget = event.targetScope;
|
if (queue) {
|
||||||
addWidget(error, widget);
|
widget = widget || this; // so that we can be used in forEach;
|
||||||
widget.$error[error] = true;
|
for (var i = 0, length = queue.length; i < length; i++) {
|
||||||
propertiesUpdate(widget);
|
if (queue[i] === widget) {
|
||||||
});
|
queue.splice(i, 1);
|
||||||
|
if (!queue.length) {
|
||||||
propertiesUpdate(form);
|
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);
|
propertiesUpdate(form);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function addWidget(errorKey, widget) {
|
|
||||||
var queue = $error[errorKey];
|
/**
|
||||||
if (queue) {
|
* @ngdoc method
|
||||||
for (var i = 0, length = queue.length; i < length; i++) {
|
* @name $createWidget
|
||||||
if (queue[i] === widget) {
|
* @methodOf angular.service.$formFactory
|
||||||
return;
|
* @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 {
|
} 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`)
|
* * `id` – `{string}` – locale id formatted as `languageId-countryId` (e.g. `en-us`)
|
||||||
*/
|
*/
|
||||||
angularServiceInject('$locale', function() {
|
function $LocaleProvider(){
|
||||||
return {
|
this.$get = function() {
|
||||||
id: 'en-us',
|
return {
|
||||||
|
id: 'en-us',
|
||||||
|
|
||||||
NUMBER_FORMATS: {
|
NUMBER_FORMATS: {
|
||||||
DECIMAL_SEP: '.',
|
DECIMAL_SEP: '.',
|
||||||
GROUP_SEP: ',',
|
GROUP_SEP: ',',
|
||||||
PATTERNS: [
|
PATTERNS: [
|
||||||
{ // Decimal Pattern
|
{ // Decimal Pattern
|
||||||
minInt: 1,
|
minInt: 1,
|
||||||
minFrac: 0,
|
minFrac: 0,
|
||||||
maxFrac: 3,
|
maxFrac: 3,
|
||||||
posPre: '',
|
posPre: '',
|
||||||
posSuf: '',
|
posSuf: '',
|
||||||
negPre: '-',
|
negPre: '-',
|
||||||
negSuf: '',
|
negSuf: '',
|
||||||
gSize: 3,
|
gSize: 3,
|
||||||
lgSize: 3
|
lgSize: 3
|
||||||
},{ //Currency Pattern
|
},{ //Currency Pattern
|
||||||
minInt: 1,
|
minInt: 1,
|
||||||
minFrac: 2,
|
minFrac: 2,
|
||||||
maxFrac: 2,
|
maxFrac: 2,
|
||||||
posPre: '\u00A4',
|
posPre: '\u00A4',
|
||||||
posSuf: '',
|
posSuf: '',
|
||||||
negPre: '(\u00A4',
|
negPre: '(\u00A4',
|
||||||
negSuf: ')',
|
negSuf: ')',
|
||||||
gSize: 3,
|
gSize: 3,
|
||||||
lgSize: 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';
|
||||||
}
|
}
|
||||||
],
|
return 'other';
|
||||||
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';
|
};
|
||||||
}
|
|
||||||
};
|
};
|
||||||
});
|
}
|
||||||
|
|
|
||||||
|
|
@ -419,94 +419,99 @@ function locationGetterSetter(property, preprocess) {
|
||||||
*
|
*
|
||||||
* For more information see {@link guide/dev_guide.services.$location Developer Guide: Angular Services: Using $location}
|
* For more information see {@link guide/dev_guide.services.$location Developer Guide: Angular Services: Using $location}
|
||||||
*/
|
*/
|
||||||
angularServiceInject('$location', function($rootScope, $browser, $sniffer, $locationConfig, $document) {
|
function $LocationProvider(){
|
||||||
var currentUrl,
|
this.$get = ['$rootScope', '$browser', '$sniffer', '$locationConfig', '$document',
|
||||||
basePath = $browser.baseHref() || '/',
|
function( $rootScope, $browser, $sniffer, $locationConfig, $document) {
|
||||||
pathPrefix = pathPrefixFromBase(basePath),
|
var currentUrl,
|
||||||
hashPrefix = $locationConfig.hashPrefix || '',
|
basePath = $browser.baseHref() || '/',
|
||||||
initUrl = $browser.url();
|
pathPrefix = pathPrefixFromBase(basePath),
|
||||||
|
hashPrefix = $locationConfig.hashPrefix || '',
|
||||||
|
initUrl = $browser.url();
|
||||||
|
|
||||||
if ($locationConfig.html5Mode) {
|
if ($locationConfig.html5Mode) {
|
||||||
if ($sniffer.history) {
|
if ($sniffer.history) {
|
||||||
currentUrl = new LocationUrl(convertToHtml5Url(initUrl, basePath, hashPrefix), pathPrefix);
|
currentUrl = new LocationUrl(convertToHtml5Url(initUrl, basePath, hashPrefix), pathPrefix);
|
||||||
} else {
|
} else {
|
||||||
currentUrl = new LocationHashbangUrl(convertToHashbangUrl(initUrl, basePath, hashPrefix),
|
currentUrl = new LocationHashbangUrl(convertToHashbangUrl(initUrl, basePath, hashPrefix),
|
||||||
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();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var href = elm.attr('href');
|
// link rewriting
|
||||||
if (!href || isDefined(elm.attr('ng:ext-link')) || elm.attr('target')) return;
|
var u = currentUrl,
|
||||||
|
absUrlPrefix = composeProtocolHostPort(u.protocol(), u.host(), u.port()) + pathPrefix;
|
||||||
|
|
||||||
// remove same domain from full url links (IE7 always returns full hrefs)
|
$document.bind('click', function(event) {
|
||||||
href = href.replace(absUrlPrefix, '');
|
// 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 (event.ctrlKey || event.metaKey || event.which == 2) return;
|
||||||
if (href.substr(0, 4) == 'http') return;
|
|
||||||
|
|
||||||
// remove pathPrefix from absolute links
|
var elm = jqLite(event.target);
|
||||||
href = href.indexOf(pathPrefix) === 0 ? href.substr(pathPrefix.length) : href;
|
|
||||||
|
|
||||||
currentUrl.url(href);
|
// traverse the DOM up to find first A tag
|
||||||
$rootScope.$apply();
|
while (elm.length && lowercase(elm[0].nodeName) !== 'a') {
|
||||||
event.preventDefault();
|
elm = elm.parent();
|
||||||
// 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);
|
|
||||||
}
|
|
||||||
|
|
||||||
// rewrite hashbang url <> html5 url
|
var href = elm.attr('href');
|
||||||
if (currentUrl.absUrl() != initUrl) {
|
if (!href || isDefined(elm.attr('ng:ext-link')) || elm.attr('target')) return;
|
||||||
$browser.url(currentUrl.absUrl(), true);
|
|
||||||
}
|
|
||||||
|
|
||||||
// update $location when $browser url changes
|
// remove same domain from full url links (IE7 always returns full hrefs)
|
||||||
$browser.onUrlChange(function(newUrl) {
|
href = href.replace(absUrlPrefix, '');
|
||||||
if (currentUrl.absUrl() != newUrl) {
|
|
||||||
currentUrl.$$parse(newUrl);
|
|
||||||
$rootScope.$apply();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// update browser
|
// link to different domain (or base path)
|
||||||
var changeCounter = 0;
|
if (href.substr(0, 4) == 'http') return;
|
||||||
$rootScope.$watch(function() {
|
|
||||||
if ($browser.url() != currentUrl.absUrl()) {
|
// remove pathPrefix from absolute links
|
||||||
changeCounter++;
|
href = href.indexOf(pathPrefix) === 0 ? href.substr(pathPrefix.length) : href;
|
||||||
$rootScope.$evalAsync(function() {
|
|
||||||
$browser.url(currentUrl.absUrl(), currentUrl.$$replace);
|
currentUrl.url(href);
|
||||||
currentUrl.$$replace = false;
|
$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;
|
// update $location when $browser url changes
|
||||||
}, ['$rootScope', '$browser', '$sniffer', '$locationConfig', '$document']);
|
$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 changeCounter;
|
||||||
return {
|
});
|
||||||
html5Mode: false,
|
|
||||||
hashPrefix: ''
|
return currentUrl;
|
||||||
|
}];
|
||||||
|
}
|
||||||
|
|
||||||
|
//TODO(misko): refactor to service
|
||||||
|
function $LocationConfigProvider(){
|
||||||
|
this.$get = function() {
|
||||||
|
return {
|
||||||
|
html5Mode: false,
|
||||||
|
hashPrefix: ''
|
||||||
|
};
|
||||||
};
|
};
|
||||||
});
|
}
|
||||||
|
|
|
||||||
|
|
@ -34,64 +34,78 @@
|
||||||
</doc:scenario>
|
</doc:scenario>
|
||||||
</doc:example>
|
</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'),
|
|
||||||
|
|
||||||
/**
|
function $LogProvider(){
|
||||||
* @ngdoc method
|
this.$get = ['$window', function($window){
|
||||||
* @name angular.service.$log#warn
|
return {
|
||||||
* @methodOf angular.service.$log
|
/**
|
||||||
*
|
* @ngdoc method
|
||||||
* @description
|
* @name angular.service.$log#log
|
||||||
* Write a warning message
|
* @methodOf angular.service.$log
|
||||||
*/
|
*
|
||||||
warn: consoleLog('warn'),
|
* @description
|
||||||
|
* Write a log message
|
||||||
|
*/
|
||||||
|
log: consoleLog('log'),
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @ngdoc method
|
* @ngdoc method
|
||||||
* @name angular.service.$log#info
|
* @name angular.service.$log#warn
|
||||||
* @methodOf angular.service.$log
|
* @methodOf angular.service.$log
|
||||||
*
|
*
|
||||||
* @description
|
* @description
|
||||||
* Write an information message
|
* Write a warning message
|
||||||
*/
|
*/
|
||||||
info: consoleLog('info'),
|
warn: consoleLog('warn'),
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @ngdoc method
|
* @ngdoc method
|
||||||
* @name angular.service.$log#error
|
* @name angular.service.$log#info
|
||||||
* @methodOf angular.service.$log
|
* @methodOf angular.service.$log
|
||||||
*
|
*
|
||||||
* @description
|
* @description
|
||||||
* Write an error message
|
* Write an information message
|
||||||
*/
|
*/
|
||||||
error: consoleLog('error')
|
info: consoleLog('info'),
|
||||||
};
|
|
||||||
|
|
||||||
function consoleLog(type) {
|
/**
|
||||||
var console = $window.console || {};
|
* @ngdoc method
|
||||||
var logFn = console[type] || console.log || noop;
|
* @name angular.service.$log#error
|
||||||
if (logFn.apply) {
|
* @methodOf angular.service.$log
|
||||||
return function() {
|
*
|
||||||
var args = [];
|
* @description
|
||||||
forEach(arguments, function(arg){
|
* Write an error message
|
||||||
args.push(formatError(arg));
|
*/
|
||||||
});
|
error: consoleLog('error')
|
||||||
return logFn.apply(console, args);
|
};
|
||||||
};
|
|
||||||
} else {
|
function formatError(arg) {
|
||||||
// we are IE, in which case there is nothing we can do
|
if (arg instanceof Error) {
|
||||||
return logFn;
|
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:scenario>
|
||||||
</doc:example>
|
</doc:example>
|
||||||
*/
|
*/
|
||||||
angularServiceInject('$resource', function($xhr){
|
function $ResourceProvider() {
|
||||||
var resource = new ResourceFactory($xhr);
|
this.$get = ['$xhr.cache', function($xhr){
|
||||||
return bind(resource, resource.route);
|
var resource = new ResourceFactory($xhr);
|
||||||
}, ['$xhr.cache']);
|
return bind(resource, resource.route);
|
||||||
|
}];
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -62,264 +62,267 @@
|
||||||
</doc:scenario>
|
</doc:scenario>
|
||||||
</doc:example>
|
</doc:example>
|
||||||
*/
|
*/
|
||||||
angularServiceInject('$route', function($rootScope, $location, $routeParams) {
|
function $RouteProvider(){
|
||||||
/**
|
this.$get = ['$rootScope', '$location', '$routeParams',
|
||||||
* @ngdoc event
|
function( $rootScope, $location, $routeParams) {
|
||||||
* @name angular.service.$route#$beforeRouteChange
|
/**
|
||||||
* @eventOf angular.service.$route
|
* @ngdoc event
|
||||||
* @eventType broadcast on root scope
|
* @name angular.service.$route#$beforeRouteChange
|
||||||
* @description
|
* @eventOf angular.service.$route
|
||||||
* Broadcasted before a route change.
|
* @eventType broadcast on root scope
|
||||||
*
|
* @description
|
||||||
* @param {Route} next Future route information.
|
* Broadcasted before a route change.
|
||||||
* @param {Route} current Current route information.
|
*
|
||||||
*
|
* @param {Route} next Future route information.
|
||||||
* The `Route` object extends the route definition with the following properties.
|
* @param {Route} current Current route information.
|
||||||
*
|
*
|
||||||
* * `scope` - The instance of the route controller.
|
* The `Route` object extends the route definition with the following properties.
|
||||||
* * `params` - The current {@link angular.service.$routeParams params}.
|
*
|
||||||
*
|
* * `scope` - The instance of the route controller.
|
||||||
*/
|
* * `params` - The current {@link angular.service.$routeParams params}.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @ngdoc event
|
* @ngdoc event
|
||||||
* @name angular.service.$route#$afterRouteChange
|
* @name angular.service.$route#$afterRouteChange
|
||||||
* @eventOf angular.service.$route
|
* @eventOf angular.service.$route
|
||||||
* @eventType broadcast on root scope
|
* @eventType broadcast on root scope
|
||||||
* @description
|
* @description
|
||||||
* Broadcasted after a route change.
|
* Broadcasted after a route change.
|
||||||
*
|
*
|
||||||
* @param {Route} current Current route information.
|
* @param {Route} current Current route information.
|
||||||
* @param {Route} previous Previous route information.
|
* @param {Route} previous Previous route information.
|
||||||
*
|
*
|
||||||
* The `Route` object extends the route definition with the following properties.
|
* The `Route` object extends the route definition with the following properties.
|
||||||
*
|
*
|
||||||
* * `scope` - The instance of the route controller.
|
* * `scope` - The instance of the route controller.
|
||||||
* * `params` - The current {@link angular.service.$routeParams params}.
|
* * `params` - The current {@link angular.service.$routeParams params}.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @ngdoc event
|
* @ngdoc event
|
||||||
* @name angular.service.$route#$routeUpdate
|
* @name angular.service.$route#$routeUpdate
|
||||||
* @eventOf angular.service.$route
|
* @eventOf angular.service.$route
|
||||||
* @eventType emit on the current route scope
|
* @eventType emit on the current route scope
|
||||||
* @description
|
* @description
|
||||||
*
|
*
|
||||||
* The `reloadOnSearch` property has been set to false, and we are reusing the same
|
* The `reloadOnSearch` property has been set to false, and we are reusing the same
|
||||||
* instance of the Controller.
|
* instance of the Controller.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
var routes = {},
|
var routes = {},
|
||||||
matcher = switchRouteMatcher,
|
matcher = switchRouteMatcher,
|
||||||
parentScope = $rootScope,
|
parentScope = $rootScope,
|
||||||
dirty = 0,
|
dirty = 0,
|
||||||
forceReload = false,
|
forceReload = false,
|
||||||
$route = {
|
$route = {
|
||||||
routes: routes,
|
routes: routes,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @ngdoc method
|
* @ngdoc method
|
||||||
* @name angular.service.$route#parent
|
* @name angular.service.$route#parent
|
||||||
* @methodOf angular.service.$route
|
* @methodOf angular.service.$route
|
||||||
*
|
*
|
||||||
* @param {Scope} [scope=rootScope] Scope to be used as parent for newly created
|
* @param {Scope} [scope=rootScope] Scope to be used as parent for newly created
|
||||||
* `$route.current.scope` scopes.
|
* `$route.current.scope` scopes.
|
||||||
*
|
*
|
||||||
* @description
|
* @description
|
||||||
* Sets a scope to be used as the parent scope for scopes created on route change. If not
|
* Sets a scope to be used as the parent scope for scopes created on route change. If not
|
||||||
* set, defaults to the root scope.
|
* set, defaults to the root scope.
|
||||||
*/
|
*/
|
||||||
parent: function(scope) {
|
parent: function(scope) {
|
||||||
if (scope) parentScope = scope;
|
if (scope) parentScope = scope;
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @ngdoc method
|
* @ngdoc method
|
||||||
* @name angular.service.$route#when
|
* @name angular.service.$route#when
|
||||||
* @methodOf angular.service.$route
|
* @methodOf angular.service.$route
|
||||||
*
|
*
|
||||||
* @param {string} path Route path (matched against `$location.hash`)
|
* @param {string} path Route path (matched against `$location.hash`)
|
||||||
* @param {Object} route Mapping information to be assigned to `$route.current` on route
|
* @param {Object} route Mapping information to be assigned to `$route.current` on route
|
||||||
* match.
|
* match.
|
||||||
*
|
*
|
||||||
* Object properties:
|
* Object properties:
|
||||||
*
|
*
|
||||||
* - `controller` – `{function()=}` – Controller fn that should be associated with newly
|
* - `controller` – `{function()=}` – Controller fn that should be associated with newly
|
||||||
* created scope.
|
* created scope.
|
||||||
* - `template` – `{string=}` – path to an html template that should be used by
|
* - `template` – `{string=}` – path to an html template that should be used by
|
||||||
* {@link angular.widget.ng:view ng:view} or
|
* {@link angular.widget.ng:view ng:view} or
|
||||||
* {@link angular.widget.ng:include ng:include} widgets.
|
* {@link angular.widget.ng:include ng:include} widgets.
|
||||||
* - `redirectTo` – {(string|function())=} – value to update
|
* - `redirectTo` – {(string|function())=} – value to update
|
||||||
* {@link angular.service.$location $location} path with and trigger route redirection.
|
* {@link angular.service.$location $location} path with and trigger route redirection.
|
||||||
*
|
*
|
||||||
* If `redirectTo` is a function, it will be called with the following parameters:
|
* If `redirectTo` is a function, it will be called with the following parameters:
|
||||||
*
|
*
|
||||||
* - `{Object.<string>}` - route parameters extracted from the current
|
* - `{Object.<string>}` - route parameters extracted from the current
|
||||||
* `$location.path()` by applying the current route template.
|
* `$location.path()` by applying the current route template.
|
||||||
* - `{string}` - current `$location.path()`
|
* - `{string}` - current `$location.path()`
|
||||||
* - `{Object}` - current `$location.search()`
|
* - `{Object}` - current `$location.search()`
|
||||||
*
|
*
|
||||||
* The custom `redirectTo` function is expected to return a string which will be used
|
* The custom `redirectTo` function is expected to return a string which will be used
|
||||||
* to update `$location.path()` and `$location.search()`.
|
* to update `$location.path()` and `$location.search()`.
|
||||||
*
|
*
|
||||||
* - `[reloadOnSearch=true]` - {boolean=} - reload route when only $location.search()
|
* - `[reloadOnSearch=true]` - {boolean=} - reload route when only $location.search()
|
||||||
* changes.
|
* changes.
|
||||||
*
|
*
|
||||||
* If the option is set to false and url in the browser changes, then
|
* 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
|
* $routeUpdate event is emited on the current route scope. You can use this event to
|
||||||
* react to {@link angular.service.$routeParams} changes:
|
* react to {@link angular.service.$routeParams} changes:
|
||||||
*
|
*
|
||||||
* function MyCtrl($route, $routeParams) {
|
* function MyCtrl($route, $routeParams) {
|
||||||
* this.$on('$routeUpdate', function() {
|
* this.$on('$routeUpdate', function() {
|
||||||
* // do stuff with $routeParams
|
* // do stuff with $routeParams
|
||||||
* });
|
* });
|
||||||
* }
|
* }
|
||||||
*
|
*
|
||||||
* @returns {Object} route object
|
* @returns {Object} route object
|
||||||
*
|
*
|
||||||
* @description
|
* @description
|
||||||
* Adds a new route definition to the `$route` service.
|
* Adds a new route definition to the `$route` service.
|
||||||
*/
|
*/
|
||||||
when: function(path, route) {
|
when: function(path, route) {
|
||||||
var routeDef = routes[path];
|
var routeDef = routes[path];
|
||||||
if (!routeDef) routeDef = routes[path] = {reloadOnSearch: true};
|
if (!routeDef) routeDef = routes[path] = {reloadOnSearch: true};
|
||||||
if (route) extend(routeDef, route); // TODO(im): what the heck? merge two route definitions?
|
if (route) extend(routeDef, route); // TODO(im): what the heck? merge two route definitions?
|
||||||
dirty++;
|
dirty++;
|
||||||
return routeDef;
|
return routeDef;
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @ngdoc method
|
* @ngdoc method
|
||||||
* @name angular.service.$route#otherwise
|
* @name angular.service.$route#otherwise
|
||||||
* @methodOf angular.service.$route
|
* @methodOf angular.service.$route
|
||||||
*
|
*
|
||||||
* @description
|
* @description
|
||||||
* Sets route definition that will be used on route change when no other route definition
|
* Sets route definition that will be used on route change when no other route definition
|
||||||
* is matched.
|
* is matched.
|
||||||
*
|
*
|
||||||
* @param {Object} params Mapping information to be assigned to `$route.current`.
|
* @param {Object} params Mapping information to be assigned to `$route.current`.
|
||||||
*/
|
*/
|
||||||
otherwise: function(params) {
|
otherwise: function(params) {
|
||||||
$route.when(null, params);
|
$route.when(null, params);
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @ngdoc method
|
* @ngdoc method
|
||||||
* @name angular.service.$route#reload
|
* @name angular.service.$route#reload
|
||||||
* @methodOf angular.service.$route
|
* @methodOf angular.service.$route
|
||||||
*
|
*
|
||||||
* @description
|
* @description
|
||||||
* Causes `$route` service to reload (and recreate the `$route.current` scope) upon the next
|
* 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.
|
* eval even if {@link angular.service.$location $location} hasn't changed.
|
||||||
*/
|
*/
|
||||||
reload: function() {
|
reload: function() {
|
||||||
dirty++;
|
dirty++;
|
||||||
forceReload = true;
|
forceReload = true;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
$rootScope.$watch(function() { return dirty + $location.url(); }, updateRoute);
|
$rootScope.$watch(function() { return dirty + $location.url(); }, updateRoute);
|
||||||
|
|
||||||
return $route;
|
return $route;
|
||||||
|
|
||||||
/////////////////////////////////////////////////////
|
/////////////////////////////////////////////////////
|
||||||
|
|
||||||
function switchRouteMatcher(on, when) {
|
function switchRouteMatcher(on, when) {
|
||||||
// TODO(i): this code is convoluted and inefficient, we should construct the route matching
|
// TODO(i): this code is convoluted and inefficient, we should construct the route matching
|
||||||
// regex only once and then reuse it
|
// regex only once and then reuse it
|
||||||
var regex = '^' + when.replace(/([\.\\\(\)\^\$])/g, "\\$1") + '$',
|
var regex = '^' + when.replace(/([\.\\\(\)\^\$])/g, "\\$1") + '$',
|
||||||
params = [],
|
params = [],
|
||||||
dst = {};
|
dst = {};
|
||||||
forEach(when.split(/\W/), function(param) {
|
forEach(when.split(/\W/), function(param) {
|
||||||
if (param) {
|
if (param) {
|
||||||
var paramRegExp = new RegExp(":" + param + "([\\W])");
|
var paramRegExp = new RegExp(":" + param + "([\\W])");
|
||||||
if (regex.match(paramRegExp)) {
|
if (regex.match(paramRegExp)) {
|
||||||
regex = regex.replace(paramRegExp, "([^\\/]*)$1");
|
regex = regex.replace(paramRegExp, "([^\\/]*)$1");
|
||||||
params.push(param);
|
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();
|
|
||||||
}
|
}
|
||||||
} 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;
|
||||||
|
|
||||||
/**
|
if (next && last && next.$route === last.$route
|
||||||
* @returns the current active route, by matching it against the URL
|
&& equals(next.pathParams, last.pathParams) && !next.reloadOnSearch && !forceReload) {
|
||||||
*/
|
$route.current = next;
|
||||||
function parseRoute() {
|
copy(next.params, $routeParams);
|
||||||
// Match a route
|
last.scope && last.scope.$emit('$routeUpdate');
|
||||||
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 {
|
} else {
|
||||||
var segmentMatch = segment.match(/(\w+)(.*)/);
|
forceReload = false;
|
||||||
var key = segmentMatch[1];
|
$rootScope.$broadcast('$beforeRouteChange', next, last);
|
||||||
result.push(params[key]);
|
last && last.scope && last.scope.$destroy();
|
||||||
result.push(segmentMatch[2] || '');
|
$route.current = next;
|
||||||
delete params[key];
|
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'}
|
* $routeParams ==> {chapterId:1, sectionId:2, search:'moby'}
|
||||||
* </pre>
|
* </pre>
|
||||||
*/
|
*/
|
||||||
angularService('$routeParams', function() {
|
function $RouteParamsProvider() {
|
||||||
return {};
|
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
|
* @description
|
||||||
* This is very simple implementation of testing browser's features.
|
* This is very simple implementation of testing browser's features.
|
||||||
*/
|
*/
|
||||||
angularServiceInject('$sniffer', function($window) {
|
function $SnifferProvider(){
|
||||||
if ($window.Modernizr) return $window.Modernizr;
|
this.$get = ['$window', function($window){
|
||||||
|
if ($window.Modernizr) return $window.Modernizr;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
history: !!($window.history && $window.history.pushState),
|
history: !!($window.history && $window.history.pushState),
|
||||||
hashchange: 'onhashchange' in $window &&
|
hashchange: 'onhashchange' in $window &&
|
||||||
// IE8 compatible mode lies
|
// IE8 compatible mode lies
|
||||||
(!$window.document.documentMode || $window.document.documentMode > 7)
|
(!$window.document.documentMode || $window.document.documentMode > 7)
|
||||||
};
|
};
|
||||||
}, ['$window']);
|
}];
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -23,4 +23,6 @@
|
||||||
</doc:scenario>
|
</doc:scenario>
|
||||||
</doc:example>
|
</doc:example>
|
||||||
*/
|
*/
|
||||||
angularServiceInject("$window", bind(window, identity, window));
|
function $WindowProvider(){
|
||||||
|
this.$get = valueFn(window);
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -11,76 +11,79 @@
|
||||||
*
|
*
|
||||||
* @example
|
* @example
|
||||||
*/
|
*/
|
||||||
angularServiceInject('$xhr.bulk', function($rootScope, $xhr, $error, $log){
|
function $XhrBulkProvider() {
|
||||||
var requests = [];
|
this.$get = ['$rootScope', '$xhr', '$xhr.error', '$log',
|
||||||
function bulkXHR(method, url, post, success, error) {
|
function( $rootScope, $xhr, $error, $log) {
|
||||||
if (isFunction(post)) {
|
var requests = [];
|
||||||
error = success;
|
function bulkXHR(method, url, post, success, error) {
|
||||||
success = post;
|
if (isFunction(post)) {
|
||||||
post = null;
|
error = success;
|
||||||
}
|
success = post;
|
||||||
var currentQueue;
|
post = null;
|
||||||
forEach(bulkXHR.urls, function(queue){
|
|
||||||
if (isFunction(queue.match) ? queue.match(url) : queue.match.exec(url)) {
|
|
||||||
currentQueue = queue;
|
|
||||||
}
|
}
|
||||||
});
|
var currentQueue;
|
||||||
if (currentQueue) {
|
forEach(bulkXHR.urls, function(queue){
|
||||||
if (!currentQueue.requests) currentQueue.requests = [];
|
if (isFunction(queue.match) ? queue.match(url) : queue.match.exec(url)) {
|
||||||
var request = {
|
currentQueue = queue;
|
||||||
method: method,
|
}
|
||||||
url: url,
|
});
|
||||||
data: post,
|
if (currentQueue) {
|
||||||
success: success};
|
if (!currentQueue.requests) currentQueue.requests = [];
|
||||||
if (error) request.error = error;
|
var request = {
|
||||||
currentQueue.requests.push(request);
|
method: method,
|
||||||
} else {
|
url: url,
|
||||||
$xhr(method, url, post, success, error);
|
data: post,
|
||||||
}
|
success: success};
|
||||||
}
|
if (error) request.error = error;
|
||||||
bulkXHR.urls = {};
|
currentQueue.requests.push(request);
|
||||||
bulkXHR.flush = function(success, errorback) {
|
} else {
|
||||||
assertArgFn(success = success || noop, 0);
|
$xhr(method, url, post, success, error);
|
||||||
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();
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
};
|
bulkXHR.urls = {};
|
||||||
$rootScope.$watch(function() { bulkXHR.flush(); });
|
bulkXHR.flush = function(success, errorback) {
|
||||||
return bulkXHR;
|
assertArgFn(success = success || noop, 0);
|
||||||
}, ['$rootScope', '$xhr', '$xhr.error', '$log']);
|
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.
|
* 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.
|
* @param {boolean=} [sync=false] in case of cache hit execute `success` synchronously.
|
||||||
*/
|
*/
|
||||||
angularServiceInject('$xhr.cache', function($xhr, $defer, $error, $log) {
|
function $XhrCacheProvider() {
|
||||||
var inflight = {};
|
this.$get = ['$xhr.bulk', '$defer', '$xhr.error', '$log',
|
||||||
function cache(method, url, post, success, error, verifyCache, sync) {
|
function($xhr, $defer, $error, $log) {
|
||||||
if (isFunction(post)) {
|
var inflight = {};
|
||||||
if (!isFunction(success)) {
|
function cache(method, url, post, success, error, verifyCache, sync) {
|
||||||
verifyCache = success;
|
if (isFunction(post)) {
|
||||||
sync = error;
|
if (!isFunction(success)) {
|
||||||
error = null;
|
verifyCache = success;
|
||||||
} else {
|
sync = error;
|
||||||
|
error = null;
|
||||||
|
} else {
|
||||||
|
sync = verifyCache;
|
||||||
|
verifyCache = error;
|
||||||
|
error = success;
|
||||||
|
}
|
||||||
|
success = post;
|
||||||
|
post = null;
|
||||||
|
} else if (!isFunction(error)) {
|
||||||
sync = verifyCache;
|
sync = verifyCache;
|
||||||
verifyCache = error;
|
verifyCache = error;
|
||||||
error = success;
|
error = null;
|
||||||
}
|
}
|
||||||
success = post;
|
|
||||||
post = null;
|
|
||||||
} else if (!isFunction(error)) {
|
|
||||||
sync = verifyCache;
|
|
||||||
verifyCache = error;
|
|
||||||
error = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (method == 'GET') {
|
if (method == 'GET') {
|
||||||
var data, dataCached;
|
var data, dataCached;
|
||||||
if ((dataCached = cache.data[url])) {
|
if ((dataCached = cache.data[url])) {
|
||||||
|
|
||||||
if (sync) {
|
if (sync) {
|
||||||
success(200, copy(dataCached.value));
|
success(200, copy(dataCached.value));
|
||||||
} else {
|
} else {
|
||||||
$defer(function() { success(200, copy(dataCached.value)); });
|
$defer(function() { success(200, copy(dataCached.value)); });
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!verifyCache)
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!verifyCache)
|
if ((data = inflight[url])) {
|
||||||
return;
|
data.successes.push(success);
|
||||||
}
|
data.errors.push(error);
|
||||||
|
} else {
|
||||||
if ((data = inflight[url])) {
|
inflight[url] = {successes: [success], errors: [error]};
|
||||||
data.successes.push(success);
|
cache.delegate(method, url, post,
|
||||||
data.errors.push(error);
|
function(status, response) {
|
||||||
} else {
|
if (status == 200)
|
||||||
inflight[url] = {successes: [success], errors: [error]};
|
cache.data[url] = {value: response};
|
||||||
cache.delegate(method, url, post,
|
var successes = inflight[url].successes;
|
||||||
function(status, response) {
|
delete inflight[url];
|
||||||
if (status == 200)
|
forEach(successes, function(success) {
|
||||||
cache.data[url] = {value: response};
|
try {
|
||||||
var successes = inflight[url].successes;
|
(success||noop)(status, copy(response));
|
||||||
delete inflight[url];
|
} catch(e) {
|
||||||
forEach(successes, function(success) {
|
$log.error(e);
|
||||||
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});
|
|
||||||
}
|
}
|
||||||
} catch(e) {
|
});
|
||||||
$log.error(e);
|
},
|
||||||
}
|
function(status, response) {
|
||||||
});
|
var errors = inflight[url].errors,
|
||||||
});
|
successes = inflight[url].successes;
|
||||||
}
|
delete inflight[url];
|
||||||
|
|
||||||
} else {
|
forEach(errors, function(error, i) {
|
||||||
cache.data = {};
|
try {
|
||||||
cache.delegate(method, url, post, success, error);
|
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.data = {};
|
cache.delegate = $xhr;
|
||||||
cache.delegate = $xhr;
|
return cache;
|
||||||
return cache;
|
}];
|
||||||
}, ['$xhr.bulk', '$defer', '$xhr.error', '$log']);
|
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -35,8 +35,10 @@
|
||||||
</doc:source>
|
</doc:source>
|
||||||
</doc:example>
|
</doc:example>
|
||||||
*/
|
*/
|
||||||
angularServiceInject('$xhr.error', function($log){
|
function $XhrErrorProvider() {
|
||||||
return function(request, response){
|
this.$get = ['$log', function($log) {
|
||||||
$log.error('ERROR: XHR: ' + request.url, request, response);
|
return function(request, response){
|
||||||
};
|
$log.error('ERROR: XHR: ' + request.url, request, response);
|
||||||
}, ['$log']);
|
};
|
||||||
|
}];
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -171,58 +171,61 @@
|
||||||
</doc:scenario>
|
</doc:scenario>
|
||||||
</doc:example>
|
</doc:example>
|
||||||
*/
|
*/
|
||||||
angularServiceInject('$xhr', function($rootScope, $browser, $error, $log){
|
function $XhrProvider() {
|
||||||
var xhrHeaderDefaults = {
|
this.$get = ['$rootScope', '$browser', '$xhr.error', '$log',
|
||||||
common: {
|
function( $rootScope, $browser, $error, $log){
|
||||||
"Accept": "application/json, text/plain, */*",
|
var xhrHeaderDefaults = {
|
||||||
"X-Requested-With": "XMLHttpRequest"
|
common: {
|
||||||
},
|
"Accept": "application/json, text/plain, */*",
|
||||||
post: {'Content-Type': 'application/x-www-form-urlencoded'},
|
"X-Requested-With": "XMLHttpRequest"
|
||||||
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
|
post: {'Content-Type': 'application/x-www-form-urlencoded'},
|
||||||
put: {}, // it also means that if we add a header for these methods in the future, it
|
get: {}, // all these empty properties are needed so that client apps can just do:
|
||||||
'delete': {}, // won't be easily silently lost due to an object assignment.
|
head: {}, // $xhr.defaults.headers.head.foo="bar" without having to create head object
|
||||||
patch: {}
|
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) {
|
function xhr(method, url, post, success, error) {
|
||||||
if (isFunction(post)) {
|
if (isFunction(post)) {
|
||||||
error = success;
|
error = success;
|
||||||
success = post;
|
success = post;
|
||||||
post = null;
|
post = null;
|
||||||
}
|
}
|
||||||
if (post && isObject(post)) {
|
if (post && isObject(post)) {
|
||||||
post = toJson(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);
|
|
||||||
}
|
}
|
||||||
}, 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;
|
xhr.defaults = {headers: xhrHeaderDefaults};
|
||||||
}, ['$rootScope', '$browser', '$xhr.error', '$log']);
|
|
||||||
|
return xhr;
|
||||||
|
}];
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -382,25 +382,6 @@ describe('angular', function() {
|
||||||
expect(fake).toEqual('new');
|
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() {
|
it('should inject dependencies specified by $inject and ignore function argument name', function() {
|
||||||
expect(angular.injector(function($provide){
|
expect(angular.injector(function($provide){
|
||||||
$provide.factory('svc1', function() { return 'svc1'; });
|
$provide.factory('svc1', function() { return 'svc1'; });
|
||||||
|
|
|
||||||
|
|
@ -222,8 +222,8 @@ describe('Binder', function() {
|
||||||
}));
|
}));
|
||||||
|
|
||||||
it('IfTextBindingThrowsErrorDecorateTheSpan', inject(
|
it('IfTextBindingThrowsErrorDecorateTheSpan', inject(
|
||||||
function($provide){
|
function($exceptionHandlerProvider){
|
||||||
$provide.factory('$exceptionHandler', $exceptionHandlerMockFactory);
|
$exceptionHandlerProvider.mode('log');
|
||||||
},
|
},
|
||||||
function($rootScope, $exceptionHandler, $compile) {
|
function($rootScope, $exceptionHandler, $compile) {
|
||||||
$compile('<div>{{error.throw()}}</div>', null, true)($rootScope);
|
$compile('<div>{{error.throw()}}</div>', null, true)($rootScope);
|
||||||
|
|
@ -245,8 +245,8 @@ describe('Binder', function() {
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
it('IfAttrBindingThrowsErrorDecorateTheAttribute', inject(function($provide){
|
it('IfAttrBindingThrowsErrorDecorateTheAttribute', inject(function($exceptionHandlerProvider){
|
||||||
$provide.factory('$exceptionHandler', $exceptionHandlerMockFactory);
|
$exceptionHandlerProvider.mode('log');
|
||||||
}, function($rootScope, $exceptionHandler, $compile) {
|
}, function($rootScope, $exceptionHandler, $compile) {
|
||||||
$compile('<div attr="before {{error.throw()}} after"></div>', null, true)($rootScope);
|
$compile('<div attr="before {{error.throw()}} after"></div>', null, true)($rootScope);
|
||||||
var errorLogs = $exceptionHandler.errors;
|
var errorLogs = $exceptionHandler.errors;
|
||||||
|
|
@ -387,8 +387,8 @@ describe('Binder', function() {
|
||||||
}));
|
}));
|
||||||
|
|
||||||
it('ActionOnAHrefThrowsError', inject(
|
it('ActionOnAHrefThrowsError', inject(
|
||||||
function($provide){
|
function($exceptionHandlerProvider){
|
||||||
$provide.factory('$exceptionHandler', $exceptionHandlerMockFactory);
|
$exceptionHandlerProvider.mode('log');
|
||||||
},
|
},
|
||||||
function($rootScope, $exceptionHandler, $compile) {
|
function($rootScope, $exceptionHandler, $compile) {
|
||||||
var input = $compile('<a ng:click="action()">Add Phone</a>')($rootScope);
|
var input = $compile('<a ng:click="action()">Add Phone</a>')($rootScope);
|
||||||
|
|
@ -471,8 +471,8 @@ describe('Binder', function() {
|
||||||
}));
|
}));
|
||||||
|
|
||||||
it('ItShouldDisplayErrorWhenActionIsSyntacticlyIncorrect', inject(
|
it('ItShouldDisplayErrorWhenActionIsSyntacticlyIncorrect', inject(
|
||||||
function($provide){
|
function($exceptionHandlerProvider){
|
||||||
$provide.factory('$exceptionHandler', $exceptionHandlerMockFactory);
|
$exceptionHandlerProvider.mode('log');
|
||||||
},
|
},
|
||||||
function($rootScope, $exceptionHandler, $log, $compile) {
|
function($rootScope, $exceptionHandler, $log, $compile) {
|
||||||
var element = $compile(
|
var element = $compile(
|
||||||
|
|
|
||||||
|
|
@ -200,10 +200,10 @@ describe('filter', function() {
|
||||||
|
|
||||||
describe('date', function() {
|
describe('date', function() {
|
||||||
|
|
||||||
var morning = new TzDate(+5, '2010-09-03T12:05:08.000Z'); //7am
|
var morning = new angular.mock.TzDate(+5, '2010-09-03T12:05:08.000Z'); //7am
|
||||||
var noon = new TzDate(+5, '2010-09-03T17:05:08.000Z'); //12pm
|
var noon = new angular.mock.TzDate(+5, '2010-09-03T17:05:08.000Z'); //12pm
|
||||||
var midnight = new TzDate(+5, '2010-09-03T05:05:08.000Z'); //12am
|
var midnight = new angular.mock.TzDate(+5, '2010-09-03T05:05:08.000Z'); //12am
|
||||||
var earlyDate = new TzDate(+5, '0001-09-03T05:05:08.000Z');
|
var earlyDate = new angular.mock.TzDate(+5, '0001-09-03T05:05:08.000Z');
|
||||||
|
|
||||||
var context, date;
|
var context, date;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -239,6 +239,12 @@ describe('injector', function() {
|
||||||
expect($injector('a')).toEqual('abc');
|
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('$provide', function() {
|
||||||
describe('value', 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() {
|
it('should take millis as constructor argument', function() {
|
||||||
expect(new TzDate(0, 0).getTime()).toBe(0);
|
expect(new angular.mock.TzDate(0, 0).getTime()).toBe(0);
|
||||||
expect(new TzDate(0, 1283555108000).getTime()).toBe(1283555108000);
|
expect(new angular.mock.TzDate(0, 1283555108000).getTime()).toBe(1283555108000);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should take dateString as constructor argument', function() {
|
it('should take dateString as constructor argument', function() {
|
||||||
expect(new TzDate(0, '1970-01-01T00:00:00.000Z').getTime()).toBe(0);
|
expect(new angular.mock.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, '2010-09-03T23:05:08.023Z').getTime()).toBe(1283555108023);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
it('should fake getLocalDateString method', function() {
|
it('should fake getLocalDateString method', function() {
|
||||||
//0 in -3h
|
//0 in -3h
|
||||||
var t0 = new TzDate(-3, 0);
|
var t0 = new angular.mock.TzDate(-3, 0);
|
||||||
expect(t0.toLocaleDateString()).toMatch('1970');
|
expect(t0.toLocaleDateString()).toMatch('1970');
|
||||||
|
|
||||||
//0 in +0h
|
//0 in +0h
|
||||||
var t1 = new TzDate(0, 0);
|
var t1 = new angular.mock.TzDate(0, 0);
|
||||||
expect(t1.toLocaleDateString()).toMatch('1970');
|
expect(t1.toLocaleDateString()).toMatch('1970');
|
||||||
|
|
||||||
//0 in +3h
|
//0 in +3h
|
||||||
var t2 = new TzDate(3, 0);
|
var t2 = new angular.mock.TzDate(3, 0);
|
||||||
expect(t2.toLocaleDateString()).toMatch('1969');
|
expect(t2.toLocaleDateString()).toMatch('1969');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
it('should fake getHours method', function() {
|
it('should fake getHours method', function() {
|
||||||
//0 in -3h
|
//0 in -3h
|
||||||
var t0 = new TzDate(-3, 0);
|
var t0 = new angular.mock.TzDate(-3, 0);
|
||||||
expect(t0.getHours()).toBe(3);
|
expect(t0.getHours()).toBe(3);
|
||||||
|
|
||||||
//0 in +0h
|
//0 in +0h
|
||||||
var t1 = new TzDate(0, 0);
|
var t1 = new angular.mock.TzDate(0, 0);
|
||||||
expect(t1.getHours()).toBe(0);
|
expect(t1.getHours()).toBe(0);
|
||||||
|
|
||||||
//0 in +3h
|
//0 in +3h
|
||||||
var t2 = new TzDate(3, 0);
|
var t2 = new angular.mock.TzDate(3, 0);
|
||||||
expect(t2.getHours()).toMatch(21);
|
expect(t2.getHours()).toMatch(21);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
it('should fake getMinutes method', function() {
|
it('should fake getMinutes method', function() {
|
||||||
//0:15 in -3h
|
//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);
|
expect(t0.getMinutes()).toBe(15);
|
||||||
|
|
||||||
//0:15 in -3.25h
|
//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);
|
expect(t0a.getMinutes()).toBe(30);
|
||||||
|
|
||||||
//0 in +0h
|
//0 in +0h
|
||||||
var t1 = new TzDate(0, minutes(0));
|
var t1 = new angular.mock.TzDate(0, minutes(0));
|
||||||
expect(t1.getMinutes()).toBe(0);
|
expect(t1.getMinutes()).toBe(0);
|
||||||
|
|
||||||
//0:15 in +0h
|
//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);
|
expect(t1a.getMinutes()).toBe(15);
|
||||||
|
|
||||||
//0:15 in +3h
|
//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);
|
expect(t2.getMinutes()).toMatch(15);
|
||||||
|
|
||||||
//0:15 in +3.25h
|
//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);
|
expect(t2a.getMinutes()).toMatch(0);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
it('should fake getSeconds method', function() {
|
it('should fake getSeconds method', function() {
|
||||||
//0 in -3h
|
//0 in -3h
|
||||||
var t0 = new TzDate(-3, 0);
|
var t0 = new angular.mock.TzDate(-3, 0);
|
||||||
expect(t0.getSeconds()).toBe(0);
|
expect(t0.getSeconds()).toBe(0);
|
||||||
|
|
||||||
//0 in +0h
|
//0 in +0h
|
||||||
var t1 = new TzDate(0, 0);
|
var t1 = new angular.mock.TzDate(0, 0);
|
||||||
expect(t1.getSeconds()).toBe(0);
|
expect(t1.getSeconds()).toBe(0);
|
||||||
|
|
||||||
//0 in +3h
|
//0 in +3h
|
||||||
var t2 = new TzDate(3, 0);
|
var t2 = new angular.mock.TzDate(3, 0);
|
||||||
expect(t2.getSeconds()).toMatch(0);
|
expect(t2.getSeconds()).toMatch(0);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
it('should create a date representing new year in Bratislava', function() {
|
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.getTimezoneOffset()).toBe(-60);
|
||||||
expect(newYearInBratislava.getFullYear()).toBe(2010);
|
expect(newYearInBratislava.getFullYear()).toBe(2010);
|
||||||
expect(newYearInBratislava.getMonth()).toBe(0);
|
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() {
|
it('should delegate all the UTC methods to the original UTC Date object', function() {
|
||||||
//from when created from string
|
//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.getUTCFullYear()).toBe(2009);
|
||||||
expect(date1.getUTCMonth()).toBe(11);
|
expect(date1.getUTCMonth()).toBe(11);
|
||||||
expect(date1.getUTCDate()).toBe(31);
|
expect(date1.getUTCDate()).toBe(31);
|
||||||
|
|
@ -113,7 +113,7 @@ describe('mocks', function() {
|
||||||
|
|
||||||
|
|
||||||
//from when created from millis
|
//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.getUTCFullYear()).toBe(2009);
|
||||||
expect(date2.getUTCMonth()).toBe(11);
|
expect(date2.getUTCMonth()).toBe(11);
|
||||||
expect(date2.getUTCDate()).toBe(31);
|
expect(date2.getUTCDate()).toBe(31);
|
||||||
|
|
@ -124,16 +124,20 @@ describe('mocks', function() {
|
||||||
|
|
||||||
|
|
||||||
it('should throw error when no third param but toString called', 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');
|
toThrow('Method \'toString\' is not implemented in the TzDate mock');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('$log mock', function() {
|
describe('$log', function() {
|
||||||
var $log;
|
var $log;
|
||||||
beforeEach(function() {
|
beforeEach(inject(['$log', function(log) {
|
||||||
$log = MockLogFactory();
|
$log = log;
|
||||||
});
|
}]));
|
||||||
|
|
||||||
|
afterEach(inject(function($log){
|
||||||
|
$log.reset();
|
||||||
|
}));
|
||||||
|
|
||||||
it('should provide log method', function() {
|
it('should provide log method', function() {
|
||||||
expect(function() { $log.log(''); }).not.toThrow();
|
expect(function() { $log.log(''); }).not.toThrow();
|
||||||
|
|
@ -170,14 +174,48 @@ describe('mocks', function() {
|
||||||
$log.error('fake log');
|
$log.error('fake log');
|
||||||
expect($log.error.logs).toContain(['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() {
|
describe('defer', function() {
|
||||||
var browser, log;
|
var browser, log;
|
||||||
beforeEach(function() {
|
beforeEach(inject(function($browser) {
|
||||||
browser = new MockBrowser();
|
browser = $browser;
|
||||||
log = '';
|
log = '';
|
||||||
});
|
}));
|
||||||
|
|
||||||
function logFn(text){ return function() {
|
function logFn(text){ return function() {
|
||||||
log += text +';';
|
log += text +';';
|
||||||
|
|
@ -229,5 +267,20 @@ describe('mocks', function() {
|
||||||
it('should rethrow exceptions', inject(function($exceptionHandler) {
|
it('should rethrow exceptions', inject(function($exceptionHandler) {
|
||||||
expect(function() { $exceptionHandler('myException'); }).toThrow('myException');
|
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() {
|
describe('$cookies', function() {
|
||||||
beforeEach(inject(function($provide) {
|
beforeEach(inject(function($provide) {
|
||||||
$provide.factory('$browser', function(){
|
$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(
|
it('should log errors', inject(
|
||||||
function($provide){
|
function($provide){
|
||||||
$provide.factory('$exceptionHandler', $exceptionHandlerFactory);
|
$provide.service('$exceptionHandler', $ExceptionHandlerProvider);
|
||||||
$provide.value('$log', $logMock);
|
|
||||||
},
|
},
|
||||||
function($log, $exceptionHandler) {
|
function($log, $exceptionHandler) {
|
||||||
$log.error.rethrow = false;
|
|
||||||
$exceptionHandler('myError');
|
$exceptionHandler('myError');
|
||||||
expect($log.error.logs.shift()).toEqual(['myError']);
|
expect($log.error.logs.shift()).toEqual(['myError']);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
describe('$locale', function() {
|
describe('$locale', function() {
|
||||||
|
|
||||||
var $locale = angular.service('$locale')();
|
var $locale = new $LocaleProvider().$get();
|
||||||
|
|
||||||
it('should have locale id set to en-us', function() {
|
it('should have locale id set to en-us', function() {
|
||||||
expect($locale.id).toBe('en-us');
|
expect($locale.id).toBe('en-us');
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,7 @@ describe('$log', function() {
|
||||||
beforeEach(inject(function($provide){
|
beforeEach(inject(function($provide){
|
||||||
$window = {};
|
$window = {};
|
||||||
logger = '';
|
logger = '';
|
||||||
$provide.factory('$log', $logFactory);
|
$provide.service('$log', $LogProvider);
|
||||||
$provide.value('$exceptionHandler', rethrow);
|
$provide.value('$exceptionHandler', rethrow);
|
||||||
$provide.value('$window', $window);
|
$provide.value('$window', $window);
|
||||||
}));
|
}));
|
||||||
|
|
@ -68,7 +68,7 @@ describe('$log', function() {
|
||||||
e.line = undefined;
|
e.line = undefined;
|
||||||
e.stack = undefined;
|
e.stack = undefined;
|
||||||
|
|
||||||
$log = $logFactory({console:{error:function() {
|
$log = new $LogProvider().$get[1]({console:{error:function() {
|
||||||
errorArgs = arguments;
|
errorArgs = arguments;
|
||||||
}}});
|
}}});
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -2,8 +2,8 @@
|
||||||
|
|
||||||
describe('Scope', function() {
|
describe('Scope', function() {
|
||||||
|
|
||||||
beforeEach(inject(function($provide) {
|
beforeEach(inject(function($exceptionHandlerProvider) {
|
||||||
$provide.factory('$exceptionHandler', $exceptionHandlerMockFactory);
|
$exceptionHandlerProvider.mode('log');
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -121,12 +121,12 @@ describe('Scope', function() {
|
||||||
expect(spy).wasCalled();
|
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.$watch('a', function() {throw new Error('abc');});
|
||||||
$rootScope.a = 1;
|
$rootScope.a = 1;
|
||||||
$rootScope.$digest();
|
$rootScope.$digest();
|
||||||
expect($exceptionHandler.errors[0].message).toEqual('abc');
|
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 log = '';
|
||||||
var child = $rootScope.$new();
|
var child = $rootScope.$new();
|
||||||
$rootScope.$watch('a', function(scope, a){ log += '1'; });
|
$rootScope.$watch('a', function(scope, a){ log += '1'; });
|
||||||
|
|
@ -430,7 +430,7 @@ describe('Scope', function() {
|
||||||
child.$apply(function() { throw new Error('MyError'); });
|
child.$apply(function() { throw new Error('MyError'); });
|
||||||
expect(log).toEqual('1');
|
expect(log).toEqual('1');
|
||||||
expect($exceptionHandler.errors[0].message).toEqual('MyError');
|
expect($exceptionHandler.errors[0].message).toEqual('MyError');
|
||||||
$logMock.error.logs.shift();
|
$log.error.logs.shift();
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@
|
||||||
describe('$sniffer', function() {
|
describe('$sniffer', function() {
|
||||||
|
|
||||||
function sniffer($window) {
|
function sniffer($window) {
|
||||||
return angular.service('$sniffer')($window);
|
return new $SnifferProvider().$get[1]($window);
|
||||||
}
|
}
|
||||||
|
|
||||||
describe('history', function() {
|
describe('history', function() {
|
||||||
|
|
|
||||||
|
|
@ -77,18 +77,13 @@ beforeEach(function() {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
$logMock.log.logs = [];
|
|
||||||
$logMock.warn.logs = [];
|
|
||||||
$logMock.info.logs = [];
|
|
||||||
$logMock.error.logs = [];
|
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
function inject(){
|
function inject(){
|
||||||
var blockFns = sliceArgs(arguments);
|
var blockFns = sliceArgs(arguments);
|
||||||
return function(){
|
return function(){
|
||||||
var spec = this;
|
var spec = this;
|
||||||
spec.$injector = spec.$injector || angular.injector('NG');
|
spec.$injector = spec.$injector || angular.injector('NG', 'NG_MOCK');
|
||||||
angular.forEach(blockFns, function(fn){
|
angular.forEach(blockFns, function(fn){
|
||||||
spec.$injector.invoke(spec, fn);
|
spec.$injector.invoke(spec, fn);
|
||||||
});
|
});
|
||||||
|
|
@ -96,30 +91,12 @@ function inject(){
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
afterEach(inject(function($rootScope) {
|
afterEach(inject(function($rootScope, $log) {
|
||||||
// release the injector
|
// release the injector
|
||||||
dealoc($rootScope);
|
dealoc($rootScope);
|
||||||
|
|
||||||
// check $log mock
|
// check $log mock
|
||||||
forEach(['error', 'warn', 'info', 'log'], function(logLevel) {
|
$log.assertEmpty && $log.assertEmpty();
|
||||||
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));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
clearJqCache();
|
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() {
|
expect(function() {
|
||||||
compile('<input type="text" ng:model="throw \'\'">');
|
compile('<input type="text" ng:model="throw \'\'">');
|
||||||
}).toThrow("Syntax Error: Token '''' is an unexpected token at column 7 of the expression [throw ''] starting at [''].");
|
}).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;');
|
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() {
|
expect(function() {
|
||||||
var element = $compile('<ul><li ng:repeat="i dont parse"></li></ul>')($rootScope);
|
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'.");
|
}).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) {
|
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() {
|
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 myApp = injector('$rootScope');
|
||||||
var $browser = myApp.$service('$browser');
|
var $browser = injector('$browser');
|
||||||
$browser.xhr.expectGET('includePartial.html').respond('view: <ng:view></ng:view>');
|
$browser.xhr.expectGET('includePartial.html').respond('view: <ng:view></ng:view>');
|
||||||
injector('$location').path('/foo');
|
injector('$location').path('/foo');
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue