describe('compiler', function(){ function element(html) { return jQuery(html)[0]; } var compiler, textMarkup, directives, widgets, compile, log; beforeEach(function(){ log = ""; directives = { hello: function(expression, element){ log += "hello "; return function() { log += expression; }; }, watch: function(expression, element){ return function() { this.$watch(expression, function(val){ log += ":" + val; }); }; } }; textMarkup = []; attrMarkup = []; widgets = {}; compiler = new Compiler(textMarkup, attrMarkup, directives, widgets); compile = function(html){ var e = element("
" + html + "
"); var view = compiler.compile(e)(e); view.init(); return view.scope; }; }); it('should recognize a directive', function(){ var e = element('
'); directives.directive = function(expression, element){ log += "found"; expect(expression).toEqual("expr"); expect(element[0]).toEqual(e); return function initFn() { log += ":init"; }; }; var template = compiler.compile(e); var init = template(e).init; expect(log).toEqual("found"); init(); expect(log).toEqual("found:init"); }); it('should recurse to children', function(){ var scope = compile('
'); expect(log).toEqual("hello misko"); }); it('should watch scope', function(){ var scope = compile(''); expect(log).toEqual(""); scope.updateView(); scope.set('name', 'misko'); scope.updateView(); scope.updateView(); scope.set('name', 'adam'); scope.updateView(); scope.updateView(); expect(log).toEqual(":misko:adam"); }); it('should prevent descend', function(){ directives.stop = function(){ this.descend(false); }; var scope = compile(''); expect(log).toEqual("hello misko"); }); it('should allow creation of templates', function(){ directives.duplicate = function(expr, element){ element.replaceWith(document.createComment("marker")); element.removeAttr("duplicate"); var template = this.compile(element); return function(marker) { this.$addEval(function() { marker.after(template(element.clone()).element); }); }; }; var scope = compile('beforexafter'); expect($(scope.element).html()).toEqual('beforeafter'); scope.updateView(); expect($(scope.element).html()).toEqual('beforexafter'); scope.updateView(); expect($(scope.element).html()).toEqual('beforexxafter'); }); it('should allow for exculsive tags which suppress others', function(){ directives.exclusive = function(){ return function() { log += ('exclusive'); }; }; directives.exclusive.exclusive = true; compile(''); expect(log).toEqual('exclusive'); }); it('should process markup before directives', function(){ textMarkup.push(function(text, textNode, parentNode) { if (text == 'middle') { expect(textNode.text()).toEqual(text); parentNode.attr('hello', text); textNode.text('replaced'); } }); var scope = compile('beforemiddleafter'); expect(scope.element.innerHTML).toEqual('beforereplacedafter'); expect(log).toEqual("hello middle"); }); it('should replace widgets', function(){ widgets['NG:BUTTON'] = function(element) { element.replaceWith('
button
', element); return function(element) { log += 'init'; }; }; var scope = compile('push me'); expect(scope.element.innerHTML).toEqual('
button
'); expect(log).toEqual('init'); }); }); 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137
/**
 * DOM generation class
 */

exports.DOM = DOM;
exports.htmlEscape = htmlEscape;

//////////////////////////////////////////////////////////

function htmlEscape(text){
  return text.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;');
}


function DOM() {
  this.out = [];
  this.headingDepth = 0;
}

var INLINE_TAGS = {
    i: true,
    b: true,
    a: true
};

DOM.prototype = {
  toString: function() {
    return this.out.join('');
  },

  text: function(content) {
    if (typeof content == "string") {
      this.out.push(htmlEscape(content));
    } else if (typeof content == 'function') {
      content.call(this, this);
    } else if (content instanceof Array) {
      this.ul(content);
    }
  },

  html: function(html) {
    if (html) {
      var headingDepth = this.headingDepth;
      for ( var i = 10; i > 0; --i) {
        html = html
          .replace(new RegExp('(<\/?h)' + i + '(>)', 'gm'), function(all, start, end){
            return start + (i + headingDepth) + end;
          });
      }
      this.out.push(html);
    }
  },

  tag: function(name, attr, text) {
    if (!text) {
      text = attr;
      attr = {};
      if (name == 'code')
        attr['ng:non-bindable'] = '';
    }
    this.out.push('<' + name);
    for(var key in attr) {
      this.out.push(" " + key + '="' + attr[key] + '"');
    }
    this.out.push('>');
    this.text(text);
    this.out.push('</' + name + '>');
    if (!INLINE_TAGS[name])
      this.out.push('\n');
  },

  code: function(text) {
    this.tag('pre', {'class':"prettyprint linenums"}, text);
  },

  div: function(attr, text) {
    this.tag('div', attr, text);
  },

  h: function(heading, content, fn){
    if (content==undefined || (content instanceof Array && content.length == 0)) return;
    this.headingDepth++;
    var className = null,
        anchor = null;
    if (typeof heading == 'string') {
      var id = heading.
          replace(/\(.*\)/mg, '').
          replace(/[^\d\w\$]/mg, '.').
          replace(/-+/gm, '-').
          replace(/-*$/gm, '');
      anchor = {'id': id};
      className = {'class': id.toLowerCase().replace(/[._]/mg, '-')};
    }
    this.tag('h' + this.headingDepth, anchor, heading);
    if (content instanceof Array) {
      this.ul(content, className, fn);
    } else if (fn) {
      this.tag('div', className, function() {
        fn.call(this, content);
      });
    } else {
      this.tag('div', className, content);
    }
    this.headingDepth--;
  },

  h1: function(attr, text) {
    this.tag('h1', attr, text);
  },

  h2: function(attr, text) {
    this.tag('h2', attr, text);
  },

  h3: function(attr, text) {
    this.tag('h3', attr, text);
  },

  p: function(attr, text) {
    this.tag('p', attr, text);
  },

  ul: function(list, attr, fn) {
    if (typeof attr == 'function') {
      fn = attr;
      attr = {};
    }
    this.tag('ul', attr, function(dom){
      list.forEach(function(item){
        dom.out.push('<li>');
        dom.text(fn ? fn(item) : item);
        dom.out.push('</li>\n');
      });
    });
  }

};