diff options
43 files changed, 2206 insertions, 2176 deletions
| diff --git a/angularFiles.js b/angularFiles.js index a0a9ce5e..936f9b7d 100644 --- a/angularFiles.js +++ b/angularFiles.js @@ -70,7 +70,6 @@ angularFiles = {      'src/jstd-scenario-adapter/*.js',      'src/scenario/*.js',      'src/angular-mocks.js', -    'test/mocks.js',      'test/scenario/*.js',      'test/scenario/output/*.js',      'test/jstd-scenario-adapter/*.js', @@ -133,7 +132,6 @@ angularFiles = {      'src/jstd-scenario-adapter/*.js',      'src/scenario/*.js',      'src/angular-mocks.js', -    'test/mocks.js',      'test/scenario/*.js',      'test/scenario/output/*.js',      'test/jstd-scenario-adapter/*.js', diff --git a/docs/src/templates/docs.js b/docs/src/templates/docs.js index b50333a5..2693da64 100644 --- a/docs/src/templates/docs.js +++ b/docs/src/templates/docs.js @@ -148,9 +148,12 @@ function TutorialInstructionsCtrl($cookieStore) {    };  } -angular.service('$locationConfig', function() { -  return { +window.angular = window.angular || {}; +angular.module = angular.module || {}; + +angular.module.ngdocs = function($provide) { +  $provide.value('$locationConfig', {      html5Mode: true,      hashPrefix: '!' -  }; -}); +  }); +}; diff --git a/example/personalLog/test/personalLogSpec.js b/example/personalLog/test/personalLogSpec.js index 2e93925c..5e3a8409 100644 --- a/example/personalLog/test/personalLogSpec.js +++ b/example/personalLog/test/personalLogSpec.js @@ -2,7 +2,7 @@ describe('example.personalLog.LogCtrl', function() {    var logCtrl;    function createNotesCtrl() { -    var injector = angular.injector('NG'); +    var injector = angular.injector('NG', 'NG_MOCK');      var scope = injector('$rootScope');      scope.$cookies = injector('$cookies');      return scope.$new(example.personalLog.LogCtrl); diff --git a/src/Angular.js b/src/Angular.js index 4344425a..931a3e5a 100644 --- a/src/Angular.js +++ b/src/Angular.js @@ -99,8 +99,8 @@ var _undefined        = undefined,                             : noop,      /** @name angular */ -    angular           = window[$angular] || (window[$angular] = {}), -    angularModules    = angular.modules || (angular.modules = {}), +    angular           = window.angular || (window.angular = {}), +    angularModule     = angular.module || (angular.module  = {}),      /** @name angular.markup */      angularTextMarkup = extensionMap(angular, 'markup'),      /** @name angular.attrMarkup */ @@ -114,7 +114,6 @@ var _undefined        = undefined,      /** @name angular.service */      angularInputType  = extensionMap(angular, 'inputType', lowercase),      /** @name angular.service */ -    angularService    = extensionMap(angular, 'service'),      angularCallbacks  = extensionMap(angular, 'callbacks'),      nodeName_,      uid               = ['0', '0', '0'], @@ -188,18 +187,6 @@ function forEachSorted(obj, iterator, context) {  } -function formatError(arg) { -  if (arg instanceof Error) { -    if (arg.stack) { -      arg = (arg.message && arg.stack.indexOf(arg.message) === -1) ? -            'Error: ' + arg.message + '\n' + arg.stack : arg.stack; -    } else if (arg.sourceURL) { -      arg = arg.message + '\n' + arg.sourceURL + ':' + arg.line; -    } -  } -  return arg; -} -  /**   * A consistent way of creating unique IDs in angular. The ID is a sequence of alpha numeric   * characters such as '012ABC'. The reason why we are not using simply a number counter is that @@ -957,7 +944,7 @@ function angularInit(config, document){          modules.push(module);        }      }); -    createInjector(modules, angularModules)(['$rootScope', '$compile', function(scope, compile){ +    createInjector(modules, angularModule)(['$rootScope', '$compile', '$injector', function(scope, compile, injector){        scope.$apply(function(){          compile(isString(autobind) ? document.getElementById(autobind) : document)(scope);        }); @@ -1030,7 +1017,7 @@ function publishExternalAPI(angular){      'extend': extend,      'equals': equals,      'forEach': forEach, -    'injector': function(){ return createInjector(arguments, angularModules); }, +    'injector': function(){ return createInjector(arguments, angularModule); },      'noop':noop,      'bind':bind,      'toJson': toJson, @@ -1049,14 +1036,39 @@ function publishExternalAPI(angular){      'uppercase': uppercase    }); -  angularModules.NG = ngModule; +  angularModule.NG = ngModule;  } -ngModule.$inject = ['$provide']; -function ngModule($provide) { -  forEach(angularService, function(factory, name){ -    $provide.factory(name, factory); -  }); +ngModule.$inject = ['$provide', '$injector']; +function ngModule($provide, $injector) { +// TODO(misko): temporary services to get the compiler working; +  $provide.value('$textMarkup', angularTextMarkup); +  $provide.value('$attrMarkup', angularAttrMarkup); +  $provide.value('$directive', angularDirective); +  $provide.value('$widget', angularWidget); + +  $provide.service('$browser', $BrowserProvider); +  $provide.service('$compile', $CompileProvider); +  $provide.service('$cookies', $CookiesProvider); +  $provide.service('$cookieStore', $CookieStoreProvider); +  $provide.service('$defer', $DeferProvider); +  $provide.service('$document', $DocumentProvider); +  $provide.service('$exceptionHandler', $ExceptionHandlerProvider); +  $provide.service('$formFactory', $FormFactoryProvider); +  $provide.service('$locale', $LocaleProvider); +  $provide.service('$location', $LocationProvider); +  $provide.service('$locationConfig', $LocationConfigProvider); +  $provide.service('$log', $LogProvider); +  $provide.service('$resource', $ResourceProvider); +  $provide.service('$route', $RouteProvider); +  $provide.service('$routeParams', $RouteParamsProvider); +  $provide.service('$rootScope', $RootScopeProvider); +  $provide.service('$sniffer', $SnifferProvider); +  $provide.service('$window', $WindowProvider); +  $provide.service('$xhr.bulk', $XhrBulkProvider); +  $provide.service('$xhr.cache', $XhrCacheProvider); +  $provide.service('$xhr.error', $XhrErrorProvider); +  $provide.service('$xhr', $XhrProvider);  } diff --git a/src/AngularPublic.js b/src/AngularPublic.js index 600f1ef8..64a7bf4d 100644 --- a/src/AngularPublic.js +++ b/src/AngularPublic.js @@ -1,16 +1,5 @@  'use strict'; -var browserSingleton; - -angularService('$browser', function($log, $sniffer) { -  if (!browserSingleton) { -    browserSingleton = new Browser(window, jqLite(window.document), jqLite(window.document.body), -                                   XHR, $log, $sniffer); -  } -  return browserSingleton; -}, {$inject: ['$log', '$sniffer']}); - -  publishExternalAPI(angular);  //try to bind to jquery now so that one can write angular.element().read() diff --git a/src/Browser.js b/src/Browser.js index 71252050..d4faebc8 100644 --- a/src/Browser.js +++ b/src/Browser.js @@ -473,3 +473,10 @@ function Browser(window, document, body, XHR, $log, $sniffer) {      return href ? href.replace(/^https?\:\/\/[^\/]*/, '') : href;    };  } + +function $BrowserProvider(){ +  this.$get = ['$window', '$log', '$sniffer', '$document', +      function( $window,   $log,   $sniffer,   $document){ +        return new Browser($window, $document, $document.find('body'), XHR, $log, $sniffer); +      }]; +} diff --git a/src/Injector.js b/src/Injector.js index 12c2ffa6..838a911d 100644 --- a/src/Injector.js +++ b/src/Injector.js @@ -33,10 +33,6 @@   *     `injector.eager()`   */ -function angularServiceInject(name, fn, inject, eager) { -  angularService(name, fn, {$inject:inject, $eager:eager}); -} -  /**   * @returns the $inject property of function. If not found the @@ -177,7 +173,11 @@ function createInjector(modulesToLoad, moduleRegistry) {    forEach(modulesToLoad, function(module){      if (isString(module)) { -      module = moduleRegistry[module]; +      if (moduleRegistry[module]) { +        module = moduleRegistry[module]; +      } else { +        throw Error("Module '" + module + "' is not defined!"); +      }      }      if (isFunction(module) || isArray(module)) {        $injector(module); diff --git a/src/angular-mocks.js b/src/angular-mocks.js index 679a78a3..b4b2fd19 100644 --- a/src/angular-mocks.js +++ b/src/angular-mocks.js @@ -1,4 +1,3 @@ -'use strict';  /**   * @license AngularJS v"NG_VERSION_FULL" @@ -8,30 +7,6 @@  /* - - NUGGGGGH MUST TONGUE WANGS -                           \ -                                ..... -                               C C  / -                              /<   / -               ___ __________/_#__=o -              /(- /(\_\________   \ -              \ ) \ )_      \o     \ -              /|\ /|\       |'     | -                            |     _| -                            /o   __\ -                           / '     | -                          / /      | -                         /_/\______| -                        (   _(    < -                         \    \    \ -                          \    \    | -                           \____\____\ -                           ____\_\__\_\ -                         /`   /`     o\ -                         |___ |_______|.. . b'ger - -   IN THE FINAL BUILD THIS FILE DOESN'T HAVE DIRECT ACCESS TO GLOBAL FUNCTIONS   DEFINED IN Angular.js YOU *MUST* REFER TO THEM VIA angular OBJECT   (e.g. angular.forEach(...)) AND MAKE SURE THAT THE GIVEN FUNCTION IS EXPORTED @@ -56,8 +31,15 @@   *   the angular service exception handler.   * * {@link angular.mock.service.$log $log } - A mock implementation of the angular service log.   */ -angular.mock = {}; +window.angular = window.angular || {}; +angular.module = angular.module || {}; +angular.mock = angular.mock || {}; +angular.module.NG_MOCK = ['$provide', function($provide){ +  $provide.service('$browser', angular.mock.$BrowserProvider); +  $provide.service('$exceptionHandler', angular.mock.$ExceptionHandlerProvider); +  $provide.service('$log', angular.mock.$LogProvider); +}];  /**   * @ngdoc service @@ -81,7 +63,12 @@ angular.mock = {};   * - $browser.defer — enables testing of code that uses   *   {@link angular.service.$defer $defer service} for executing functions via the `setTimeout` api.   */ -function MockBrowser() { +angular.mock.$BrowserProvider = function(){ +  this.$get = function(){ +    return new angular.mock.$Browser(); +  }; +}; +angular.mock.$Browser = function() {    var self = this,        expectations = {},        requests = []; @@ -309,7 +296,7 @@ function MockBrowser() {      return this.$$baseHref;    };  } -MockBrowser.prototype = { +angular.mock.$Browser.prototype = {  /**    * @name angular.mock.service.$browser#poll @@ -360,10 +347,6 @@ MockBrowser.prototype = {    addJs: function() {}  }; -angular.service('$browser', function() { -  return new MockBrowser(); -}); -  /**   * @ngdoc service @@ -376,9 +359,29 @@ angular.service('$browser', function() {   *   * See {@link angular.mock} for more info on angular mocks.   */ -angular.service('$exceptionHandler', function() { -  return function(e) { throw e; }; -}); +angular.mock.$ExceptionHandlerProvider = function(){ +  var handler; + +  this.mode = function(mode){ +    handler = { +     rethrow: function(e) { +      throw e; +     }, +     log: angular.extend(function log(e) { +       log.errors.push(e); +     }, {errors:[]}) +    }[mode]; +    if (!handler) { +      throw Error("Unknown mode '" + mode + "', only 'log'/'rethrow' modes are allowed!"); +    } +  }; + +  this.$get = function(){ +    return handler; +  }; + +  this.mode('rethrow'); +};  /** @@ -392,23 +395,43 @@ angular.service('$exceptionHandler', function() {   *   * See {@link angular.mock} for more info on angular mocks.   */ -angular.service('$log', MockLogFactory); - -function MockLogFactory() { -  var $log = { -    log: function() { $log.log.logs.push(arguments); }, -    warn: function() { $log.warn.logs.push(arguments); }, -    info: function() { $log.info.logs.push(arguments); }, -    error: function() { $log.error.logs.push(arguments); } -  }; +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)); } +    }; -  $log.log.logs = []; -  $log.warn.logs = []; -  $log.info.logs = []; -  $log.error.logs = []; +    $log.reset = function (){ +      $log.log.logs = []; +      $log.warn.logs = []; +      $log.info.logs = []; +      $log.error.logs = []; +    }; -  return $log; -} +    $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; +  }; +};  /** @@ -441,7 +464,7 @@ function MockLogFactory() {   * </pre>   *   */ -function TzDate(offset, timestamp) { +angular.mock.TzDate = function (offset, timestamp) {    if (angular.isString(timestamp)) {      var tsStr = timestamp; @@ -545,4 +568,4 @@ function TzDate(offset, timestamp) {  }  //make "tzDateInstance instanceof Date" return true -TzDate.prototype = Date.prototype; +angular.mock.TzDate.prototype = Date.prototype; diff --git a/src/service/compiler.js b/src/service/compiler.js index 0bd3e54d..220d9c39 100644 --- a/src/service/compiler.js +++ b/src/service/compiler.js @@ -1,325 +1,322 @@  'use strict'; -// TODO(misko): temporary services to get the compiler working; -angularService('$textMarkup', valueFn(angularTextMarkup)); -angularService('$attrMarkup', valueFn(angularAttrMarkup)); -angularService('$directive', valueFn(angularDirective)); -angularService('$widget', valueFn(angularWidget)); -angularServiceInject('$compile', function($injector, $exceptionHandler, $textMarkup, $attrMarkup, $directive, $widget){ -  /** -   * Template provides directions an how to bind to a given element. -   * It contains a list of init functions which need to be called to -   * bind to a new instance of elements. It also provides a list -   * of child paths which contain child templates -   */ -  function Template() { -    this.paths = []; -    this.children = []; -    this.linkFns = []; -    this.newScope = false; -  } - -  Template.prototype = { -    link: function(element, scope) { -      var childScope = scope, -          locals = {$element: element}; -      if (this.newScope) { -        childScope = isFunction(this.newScope) ? scope.$new(this.newScope(scope)) : scope.$new(); -        element.data($$scope, childScope); -      } -      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 +function $CompileProvider(){ +  this.$get = ['$injector', '$exceptionHandler', '$textMarkup', '$attrMarkup', '$directive', '$widget', +    function(   $injector,   $exceptionHandler,   $textMarkup,   $attrMarkup,   $directive,   $widget){ +      /** +       * Template provides directions an how to bind to a given element. +       * It contains a list of init functions which need to be called to +       * bind to a new instance of elements. It also provides a list +       * of child paths which contain child templates +       */ +      function Template() { +        this.paths = []; +        this.children = []; +        this.linkFns = []; +        this.newScope = false;        } -    }, - -    addLinkFn:function(linkingFn) { -      if (linkingFn) { -        //TODO(misko): temporary hack. -        if (isFunction(linkingFn) && !linkingFn.$inject) { -          linkingFn.$inject = ['$element']; -        } -        this.linkFns.push(linkingFn); -      } -    }, +      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 +          } +        }, -    addChild: function(index, template) { -      if (template) { -        this.paths.push(index); -        this.children.push(template); -      } -    }, +        addLinkFn:function(linkingFn) { +          if (linkingFn) { +            //TODO(misko): temporary hack. +            if (isFunction(linkingFn) && !linkingFn.$inject) { +              linkingFn.$inject = ['$element']; +            } +            this.linkFns.push(linkingFn); +          } +        }, -    empty: function() { -      return this.linkFns.length === 0 && this.paths.length === 0; -    } -  }; -  /////////////////////////////////// -  //Compiler -  ////////////////////////////////// +        addChild: function(index, template) { +          if (template) { +            this.paths.push(index); +            this.children.push(template); +          } +        }, -  /** -   * @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)(); +        empty: function() { +          return this.linkFns.length === 0 && this.paths.length === 0; +        } +      }; -      // compile a piece of html -      var rootScope2 = angular.compile('<div ng:click="clicked = true">click me</div>')(); +      /////////////////////////////////// +      //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 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; -  } +          // compile a piece of html +          var rootScope2 = angular.compile('<div ng:click="clicked = true">click me</div>')(); -  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; -          } -        } +          // 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;        } -      template = this.templatize(templateElement, index) || new Template(); -      return function(scope, cloneConnectFn){ -        assertArg(scope, 'scope'); -        // important!!: we must call our jqLite.clone() since the jQuery one is trying to be smart -        // and sometimes changes the structure of the DOM. -        var element = cloneConnectFn -          ? JQLitePrototype.clone.call(templateElement) // IMPORTANT!!! -          : templateElement; -        element.data($$scope, scope); -        scope.$element = element; -        (cloneConnectFn||noop)(element, scope); -        template.link(element, scope); -        return element; -      }; -    }, -    templatize: function(element, elementIndex){ -      var self = this, -          widget, -          fn, -          directiveFns = self.directives, -          descend = true, -          directives = true, -          elementName = nodeName_(element), -          elementNamespace = elementName.indexOf(':') > 0 ? lowercase(elementName).replace(':', '-') : '', -          template, -          selfApi = { -            compile: bind(self, self.compile), -            descend: function(value){ if(isDefined(value)) descend = value; return descend;}, -            directives: function(value){ if(isDefined(value)) directives = value; return directives;}, -            scope: function(value){ if(isDefined(value)) template.newScope = template.newScope || value; return template.newScope;} -          }; -      element.addClass(elementNamespace); -      template = new Template(); -      eachAttribute(element, function(value, name){ -        if (!widget) { -          if ((widget = self.widgets('@' + name))) { -            element.addClass('ng-attr-widget'); -            widget = bind(selfApi, widget, value, element); +      Compiler.prototype = { +        compile: function(templateElement) { +          templateElement = jqLite(templateElement); +          var index = 0, +              template, +              parent = templateElement.parent(); +          if (templateElement.length > 1) { +            // https://github.com/angular/angular.js/issues/338 +            throw Error("Cannot compile multiple element roots: " + +                jqLite('<div>').append(templateElement.clone()).html());            } -        } -      }); -      if (!widget) { -        if ((widget = self.widgets(elementName))) { -          if (elementNamespace) -            element.addClass('ng-widget'); -          widget = bind(selfApi, widget, element); -        } -      } -      if (widget) { -        descend = false; -        directives = false; -        var parent = element.parent(); -        template.addLinkFn(widget.call(selfApi, element)); -        if (parent && parent[0]) { -          element = jqLite(parent[0].childNodes[elementIndex]); -        } -      } -      if (descend){ -        // process markup for text nodes only -        for(var i=0, child=element[0].childNodes; -            i<child.length; i++) { -          if (isTextNode(child[i])) { -            forEach(self.markup, function(markup){ -              if (i<child.length) { -                var textNode = jqLite(child[i]); -                markup.call(selfApi, textNode.text(), textNode, element); +          if (parent && parent[0]) { +            parent = parent[0]; +            for(var i = 0; i < parent.childNodes.length; i++) { +              if (parent.childNodes[i] == templateElement[0]) { +                index = i;                } -            }); +            }            } -        } -      } +          template = this.templatize(templateElement, index) || new Template(); +          return function(scope, cloneConnectFn){ +            assertArg(scope, 'scope'); +            // important!!: we must call our jqLite.clone() since the jQuery one is trying to be smart +            // and sometimes changes the structure of the DOM. +            var element = cloneConnectFn +              ? JQLitePrototype.clone.call(templateElement) // IMPORTANT!!! +              : templateElement; +            element.data($$scope, scope); +            scope.$element = element; +            (cloneConnectFn||noop)(element, scope); +            template.link(element, scope); +            return element; +          }; +        }, -      if (directives) { -        // Process attributes/directives -        eachAttribute(element, function(value, name){ -          forEach(self.attrMarkup, function(markup){ -            markup.call(selfApi, value, name, element); +        templatize: function(element, elementIndex){ +          var self = this, +              widget, +              fn, +              directiveFns = self.directives, +              descend = true, +              directives = true, +              elementName = nodeName_(element), +              elementNamespace = elementName.indexOf(':') > 0 ? lowercase(elementName).replace(':', '-') : '', +              template, +              selfApi = { +                compile: bind(self, self.compile), +                descend: function(value){ if(isDefined(value)) descend = value; return descend;}, +                directives: function(value){ if(isDefined(value)) directives = value; return directives;}, +                scope: function(value){ if(isDefined(value)) template.newScope = template.newScope || value; return template.newScope;} +              }; +          element.addClass(elementNamespace); +          template = new Template(); +          eachAttribute(element, function(value, name){ +            if (!widget) { +              if ((widget = self.widgets('@' + name))) { +                element.addClass('ng-attr-widget'); +                widget = bind(selfApi, widget, value, element); +              } +            }            }); -        }); -        eachAttribute(element, function(value, name){ -          name = lowercase(name); -          fn = directiveFns[name]; -          if (fn) { -            element.addClass('ng-directive'); -            template.addLinkFn((directiveFns[name]).call(selfApi, value, element)); +          if (!widget) { +            if ((widget = self.widgets(elementName))) { +              if (elementNamespace) +                element.addClass('ng-widget'); +              widget = bind(selfApi, widget, element); +            }            } -        }); -      } -      // Process non text child nodes -      if (descend) { -        eachNode(element, function(child, i){ -          template.addChild(i, self.templatize(child, i)); -        }); -      } -      return template.empty() ? null : template; -    } -  }; +          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){ +              template.addChild(i, self.templatize(child, i)); +            }); +          } +          return template.empty() ? null : template; +        } +      }; -  ///////////////////////////////////////////////////////////////////// -  var compiler = new Compiler($textMarkup, $attrMarkup, $directive, $widget); -  return bind(compiler, compiler.compile); -}, ['$injector', '$exceptionHandler', '$textMarkup', '$attrMarkup', '$directive', '$widget']); +      ///////////////////////////////////////////////////////////////////// +      var compiler = new Compiler($textMarkup, $attrMarkup, $directive, $widget); +      return bind(compiler, compiler.compile); +    }]; +};  function eachNode(element, fn){ diff --git a/src/service/cookieStore.js b/src/service/cookieStore.js index 1b38cfac..0aa13ed2 100644 --- a/src/service/cookieStore.js +++ b/src/service/cookieStore.js @@ -11,52 +11,54 @@   * deserialized by angular's toJson/fromJson.   * @example   */ -angularServiceInject('$cookieStore', function($store) { +function $CookieStoreProvider(){ +  this.$get = ['$cookies', function($cookies) { -  return { -    /** -     * @ngdoc method -     * @name angular.service.$cookieStore#get -     * @methodOf angular.service.$cookieStore -     * -     * @description -     * Returns the value of given cookie key -     * -     * @param {string} key Id to use for lookup. -     * @returns {Object} Deserialized cookie value. -     */ -    get: function(key) { -      return fromJson($store[key]); -    }, +    return { +      /** +       * @ngdoc method +       * @name angular.service.$cookieStore#get +       * @methodOf angular.service.$cookieStore +       * +       * @description +       * Returns the value of given cookie key +       * +       * @param {string} key Id to use for lookup. +       * @returns {Object} Deserialized cookie value. +       */ +      get: function(key) { +        return fromJson($cookies[key]); +      }, -    /** -     * @ngdoc method -     * @name angular.service.$cookieStore#put -     * @methodOf angular.service.$cookieStore -     * -     * @description -     * Sets a value for given cookie key -     * -     * @param {string} key Id for the `value`. -     * @param {Object} value Value to be stored. -     */ -    put: function(key, value) { -      $store[key] = toJson(value); -    }, +      /** +       * @ngdoc method +       * @name angular.service.$cookieStore#put +       * @methodOf angular.service.$cookieStore +       * +       * @description +       * Sets a value for given cookie key +       * +       * @param {string} key Id for the `value`. +       * @param {Object} value Value to be stored. +       */ +      put: function(key, value) { +        $cookies[key] = toJson(value); +      }, -    /** -     * @ngdoc method -     * @name angular.service.$cookieStore#remove -     * @methodOf angular.service.$cookieStore -     * -     * @description -     * Remove given cookie -     * -     * @param {string} key Id of the key-value pair to delete. -     */ -    remove: function(key) { -      delete $store[key]; -    } -  }; +      /** +       * @ngdoc method +       * @name angular.service.$cookieStore#remove +       * @methodOf angular.service.$cookieStore +       * +       * @description +       * Remove given cookie +       * +       * @param {string} key Id of the key-value pair to delete. +       */ +      remove: function(key) { +        delete $cookies[key]; +      } +    }; -}, ['$cookies']); +  }]; +} diff --git a/src/service/cookies.js b/src/service/cookies.js index 2cd2b9d6..16e14847 100644 --- a/src/service/cookies.js +++ b/src/service/cookies.js @@ -13,80 +13,82 @@   *   * @example   */ -angularServiceInject('$cookies', function($rootScope, $browser) { -  var cookies = {}, -      lastCookies = {}, -      lastBrowserCookies, -      runEval = false; +function $CookiesProvider() { +  this.$get = ['$rootScope', '$browser', function ($rootScope, $browser) { +    var cookies = {}, +        lastCookies = {}, +        lastBrowserCookies, +        runEval = false; -  //creates a poller fn that copies all cookies from the $browser to service & inits the service -  $browser.addPollFn(function() { -    var currentCookies = $browser.cookies(); -    if (lastBrowserCookies != currentCookies) { //relies on browser.cookies() impl -      lastBrowserCookies = currentCookies; -      copy(currentCookies, lastCookies); -      copy(currentCookies, cookies); -      if (runEval) $rootScope.$apply(); -    } -  })(); +    //creates a poller fn that copies all cookies from the $browser to service & inits the service +    $browser.addPollFn(function() { +      var currentCookies = $browser.cookies(); +      if (lastBrowserCookies != currentCookies) { //relies on browser.cookies() impl +        lastBrowserCookies = currentCookies; +        copy(currentCookies, lastCookies); +        copy(currentCookies, cookies); +        if (runEval) $rootScope.$apply(); +      } +    })(); -  runEval = true; +    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); +    //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; +    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; +    /** +     * 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); +      //delete any cookies deleted in $cookies +      for (name in lastCookies) { +        if (isUndefined(cookies[name])) { +          $browser.cookies(name, undefined); +        }        } -    } -    //update all cookies updated in $cookies -    for(name in cookies) { -      value = cookies[name]; -      if (!isString(value)) { -        if (isDefined(lastCookies[name])) { -          cookies[name] = lastCookies[name]; -        } else { -          delete cookies[name]; +      //update all cookies updated in $cookies +      for(name in cookies) { +        value = cookies[name]; +        if (!isString(value)) { +          if (isDefined(lastCookies[name])) { +            cookies[name] = lastCookies[name]; +          } else { +            delete cookies[name]; +          } +        } else if (value !== lastCookies[name]) { +          $browser.cookies(name, value); +          updated = true;          } -      } else if (value !== lastCookies[name]) { -        $browser.cookies(name, value); -        updated = true;        } -    } -    //verify what was actually stored -    if (updated){ -      updated = false; -      browserCookies = $browser.cookies(); +      //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]; +        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;            } -          updated = true;          }        }      } -  } -}, ['$rootScope', '$browser']); +  }]; +} diff --git a/src/service/defer.js b/src/service/defer.js index 07c98065..90e6f19f 100644 --- a/src/service/defer.js +++ b/src/service/defer.js @@ -28,16 +28,18 @@   * @param {*} deferId Token returned by the `$defer` function.   * @returns {boolean} Returns `true` if the task hasn't executed yet and was successfuly canceled.   */ -angularServiceInject('$defer', function($rootScope, $browser) { -  function defer(fn, delay) { -    return $browser.defer(function() { -      $rootScope.$apply(fn); -    }, delay); -  } +function $DeferProvider(){ +  this.$get = ['$rootScope', '$browser', function($rootScope, $browser) { +    function defer(fn, delay) { +      return $browser.defer(function() { +        $rootScope.$apply(fn); +      }, delay); +    } -  defer.cancel = function(deferId) { -    return $browser.defer.cancel(deferId); -  }; +    defer.cancel = function(deferId) { +      return $browser.defer.cancel(deferId); +    }; -  return defer; -}, ['$rootScope', '$browser']); +    return defer; +  }]; +} diff --git a/src/service/document.js b/src/service/document.js index ae13c1ed..f71ac33a 100644 --- a/src/service/document.js +++ b/src/service/document.js @@ -9,6 +9,8 @@   * A {@link angular.element jQuery (lite)}-wrapped reference to the browser's `window.document`   * element.   */ -angularServiceInject("$document", function(window){ -  return jqLite(window.document); -}, ['$window']); +function $DocumentProvider(){ +  this.$get = ['$window', function(window){ +    return jqLite(window.document); +  }]; +} diff --git a/src/service/exceptionHandler.js b/src/service/exceptionHandler.js index 4bf6b6a6..b0c8e822 100644 --- a/src/service/exceptionHandler.js +++ b/src/service/exceptionHandler.js @@ -15,9 +15,10 @@   *   * @example   */ -var $exceptionHandlerFactory; //reference to be used only in tests -angularServiceInject('$exceptionHandler', $exceptionHandlerFactory = function($log){ -  return function(e) { -    $log.error(e); -  }; -}, ['$log']); +function $ExceptionHandlerProvider(){ +  this.$get = ['$log', function($log){ +    return function(e) { +      $log.error(e); +    }; +  }]; +} diff --git a/src/service/formFactory.js b/src/service/formFactory.js index fa6ad201..c9cb9c53 100644 --- a/src/service/formFactory.js +++ b/src/service/formFactory.js @@ -96,297 +96,299 @@        </doc:scenario>      </doc:example>   */ -angularServiceInject('$formFactory', function($rootScope) { +function $FormFactoryProvider() { +  this.$get = ['$rootScope', function($rootScope) { + +    /** +     * @ngdoc proprety +     * @name rootForm +     * @propertyOf angular.service.$formFactory +     * @description +     * Static property on `$formFactory` +     * +     * Each application ({@link guide/dev_guide.scopes.internals root scope}) gets a root form which +     * is the top-level parent of all forms. +     */ +    formFactory.rootForm = formFactory($rootScope); + + +    /** +     * @ngdoc method +     * @name forElement +     * @methodOf angular.service.$formFactory +     * @description +     * Static method on `$formFactory` service. +     * +     * Retrieve the closest form for a given element or defaults to the `root` form. Used by the +     * {@link angular.widget.form form} element. +     * @param {Element} element The element where the search for form should initiate. +     */ +    formFactory.forElement = function(element) { +      return element.inheritedData('$form') || formFactory.rootForm; +    }; +    return formFactory; + +    function formFactory(parent) { +      return (parent || formFactory.rootForm).$new(FormController); +    } + +  }]; + +  function propertiesUpdate(widget) { +    widget.$valid = !(widget.$invalid = +      !(widget.$readonly || widget.$disabled || equals(widget.$error, {}))); +  }    /** -   * @ngdoc proprety -   * @name rootForm +   * @ngdoc property +   * @name $error     * @propertyOf angular.service.$formFactory     * @description -   * Static property on `$formFactory` +   * Property of the form and widget instance.     * -   * Each application ({@link guide/dev_guide.scopes.internals root scope}) gets a root form which -   * is the top-level parent of all forms. +   * 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 ]`.     */ -  formFactory.rootForm = formFactory($rootScope); -    /** -   * @ngdoc method -   * @name forElement -   * @methodOf angular.service.$formFactory +   * @ngdoc property +   * @name $invalid +   * @propertyOf angular.service.$formFactory     * @description -   * Static method on `$formFactory` service. +   * Property of the form and widget instance.     * -   * 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. +   * True if any of the widgets of the form are invalid.     */ -  formFactory.forElement = function(element) { -    return element.inheritedData('$form') || formFactory.rootForm; -  }; -  return formFactory; - -  function formFactory(parent) { -    return (parent || formFactory.rootForm).$new(FormController); -  } - -}, ['$rootScope']); -function propertiesUpdate(widget) { -  widget.$valid = !(widget.$invalid = -    !(widget.$readonly || widget.$disabled || equals(widget.$error, {}))); -} - -/** - * @ngdoc property - * @name $error - * @propertyOf angular.service.$formFactory - * @description - * Property of the form and widget instance. - * - * Summary of all of the errors on the page. If a widget emits `$invalid` with `REQUIRED` key, - * then the `$error` object will have a `REQUIRED` key with an array of widgets which have - * emitted this key. `form.$error.REQUIRED == [ widget ]`. - */ - -/** - * @ngdoc property - * @name $invalid - * @propertyOf angular.service.$formFactory - * @description - * Property of the form and widget instance. - * - * True if any of the widgets of the form are invalid. - */ - -/** - * @ngdoc property - * @name $valid - * @propertyOf angular.service.$formFactory - * @description - * Property of the form and widget instance. - * - * True if all of the widgets of the form are valid. - */ +  /** +   * @ngdoc property +   * @name $valid +   * @propertyOf angular.service.$formFactory +   * @description +   * Property of the form and widget instance. +   * +   * True if all of the widgets of the form are valid. +   */ -/** - * @ngdoc event - * @name angular.service.$formFactory#$valid - * @eventOf angular.service.$formFactory - * @eventType listen on form - * @description - * Upon receiving the `$valid` event from the widget update the `$error`, `$valid` and `$invalid` - * properties of both the widget as well as the from. - * - * @param {String} validationKey The validation key to be used when updating the `$error` object. - *    The validation key is what will allow the template to bind to a specific validation error - *    such as `<div ng:show="form.$error.KEY">error for key</div>`. - */ +  /** +   * @ngdoc event +   * @name angular.service.$formFactory#$valid +   * @eventOf angular.service.$formFactory +   * @eventType listen on form +   * @description +   * Upon receiving the `$valid` event from the widget update the `$error`, `$valid` and `$invalid` +   * properties of both the widget as well as the from. +   * +   * @param {String} validationKey The validation key to be used when updating the `$error` object. +   *    The validation key is what will allow the template to bind to a specific validation error +   *    such as `<div ng:show="form.$error.KEY">error for key</div>`. +   */ -/** - * @ngdoc event - * @name angular.service.$formFactory#$invalid - * @eventOf angular.service.$formFactory - * @eventType listen on form - * @description - * Upon receiving the `$invalid` event from the widget update the `$error`, `$valid` and `$invalid` - * properties of both the widget as well as the from. - * - * @param {String} validationKey The validation key to be used when updating the `$error` object. - *    The validation key is what will allow the template to bind to a specific validation error - *    such as `<div ng:show="form.$error.KEY">error for key</div>`. - */ +  /** +   * @ngdoc event +   * @name angular.service.$formFactory#$invalid +   * @eventOf angular.service.$formFactory +   * @eventType listen on form +   * @description +   * Upon receiving the `$invalid` event from the widget update the `$error`, `$valid` and `$invalid` +   * properties of both the widget as well as the from. +   * +   * @param {String} validationKey The validation key to be used when updating the `$error` object. +   *    The validation key is what will allow the template to bind to a specific validation error +   *    such as `<div ng:show="form.$error.KEY">error for key</div>`. +   */ -/** - * @ngdoc event - * @name angular.service.$formFactory#$validate - * @eventOf angular.service.$formFactory - * @eventType emit on widget - * @description - * Emit the `$validate` event on the widget, giving a widget a chance to emit a - * `$valid` / `$invalid` event base on its state. The `$validate` event is triggered when the - * model or the view changes. - */ +  /** +   * @ngdoc event +   * @name angular.service.$formFactory#$validate +   * @eventOf angular.service.$formFactory +   * @eventType emit on widget +   * @description +   * Emit the `$validate` event on the widget, giving a widget a chance to emit a +   * `$valid` / `$invalid` event base on its state. The `$validate` event is triggered when the +   * model or the view changes. +   */ -/** - * @ngdoc event - * @name angular.service.$formFactory#$viewChange - * @eventOf angular.service.$formFactory - * @eventType listen on widget - * @description - * A widget is responsible for emitting this event whenever the view changes do to user interaction. - * The event takes a `$viewValue` parameter, which is the new value of the view. This - * event triggers a call to `$parseView()` as well as `$validate` event on widget. - * - * @param {*} viewValue The new value for the view which will be assigned to `widget.$viewValue`. - */ +  /** +   * @ngdoc event +   * @name angular.service.$formFactory#$viewChange +   * @eventOf angular.service.$formFactory +   * @eventType listen on widget +   * @description +   * A widget is responsible for emitting this event whenever the view changes do to user interaction. +   * The event takes a `$viewValue` parameter, which is the new value of the view. This +   * event triggers a call to `$parseView()` as well as `$validate` event on widget. +   * +   * @param {*} viewValue The new value for the view which will be assigned to `widget.$viewValue`. +   */ -function FormController() { -  var form = this, -      $error = form.$error = {}; +  function FormController() { +    var form = this, +        $error = form.$error = {}; -  form.$on('$destroy', function(event){ -    var widget = event.targetScope; -    if (widget.$widgetId) { -      delete form[widget.$widgetId]; -    } -    forEach($error, removeWidget, widget); -  }); +    form.$on('$destroy', function(event){ +      var widget = event.targetScope; +      if (widget.$widgetId) { +        delete form[widget.$widgetId]; +      } +      forEach($error, removeWidget, widget); +    }); + +    form.$on('$valid', function(event, error){ +      var widget = event.targetScope; +      delete widget.$error[error]; +      propertiesUpdate(widget); +      removeWidget($error[error], error, widget); +    }); + +    form.$on('$invalid', function(event, error){ +      var widget = event.targetScope; +      addWidget(error, widget); +      widget.$error[error] = true; +      propertiesUpdate(widget); +    }); -  form.$on('$valid', function(event, error){ -    var widget = event.targetScope; -    delete widget.$error[error]; -    propertiesUpdate(widget); -    removeWidget($error[error], error, widget); -  }); +    propertiesUpdate(form); -  form.$on('$invalid', function(event, error){ -    var widget = event.targetScope; -    addWidget(error, widget); -    widget.$error[error] = true; -    propertiesUpdate(widget); -  }); - -  propertiesUpdate(form); - -  function removeWidget(queue, errorKey, widget) { -    if (queue) { -      widget = widget || this; // so that we can be used in forEach; -      for (var i = 0, length = queue.length; i < length; i++) { -        if (queue[i] === widget) { -          queue.splice(i, 1); -          if (!queue.length) { -            delete $error[errorKey]; +    function removeWidget(queue, errorKey, widget) { +      if (queue) { +        widget = widget || this; // so that we can be used in forEach; +        for (var i = 0, length = queue.length; i < length; i++) { +          if (queue[i] === widget) { +            queue.splice(i, 1); +            if (!queue.length) { +              delete $error[errorKey]; +            }            }          } +        propertiesUpdate(form);        } -      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; +    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 = [];        } -    } else { -      $error[errorKey] = queue = []; +      queue.push(widget); +      propertiesUpdate(form);      } -    queue.push(widget); -    propertiesUpdate(form);    } -} -/** - * @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'); -    } -  }); +  /** +   * @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); +    propertiesUpdate(widget); -  // assign the widgetModel to the form -  if (alias && !form.hasOwnProperty(alias)) { -    form[alias] = widget; -    widget.$widgetId = alias; -  } else { -    alias = null; -  } +    // assign the widgetModel to the form +    if (alias && !form.hasOwnProperty(alias)) { +      form[alias] = widget; +      widget.$widgetId = alias; +    } else { +      alias = null; +    } -  return widget; -}; +    return widget; +  }; +} diff --git a/src/service/locale.js b/src/service/locale.js index 069a691e..aae325ae 100644 --- a/src/service/locale.js +++ b/src/service/locale.js @@ -10,61 +10,63 @@   *   * * `id` – `{string}` – locale id formatted as `languageId-countryId` (e.g. `en-us`)   */ -angularServiceInject('$locale', function() { -  return { -    id: 'en-us', +function $LocaleProvider(){ +  this.$get = function() { +    return { +      id: 'en-us', -    NUMBER_FORMATS: { -      DECIMAL_SEP: '.', -      GROUP_SEP: ',', -      PATTERNS: [ -        { // Decimal Pattern -          minInt: 1, -          minFrac: 0, -          maxFrac: 3, -          posPre: '', -          posSuf: '', -          negPre: '-', -          negSuf: '', -          gSize: 3, -          lgSize: 3 -        },{ //Currency Pattern -          minInt: 1, -          minFrac: 2, -          maxFrac: 2, -          posPre: '\u00A4', -          posSuf: '', -          negPre: '(\u00A4', -          negSuf: ')', -          gSize: 3, -          lgSize: 3 -        } -      ], -      CURRENCY_SYM: '$' -    }, +      NUMBER_FORMATS: { +        DECIMAL_SEP: '.', +        GROUP_SEP: ',', +        PATTERNS: [ +          { // Decimal Pattern +            minInt: 1, +            minFrac: 0, +            maxFrac: 3, +            posPre: '', +            posSuf: '', +            negPre: '-', +            negSuf: '', +            gSize: 3, +            lgSize: 3 +          },{ //Currency Pattern +            minInt: 1, +            minFrac: 2, +            maxFrac: 2, +            posPre: '\u00A4', +            posSuf: '', +            negPre: '(\u00A4', +            negSuf: ')', +            gSize: 3, +            lgSize: 3 +          } +        ], +        CURRENCY_SYM: '$' +      }, -    DATETIME_FORMATS: { -      MONTH: 'January,February,March,April,May,June,July,August,September,October,November,December' -              .split(','), -      SHORTMONTH:  'Jan,Feb,Mar,Apr,May,Jun,Jul,Aug,Sep,Oct,Nov,Dec'.split(','), -      DAY: 'Sunday,Monday,Tuesday,Wednesday,Thursday,Friday,Saturday'.split(','), -      SHORTDAY: 'Sun,Mon,Tue,Wed,Thu,Fri,Sat'.split(','), -      AMPMS: ['AM','PM'], -      medium: 'MMM d, y h:mm:ss a', -      short: 'M/d/yy h:mm a', -      fullDate: 'EEEE, MMMM d, y', -      longDate: 'MMMM d, y', -      mediumDate: 'MMM d, y', -      shortDate: 'M/d/yy', -      mediumTime: 'h:mm:ss a', -      shortTime: 'h:mm a' -    }, +      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'; +      pluralCat: function(num) { +        if (num === 1) { +          return 'one'; +        } +        return 'other';        } -      return 'other'; -    } +    };    }; -}); +} diff --git a/src/service/location.js b/src/service/location.js index c9b76122..7cc5c273 100644 --- a/src/service/location.js +++ b/src/service/location.js @@ -419,94 +419,99 @@ function locationGetterSetter(property, preprocess) {   *   * For more information see {@link guide/dev_guide.services.$location Developer Guide: Angular Services: Using $location}   */ -angularServiceInject('$location', function($rootScope, $browser, $sniffer, $locationConfig, $document) { -  var currentUrl, -      basePath = $browser.baseHref() || '/', -      pathPrefix = pathPrefixFromBase(basePath), -      hashPrefix = $locationConfig.hashPrefix || '', -      initUrl = $browser.url(); - -  if ($locationConfig.html5Mode) { -    if ($sniffer.history) { -      currentUrl = new LocationUrl(convertToHtml5Url(initUrl, basePath, hashPrefix), pathPrefix); -    } else { -      currentUrl = new LocationHashbangUrl(convertToHashbangUrl(initUrl, basePath, hashPrefix), -                                           hashPrefix); -    } - -    // link rewriting -    var u = currentUrl, -        absUrlPrefix = composeProtocolHostPort(u.protocol(), u.host(), u.port()) + pathPrefix; +function $LocationProvider(){ +  this.$get = ['$rootScope', '$browser', '$sniffer', '$locationConfig', '$document', +      function( $rootScope,   $browser,   $sniffer,   $locationConfig,   $document) { +    var currentUrl, +        basePath = $browser.baseHref() || '/', +        pathPrefix = pathPrefixFromBase(basePath), +        hashPrefix = $locationConfig.hashPrefix || '', +        initUrl = $browser.url(); -    $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 ($locationConfig.html5Mode) { +      if ($sniffer.history) { +        currentUrl = new LocationUrl(convertToHtml5Url(initUrl, basePath, hashPrefix), pathPrefix); +      } else { +        currentUrl = new LocationHashbangUrl(convertToHashbangUrl(initUrl, basePath, hashPrefix), +                                             hashPrefix); +      } -      if (event.ctrlKey || event.metaKey || event.which == 2) return; +      // link rewriting +      var u = currentUrl, +          absUrlPrefix = composeProtocolHostPort(u.protocol(), u.host(), u.port()) + pathPrefix; -      var elm = jqLite(event.target); +      $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 -      // traverse the DOM up to find first A tag -      while (elm.length && lowercase(elm[0].nodeName) !== 'a') { -        elm = elm.parent(); -      } +        if (event.ctrlKey || event.metaKey || event.which == 2) return; -      var href = elm.attr('href'); -      if (!href || isDefined(elm.attr('ng:ext-link')) || elm.attr('target')) return; +        var elm = jqLite(event.target); -      // remove same domain from full url links (IE7 always returns full hrefs) -      href = href.replace(absUrlPrefix, ''); +        // traverse the DOM up to find first A tag +        while (elm.length && lowercase(elm[0].nodeName) !== 'a') { +          elm = elm.parent(); +        } -      // link to different domain (or base path) -      if (href.substr(0, 4) == 'http') return; +        var href = elm.attr('href'); +        if (!href || isDefined(elm.attr('ng:ext-link')) || elm.attr('target')) return; -      // remove pathPrefix from absolute links -      href = href.indexOf(pathPrefix) === 0 ? href.substr(pathPrefix.length) : href; +        // remove same domain from full url links (IE7 always returns full hrefs) +        href = href.replace(absUrlPrefix, ''); -      currentUrl.url(href); -      $rootScope.$apply(); -      event.preventDefault(); -      // hack to work around FF6 bug 684208 when scenario runner clicks on links -      window.angular['ff-684208-preventDefault'] = true; -    }); -  } else { -    currentUrl = new LocationHashbangUrl(initUrl, hashPrefix); -  } +        // link to different domain (or base path) +        if (href.substr(0, 4) == 'http') return; -  // rewrite hashbang url <> html5 url -  if (currentUrl.absUrl() != initUrl) { -    $browser.url(currentUrl.absUrl(), true); -  } +        // remove pathPrefix from absolute links +        href = href.indexOf(pathPrefix) === 0 ? href.substr(pathPrefix.length) : href; -  // update $location when $browser url changes -  $browser.onUrlChange(function(newUrl) { -    if (currentUrl.absUrl() != newUrl) { -      currentUrl.$$parse(newUrl); -      $rootScope.$apply(); -    } -  }); - -  // update browser -  var changeCounter = 0; -  $rootScope.$watch(function() { -    if ($browser.url() != currentUrl.absUrl()) { -      changeCounter++; -      $rootScope.$evalAsync(function() { -        $browser.url(currentUrl.absUrl(), currentUrl.$$replace); -        currentUrl.$$replace = false; +        currentUrl.url(href); +        $rootScope.$apply(); +        event.preventDefault(); +        // hack to work around FF6 bug 684208 when scenario runner clicks on links +        window.angular['ff-684208-preventDefault'] = true;        }); +    } else { +      currentUrl = new LocationHashbangUrl(initUrl, hashPrefix);      } -    return changeCounter; -  }); +    // rewrite hashbang url <> html5 url +    if (currentUrl.absUrl() != initUrl) { +      $browser.url(currentUrl.absUrl(), true); +    } -  return currentUrl; -}, ['$rootScope', '$browser', '$sniffer', '$locationConfig', '$document']); +    // update $location when $browser url changes +    $browser.onUrlChange(function(newUrl) { +      if (currentUrl.absUrl() != newUrl) { +        currentUrl.$$parse(newUrl); +        $rootScope.$apply(); +      } +    }); +    // update browser +    var changeCounter = 0; +    $rootScope.$watch(function() { +      if ($browser.url() != currentUrl.absUrl()) { +        changeCounter++; +        $rootScope.$evalAsync(function() { +          $browser.url(currentUrl.absUrl(), currentUrl.$$replace); +          currentUrl.$$replace = false; +        }); +      } -angular.service('$locationConfig', function() { -  return { -    html5Mode: false, -    hashPrefix: '' +      return changeCounter; +    }); + +    return currentUrl; +}]; +} + +//TODO(misko): refactor to service +function $LocationConfigProvider(){ +  this.$get = function() { +    return { +      html5Mode: false, +      hashPrefix: '' +    };    }; -}); +} diff --git a/src/service/log.js b/src/service/log.js index 29577166..59094c42 100644 --- a/src/service/log.js +++ b/src/service/log.js @@ -34,64 +34,78 @@        </doc:scenario>      </doc:example>   */ -var $logFactory; //reference to be used only in tests -angularServiceInject("$log", $logFactory = function($window){ -  return { -    /** -     * @ngdoc method -     * @name angular.service.$log#log -     * @methodOf angular.service.$log -     * -     * @description -     * Write a log message -     */ -    log: consoleLog('log'), -    /** -     * @ngdoc method -     * @name angular.service.$log#warn -     * @methodOf angular.service.$log -     * -     * @description -     * Write a warning message -     */ -    warn: consoleLog('warn'), +function $LogProvider(){ +  this.$get = ['$window', function($window){ +    return { +      /** +       * @ngdoc method +       * @name angular.service.$log#log +       * @methodOf angular.service.$log +       * +       * @description +       * Write a log message +       */ +      log: consoleLog('log'), -    /** -     * @ngdoc method -     * @name angular.service.$log#info -     * @methodOf angular.service.$log -     * -     * @description -     * Write an information message -     */ -    info: consoleLog('info'), +      /** +       * @ngdoc method +       * @name angular.service.$log#warn +       * @methodOf angular.service.$log +       * +       * @description +       * Write a warning message +       */ +      warn: consoleLog('warn'), -    /** -     * @ngdoc method -     * @name angular.service.$log#error -     * @methodOf angular.service.$log -     * -     * @description -     * Write an error message -     */ -    error: consoleLog('error') -  }; +      /** +       * @ngdoc method +       * @name angular.service.$log#info +       * @methodOf angular.service.$log +       * +       * @description +       * Write an information message +       */ +      info: consoleLog('info'), -  function consoleLog(type) { -    var console = $window.console || {}; -    var logFn = console[type] || console.log || noop; -    if (logFn.apply) { -      return function() { -        var args = []; -        forEach(arguments, function(arg){ -          args.push(formatError(arg)); -        }); -        return logFn.apply(console, args); -      }; -    } else { -      // we are IE, in which case there is nothing we can do -      return logFn; +      /** +       * @ngdoc method +       * @name angular.service.$log#error +       * @methodOf angular.service.$log +       * +       * @description +       * Write an error message +       */ +      error: consoleLog('error') +    }; + +    function formatError(arg) { +      if (arg instanceof Error) { +        if (arg.stack) { +          arg = (arg.message && arg.stack.indexOf(arg.message) === -1) ? +                'Error: ' + arg.message + '\n' + arg.stack : arg.stack; +        } else if (arg.sourceURL) { +          arg = arg.message + '\n' + arg.sourceURL + ':' + arg.line; +        } +      } +      return arg; +    } + +    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; +      }      } -  } -}, ['$window']); +  }]; +} diff --git a/src/service/resource.js b/src/service/resource.js index 8d77a9e4..e396e521 100644 --- a/src/service/resource.js +++ b/src/service/resource.js @@ -200,7 +200,9 @@        </doc:scenario>      </doc:example>   */ -angularServiceInject('$resource', function($xhr){ -  var resource = new ResourceFactory($xhr); -  return bind(resource, resource.route); -}, ['$xhr.cache']); +function $ResourceProvider() { +  this.$get = ['$xhr.cache', function($xhr){ +    var resource = new ResourceFactory($xhr); +    return bind(resource, resource.route); +  }]; +} diff --git a/src/service/route.js b/src/service/route.js index 3918c251..f2ecbd2a 100644 --- a/src/service/route.js +++ b/src/service/route.js @@ -62,264 +62,267 @@        </doc:scenario>      </doc:example>   */ -angularServiceInject('$route', function($rootScope, $location, $routeParams) { -  /** -   * @ngdoc event -   * @name angular.service.$route#$beforeRouteChange -   * @eventOf angular.service.$route -   * @eventType broadcast on root scope -   * @description -   * Broadcasted before a route change. -   * -   * @param {Route} next Future route information. -   * @param {Route} current Current route information. -   * -   * The `Route` object extends the route definition with the following properties. -   * -   *    * `scope` - The instance of the route controller. -   *    * `params` - The current {@link angular.service.$routeParams params}. -   * -   */ +function $RouteProvider(){ +  this.$get = ['$rootScope', '$location', '$routeParams', +      function( $rootScope,  $location,  $routeParams) { +    /** +     * @ngdoc event +     * @name angular.service.$route#$beforeRouteChange +     * @eventOf angular.service.$route +     * @eventType broadcast on root scope +     * @description +     * Broadcasted before a route change. +     * +     * @param {Route} next Future route information. +     * @param {Route} current Current route information. +     * +     * The `Route` object extends the route definition with the following properties. +     * +     *    * `scope` - The instance of the route controller. +     *    * `params` - The current {@link angular.service.$routeParams params}. +     * +     */ -  /** -   * @ngdoc event -   * @name angular.service.$route#$afterRouteChange -   * @eventOf angular.service.$route -   * @eventType broadcast on root scope -   * @description -   * Broadcasted after a route change. -   * -   * @param {Route} current Current route information. -   * @param {Route} previous Previous route information. -   * -   * The `Route` object extends the route definition with the following properties. -   * -   *    * `scope` - The instance of the route controller. -   *    * `params` - The current {@link angular.service.$routeParams params}. -   * -   */ +    /** +     * @ngdoc event +     * @name angular.service.$route#$afterRouteChange +     * @eventOf angular.service.$route +     * @eventType broadcast on root scope +     * @description +     * Broadcasted after a route change. +     * +     * @param {Route} current Current route information. +     * @param {Route} previous Previous route information. +     * +     * The `Route` object extends the route definition with the following properties. +     * +     *    * `scope` - The instance of the route controller. +     *    * `params` - The current {@link angular.service.$routeParams params}. +     * +     */ -  /** -   * @ngdoc event -   * @name angular.service.$route#$routeUpdate -   * @eventOf angular.service.$route -   * @eventType emit on the current route scope -   * @description -   * -   * The `reloadOnSearch` property has been set to false, and we are reusing the same -   * instance of the Controller. -   */ +    /** +     * @ngdoc event +     * @name angular.service.$route#$routeUpdate +     * @eventOf angular.service.$route +     * @eventType emit on the current route scope +     * @description +     * +     * The `reloadOnSearch` property has been set to false, and we are reusing the same +     * instance of the Controller. +     */ -  var routes = {}, -      matcher = switchRouteMatcher, -      parentScope = $rootScope, -      dirty = 0, -      forceReload = false, -      $route = { -        routes: routes, +    var routes = {}, +        matcher = switchRouteMatcher, +        parentScope = $rootScope, +        dirty = 0, +        forceReload = false, +        $route = { +          routes: routes, -        /** -         * @ngdoc method -         * @name angular.service.$route#parent -         * @methodOf angular.service.$route -         * -         * @param {Scope} [scope=rootScope] Scope to be used as parent for newly created -         *    `$route.current.scope` scopes. -         * -         * @description -         * Sets a scope to be used as the parent scope for scopes created on route change. If not -         * set, defaults to the root scope. -         */ -        parent: function(scope) { -          if (scope) parentScope = scope; -        }, +          /** +           * @ngdoc method +           * @name angular.service.$route#parent +           * @methodOf angular.service.$route +           * +           * @param {Scope} [scope=rootScope] Scope to be used as parent for newly created +           *    `$route.current.scope` scopes. +           * +           * @description +           * Sets a scope to be used as the parent scope for scopes created on route change. If not +           * set, defaults to the root scope. +           */ +          parent: function(scope) { +            if (scope) parentScope = scope; +          }, -        /** -         * @ngdoc method -         * @name angular.service.$route#when -         * @methodOf angular.service.$route -         * -         * @param {string} path Route path (matched against `$location.hash`) -         * @param {Object} route Mapping information to be assigned to `$route.current` on route -         *    match. -         * -         *    Object properties: -         * -         *    - `controller` – `{function()=}` – Controller fn that should be associated with newly -         *      created scope. -         *    - `template` – `{string=}` – path to an html template that should be used by -         *      {@link angular.widget.ng:view ng:view} or -         *      {@link angular.widget.ng:include ng:include} widgets. -         *    - `redirectTo` – {(string|function())=} – value to update -         *      {@link angular.service.$location $location} path with and trigger route redirection. -         * -         *      If `redirectTo` is a function, it will be called with the following parameters: -         * -         *      - `{Object.<string>}` - route parameters extracted from the current -         *        `$location.path()` by applying the current route template. -         *      - `{string}` - current `$location.path()` -         *      - `{Object}` - current `$location.search()` -         * -         *      The custom `redirectTo` function is expected to return a string which will be used -         *      to update `$location.path()` and `$location.search()`. -         * -         *    - `[reloadOnSearch=true]` - {boolean=} - reload route when only $location.search() -         *    changes. -         * -         *      If the option is set to false and url in the browser changes, then -         *      $routeUpdate event is emited on the current route scope. You can use this event to -         *      react to {@link angular.service.$routeParams} changes: -         * -         *            function MyCtrl($route, $routeParams) { -         *              this.$on('$routeUpdate', function() { -         *                // do stuff with $routeParams -         *              }); -         *            } -         * -         * @returns {Object} route object -         * -         * @description -         * Adds a new route definition to the `$route` service. -         */ -        when: function(path, route) { -          var routeDef = routes[path]; -          if (!routeDef) routeDef = routes[path] = {reloadOnSearch: true}; -          if (route) extend(routeDef, route); // TODO(im): what the heck? merge two route definitions? -          dirty++; -          return routeDef; -        }, +          /** +           * @ngdoc method +           * @name angular.service.$route#when +           * @methodOf angular.service.$route +           * +           * @param {string} path Route path (matched against `$location.hash`) +           * @param {Object} route Mapping information to be assigned to `$route.current` on route +           *    match. +           * +           *    Object properties: +           * +           *    - `controller` – `{function()=}` – Controller fn that should be associated with newly +           *      created scope. +           *    - `template` – `{string=}` – path to an html template that should be used by +           *      {@link angular.widget.ng:view ng:view} or +           *      {@link angular.widget.ng:include ng:include} widgets. +           *    - `redirectTo` – {(string|function())=} – value to update +           *      {@link angular.service.$location $location} path with and trigger route redirection. +           * +           *      If `redirectTo` is a function, it will be called with the following parameters: +           * +           *      - `{Object.<string>}` - route parameters extracted from the current +           *        `$location.path()` by applying the current route template. +           *      - `{string}` - current `$location.path()` +           *      - `{Object}` - current `$location.search()` +           * +           *      The custom `redirectTo` function is expected to return a string which will be used +           *      to update `$location.path()` and `$location.search()`. +           * +           *    - `[reloadOnSearch=true]` - {boolean=} - reload route when only $location.search() +           *    changes. +           * +           *      If the option is set to false and url in the browser changes, then +           *      $routeUpdate event is emited on the current route scope. You can use this event to +           *      react to {@link angular.service.$routeParams} changes: +           * +           *            function MyCtrl($route, $routeParams) { +           *              this.$on('$routeUpdate', function() { +           *                // do stuff with $routeParams +           *              }); +           *            } +           * +           * @returns {Object} route object +           * +           * @description +           * Adds a new route definition to the `$route` service. +           */ +          when: function(path, route) { +            var routeDef = routes[path]; +            if (!routeDef) routeDef = routes[path] = {reloadOnSearch: true}; +            if (route) extend(routeDef, route); // TODO(im): what the heck? merge two route definitions? +            dirty++; +            return routeDef; +          }, -        /** -         * @ngdoc method -         * @name angular.service.$route#otherwise -         * @methodOf angular.service.$route -         * -         * @description -         * Sets route definition that will be used on route change when no other route definition -         * is matched. -         * -         * @param {Object} params Mapping information to be assigned to `$route.current`. -         */ -        otherwise: function(params) { -          $route.when(null, params); -        }, +          /** +           * @ngdoc method +           * @name angular.service.$route#otherwise +           * @methodOf angular.service.$route +           * +           * @description +           * Sets route definition that will be used on route change when no other route definition +           * is matched. +           * +           * @param {Object} params Mapping information to be assigned to `$route.current`. +           */ +          otherwise: function(params) { +            $route.when(null, params); +          }, -        /** -         * @ngdoc method -         * @name angular.service.$route#reload -         * @methodOf angular.service.$route -         * -         * @description -         * Causes `$route` service to reload (and recreate the `$route.current` scope) upon the next -         * eval even if {@link angular.service.$location $location} hasn't changed. -         */ -        reload: function() { -          dirty++; -          forceReload = true; -        } -      }; +          /** +           * @ngdoc method +           * @name angular.service.$route#reload +           * @methodOf angular.service.$route +           * +           * @description +           * Causes `$route` service to reload (and recreate the `$route.current` scope) upon the next +           * eval even if {@link angular.service.$location $location} hasn't changed. +           */ +          reload: function() { +            dirty++; +            forceReload = true; +          } +        }; -  $rootScope.$watch(function() { return dirty + $location.url(); }, updateRoute); +    $rootScope.$watch(function() { return dirty + $location.url(); }, updateRoute); -  return $route; +    return $route; -  ///////////////////////////////////////////////////// +    ///////////////////////////////////////////////////// -  function switchRouteMatcher(on, when) { -    // TODO(i): this code is convoluted and inefficient, we should construct the route matching -    //   regex only once and then reuse it -    var regex = '^' + when.replace(/([\.\\\(\)\^\$])/g, "\\$1") + '$', -        params = [], -        dst = {}; -    forEach(when.split(/\W/), function(param) { -      if (param) { -        var paramRegExp = new RegExp(":" + param + "([\\W])"); -        if (regex.match(paramRegExp)) { -          regex = regex.replace(paramRegExp, "([^\\/]*)$1"); -          params.push(param); +    function switchRouteMatcher(on, when) { +      // TODO(i): this code is convoluted and inefficient, we should construct the route matching +      //   regex only once and then reuse it +      var regex = '^' + when.replace(/([\.\\\(\)\^\$])/g, "\\$1") + '$', +          params = [], +          dst = {}; +      forEach(when.split(/\W/), function(param) { +        if (param) { +          var paramRegExp = new RegExp(":" + param + "([\\W])"); +          if (regex.match(paramRegExp)) { +            regex = regex.replace(paramRegExp, "([^\\/]*)$1"); +            params.push(param); +          }          } -      } -    }); -    var match = on.match(new RegExp(regex)); -    if (match) { -      forEach(params, function(name, index) { -        dst[name] = match[index + 1];        }); +      var match = on.match(new RegExp(regex)); +      if (match) { +        forEach(params, function(name, index) { +          dst[name] = match[index + 1]; +        }); +      } +      return match ? dst : null;      } -    return match ? dst : null; -  } -  function updateRoute() { -    var next = parseRoute(), -        last = $route.current, -        Controller; +    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(); +      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 { -            $location.url(next.redirectTo(next.pathParams, $location.path(), $location.search())) -                     .replace(); +            copy(next.params, $routeParams); +            (Controller = next.controller) && inferInjectionArgs(Controller); +            next.scope = parentScope.$new(Controller);            } -        } else { -          copy(next.params, $routeParams); -          (Controller = next.controller) && inferInjectionArgs(Controller); -          next.scope = parentScope.$new(Controller);          } +        $rootScope.$broadcast('$afterRouteChange', next, last);        } -      $rootScope.$broadcast('$afterRouteChange', next, last);      } -  } -  /** -   * @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 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(''); -  } +    /** +     * @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(''); +    } -}, ['$rootScope', '$location', '$routeParams']); +  }]; +} diff --git a/src/service/routeParams.js b/src/service/routeParams.js index a9e201a3..df89c739 100644 --- a/src/service/routeParams.js +++ b/src/service/routeParams.js @@ -25,6 +25,6 @@   *  $routeParams ==> {chapterId:1, sectionId:2, search:'moby'}   * </pre>   */ -angularService('$routeParams', function() { -  return {}; -}); +function $RouteParamsProvider() { +  this.$get = valueFn({}); +} diff --git a/src/service/scope.js b/src/service/scope.js index 94c41041..f78ac448 100644 --- a/src/service/scope.js +++ b/src/service/scope.js @@ -24,631 +24,633 @@   * implemented in the same way as watch. Watch requires return of initialization function which   * are expensive to construct.   */ - -angularServiceInject('$rootScope', function($injector, $exceptionHandler){ -  /** -   * @ngdoc function -   * @name angular.scope -   * -   * @description -   * A root scope can be created by calling {@link angular.scope angular.scope()}. Child scopes -   * are created using the {@link angular.scope.$new $new()} method. -   * (Most scopes are created automatically when compiled HTML template is executed.) -   * -   * Here is a simple scope snippet to show how you can interact with the scope. -   * <pre> -         var scope = angular.scope(); -         scope.salutation = 'Hello'; -         scope.name = 'World'; - -         expect(scope.greeting).toEqual(undefined); - -         scope.$watch('name', function() { -           this.greeting = this.salutation + ' ' + this.name + '!'; -         }); // initialize the watch - -         expect(scope.greeting).toEqual(undefined); -         scope.name = 'Misko'; -         // still old value, since watches have not been called yet -         expect(scope.greeting).toEqual(undefined); - -         scope.$digest(); // fire all  the watches -         expect(scope.greeting).toEqual('Hello Misko!'); -   * </pre> -   * -   * # Inheritance -   * A scope can inherit from a parent scope, as in this example: -   * <pre> -       var parent = angular.scope(); -       var child = parent.$new(); - -       parent.salutation = "Hello"; -       child.name = "World"; -       expect(child.salutation).toEqual('Hello'); - -       child.salutation = "Welcome"; -       expect(child.salutation).toEqual('Welcome'); -       expect(parent.salutation).toEqual('Hello'); -   * </pre> -   * -   * # Dependency Injection -   * See {@link guide/dev_guide.di dependency injection}. -   * -   * -   * @param {Object.<string, function()>=} providers Map of service factory which need to be provided -   *     for the current scope. Defaults to {@link angular.service}. -   * @param {Object.<string, *>=} instanceCache Provides pre-instantiated services which should -   *     append/override services provided by `providers`. This is handy when unit-testing and having -   *     the need to override a default service. -   * @returns {Object} Newly created scope. -   * -   */ -  function Scope() { -    this.$id = nextUid(); -    this.$$phase = this.$parent = this.$$watchers = -                   this.$$nextSibling = this.$$prevSibling = -                   this.$$childHead = this.$$childTail = null; -    this.$destructor = noop; -    this['this'] = this.$root =  this; -    this.$$asyncQueue = []; -    this.$$listeners = {}; -  } - -  /** -   * @ngdoc property -   * @name angular.scope.$id -   * @returns {number} Unique scope ID (monotonically increasing alphanumeric sequence) useful for -   *   debugging. -   */ - - -  Scope.prototype = { +function $RootScopeProvider(){ +  this.$get = ['$injector', '$exceptionHandler', +      function( $injector,  $exceptionHandler){      /**       * @ngdoc function -     * @name angular.scope.$new -     * @function +     * @name angular.scope       *       * @description -     * Creates a new child {@link angular.scope scope}. The new scope can optionally behave as a -     * controller. The parent scope will propagate the {@link angular.scope.$digest $digest()} and -     * {@link angular.scope.$digest $digest()} events. The scope can be removed from the scope -     * hierarchy using {@link angular.scope.$destroy $destroy()}. -     * -     * {@link angular.scope.$destroy $destroy()} must be called on a scope when it is desired for -     * the scope and its child scopes to be permanently detached from the parent and thus stop -     * participating in model change detection and listener notification by invoking. -     * -     * @param {function()=} Class Constructor function which the scope should be applied to the scope. -     * @param {...*} curryArguments Any additional arguments which are curried into the constructor. -     *        See {@link guide/dev_guide.di dependency injection}. -     * @returns {Object} The newly created child scope. +     * A root scope can be created by calling {@link angular.scope angular.scope()}. Child scopes +     * are created using the {@link angular.scope.$new $new()} method. +     * (Most scopes are created automatically when compiled HTML template is executed.)       * -     */ -    $new: function(Class, curryArguments) { -      var Child = function() {}; // should be anonymous; This is so that when the minifier munges -        // the name it does not become random set of chars. These will then show up as class -        // name in the debugger. -      var child; -      Child.prototype = this; -      child = new Child(); -      child['this'] = child; -      child.$$listeners = {}; -      child.$parent = this; -      child.$id = nextUid(); -      child.$$asyncQueue = []; -      child.$$phase = child.$$watchers = -        child.$$nextSibling = child.$$childHead = child.$$childTail = null; -      child.$$prevSibling = this.$$childTail; -      if (this.$$childHead) { -        this.$$childTail.$$nextSibling = child; -        this.$$childTail = child; -      } else { -        this.$$childHead = this.$$childTail = child; -      } -      // short circuit if we have no class -      if (Class) { -        // can't use forEach, we need speed! -        var ClassPrototype = Class.prototype; -        for(var key in ClassPrototype) { -          child[key] = bind(child, ClassPrototype[key]); -        } -        $injector.invoke(child, Class, curryArguments); -      } -      return child; -    }, +     * Here is a simple scope snippet to show how you can interact with the scope. +     * <pre> +           var scope = angular.scope(); +           scope.salutation = 'Hello'; +           scope.name = 'World'; -    /** -     * @ngdoc function -     * @name angular.scope.$watch -     * @function -     * -     * @description -     * Registers a `listener` callback to be executed whenever the `watchExpression` changes. -     * -     * - The `watchExpression` is called on every call to {@link angular.scope.$digest $digest()} and -     *   should return the value which will be watched. (Since {@link angular.scope.$digest $digest()} -     *   reruns when it detects changes the `watchExpression` can execute multiple times per -     *   {@link angular.scope.$digest $digest()} and should be idempotent.) -     * - The `listener` is called only when the value from the current `watchExpression` and the -     *   previous call to `watchExpression' are not equal. The inequality is determined according to -     *   {@link angular.equals} function. To save the value of the object for later comparison -     *   {@link angular.copy} function is used. It also means that watching complex options will -     *   have adverse memory and performance implications. -     * - The watch `listener` may change the model, which may trigger other `listener`s to fire. This -     *   is achieved by rerunning the watchers until no changes are detected. The rerun iteration -     *   limit is 100 to prevent infinity loop deadlock. -     * -     * -     * If you want to be notified whenever {@link angular.scope.$digest $digest} is called, -     * you can register an `watchExpression` function with no `listener`. (Since `watchExpression`, -     * can execute multiple times per {@link angular.scope.$digest $digest} cycle when a change is -     * detected, be prepared for multiple calls to your listener.) -     * -     * -     * # Example -       <pre> -         var scope = angular.scope(); -         scope.name = 'misko'; -         scope.counter = 0; - -         expect(scope.counter).toEqual(0); -         scope.$watch('name', function(scope, newValue, oldValue) { counter = counter + 1; }); -         expect(scope.counter).toEqual(0); - -         scope.$digest(); -         // no variable change -         expect(scope.counter).toEqual(0); - -         scope.name = 'adam'; -         scope.$digest(); -         expect(scope.counter).toEqual(1); -       </pre> +           expect(scope.greeting).toEqual(undefined); + +           scope.$watch('name', function() { +             this.greeting = this.salutation + ' ' + this.name + '!'; +           }); // initialize the watch + +           expect(scope.greeting).toEqual(undefined); +           scope.name = 'Misko'; +           // still old value, since watches have not been called yet +           expect(scope.greeting).toEqual(undefined); + +           scope.$digest(); // fire all  the watches +           expect(scope.greeting).toEqual('Hello Misko!'); +     * </pre>       * +     * # Inheritance +     * A scope can inherit from a parent scope, as in this example: +     * <pre> +         var parent = angular.scope(); +         var child = parent.$new(); + +         parent.salutation = "Hello"; +         child.name = "World"; +         expect(child.salutation).toEqual('Hello'); + +         child.salutation = "Welcome"; +         expect(child.salutation).toEqual('Welcome'); +         expect(parent.salutation).toEqual('Hello'); +     * </pre>       * +     * # Dependency Injection +     * See {@link guide/dev_guide.di dependency injection}.       * -     * @param {(function()|string)} watchExpression Expression that is evaluated on each -     *    {@link angular.scope.$digest $digest} cycle. A change in the return value triggers a -     *    call to the `listener`.       * -     *    - `string`: Evaluated as {@link guide/dev_guide.expressions expression} -     *    - `function(scope)`: called with current `scope` as a parameter. -     * @param {(function()|string)=} listener Callback called whenever the return value of -     *   the `watchExpression` changes. +     * @param {Object.<string, function()>=} providers Map of service factory which need to be provided +     *     for the current scope. Defaults to {@link angular.service}. +     * @param {Object.<string, *>=} instanceCache Provides pre-instantiated services which should +     *     append/override services provided by `providers`. This is handy when unit-testing and having +     *     the need to override a default service. +     * @returns {Object} Newly created scope.       * -     *    - `string`: Evaluated as {@link guide/dev_guide.expressions expression} -     *    - `function(scope, newValue, oldValue)`: called with current `scope` an previous and -     *       current values as parameters. -     * @returns {function()} Returns a deregistration function for this listener.       */ -    $watch: function(watchExp, listener) { -      var scope = this, -          get = compileToFn(watchExp, 'watch'), -          listenFn = compileToFn(listener || noop, 'listener'), -          array = scope.$$watchers, -          watcher = { -            fn: listenFn, -            last: Number.NaN, // NaN !== NaN. We used this to force $watch to fire on first run. -            get: get, -            exp: watchExp -          }; - -      if (!array) { -        array = scope.$$watchers = []; -      } -      // we use unshift since we use a while loop in $digest for speed. -      // the while loop reads in reverse order. -      array.unshift(watcher); - -      return function() { -        angularArray.remove(array, watcher); -      }; -    }, +    function Scope() { +      this.$id = nextUid(); +      this.$$phase = this.$parent = this.$$watchers = +                     this.$$nextSibling = this.$$prevSibling = +                     this.$$childHead = this.$$childTail = null; +      this.$destructor = noop; +      this['this'] = this.$root =  this; +      this.$$asyncQueue = []; +      this.$$listeners = {}; +    }      /** -     * @ngdoc function -     * @name angular.scope.$digest -     * @function -     * -     * @description -     * Process all of the {@link angular.scope.$watch watchers} of the current scope and its children. -     * Because a {@link angular.scope.$watch watcher}'s listener can change the model, the -     * `$digest()` keeps calling the {@link angular.scope.$watch watchers} until no more listeners are -     * firing. This means that it is possible to get into an infinite loop. This function will throw -     * `'Maximum iteration limit exceeded.'` if the number of iterations exceeds 100. -     * -     * Usually you don't call `$digest()` directly in -     * {@link angular.directive.ng:controller controllers} or in {@link angular.directive directives}. -     * Instead a call to {@link angular.scope.$apply $apply()} (typically from within a -     * {@link angular.directive directive}) will force a `$digest()`. -     * -     * If you want to be notified whenever `$digest()` is called, -     * you can register a `watchExpression` function  with {@link angular.scope.$watch $watch()} -     * with no `listener`. -     * -     * You may have a need to call `$digest()` from within unit-tests, to simulate the scope -     * life-cycle. -     * -     * # Example -       <pre> -         var scope = angular.scope(); -         scope.name = 'misko'; -         scope.counter = 0; - -         expect(scope.counter).toEqual(0); -         scope.$digest('name', function(scope, newValue, oldValue) { counter = counter + 1; }); -         expect(scope.counter).toEqual(0); - -         scope.$digest(); -         // no variable change -         expect(scope.counter).toEqual(0); - -         scope.name = 'adam'; -         scope.$digest(); -         expect(scope.counter).toEqual(1); -       </pre> -     * +     * @ngdoc property +     * @name angular.scope.$id +     * @returns {number} Unique scope ID (monotonically increasing alphanumeric sequence) useful for +     *   debugging.       */ -    $digest: function() { -      var watch, value, last, -          watchers, -          asyncQueue, -          length, -          dirty, ttl = 100, -          next, current, target = this, -          watchLog = []; - -      if (target.$$phase) { -        throw Error(target.$$phase + ' already in progress'); -      } -      do { -        dirty = false; -        current = target; -        do { -          current.$$phase = '$digest'; -          asyncQueue = current.$$asyncQueue; -          while(asyncQueue.length) { -            try { -              current.$eval(asyncQueue.shift()); -            } catch (e) { -              current.$service('$exceptionHandler')(e); -            } + +    Scope.prototype = { +      /** +       * @ngdoc function +       * @name angular.scope.$new +       * @function +       * +       * @description +       * Creates a new child {@link angular.scope scope}. The new scope can optionally behave as a +       * controller. The parent scope will propagate the {@link angular.scope.$digest $digest()} and +       * {@link angular.scope.$digest $digest()} events. The scope can be removed from the scope +       * hierarchy using {@link angular.scope.$destroy $destroy()}. +       * +       * {@link angular.scope.$destroy $destroy()} must be called on a scope when it is desired for +       * the scope and its child scopes to be permanently detached from the parent and thus stop +       * participating in model change detection and listener notification by invoking. +       * +       * @param {function()=} Class Constructor function which the scope should be applied to the scope. +       * @param {...*} curryArguments Any additional arguments which are curried into the constructor. +       *        See {@link guide/dev_guide.di dependency injection}. +       * @returns {Object} The newly created child scope. +       * +       */ +      $new: function(Class, curryArguments) { +        var Child = function() {}; // should be anonymous; This is so that when the minifier munges +          // the name it does not become random set of chars. These will then show up as class +          // name in the debugger. +        var child; +        Child.prototype = this; +        child = new Child(); +        child['this'] = child; +        child.$$listeners = {}; +        child.$parent = this; +        child.$id = nextUid(); +        child.$$asyncQueue = []; +        child.$$phase = child.$$watchers = +          child.$$nextSibling = child.$$childHead = child.$$childTail = null; +        child.$$prevSibling = this.$$childTail; +        if (this.$$childHead) { +          this.$$childTail.$$nextSibling = child; +          this.$$childTail = child; +        } else { +          this.$$childHead = this.$$childTail = child; +        } +        // short circuit if we have no class +        if (Class) { +          // can't use forEach, we need speed! +          var ClassPrototype = Class.prototype; +          for(var key in ClassPrototype) { +            child[key] = bind(child, ClassPrototype[key]);            } -          if ((watchers = current.$$watchers)) { -            // process our watches -            length = watchers.length; -            while (length--) { +          $injector.invoke(child, Class, curryArguments); +        } +        return child; +      }, + +      /** +       * @ngdoc function +       * @name angular.scope.$watch +       * @function +       * +       * @description +       * Registers a `listener` callback to be executed whenever the `watchExpression` changes. +       * +       * - The `watchExpression` is called on every call to {@link angular.scope.$digest $digest()} and +       *   should return the value which will be watched. (Since {@link angular.scope.$digest $digest()} +       *   reruns when it detects changes the `watchExpression` can execute multiple times per +       *   {@link angular.scope.$digest $digest()} and should be idempotent.) +       * - The `listener` is called only when the value from the current `watchExpression` and the +       *   previous call to `watchExpression' are not equal. The inequality is determined according to +       *   {@link angular.equals} function. To save the value of the object for later comparison +       *   {@link angular.copy} function is used. It also means that watching complex options will +       *   have adverse memory and performance implications. +       * - The watch `listener` may change the model, which may trigger other `listener`s to fire. This +       *   is achieved by rerunning the watchers until no changes are detected. The rerun iteration +       *   limit is 100 to prevent infinity loop deadlock. +       * +       * +       * If you want to be notified whenever {@link angular.scope.$digest $digest} is called, +       * you can register an `watchExpression` function with no `listener`. (Since `watchExpression`, +       * can execute multiple times per {@link angular.scope.$digest $digest} cycle when a change is +       * detected, be prepared for multiple calls to your listener.) +       * +       * +       * # Example +         <pre> +           var scope = angular.scope(); +           scope.name = 'misko'; +           scope.counter = 0; + +           expect(scope.counter).toEqual(0); +           scope.$watch('name', function(scope, newValue, oldValue) { counter = counter + 1; }); +           expect(scope.counter).toEqual(0); + +           scope.$digest(); +           // no variable change +           expect(scope.counter).toEqual(0); + +           scope.name = 'adam'; +           scope.$digest(); +           expect(scope.counter).toEqual(1); +         </pre> +       * +       * +       * +       * @param {(function()|string)} watchExpression Expression that is evaluated on each +       *    {@link angular.scope.$digest $digest} cycle. A change in the return value triggers a +       *    call to the `listener`. +       * +       *    - `string`: Evaluated as {@link guide/dev_guide.expressions expression} +       *    - `function(scope)`: called with current `scope` as a parameter. +       * @param {(function()|string)=} listener Callback called whenever the return value of +       *   the `watchExpression` changes. +       * +       *    - `string`: Evaluated as {@link guide/dev_guide.expressions expression} +       *    - `function(scope, newValue, oldValue)`: called with current `scope` an previous and +       *       current values as parameters. +       * @returns {function()} Returns a deregistration function for this listener. +       */ +      $watch: function(watchExp, listener) { +        var scope = this, +            get = compileToFn(watchExp, 'watch'), +            listenFn = compileToFn(listener || noop, 'listener'), +            array = scope.$$watchers, +            watcher = { +              fn: listenFn, +              last: Number.NaN, // NaN !== NaN. We used this to force $watch to fire on first run. +              get: get, +              exp: watchExp +            }; + +        if (!array) { +          array = scope.$$watchers = []; +        } +        // we use unshift since we use a while loop in $digest for speed. +        // the while loop reads in reverse order. +        array.unshift(watcher); + +        return function() { +          angularArray.remove(array, watcher); +        }; +      }, + +      /** +       * @ngdoc function +       * @name angular.scope.$digest +       * @function +       * +       * @description +       * Process all of the {@link angular.scope.$watch watchers} of the current scope and its children. +       * Because a {@link angular.scope.$watch watcher}'s listener can change the model, the +       * `$digest()` keeps calling the {@link angular.scope.$watch watchers} until no more listeners are +       * firing. This means that it is possible to get into an infinite loop. This function will throw +       * `'Maximum iteration limit exceeded.'` if the number of iterations exceeds 100. +       * +       * Usually you don't call `$digest()` directly in +       * {@link angular.directive.ng:controller controllers} or in {@link angular.directive directives}. +       * Instead a call to {@link angular.scope.$apply $apply()} (typically from within a +       * {@link angular.directive directive}) will force a `$digest()`. +       * +       * If you want to be notified whenever `$digest()` is called, +       * you can register a `watchExpression` function  with {@link angular.scope.$watch $watch()} +       * with no `listener`. +       * +       * You may have a need to call `$digest()` from within unit-tests, to simulate the scope +       * life-cycle. +       * +       * # Example +         <pre> +           var scope = angular.scope(); +           scope.name = 'misko'; +           scope.counter = 0; + +           expect(scope.counter).toEqual(0); +           scope.$digest('name', function(scope, newValue, oldValue) { counter = counter + 1; }); +           expect(scope.counter).toEqual(0); + +           scope.$digest(); +           // no variable change +           expect(scope.counter).toEqual(0); + +           scope.name = 'adam'; +           scope.$digest(); +           expect(scope.counter).toEqual(1); +         </pre> +       * +       */ +      $digest: function() { +        var watch, value, last, +            watchers, +            asyncQueue, +            length, +            dirty, ttl = 100, +            next, current, target = this, +            watchLog = []; + +        if (target.$$phase) { +          throw Error(target.$$phase + ' already in progress'); +        } +        do { + +          dirty = false; +          current = target; +          do { +            current.$$phase = '$digest'; +            asyncQueue = current.$$asyncQueue; +            while(asyncQueue.length) {                try { -                watch = watchers[length]; -                // Most common watches are on primitives, in which case we can short -                // circuit it with === operator, only when === fails do we use .equals -                if ((value = watch.get(current)) !== (last = watch.last) && !equals(value, last)) { -                  dirty = true; -                  watch.last = copy(value); -                  watch.fn(current, value, last); -                  if (ttl < 5) { -                    if (!watchLog[4-ttl]) watchLog[4-ttl] = []; -                    if (isFunction(watch.exp)) { -                      watchLog[4-ttl].push('fn: ' + (watch.exp.name || watch.exp.toString())); -                    } else { -                      watchLog[4-ttl].push(watch.exp); +                current.$eval(asyncQueue.shift()); +              } catch (e) { +                current.$service('$exceptionHandler')(e); +              } +            } +            if ((watchers = current.$$watchers)) { +              // process our watches +              length = watchers.length; +              while (length--) { +                try { +                  watch = watchers[length]; +                  // Most common watches are on primitives, in which case we can short +                  // circuit it with === operator, only when === fails do we use .equals +                  if ((value = watch.get(current)) !== (last = watch.last) && !equals(value, last)) { +                    dirty = true; +                    watch.last = copy(value); +                    watch.fn(current, value, last); +                    if (ttl < 5) { +                      if (!watchLog[4-ttl]) watchLog[4-ttl] = []; +                      if (isFunction(watch.exp)) { +                        watchLog[4-ttl].push('fn: ' + (watch.exp.name || watch.exp.toString())); +                      } else { +                        watchLog[4-ttl].push(watch.exp); +                      }                      }                    } +                } catch (e) { +                  $exceptionHandler(e);                  } -              } catch (e) { -                $exceptionHandler(e);                }              } + +            current.$$phase = null; + +            // Insanity Warning: scope depth-first traversal +            // yes, this code is a bit crazy, but it works and we have tests to prove it! +            // this piece should be kept in sync with the traversal in $broadcast +            if (!(next = (current.$$childHead || (current !== target && current.$$nextSibling)))) { +              while(current !== target && !(next = current.$$nextSibling)) { +                current = current.$parent; +              } +            } +          } while ((current = next)); + +          if(!(ttl--)) { +            throw Error('100 $digest() iterations reached. Aborting!\n' + +                'Watchers fired in the last 5 iterations: ' + toJson(watchLog)); +          } +        } while (dirty); +      }, + +      /** +       * @ngdoc function +       * @name angular.scope.$destroy +       * @function +       * +       * @description +       * Remove the current scope (and all of its children) from the parent scope. Removal implies +       * that calls to {@link angular.scope.$digest $digest()} will no longer propagate to the current +       * scope and its children. Removal also implies that the current scope is eligible for garbage +       * collection. +       * +       * The destructing scope emits an `$destroy` {@link angular.scope.$emit event}. +       * +       * The `$destroy()` is usually used by directives such as +       * {@link angular.widget.@ng:repeat ng:repeat} for managing the unrolling of the loop. +       * +       */ +      $destroy: function() { +        if (this.$root == this) return; // we can't remove the root node; +        this.$emit('$destroy'); +        var parent = this.$parent; + +        if (parent.$$childHead == this) parent.$$childHead = this.$$nextSibling; +        if (parent.$$childTail == this) parent.$$childTail = this.$$prevSibling; +        if (this.$$prevSibling) this.$$prevSibling.$$nextSibling = this.$$nextSibling; +        if (this.$$nextSibling) this.$$nextSibling.$$prevSibling = this.$$prevSibling; +      }, + +      /** +       * @ngdoc function +       * @name angular.scope.$eval +       * @function +       * +       * @description +       * Executes the `expression` on the current scope returning the result. Any exceptions in the +       * expression are propagated (uncaught). This is useful when evaluating engular expressions. +       * +       * # Example +         <pre> +           var scope = angular.scope(); +           scope.a = 1; +           scope.b = 2; + +           expect(scope.$eval('a+b')).toEqual(3); +           expect(scope.$eval(function(scope){ return scope.a + scope.b; })).toEqual(3); +         </pre> +       * +       * @param {(string|function())=} expression An angular expression to be executed. +       * +       *    - `string`: execute using the rules as defined in  {@link guide/dev_guide.expressions expression}. +       *    - `function(scope)`: execute the function with the current `scope` parameter. +       * +       * @returns {*} The result of evaluating the expression. +       */ +      $eval: function(expr) { +        var fn = isString(expr) +          ? expressionCompile(expr) +          : expr || noop; +        return fn(this); +      }, + +      /** +       * @ngdoc function +       * @name angular.scope.$evalAsync +       * @function +       * +       * @description +       * Executes the expression on the current scope at a later point in time. +       * +       * The `$evalAsync` makes no guarantees as to when the `expression` will be executed, only that: +       * +       *   - it will execute in the current script execution context (before any DOM rendering). +       *   - at least one {@link angular.scope.$digest $digest cycle} will be performed after +       *     `expression` execution. +       * +       * Any exceptions from the execution of the expression are forwarded to the +       * {@link angular.service.$exceptionHandler $exceptionHandler} service. +       * +       * @param {(string|function())=} expression An angular expression to be executed. +       * +       *    - `string`: execute using the rules as defined in  {@link guide/dev_guide.expressions expression}. +       *    - `function(scope)`: execute the function with the current `scope` parameter. +       * +       */ +      $evalAsync: function(expr) { +        this.$$asyncQueue.push(expr); +      }, + +      /** +       * @ngdoc function +       * @name angular.scope.$apply +       * @function +       * +       * @description +       * `$apply()` is used to execute an expression in angular from outside of the angular framework. +       * (For example from browser DOM events, setTimeout, XHR or third party libraries). +       * Because we are calling into the angular framework we need to perform proper scope life-cycle +       * of {@link angular.service.$exceptionHandler exception handling}, +       * {@link angular.scope.$digest executing watches}. +       * +       * ## Life cycle +       * +       * # Pseudo-Code of `$apply()` +          function $apply(expr) { +            try { +              return $eval(expr); +            } catch (e) { +              $exceptionHandler(e); +            } finally { +              $root.$digest(); +            }            } +       * +       * +       * Scope's `$apply()` method transitions through the following stages: +       * +       * 1. The {@link guide/dev_guide.expressions expression} is executed using the +       *    {@link angular.scope.$eval $eval()} method. +       * 2. Any exceptions from the execution of the expression are forwarded to the +       *    {@link angular.service.$exceptionHandler $exceptionHandler} service. +       * 3. The {@link angular.scope.$watch watch} listeners are fired immediately after the expression +       *    was executed using the {@link angular.scope.$digest $digest()} method. +       * +       * +       * @param {(string|function())=} exp An angular expression to be executed. +       * +       *    - `string`: execute using the rules as defined in {@link guide/dev_guide.expressions expression}. +       *    - `function(scope)`: execute the function with current `scope` parameter. +       * +       * @returns {*} The result of evaluating the expression. +       */ +      $apply: function(expr) { +        try { +          return this.$eval(expr); +        } catch (e) { +          $exceptionHandler(e); +        } finally { +          this.$root.$digest(); +        } +      }, + +      /** +       * @ngdoc function +       * @name angular.scope.$on +       * @function +       * +       * @description +       * Listen on events of a given type. See {@link angular.scope.$emit $emit} for discussion of +       * event life cycle. +       * +       * @param {string} name Event name to listen on. +       * @param {function(event)} listener Function to call when the event is emitted. +       * @returns {function()} Returns a deregistration function for this listener. +       * +       * The event listener function format is: `function(event)`. The `event` object passed into the +       * listener has the following attributes +       *   - `targetScope` - {Scope}: the scope on which the event was `$emit`-ed or `$broadcast`-ed. +       *   - `currentScope` - {Scope}: the current scope which is handling the event. +       *   - `name` - {string}: Name of the event. +       *   - `cancel` - {function=}: calling `cancel` function will cancel further event propagation +       *     (available only for events that were `$emit`-ed). +       */ +      $on: function(name, listener) { +        var namedListeners = this.$$listeners[name]; +        if (!namedListeners) { +          this.$$listeners[name] = namedListeners = []; +        } +        namedListeners.push(listener); + +        return function() { +          angularArray.remove(namedListeners, listener); +        }; +      }, + + +      /** +       * @ngdoc function +       * @name angular.scope.$emit +       * @function +       * +       * @description +       * Dispatches an event `name` upwards through the scope hierarchy notifying the +       * registered {@link angular.scope.$on} listeners. +       * +       * The event life cycle starts at the scope on which `$emit` was called. All +       * {@link angular.scope.$on listeners} listening for `name` event on this scope get notified. +       * Afterwards, the event traverses upwards toward the root scope and calls all registered +       * listeners along the way. The event will stop propagating if one of the listeners cancels it. +       * +       * Any exception emmited from the {@link angular.scope.$on listeners} will be passed +       * onto the {@link angular.service.$exceptionHandler $exceptionHandler} service. +       * +       * @param {string} name Event name to emit. +       * @param {...*} args Optional set of arguments which will be passed onto the event listeners. +       */ +      $emit: function(name, args) { +        var empty = [], +            namedListeners, +            canceled = false, +            scope = this, +            event = { +              name: name, +              targetScope: scope, +              cancel: function() {canceled = true;} +            }, +            listenerArgs = concat([event], arguments, 1), +            i, length; -          current.$$phase = null; +        do { +          namedListeners = scope.$$listeners[name] || empty; +          event.currentScope = scope; +          for (i=0, length=namedListeners.length; i<length; i++) { +            try { +              namedListeners[i].apply(null, listenerArgs); +              if (canceled) return; +            } catch (e) { +              $exceptionHandler(e); +            } +          } +          //traverse upwards +          scope = scope.$parent; +        } while (scope); +      }, + + +      /** +       * @ngdoc function +       * @name angular.scope.$broadcast +       * @function +       * +       * @description +       * Dispatches an event `name` downwards to all child scopes (and their children) notifying the +       * registered {@link angular.scope.$on} listeners. +       * +       * The event life cycle starts at the scope on which `$broadcast` was called. All +       * {@link angular.scope.$on listeners} listening for `name` event on this scope get notified. +       * Afterwards, the event propagates to all direct and indirect scopes of the current scope and +       * calls all registered listeners along the way. The event cannot be canceled. +       * +       * Any exception emmited from the {@link angular.scope.$on listeners} will be passed +       * onto the {@link angular.service.$exceptionHandler $exceptionHandler} service. +       * +       * @param {string} name Event name to emit. +       * @param {...*} args Optional set of arguments which will be passed onto the event listeners. +       */ +      $broadcast: function(name, args) { +        var target = this, +            current = target, +            next = target, +            event = { name: name, +                      targetScope: target }, +            listenerArgs = concat([event], arguments, 1); + +        //down while you can, then up and next sibling or up and next sibling until back at root +        do { +          current = next; +          event.currentScope = current; +          forEach(current.$$listeners[name], function(listener) { +            try { +              listener.apply(null, listenerArgs); +            } catch(e) { +              $exceptionHandler(e); +            } +          });            // Insanity Warning: scope depth-first traversal            // yes, this code is a bit crazy, but it works and we have tests to prove it! -          // this piece should be kept in sync with the traversal in $broadcast +          // this piece should be kept in sync with the traversal in $digest            if (!(next = (current.$$childHead || (current !== target && current.$$nextSibling)))) {              while(current !== target && !(next = current.$$nextSibling)) {                current = current.$parent;              }            }          } while ((current = next)); - -        if(!(ttl--)) { -          throw Error('100 $digest() iterations reached. Aborting!\n' + -              'Watchers fired in the last 5 iterations: ' + toJson(watchLog)); -        } -      } while (dirty); -    }, - -    /** -     * @ngdoc function -     * @name angular.scope.$destroy -     * @function -     * -     * @description -     * Remove the current scope (and all of its children) from the parent scope. Removal implies -     * that calls to {@link angular.scope.$digest $digest()} will no longer propagate to the current -     * scope and its children. Removal also implies that the current scope is eligible for garbage -     * collection. -     * -     * The destructing scope emits an `$destroy` {@link angular.scope.$emit event}. -     * -     * The `$destroy()` is usually used by directives such as -     * {@link angular.widget.@ng:repeat ng:repeat} for managing the unrolling of the loop. -     * -     */ -    $destroy: function() { -      if (this.$root == this) return; // we can't remove the root node; -      this.$emit('$destroy'); -      var parent = this.$parent; - -      if (parent.$$childHead == this) parent.$$childHead = this.$$nextSibling; -      if (parent.$$childTail == this) parent.$$childTail = this.$$prevSibling; -      if (this.$$prevSibling) this.$$prevSibling.$$nextSibling = this.$$nextSibling; -      if (this.$$nextSibling) this.$$nextSibling.$$prevSibling = this.$$prevSibling; -    }, - -    /** -     * @ngdoc function -     * @name angular.scope.$eval -     * @function -     * -     * @description -     * Executes the `expression` on the current scope returning the result. Any exceptions in the -     * expression are propagated (uncaught). This is useful when evaluating engular expressions. -     * -     * # Example -       <pre> -         var scope = angular.scope(); -         scope.a = 1; -         scope.b = 2; - -         expect(scope.$eval('a+b')).toEqual(3); -         expect(scope.$eval(function(scope){ return scope.a + scope.b; })).toEqual(3); -       </pre> -     * -     * @param {(string|function())=} expression An angular expression to be executed. -     * -     *    - `string`: execute using the rules as defined in  {@link guide/dev_guide.expressions expression}. -     *    - `function(scope)`: execute the function with the current `scope` parameter. -     * -     * @returns {*} The result of evaluating the expression. -     */ -    $eval: function(expr) { -      var fn = isString(expr) -        ? expressionCompile(expr) -        : expr || noop; -      return fn(this); -    }, - -    /** -     * @ngdoc function -     * @name angular.scope.$evalAsync -     * @function -     * -     * @description -     * Executes the expression on the current scope at a later point in time. -     * -     * The `$evalAsync` makes no guarantees as to when the `expression` will be executed, only that: -     * -     *   - it will execute in the current script execution context (before any DOM rendering). -     *   - at least one {@link angular.scope.$digest $digest cycle} will be performed after -     *     `expression` execution. -     * -     * Any exceptions from the execution of the expression are forwarded to the -     * {@link angular.service.$exceptionHandler $exceptionHandler} service. -     * -     * @param {(string|function())=} expression An angular expression to be executed. -     * -     *    - `string`: execute using the rules as defined in  {@link guide/dev_guide.expressions expression}. -     *    - `function(scope)`: execute the function with the current `scope` parameter. -     * -     */ -    $evalAsync: function(expr) { -      this.$$asyncQueue.push(expr); -    }, - -    /** -     * @ngdoc function -     * @name angular.scope.$apply -     * @function -     * -     * @description -     * `$apply()` is used to execute an expression in angular from outside of the angular framework. -     * (For example from browser DOM events, setTimeout, XHR or third party libraries). -     * Because we are calling into the angular framework we need to perform proper scope life-cycle -     * of {@link angular.service.$exceptionHandler exception handling}, -     * {@link angular.scope.$digest executing watches}. -     * -     * ## Life cycle -     * -     * # Pseudo-Code of `$apply()` -        function $apply(expr) { -          try { -            return $eval(expr); -          } catch (e) { -            $exceptionHandler(e); -          } finally { -            $root.$digest(); -          } -        } -     * -     * -     * Scope's `$apply()` method transitions through the following stages: -     * -     * 1. The {@link guide/dev_guide.expressions expression} is executed using the -     *    {@link angular.scope.$eval $eval()} method. -     * 2. Any exceptions from the execution of the expression are forwarded to the -     *    {@link angular.service.$exceptionHandler $exceptionHandler} service. -     * 3. The {@link angular.scope.$watch watch} listeners are fired immediately after the expression -     *    was executed using the {@link angular.scope.$digest $digest()} method. -     * -     * -     * @param {(string|function())=} exp An angular expression to be executed. -     * -     *    - `string`: execute using the rules as defined in {@link guide/dev_guide.expressions expression}. -     *    - `function(scope)`: execute the function with current `scope` parameter. -     * -     * @returns {*} The result of evaluating the expression. -     */ -    $apply: function(expr) { -      try { -        return this.$eval(expr); -      } catch (e) { -        $exceptionHandler(e); -      } finally { -        this.$root.$digest(); -      } -    }, - -    /** -     * @ngdoc function -     * @name angular.scope.$on -     * @function -     * -     * @description -     * Listen on events of a given type. See {@link angular.scope.$emit $emit} for discussion of -     * event life cycle. -     * -     * @param {string} name Event name to listen on. -     * @param {function(event)} listener Function to call when the event is emitted. -     * @returns {function()} Returns a deregistration function for this listener. -     * -     * The event listener function format is: `function(event)`. The `event` object passed into the -     * listener has the following attributes -     *   - `targetScope` - {Scope}: the scope on which the event was `$emit`-ed or `$broadcast`-ed. -     *   - `currentScope` - {Scope}: the current scope which is handling the event. -     *   - `name` - {string}: Name of the event. -     *   - `cancel` - {function=}: calling `cancel` function will cancel further event propagation -     *     (available only for events that were `$emit`-ed). -     */ -    $on: function(name, listener) { -      var namedListeners = this.$$listeners[name]; -      if (!namedListeners) { -        this.$$listeners[name] = namedListeners = [];        } -      namedListeners.push(listener); - -      return function() { -        angularArray.remove(namedListeners, listener); -      }; -    }, - - -    /** -     * @ngdoc function -     * @name angular.scope.$emit -     * @function -     * -     * @description -     * Dispatches an event `name` upwards through the scope hierarchy notifying the -     * registered {@link angular.scope.$on} listeners. -     * -     * The event life cycle starts at the scope on which `$emit` was called. All -     * {@link angular.scope.$on listeners} listening for `name` event on this scope get notified. -     * Afterwards, the event traverses upwards toward the root scope and calls all registered -     * listeners along the way. The event will stop propagating if one of the listeners cancels it. -     * -     * Any exception emmited from the {@link angular.scope.$on listeners} will be passed -     * onto the {@link angular.service.$exceptionHandler $exceptionHandler} service. -     * -     * @param {string} name Event name to emit. -     * @param {...*} args Optional set of arguments which will be passed onto the event listeners. -     */ -    $emit: function(name, args) { -      var empty = [], -          namedListeners, -          canceled = false, -          scope = this, -          event = { -            name: name, -            targetScope: scope, -            cancel: function() {canceled = true;} -          }, -          listenerArgs = concat([event], arguments, 1), -          i, length; - -      do { -        namedListeners = scope.$$listeners[name] || empty; -        event.currentScope = scope; -        for (i=0, length=namedListeners.length; i<length; i++) { -          try { -            namedListeners[i].apply(null, listenerArgs); -            if (canceled) return; -          } catch (e) { -            $exceptionHandler(e); -          } -        } -        //traverse upwards -        scope = scope.$parent; -      } while (scope); -    }, - - -    /** -     * @ngdoc function -     * @name angular.scope.$broadcast -     * @function -     * -     * @description -     * Dispatches an event `name` downwards to all child scopes (and their children) notifying the -     * registered {@link angular.scope.$on} listeners. -     * -     * The event life cycle starts at the scope on which `$broadcast` was called. All -     * {@link angular.scope.$on listeners} listening for `name` event on this scope get notified. -     * Afterwards, the event propagates to all direct and indirect scopes of the current scope and -     * calls all registered listeners along the way. The event cannot be canceled. -     * -     * Any exception emmited from the {@link angular.scope.$on listeners} will be passed -     * onto the {@link angular.service.$exceptionHandler $exceptionHandler} service. -     * -     * @param {string} name Event name to emit. -     * @param {...*} args Optional set of arguments which will be passed onto the event listeners. -     */ -    $broadcast: function(name, args) { -      var target = this, -          current = target, -          next = target, -          event = { name: name, -                    targetScope: target }, -          listenerArgs = concat([event], arguments, 1); - -      //down while you can, then up and next sibling or up and next sibling until back at root -      do { -        current = next; -        event.currentScope = current; -        forEach(current.$$listeners[name], function(listener) { -          try { -            listener.apply(null, listenerArgs); -          } catch(e) { -            $exceptionHandler(e); -          } -        }); - -        // Insanity Warning: scope depth-first traversal -        // yes, this code is a bit crazy, but it works and we have tests to prove it! -        // this piece should be kept in sync with the traversal in $digest -        if (!(next = (current.$$childHead || (current !== target && current.$$nextSibling)))) { -          while(current !== target && !(next = current.$$nextSibling)) { -            current = current.$parent; -          } -        } -      } while ((current = next)); +    }; + +    // TODO(misko): remove this; +    var scope = new Scope(); +    scope.$service = $injector; +    return scope; + +    function compileToFn(exp, name) { +      var fn = isString(exp) +        ? expressionCompile(exp) +        : exp; +      assertArgFn(fn, name); +      return fn;      } -  }; - -  // TODO(misko): remove this; -  var scope = new Scope(); -  scope.$service = $injector; -  return scope; - -  function compileToFn(exp, name) { -    var fn = isString(exp) -      ? expressionCompile(exp) -      : exp; -    assertArgFn(fn, name); -    return fn; -  } -}, ['$injector', '$exceptionHandler']); +  }]; +} diff --git a/src/service/sniffer.js b/src/service/sniffer.js index ed6d688d..8f6efeef 100644 --- a/src/service/sniffer.js +++ b/src/service/sniffer.js @@ -11,13 +11,15 @@   * @description   * This is very simple implementation of testing browser's features.   */ -angularServiceInject('$sniffer', function($window) { -  if ($window.Modernizr) return $window.Modernizr; +function $SnifferProvider(){ +  this.$get = ['$window', function($window){ +    if ($window.Modernizr) return $window.Modernizr; -  return { -    history: !!($window.history && $window.history.pushState), -    hashchange: 'onhashchange' in $window && -                // IE8 compatible mode lies -                (!$window.document.documentMode || $window.document.documentMode > 7) -  }; -}, ['$window']); +    return { +      history: !!($window.history && $window.history.pushState), +      hashchange: 'onhashchange' in $window && +                  // IE8 compatible mode lies +                  (!$window.document.documentMode || $window.document.documentMode > 7) +    }; +  }]; +} diff --git a/src/service/window.js b/src/service/window.js index bc264e30..d5c62146 100644 --- a/src/service/window.js +++ b/src/service/window.js @@ -23,4 +23,6 @@       </doc:scenario>     </doc:example>   */ -angularServiceInject("$window", bind(window, identity, window)); +function $WindowProvider(){ +  this.$get = valueFn(window); +} diff --git a/src/service/xhr.bulk.js b/src/service/xhr.bulk.js index bf5a1d95..b75f4046 100644 --- a/src/service/xhr.bulk.js +++ b/src/service/xhr.bulk.js @@ -11,76 +11,79 @@   *   * @example   */ -angularServiceInject('$xhr.bulk', function($rootScope, $xhr, $error, $log){ -  var requests = []; -  function bulkXHR(method, url, post, success, error) { -    if (isFunction(post)) { -      error = success; -      success = post; -      post = null; -    } -    var currentQueue; -    forEach(bulkXHR.urls, function(queue){ -      if (isFunction(queue.match) ? queue.match(url) : queue.match.exec(url)) { -        currentQueue = queue; +function $XhrBulkProvider() { +  this.$get = ['$rootScope', '$xhr', '$xhr.error', '$log', +      function( $rootScope,   $xhr,   $error,       $log) { +    var requests = []; +    function bulkXHR(method, url, post, success, error) { +      if (isFunction(post)) { +        error = success; +        success = post; +        post = null; +      } +      var currentQueue; +      forEach(bulkXHR.urls, function(queue){ +        if (isFunction(queue.match) ? queue.match(url) : queue.match.exec(url)) { +          currentQueue = queue; +        } +      }); +      if (currentQueue) { +        if (!currentQueue.requests) currentQueue.requests = []; +        var request = { +            method: method, +            url: url, +            data: post, +            success: success}; +        if (error) request.error = error; +        currentQueue.requests.push(request); +      } else { +        $xhr(method, url, post, success, error);        } -    }); -    if (currentQueue) { -      if (!currentQueue.requests) currentQueue.requests = []; -      var request = { -          method: method, -          url: url, -          data: post, -          success: success}; -      if (error) request.error = error; -      currentQueue.requests.push(request); -    } else { -      $xhr(method, url, post, success, error);      } -  } -  bulkXHR.urls = {}; -  bulkXHR.flush = function(success, errorback) { -    assertArgFn(success = success || noop, 0); -    assertArgFn(errorback = errorback || noop, 1); -    forEach(bulkXHR.urls, function(queue, url) { -      var currentRequests = queue.requests; -      if (currentRequests && currentRequests.length) { -        queue.requests = []; -        queue.callbacks = []; -        $xhr('POST', url, {requests: currentRequests}, -          function(code, response) { -            forEach(response, function(response, i) { -              try { -                if (response.status == 200) { -                  (currentRequests[i].success || noop)(response.status, response.response); -                } else if (isFunction(currentRequests[i].error)) { -                    currentRequests[i].error(response.status, response.response); -                } else { -                  $error(currentRequests[i], response); +    bulkXHR.urls = {}; +    bulkXHR.flush = function(success, errorback) { +      assertArgFn(success = success || noop, 0); +      assertArgFn(errorback = errorback || noop, 1); +      forEach(bulkXHR.urls, function(queue, url) { +        var currentRequests = queue.requests; +        if (currentRequests && currentRequests.length) { +          queue.requests = []; +          queue.callbacks = []; +          $xhr('POST', url, {requests: currentRequests}, +            function(code, response) { +              forEach(response, function(response, i) { +                try { +                  if (response.status == 200) { +                    (currentRequests[i].success || noop)(response.status, response.response); +                  } else if (isFunction(currentRequests[i].error)) { +                      currentRequests[i].error(response.status, response.response); +                  } else { +                    $error(currentRequests[i], response); +                  } +                } catch(e) { +                  $log.error(e);                  } -              } 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); +              }); +              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);                  } -              } catch(e) { -                $log.error(e); -              } +              }); +              noop();              }); -            noop(); -          }); -      } -    }); -  }; -  $rootScope.$watch(function() { bulkXHR.flush(); }); -  return bulkXHR; -}, ['$rootScope', '$xhr', '$xhr.error', '$log']); +        } +      }); +    }; +    $rootScope.$watch(function() { bulkXHR.flush(); }); +    return bulkXHR; +  }]; +} diff --git a/src/service/xhr.cache.js b/src/service/xhr.cache.js index 335c481d..a448bfe0 100644 --- a/src/service/xhr.cache.js +++ b/src/service/xhr.cache.js @@ -28,87 +28,91 @@   *   cached entry. The `success` function will be called when the response is received.   * @param {boolean=} [sync=false] in case of cache hit execute `success` synchronously.   */ -angularServiceInject('$xhr.cache', function($xhr, $defer, $error, $log) { -  var inflight = {}; -  function cache(method, url, post, success, error, verifyCache, sync) { -    if (isFunction(post)) { -      if (!isFunction(success)) { -        verifyCache = success; -        sync = error; -        error = null; -      } else { +function $XhrCacheProvider() { +  this.$get = ['$xhr.bulk', '$defer', '$xhr.error', '$log', +       function($xhr,        $defer,   $error,       $log) { +    var inflight = {}; +    function cache(method, url, post, success, error, verifyCache, sync) { +      if (isFunction(post)) { +        if (!isFunction(success)) { +          verifyCache = success; +          sync = error; +          error = null; +        } else { +          sync = verifyCache; +          verifyCache = error; +          error = success; +        } +        success = post; +        post = null; +      } else if (!isFunction(error)) {          sync = verifyCache;          verifyCache = error; -        error = success; +        error = null;        } -      success = post; -      post = null; -    } else if (!isFunction(error)) { -      sync = verifyCache; -      verifyCache = error; -      error = null; -    } -    if (method == 'GET') { -      var data, dataCached; -      if ((dataCached = cache.data[url])) { +      if (method == 'GET') { +        var data, dataCached; +        if ((dataCached = cache.data[url])) { -        if (sync) { -          success(200, copy(dataCached.value)); -        } else { -          $defer(function() { success(200, copy(dataCached.value)); }); -        } +          if (sync) { +            success(200, copy(dataCached.value)); +          } else { +            $defer(function() { success(200, copy(dataCached.value)); }); +          } -        if (!verifyCache) -          return; -      } +          if (!verifyCache) +            return; +        } -      if ((data = inflight[url])) { -        data.successes.push(success); -        data.errors.push(error); -      } else { -        inflight[url] = {successes: [success], errors: [error]}; -        cache.delegate(method, url, post, -          function(status, response) { -            if (status == 200) -              cache.data[url] = {value: response}; -            var successes = inflight[url].successes; -            delete inflight[url]; -            forEach(successes, function(success) { -              try { -                (success||noop)(status, copy(response)); -              } catch(e) { -                $log.error(e); -              } -            }); -          }, -          function(status, response) { -            var errors = inflight[url].errors, -                successes = inflight[url].successes; -            delete inflight[url]; +        if ((data = inflight[url])) { +          data.successes.push(success); +          data.errors.push(error); +        } else { +          inflight[url] = {successes: [success], errors: [error]}; +          cache.delegate(method, url, post, +            function(status, response) { +              if (status == 200) +                cache.data[url] = {value: response}; +              var successes = inflight[url].successes; +              delete inflight[url]; +              forEach(successes, function(success) { +                try { +                  (success||noop)(status, copy(response)); +                } catch(e) { +                  $log.error(e); +                } +              }); +            }, +            function(status, response) { +              var errors = inflight[url].errors, +                  successes = inflight[url].successes; +              delete inflight[url]; -            forEach(errors, function(error, i) { -              try { -                if (isFunction(error)) { -                  error(status, copy(response)); -                } else { -                  $error( -                    {method: method, url: url, data: post, success: successes[i]}, -                    {status: status, body: response}); +              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);                  } -              } catch(e) { -                $log.error(e); -              } +              });              }); -          }); -      } +        } -    } else { -      cache.data = {}; -      cache.delegate(method, url, post, success, error); +      } else { +        cache.data = {}; +        cache.delegate(method, url, post, success, error); +      }      } -  } -  cache.data = {}; -  cache.delegate = $xhr; -  return cache; -}, ['$xhr.bulk', '$defer', '$xhr.error', '$log']); +    cache.data = {}; +    cache.delegate = $xhr; +    return cache; +  }]; + +} diff --git a/src/service/xhr.error.js b/src/service/xhr.error.js index 01fb8fff..6bd2ac78 100644 --- a/src/service/xhr.error.js +++ b/src/service/xhr.error.js @@ -35,8 +35,10 @@        </doc:source>      </doc:example>   */ -angularServiceInject('$xhr.error', function($log){ -  return function(request, response){ -    $log.error('ERROR: XHR: ' + request.url, request, response); -  }; -}, ['$log']); +function $XhrErrorProvider() { +  this.$get = ['$log', function($log) { +    return function(request, response){ +      $log.error('ERROR: XHR: ' + request.url, request, response); +    }; +  }]; +} diff --git a/src/service/xhr.js b/src/service/xhr.js index b2a5bdf2..bd1217b9 100644 --- a/src/service/xhr.js +++ b/src/service/xhr.js @@ -171,58 +171,61 @@       </doc:scenario>     </doc:example>   */ -angularServiceInject('$xhr', function($rootScope, $browser, $error, $log){ -  var xhrHeaderDefaults = { -    common: { -      "Accept": "application/json, text/plain, */*", -      "X-Requested-With": "XMLHttpRequest" -    }, -    post: {'Content-Type': 'application/x-www-form-urlencoded'}, -    get: {},      // all these empty properties are needed so that client apps can just do: -    head: {},     // $xhr.defaults.headers.head.foo="bar" without having to create head object -    put: {},      // it also means that if we add a header for these methods in the future, it -    'delete': {}, // won't be easily silently lost due to an object assignment. -    patch: {} -  }; +function $XhrProvider() { +  this.$get = ['$rootScope', '$browser', '$xhr.error', '$log', +      function( $rootScope,   $browser,   $error,       $log){ +    var xhrHeaderDefaults = { +      common: { +        "Accept": "application/json, text/plain, */*", +        "X-Requested-With": "XMLHttpRequest" +      }, +      post: {'Content-Type': 'application/x-www-form-urlencoded'}, +      get: {},      // all these empty properties are needed so that client apps can just do: +      head: {},     // $xhr.defaults.headers.head.foo="bar" without having to create head object +      put: {},      // it also means that if we add a header for these methods in the future, it +      'delete': {}, // won't be easily silently lost due to an object assignment. +      patch: {} +    }; -  function xhr(method, url, post, success, error) { -    if (isFunction(post)) { -      error = success; -      success = post; -      post = null; -    } -    if (post && isObject(post)) { -      post = toJson(post); -    } +    function xhr(method, url, post, success, error) { +      if (isFunction(post)) { +        error = success; +        success = post; +        post = null; +      } +      if (post && isObject(post)) { +        post = toJson(post); +      } -    $browser.xhr(method, url, post, function(code, response){ -      try { -        if (isString(response)) { -          if (response.match(/^\)\]\}',\n/)) response=response.substr(6); -          if (/^\s*[\[\{]/.exec(response) && /[\}\]]\s*$/.exec(response)) { -            response = fromJson(response, true); +      $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);          } -        $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)])); -  } +      }, extend({'X-XSRF-TOKEN': $browser.cookies()['XSRF-TOKEN']}, +                xhrHeaderDefaults.common, +                xhrHeaderDefaults[lowercase(method)])); +    } -  xhr.defaults = {headers: xhrHeaderDefaults}; +    xhr.defaults = {headers: xhrHeaderDefaults}; -  return xhr; -}, ['$rootScope', '$browser', '$xhr.error', '$log']); +    return xhr; +  }]; +} diff --git a/test/AngularSpec.js b/test/AngularSpec.js index 8902c2a9..6229120d 100644 --- a/test/AngularSpec.js +++ b/test/AngularSpec.js @@ -382,25 +382,6 @@ describe('angular', function() {        expect(fake).toEqual('new');      })); -    it('should not preserve properties on override', function() { -      angular.service('fake', {$one: true}, {$two: true}, {three: true}); -      var result = angular.service('fake', {$four: true}); - -      expect(result.$one).toBeUndefined(); -      expect(result.$two).toBeUndefined(); -      expect(result.three).toBeUndefined(); -      expect(result.$four).toBe(true); -    }); - -    it('should not preserve non-angular properties on override', function() { -      angular.service('fake', {one: true}, {two: true}); -      var result = angular.service('fake', {third: true}); - -      expect(result.one).not.toBeDefined(); -      expect(result.two).not.toBeDefined(); -      expect(result.third).toBeTruthy(); -    }); -      it('should inject dependencies specified by $inject and ignore function argument name', function() {        expect(angular.injector(function($provide){          $provide.factory('svc1', function() { return 'svc1'; }); diff --git a/test/BinderSpec.js b/test/BinderSpec.js index 495d98a8..6ab94401 100644 --- a/test/BinderSpec.js +++ b/test/BinderSpec.js @@ -222,8 +222,8 @@ describe('Binder', function() {    }));    it('IfTextBindingThrowsErrorDecorateTheSpan', inject( -    function($provide){ -      $provide.factory('$exceptionHandler', $exceptionHandlerMockFactory); +    function($exceptionHandlerProvider){ +      $exceptionHandlerProvider.mode('log');      },      function($rootScope, $exceptionHandler, $compile) {        $compile('<div>{{error.throw()}}</div>', null, true)($rootScope); @@ -245,8 +245,8 @@ describe('Binder', function() {      })    ); -  it('IfAttrBindingThrowsErrorDecorateTheAttribute', inject(function($provide){ -    $provide.factory('$exceptionHandler', $exceptionHandlerMockFactory); +  it('IfAttrBindingThrowsErrorDecorateTheAttribute', inject(function($exceptionHandlerProvider){ +    $exceptionHandlerProvider.mode('log');    }, function($rootScope, $exceptionHandler, $compile) {      $compile('<div attr="before {{error.throw()}} after"></div>', null, true)($rootScope);      var errorLogs = $exceptionHandler.errors; @@ -387,8 +387,8 @@ describe('Binder', function() {    }));    it('ActionOnAHrefThrowsError', inject( -    function($provide){ -      $provide.factory('$exceptionHandler', $exceptionHandlerMockFactory); +    function($exceptionHandlerProvider){ +      $exceptionHandlerProvider.mode('log');      },      function($rootScope, $exceptionHandler, $compile) {        var input = $compile('<a ng:click="action()">Add Phone</a>')($rootScope); @@ -471,8 +471,8 @@ describe('Binder', function() {    }));    it('ItShouldDisplayErrorWhenActionIsSyntacticlyIncorrect', inject( -    function($provide){ -      $provide.factory('$exceptionHandler', $exceptionHandlerMockFactory); +    function($exceptionHandlerProvider){ +      $exceptionHandlerProvider.mode('log');      },      function($rootScope, $exceptionHandler, $log, $compile) {        var element = $compile( diff --git a/test/FiltersSpec.js b/test/FiltersSpec.js index 4b69bf03..8c567441 100644 --- a/test/FiltersSpec.js +++ b/test/FiltersSpec.js @@ -200,10 +200,10 @@ describe('filter', function() {    describe('date', function() { -    var morning  = new TzDate(+5, '2010-09-03T12:05:08.000Z'); //7am -    var noon =     new TzDate(+5, '2010-09-03T17:05:08.000Z'); //12pm -    var midnight = new TzDate(+5, '2010-09-03T05:05:08.000Z'); //12am -    var earlyDate = new TzDate(+5, '0001-09-03T05:05:08.000Z'); +    var morning  = new angular.mock.TzDate(+5, '2010-09-03T12:05:08.000Z'); //7am +    var noon =     new angular.mock.TzDate(+5, '2010-09-03T17:05:08.000Z'); //12pm +    var midnight = new angular.mock.TzDate(+5, '2010-09-03T05:05:08.000Z'); //12am +    var earlyDate = new angular.mock.TzDate(+5, '0001-09-03T05:05:08.000Z');      var context, date; diff --git a/test/InjectorSpec.js b/test/InjectorSpec.js index 3ba819b8..ac412c39 100644 --- a/test/InjectorSpec.js +++ b/test/InjectorSpec.js @@ -239,6 +239,12 @@ describe('injector', function() {        expect($injector('a')).toEqual('abc');      }); +    it('should error on invalid madule name', function(){ +      expect(function(){ +        createInjector(['IDontExist'], {}); +      }).toThrow("Module 'IDontExist' is not defined!"); +    }); +      describe('$provide', function() {        describe('value', function(){ diff --git a/test/angular-mocksSpec.js b/test/angular-mocksSpec.js index 205461af..343be966 100644 --- a/test/angular-mocksSpec.js +++ b/test/angular-mocksSpec.js @@ -8,90 +8,90 @@ describe('mocks', function() {      }      it('should take millis as constructor argument', function() { -      expect(new TzDate(0, 0).getTime()).toBe(0); -      expect(new TzDate(0, 1283555108000).getTime()).toBe(1283555108000); +      expect(new angular.mock.TzDate(0, 0).getTime()).toBe(0); +      expect(new angular.mock.TzDate(0, 1283555108000).getTime()).toBe(1283555108000);      });      it('should take dateString as constructor argument', function() { -      expect(new TzDate(0, '1970-01-01T00:00:00.000Z').getTime()).toBe(0); -      expect(new TzDate(0, '2010-09-03T23:05:08.023Z').getTime()).toBe(1283555108023); +      expect(new angular.mock.TzDate(0, '1970-01-01T00:00:00.000Z').getTime()).toBe(0); +      expect(new angular.mock.TzDate(0, '2010-09-03T23:05:08.023Z').getTime()).toBe(1283555108023);      });      it('should fake getLocalDateString method', function() {        //0 in -3h -      var t0 = new TzDate(-3, 0); +      var t0 = new angular.mock.TzDate(-3, 0);        expect(t0.toLocaleDateString()).toMatch('1970');        //0 in +0h -      var t1 = new TzDate(0, 0); +      var t1 = new angular.mock.TzDate(0, 0);        expect(t1.toLocaleDateString()).toMatch('1970');        //0 in +3h -      var t2 = new TzDate(3, 0); +      var t2 = new angular.mock.TzDate(3, 0);        expect(t2.toLocaleDateString()).toMatch('1969');      });      it('should fake getHours method', function() {        //0 in -3h -      var t0 = new TzDate(-3, 0); +      var t0 = new angular.mock.TzDate(-3, 0);        expect(t0.getHours()).toBe(3);        //0 in +0h -      var t1 = new TzDate(0, 0); +      var t1 = new angular.mock.TzDate(0, 0);        expect(t1.getHours()).toBe(0);        //0 in +3h -      var t2 = new TzDate(3, 0); +      var t2 = new angular.mock.TzDate(3, 0);        expect(t2.getHours()).toMatch(21);      });      it('should fake getMinutes method', function() {        //0:15 in -3h -      var t0 = new TzDate(-3, minutes(15)); +      var t0 = new angular.mock.TzDate(-3, minutes(15));        expect(t0.getMinutes()).toBe(15);        //0:15 in -3.25h -      var t0a = new TzDate(-3.25, minutes(15)); +      var t0a = new angular.mock.TzDate(-3.25, minutes(15));        expect(t0a.getMinutes()).toBe(30);        //0 in +0h -      var t1 = new TzDate(0, minutes(0)); +      var t1 = new angular.mock.TzDate(0, minutes(0));        expect(t1.getMinutes()).toBe(0);        //0:15 in +0h -      var t1a = new TzDate(0, minutes(15)); +      var t1a = new angular.mock.TzDate(0, minutes(15));        expect(t1a.getMinutes()).toBe(15);        //0:15 in +3h -      var t2 = new TzDate(3, minutes(15)); +      var t2 = new angular.mock.TzDate(3, minutes(15));        expect(t2.getMinutes()).toMatch(15);        //0:15 in +3.25h -      var t2a = new TzDate(3.25, minutes(15)); +      var t2a = new angular.mock.TzDate(3.25, minutes(15));        expect(t2a.getMinutes()).toMatch(0);      });      it('should fake getSeconds method', function() {        //0 in -3h -      var t0 = new TzDate(-3, 0); +      var t0 = new angular.mock.TzDate(-3, 0);        expect(t0.getSeconds()).toBe(0);        //0 in +0h -      var t1 = new TzDate(0, 0); +      var t1 = new angular.mock.TzDate(0, 0);        expect(t1.getSeconds()).toBe(0);        //0 in +3h -      var t2 = new TzDate(3, 0); +      var t2 = new angular.mock.TzDate(3, 0);        expect(t2.getSeconds()).toMatch(0);      });      it('should create a date representing new year in Bratislava', function() { -      var newYearInBratislava = new TzDate(-1, '2009-12-31T23:00:00.000Z'); +      var newYearInBratislava = new angular.mock.TzDate(-1, '2009-12-31T23:00:00.000Z');        expect(newYearInBratislava.getTimezoneOffset()).toBe(-60);        expect(newYearInBratislava.getFullYear()).toBe(2010);        expect(newYearInBratislava.getMonth()).toBe(0); @@ -103,7 +103,7 @@ describe('mocks', function() {      it('should delegate all the UTC methods to the original UTC Date object', function() {        //from when created from string -      var date1 = new TzDate(-1, '2009-12-31T23:00:00.000Z'); +      var date1 = new angular.mock.TzDate(-1, '2009-12-31T23:00:00.000Z');        expect(date1.getUTCFullYear()).toBe(2009);        expect(date1.getUTCMonth()).toBe(11);        expect(date1.getUTCDate()).toBe(31); @@ -113,7 +113,7 @@ describe('mocks', function() {        //from when created from millis -      var date2 = new TzDate(-1, angular.String.toDate('2009-12-31T23:00:00.000Z').getTime()); +      var date2 = new angular.mock.TzDate(-1, angular.String.toDate('2009-12-31T23:00:00.000Z').getTime());        expect(date2.getUTCFullYear()).toBe(2009);        expect(date2.getUTCMonth()).toBe(11);        expect(date2.getUTCDate()).toBe(31); @@ -124,16 +124,20 @@ describe('mocks', function() {      it('should throw error when no third param but toString called', function() { -      expect(function() { new TzDate(0,0).toString(); }). +      expect(function() { new angular.mock.TzDate(0,0).toString(); }).                             toThrow('Method \'toString\' is not implemented in the TzDate mock');      });    }); -  describe('$log mock', function() { +  describe('$log', function() {      var $log; -    beforeEach(function() { -      $log = MockLogFactory(); -    }); +    beforeEach(inject(['$log', function(log) { +      $log = log; +    }])); + +    afterEach(inject(function($log){ +      $log.reset(); +    }));      it('should provide log method', function() {        expect(function() { $log.log(''); }).not.toThrow(); @@ -170,14 +174,48 @@ describe('mocks', function() {        $log.error('fake log');        expect($log.error.logs).toContain(['fake log']);      }); + +    it('should assertEmpty', function(){ +      try { +        $log.error(Error('MyError')); +        $log.warn(Error('MyWarn')); +        $log.info(Error('MyInfo')); +        $log.log(Error('MyLog')); +        $log.assertEmpty(); +      } catch (error) { +        error = error.message || error; +        expect(error).toMatch(/Error: MyError/m); +        expect(error).toMatch(/Error: MyWarn/m); +        expect(error).toMatch(/Error: MyInfo/m); +        expect(error).toMatch(/Error: MyLog/m); +      } finally { +        $log.reset(); +      } +    }); + +    it('should reset state', function(){ +      $log.error(Error('MyError')); +      $log.warn(Error('MyWarn')); +      $log.info(Error('MyInfo')); +      $log.log(Error('MyLog')); +      $log.reset(); +      var passed = false; +      try { +        $log.assertEmpty(); // should not throw error! +        passed = true; +      } catch (e) { +        passed = e; +      } +      expect(passed).toBe(true); +    });    });    describe('defer', function() {      var browser, log; -    beforeEach(function() { -      browser = new MockBrowser(); +    beforeEach(inject(function($browser) { +      browser = $browser;        log = ''; -    }); +    }));      function logFn(text){ return function() {          log += text +';'; @@ -229,5 +267,20 @@ describe('mocks', function() {      it('should rethrow exceptions', inject(function($exceptionHandler) {        expect(function() { $exceptionHandler('myException'); }).toThrow('myException');      })); + + +    it('should log exceptions', inject(function($exceptionHandlerProvider){ +      $exceptionHandlerProvider.mode('log'); +      var $exceptionHandler = $exceptionHandlerProvider.$get(); +      $exceptionHandler('MyError'); +      expect($exceptionHandler.errors).toEqual(['MyError']); +    })); + + +    it('should thorw on wrong argument', inject(function($exceptionHandlerProvider) { +      expect(function() { +        $exceptionHandlerProvider.mode('XXX'); +      }).toThrow("Unknown mode 'XXX', only 'log'/'rethrow' modes are allowed!"); +    }));    });  }); diff --git a/test/mocks.js b/test/mocks.js deleted file mode 100644 index da0449ac..00000000 --- a/test/mocks.js +++ /dev/null @@ -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; -} diff --git a/test/service/cookiesSpec.js b/test/service/cookiesSpec.js index 66bdc504..d73923a6 100644 --- a/test/service/cookiesSpec.js +++ b/test/service/cookiesSpec.js @@ -3,7 +3,7 @@  describe('$cookies', function() {    beforeEach(inject(function($provide) {      $provide.factory('$browser', function(){ -      return angular.extend(new MockBrowser(), {cookieHash: {preexisting:'oldCookie'}});  +      return angular.extend(new angular.mock.$Browser(), {cookieHash: {preexisting:'oldCookie'}});      });    })); diff --git a/test/service/exceptionHandlerSpec.js b/test/service/exceptionHandlerSpec.js index 821ad7b8..00abff30 100644 --- a/test/service/exceptionHandlerSpec.js +++ b/test/service/exceptionHandlerSpec.js @@ -5,11 +5,9 @@ describe('$exceptionHandler', function() {    it('should log errors', inject(      function($provide){ -      $provide.factory('$exceptionHandler', $exceptionHandlerFactory); -      $provide.value('$log', $logMock); +      $provide.service('$exceptionHandler', $ExceptionHandlerProvider);      },      function($log, $exceptionHandler) { -      $log.error.rethrow = false;        $exceptionHandler('myError');        expect($log.error.logs.shift()).toEqual(['myError']);      } diff --git a/test/service/localeSpec.js b/test/service/localeSpec.js index 86bc7b0e..83c4ecee 100644 --- a/test/service/localeSpec.js +++ b/test/service/localeSpec.js @@ -2,7 +2,7 @@  describe('$locale', function() { -  var $locale = angular.service('$locale')(); +  var $locale = new $LocaleProvider().$get();    it('should have locale id set to en-us', function() {      expect($locale.id).toBe('en-us'); diff --git a/test/service/logSpec.js b/test/service/logSpec.js index 3b860756..93e705a9 100644 --- a/test/service/logSpec.js +++ b/test/service/logSpec.js @@ -12,7 +12,7 @@ describe('$log', function() {    beforeEach(inject(function($provide){      $window = {};      logger = ''; -    $provide.factory('$log', $logFactory); +    $provide.service('$log', $LogProvider);      $provide.value('$exceptionHandler', rethrow);      $provide.value('$window', $window);    })); @@ -68,7 +68,7 @@ describe('$log', function() {        e.line = undefined;        e.stack = undefined; -      $log = $logFactory({console:{error:function() { +      $log = new $LogProvider().$get[1]({console:{error:function() {          errorArgs = arguments;        }}});      }); diff --git a/test/service/scopeSpec.js b/test/service/scopeSpec.js index b7f50edb..88824fcd 100644 --- a/test/service/scopeSpec.js +++ b/test/service/scopeSpec.js @@ -2,8 +2,8 @@  describe('Scope', function() { -  beforeEach(inject(function($provide) { -    $provide.factory('$exceptionHandler', $exceptionHandlerMockFactory); +  beforeEach(inject(function($exceptionHandlerProvider) { +    $exceptionHandlerProvider.mode('log');    })); @@ -121,12 +121,12 @@ describe('Scope', function() {        expect(spy).wasCalled();      })); -    it('should delegate exceptions', inject(function($rootScope, $exceptionHandler) { +    it('should delegate exceptions', inject(function($rootScope, $exceptionHandler, $log) {        $rootScope.$watch('a', function() {throw new Error('abc');});        $rootScope.a = 1;        $rootScope.$digest();        expect($exceptionHandler.errors[0].message).toEqual('abc'); -      $logMock.error.logs.length = 0; +      $log.assertEmpty();      })); @@ -422,7 +422,7 @@ describe('Scope', function() {      })); -    it('should catch exceptions', inject(function($rootScope, $exceptionHandler) { +    it('should catch exceptions', inject(function($rootScope, $exceptionHandler, $log) {        var log = '';        var child = $rootScope.$new();        $rootScope.$watch('a', function(scope, a){ log += '1'; }); @@ -430,7 +430,7 @@ describe('Scope', function() {        child.$apply(function() { throw new Error('MyError'); });        expect(log).toEqual('1');        expect($exceptionHandler.errors[0].message).toEqual('MyError'); -      $logMock.error.logs.shift(); +      $log.error.logs.shift();      })); diff --git a/test/service/snifferSpec.js b/test/service/snifferSpec.js index 72c32046..7ec1dc8d 100644 --- a/test/service/snifferSpec.js +++ b/test/service/snifferSpec.js @@ -3,7 +3,7 @@  describe('$sniffer', function() {    function sniffer($window) { -    return angular.service('$sniffer')($window); +    return new $SnifferProvider().$get[1]($window);    }    describe('history', function() { diff --git a/test/testabilityPatch.js b/test/testabilityPatch.js index eb98cb6f..4d576ab9 100644 --- a/test/testabilityPatch.js +++ b/test/testabilityPatch.js @@ -77,18 +77,13 @@ beforeEach(function() {      }    }); -  $logMock.log.logs = []; -  $logMock.warn.logs = []; -  $logMock.info.logs = []; -  $logMock.error.logs = []; -  });  function inject(){    var blockFns = sliceArgs(arguments);    return function(){      var spec = this; -    spec.$injector = spec.$injector || angular.injector('NG'); +    spec.$injector = spec.$injector || angular.injector('NG', 'NG_MOCK');      angular.forEach(blockFns, function(fn){        spec.$injector.invoke(spec, fn);      }); @@ -96,30 +91,12 @@ function inject(){  } -afterEach(inject(function($rootScope) { +afterEach(inject(function($rootScope, $log) {    // release the injector    dealoc($rootScope);    // check $log mock -  forEach(['error', 'warn', 'info', 'log'], function(logLevel) { -    if ($logMock[logLevel].logs.length) { -      forEach($logMock[logLevel].logs, function(log) { -        forEach(log, function deleteStack(logItem) { -          if (logItem instanceof Error) { -            dump(logItem.stack); -            delete logItem.stack; -            delete logItem.arguments; -          } else { -            dump(logItem); -          } -        }); -      }); - -      throw new Error("Exprected $log." + logLevel + ".logs array to be empty. " + -        "Either a message was logged unexpectedly, or an expected log message was not checked " + -        "and removed. Array contents: " + toJson($logMock[logLevel].logs)); -    } -  }); +  $log.assertEmpty && $log.assertEmpty();    clearJqCache();  })); diff --git a/test/widget/inputSpec.js b/test/widget/inputSpec.js index 8d409b25..cd70cea4 100644 --- a/test/widget/inputSpec.js +++ b/test/widget/inputSpec.js @@ -448,12 +448,12 @@ describe('widget: input', function() {      }); -    it('should report error on assignment error', function() { +    it('should report error on assignment error', inject(function($log) {        expect(function() {          compile('<input type="text" ng:model="throw \'\'">');        }).toThrow("Syntax Error: Token '''' is an unexpected token at column 7 of the expression [throw ''] starting at ['']."); -      $logMock.error.logs.shift(); -    }); +      $log.error.logs.shift(); +    }));    }); diff --git a/test/widgetsSpec.js b/test/widgetsSpec.js index ef28ea77..fc898f83 100644 --- a/test/widgetsSpec.js +++ b/test/widgetsSpec.js @@ -223,12 +223,12 @@ describe("widget", function() {        expect(element.text()).toEqual('name:value;');      })); -    it('should error on wrong parsing of ng:repeat', inject(function($rootScope, $compile) { +    it('should error on wrong parsing of ng:repeat', inject(function($rootScope, $compile, $log) {        expect(function() {          var element = $compile('<ul><li ng:repeat="i dont parse"></li></ul>')($rootScope);        }).toThrow("Expected ng:repeat in form of '_item_ in _collection_' but got 'i dont parse'."); -      $logMock.error.logs.shift(); +      $log.error.logs.shift();      }));      it('should expose iterator offset as $index when iterating over arrays', inject(function($rootScope, $compile) { @@ -487,9 +487,9 @@ describe("widget", function() {      }));      it('should be possible to nest ng:view in ng:include', inject(function() { -      var injector = angular.injector('NG'); +      var injector = angular.injector('NG', 'NG_MOCK');        var myApp = injector('$rootScope'); -      var $browser = myApp.$service('$browser'); +      var $browser = injector('$browser');        $browser.xhr.expectGET('includePartial.html').respond('view: <ng:view></ng:view>');        injector('$location').path('/foo'); | 
