text: 'use strict';
  
  /* ! VARIABLE/FUNCTION NAMING CONVENTIONS THAT APPLY TO THIS FILE!
   *
   * DOM-related variables:
   *
   * - "node" - DOM Node
   * - "element" - DOM Element or Node
   * - "$node" or "$element" - jqLite-wrapped node or element
   *
   *
   * Compiler related stuff:
   *
   * - "linkFn" - linking fn of a single directive
   * - "nodeLinkFn" - function that aggregates all linking fns for a particular node
   * - "childLinkFn" -  function that aggregates all linking fns for child nodes of a particular node
   * - "compositeLinkFn" - function that aggregates all linking fns for a compilation root (nodeList)
   */
  
  
  /**
   * @ngdoc service
   * @name $compile
   * @kind function
   *
   * @description
   * Compiles an HTML string or DOM into a template and produces a template function, which
   * can then be used to link {@link ng.$rootScope.Scope `scope`} and the template together.
   *
   * The compilation is a process of walking the DOM tree and matching DOM elements to
   * {@link ng.$compileProvider#directive directives}.
   *
   * <div class="alert alert-warning">
   * **Note:** This document is an in-depth reference of all directive options.
   * For a gentle introduction to directives with examples of common use cases,
   * see the {@link guide/directive directive guide}.
   * </div>
   *
   * ## Comprehensive Directive API
   *
   * There are many different options for a directive.
   *
   * The difference resides in the return value of the factory function.
   * You can either return a "Directive Definition Object" (see below) that defines the directive properties,
   * or just the `postLink` function (all other properties will have the default values).
   *
   * <div class="alert alert-success">
   * **Best Practice:** It's recommended to use the "directive definition object" form.
   * </div>
   *
   * Here's an example directive declared with a Directive Definition Object:
   *
   * ```js
   *   var myModule = angular.module(...);
   *
   *   myModule.directive('directiveName', function factory(injectables) {
   *     var directiveDefinitionObject = {
   *       priority: 0,
   *       template: '<div></div>', // or // function(tElement, tAttrs) { ... },
   *       // or
   *       // templateUrl: 'directive.html', // or // function(tElement, tAttrs) { ... },
   *       transclude: false,
   *       restrict: 'A',
   *       templateNamespace: 'html',
   *       scope: false,
   *       controller: function($scope, $element, $attrs, $transclude, otherInjectables) { ... },
   *       controllerAs: 'stringAlias',
   *       require: 'siblingDirectiveName', // or // ['^parentDirectiveName', '?optionalDirectiveName', '?^optionalParent'],
   *       compile: function compile(tElement, tAttrs, transclude) {
   *         return {
   *           pre: function preLink(scope, iElement, iAttrs, controller) { ... },
   *           post: function postLink(scope, iElement, iAttrs, controller) { ... }
   *         }
   *         // or
   *         // return function postLink( ... ) { ... }
   *       },
   *       // or
   *       // link: {
   *       //  pre: function preLink(scope, iElement, iAttrs, controller) { ... },
   *       //  post: function postLink(scope, iElement, iAttrs, controller) { ... }
   *       // }
   *       // or
   *       // link: function postLink( ... ) { ... }
   *     };
   *     return directiveDefinitionObject;
   *   });
   * ```
   *
   * <div class="alert alert-warning">
   * **Note:** Any unspecified options will use the default value. You can see the default values below.
   * </div>
   *
   * Therefore the above can be simplified as:
   *
   * ```js
   *   var myModule = angular.module(...);
   *
   *   myModule.directive('directiveName', function factory(injectables) {
   *     var directiveDefinitionObject = {
   *       link: function postLink(scope, iElement, iAttrs) { ... }
   *     };
   *     return directiveDefinitionObject;
   *     // or
   *     // return function postLink(scope, iElement, iAttrs) { ... }
   *   });
   * ```
   *
   *
   *
   * ### Directive Definition Object
   *
   * The directive definition object provides instructions to the {@link ng.$compile
   * compiler}. The attributes are:
   *
   * #### `multiElement`
   * When this property is set to true, the HTML compiler will collect DOM nodes between
   * nodes with the attributes `directive-name-start` and `directive-name-end`, and group them
   * together as the directive elements. It is recomended that this feature be used on directives
   * which are not strictly behavioural (such as {@link ngClick}), and which
   * do not manipulate or replace child nodes (such as {@link ngInclude}).
   *
   * #### `priority`
   * When there are multiple directives defined on a single DOM element, sometimes it
   * is necessary to specify the order in which the directives are applied. The `priority` is used
   * to sort the directives before their `compile` functions get called. Priority is defined as a
   * number. Directives with greater numerical `priority` are compiled first. Pre-link functions
   * are also run in priority order, but post-link functions are run in reverse order. The order
   * of directives with the same priority is undefined. The default priority is `0`.
   *
   * #### `terminal`
   * If set to true then the current `priority` will be the last set of directives
   * which will execute (any directives at the current priority will still execute
   * as the order of execution on same `priority` is undefined). Note that expressions
   * and other directives used in the directive's template will also be excluded from execution.
   *
   * #### `scope`
   * **If set to `true`,** then a new scope will be created for this directive. If multiple directives on the
   * same element request a new scope, only one new scope is created. The new scope rule does not
   * apply for the root of the template since the root of the template always gets a new scope.
   *
   * **If set to `{}` (object hash),** then a new "isolate" scope is created. The 'isolate' scope differs from
   * normal scope in that it does not prototypically inherit from the parent scope. This is useful
   * when creating reusable components, which should not accidentally read or modify data in the
   * parent scope.
   *
   * The 'isolate' scope takes an object hash which defines a set of local scope properties
   * derived from the parent scope. These local properties are useful for aliasing values for
   * templates. Locals definition is a hash of local scope property to its source:
   *
   * * `@` or `@attr` - bind a local scope property to the value of DOM attribute. The result is
   *   always a string since DOM attributes are strings. If no `attr` name is specified  then the
   *   attribute name is assumed to be the same as the local name.
   *   Given `<widget my-attr="hello {{name}}">` and widget definition
   *   of `scope: { localName:'@myAttr' }`, then widget scope property `localName` will reflect
   *   the interpolated value of `hello {{name}}`. As the `name` attribute changes so will the
   *   `localName` property on the widget scope. The `name` is read from the parent scope (not
   *   component scope).
   *
   * * `=` or `=attr` - set up bi-directional binding between a local scope property and the
   *   parent scope property of name defined via the value of the `attr` attribute. If no `attr`
   *   name is specified then the attribute name is assumed to be the same as the local name.
   *   Given `<widget my-attr="parentModel">` and widget definition of
   *   `scope: { localModel:'=myAttr' }`, then widget scope property `localModel` will reflect the
   *   value of `parentModel` on the parent scope. Any changes to `parentModel` will be reflected
   *   in `localModel` and any changes in `localModel` will reflect in `parentModel`. If the parent
   *   scope property doesn't exist, it will throw a NON_ASSIGNABLE_MODEL_EXPRESSION exception. You
   *   can avoid this behavior using `=?` or `=?attr` in order to flag the property as optional. If
   *   you want to shallow watch for changes (i.e. $watchCollection instead of $watch) you can use
   *   `=*` or `=*attr` (`=*?` or `=*?attr` if the property is optional).
   *
   * * `&` or `&attr` - provides a way to execute an expression in the context of the parent scope.
   *   If no `attr` name is specified then the attribute name is assumed to be the same as the
   *   local name. Given `<widget my-attr="count = count + value">` and widget definition of
   *   `scope: { localFn:'&myAttr' }`, then isolate scope property `localFn` will point to
   *   a function wrapper for the `count = count + value` expression. Often it's desirable to
   *   pass data from the isolated scope via an expression to the parent scope, this can be
   *   done by passing a map of local variable names and values into the expression wrapper fn.
   *   For example, if the expression is `increment(amount)` then we can specify the amount value
   *   by calling the `localFn` as `localFn({amount: 22})`.
   *
   *
   * #### `bindToController`
   * When an isolate scope is used for a component (see above), and `controllerAs` is used, `bindToController` will
   * allow a component to have its properties bound to the controller, rather than to scope. When the controller
   * is instantiated, the initial values of the isolate scope bindings are already available.
   *
   * #### `controller`
   * Controller constructor function. The controller is instantiated before the
   * pre-linking phase and it is shared with other directives (see
   * `require` attribute). This allows the directives to communicate with each other and augment
   * each other's behavior. The controller is injectable (and supports bracket notation) with the following locals:
   *
   * * `$scope` - Current scope associated with the element
   * * `$element` - Current element
   * * `$attrs` - Current attributes object for the element
   * * `$transclude` - A transclude linking function pre-bound to the correct transclusion scope:
   *   `function([scope], cloneLinkingFn, futureParentElement)`.
   *    * `scope`: optional argument to override the scope.
   *    * `cloneLinkingFn`: optional argument to create clones of the original transcluded content.
   *    * `futureParentElement`:
   *        * defines the parent to which the `cloneLinkingFn` will add the cloned elements.
   *        * default: `$element.parent()` resp. `$element` for `transclude:'element'` resp. `transclude:true`.
   *        * only needed for transcludes that are allowed to contain non html elements (e.g. SVG elements)
   *          and when the `cloneLinkinFn` is passed,
   *          as those elements need to created and cloned in a special way when they are defined outside their
   *          usual containers (e.g. like `<svg>`).
   *        * See also the `directive.templateNamespace` property.
   *
   *
   * #### `require`
   * Require another directive and inject its controller as the fourth argument to the linking function. The
   * `require` takes a string name (or array of strings) of the directive(s) to pass in. If an array is used, the
   * injected argument will be an array in corresponding order. If no such directive can be
   * found, or if the directive does not have a controller, then an error is raised. The name can be prefixed with:
   *
   * * (no prefix) - Locate the required controller on the current element. Throw an error if not found.
   * * `?` - Attempt to locate the required controller or pass `null` to the `link` fn if not found.
   * * `^` - Locate the required controller by searching the element and its parents. Throw an error if not found.
   * * `^^` - Locate the required controller by searching the element's parents. Throw an error if not found.
   * * `?^` - Attempt to locate the required controller by searching the element and its parents or pass
   *   `null` to the `link` fn if not found.
   * * `?^^` - Attempt to locate the required controller by searching the element's parents, or pass
   *   `null` to the `link` fn if not found.
   *
   *
   * #### `controllerAs`
   * Controller alias at the directive scope. An alias for the controller so it
   * can be referenced at the directive template. The directive needs to define a scope for this
   * configuration to be used. Useful in the case when directive is used as component.
   *
   *
   * #### `restrict`
   * String of subset of `EACM` which restricts the directive to a specific directive
   * declaration style. If omitted, the defaults (elements and attributes) are used.
   *
   * * `E` - Element name (default): `<my-directive></my-directive>`
   * * `A` - Attribute (default): `<div my-directive="exp"></div>`
   * * `C` - Class: `<div class="my-directive: exp;"></div>`
   * * `M` - Comment: `<!-- directive: my-directive exp -->`
   *
   *
   * #### `templateNamespace`
   * String representing the document type used by the markup in the template.
   * AngularJS needs this information as those elements need to be created and cloned
   * in a special way when they are defined outside their usual containers like `<svg>` and `<math>`.
   *
   * * `html` - All root nodes in the template are HTML. Root nodes may also be
   *   top-level elements such as `<svg>` or `<math>`.
   * * `svg` - The root nodes in the template are SVG elements (excluding `<math>`).
   * * `math` - The root nodes in the template are MathML elements (excluding `<svg>`).
   *
   * If no `templateNamespace` is specified, then the namespace is considered to be `html`.
   *
   * #### `template`
   * HTML markup that may:
   * * Replace the contents of the directive's element (default).
   * * Replace the directive's element itself (if `replace` is true - DEPRECATED).
   * * Wrap the contents of the directive's element (if `transclude` is true).
   *
   * Value may be:
   *
   * * A string. For example `<div red-on-hover>{{delete_str}}</div>`.
   * * A function which takes two arguments `tElement` and `tAttrs` (described in the `compile`
   *   function api below) and returns a string value.
   *
   *
   * #### `templateUrl`
   * This is similar to `template` but the template is loaded from the specified URL, asynchronously.
   *
   * Because template loading is asynchronous the compiler will suspend compilation of directives on that element
   * for later when the template has been resolved.  In the meantime it will continue to compile and link
   * sibling and parent elements as though this element had not contained any directives.
   *
   * The compiler does not suspend the entire compilation to wait for templates to be loaded because this
   * would result in the whole app "stalling" until all templates are loaded asynchronously - even in the
   * case when only one deeply nested directive has `templateUrl`.
   *
   * Template loading is asynchronous even if the template has been preloaded into the {@link $templateCache}
   *
   * You can specify `templateUrl` as a string representing the URL or as a function which takes two
   * arguments `tElement` and `tAttrs` (described in the `compile` function api below) and returns
   * a string value representing the url.  In either case, the template URL is passed through {@link
   * $sce#getTrustedResourceUrl $sce.getTrustedResourceUrl}.
   *
   *
   * #### `replace` ([*DEPRECATED*!], will be removed in next major release - i.e. v2.0)
   * specify what the template should replace. Defaults to `false`.
   *
   * * `true` - the template will replace the directive's element.
   * * `false` - the template will replace the contents of the directive's element.
   *
   * The replacement process migrates all of the attributes / classes from the old element to the new
   * one. See the {@link guide/directive#template-expanding-directive
   * Directives Guide} for an example.
   *
   * There are very few scenarios where element replacement is required for the application function,
   * the main one being reusable custom components that are used within SVG contexts
   * (because SVG doesn't work with custom elements in the DOM tree).
   *
   * #### `transclude`
   * Extract the contents of the element where the directive appears and make it available to the directive.
   * The contents are compiled and provided to the directive as a **transclusion function**. See the
   * {@link $compile#transclusion Transclusion} section below.
   *
   * There are two kinds of transclusion depending upon whether you want to transclude just the contents of the
   * directive's element or the entire element:
   *
   * * `true` - transclude the content (i.e. the child nodes) of the directive's element.
   * * `'element'` - transclude the whole of the directive's element including any directives on this
   *   element that defined at a lower priority than this directive. When used, the `template`
   *   property is ignored.
   *
   *
   * #### `compile`
   *
   * ```js
   *   function compile(tElement, tAttrs, transclude) { ... }
   * ```
   *
   * The compile function deals with transforming the template DOM. Since most directives do not do
   * template transformation, it is not used often. The compile function takes the following arguments:
   *
   *   * `tElement` - template element - The element where the directive has been declared. It is
   *     safe to do template transformation on the element and child elements only.
   *
   *   * `tAttrs` - template attributes - Normalized list of attributes declared on this element shared
   *     between all directive compile functions.
   *
   *   * `transclude` -  [*DEPRECATED*!] A transclude linking function: `function(scope, cloneLinkingFn)`
   *
   * <div class="alert alert-warning">
   * **Note:** The template instance and the link instance may be different objects if the template has
   * been cloned. For this reason it is **not** safe to do anything other than DOM transformations that
   * apply to all cloned DOM nodes within the compile function. Specifically, DOM listener registration
   * should be done in a linking function rather than in a compile function.
   * </div>
  
   * <div class="alert alert-warning">
   * **Note:** The compile function cannot handle directives that recursively use themselves in their
   * own templates or compile functions. Compiling these directives results in an infinite loop and a
   * stack overflow errors.
   *
   * This can be avoided by manually using $compile in the postLink function to imperatively compile
   * a directive's template instead of relying on automatic template compilation via `template` or
   * `templateUrl` declaration or manual compilation inside the compile function.
   * </div>
   *
   * <div class="alert alert-error">
   * **Note:** The `transclude` function that is passed to the compile function is deprecated, as it
   *   e.g. does not know about the right outer scope. Please use the transclude function that is passed
   *   to the link function instead.
   * </div>
  
   * A compile function can have a return value which can be either a function or an object.
   *
   * * returning a (post-link) function - is equivalent to registering the linking function via the
   *   `link` property of the config object when the compile function is empty.
   *
   * * returning an object with function(s) registered via `pre` and `post` properties - allows you to
   *   control when a linking function should be called during the linking phase. See info about
   *   pre-linking and post-linking functions below.
   *
   *
   * #### `link`
   * This property is used only if the `compile` property is not defined.
   *
   * ```js
   *   function link(scope, iElement, iAttrs, controller, transcludeFn) { ... }
   * ```
   *
   * The link function is responsible for registering DOM listeners as well as updating the DOM. It is
   * executed after the template has been cloned. This is where most of the directive logic will be
   * put.
   *
   *   * `scope` - {@link ng.$rootScope.Scope Scope} - The scope to be used by the
   *     directive for registering {@link ng.$rootScope.Scope#$watch watches}.
   *
   *   * `iElement` - instance element - The element where the directive is to be used. It is safe to
   *     manipulate the children of the element only in `postLink` function since the children have
   *     already been linked.
   *
   *   * `iAttrs` - instance attributes - Normalized list of attributes declared on this element shared
   *     between all directive linking functions.
   *
   *   * `controller` - a controller instance - A controller instance if at least one directive on the
   *     element defines a controller. The controller is shared among all the directives, which allows
   *     the directives to use the controllers as a communication channel.
   *
   *   * `transcludeFn` - A transclude linking function pre-bound to the correct transclusion scope.
   *     This is the same as the `$transclude`
   *     parameter of directive controllers, see there for details.
   *     `function([scope], cloneLinkingFn, futureParentElement)`.
   *
   * #### Pre-linking function
   *
   * Executed before the child elements are linked. Not safe to do DOM transformation since the
   * compiler linking function will fail to locate the correct elements for linking.
   *
   * #### Post-linking function
   *
   * Executed after the child elements are linked.
   *
   * Note that child elements that contain `templateUrl` directives will not have been compiled
   * and linked since they are waiting for their template to load asynchronously and their own
   * compilation and linking has been suspended until that occurs.
   *
   * It is safe to do DOM transformation in the post-linking function on elements that are not waiting
   * for their async templates to be resolved.
   *
   *
   * ### Transclusion
   *
   * Transclusion is the process of extracting a collection of DOM element from one part of the DOM and
   * copying them to another part of the DOM, while maintaining their connection to the original AngularJS
   * scope from where they were taken.
   *
   * Transclusion is used (often with {@link ngTransclude}) to insert the
   * original contents of a directive's element into a specified place in the template of the directive.
   * The benefit of transclusion, over simply moving the DOM elements manually, is that the transcluded
   * content has access to the properties on the scope from which it was taken, even if the directive
   * has isolated scope.
   * See the {@link guide/directive#creating-a-directive-that-wraps-other-elements Directives Guide}.
   *
   * This makes it possible for the widget to have private state for its template, while the transcluded
   * content has access to its originating scope.
   *
   * <div class="alert alert-warning">
   * **Note:** When testing an element transclude directive you must not place the directive at the root of the
   * DOM fragment that is being compiled. See {@link guide/unit-testing#testing-transclusion-directives
   * Testing Transclusion Directives}.
   * </div>
   *
   * #### Transclusion Functions
   *
   * When a directive requests transclusion, the compiler extracts its contents and provides a **transclusion
   * function** to the directive's `link` function and `controller`. This transclusion function is a special
   * **linking function** that will return the compiled contents linked to a new transclusion scope.
   *
   * <div class="alert alert-info">
   * If you are just using {@link ngTransclude} then you don't need to worry about this function, since
   * ngTransclude will deal with it for us.
   * </div>
   *
   * If you want to manually control the insertion and removal of the transcluded content in your directive
   * then you must use this transclude function. When you call a transclude function it returns a a jqLite/JQuery
   * object that contains the compiled DOM, which is linked to the correct transclusion scope.
   *
   * When you call a transclusion function you can pass in a **clone attach function**. This function accepts
   * two parameters, `function(clone, scope) { ... }`, where the `clone` is a fresh compiled copy of your transcluded
   * content and the `scope` is the newly created transclusion scope, to which the clone is bound.
   *
   * <div class="alert alert-info">
   * **Best Practice**: Always provide a `cloneFn` (clone attach function) when you call a translude function
   * since you then get a fresh clone of the original DOM and also have access to the new transclusion scope.
   * </div>
   *
   * It is normal practice to attach your transcluded content (`clone`) to the DOM inside your **clone
   * attach function**:
   *
   * ```js
   * var transcludedContent, transclusionScope;
   *
   * $transclude(function(clone, scope) {
   *   element.append(clone);
   *   transcludedContent = clone;
   *   transclusionScope = scope;
   * });
   * ```
   *
   * Later, if you want to remove the transcluded content from your DOM then you should also destroy the
   * associated transclusion scope:
   *
   * ```js
   * transcludedContent.remove();
   * transclusionScope.$destroy();
   * ```
   *
   * <div class="alert alert-info">
   * **Best Practice**: if you intend to add and remove transcluded content manually in your directive
   * (by calling the transclude function to get the DOM and and calling `element.remove()` to remove it),
   * then you are also responsible for calling `$destroy` on the transclusion scope.
   * </div>
   *
   * The built-in DOM manipulation directives, such as {@link ngIf}, {@link ngSwitch} and {@link ngRepeat}
   * automatically destroy their transluded clones as necessary so you do not need to worry about this if
   * you are simply using {@link ngTransclude} to inject the transclusion into your directive.
   *
   *
   * #### Transclusion Scopes
   *
   * When you call a transclude function it returns a DOM fragment that is pre-bound to a **transclusion
   * scope**. This scope is special, in that it is a child of the directive's scope (and so gets destroyed
   * when the directive's scope gets destroyed) but it inherits the properties of the scope from which it
   * was taken.
   *
   * For example consider a directive that uses transclusion and isolated scope. The DOM hierarchy might look
   * like this:
   *
   * ```html
   * <div ng-app>
   *   <div isolate>
   *     <div transclusion>
   *     </div>
   *   </div>
   * </div>
   * ```
   *
   * The `$parent` scope hierarchy will look like this:
   *
   * ```
   * - $rootScope
   *   - isolate
   *     - transclusion
   * ```
   *
   * but the scopes will inherit prototypically from different scopes to their `$parent`.
   *
   * ```
   * - $rootScope
   *   - transclusion
   * - isolate
   * ```
   *
   *
   * ### Attributes
   *
   * The {@link ng.$compile.directive.Attributes Attributes} object - passed as a parameter in the
   * `link()` or `compile()` functions. It has a variety of uses.
   *
   * accessing *Normalized attribute names:*
   * Directives like 'ngBind' can be expressed in many ways: 'ng:bind', `data-ng-bind`, or 'x-ng-bind'.
   * the attributes object allows for normalized access to
   *   the attributes.
   *
   * * *Directive inter-communication:* All directives share the same instance of the attributes
   *   object which allows the directives to use the attributes object as inter directive
   *   communication.
   *
   * * *Supports interpolation:* Interpolation attributes are assigned to the attribute object
   *   allowing other directives to read the interpolated value.
   *
   * * *Observing interpolated attributes:* Use `$observe` to observe the value changes of attributes
   *   that contain interpolation (e.g. `src="{{bar}}"`). Not only is this very efficient but it's also
   *   the only way to easily get the actual value because during the linking phase the interpolation
   *   hasn't been evaluated yet and so the value is at this time set to `undefined`.
   *
   * ```js
   * function linkingFn(scope, elm, attrs, ctrl) {
   *   // get the attribute value
   *   console.log(attrs.ngModel);
   *
   *   // change the attribute
   *   attrs.$set('ngModel', 'new value');
   *
   *   // observe changes to interpolated attribute
   *   attrs.$observe('ngModel', function(value) {
   *     console.log('ngModel has changed value to ' + value);
   *   });
   * }
   * ```
   *
   * ## Example
   *
   * <div class="alert alert-warning">
   * **Note**: Typically directives are registered with `module.directive`. The example below is
   * to illustrate how `$compile` works.
   * </div>
   *
   <example module="compileExample">
     <file name="index.html">
      <script>
        angular.module('compileExample', [], function($compileProvider) {
          // configure new 'compile' directive by passing a directive
          // factory function. The factory function injects the '$compile'
          $compileProvider.directive('compile', function($compile) {
            // directive factory creates a link function
            return function(scope, element, attrs) {
              scope.$watch(
                function(scope) {
                   // watch the 'compile' expression for changes
                  return scope.$eval(attrs.compile);
                },
                function(value) {
                  // when the 'compile' expression changes
                  // assign it into the current DOM
                  element.html(value);
  
                  // compile the new DOM and link it to the current
                  // scope.
                  // NOTE: we only compile .childNodes so that
                  // we don't get into infinite loop compiling ourselves
                  $compile(element.contents())(scope);
                }
              );
            };
          });
        })
        .controller('GreeterController', ['$scope', function($scope) {
          $scope.name = 'Angular';
          $scope.html = 'Hello {{name}}';
        }]);
      </script>
      <div ng-controller="GreeterController">
        <input ng-model="name"> <br>
        <textarea ng-model="html"></textarea> <br>
        <div compile="html"></div>
      </div>
     </file>
     <file name="protractor.js" type="protractor">
       it('should auto compile', function() {
         var textarea = $('textarea');
         var output = $('div[compile]');
         // The initial state reads 'Hello Angular'.
         expect(output.getText()).toBe('Hello Angular');
         textarea.clear();
         textarea.sendKeys('{{name}}!');
         expect(output.getText()).toBe('Angular!');
       });
     </file>
   </example>
  
   *
   *
   * @param {string|DOMElement} element Element or HTML string to compile into a template function.
   * @param {function(angular.Scope, cloneAttachFn=)} transclude function available to directives - DEPRECATED.
   *
   * <div class="alert alert-error">
   * **Note:** Passing a `transclude` function to the $compile function is deprecated, as it
   *   e.g. will not use the right outer scope. Please pass the transclude function as a
   *   `parentBoundTranscludeFn` to the link function instead.
   * </div>
   *
   * @param {number} maxPriority only apply directives lower than given priority (Only effects the
   *                 root element(s), not their children)
   * @returns {function(scope, cloneAttachFn=, options=)} a link function which is used to bind template
   * (a DOM element/tree) to a scope. Where:
   *
   *  * `scope` - A {@link ng.$rootScope.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.
   *
   *  * `options` - An optional object hash with linking options. If `options` is provided, then the following
   *  keys may be used to control linking behavior:
   *
   *      * `parentBoundTranscludeFn` - the transclude function made available to
   *        directives; if given, it will be passed through to the link functions of
   *        directives found in `element` during compilation.
   *      * `transcludeControllers` - an object hash with keys that map controller names
   *        to controller instances; if given, it will make the controllers
   *        available to directives.
   *      * `futureParentElement` - defines the parent to which the `cloneAttachFn` will add
   *        the cloned elements; only needed for transcludes that are allowed to contain non html
   *        elements (e.g. SVG elements). See also the directive.controller property.
   *
   * Calling the linking 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.
   *
   * After linking the view is not updated until after a call to $digest which typically is done by
   * Angular automatically.
   *
   * 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.
   *   ```js
   *     var element = $compile('<p>{{total}}</p>')(scope);
   *   ```
   *
   * - 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:
   *   ```js
   *     var templateElement = angular.element('<p>{{total}}</p>'),
   *         scope = ....;
   *
   *     var clonedElement = $compile(templateElement)(scope, function(clonedElement, scope) {
   *       //attach the clone to DOM document at the right place
   *     });
   *
   *     //now we have reference to the cloned DOM via `clonedElement`
   *   ```
   *
   *
   * For information on how the compiler works, see the
   * {@link guide/compiler Angular HTML Compiler} section of the Developer Guide.
   */
  
  var $compileMinErr = minErr('$compile');
  
  /**
   * @ngdoc provider
   * @name $compileProvider
   *
   * @description
   */
  $CompileProvider.$inject = ['$provide', '$$sanitizeUriProvider'];
  function $CompileProvider($provide, $$sanitizeUriProvider) {
    var hasDirectives = {},
        Suffix = 'Directive',
        COMMENT_DIRECTIVE_REGEXP = /^\s*directive\:\s*([\w\-]+)\s+(.*)$/,
        CLASS_DIRECTIVE_REGEXP = /(([\w\-]+)(?:\:([^;]+))?;?)/,
        ALL_OR_NOTHING_ATTRS = makeMap('ngSrc,ngSrcset,src,srcset'),
        REQUIRE_PREFIX_REGEXP = /^(?:(\^\^?)?(\?)?(\^\^?)?)?/;
  
    // Ref: http://developers.whatwg.org/webappapis.html#event-handler-idl-attributes
    // The assumption is that future DOM event attribute names will begin with
    // 'on' and be composed of only English letters.
    var EVENT_HANDLER_ATTR_REGEXP = /^(on[a-z]+|formaction)$/;
  
    function parseIsolateBindings(scope, directiveName) {
      var LOCAL_REGEXP = /^\s*([@&]|=(\*?))(\??)\s*(\w*)\s*$/;
  
      var bindings = {};
  
      forEach(scope, function(definition, scopeName) {
        var match = definition.match(LOCAL_REGEXP);
  
        if (!match) {
          throw $compileMinErr('iscp',
              "Invalid isolate scope definition for directive '{0}'." +
              " Definition: {... {1}: '{2}' ...}",
              directiveName, scopeName, definition);
        }
  
        bindings[scopeName] = {
          mode: match[1][0],
          collection: match[2] === '*',
          optional: match[3] === '?',
          attrName: match[4] || scopeName
        };
      });
  
      return bindings;
    }
  
    /**
     * @ngdoc method
     * @name $compileProvider#directive
     * @kind function
     *
     * @description
     * Register a new directive with the compiler.
     *
     * @param {string|Object} name Name of the directive in camel-case (i.e. <code>ngBind</code> which
     *    will match as <code>ng-bind</code>), or an object map of directives where the keys are the
     *    names and the values are the factories.
     * @param {Function|Array} directiveFactory An injectable directive factory function. See
     *    {@link guide/directive} for more info.
     * @returns {ng.$compileProvider} Self for chaining.
     */
     this.directive = function registerDirective(name, directiveFactory) {
      assertNotHasOwnProperty(name, 'directive');
      if (isString(name)) {
        assertArg(directiveFactory, 'directiveFactory');
        if (!hasDirectives.hasOwnProperty(name)) {
          hasDirectives[name] = [];
          $provide.factory(name + Suffix, ['$injector', '$exceptionHandler',
            function($injector, $exceptionHandler) {
              var directives = [];
              forEach(hasDirectives[name], function(directiveFactory, index) {
                try {
                  var directive = $injector.invoke(directiveFactory);
                  if (isFunction(directive)) {
                    directive = { compile: valueFn(directive) };
                  } else if (!directive.compile && directive.link) {
                    directive.compile = valueFn(directive.link);
                  }
                  directive.priority = directive.priority || 0;
                  directive.index = index;
                  directive.name = directive.name || name;
                  directive.require = directive.require || (directive.controller && directive.name);
                  directive.restrict = directive.restrict || 'EA';
                  if (isObject(directive.scope)) {
                    directive.$$isolateBindings = parseIsolateBindings(directive.scope, directive.name);
                  }
                  directives.push(directive);
                } catch (e) {
                  $exceptionHandler(e);
                }
              });
              return directives;
            }]);
        }
        hasDirectives[name].push(directiveFactory);
      } else {
        forEach(name, reverseParams(registerDirective));
      }
      return this;
    };
  
  
    /**
     * @ngdoc method
     * @name $compileProvider#aHrefSanitizationWhitelist
     * @kind function
     *
     * @description
     * Retrieves or overrides the default regular expression that is used for whitelisting of safe
     * urls during a[href] sanitization.
     *
     * The sanitization is a security measure aimed at prevent XSS attacks via html links.
     *
     * Any url about to be assigned to a[href] via data-binding is first normalized and turned into
     * an absolute url. Afterwards, the url is matched against the `aHrefSanitizationWhitelist`
     * regular expression. If a match is found, the original url is written into the dom. Otherwise,
     * the absolute url is prefixed with `'unsafe:'` string and only then is it written into the DOM.
     *
     * @param {RegExp=} regexp New regexp to whitelist urls with.
     * @returns {RegExp|ng.$compileProvider} Current RegExp if called without value or self for
     *    chaining otherwise.
     */
    this.aHrefSanitizationWhitelist = function(regexp) {
      if (isDefined(regexp)) {
        $$sanitizeUriProvider.aHrefSanitizationWhitelist(regexp);
        return this;
      } else {
        return $$sanitizeUriProvider.aHrefSanitizationWhitelist();
      }
    };
  
  
    /**
     * @ngdoc method
     * @name $compileProvider#imgSrcSanitizationWhitelist
     * @kind function
     *
     * @description
     * Retrieves or overrides the default regular expression that is used for whitelisting of safe
     * urls during img[src] sanitization.
     *
     * The sanitization is a security measure aimed at prevent XSS attacks via html links.
     *
     * Any url about to be assigned to img[src] via data-binding is first normalized and turned into
     * an absolute url. Afterwards, the url is matched against the `imgSrcSanitizationWhitelist`
     * regular expression. If a match is found, the original url is written into the dom. Otherwise,
     * the absolute url is prefixed with `'unsafe:'` string and only then is it written into the DOM.
     *
     * @param {RegExp=} regexp New regexp to whitelist urls with.
     * @returns {RegExp|ng.$compileProvider} Current RegExp if called without value or self for
     *    chaining otherwise.
     */
    this.imgSrcSanitizationWhitelist = function(regexp) {
      if (isDefined(regexp)) {
        $$sanitizeUriProvider.imgSrcSanitizationWhitelist(regexp);
        return this;
      } else {
        return $$sanitizeUriProvider.imgSrcSanitizationWhitelist();
      }
    };
  
    /**
     * @ngdoc method
     * @name  $compileProvider#debugInfoEnabled
     *
     * @param {boolean=} enabled update the debugInfoEnabled state if provided, otherwise just return the
     * current debugInfoEnabled state
     * @returns {*} current value if used as getter or itself (chaining) if used as setter
     *
     * @kind function
     *
     * @description
     * Call this method to enable/disable various debug runtime information in the compiler such as adding
     * binding information and a reference to the current scope on to DOM elements.
     * If enabled, the compiler will add the following to DOM elements that have been bound to the scope
     * * `ng-binding` CSS class
     * * `$binding` data property containing an array of the binding expressions
     *
     * You may want to use this in production for a significant performance boost. See
     * {@link guide/production#disabling-debug-data Disabling Debug Data} for more.
     *
     * The default value is true.
     */
    var debugInfoEnabled = true;
    this.debugInfoEnabled = function(enabled) {
      if (isDefined(enabled)) {
        debugInfoEnabled = enabled;
        return this;
      }
      return debugInfoEnabled;
    };
  
    this.$get = [
              '$injector', '$interpolate', '$exceptionHandler', '$templateRequest', '$parse',
              '$controller', '$rootScope', '$document', '$sce', '$animate', '$$sanitizeUri',
      function($injector,   $interpolate,   $exceptionHandler,   $templateRequest,   $parse,
               $controller,   $rootScope,   $document,   $sce,   $animate,   $$sanitizeUri) {
  
      var Attributes = function(element, attributesToCopy) {
        if (attributesToCopy) {
          var keys = Object.keys(attributesToCopy);
          var i, l, key;
  
          for (i = 0, l = keys.length; i < l; i++) {
            key = keys[i];
            this[key] = attributesToCopy[key];
          }
        } else {
          this.$attr = {};
        }
  
        this.$$element = element;
      };
  
      Attributes.prototype = {
        $normalize: directiveNormalize,
  
  
        /**
         * @ngdoc method
         * @name $compile.directive.Attributes#$addClass
         * @kind function
         *
         * @description
         * Adds the CSS class value specified by the classVal parameter to the element. If animations
         * are enabled then an animation will be triggered for the class addition.
         *
         * @param {string} classVal The className value that will be added to the element
         */
        $addClass: function(classVal) {
          if (classVal && classVal.length > 0) {
            $animate.addClass(this.$$element, classVal);
          }
        },
  
        /**
         * @ngdoc method
         * @name $compile.directive.Attributes#$removeClass
         * @kind function
         *
         * @description
         * Removes the CSS class value specified by the classVal parameter from the element. If
         * animations are enabled then an animation will be triggered for the class removal.
         *
         * @param {string} classVal The className value that will be removed from the element
         */
        $removeClass: function(classVal) {
          if (classVal && classVal.length > 0) {
            $animate.removeClass(this.$$element, classVal);
          }
        },
  
        /**
         * @ngdoc method
         * @name $compile.directive.Attributes#$updateClass
         * @kind function
         *
         * @description
         * Adds and removes the appropriate CSS class values to the element based on the difference
         * between the new and old CSS class values (specified as newClasses and oldClasses).
         *
         * @param {string} newClasses The current CSS className value
         * @param {string} oldClasses The former CSS className value
         */
        $updateClass: function(newClasses, oldClasses) {
          var toAdd = tokenDifference(newClasses, oldClasses);
          if (toAdd && toAdd.length) {
            $animate.addClass(this.$$element, toAdd);
          }
  
          var toRemove = tokenDifference(oldClasses, newClasses);
          if (toRemove && toRemove.length) {
            $animate.removeClass(this.$$element, toRemove);
          }
        },
  
        /**
         * Set a normalized attribute on the element in a way such that all directives
         * can share the attribute. This function properly handles boolean attributes.
         * @param {string} key Normalized key. (ie ngAttribute)
         * @param {string|boolean} value The value to set. If `null` attribute will be deleted.
         * @param {boolean=} writeAttr If false, does not write the value to DOM element attribute.
         *     Defaults to true.
         * @param {string=} attrName Optional none normalized name. Defaults to key.
         */
        $set: function(key, value, writeAttr, attrName) {
          // TODO: decide whether or not to throw an error if "class"
          //is set through this function since it may cause $updateClass to
          //become unstable.
  
          var node = this.$$element[0],
              booleanKey = getBooleanAttrName(node, key),
              aliasedKey = getAliasedAttrName(node, key),
              observer = key,
              nodeName;
  
          if (booleanKey) {
            this.$$element.prop(key, value);
            attrName = booleanKey;
          } else if (aliasedKey) {
            this[aliasedKey] = value;
            observer = aliasedKey;
          }
  
          this[key] = value;
  
          // translate normalized key to actual key
          if (attrName) {
            this.$attr[key] = attrName;
          } else {
            attrName = this.$attr[key];
            if (!attrName) {
              this.$attr[key] = attrName = snake_case(key, '-');
            }
          }
  
          nodeName = nodeName_(this.$$element);
  
          if ((nodeName === 'a' && key === 'href') ||
              (nodeName === 'img' && key === 'src')) {
            // sanitize a[href] and img[src] values
            this[key] = value = $$sanitizeUri(value, key === 'src');
          } else if (nodeName === 'img' && key === 'srcset') {
            // sanitize img[srcset] values
            var result = "";
  
            // first check if there are spaces because it's not the same pattern
            var trimmedSrcset = trim(value);
            //                (   999x   ,|   999w   ,|   ,|,   )
            var srcPattern = /(\s+\d+x\s*,|\s+\d+w\s*,|\s+,|,\s+)/;
            var pattern = /\s/.test(trimmedSrcset) ? srcPattern : /(,)/;
  
            // split srcset into tuple of uri and descriptor except for the last item
            var rawUris = trimmedSrcset.split(pattern);
  
            // for each tuples
            var nbrUrisWith2parts = Math.floor(rawUris.length / 2);
            for (var i=0; i<nbrUrisWith2parts; i++) {
              var innerIdx = i*2;
              // sanitize the uri
              result += $$sanitizeUri(trim(rawUris[innerIdx]), true);
              // add the descriptor
              result += (" " + trim(rawUris[innerIdx+1]));
            }
  
            // split the last item into uri and descriptor
            var lastTuple = trim(rawUris[i*2]).split(/\s/);
  
            // sanitize the last uri
            result += $$sanitizeUri(trim(lastTuple[0]), true);
  
            // and add the last descriptor if any
            if (lastTuple.length === 2) {
              result += (" " + trim(lastTuple[1]));
            }
            this[key] = value = result;
          }
  
          if (writeAttr !== false) {
            if (value === null || value === undefined) {
              this.$$element.removeAttr(attrName);
            } else {
              this.$$element.attr(attrName, value);
            }
          }
  
          // fire observers
          var $$observers = this.$$observers;
          $$observers && forEach($$observers[observer], function(fn) {
            try {
              fn(value);
            } catch (e) {
              $exceptionHandler(e);
            }
          });
        },
  
  
        /**
         * @ngdoc method
         * @name $compile.directive.Attributes#$observe
         * @kind function
         *
         * @description
         * Observes an interpolated attribute.
         *
         * The observer function will be invoked once during the next `$digest` following
         * compilation. The observer is then invoked whenever the interpolated value
         * changes.
         *
         * @param {string} key Normalized key. (ie ngAttribute) .
         * @param {function(interpolatedValue)} fn Function that will be called whenever
                  the interpolated value of the attribute changes.
         *        See the {@link guide/directive#text-and-attribute-bindings Directives} guide for more info.
         * @returns {function()} Returns a deregistration function for this observer.
         */
        $observe: function(key, fn) {
          var attrs = this,
              $$observers = (attrs.$$observers || (attrs.$$observers = createMap())),
              listeners = ($$observers[key] || ($$observers[key] = []));
  
          listeners.push(fn);
          $rootScope.$evalAsync(function() {
            if (!listeners.$$inter && attrs.hasOwnProperty(key)) {
              // no one registered attribute interpolation function, so lets call it manually
              fn(attrs[key]);
            }
          });
  
          return function() {
            arrayRemove(listeners, fn);
          };
        }
      };
  
  
      function safeAddClass($element, className) {
        try {
          $element.addClass(className);
        } catch (e) {
          // ignore, since it means that we are trying to set class on
          // SVG element, where class name is read-only.
        }
      }
  
  
      var startSymbol = $interpolate.startSymbol(),
          endSymbol = $interpolate.endSymbol(),
          denormalizeTemplate = (startSymbol == '{{' || endSymbol  == '}}')
              ? identity
              : function denormalizeTemplate(template) {
                return template.replace(/\{\{/g, startSymbol).replace(/}}/g, endSymbol);
          },
          NG_ATTR_BINDING = /^ngAttr[A-Z]/;
  
      compile.$$addBindingInfo = debugInfoEnabled ? function $$addBindingInfo($element, binding) {
        var bindings = $element.data('$binding') || [];
  
        if (isArray(binding)) {
          bindings = bindings.concat(binding);
        } else {
          bindings.push(binding);
        }
  
        $element.data('$binding', bindings);
      } : noop;
  
      compile.$$addBindingClass = debugInfoEnabled ? function $$addBindingClass($element) {
        safeAddClass($element, 'ng-binding');
      } : noop;
  
      compile.$$addScopeInfo = debugInfoEnabled ? function $$addScopeInfo($element, scope, isolated, noTemplate) {
        var dataName = isolated ? (noTemplate ? '$isolateScopeNoTemplate' : '$isolateScope') : '$scope';
        $element.data(dataName, scope);
      } : noop;
  
      compile.$$addScopeClass = debugInfoEnabled ? function $$addScopeClass($element, isolated) {
        safeAddClass($element, isolated ? 'ng-isolate-scope' : 'ng-scope');
      } : noop;
  
      return compile;
  
      //================================
  
      function compile($compileNodes, transcludeFn, maxPriority, ignoreDirective,
                          previousCompileContext) {
        if (!($compileNodes instanceof jqLite)) {
          // jquery always rewraps, whereas we need to preserve the original selector so that we can
          // modify it.
          $compileNodes = jqLite($compileNodes);
        }
        // We can not compile top level text elements since text nodes can be merged and we will
        // not be able to attach scope data to them, so we will wrap them in <span>
        forEach($compileNodes, function(node, index) {
          if (node.nodeType == NODE_TYPE_TEXT && node.nodeValue.match(/\S+/) /* non-empty */ ) {
            $compileNodes[index] = jqLite(node).wrap('<span></span>').parent()[0];
          }
        });
        var compositeLinkFn =
                compileNodes($compileNodes, transcludeFn, $compileNodes,
                             maxPriority, ignoreDirective, previousCompileContext);
        compile.$$addScopeClass($compileNodes);
        var namespace = null;
        return function publicLinkFn(scope, cloneConnectFn, options) {
          assertArg(scope, 'scope');
  
          options = options || {};
          var parentBoundTranscludeFn = options.parentBoundTranscludeFn,
            transcludeControllers = options.transcludeControllers,
            futureParentElement = options.futureParentElement;
  
          // When `parentBoundTranscludeFn` is passed, it is a
          // `controllersBoundTransclude` function (it was previously passed
          // as `transclude` to directive.link) so we must unwrap it to get
          // its `boundTranscludeFn`
          if (parentBoundTranscludeFn && parentBoundTranscludeFn.$$boundTransclude) {
            parentBoundTranscludeFn = parentBoundTranscludeFn.$$boundTransclude;
          }
  
          if (!namespace) {
            namespace = detectNamespaceForChildElements(futureParentElement);
          }
          var $linkNode;
          if (namespace !== 'html') {
            // When using a directive with replace:true and templateUrl the $compileNodes
            // (or a child element inside of them)
            // might change, so we need to recreate the namespace adapted compileNodes
            // for call to the link function.
            // Note: This will already clone the nodes...
            $linkNode = jqLite(
              wrapTemplate(namespace, jqLite('<div>').append($compileNodes).html())
            );
          } else if (cloneConnectFn) {
            // important!!: we must call our jqLite.clone() since the jQuery one is trying to be smart
            // and sometimes changes the structure of the DOM.
            $linkNode = JQLitePrototype.clone.call($compileNodes);
          } else {
            $linkNode = $compileNodes;
          }
  
          if (transcludeControllers) {
            for (var controllerName in transcludeControllers) {
              $linkNode.data('$' + controllerName + 'Controller', transcludeControllers[controllerName].instance);
            }
          }
  
          compile.$$addScopeInfo($linkNode, scope);
  
          if (cloneConnectFn) cloneConnectFn($linkNode, scope);
          if (compositeLinkFn) compositeLinkFn(scope, $linkNode, $linkNode, parentBoundTranscludeFn);
          return $linkNode;
        };
      }
  
      function detectNamespaceForChildElements(parentElement) {
        // TODO: Make this detect MathML as well...
        var node = parentElement && parentElement[0];
        if (!node) {
          return 'html';
        } else {
          return nodeName_(node) !== 'foreignobject' && node.toString().match(/SVG/) ? 'svg': 'html';
        }
      }
  
      /**
       * Compile function matches each node in nodeList against the directives. Once all directives
       * for a particular node are collected their compile functions are executed. The compile
       * functions return values - the linking functions - are combined into a composite linking
       * function, which is the a linking function for the node.
       *
       * @param {NodeList} nodeList an array of nodes or NodeList to compile
       * @param {function(angular.Scope, cloneAttachFn=)} transcludeFn A linking function, where the
       *        scope argument is auto-generated to the new child of the transcluded parent scope.
       * @param {DOMElement=} $rootElement If the nodeList is the root of the compilation tree then
       *        the rootElement must be set the jqLite collection of the compile root. This is
       *        needed so that the jqLite collection items can be replaced with widgets.
       * @param {number=} maxPriority Max directive priority.
       * @returns {Function} A composite linking function of all of the matched directives or null.
       */
      function compileNodes(nodeList, transcludeFn, $rootElement, maxPriority, ignoreDirective,
                              previousCompileContext) {
        var linkFns = [],
            attrs, directives, nodeLinkFn, childNodes, childLinkFn, linkFnFound, nodeLinkFnFound;
  
        for (var i = 0; i < nodeList.length; i++) {
          attrs = new Attributes();
  
          // we must always refer to nodeList[i] since the nodes can be replaced underneath us.
          directives = collectDirectives(nodeList[i], [], attrs, i === 0 ? maxPriority : undefined,
                                          ignoreDirective);
  
          nodeLinkFn = (directives.length)
              ? applyDirectivesToNode(directives, nodeList[i], attrs, transcludeFn, $rootElement,
                                        null, [], [], previousCompileContext)
              : null;
  
          if (nodeLinkFn && nodeLinkFn.scope) {
            compile.$$addScopeClass(attrs.$$element);
          }
  
          childLinkFn = (nodeLinkFn && nodeLinkFn.terminal ||
                        !(childNodes = nodeList[i].childNodes) ||
                        !childNodes.length)
              ? null
              : compileNodes(childNodes,
                   nodeLinkFn ? (
                    (nodeLinkFn.transcludeOnThisElement || !nodeLinkFn.templateOnThisElement)
                       && nodeLinkFn.transclude) : transcludeFn);
  
          if (nodeLinkFn || childLinkFn) {
            linkFns.push(i, nodeLinkFn, childLinkFn);
            linkFnFound = true;
            nodeLinkFnFound = nodeLinkFnFound || nodeLinkFn;
          }
  
          //use the previous context only for the first element in the virtual group
          previousCompileContext = null;
        }
  
        // return a linking function if we have found anything, null otherwise
        return linkFnFound ? compositeLinkFn : null;
  
        function compositeLinkFn(scope, nodeList, $rootElement, parentBoundTranscludeFn) {
          var nodeLinkFn, childLinkFn, node, childScope, i, ii, idx, childBoundTranscludeFn;
          var stableNodeList;
  
  
          if (nodeLinkFnFound) {
            // copy nodeList so that if a nodeLinkFn removes or adds an element at this DOM level our
            // offsets don't get screwed up
            var nodeListLength = nodeList.length;
            stableNodeList = new Array(nodeListLength);
  
            // create a sparse array by only copying the elements which have a linkFn
            for (i = 0; i < linkFns.length; i+=3) {
              idx = linkFns[i];
              stableNodeList[idx] = nodeList[idx];
            }
          } else {
            stableNodeList = nodeList;
          }
  
          for (i = 0, ii = linkFns.length; i < ii;) {
            node = stableNodeList[linkFns[i++]];
            nodeLinkFn = linkFns[i++];
            childLinkFn = linkFns[i++];
  
            if (nodeLinkFn) {
              if (nodeLinkFn.scope) {
                childScope = scope.$new();
                compile.$$addScopeInfo(jqLite(node), childScope);
              } else {
                childScope = scope;
              }
  
              if (nodeLinkFn.transcludeOnThisElement) {
                childBoundTranscludeFn = createBoundTranscludeFn(
                    scope, nodeLinkFn.transclude, parentBoundTranscludeFn,
                    nodeLinkFn.elementTranscludeOnThisElement);
  
              } else if (!nodeLinkFn.templateOnThisElement && parentBoundTranscludeFn) {
                childBoundTranscludeFn = parentBoundTranscludeFn;
  
              } else if (!parentBoundTranscludeFn && transcludeFn) {
                childBoundTranscludeFn = createBoundTranscludeFn(scope, transcludeFn);
  
              } else {
                childBoundTranscludeFn = null;
              }
  
              nodeLinkFn(childLinkFn, childScope, node, $rootElement, childBoundTranscludeFn);
  
            } else if (childLinkFn) {
              childLinkFn(scope, node.childNodes, undefined, parentBoundTranscludeFn);
            }
          }
        }
      }
  
      function createBoundTranscludeFn(scope, transcludeFn, previousBoundTranscludeFn, elementTransclusion) {
  
        var boundTranscludeFn = function(transcludedScope, cloneFn, controllers, futureParentElement, containingScope) {
  
          if (!transcludedScope) {
            transcludedScope = scope.$new(false, containingScope);
            transcludedScope.$$transcluded = true;
          }
  
          return transcludeFn(transcludedScope, cloneFn, {
            parentBoundTranscludeFn: previousBoundTranscludeFn,
            transcludeControllers: controllers,
            futureParentElement: futureParentElement
          });
        };
  
        return boundTranscludeFn;
      }
  
      /**
       * Looks for directives on the given node and adds them to the directive collection which is
       * sorted.
       *
       * @param node Node to search.
       * @param directives An array to which the directives are added to. This array is sorted before
       *        the function returns.
       * @param attrs The shared attrs object which is used to populate the normalized attributes.
       * @param {number=} maxPriority Max directive priority.
       */
      function collectDirectives(node, directives, attrs, maxPriority, ignoreDirective) {
        var nodeType = node.nodeType,
            attrsMap = attrs.$attr,
            match,
            className;
  
        switch (nodeType) {
          case NODE_TYPE_ELEMENT: /* Element */
            // use the node name: <directive>
            addDirective(directives,
                directiveNormalize(nodeName_(node)), 'E', maxPriority, ignoreDirective);
  
            // iterate over the attributes
            for (var attr, name, nName, ngAttrName, value, isNgAttr, nAttrs = node.attributes,
                     j = 0, jj = nAttrs && nAttrs.length; j < jj; j++) {
              var attrStartName = false;
              var attrEndName = false;
  
              attr = nAttrs[j];
              name = attr.name;
              value = trim(attr.value);
  
              // support ngAttr attribute binding
              ngAttrName = directiveNormalize(name);
              if (isNgAttr = NG_ATTR_BINDING.test(ngAttrName)) {
                name = snake_case(ngAttrName.substr(6), '-');
              }
  
              var directiveNName = ngAttrName.replace(/(Start|End)$/, '');
              if (directiveIsMultiElement(directiveNName)) {
                if (ngAttrName === directiveNName + 'Start') {
                  attrStartName = name;
                  attrEndName = name.substr(0, name.length - 5) + 'end';
                  name = name.substr(0, name.length - 6);
                }
              }
  
              nName = directiveNormalize(name.toLowerCase());
              attrsMap[nName] = name;
              if (isNgAttr || !attrs.hasOwnProperty(nName)) {
                  attrs[nName] = value;
                  if (getBooleanAttrName(node, nName)) {
                    attrs[nName] = true; // presence means true
                  }
              }
              addAttrInterpolateDirective(node, directives, value, nName, isNgAttr);
              addDirective(directives, nName, 'A', maxPriority, ignoreDirective, attrStartName,
                            attrEndName);
            }
  
            // use class as directive
            className = node.className;
            if (isString(className) && className !== '') {
              while (match = CLASS_DIRECTIVE_REGEXP.exec(className)) {
                nName = directiveNormalize(match[2]);
                if (addDirective(directives, nName, 'C', maxPriority, ignoreDirective)) {
                  attrs[nName] = trim(match[3]);
                }
                className = className.substr(match.index + match[0].length);
              }
            }
            break;
          case NODE_TYPE_TEXT: /* Text Node */
            addTextInterpolateDirective(directives, node.nodeValue);
            break;
          case NODE_TYPE_COMMENT: /* Comment */
            try {
              match = COMMENT_DIRECTIVE_REGEXP.exec(node.nodeValue);
              if (match) {
                nName = directiveNormalize(match[1]);
                if (addDirective(directives, nName, 'M', maxPriority, ignoreDirective)) {
                  attrs[nName] = trim(match[2]);
                }
              }
            } catch (e) {
              // turns out that under some circumstances IE9 throws errors when one attempts to read
              // comment's node value.
              // Just ignore it and continue. (Can't seem to reproduce in test case.)
            }
            break;
        }
  
        directives.sort(byPriority);
        return directives;
      }
  
      /**
       * Given a node with an directive-start it collects all of the siblings until it finds
       * directive-end.
       * @param node
       * @param attrStart
       * @param attrEnd
       * @returns {*}
       */
      function groupScan(node, attrStart, attrEnd) {
        var nodes = [];
        var depth = 0;
        if (attrStart && node.hasAttribute && node.hasAttribute(attrStart)) {
          do {
            if (!node) {
              throw $compileMinErr('uterdir',
                        "Unterminated attribute, found '{0}' but no matching '{1}' found.",
                        attrStart, attrEnd);
            }
            if (node.nodeType == NODE_TYPE_ELEMENT) {
              if (node.hasAttribute(attrStart)) depth++;
              if (node.hasAttribute(attrEnd)) depth--;
            }
            nodes.push(node);
            node = node.nextSibling;
          } while (depth > 0);
        } else {
          nodes.push(node);
        }
  
        return jqLite(nodes);
      }
  
      /**
       * Wrapper for linking function which converts normal linking function into a grouped
       * linking function.
       * @param linkFn
       * @param attrStart
       * @param attrEnd
       * @returns {Function}
       */
      function groupElementsLinkFnWrapper(linkFn, attrStart, attrEnd) {
        return function(scope, element, attrs, controllers, transcludeFn) {
          element = groupScan(element[0], attrStart, attrEnd);
          return linkFn(scope, element, attrs, controllers, transcludeFn);
        };
      }
  
      /**
       * Once the directives have been collected, their compile functions are executed. This method
       * is responsible for inlining directive templates as well as terminating the application
       * of the directives if the terminal directive has been reached.
       *
       * @param {Array} directives Array of collected directives to execute their compile function.
       *        this needs to be pre-sorted by priority order.
       * @param {Node} compileNode The raw DOM node to apply the compile functions to
       * @param {Object} templateAttrs The shared attribute function
       * @param {function(angular.Scope, cloneAttachFn=)} transcludeFn A linking function, where the
       *                                                  scope argument is auto-generated to the new
       *                                                  child of the transcluded parent scope.
       * @param {JQLite} jqCollection If we are working on the root of the compile tree then this
       *                              argument has the root jqLite array so that we can replace nodes
       *                              on it.
       * @param {Object=} originalReplaceDirective An optional directive that will be ignored when
       *                                           compiling the transclusion.
       * @param {Array.<Function>} preLinkFns
       * @param {Array.<Function>} postLinkFns
       * @param {Object} previousCompileContext Context used for previous compilation of the current
       *                                        node
       * @returns {Function} linkFn
       */
      function applyDirectivesToNode(directives, compileNode, templateAttrs, transcludeFn,
                                     jqCollection, originalReplaceDirective, preLinkFns, postLinkFns,
                                     previousCompileContext) {
        previousCompileContext = previousCompileContext || {};
  
        var terminalPriority = -Number.MAX_VALUE,
            newScopeDirective,
            controllerDirectives = previousCompileContext.controllerDirectives,
            controllers,
            newIsolateScopeDirective = previousCompileContext.newIsolateScopeDirective,
            templateDirective = previousCompileContext.templateDirective,
            nonTlbTranscludeDirective = previousCompileContext.nonTlbTranscludeDirective,
            hasTranscludeDirective = false,
            hasTemplate = false,
            hasElementTranscludeDirective = previousCompileContext.hasElementTranscludeDirective,
            $compileNode = templateAttrs.$$element = jqLite(compileNode),
            directive,
            directiveName,
            $template,
            replaceDirective = originalReplaceDirective,
            childTranscludeFn = transcludeFn,
            linkFn,
            directiveValue;
  
        // executes all directives on the current element
        for (var i = 0, ii = directives.length; i < ii; i++) {
          directive = directives[i];
          var attrStart = directive.$$start;
          var attrEnd = directive.$$end;
  
          // collect multiblock sections
          if (attrStart) {
            $compileNode = groupScan(compileNode, attrStart, attrEnd);
          }
          $template = undefined;
  
          if (terminalPriority > directive.priority) {
            break; // prevent further processing of directives
          }
  
          if (directiveValue = directive.scope) {
  
            // skip the check for directives with async templates, we'll check the derived sync
            // directive when the template arrives
            if (!directive.templateUrl) {
              if (isObject(directiveValue)) {
                // This directive is trying to add an isolated scope.
                // Check that there is no scope of any kind already
                assertNoDuplicate('new/isolated scope', newIsolateScopeDirective || newScopeDirective,
                                  directive, $compileNode);
                newIsolateScopeDirective = directive;
              } else {
                // This directive is trying to add a child scope.
                // Check that there is no isolated scope already
                assertNoDuplicate('new/isolated scope', newIsolateScopeDirective, directive,
                                  $compileNode);
              }
            }
  
            newScopeDirective = newScopeDirective || directive;
          }
  
          directiveName = directive.name;
  
          if (!directive.templateUrl && directive.controller) {
            directiveValue = directive.controller;
            controllerDirectives = controllerDirectives || {};
            assertNoDuplicate("'" + directiveName + "' controller",
                controllerDirectives[directiveName], directive, $compileNode);
            controllerDirectives[directiveName] = directive;
          }
  
          if (directiveValue = directive.transclude) {
            hasTranscludeDirective = true;
  
            // Special case ngIf and ngRepeat so that we don't complain about duplicate transclusion.
            // This option should only be used by directives that know how to safely handle element transclusion,
            // where the transcluded nodes are added or replaced after linking.
            if (!directive.$$tlb) {
              assertNoDuplicate('transclusion', nonTlbTranscludeDirective, directive, $compileNode);
              nonTlbTranscludeDirective = directive;
            }
  
            if (directiveValue == 'element') {
              hasElementTranscludeDirective = true;
              terminalPriority = directive.priority;
              $template = $compileNode;
              $compileNode = templateAttrs.$$element =
                  jqLite(document.createComment(' ' + directiveName + ': ' +
                                                templateAttrs[directiveName] + ' '));
              compileNode = $compileNode[0];
              replaceWith(jqCollection, sliceArgs($template), compileNode);
  
              childTranscludeFn = compile($template, transcludeFn, terminalPriority,
                                          replaceDirective && replaceDirective.name, {
                                            // Don't pass in:
                                            // - controllerDirectives - otherwise we'll create duplicates controllers
                                            // - newIsolateScopeDirective or templateDirective - combining templates with
                                            //   element transclusion doesn't make sense.
                                            //
                                            // We need only nonTlbTranscludeDirective so that we prevent putting transclusion
                                            // on the same element more than once.
                                            nonTlbTranscludeDirective: nonTlbTranscludeDirective
                                          });
            } else {
              $template = jqLite(jqLiteClone(compileNode)).contents();
              $compileNode.empty(); // clear contents
              childTranscludeFn = compile($template, transcludeFn);
            }
          }
  
          if (directive.template) {
            hasTemplate = true;
            assertNoDuplicate('template', templateDirective, directive, $compileNode);
            templateDirective = directive;
  
            directiveValue = (isFunction(directive.template))
                ? directive.template($compileNode, templateAttrs)
                : directive.template;
  
            directiveValue = denormalizeTemplate(directiveValue);
  
            if (directive.replace) {
              replaceDirective = directive;
              if (jqLiteIsTextNode(directiveValue)) {
                $template = [];
              } else {
                $template = removeComments(wrapTemplate(directive.templateNamespace, trim(directiveValue)));
              }
              compileNode = $template[0];
  
              if ($template.length != 1 || compileNode.nodeType !== NODE_TYPE_ELEMENT) {
                throw $compileMinErr('tplrt',
                    "Template for directive '{0}' must have exactly one root element. {1}",
                    directiveName, '');
              }
  
              replaceWith(jqCollection, $compileNode, compileNode);
  
              var newTemplateAttrs = {$attr: {}};
  
              // combine directives from the original node and from the template:
              // - take the array of directives for this element
              // - split it into two parts, those that already applied (processed) and those that weren't (unprocessed)
              // - collect directives from the template and sort them by priority
              // - combine directives as: processed + template + unprocessed
              var templateDirectives = collectDirectives(compileNode, [], newTemplateAttrs);
              var unprocessedDirectives = directives.splice(i + 1, directives.length - (i + 1));
  
              if (newIsolateScopeDirective) {
                markDirectivesAsIsolate(templateDirectives);
              }
              directives = directives.concat(templateDirectives).concat(unprocessedDirectives);
              mergeTemplateAttributes(templateAttrs, newTemplateAttrs);
  
              ii = directives.length;
            } else {
              $compileNode.html(directiveValue);
            }
          }
  
          if (directive.templateUrl) {
            hasTemplate = true;
            assertNoDuplicate('template', templateDirective, directive, $compileNode);
            templateDirective = directive;
  
            if (directive.replace) {
              replaceDirective = directive;
            }
  
            nodeLinkFn = compileTemplateUrl(directives.splice(i, directives.length - i), $compileNode,
                templateAttrs, jqCollection, hasTranscludeDirective && childTranscludeFn, preLinkFns, postLinkFns, {
                  controllerDirectives: controllerDirectives,
                  newIsolateScopeDirective: newIsolateScopeDirective,
                  templateDirective: templateDirective,
                  nonTlbTranscludeDirective: nonTlbTranscludeDirective
                });
            ii = directives.length;
          } else if (directive.compile) {
            try {
              linkFn = directive.compile($compileNode, templateAttrs, childTranscludeFn);
              if (isFunction(linkFn)) {
                addLinkFns(null, linkFn, attrStart, attrEnd);
              } else if (linkFn) {
                addLinkFns(linkFn.pre, linkFn.post, attrStart, attrEnd);
              }
            } catch (e) {
              $exceptionHandler(e, startingTag($compileNode));
            }
          }
  
          if (directive.terminal) {
            nodeLinkFn.terminal = true;
            terminalPriority = Math.max(terminalPriority, directive.priority);
          }
  
        }
  
        nodeLinkFn.scope = newScopeDirective && newScopeDirective.scope === true;
        nodeLinkFn.transcludeOnThisElement = hasTranscludeDirective;
        nodeLinkFn.elementTranscludeOnThisElement = hasElementTranscludeDirective;
        nodeLinkFn.templateOnThisElement = hasTemplate;
        nodeLinkFn.transclude = childTranscludeFn;
  
        previousCompileContext.hasElementTranscludeDirective = hasElementTranscludeDirective;
  
        // might be normal or delayed nodeLinkFn depending on if templateUrl is present
        return nodeLinkFn;
  
        ////////////////////
  
        function addLinkFns(pre, post, attrStart, attrEnd) {
          if (pre) {
            if (attrStart) pre = groupElementsLinkFnWrapper(pre, attrStart, attrEnd);
            pre.require = directive.require;
            pre.directiveName = directiveName;
            if (newIsolateScopeDirective === directive || directive.$$isolateScope) {
              pre = cloneAndAnnotateFn(pre, {isolateScope: true});
            }
            preLinkFns.push(pre);
          }
          if (post) {
            if (attrStart) post = groupElementsLinkFnWrapper(post, attrStart, attrEnd);
            post.require = directive.require;
            post.directiveName = directiveName;
            if (newIsolateScopeDirective === directive || directive.$$isolateScope) {
              post = cloneAndAnnotateFn(post, {isolateScope: true});
            }
            postLinkFns.push(post);
          }
        }
  
  
        function getControllers(directiveName, require, $element, elementControllers) {
          var value, retrievalMethod = 'data', optional = false;
          var $searchElement = $element;
          var match;
          if (isString(require)) {
            match = require.match(REQUIRE_PREFIX_REGEXP);
            require = require.substring(match[0].length);
  
            if (match[3]) {
              if (match[1]) match[3] = null;
              else match[1] = match[3];
            }
            if (match[1] === '^') {
              retrievalMethod = 'inheritedData';
            } else if (match[1] === '^^') {
              retrievalMethod = 'inheritedData';
              $searchElement = $element.parent();
            }
            if (match[2] === '?') {
              optional = true;
            }
  
            value = null;
  
            if (elementControllers && retrievalMethod === 'data') {
              if (value = elementControllers[require]) {
                value = value.instance;
              }
            }
            value = value || $searchElement[retrievalMethod]('$' + require + 'Controller');
  
            if (!value && !optional) {
              throw $compileMinErr('ctreq',
                  "Controller '{0}', required by directive '{1}', can't be found!",
                  require, directiveName);
            }
            return value || null;
          } else if (isArray(require)) {
            value = [];
            forEach(require, function(require) {
              value.push(getControllers(directiveName, require, $element, elementControllers));
            });
          }
          return value;
        }
  
  
        function nodeLinkFn(childLinkFn, scope, linkNode, $rootElement, boundTranscludeFn) {
          var i, ii, linkFn, controller, isolateScope, elementControllers, transcludeFn, $element,
              attrs;
  
          if (compileNode === linkNode) {
            attrs = templateAttrs;
            $element = templateAttrs.$$element;
          } else {
            $element = jqLite(linkNode);
            attrs = new Attributes($element, templateAttrs);
          }
  
          if (newIsolateScopeDirective) {
            isolateScope = scope.$new(true);
          }
  
          if (boundTranscludeFn) {
            // track `boundTranscludeFn` so it can be unwrapped if `transcludeFn`
            // is later passed as `parentBoundTranscludeFn` to `publicLinkFn`
            transcludeFn = controllersBoundTransclude;
            transcludeFn.$$boundTransclude = boundTranscludeFn;
          }
  
          if (controllerDirectives) {
            // TODO: merge `controllers` and `elementControllers` into single object.
            controllers = {};
            elementControllers = {};
            forEach(controllerDirectives, function(directive) {
              var locals = {
                $scope: directive === newIsolateScopeDirective || directive.$$isolateScope ? isolateScope : scope,
                $element: $element,
                $attrs: attrs,
                $transclude: transcludeFn
              }, controllerInstance;
  
              controller = directive.controller;
              if (controller == '@') {
                controller = attrs[directive.name];
              }
  
              controllerInstance = $controller(controller, locals, true, directive.controllerAs);
  
              // For directives with element transclusion the element is a comment,
              // but jQuery .data doesn't support attaching data to comment nodes as it's hard to
              // clean up (http://bugs.jquery.com/ticket/8335).
              // Instead, we save the controllers for the element in a local hash and attach to .data
              // later, once we have the actual element.
              elementControllers[directive.name] = controllerInstance;
              if (!hasElementTranscludeDirective) {
                $element.data('$' + directive.name + 'Controller', controllerInstance.instance);
              }
  
              controllers[directive.name] = controllerInstance;
            });
          }
  
          if (newIsolateScopeDirective) {
            compile.$$addScopeInfo($element, isolateScope, true, !(templateDirective && (templateDirective === newIsolateScopeDirective ||
                templateDirective === newIsolateScopeDirective.$$originalDirective)));
            compile.$$addScopeClass($element, true);
  
            var isolateScopeController = controllers && controllers[newIsolateScopeDirective.name];
            var isolateBindingContext = isolateScope;
            if (isolateScopeController && isolateScopeController.identifier &&
                newIsolateScopeDirective.bindToController === true) {
              isolateBindingContext = isolateScopeController.instance;
            }
  
            forEach(isolateScope.$$isolateBindings = newIsolateScopeDirective.$$isolateBindings, function(definition, scopeName) {
              var attrName = definition.attrName,
                  optional = definition.optional,
                  mode = definition.mode, // @, =, or &
                  lastValue,
                  parentGet, parentSet, compare;
  
              switch (mode) {
  
                case '@':
                  attrs.$observe(attrName, function(value) {
                    isolateBindingContext[scopeName] = value;
                  });
                  attrs.$$observers[attrName].$$scope = scope;
                  if (attrs[attrName]) {
                    // If the attribute has been provided then we trigger an interpolation to ensure
                    // the value is there for use in the link fn
                    isolateBindingContext[scopeName] = $interpolate(attrs[attrName])(scope);
                  }
                  break;
  
                case '=':
                  if (optional && !attrs[attrName]) {
                    return;
                  }
                  parentGet = $parse(attrs[attrName]);
                  if (parentGet.literal) {
                    compare = equals;
                  } else {
                    compare = function(a, b) { return a === b || (a !== a && b !== b); };
                  }
                  parentSet = parentGet.assign || function() {
                    // reset the change, or we will throw this exception on every $digest
                    lastValue = isolateBindingContext[scopeName] = parentGet(scope);
                    throw $compileMinErr('nonassign',
                        "Expression '{0}' used with directive '{1}' is non-assignable!",
                        attrs[attrName], newIsolateScopeDirective.name);
                  };
                  lastValue = isolateBindingContext[scopeName] = parentGet(scope);
                  var parentValueWatch = function parentValueWatch(parentValue) {
                    if (!compare(parentValue, isolateBindingContext[scopeName])) {
                      // we are out of sync and need to copy
                      if (!compare(parentValue, lastValue)) {
                        // parent changed and it has precedence
                        isolateBindingContext[scopeName] = parentValue;
                      } else {
                        // if the parent can be assigned then do so
                        parentSet(scope, parentValue = isolateBindingContext[scopeName]);
                      }
                    }
                    return lastValue = parentValue;
                  };
                  parentValueWatch.$stateful = true;
                  var unwatch;
                  if (definition.collection) {
                    unwatch = scope.$watchCollection(attrs[attrName], parentValueWatch);
                  } else {
                    unwatch = scope.$watch($parse(attrs[attrName], parentValueWatch), null, parentGet.literal);
                  }
                  isolateScope.$on('$destroy', unwatch);
                  break;
  
                case '&':
                  parentGet = $parse(attrs[attrName]);
                  isolateBindingContext[scopeName] = function(locals) {
                    return parentGet(scope, locals);
                  };
                  break;
              }
            });
          }
          if (controllers) {
            forEach(controllers, function(controller) {
              controller();
            });
            controllers = null;
          }
  
          // PRELINKING
          for (i = 0, ii = preLinkFns.length; i < ii; i++) {
            linkFn = preLinkFns[i];
            invokeLinkFn(linkFn,
                linkFn.isolateScope ? isolateScope : scope,
                $element,
                attrs,
                linkFn.require && getControllers(linkFn.directiveName, linkFn.require, $element, elementControllers),
                transcludeFn
            );
          }
  
          // RECURSION
          // We only pass the isolate scope, if the isolate directive has a template,
          // otherwise the child elements do not belong to the isolate directive.
          var scopeToChild = scope;
          if (newIsolateScopeDirective && (newIsolateScopeDirective.template || newIsolateScopeDirective.templateUrl === null)) {
            scopeToChild = isolateScope;
          }
          childLinkFn && childLinkFn(scopeToChild, linkNode.childNodes, undefined, boundTranscludeFn);
  
          // POSTLINKING
          for (i = postLinkFns.length - 1; i >= 0; i--) {
            linkFn = postLinkFns[i];
            invokeLinkFn(linkFn,
                linkFn.isolateScope ? isolateScope : scope,
                $element,
                attrs,
                linkFn.require && getControllers(linkFn.directiveName, linkFn.require, $element, elementControllers),
                transcludeFn
            );
          }
  
          // This is the function that is injected as `$transclude`.
          // Note: all arguments are optional!
          function controllersBoundTransclude(scope, cloneAttachFn, futureParentElement) {
            var transcludeControllers;
  
            // No scope passed in:
            if (!isScope(scope)) {
              futureParentElement = cloneAttachFn;
              cloneAttachFn = scope;
              scope = undefined;
            }
  
            if (hasElementTranscludeDirective) {
              transcludeControllers = elementControllers;
            }
            if (!futureParentElement) {
              futureParentElement = hasElementTranscludeDirective ? $element.parent() : $element;
            }
            return boundTranscludeFn(scope, cloneAttachFn, transcludeControllers, futureParentElement, scopeToChild);
          }
        }
      }
  
      function markDirectivesAsIsolate(directives) {
        // mark all directives as needing isolate scope.
        for (var j = 0, jj = directives.length; j < jj; j++) {
          directives[j] = inherit(directives[j], {$$isolateScope: true});
        }
      }
  
      /**
       * looks up the directive and decorates it with exception handling and proper parameters. We
       * call this the boundDirective.
       *
       * @param {string} name name of the directive to look up.
       * @param {string} location The directive must be found in specific format.
       *   String containing any of theses characters:
       *
       *   * `E`: element name
       *   * `A': attribute
       *   * `C`: class
       *   * `M`: comment
       * @returns {boolean} true if directive was added.
       */
      function addDirective(tDirectives, name, location, maxPriority, ignoreDirective, startAttrName,
                            endAttrName) {
        if (name === ignoreDirective) return null;
        var match = null;
        if (hasDirectives.hasOwnProperty(name)) {
          for (var directive, directives = $injector.get(name + Suffix),
              i = 0, ii = directives.length; i<ii; i++) {
            try {
              directive = directives[i];
              if ((maxPriority === undefined || maxPriority > directive.priority) &&
                   directive.restrict.indexOf(location) != -1) {
                if (startAttrName) {
                  directive = inherit(directive, {$$start: startAttrName, $$end: endAttrName});
                }
                tDirectives.push(directive);
                match = directive;
              }
            } catch (e) { $exceptionHandler(e); }
          }
        }
        return match;
      }
  
  
      /**
       * looks up the directive and returns true if it is a multi-element directive,
       * and therefore requires DOM nodes between -start and -end markers to be grouped
       * together.
       *
       * @param {string} name name of the directive to look up.
       * @returns true if directive was registered as multi-element.
       */
      function directiveIsMultiElement(name) {
        if (hasDirectives.hasOwnProperty(name)) {
          for (var directive, directives = $injector.get(name + Suffix),
              i = 0, ii = directives.length; i<ii; i++) {
            directive = directives[i];
            if (directive.multiElement) {
              return true;
            }
          }
        }
        return false;
      }
  
      /**
       * When the element is replaced with HTML template then the new attributes
       * on the template need to be merged with the existing attributes in the DOM.
       * The desired effect is to have both of the attributes present.
       *
       * @param {object} dst destination attributes (original DOM)
       * @param {object} src source attributes (from the directive template)
       */
      function mergeTemplateAttributes(dst, src) {
        var srcAttr = src.$attr,
            dstAttr = dst.$attr,
            $element = dst.$$element;
  
        // reapply the old attributes to the new element
        forEach(dst, function(value, key) {
          if (key.charAt(0) != '$') {
            if (src[key] && src[key] !== value) {
              value += (key === 'style' ? ';' : ' ') + src[key];
            }
            dst.$set(key, value, true, srcAttr[key]);
          }
        });
  
        // copy the new attributes on the old attrs object
        forEach(src, function(value, key) {
          if (key == 'class') {
            safeAddClass($element, value);
            dst['class'] = (dst['class'] ? dst['class'] + ' ' : '') + value;
          } else if (key == 'style') {
            $element.attr('style', $element.attr('style') + ';' + value);
            dst['style'] = (dst['style'] ? dst['style'] + ';' : '') + value;
            // `dst` will never contain hasOwnProperty as DOM parser won't let it.
            // You will get an "InvalidCharacterError: DOM Exception 5" error if you
            // have an attribute like "has-own-property" or "data-has-own-property", etc.
          } else if (key.charAt(0) != '$' && !dst.hasOwnProperty(key)) {
            dst[key] = value;
            dstAttr[key] = srcAttr[key];
          }
        });
      }
  
  
      function compileTemplateUrl(directives, $compileNode, tAttrs,
          $rootElement, childTranscludeFn, preLinkFns, postLinkFns, previousCompileContext) {
        var linkQueue = [],
            afterTemplateNodeLinkFn,
            afterTemplateChildLinkFn,
            beforeTemplateCompileNode = $compileNode[0],
            origAsyncDirective = directives.shift(),
            // The fact that we have to copy and patch the directive seems wrong!
            derivedSyncDirective = extend({}, origAsyncDirective, {
              templateUrl: null, transclude: null, replace: null, $$originalDirective: origAsyncDirective
            }),
            templateUrl = (isFunction(origAsyncDirective.templateUrl))
                ? origAsyncDirective.templateUrl($compileNode, tAttrs)
                : origAsyncDirective.templateUrl,
            templateNamespace = origAsyncDirective.templateNamespace;
  
        $compileNode.empty();
  
        $templateRequest($sce.getTrustedResourceUrl(templateUrl))
          .then(function(content) {
            var compileNode, tempTemplateAttrs, $template, childBoundTranscludeFn;
  
            content = denormalizeTemplate(content);
  
            if (origAsyncDirective.replace) {
              if (jqLiteIsTextNode(content)) {
                $template = [];
              } else {
                $template = removeComments(wrapTemplate(templateNamespace, trim(content)));
              }
              compileNode = $template[0];
  
              if ($template.length != 1 || compileNode.nodeType !== NODE_TYPE_ELEMENT) {
                throw $compileMinErr('tplrt',
                    "Template for directive '{0}' must have exactly one root element. {1}",
                    origAsyncDirective.name, templateUrl);
              }
  
              tempTemplateAttrs = {$attr: {}};
              replaceWith($rootElement, $compileNode, compileNode);
              var templateDirectives = collectDirectives(compileNode, [], tempTemplateAttrs);
  
              if (isObject(origAsyncDirective.scope)) {
                markDirectivesAsIsolate(templateDirectives);
              }
              directives = templateDirectives.concat(directives);
              mergeTemplateAttributes(tAttrs, tempTemplateAttrs);
            } else {
              compileNode = beforeTemplateCompileNode;
              $compileNode.html(content);
            }
  
            directives.unshift(derivedSyncDirective);
  
            afterTemplateNodeLinkFn = applyDirectivesToNode(directives, compileNode, tAttrs,
                childTranscludeFn, $compileNode, origAsyncDirective, preLinkFns, postLinkFns,
                previousCompileContext);
            forEach($rootElement, function(node, i) {
              if (node == compileNode) {
                $rootElement[i] = $compileNode[0];
              }
            });
            afterTemplateChildLinkFn = compileNodes($compileNode[0].childNodes, childTranscludeFn);
  
            while (linkQueue.length) {
              var scope = linkQueue.shift(),
                  beforeTemplateLinkNode = linkQueue.shift(),
                  linkRootElement = linkQueue.shift(),
                  boundTranscludeFn = linkQueue.shift(),
                  linkNode = $compileNode[0];
  
              if (scope.$$destroyed) continue;
  
              if (beforeTemplateLinkNode !== beforeTemplateCompileNode) {
                var oldClasses = beforeTemplateLinkNode.className;
  
                if (!(previousCompileContext.hasElementTranscludeDirective &&
                    origAsyncDirective.replace)) {
                  // it was cloned therefore we have to clone as well.
                  linkNode = jqLiteClone(compileNode);
                }
                replaceWith(linkRootElement, jqLite(beforeTemplateLinkNode), linkNode);
  
                // Copy in CSS classes from original node
                safeAddClass(jqLite(linkNode), oldClasses);
              }
              if (afterTemplateNodeLinkFn.transcludeOnThisElement) {
                childBoundTranscludeFn = createBoundTranscludeFn(scope, afterTemplateNodeLinkFn.transclude, boundTranscludeFn);
              } else {
                childBoundTranscludeFn = boundTranscludeFn;
              }
              afterTemplateNodeLinkFn(afterTemplateChildLinkFn, scope, linkNode, $rootElement,
                childBoundTranscludeFn);
            }
            linkQueue = null;
          });
  
        return function delayedNodeLinkFn(ignoreChildLinkFn, scope, node, rootElement, boundTranscludeFn) {
          var childBoundTranscludeFn = boundTranscludeFn;
          if (scope.$$destroyed) return;
          if (linkQueue) {
            linkQueue.push(scope);
            linkQueue.push(node);
            linkQueue.push(rootElement);
            linkQueue.push(childBoundTranscludeFn);
          } else {
            if (afterTemplateNodeLinkFn.transcludeOnThisElement) {
              childBoundTranscludeFn = createBoundTranscludeFn(scope, afterTemplateNodeLinkFn.transclude, boundTranscludeFn);
            }
            afterTemplateNodeLinkFn(afterTemplateChildLinkFn, scope, node, rootElement, childBoundTranscludeFn);
          }
        };
      }
  
  
      /**
       * Sorting function for bound directives.
       */
      function byPriority(a, b) {
        var diff = b.priority - a.priority;
        if (diff !== 0) return diff;
        if (a.name !== b.name) return (a.name < b.name) ? -1 : 1;
        return a.index - b.index;
      }
  
  
      function assertNoDuplicate(what, previousDirective, directive, element) {
        if (previousDirective) {
          throw $compileMinErr('multidir', 'Multiple directives [{0}, {1}] asking for {2} on: {3}',
              previousDirective.name, directive.name, what, startingTag(element));
        }
      }
  
  
      function addTextInterpolateDirective(directives, text) {
        var interpolateFn = $interpolate(text, true);
        if (interpolateFn) {
          directives.push({
            priority: 0,
            compile: function textInterpolateCompileFn(templateNode) {
              var templateNodeParent = templateNode.parent(),
                  hasCompileParent = !!templateNodeParent.length;
  
              // When transcluding a template that has bindings in the root
              // we don't have a parent and thus need to add the class during linking fn.
              if (hasCompileParent) compile.$$addBindingClass(templateNodeParent);
  
              return function textInterpolateLinkFn(scope, node) {
                var parent = node.parent();
                if (!hasCompileParent) compile.$$addBindingClass(parent);
                compile.$$addBindingInfo(parent, interpolateFn.expressions);
                scope.$watch(interpolateFn, function interpolateFnWatchAction(value) {
                  node[0].nodeValue = value;
                });
              };
            }
          });
        }
      }
  
  
      function wrapTemplate(type, template) {
        type = lowercase(type || 'html');
        switch (type) {
        case 'svg':
        case 'math':
          var wrapper = document.createElement('div');
          wrapper.innerHTML = '<'+type+'>'+template+'</'+type+'>';
          return wrapper.childNodes[0].childNodes;
        default:
          return template;
        }
      }
  
  
      function getTrustedContext(node, attrNormalizedName) {
        if (attrNormalizedName == "srcdoc") {
          return $sce.HTML;
        }
        var tag = nodeName_(node);
        // maction[xlink:href] can source SVG.  It's not limited to <maction>.
        if (attrNormalizedName == "xlinkHref" ||
            (tag == "form" && attrNormalizedName == "action") ||
            (tag != "img" && (attrNormalizedName == "src" ||
                              attrNormalizedName == "ngSrc"))) {
          return $sce.RESOURCE_URL;
        }
      }
  
  
      function addAttrInterpolateDirective(node, directives, value, name, allOrNothing) {
        var interpolateFn = $interpolate(value, true);
  
        // no interpolation found -> ignore
        if (!interpolateFn) return;
  
  
        if (name === "multiple" && nodeName_(node) === "select") {
          throw $compileMinErr("selmulti",
              "Binding to the 'multiple' attribute is not supported. Element: {0}",
              startingTag(node));
        }
  
        directives.push({
          priority: 100,
          compile: function() {
              return {
                pre: function attrInterpolatePreLinkFn(scope, element, attr) {
                  var $$observers = (attr.$$observers || (attr.$$observers = {}));
  
                  if (EVENT_HANDLER_ATTR_REGEXP.test(name)) {
                    throw $compileMinErr('nodomevents',
                        "Interpolations for HTML DOM event attributes are disallowed.  Please use the " +
                            "ng- versions (such as ng-click instead of onclick) instead.");
                  }
  
                  // If the attribute was removed, then we are done
                  if (!attr[name]) {
                    return;
                  }
  
                  // we need to interpolate again, in case the attribute value has been updated
                  // (e.g. by another directive's compile function)
                  interpolateFn = $interpolate(attr[name], true, getTrustedContext(node, name),
                      ALL_OR_NOTHING_ATTRS[name] || allOrNothing);
  
                  // if attribute was updated so that there is no interpolation going on we don't want to
                  // register any observers
                  if (!interpolateFn) return;
  
                  // initialize attr object so that it's ready in case we need the value for isolate
                  // scope initialization, otherwise the value would not be available from isolate
                  // directive's linking fn during linking phase
                  attr[name] = interpolateFn(scope);
  
                  ($$observers[name] || ($$observers[name] = [])).$$inter = true;
                  (attr.$$observers && attr.$$observers[name].$$scope || scope).
                    $watch(interpolateFn, function interpolateFnWatchAction(newValue, oldValue) {
                      //special case for class attribute addition + removal
                      //so that class changes can tap into the animation
                      //hooks provided by the $animate service. Be sure to
                      //skip animations when the first digest occurs (when
                      //both the new and the old values are the same) since
                      //the CSS classes are the non-interpolated values
                      if (name === 'class' && newValue != oldValue) {
                        attr.$updateClass(newValue, oldValue);
                      } else {
                        attr.$set(name, newValue);
                      }
                    });
                }
              };
            }
        });
      }
  
  
      /**
       * This is a special jqLite.replaceWith, which can replace items which
       * have no parents, provided that the containing jqLite collection is provided.
       *
       * @param {JqLite=} $rootElement The root of the compile tree. Used so that we can replace nodes
       *                               in the root of the tree.
       * @param {JqLite} elementsToRemove The jqLite element which we are going to replace. We keep
       *                                  the shell, but replace its DOM node reference.
       * @param {Node} newNode The new DOM node.
       */
      function replaceWith($rootElement, elementsToRemove, newNode) {
        var firstElementToRemove = elementsToRemove[0],
            removeCount = elementsToRemove.length,
            parent = firstElementToRemove.parentNode,
            i, ii;
  
        if ($rootElement) {
          for (i = 0, ii = $rootElement.length; i < ii; i++) {
            if ($rootElement[i] == firstElementToRemove) {
              $rootElement[i++] = newNode;
              for (var j = i, j2 = j + removeCount - 1,
                       jj = $rootElement.length;
                   j < jj; j++, j2++) {
                if (j2 < jj) {
                  $rootElement[j] = $rootElement[j2];
                } else {
                  delete $rootElement[j];
                }
              }
              $rootElement.length -= removeCount - 1;
  
              // If the replaced element is also the jQuery .context then replace it
              // .context is a deprecated jQuery api, so we should set it only when jQuery set it
              // http://api.jquery.com/context/
              if ($rootElement.context === firstElementToRemove) {
                $rootElement.context = newNode;
              }
              break;
            }
          }
        }
  
        if (parent) {
          parent.replaceChild(newNode, firstElementToRemove);
        }
  
        // TODO(perf): what's this document fragment for? is it needed? can we at least reuse it?
        var fragment = document.createDocumentFragment();
        fragment.appendChild(firstElementToRemove);
  
        // Copy over user data (that includes Angular's $scope etc.). Don't copy private
        // data here because there's no public interface in jQuery to do that and copying over
        // event listeners (which is the main use of private data) wouldn't work anyway.
        jqLite(newNode).data(jqLite(firstElementToRemove).data());
  
        // Remove data of the replaced element. We cannot just call .remove()
        // on the element it since that would deallocate scope that is needed
        // for the new node. Instead, remove the data "manually".
        if (!jQuery) {
          delete jqLite.cache[firstElementToRemove[jqLite.expando]];
        } else {
          // jQuery 2.x doesn't expose the data storage. Use jQuery.cleanData to clean up after
          // the replaced element. The cleanData version monkey-patched by Angular would cause
          // the scope to be trashed and we do need the very same scope to work with the new
          // element. However, we cannot just cache the non-patched version and use it here as
          // that would break if another library patches the method after Angular does (one
          // example is jQuery UI). Instead, set a flag indicating scope destroying should be
          // skipped this one time.
          skipDestroyOnNextJQueryCleanData = true;
          jQuery.cleanData([firstElementToRemove]);
        }
  
        for (var k = 1, kk = elementsToRemove.length; k < kk; k++) {
          var element = elementsToRemove[k];
          jqLite(element).remove(); // must do this way to clean up expando
          fragment.appendChild(element);
          delete elementsToRemove[k];
        }
  
        elementsToRemove[0] = newNode;
        elementsToRemove.length = 1;
      }
  
  
      function cloneAndAnnotateFn(fn, annotation) {
        return extend(function() { return fn.apply(null, arguments); }, fn, annotation);
      }
  
  
      function invokeLinkFn(linkFn, scope, $element, attrs, controllers, transcludeFn) {
        try {
          linkFn(scope, $element, attrs, controllers, transcludeFn);
        } catch (e) {
          $exceptionHandler(e, startingTag($element));
        }
      }
    }];
  }
  
  var PREFIX_REGEXP = /^((?:x|data)[\:\-_])/i;
  /**
   * Converts all accepted directives format into proper directive name.
   * All of these will become 'myDirective':
   *   my:Directive
   *   my-directive
   *   x-my-directive
   *   data-my:directive
   *
   * Also there is special case for Moz prefix starting with upper case letter.
   * @param name Name to normalize
   */
  function directiveNormalize(name) {
    return camelCase(name.replace(PREFIX_REGEXP, ''));
  }
  
  /**
   * @ngdoc type
   * @name $compile.directive.Attributes
   *
   * @description
   * A shared object between directive compile / linking functions which contains normalized DOM
   * element attributes. The values reflect current binding state `{{ }}`. The normalization is
   * needed since all of these are treated as equivalent in Angular:
   *
   * ```
   *    <span ng:bind="a" ng-bind="a" data-ng-bind="a" x-ng-bind="a">
   * ```
   */
  
  /**
   * @ngdoc property
   * @name $compile.directive.Attributes#$attr
   *
   * @description
   * A map of DOM element attribute names to the normalized name. This is
   * needed to do reverse lookup from normalized name back to actual name.
   */
  
  
  /**
   * @ngdoc method
   * @name $compile.directive.Attributes#$set
   * @kind function
   *
   * @description
   * Set DOM element attribute value.
   *
   *
   * @param {string} name Normalized element attribute name of the property to modify. The name is
   *          reverse-translated using the {@link ng.$compile.directive.Attributes#$attr $attr}
   *          property to the original name.
   * @param {string} value Value to set the attribute to. The value can be an interpolated string.
   */
  
  
  
  /**
   * Closure compiler type information
   */
  
  function nodesetLinkingFn(
    /* angular.Scope */ scope,
    /* NodeList */ nodeList,
    /* Element */ rootElement,
    /* function(Function) */ boundTranscludeFn
  ) {}
  
  function directiveLinkingFn(
    /* nodesetLinkingFn */ nodesetLinkingFn,
    /* angular.Scope */ scope,
    /* Node */ node,
    /* Element */ rootElement,
    /* function(Function) */ boundTranscludeFn
  ) {}
  
  function tokenDifference(str1, str2) {
    var values = '',
        tokens1 = str1.split(/\s+/),
        tokens2 = str2.split(/\s+/);
  
    outer:
    for (var i = 0; i < tokens1.length; i++) {
      var token = tokens1[i];
      for (var j = 0; j < tokens2.length; j++) {
        if (token == tokens2[j]) continue outer;
      }
      values += (values.length > 0 ? ' ' : '') + token;
    }
    return values;
  }
  
  function removeComments(jqNodes) {
    jqNodes = jqLite(jqNodes);
    var i = jqNodes.length;
  
    if (i <= 1) {
      return jqNodes;
    }
  
    while (i--) {
      var node = jqNodes[i];
      if (node.nodeType === NODE_TYPE_COMMENT) {
        splice.call(jqNodes, i, 1);
      }
    }
    return jqNodes;
  }
  