// selleck.js —- javascript templating

var selleck = (function() {
  var cache = {};
  // A forgiving object[key]
  var getValue = function(obj, key) {
    var gv = function(obj, keys) {
      if (keys.length === 0) {
        return obj;
      }
      first = keys.shift();
      // empty return on missing key
      if (!obj.hasOwnProperty(first)) return '';
      return gv(obj[first], keys);
    };
    var keys = key.split('.');
    return gv(obj, keys);
  }; 
  var getArray = function(obj, key) {
    // XXX more checks on whether gotten value is array?
    return getValue(obj, key) || [];
  };
  // expand all partials
  var expand = function(template, partials) {
    template = template.replace(/{>([\w.]+)}/g, function(str, partialKey) {
      // get rid of extra whitespace at the end of the partial
      return getValue(partials, partialKey).replace(/\s+$/, '');
    });
    return template;
  };
  var compile = function(template, partials) {
    var expanded = template = expand(template, partials);
    // first check cache and return if cached
    if (cache[expanded]) {
      return cache[expanded];
    } 
    // compile if not cached
    var replacement;
    var levels = {'for':0, 'if': 0};
    var nearby = function(offset, s) {
      return s.slice(offset-30, offset+10);
    };
    // replace simple key tags (like {foo})
    template = template.replace(/{([\w.]+)}/g, function(str, key) {
      return '\'+getValue(context,\''+key+'\')+\'';
    });
    // replace logic tags (like {for foo in bar} and {if foo})
    template = template.replace(/((?:\n|\r|\r\n)[ \t]*|){(\/?)(for|if|)( +|)(not +|)((\w+) +in +|)([\w.]+|)}(?:([ \t]*(?=\n|\r|\r\n))|)/g, function(str, newlineBefore, close, statement, statementSpace, ifNot, forIn, iterVar, key, newlineAfter, offset, s) {
      // if a logic tag is on a line by itself remove preceding newline and 
      // whitespace
      if (newlineBefore && newlineAfter !== undefined) {
        newlineBefore = '';
      }
      // {for foo in bar}
      if (statement === 'for' && statementSpace && iterVar && key) {
        levels['for']++;
        replacement = newlineBefore+'\';var '+iterVar+'A=getArray(context,\''+key+'\');for(var '+iterVar+'I=0;'+iterVar+'I<'+iterVar+'A.length;'+iterVar+'I++){context[\''+iterVar+'\']='+iterVar+'A['+iterVar+'I];buffer+=\'';
      // {if foo} or {if not foo}
      } else if (statement === 'if' && statementSpace && key) {
        levels['if']++;
        replacement = newlineBefore+'\';if('+(ifNot?'!':'')+'getValue(context,\''+key+'\')){buffer+=\'';
      // {/for} or {/if}
      } else if (close && statement) {
        if (levels[statement] === 0) {
          console.warn('extra {/'+statement+'} ignored at "'+nearby(offset, s)+'"');
          replacement = '';
        } else {
          levels[statement]--;
          replacement = newlineBefore+'\';}buffer+=\'';
        }
      // not a tag, don't replace
      } else {
        replacement = str;
      }
      return replacement;
    });
    // close extra fors and ifs
    for (statement in levels) {
      for (var i=0; i<levels[statement]; i++) {
        console.warn('extra "'+statement+'" closed at end of template');
        template = template+'\';}buffer+=\'';
      }
    }
    // ready newlines for eval
    template = template.replace(/\n/g, '\\n');
    // wrap in function
    template = '(function(){return function(context){var buffer=\''+template+'\';return buffer}})()';
    // cache template
    cache[expanded] = eval(template);
    return cache[expanded];
  };
  var render = function(template, context, partials) {
    var compiled = compile(template, partials);
    return compiled(context);
  };
  return {
    compile: compile,
    render: render
  };
})();

// populate exports for commonJS
for (attribute in selleck) {
  if (selleck.hasOwnProperty(attribute)) {
    exports[attribute] = selleck[attribute];
  }
}

@@ -281,20 +281,27 @@
  // 
-empty 
 return
+ empty string
  on 
@@ -519,19 +519,13 @@
 XXX 
-more 
 check
-s
  on 
@@ -536,14 +536,17 @@
 her 
-gotten
+retrieved
  val
@@ -946,218 +946,11 @@
 late
-, partials) %7B%0A    var expanded = template = expand(template, partials);%0A    // first check cache and return if cached%0A    if (cache%5Bexpanded%5D) %7B%0A      return cache%5Bexpanded%5D;%0A    %7D %0A    // compile if not cached
+) %7B
 %0A   
@@ -3252,16 +3252,14 @@
     
-// cache
+return
  tem
@@ -3267,112 +3267,190 @@
 late
+;
 %0A  
-  cache%5Bexpanded%5D = eval(template);%0A    return cache%5Bexpanded%5D;%0A  %7D;%0A  var render = function(
+%7D;%0A  var render = function(template, context, partials) %7B%0A    template = expand(template, partials);%0A    // use cached if it exists%0A    if (cache%5Btemplate%5D) return cache%5B
 template
 , co
@@ -3449,50 +3449,72 @@
 late
-, 
+%5D(
 context
-, partials) %7B%0A    var compiled = 
+);%0A    // compile and cache%0A    cache%5Btemplate%5D = eval(
 comp
@@ -3521,34 +3521,25 @@
 ile(template
-, partials
+)
 );%0A    retur
@@ -3545,15 +3545,22 @@
 rn c
-ompiled
+ache%5Btemplate%5D
 (con

Handle caching in render