text: require('./helper/shim.js')();
  
  
  
  var _  = module.exports;
  var entities = require('./helper/entities.js');
  var slice = [].slice;
  var o2str = ({}).toString;
  var win = typeof window !=='undefined'? window: global;
  var MAX_PRIORITY = 9999;
  
  
  _.noop = function(){};
  _.uid = (function(){
    var _uid=0;
    return function(){
      return _uid++;
    }
  })();
  
  _.extend = function( o1, o2, override ){
    for(var i in o2) if (o2.hasOwnProperty(i)){
      if( o1[i] === undefined || override === true ){
        o1[i] = o2[i]
      }
    }
    return o1;
  }
  
  _.keys = Object.keys? Object.keys: function(obj){
    var res = [];
    for(var i in obj) if(obj.hasOwnProperty(i)){
      res.push(i);
    }
    return res;
  }
  
  _.some = function(list, fn){
    for(var i =0,len = list.length; i < len; i++){
      if(fn(list[i])) return true
    }
  }
  
  _.varName = 'd';
  _.setName = 'p_';
  _.ctxName = 'c';
  _.extName = 'e';
  
  _.rWord = /^[\$\w]+$/;
  _.rSimpleAccessor = /^[\$\w]+(\.[\$\w]+)*$/;
  
  _.nextTick = typeof setImmediate === 'function'? 
    setImmediate.bind(win) : 
    function(callback) {
      setTimeout(callback, 0) 
    }
  
  
  
  _.prefix = "'use strict';var " + _.varName + "=" + _.ctxName + ".data;" +  _.extName  + "=" + _.extName + "||'';";
  
  
  _.slice = function(obj, start, end){
    var res = [];
    for(var i = start || 0, len = end || obj.length; i < len; i++){
      res.push(obj[i])
    }
    return res;
  }
  
  // beacuse slice and toLowerCase is expensive. we handle undefined and null in another way
  _.typeOf = function (o) {
    return o == null ? String(o) :o2str.call(o).slice(8, -1).toLowerCase();
  }
  
  
  
  
  _.makePredicate = function makePredicate(words, prefix) {
      if (typeof words === "string") {
          words = words.split(" ");
      }
      var f = "",
      cats = [];
      out: for (var i = 0; i < words.length; ++i) {
          for (var j = 0; j < cats.length; ++j){
            if (cats[j][0].length === words[i].length) {
                cats[j].push(words[i]);
                continue out;
            }
          }
          cats.push([words[i]]);
      }
      function compareTo(arr) {
          if (arr.length === 1) return f += "return str === '" + arr[0] + "';";
          f += "switch(str){";
          for (var i = 0; i < arr.length; ++i){
             f += "case '" + arr[i] + "':";
          }
          f += "return true}return false;";
      }
  
      // When there are more than three length categories, an outer
      // switch first dispatches on the lengths, to save on comparisons.
      if (cats.length > 3) {
          cats.sort(function(a, b) {
              return b.length - a.length;
          });
          f += "switch(str.length){";
          for (var i = 0; i < cats.length; ++i) {
              var cat = cats[i];
              f += "case " + cat[0].length + ":";
              compareTo(cat);
          }
          f += "}";
  
          // Otherwise, simply generate a flat `switch` statement.
      } else {
          compareTo(words);
      }
      return new Function("str", f);
  }
  
  
  _.trackErrorPos = (function (){
    // linebreak
    var lb = /\r\n|[\n\r\u2028\u2029]/g;
    var minRange = 20, maxRange = 20;
    function findLine(lines, pos){
      var tmpLen = 0;
      for(var i = 0,len = lines.length; i < len; i++){
        var lineLen = (lines[i] || "").length;
  
        if(tmpLen + lineLen > pos) {
          return {num: i, line: lines[i], start: pos - i - tmpLen , prev:lines[i-1], next: lines[i+1] };
        }
        // 1 is for the linebreak
        tmpLen = tmpLen + lineLen ;
      }
    }
    function formatLine(str,  start, num, target){
      var len = str.length;
      var min = start - minRange;
      if(min < 0) min = 0;
      var max = start + maxRange;
      if(max > len) max = len;
  
      var remain = str.slice(min, max);
      var prefix = "[" +(num+1) + "] " + (min > 0? ".." : "")
      var postfix = max < len ? "..": "";
      var res = prefix + remain + postfix;
      if(target) res += "\n" + new Array(start-min + prefix.length + 1).join(" ") + "^^^";
      return res;
    }
    return function(input, pos){
      if(pos > input.length-1) pos = input.length-1;
      lb.lastIndex = 0;
      var lines = input.split(lb);
      var line = findLine(lines,pos);
      var start = line.start, num = line.num;
  
      return (line.prev? formatLine(line.prev, start, num-1 ) + '\n': '' ) + 
        formatLine(line.line, start, num, true) + '\n' + 
        (line.next? formatLine(line.next, start, num+1 ) + '\n': '' );
  
    }
  })();
  
  
  var ignoredRef = /\((\?\!|\?\:|\?\=)/g;
  _.findSubCapture = function (regStr) {
    var left = 0,
      right = 0,
      len = regStr.length,
      ignored = regStr.match(ignoredRef); // ignored uncapture
    if(ignored) ignored = ignored.length
    else ignored = 0;
    for (; len--;) {
      var letter = regStr.charAt(len);
      if (len === 0 || regStr.charAt(len - 1) !== "\\" ) { 
        if (letter === "(") left++;
        if (letter === ")") right++;
      }
    }
    if (left !== right) throw "RegExp: "+ regStr + "'s bracket is not marched";
    else return left - ignored;
  };
  
  
  _.escapeRegExp = function( str){// Credit: XRegExp 0.6.1 (c) 2007-2008 Steven Levithan <http://stevenlevithan.com/regex/xregexp/> MIT License
    return str.replace(/[-[\]{}()*+?.\\^$|,#\s]/g, function(match){
      return '\\' + match;
    });
  };
  
  
  var rEntity = new RegExp("&(?:(#x[0-9a-fA-F]+)|(#[0-9]+)|(" + _.keys(entities).join('|') + '));', 'gi');
  
  _.convertEntity = function(chr){
  
    return ("" + chr).replace(rEntity, function(all, hex, dec, capture){
      var charCode;
      if( dec ) charCode = parseInt( dec.slice(1), 10 );
      else if( hex ) charCode = parseInt( hex.slice(2), 16 );
      else charCode = entities[capture]
  
      return String.fromCharCode( charCode )
    });
  
  }
  
  
  // simple get accessor
  
  _.createObject = Object.create? function(o){
    return Object.create(o || null)
  }: (function(){
      function Temp() {}
      return function(o){
        if(!o) return {}
        Temp.prototype = o;
        var obj = new Temp();
        Temp.prototype = null; // 不要保持一个 O 的杂散引用（a stray reference）...
        return obj
      }
  })();
  
  _.createProto = function(fn, o){
      function Foo() { this.constructor = fn;}
      Foo.prototype = o;
      return (fn.prototype = new Foo());
  }
  
  
  _.removeOne = function(list , filter){
    var len = list.length;
    for(;len--;){
      if(filter(list[len])) {
        list.splice(len, 1)
        return;
      }
    }
  }
  
  
  /**
  clone
  */
  _.clone = function clone(obj){
    if(!obj || (typeof obj !== 'object' )) return obj;
    if(Array.isArray(obj)){
      var cloned = [];
      for(var i=0,len = obj.length; i< len;i++){
        cloned[i] = obj[i]
      }
      return cloned;
    }else{
      var cloned = {};
      for(var i in obj) if(obj.hasOwnProperty(i)){
        cloned[i] = obj[i];
      }
      return cloned;
    }
  }
  
  _.equals = function(now, old){
    var type = typeof now;
    if(type === 'number' && typeof old === 'number'&& isNaN(now) && isNaN(old)) return true
    return now === old;
  }
  
  var dash = /-([a-z])/g;
  _.camelCase = function(str){
    return str.replace(dash, function(all, capture){
      return capture.toUpperCase();
    })
  }
  
  
  
  _.throttle = function throttle(func, wait){
    var wait = wait || 100;
    var context, args, result;
    var timeout = null;
    var previous = 0;
    var later = function() {
      previous = +new Date;
      timeout = null;
      result = func.apply(context, args);
      context = args = null;
    };
    return function() {
      var now = + new Date;
      var remaining = wait - (now - previous);
      context = this;
      args = arguments;
      if (remaining <= 0 || remaining > wait) {
        clearTimeout(timeout);
        timeout = null;
        previous = now;
        result = func.apply(context, args);
        context = args = null;
      } else if (!timeout) {
        timeout = setTimeout(later, remaining);
      }
      return result;
    };
  };
  
  // hogan escape
  // ==============
  _.escape = (function(){
    var rAmp = /&/g,
        rLt = /</g,
        rGt = />/g,
        rApos = /\'/g,
        rQuot = /\"/g,
        hChars = /[&<>\"\']/;
  
    return function(str) {
      return hChars.test(str) ?
        str
          .replace(rAmp, '&amp;')
          .replace(rLt, '&lt;')
          .replace(rGt, '&gt;')
          .replace(rApos, '&#39;')
          .replace(rQuot, '&quot;') :
        str;
    }
  })();
  
  _.cache = function(max){
    max = max || 1000;
    var keys = [],
        cache = {};
    return {
      set: function(key, value) {
        if (keys.length > this.max) {
          cache[keys.shift()] = undefined;
        }
        // 
        if(cache[key] === undefined){
          keys.push(key);
        }
        cache[key] = value;
        return value;
      },
      get: function(key) {
        if (key === undefined) return cache;
        return cache[key];
      },
      max: max,
      len:function(){
        return keys.length;
      }
    };
  }
  
  // // setup the raw Expression
  
  
  // handle the same logic on component's `on-*` and element's `on-*`
  // return the fire object
  _.handleEvent = function(value, type ){
    var self = this, evaluate;
    if(value.type === 'expression'){ // if is expression, go evaluated way
      evaluate = value.get;
    }
    if(evaluate){
      return function fire(obj){
        self.$update(function(){
          var data = this.data;
          data.$event = obj;
          var res = evaluate(self);
          if(res === false && obj && obj.preventDefault) obj.preventDefault();
          data.$event = undefined;
        })
  
      }
    }else{
      return function fire(){
        var args = _.slice(arguments);
        args.unshift(value);
        self.$update(function(){
          self.$emit.apply(self, args);
        })
      }
    }
  }
  
  // only call once
  _.once = function(fn){
    var time = 0;
    return function(){
      if( time++ === 0) fn.apply(this, arguments);
    }
  }
  
  _.fixObjStr = function(str){
    if(str.trim().indexOf('{') !== 0){
      return '{' + str + '}';
    }
    return str;
  }
  
  
  _.map= function(array, callback){
    var res = [];
    for (var i = 0, len = array.length; i < len; i++) {
      res.push(callback(array[i], i));
    }
    return res;
  }
  
  function log(msg, type){
    if(typeof console !== "undefined")  console[type || "log"](msg);
  }
  
  _.log = log;
  
  
  _.normListener = function( events  ){
      var eventListeners = [];
      var pType = _.typeOf( events );
      if( pType === 'array' ){
        return events;
      }else if ( pType === 'object' ){
        for( var i in events ) if ( events.hasOwnProperty(i) ){
          eventListeners.push({
            type: i,
            listener: events[i]
          })
        }
      }
      return eventListeners;
  }
  
  
  //http://www.w3.org/html/wg/drafts/html/master/single-page.html#void-elements
  _.isVoidTag = _.makePredicate("area base br col embed hr img input keygen link menuitem meta param source track wbr r-content");
  _.isBooleanAttr = _.makePredicate('selected checked disabled readonly required open autofocus controls autoplay compact loop defer multiple');
  
  
  _.isExpr = function(expr){
    return expr && expr.type === 'expression';
  }
  // @TODO: make it more strict
  _.isGroup = function(group){
    return group.inject || group.$inject;
  }
  
  _.getCompileFn = function(source, ctx, options){
    return ctx.$compile.bind(ctx,source, options)
  }
  
  // remove directive param from AST
  _.fixTagAST = function( tagAST, Component ){
  
    if( tagAST.touched ) return;
  
    var attrs = tagAST.attrs;
  
    if( !attrs ) return;
  
    // Maybe multiple directive need same param, 
    // We place all param in totalParamMap
    var len = attrs.length;
    if(!len) return;
    var directives=[], otherAttrMap = {};
    for(;len--;){
  
      var attr = attrs[ len ];
  
  
      // @IE fix IE9- input type can't assign after value
      if(attr.name === 'type') attr.priority = MAX_PRIORITY+1;
  
      var directive = Component.directive( attr.name );
      if( directive ) {
  
        attr.priority = directive.priority || 1;
        attr.directive = true;
        directives.push(attr);
  
      }else if(attr.type === 'attribute'){
        otherAttrMap[attr.name] = attr.value;
      }
    }
  
    directives.forEach( function( attr ){
      var directive = Component.directive(attr.name);
      var param = directive.param;
      if(param && param.length){
        attr.param = {};
        param.forEach(function( name ){
          if( name in otherAttrMap ){
            attr.param[name] = otherAttrMap[name] === undefined? true: otherAttrMap[name]
            _.removeOne(attrs, function(attr){
              return attr.name === name
            })
          }
        })
      }
    });
  
    attrs.sort(function(a1, a2){
      
      var p1 = a1.priority;
      var p2 = a2.priority;
  
      if( p1 == null ) p1 = MAX_PRIORITY;
      if( p2 == null ) p2 = MAX_PRIORITY;
  
      return p2 - p1;
  
    })
  
    tagAST.touched = true;
  }
  
  _.findItem = function(list, filter){
    if(!list || !list.length) return;
    var len = list.length;
    while(len--){
      if(filter(list[len])) return list[len]
    }
  }
  
  _.getParamObj = function(component, param){
    var paramObj = {};
    if(param) {
      for(var i in param) if(param.hasOwnProperty(i)){
        var value = param[i];
        paramObj[i] =  value && value.type==='expression'? component.$get(value): value;
      }
    }
    return paramObj;
  }
  
  
  
  
  
  
  