')
            ($rootScope);
          var div = element.find('div');
          expect(div.hasClass('medium-log')).toBe(true);
          expect(div.hasClass('log')).toBe(true);
          expect(div.css('width')).toBe('10px');
          expect(div.css('height')).toBe('20px');
          expect(div.attr('replace')).toEqual('');
          expect(div.attr('high-log')).toEqual('');
        }));
        it('should prevent multiple templates per element', inject(function($compile) {
          try {
            $compile('
')
            fail();
          } catch(e) {
            expect(e.message).toMatch(/Multiple directives .* asking for template/);
          }
        }));
        it('should play nice with repeater when inline', inject(function($compile, $rootScope) {
          element = $compile(
            '
')($rootScope);
          $rootScope.$digest();
          expect(element.text()).toEqual('Hello: 1; Hello: 2; ');
        }));
        it('should play nice with repeater when append', inject(function($compile, $rootScope) {
          element = $compile(
            '
')($rootScope);
          $rootScope.$digest();
          expect(element.text()).toEqual('Hello: 1; Hello: 2; ');
        }));
      });
      describe('async templates', function() {
        beforeEach(module(
          function($compileProvider) {
            $compileProvider.directive('hello', valueFn({ templateUrl: 'hello.html' }));
            $compileProvider.directive('cau', valueFn({ templateUrl:'cau.html' }));
            $compileProvider.directive('cError', valueFn({
              templateUrl:'error.html',
              compile: function() {
                throw Error('cError');
              }
            }));
            $compileProvider.directive('lError', valueFn({
              templateUrl: 'error.html',
              compile: function() {
                throw Error('lError');
              }
            }));
            $compileProvider.directive('iHello', valueFn({
              replace: true,
              templateUrl: 'hello.html'
            }));
            $compileProvider.directive('iCau', valueFn({
              replace: true,
              templateUrl:'cau.html'
            }));
            $compileProvider.directive('iCError', valueFn({
              replace: true,
              templateUrl:'error.html',
              compile: function() {
                throw Error('cError');
              }
            }));
            $compileProvider.directive('iLError', valueFn({
              replace: true,
              templateUrl: 'error.html',
              compile: function() {
                throw Error('lError');
              }
            }));
          }
        ));
        it('should append template via $http and cache it in $templateCache', inject(
            function($compile, $httpBackend, $templateCache, $rootScope, $browser) {
              $httpBackend.expect('GET', 'hello.html').respond('
Hello! World!');
              $templateCache.put('cau.html', '
Cau!');
              element = $compile('
ignoreignore
')($rootScope);
              expect(sortedHtml(element)).
                  toEqual('
');
              $rootScope.$digest();
              expect(sortedHtml(element)).
                  toEqual('
Cau!
');
              $httpBackend.flush();
              expect(sortedHtml(element)).toEqual(
                  '
' +
                    'Hello! World!' +
                    'Cau!' +
                  '
');
            }
        ));
        it('should inline template via $http and cache it in $templateCache', inject(
            function($compile, $httpBackend, $templateCache, $rootScope) {
              $httpBackend.expect('GET', 'hello.html').respond('
Hello!');
              $templateCache.put('cau.html', '
Cau!');
              element = $compile('
ignoreignore
')($rootScope);
              expect(sortedHtml(element)).
                  toEqual('
');
              $rootScope.$digest();
              expect(sortedHtml(element)).
                  toEqual('
Cau!
');
              $httpBackend.flush();
              expect(sortedHtml(element)).
                  toEqual('
Hello!Cau!
');
            }
        ));
        it('should compile, link and flush the template append', inject(
            function($compile, $templateCache, $rootScope, $browser) {
              $templateCache.put('hello.html', '
Hello, {{name}}!');
              $rootScope.name = 'Elvis';
              element = $compile('
')($rootScope);
              $rootScope.$digest();
              expect(sortedHtml(element)).
                  toEqual('
Hello, Elvis!
');
            }
        ));
        it('should compile, link and flush the template inline', inject(
            function($compile, $templateCache, $rootScope) {
              $templateCache.put('hello.html', '
Hello, {{name}}!');
              $rootScope.name = 'Elvis';
              element = $compile('
')($rootScope);
              $rootScope.$digest();
              expect(sortedHtml(element)).
                  toEqual('
Hello, Elvis!
');
            }
        ));
        it('should compile, flush and link the template append', inject(
            function($compile, $templateCache, $rootScope) {
              $templateCache.put('hello.html', '
Hello, {{name}}!');
              $rootScope.name = 'Elvis';
              var template = $compile('
');
              element = template($rootScope);
              $rootScope.$digest();
              expect(sortedHtml(element)).
                  toEqual('
Hello, Elvis!
');
            }
        ));
        it('should compile, flush and link the template inline', inject(
            function($compile, $templateCache, $rootScope) {
              $templateCache.put('hello.html', '
Hello, {{name}}!');
              $rootScope.name = 'Elvis';
              var template = $compile('
');
              element = template($rootScope);
              $rootScope.$digest();
              expect(sortedHtml(element)).
                  toEqual('
Hello, Elvis!
');
            }
        ));
        it('should resolve widgets after cloning in append mode', function() {
          module(function($exceptionHandlerProvider) {
            $exceptionHandlerProvider.mode('log');
          });
          inject(function($compile, $templateCache, $rootScope, $httpBackend, $browser,
                   $exceptionHandler) {
            $httpBackend.expect('GET', 'hello.html').respond('
{{greeting}} ');
            $httpBackend.expect('GET', 'error.html').respond('
');
            $templateCache.put('cau.html', '
{{name}}');
            $rootScope.greeting = 'Hello';
            $rootScope.name = 'Elvis';
            var template = $compile(
              '
' +
                '' +
                '' +
                '' +
                '' +
              '
');
            var e1;
            var e2;
            e1 = template($rootScope.$new(), noop); // clone
            expect(e1.text()).toEqual('');
            $httpBackend.flush();
            e2 = template($rootScope.$new(), noop); // clone
            $rootScope.$digest();
            expect(e1.text()).toEqual('Hello Elvis');
            expect(e2.text()).toEqual('Hello Elvis');
            expect($exceptionHandler.errors.length).toEqual(2);
            expect($exceptionHandler.errors[0][0].message).toEqual('cError');
            expect($exceptionHandler.errors[1][0].message).toEqual('lError');
            dealoc(e1);
            dealoc(e2);
          });
        });
        it('should resolve widgets after cloning in inline mode', function() {
          module(function($exceptionHandlerProvider) {
            $exceptionHandlerProvider.mode('log');
          });
          inject(function($compile, $templateCache, $rootScope, $httpBackend, $browser,
                   $exceptionHandler) {
            $httpBackend.expect('GET', 'hello.html').respond('
{{greeting}} ');
            $httpBackend.expect('GET', 'error.html').respond('
');
            $templateCache.put('cau.html', '
{{name}}');
            $rootScope.greeting = 'Hello';
            $rootScope.name = 'Elvis';
            var template = $compile(
              '
' +
                '' +
                '' +
                '' +
                '' +
              '
');
            var e1;
            var e2;
            e1 = template($rootScope.$new(), noop); // clone
            expect(e1.text()).toEqual('');
            $httpBackend.flush();
            e2 = template($rootScope.$new(), noop); // clone
            $rootScope.$digest();
            expect(e1.text()).toEqual('Hello Elvis');
            expect(e2.text()).toEqual('Hello Elvis');
            expect($exceptionHandler.errors.length).toEqual(2);
            expect($exceptionHandler.errors[0][0].message).toEqual('cError');
            expect($exceptionHandler.errors[1][0].message).toEqual('lError');
            dealoc(e1);
            dealoc(e2);
          });
        });
        it('should be implicitly terminal and not compile placeholder content in append', inject(
            function($compile, $templateCache, $rootScope, log) {
              // we can't compile the contents because that would result in a memory leak
              $templateCache.put('hello.html', 'Hello!');
              element = $compile('
')($rootScope);
              expect(log).toEqual('');
            }
        ));
        it('should be implicitly terminal and not compile placeholder content in inline', inject(
            function($compile, $templateCache, $rootScope, log) {
              // we can't compile the contents because that would result in a memory leak
              $templateCache.put('hello.html', 'Hello!');
              element = $compile('
')($rootScope);
              expect(log).toEqual('');
            }
        ));
        it('should throw an error and clear element content if the template fails to load', inject(
            function($compile, $httpBackend, $rootScope) {
              $httpBackend.expect('GET', 'hello.html').respond(404, 'Not Found!');
              element = $compile('
content
')($rootScope);
              expect(function() {
                $httpBackend.flush();
              }).toThrow('Failed to load template: hello.html');
              expect(sortedHtml(element)).toBe('
');
            }
        ));
        it('should prevent multiple templates per element', function() {
          module(function($compileProvider) {
            $compileProvider.directive('sync', valueFn({
              template: '
'
            }));
            $compileProvider.directive('async', valueFn({
              templateUrl: 'template.html'
            }));
          });
          inject(function($compile){
            expect(function() {
              $compile('
');
            }).toThrow('Multiple directives [sync, async] asking for template on: <'+
                (msie <= 8 ? 'DIV' : 'div') + ' class="sync async">');
          });
        });
        describe('delay compile / linking functions until after template is resolved', function(){
          var template;
          beforeEach(module(function($compileProvider) {
            function directive (name, priority, options) {
              $compileProvider.directive(name, function(log) {
                return (extend({
                 priority: priority,
                 compile: function() {
                   log(name + '-C');
                   return function() { log(name + '-L'); }
                 }
               }, options || {}));
              });
            }
            directive('first', 10);
            directive('second', 5, { templateUrl: 'second.html' });
            directive('third', 3);
            directive('last', 0);
            directive('iFirst', 10, {replace: true});
            directive('iSecond', 5, {replace: true, templateUrl: 'second.html' });
            directive('iThird', 3, {replace: true});
            directive('iLast', 0, {replace: true});
          }));
          it('should flush after link append', inject(
              function($compile, $rootScope, $httpBackend, log) {
            $httpBackend.expect('GET', 'second.html').respond('
{{1+2}}
');
            template = $compile('
');
            element = template($rootScope);
            expect(log).toEqual('first-C');
            log('FLUSH');
            $httpBackend.flush();
            $rootScope.$digest();
            expect(log).toEqual(
              'first-C; FLUSH; second-C; last-C; third-C; ' +
              'third-L; first-L; second-L; last-L');
            var span = element.find('span');
            expect(span.attr('first')).toEqual('');
            expect(span.attr('second')).toEqual('');
            expect(span.find('div').attr('third')).toEqual('');
            expect(span.attr('last')).toEqual('');
            expect(span.text()).toEqual('3');
          }));
          it('should flush after link inline', inject(
              function($compile, $rootScope, $httpBackend, log) {
            $httpBackend.expect('GET', 'second.html').respond('
{{1+2}}
');
            template = $compile('
');
            element = template($rootScope);
            expect(log).toEqual('iFirst-C');
            log('FLUSH');
            $httpBackend.flush();
            $rootScope.$digest();
            expect(log).toEqual(
              'iFirst-C; FLUSH; iSecond-C; iThird-C; iLast-C; ' +
              'iFirst-L; iSecond-L; iThird-L; iLast-L');
            var div = element.find('div');
            expect(div.attr('i-first')).toEqual('');
            expect(div.attr('i-second')).toEqual('');
            expect(div.attr('i-third')).toEqual('');
            expect(div.attr('i-last')).toEqual('');
            expect(div.text()).toEqual('3');
          }));
          it('should flush before link append', inject(
              function($compile, $rootScope, $httpBackend, log) {
            $httpBackend.expect('GET', 'second.html').respond('
{{1+2}}
');
            template = $compile('
');
            expect(log).toEqual('first-C');
            log('FLUSH');
            $httpBackend.flush();
            expect(log).toEqual('first-C; FLUSH; second-C; last-C; third-C');
            element = template($rootScope);
            $rootScope.$digest();
            expect(log).toEqual(
              'first-C; FLUSH; second-C; last-C; third-C; ' +
              'third-L; first-L; second-L; last-L');
            var span = element.find('span');
            expect(span.attr('first')).toEqual('');
            expect(span.attr('second')).toEqual('');
            expect(span.find('div').attr('third')).toEqual('');
            expect(span.attr('last')).toEqual('');
            expect(span.text()).toEqual('3');
          }));
          it('should flush before link inline', inject(
              function($compile, $rootScope, $httpBackend, log) {
            $httpBackend.expect('GET', 'second.html').respond('
{{1+2}}
');
            template = $compile('
');
            expect(log).toEqual('iFirst-C');
            log('FLUSH');
            $httpBackend.flush();
            expect(log).toEqual('iFirst-C; FLUSH; iSecond-C; iThird-C; iLast-C');
            element = template($rootScope);
            $rootScope.$digest();
            expect(log).toEqual(
              'iFirst-C; FLUSH; iSecond-C; iThird-C; iLast-C; ' +
              'iFirst-L; iSecond-L; iThird-L; iLast-L');
            var div = element.find('div');
            expect(div.attr('i-first')).toEqual('');
            expect(div.attr('i-second')).toEqual('');
            expect(div.attr('i-third')).toEqual('');
            expect(div.attr('i-last')).toEqual('');
            expect(div.text()).toEqual('3');
          }));
        });
        it('should check that template has root element', inject(function($compile, $httpBackend) {
          $httpBackend.expect('GET', 'hello.html').respond('before 
mid after');
          $compile('
');
          expect(function(){
            $httpBackend.flush();
          }).toThrow('Template must have exactly one root element: before 
mid after');
        }));
        it('should allow multiple elements in template', inject(function($compile, $httpBackend) {
          $httpBackend.expect('GET', 'hello.html').respond('before 
mid after');
          element = jqLite('
');
          $compile(element);
          $httpBackend.flush();
          expect(element.text()).toEqual('before mid after');
        }));
        it('should work when widget is in root element', inject(
          function($compile, $httpBackend, $rootScope) {
            $httpBackend.expect('GET', 'hello.html').respond('
3==<>');
            element = jqLite('
{{1+2}}');
            $compile(element)($rootScope);
            $httpBackend.flush();
            expect(element.text()).toEqual('3==3');
          }
        ));
        it('should work when widget is a repeater', inject(
          function($compile, $httpBackend, $rootScope) {
            $httpBackend.expect('GET', 'hello.html').respond('
i=<>;');
            element = jqLite('
{{i}}
');
            $compile(element)($rootScope);
            $httpBackend.flush();
            expect(element.text()).toEqual('i=1;i=2;');
          }
        ));
      });
      describe('scope', function() {
        beforeEach(module(function($compileProvider) {
          forEach(['', 'a', 'b'], function(name) {
            $compileProvider.directive('scope' + uppercase(name), function(log) {
              return {
                scope: true,
                compile: function() {
                  return function (scope, element) {
                    log(scope.$id);
                    expect(element.data('$scope')).toBe(scope);
                  };
                }
              };
            });
          });
          $compileProvider.directive('log', function(log) {
            return function(scope) {
              log('log-' + scope.$id + '-' + scope.$parent.$id);
            };
          });
        }));
        it('should allow creation of new scopes', inject(function($rootScope, $compile, log) {
          element = $compile('
')($rootScope);
          expect(log).toEqual('LOG; log-002-001; 002');
        }));
        it('should correctly create the scope hierachy properly', inject(
            function($rootScope, $compile, log) {
          element = $compile(
              '
' + //1
                '' + //2
                  '' + //3
                  '' +
                '' +
                '' + //4
                  '' +
                '' +
              '
'
            )($rootScope);
          expect(log).toEqual('LOG; log-003-002; 003; LOG; log-002-001; 002; LOG; log-004-001; 004');
        }));
        it('should not allow more then one scope creation per element', inject(
          function($rootScope, $compile) {
            expect(function(){
              $compile('
');
            }).toThrow('Multiple directives [scopeA, scopeB] asking for new scope on: ' +
                '<' + (msie < 9 ? 'DIV' : 'div') + ' class="scope-a; scope-b">');
          }));
        it('should treat new scope on new template as noop', inject(
          function($rootScope, $compile, log) {
            element = $compile('
')($rootScope);
            expect(log).toEqual('001');
          }));
      });
    });
  });
  describe('interpolation', function() {
    it('should compile and link both attribute and text bindings', inject(
        function($rootScope, $compile) {
          $rootScope.name = 'angular';
          element = $compile('
text: {{name}}
')($rootScope);
          $rootScope.$digest();
          expect(element.text()).toEqual('text: angular');
          expect(element.attr('name')).toEqual('attr: angular');
        }));
    it('should decorate the binding with ng-binding and interpolation function', inject(
        function($compile, $rootScope) {
          element = $compile('
{{1+2}}
')($rootScope);
          expect(element.hasClass('ng-binding')).toBe(true);
          expect(element.data('$binding')[0].exp).toEqual('{{1+2}}');
        }));
  });
  describe('link phase', function() {
    beforeEach(module(function($compileProvider) {
      forEach(['a', 'b', 'c'], function(name) {
        $compileProvider.directive(name, function(log) {
          return {
            compile: function() {
              log('t' + uppercase(name))
              return {
                pre: function() {
                  log('pre' + uppercase(name));
                },
                post: function linkFn() {
                  log('post' + uppercase(name));
                }
              };
            }
          };
        });
      });
    }));
    it('should not store linkingFns for noop branches', inject(function ($rootScope, $compile) {
      element = jqLite('
ignore
');
      var linkingFn = $compile(element);
      // Now prune the branches with no directives
      element.find('span').remove();
      expect(element.find('span').length).toBe(0);
      // and we should still be able to compile without errors
      linkingFn($rootScope);
    }));
    it('should compile from top to bottom but link from bottom up', inject(
        function($compile, $rootScope, log) {
          element = $compile('
')($rootScope);
          expect(log).toEqual('tA; tB; tC; preA; preB; preC; postC; postA; postB');
        }
    ));
    it('should support link function on directive object', function() {
      module(function($compileProvider) {
        $compileProvider.directive('abc', valueFn({
          link: function(scope, element, attrs) {
            element.text(attrs.abc);
          }
        }));
      });
      inject(function($compile, $rootScope) {
        element = $compile('
FAIL
')($rootScope);
        expect(element.text()).toEqual('WORKS');
      });
    });
  });
  describe('attrs', function() {
    it('should allow setting of attributes', function() {
      module(function($compileProvider) {
        $compileProvider.directive({
          setter: valueFn(function(scope, element, attr) {
            attr.$set('name', 'abc');
            attr.$set('disabled', true);
            expect(attr.name).toBe('abc');
            expect(attr.disabled).toBe(true);
          })
        });
      });
      inject(function($rootScope, $compile) {
        element = $compile('
')($rootScope);
        expect(element.attr('name')).toEqual('abc');
        expect(element.attr('disabled')).toEqual('disabled');
      });
    });
    it('should read boolean attributes as boolean', function() {
      module(function($compileProvider) {
        $compileProvider.directive({
          div: valueFn(function(scope, element, attr) {
            element.text(attr.required);
          })
        });
      });
      inject(function($rootScope, $compile) {
        element = $compile('
')($rootScope);
        expect(element.text()).toEqual('true');
      });
    });
    it('should allow setting of attributes', function() {
      module(function($compileProvider) {
        $compileProvider.directive({
          setter: valueFn(function(scope, element, attr) {
            attr.$set('name', 'abc');
            attr.$set('disabled', true);
            expect(attr.name).toBe('abc');
            expect(attr.disabled).toBe(true);
          })
        });
      });
      inject(function($rootScope, $compile) {
        element = $compile('
')($rootScope);
        expect(element.attr('name')).toEqual('abc');
        expect(element.attr('disabled')).toEqual('disabled');
      });
    });
    it('should read boolean attributes as boolean', function() {
      module(function($compileProvider) {
        $compileProvider.directive({
          div: valueFn(function(scope, element, attr) {
            element.text(attr.required);
          })
        });
      });
      inject(function($rootScope, $compile) {
        element = $compile('
')($rootScope);
        expect(element.text()).toEqual('true');
      });
    });
    it('should create new instance of attr for each template stamping', function() {
      module(function($compileProvider, $provide) {
        var state = { first: [], second: [] };
        $provide.value('state', state);
        $compileProvider.directive({
          first: valueFn({
            priority: 1,
            compile: function(templateElement, templateAttr) {
              return function(scope, element, attr) {
                state.first.push({
                  template: {element: templateElement, attr:templateAttr},
                  link: {element: element, attr: attr}
                });
              }
            }
          }),
          second: valueFn({
            priority: 2,
            compile: function(templateElement, templateAttr) {
              return function(scope, element, attr) {
                state.second.push({
                  template: {element: templateElement, attr:templateAttr},
                  link: {element: element, attr: attr}
                });
              }
            }
          })
        });
      });
      inject(function($rootScope, $compile, state) {
        var template = $compile('
');
        dealoc(template($rootScope.$new(), noop));
        dealoc(template($rootScope.$new(), noop));
        // instance between directives should be shared
        expect(state.first[0].template.element).toBe(state.second[0].template.element);
        expect(state.first[0].template.attr).toBe(state.second[0].template.attr);
        // the template and the link can not be the same instance
        expect(state.first[0].template.element).not.toBe(state.first[0].link.element);
        expect(state.first[0].template.attr).not.toBe(state.first[0].link.attr);
        // each new template needs to be new instance
        expect(state.first[0].link.element).not.toBe(state.first[1].link.element);
        expect(state.first[0].link.attr).not.toBe(state.first[1].link.attr);
        expect(state.second[0].link.element).not.toBe(state.second[1].link.element);
        expect(state.second[0].link.attr).not.toBe(state.second[1].link.attr);
      });
    });
    describe('$set', function() {
      var attr;
      beforeEach(function(){
        module(function($compileProvider) {
          $compileProvider.directive('div', valueFn(function(scope, element, attr){
            scope.attr = attr;
          }));
        });
        inject(function($compile, $rootScope) {
          element = $compile('
')($rootScope);
          attr = $rootScope.attr;
          expect(attr).toBeDefined();
        });
      });
      it('should set attributes', function() {
        attr.$set('ngMyAttr', 'value');
        expect(element.attr('ng-my-attr')).toEqual('value');
        expect(attr.ngMyAttr).toEqual('value');
      });
      it('should allow overriding of attribute name and remember the name', function() {
        attr.$set('ngOther', '123', 'other');
        expect(element.attr('other')).toEqual('123');
        expect(attr.ngOther).toEqual('123');
        attr.$set('ngOther', '246');
        expect(element.attr('other')).toEqual('246');
        expect(attr.ngOther).toEqual('246');
      });
      it('should set boolean attributes', function() {
        attr.$set('disabled', 'true');
        attr.$set('readOnly', 'true');
        expect(element.attr('disabled')).toEqual('disabled');
        expect(element.attr('readonly')).toEqual('readonly');
        attr.$set('disabled', 'false');
        expect(element.attr('disabled')).toEqual(undefined);
        attr.$set('disabled', false);
        attr.$set('readOnly', false);
        expect(element.attr('disabled')).toEqual(undefined);
        expect(element.attr('readonly')).toEqual(undefined);
      });
      it('should remove attribute', function() {
        attr.$set('ngMyAttr', 'value');
        expect(element.attr('ng-my-attr')).toEqual('value');
        attr.$set('ngMyAttr', undefined);
        expect(element.attr('ng-my-attr')).toBe(undefined);
        attr.$set('ngMyAttr', 'value');
        attr.$set('ngMyAttr', null);
        expect(element.attr('ng-my-attr')).toBe(undefined);
      })
    });
  });
});