'use strict';
describe('compiler', function() {
  var compiler, textMmarkup, attrMarkup, directives, widgets, compile, log, $rootScope;
  beforeEach(inject(function($provide){
    textMmarkup = [];
    attrMarkup = [];
    widgets = extensionMap({}, 'widget');
    directives = {
      hello: function(expression, element){
        log += "hello ";
        return function() {
          log += expression;
        };
      },
      observe: function(expression, element){
        return function() {
          this.$watch(expression, function(scope, val){
            if (val)
              log += ":" + val;
          });
        };
      }
    };
    log = "";
    $provide.value('$textMarkup', textMmarkup);
    $provide.value('$attrMarkup', attrMarkup);
    $provide.value('$directive', directives);
    $provide.value('$widget', widgets);
  }));
  it('should not allow compilation of multiple roots', inject(function($rootScope, $compile) {
    expect(function() {
      $compile('
A
');
    }).toThrow("Cannot compile multiple element roots: " + ie("A
"));
    function ie(text) {
      return msie < 9 ? uppercase(text) : text;
    }
  }));
  it('should recognize a directive', inject(function($rootScope, $compile) {
    var e = jqLite('');
    directives.directive = function(expression, element){
      log += "found";
      expect(expression).toEqual("expr");
      expect(element).toEqual(e);
      return function initFn() {
        log += ":init";
      };
    };
    var linkFn = $compile(e);
    expect(log).toEqual("found");
    linkFn($rootScope);
    expect(e.hasClass('ng-directive')).toEqual(true);
    expect(log).toEqual("found:init");
  }));
  it('should recurse to children', inject(function($rootScope, $compile) {
    $compile('
')($rootScope);
    expect(log).toEqual("hello misko");
  }));
  it('should observe scope', inject(function($rootScope, $compile) {
    $compile('')($rootScope);
    expect(log).toEqual("");
    $rootScope.$digest();
    $rootScope.name = 'misko';
    $rootScope.$digest();
    $rootScope.$digest();
    $rootScope.name = 'adam';
    $rootScope.$digest();
    $rootScope.$digest();
    expect(log).toEqual(":misko:adam");
  }));
  it('should prevent descend', inject(function($rootScope, $compile) {
    directives.stop = function() { this.descend(false); };
    $compile('')($rootScope);
    expect(log).toEqual("hello misko");
  }));
  it('should allow creation of templates', inject(function($rootScope, $compile) {
    directives.duplicate = function(expr, element){
      element.replaceWith(document.createComment("marker"));
      element.removeAttr("duplicate");
      var linker = this.compile(element);
      return function(marker) {
        this.$watch('value', function() {
          var scope = $rootScope.$new;
          linker(scope, noop);
          marker.after(scope.$element);
        });
      };
    };
    $compile('beforexafter
')($rootScope);
    expect(sortedHtml($rootScope.$element)).
      toEqual('' +
                'before<#comment>#comment>' +
                'after' +
              '
');
    $rootScope.value = 1;
    $rootScope.$digest();
    expect(sortedHtml($rootScope.$element)).
      toEqual('' +
          'before<#comment>#comment>' +
          'x' +
          'after' +
        '
');
    $rootScope.value = 2;
    $rootScope.$digest();
    expect(sortedHtml($rootScope.$element)).
      toEqual('' +
          'before<#comment>#comment>' +
          'x' +
          'x' +
          'after' +
        '
');
    $rootScope.value = 3;
    $rootScope.$digest();
    expect(sortedHtml($rootScope.$element)).
      toEqual('' +
          'before<#comment>#comment>' +
          'x' +
          'x' +
          'x' +
          'after' +
        '
');
  }));
  it('should process markup before directives', inject(function($rootScope, $compile) {
    textMmarkup.push(function(text, textNode, parentNode) {
      if (text == 'middle') {
        expect(textNode.text()).toEqual(text);
        parentNode.attr('hello', text);
        textNode[0].nodeValue = 'replaced';
      }
    });
    $compile('beforemiddleafter
')($rootScope);
    expect(sortedHtml($rootScope.$element[0], true)).
      toEqual('beforereplacedafter
');
    expect(log).toEqual("hello middle");
  }));
  it('should replace widgets', inject(function($rootScope, $compile) {
    widgets['NG:BUTTON'] = function(element) {
      expect(element.hasClass('ng-widget')).toEqual(true);
      element.replaceWith('button
');
      return function(element) {
        log += 'init';
      };
    };
    $compile('push me
')($rootScope);
    expect(lowercase($rootScope.$element[0].innerHTML)).toEqual('button
');
    expect(log).toEqual('init');
  }));
  it('should use the replaced element after calling widget', inject(function($rootScope, $compile) {
    widgets['H1'] = function(element) {
      // HTML elements which are augmented by acting as widgets, should not be marked as so
      expect(element.hasClass('ng-widget')).toEqual(false);
      var span = angular.element('{{1+2}}');
      element.replaceWith(span);
      this.descend(true);
      this.directives(true);
      return noop;
    };
    textMmarkup.push(function(text, textNode, parent){
      if (text == '{{1+2}}')
        parent.text('3');
    });
    $compile('ignore me
')($rootScope);
    expect($rootScope.$element.text()).toEqual('3');
  }));
  it('should allow multiple markups per text element', inject(function($rootScope, $compile) {
    textMmarkup.push(function(text, textNode, parent){
      var index = text.indexOf('---');
      if (index > -1) {
        textNode.after(text.substring(index + 3));
        textNode.after("
");
        textNode.after(text.substring(0, index));
        textNode.remove();
      }
    });
    textMmarkup.push(function(text, textNode, parent){
      var index = text.indexOf('===');
      if (index > -1) {
        textNode.after(text.substring(index + 3));
        textNode.after("");
        textNode.after(text.substring(0, index));
        textNode.remove();
      }
    });
    $compile('
A---B---C===D
')($rootScope);
    expect(sortedHtml($rootScope.$element)).toEqual('');
  }));
  it('should add class for namespace elements', inject(function($rootScope, $compile) {
    var element = $compile('abc')($rootScope);
    expect(element.hasClass('ng-space')).toEqual(true);
  }));
});