<commit_before>/**
 * marked - A markdown parser
 * Copyright (c) 2011, Christopher Jeffrey. (MIT Licensed)
 */

;(function() {

/**
 * Block-Level Grammar
 */

var block = {
  newline: /^\n+/,
  block: /^ {4,}[^\n]*(?:\n {4,}[^\n]*)*/,
  hr: /^( *[\-*_]){3,}/,
  heading: /^ *(#{1,6}) *([^\n#]*) *#*/,
  lheading: /^([^\n]+)\n *(=|-){3,}/,
  blockquote: /^ *>[^\n]*(?:\n *>[^\n]*)*/,
  //list: /^(?:( *)(\*|\+|-|\d+\.)[^\n]+(?:\n[^\n]+)*(?:\n{1,2}|$)){2,}/,
  //list: /^(?:( *)(\*|\+|-|\d+\.)[^\n]+(?:\n[^\n]*)*){2,}(?:\n\n\1?(?!\*|\+|-|\d\.)|$)/,
  //list: /^(?:( *)(\*|\+|-|\d+\.)[^\n]+(?:\n[^\n]+|\n\n)*){2,}/,
  //list: /^( *)(\*|\+|-|\d+\.)[\s\S]+?(\1\2[\s\S]+?(?=\1\2))*/,

  // simple
  //list: /^(?:( *)(\*|\+|-|\d+\.)[^\n]*(?:\n\1 [^\n]+)*){2,}/,

  // very good, does it all
  //list: /^(?:( *)(\*|\+|-|\d+\.)[^\n]*(?:\n(?:\1 |)[^\n]+)*){2,}/,

  // better, faster
  //list: /^(?:( *)(\*|\+|-|\d+\.)[^\n]*(?:\n[^\n]+)*){2,}/,

  // fastest
  //list: /^( *)(\*|\+|-|\d+\.)[^\n]*(?:\n[^\n]+)*/,

  // fastest with loose list support
  list: /^( *)(\*|\+|-|\d+\.)[^\n]*(?:\n[^\n]+|\n{2}\1\2[^\n]*)*/,

  // the first list item
  //list: /^( *)(\*|\+|-|\d+\.)[^\n]*(?:\n(?!\1\2)[^\n]+)*/,

  // all list items
  //list: /^(?:( *)(\*|\+|-|\d+\.)[^\n]*(?:\n(?!\1\2)[^\n]+)*){2,}/,

  // need the question mark because a trailing list item might not have a newline after it
  //list: /^(?:( *)(\*|\+|-|\d+\.)[^\n]*\n(?:(?!\1\2)[^\n]+\n?)*){2,}/,

  // instead of question mark...
  //list: /^(?:( *)(\*|\+|-|\d+\.)[^\n]*\n(?:(?!\1\2)[^\n]+(?:\n|$))*){2,}/,

  //list: /^(?:( *)(\*|\+|-|\d+\.)[^\n]*\n(?:(?!\1\2)[^\n]+(?:\n|$))*){2,}/,

  html: /^<([^\/\s>]+)[^\n>]*>[^\n]*(?:\n[^\n]+)*\n?<\/\1>/,
  text: /^[^\n]+/
};

block.keys = [
  'newline',
  'block',
  'heading',
  'lheading',
  'hr',
  'blockquote',
  'list',
  'html',
  'text'
];

/**
 * Lexer
 */

block.lexer = function(str) {
  var tokens = []
    , links = {};

  // normalize whitespace
  str = str.replace(/\r\n/g, '\n')
           .replace(/\r/g, '\n')
           .replace(/\t/g, '    ');

  // experimental
  // str = str.replace(/^ +$/gm, '');

  // grab link definitons
  str = str.replace(
    /^ {0,3}\[([^\]]+)\]: *([^ ]+)(?: +"([^"]+)")? *(?:\n|$)/gm,
    function(_, id, href, title) {
      links[id] = {
        href: href,
        title: title
      };
      return '';
    }
  );

  tokens.links = links;

  return block.token(str, tokens);
};

block.token = function(str, tokens) {
  var rules = block
    , keys = block.keys
    , len = keys.length
    , key
    , cap
    , loose;

  var scan = function() {
    if (!str) return;
    for (var i = 0; i < len; i++) {
      key = keys[i];
      if (cap = rules[key].exec(str)) {
        str = str.substring(cap[0].length);
        return true;
      }
    }
  };

  while (scan()) {
    switch (key) {
      case 'newline':
        if (cap[0].length > 1) {
          tokens.push({
            type: 'space'
          });
        }
        break;
      case 'hr':
        tokens.push({
          type: 'hr'
        });
        break;
      case 'lheading':
        tokens.push({
          type: 'heading',
          depth: cap[2] === '=' ? 1 : 2,
          text: cap[1]
        });
        break;
      case 'heading':
        tokens.push({
          type: 'heading',
          depth: cap[1].length,
          text: cap[2]
        });
        break;
      case 'block':
        cap = cap[0].replace(/^ {4}/gm, '');
        tokens.push({
          type: 'block',
          text: cap
        });
        break;
      case 'list':
        tokens.push({
          type: 'list_start',
          ordered: isFinite(cap[2])
        });
        loose = /\n *\n *(?:\*|\+|-|\d+\.)/.test(cap[0]);
        //loose = false; // /\n *\n(?! *$)/.test(cap[0]);
        //loose = '\n *\n' + cap[1]
        //      + '(?:\\*|\\+|-|\\d+\\.)';
        //loose = new RegExp(loose).test(cap[0]);
        // get each top-level
        // item in the list
        //console.log('----');
        //console.log(cap[0]);
        //console.log('----');
        cap = cap[0].match(
          // /^( *)(\*|\+|-|\d+\.)[^\n]+(?:\n\1+(?!\*|\+|-|\d\.)[^\n]*)*/gm
          // /^( *)(\*|\+|-|\d+\.)[^\n]+(?:\n\1+(?!\*|\+|-|\d\.)[^\n]*|\n)*/gm
          /^( *)(\*|\+|-|\d+\.)[^\n]*(?:\n(?!\1\2)[^\n]*)*/gm
        );
        each(cap, function(item) {
          // remove the list items sigil
          // so its seen as the next token
          item = item.replace(/^ *(\*|\+|-|\d+\.) */, '');
          // outdent whatever the
          // list item contains, hacky
          var space = /\n( +)/.exec(item);
          if (space) {
            space = new RegExp('^' + space[1], 'gm');
            item = item.replace(space, '');
          }
          tokens.push({
            type: loose
              ? 'loose_item_start'
              : 'list_item_start'
          });
          block.token(item, tokens);
          tokens.push({
            type: 'list_item_end'
          });
        });
        tokens.push({
          type: 'list_end'
        });
        break;
      case 'html':
      case 'text':
        tokens.push({
          type: key,
          text: cap[0]
        });
        break;
      case 'blockquote':
        tokens.push({
          type: 'blockquote_start'
        });
        cap = cap[0].replace(/^ *>/gm, '');
        block.token(cap, tokens);
        tokens.push({
          type: 'blockquote_end'
        });
        break;
    }
  }

  return tokens;
};

/**
 * Inline Processing
 */

var inline = {
  escape: /^\\([\\`*{}\[\]()#+\-.!])/,
  autolink: /^<([^ >]+(@|:\/)[^ >]+)>/,
  tag: /^<[^\n>]+>/,
  link: /^!?\[([^\]]+)\]\(([^\)]+)\)/,
  reflink: /^!?\[([^\]]+)\]\[([^\]]+)\]/,
  strong: /^__([\s\S]+?)__|^\*\*([\s\S]+?)\*\*/,
  em: /^_([^_]+)_|^\*([^*]+)\*/,
  code: /^`([^`]+)`|^``([\s\S]+?)``/
};

inline.keys = [
  'escape',
  'autolink',
  'tag',
  'link',
  'reflink',
  'strong',
  'em',
  'code'
];

// hacky, but performant
inline.text = (function(rules) {
  var keys = rules.keys
    , i = 0
    , l = keys.length
    , body = [];

  for (; i < l; i++) {
    body.push(rules[keys[i]].source
      .replace(/(^|[^\[])\^/g, '$1'));
  }

  keys.push('text');

  return new RegExp(
    '^([\\s\\S]+?)(?='
    + body.join('|')
    + '|$)'
  );
})(inline);

/**
 * Inline Lexer
 */

inline.lexer = function(str) {
  var out = ''
    , links = tokens.links
    , link
    , text
    , href;

  var rules = inline
    , keys = inline.keys
    , len = keys.length
    , key
    , cap;

  var scan = function() {
    if (!str) return;
    for (var i = 0; i < len; i++) {
      key = keys[i];
      if (cap = rules[key].exec(str)) {
        str = str.substring(cap[0].length);
        return true;
      }
    }
  };

  while (scan()) {
    switch (key) {
      case 'escape':
        out += cap[1];
        break;
      case 'tag':
        out += cap[0];
        break;
      case 'link':
      case 'reflink':
        if (cap[0][0] !== '!') {
          if (key === 'reflink') {
            link = links[cap[2]];
            if (!link) throw new
              Error('Undefined Reference: ' + cap[2]);
          } else {
            link = {
              href: cap[2],
              title: cap[3]
            };
          }
          out += ''
            + inline.lexer(cap[1])
            + '';
        } else {
          if (key === 'reflink') {
            link = links[cap[2]];
            if (!link) throw new
              Error('Undefined Reference: ' + cap[2]);
          } else {
            text = /^([^\s]+)\s*(.+)?$/.exec(cap[2]);
            link = {
              href: text[1],
              title: text[2]
            };
          }
          out += '';
        }
        break;
      case 'autolink':
        if (cap[2] === '@') {
          text = mangle(cap[1]);
          href = mangle('mailto:') + text;
        } else {
          text = escape(cap[1]);
          href = text;
        }
        out += ''
          + text
          + '';
        break;
      case 'strong':
        out += ''
          + inline.lexer(cap[2] || cap[1])
          + '';
        break;
      case 'em':
        out += ''
          + inline.lexer(cap[2] || cap[1])
          + '';
        break;
      case 'code':
        out += ''
          + escape(cap[2] || cap[1])
          + '';
        break;
      case 'text':
        out += escape(cap[1]);
        break;
      default:
        break;
    }
  }

  return out;
};

/**
 * Parsing
 */

var tokens
  , token;

var next = function() {
  return token = tokens.pop();
};

var tok = function() {
  switch (token.type) {
    case 'space':
      return '';
    case 'hr':
      return '
';
    case 'heading':
      return '<h' + token.depth + '>'
        + inline.lexer(token.text)
        + '</h' + token.depth + '>';
    case 'block':
      return '
'
        + escape(token.text)
        + '
';
    case 'blockquote_start':
      var body = [];
      while (next().type !== 'blockquote_end') {
        body.push(tok());
      }
      return '
'
        + body.join('')
        + '
';
    case 'list_start':
      var body = []
        , type = token.ordered ? 'ol' : 'ul';
      while (next().type !== 'list_end') {
        body.push(tok());
      }
      return '<' + type + '>'
        + body.join('')
        + '</' + type + '>';
    case 'list_item_start':
      var body = [];
      while (next().type !== 'list_item_end') {
        body.push(token.type === 'text'
          ? inline.lexer(token.text)
          : tok());
      }
      return '
'
        + body.join(' ')
        + '
';
    case 'loose_item_start':
      var body = [];
      while (next().type !== 'list_item_end') {
        body.push(tok());
      }
      return '
'
        + body.join(' ')
        + '
';
    case 'html':
      return inline.lexer(token.text);
    case 'text':
      var body = [ token.text ]
        , top;
      while ((top = tokens[tokens.length-1])
             && top.type === 'text') {
        body.push(next().text);
      }
      return '
'
        + inline.lexer(body.join(' '))
        + '
';
  }
};

var parse = function(src) {
  tokens = src.reverse();

  var out = [];
  while (next()) {
    out.push(tok());
  }

  tokens = null;
  token = null;

  return out.join(' ');
};

/**
 * Helpers
 */

var escape = function(html) {
  return html
    .replace(/&/g, '&')
    .replace(/</g, '<')
    .replace(/>/g, '>')
    .replace(/"/g, '"')
    .replace(/'/g, ''');
};

var mangle = function(str) {
  var out = ''
    , ch
    , i = 0
    , l = str.length;

  for (; i < l; i++) {
    ch = str.charCodeAt(i);
    if (Math.random() > 0.5) {
      ch = 'x' + ch.toString(16);
    }
    out += '&#' + ch + ';';
  }

  return out;
};

var each = function(obj, func) {
  var i = 0, l = obj.length;
  for (; i < l; i++) func(obj[i]);
};

/**
 * Expose
 */

var marked = function(str) {
  return parse(block.lexer(str));
};

marked.parser = parse;
marked.lexer = block.lexer;

if (typeof module !== 'undefined') {
  module.exports = marked;
} else {
  this.marked = marked;
}

}).call(this);
<commit_msg>fix ordered lists<commit_after>/**
 * marked - A markdown parser
 * Copyright (c) 2011, Christopher Jeffrey. (MIT Licensed)
 */

;(function() {

/**
 * Block-Level Grammar
 */

var block = {
  newline: /^\n+/,
  block: /^ {4,}[^\n]*(?:\n {4,}[^\n]*)*/,
  hr: /^( *[\-*_]){3,}/,
  heading: /^ *(#{1,6}) *([^\n#]*) *#*/,
  lheading: /^([^\n]+)\n *(=|-){3,}/,
  blockquote: /^ *>[^\n]*(?:\n *>[^\n]*)*/,
  //list: /^(?:( *)(\*|\+|-|\d+\.)[^\n]+(?:\n[^\n]+)*(?:\n{1,2}|$)){2,}/,
  //list: /^(?:( *)(\*|\+|-|\d+\.)[^\n]+(?:\n[^\n]*)*){2,}(?:\n\n\1?(?!\*|\+|-|\d\.)|$)/,
  //list: /^(?:( *)(\*|\+|-|\d+\.)[^\n]+(?:\n[^\n]+|\n\n)*){2,}/,
  //list: /^( *)(\*|\+|-|\d+\.)[\s\S]+?(\1\2[\s\S]+?(?=\1\2))*/,

  // simple
  //list: /^(?:( *)(\*|\+|-|\d+\.)[^\n]*(?:\n\1 [^\n]+)*){2,}/,

  // very good, does it all
  //list: /^(?:( *)(\*|\+|-|\d+\.)[^\n]*(?:\n(?:\1 |)[^\n]+)*){2,}/,

  // better, faster
  //list: /^(?:( *)(\*|\+|-|\d+\.)[^\n]*(?:\n[^\n]+)*){2,}/,

  // fastest
  //list: /^( *)(\*|\+|-|\d+\.)[^\n]*(?:\n[^\n]+)*/,

  // fastest with loose list support
  // list: /^( *)(\*|\+|-|\d+\.)[^\n]*(?:\n[^\n]+|\n{2}\1\2[^\n]*)*/,

  // fix ordered lists:
  list: /^( *)([*+-]|\d+\.)[^\n]*(?:\n[^\n]+|\n{2}\1(?:\2|\d+\.)[^\n]*)*/,

  // the first list item
  //list: /^( *)(\*|\+|-|\d+\.)[^\n]*(?:\n(?!\1\2)[^\n]+)*/,

  // all list items
  //list: /^(?:( *)(\*|\+|-|\d+\.)[^\n]*(?:\n(?!\1\2)[^\n]+)*){2,}/,

  // need the question mark because a trailing list item might not have a newline after it
  //list: /^(?:( *)(\*|\+|-|\d+\.)[^\n]*\n(?:(?!\1\2)[^\n]+\n?)*){2,}/,

  // instead of question mark...
  //list: /^(?:( *)(\*|\+|-|\d+\.)[^\n]*\n(?:(?!\1\2)[^\n]+(?:\n|$))*){2,}/,

  //list: /^(?:( *)(\*|\+|-|\d+\.)[^\n]*\n(?:(?!\1\2)[^\n]+(?:\n|$))*){2,}/,

  html: /^<([^\/\s>]+)[^\n>]*>[^\n]*(?:\n[^\n]+)*\n?<\/\1>/,
  text: /^[^\n]+/
};

block.keys = [
  'newline',
  'block',
  'heading',
  'lheading',
  'hr',
  'blockquote',
  'list',
  'html',
  'text'
];

/**
 * Lexer
 */

block.lexer = function(str) {
  var tokens = []
    , links = {};

  // normalize whitespace
  str = str.replace(/\r\n/g, '\n')
           .replace(/\r/g, '\n')
           .replace(/\t/g, '    ');

  // experimental
  // str = str.replace(/^ +$/gm, '');

  // grab link definitons
  str = str.replace(
    /^ {0,3}\[([^\]]+)\]: *([^ ]+)(?: +"([^"]+)")? *(?:\n|$)/gm,
    function(_, id, href, title) {
      links[id] = {
        href: href,
        title: title
      };
      return '';
    }
  );

  tokens.links = links;

  return block.token(str, tokens);
};

block.token = function(str, tokens) {
  var rules = block
    , keys = block.keys
    , len = keys.length
    , key
    , cap
    , loose;

  var scan = function() {
    if (!str) return;
    for (var i = 0; i < len; i++) {
      key = keys[i];
      if (cap = rules[key].exec(str)) {
        str = str.substring(cap[0].length);
        return true;
      }
    }
  };

  while (scan()) {
    switch (key) {
      case 'newline':
        if (cap[0].length > 1) {
          tokens.push({
            type: 'space'
          });
        }
        break;
      case 'hr':
        tokens.push({
          type: 'hr'
        });
        break;
      case 'lheading':
        tokens.push({
          type: 'heading',
          depth: cap[2] === '=' ? 1 : 2,
          text: cap[1]
        });
        break;
      case 'heading':
        tokens.push({
          type: 'heading',
          depth: cap[1].length,
          text: cap[2]
        });
        break;
      case 'block':
        cap = cap[0].replace(/^ {4}/gm, '');
        tokens.push({
          type: 'block',
          text: cap
        });
        break;
      case 'list':
        tokens.push({
          type: 'list_start',
          ordered: isFinite(cap[2])
        });
        loose = /\n *\n *(?:\*|\+|-|\d+\.)/.test(cap[0]);
        //loose = false; // /\n *\n(?! *$)/.test(cap[0]);
        //loose = '\n *\n' + cap[1]
        //      + '(?:\\*|\\+|-|\\d+\\.)';
        //loose = new RegExp(loose).test(cap[0]);
        // get each top-level
        // item in the list
        //console.log('----');
        //console.log(cap[0]);
        //console.log('----');
        cap = cap[0].match(
          // /^( *)(\*|\+|-|\d+\.)[^\n]+(?:\n\1+(?!\*|\+|-|\d\.)[^\n]*)*/gm
          // /^( *)(\*|\+|-|\d+\.)[^\n]+(?:\n\1+(?!\*|\+|-|\d\.)[^\n]*|\n)*/gm

          // /^( *)(\*|\+|-|\d+\.)[^\n]*(?:\n(?!\1\2)[^\n]*)*/gm

          // fix ordered lists:
          /^( *)([*+-]|\d+\.)[^\n]*(?:\n(?!\1(?:\2|\d+\.))[^\n]*)*/gm
        );
        each(cap, function(item) {
          // remove the list items sigil
          // so its seen as the next token
          item = item.replace(/^ *(\*|\+|-|\d+\.) */, '');
          // outdent whatever the
          // list item contains, hacky
          var space = /\n( +)/.exec(item);
          if (space) {
            space = new RegExp('^' + space[1], 'gm');
            item = item.replace(space, '');
          }
          tokens.push({
            type: loose
              ? 'loose_item_start'
              : 'list_item_start'
          });
          block.token(item, tokens);
          tokens.push({
            type: 'list_item_end'
          });
        });
        tokens.push({
          type: 'list_end'
        });
        break;
      case 'html':
      case 'text':
        tokens.push({
          type: key,
          text: cap[0]
        });
        break;
      case 'blockquote':
        tokens.push({
          type: 'blockquote_start'
        });
        cap = cap[0].replace(/^ *>/gm, '');
        block.token(cap, tokens);
        tokens.push({
          type: 'blockquote_end'
        });
        break;
    }
  }

  return tokens;
};

/**
 * Inline Processing
 */

var inline = {
  escape: /^\\([\\`*{}\[\]()#+\-.!])/,
  autolink: /^<([^ >]+(@|:\/)[^ >]+)>/,
  tag: /^<[^\n>]+>/,
  link: /^!?\[([^\]]+)\]\(([^\)]+)\)/,
  reflink: /^!?\[([^\]]+)\]\[([^\]]+)\]/,
  strong: /^__([\s\S]+?)__|^\*\*([\s\S]+?)\*\*/,
  em: /^_([^_]+)_|^\*([^*]+)\*/,
  code: /^`([^`]+)`|^``([\s\S]+?)``/
};

inline.keys = [
  'escape',
  'autolink',
  'tag',
  'link',
  'reflink',
  'strong',
  'em',
  'code'
];

// hacky, but performant
inline.text = (function(rules) {
  var keys = rules.keys
    , i = 0
    , l = keys.length
    , body = [];

  for (; i < l; i++) {
    body.push(rules[keys[i]].source
      .replace(/(^|[^\[])\^/g, '$1'));
  }

  keys.push('text');

  return new RegExp(
    '^([\\s\\S]+?)(?='
    + body.join('|')
    + '|$)'
  );
})(inline);

/**
 * Inline Lexer
 */

inline.lexer = function(str) {
  var out = ''
    , links = tokens.links
    , link
    , text
    , href;

  var rules = inline
    , keys = inline.keys
    , len = keys.length
    , key
    , cap;

  var scan = function() {
    if (!str) return;
    for (var i = 0; i < len; i++) {
      key = keys[i];
      if (cap = rules[key].exec(str)) {
        str = str.substring(cap[0].length);
        return true;
      }
    }
  };

  while (scan()) {
    switch (key) {
      case 'escape':
        out += cap[1];
        break;
      case 'tag':
        out += cap[0];
        break;
      case 'link':
      case 'reflink':
        if (cap[0][0] !== '!') {
          if (key === 'reflink') {
            link = links[cap[2]];
            if (!link) throw new
              Error('Undefined Reference: ' + cap[2]);
          } else {
            link = {
              href: cap[2],
              title: cap[3]
            };
          }
          out += ''
            + inline.lexer(cap[1])
            + '';
        } else {
          if (key === 'reflink') {
            link = links[cap[2]];
            if (!link) throw new
              Error('Undefined Reference: ' + cap[2]);
          } else {
            text = /^([^\s]+)\s*(.+)?$/.exec(cap[2]);
            link = {
              href: text[1],
              title: text[2]
            };
          }
          out += '';
        }
        break;
      case 'autolink':
        if (cap[2] === '@') {
          text = mangle(cap[1]);
          href = mangle('mailto:') + text;
        } else {
          text = escape(cap[1]);
          href = text;
        }
        out += ''
          + text
          + '';
        break;
      case 'strong':
        out += ''
          + inline.lexer(cap[2] || cap[1])
          + '';
        break;
      case 'em':
        out += ''
          + inline.lexer(cap[2] || cap[1])
          + '';
        break;
      case 'code':
        out += ''
          + escape(cap[2] || cap[1])
          + '';
        break;
      case 'text':
        out += escape(cap[1]);
        break;
      default:
        break;
    }
  }

  return out;
};

/**
 * Parsing
 */

var tokens
  , token;

var next = function() {
  return token = tokens.pop();
};

var tok = function() {
  switch (token.type) {
    case 'space':
      return '';
    case 'hr':
      return '
';
    case 'heading':
      return '<h' + token.depth + '>'
        + inline.lexer(token.text)
        + '</h' + token.depth + '>';
    case 'block':
      return '
'
        + escape(token.text)
        + '
';
    case 'blockquote_start':
      var body = [];
      while (next().type !== 'blockquote_end') {
        body.push(tok());
      }
      return '
'
        + body.join('')
        + '
';
    case 'list_start':
      var body = []
        , type = token.ordered ? 'ol' : 'ul';
      while (next().type !== 'list_end') {
        body.push(tok());
      }
      return '<' + type + '>'
        + body.join('')
        + '</' + type + '>';
    case 'list_item_start':
      var body = [];
      while (next().type !== 'list_item_end') {
        body.push(token.type === 'text'
          ? inline.lexer(token.text)
          : tok());
      }
      return '
'
        + body.join(' ')
        + '
';
    case 'loose_item_start':
      var body = [];
      while (next().type !== 'list_item_end') {
        body.push(tok());
      }
      return '
'
        + body.join(' ')
        + '
';
    case 'html':
      return inline.lexer(token.text);
    case 'text':
      var body = [ token.text ]
        , top;
      while ((top = tokens[tokens.length-1])
             && top.type === 'text') {
        body.push(next().text);
      }
      return '
'
        + inline.lexer(body.join(' '))
        + '
';
  }
};

var parse = function(src) {
  tokens = src.reverse();

  var out = [];
  while (next()) {
    out.push(tok());
  }

  tokens = null;
  token = null;

  return out.join(' ');
};

/**
 * Helpers
 */

var escape = function(html) {
  return html
    .replace(/&/g, '&')
    .replace(/</g, '<')
    .replace(/>/g, '>')
    .replace(/"/g, '"')
    .replace(/'/g, ''');
};

var mangle = function(str) {
  var out = ''
    , ch
    , i = 0
    , l = str.length;

  for (; i < l; i++) {
    ch = str.charCodeAt(i);
    if (Math.random() > 0.5) {
      ch = 'x' + ch.toString(16);
    }
    out += '&#' + ch + ';';
  }

  return out;
};

var each = function(obj, func) {
  var i = 0, l = obj.length;
  for (; i < l; i++) func(obj[i]);
};

/**
 * Expose
 */

var marked = function(str) {
  return parse(block.lexer(str));
};

marked.parser = parse;
marked.lexer = block.lexer;

if (typeof module !== 'undefined') {
  module.exports = marked;
} else {
  this.marked = marked;
}

}).call(this);
<|endoftext|>


<commit_before>'use strict';

var fsnode  = require('fs');
var fsx     = require('fs-extra');
var path    = require('path');
var through = require('through2');
var assert  = require('assert');

var fs = require('../index.js');

describe('create', function () {
  var root = path.resolve('test');
  var dir  = 'dir';

  after(function () {
    fsx.removeSync(path.join(root, dir));
  });

  var configuration = [
    {                             },
    {type: 'file'                 },
    {type: 'directory'            },
    {                   add: true },
    {type: 'file',      add: true },
    {type: 'directory', add: true },
    {                   add: false},
    {type: 'file',      add: false},
    {type: 'directory', add: false}
  ];

  function buildFirstParam(paramType) {
    return function (conf, i) {
      var addType  = 'add' in conf ? conf.add ? 'true' : 'false' : 'default';
      var add      = addType === 'default' ? true : conf.add;
      var addTitle = [
        'The new ', type, ' is', (add ? ' ' : ' not '), 'part of the stream'
      ].join('');

      var type     = conf.type || 'file';
      var typeFn   = type === 'file' ? 'isFile' : 'isDirectory';
      var typeTile = 'The object created is a ' + type;

      var filename = paramType + i + '.txt';
      var filepath = path.join(root, dir, filename);
      var filerel  = path.join(dir, filename);

      var title    = [
        'A new ', type, (conf.type ? ' (' : ' (implicite, '),
        paramType, ' path, opt.add === ', addType, ')'
      ].join('');

      var files    = [];

      function inStream(exist) {
        if (exist) { return files.indexOf(filepath) > -1 }
        return files.indexOf(filepath) === -1;
      }

      it(title, function (done) {
        var file = filerel;

        if (paramType === 'fn') {
          file = function (f) {
            assert(f === root, 'Path as a function get a path as param');
            return filerel;
          }
        }

        function end() {
          assert(inStream(add), addTitle);

          fsnode.stat(filepath, function (err, stats) {
            var isGoodType = err ? false : stats[typeFn]();
            assert(isGoodType, typeTile);
            done();
          });
        }

        fs(root)
          .pipe(fs.create(file, conf))
          .on('end', end)
          .on('error', done)
          .pipe(through.obj(function (file, end, cb) {
            files.push(file.path);

            cb(null, file);
          }));
      });
    }
  }

  configuration.forEach(buildFirstParam('string'));
  configuration.forEach(buildFirstParam('fn'));
});
<commit_msg>code cleaning<commit_after>'use strict';

var fsnode  = require('fs');
var fsx     = require('fs-extra');
var path    = require('path');
var through = require('through2');
var assert  = require('assert');

var fs = require('../index.js');

describe('create', function () {
  var root = path.resolve('test');
  var dir  = 'dir';

  after(function () {
    fsx.removeSync(path.join(root, dir));
  });

  var configuration = [
    {                             },
    {type: 'file'                 },
    {type: 'directory'            },
    {                   add: true },
    {type: 'file',      add: true },
    {type: 'directory', add: true },
    {                   add: false},
    {type: 'file',      add: false},
    {type: 'directory', add: false}
  ];

  function buildFirstParam(paramType) {
    return function (conf, i) {
      var type     = conf.type || 'file';
      var typeFn   = type === 'file' ? 'isFile' : 'isDirectory';
      var typeTile = 'The object created is a ' + type;

      var addType  = 'add' in conf ? conf.add ? 'true' : 'false' : 'default';
      var add      = addType === 'default' ? true : conf.add;
      var addTitle = [
        'The new ', type, ' is', (add ? ' ' : ' not '), 'part of the stream'
      ].join('');

      var filename = paramType + i + '.txt';
      var filepath = path.join(root, dir, filename);
      var filerel  = path.join(dir, filename);

      var title    = [
        'A new ', type, (conf.type ? ' (' : ' (implicite, '),
        paramType, ' path, opt.add === ', addType, ')'
      ].join('');

      var files = [];

      function inStream(exist) {
        if (exist) { return files.indexOf(filepath) > -1; }
        return files.indexOf(filepath) === -1;
      }

      it(title, function (done) {
        var file = filerel;

        if (paramType === 'fn') {
          file = function (f) {
            assert(f === root, 'Path as a function get a path as param');
            return filerel;
          };
        }

        function end() {
          assert(inStream(add), addTitle);

          fsnode.stat(filepath, function (err, stats) {
            var isGoodType = err ? false : stats[typeFn]();
            assert(isGoodType, typeTile);
            done();
          });
        }

        fs(root)
          .pipe(fs.create(file, conf))
          .on('end', end)
          .on('error', done)
          .pipe(through.obj(function (file, end, cb) {
            files.push(file.path);

            cb(null, file);
          }));
      });
    };
  }

  configuration.forEach(buildFirstParam('string'));
  configuration.forEach(buildFirstParam('fn'));
});
<|endoftext|>


<commit_before>var yaml = require( "js-yaml" ),
	config = require("./config.json");

module.exports = function( grunt ) {

"use strict";

grunt.loadNpmTasks( "grunt-clean" );
grunt.loadNpmTasks( "grunt-html" );
grunt.loadNpmTasks( "grunt-wordpress" );
grunt.loadNpmTasks( "grunt-jquery-content" );
grunt.loadNpmTasks( "grunt-check-modules" );

grunt.initConfig({
	clean: {
		wordpress: "dist/"
	},
	htmllint: {
		resources: "resources/*.html"
	},
	jshint: {
		options: {
			undef: true,
			node: true
		}
	},
	lint: {
		grunt: "grunt.js"
	},
	watch: {
		pages: {
			files: "page/**",
			tasks: "deploy"
		}
	},
	"build-pages": {
		all: grunt.file.expandFiles( "page/**" )
	},
	"build-resources": {
		all: grunt.file.expandFiles( "resources/**/*" )
	},
	wordpress: grunt.utils._.extend({
		dir: "dist/wordpress",
		order: "order.yml"
	}, grunt.file.readJSON( "config.json" ) )
});

// Process a YAML order file and return an object of page slugs and their ordinal indices
grunt.registerHelper( "read-order", function( orderFile ) {
	var order,
		map = {},
		index = 0;

	try {
		order = yaml.load( grunt.file.read( orderFile ) );
	} catch( error ) {
		grunt.warn( "Invalid order file: " + orderFile );
		return null;
	}

	function flatten( item, folder ) {
		var title,
		path = folder ? [ folder ] : [];

		if ( grunt.utils._.isObject( item ) ) {
			title = Object.keys( item )[ 0 ];
			path.push( title );
			path = path.join( "/" );
			map[ path ] = ++index;

			item[ title ].forEach(function( item ) {
				flatten( item, path );
			});
		} else {
			path.push( item );
			map[ path.join( "/" ) ] = ++index;
		}
	}
	order.forEach(function( item ) {
		flatten( item );
	});
	return map;
});

grunt.registerHelper( "build-pages-preprocess", (function() {
	var orderMap = grunt.helper( "read-order", "order.yml" );

	return function( post, fileName, done ) {
		var slug = fileName.replace( /^.+?\/(.+)\.\w+$/, "$1" ),
			menuOrder = orderMap[ slug ];
		if ( menuOrder ) {
			post.menuOrder = menuOrder;
		}
		done();
	};
})());

grunt.registerTask( "default", "wordpress-deploy" );
grunt.registerTask( "build-wordpress", "check-modules clean lint build-pages build-resources");
grunt.registerTask( "deploy", "wordpress-deploy" );

};

<commit_msg>Initial implementation of parsing git file history and pushing git contributor/legacy attribution list into WordPress<commit_after>var yaml = require( "js-yaml" ),
	config = require("./config.json");

module.exports = function( grunt ) {

"use strict";

grunt.loadNpmTasks( "grunt-clean" );
grunt.loadNpmTasks( "grunt-html" );
grunt.loadNpmTasks( "grunt-wordpress" );
grunt.loadNpmTasks( "grunt-jquery-content" );
grunt.loadNpmTasks( "grunt-check-modules" );

grunt.initConfig({
	clean: {
		wordpress: "dist/"
	},
	htmllint: {
		resources: "resources/*.html"
	},
	jshint: {
		options: {
			undef: true,
			node: true
		}
	},
	lint: {
		grunt: "grunt.js"
	},
	watch: {
		pages: {
			files: "page/**",
			tasks: "deploy"
		}
	},
	"build-pages": {
		all: grunt.file.expandFiles( "page/**" )
	},
	"build-resources": {
		all: grunt.file.expandFiles( "resources/**/*" )
	},
	wordpress: grunt.utils._.extend({
		dir: "dist/wordpress",
		order: "order.yml"
	}, grunt.file.readJSON( "config.json" ) )
});

// Process a YAML order file and return an object of page slugs and their ordinal indices
grunt.registerHelper( "read-order", function( orderFile ) {
	var order,
		map = {},
		index = 0;

	try {
		order = yaml.load( grunt.file.read( orderFile ) );
	} catch( error ) {
		grunt.warn( "Invalid order file: " + orderFile );
		return null;
	}

	function flatten( item, folder ) {
		var title,
		path = folder ? [ folder ] : [];

		if ( grunt.utils._.isObject( item ) ) {
			title = Object.keys( item )[ 0 ];
			path.push( title );
			path = path.join( "/" );
			map[ path ] = ++index;

			item[ title ].forEach(function( item ) {
				flatten( item, path );
			});
		} else {
			path.push( item );
			map[ path.join( "/" ) ] = ++index;
		}
	}
	order.forEach(function( item ) {
		flatten( item );
	});
	return map;
});

grunt.registerHelper( "contributor-attribution", function( post, fileName, fn ) {
	var contribs = [],
		_ = grunt.utils._,
		parseRE = /^(.*)<(.*)>$/; // could certainly be better.

	// Read contributors from git file information
	grunt.utils.spawn({
		cmd: "git",
		args: [ "log", "--format=%aN <%aE>", fileName ]
	}, function( err, result ) {
		if ( err ) {
			grunt.verbose.error();
			grunt.log.error( err );
			return;
		}
		// make unique.)
		result.stdout.split( /\r?\n/g ).forEach(function(line) {
			if ( _.contains( contribs, line ) ) {
				return;
			}
			contribs.push(line);
		});

		// make object { name: 'name', email: 'email@address.com' }
		contribs.forEach(function(str, idx) {
			var m = parseRE.exec(str);
			if ( m ) {
				contribs[idx] = { name: m[1].trim(), email: m[2] };
			}
			else {
				contribs[idx] = { name: str };
			}
		});

		// Alphabetize by 'last name' (relatively crude)
		contribs = _.sortBy( contribs, function(a) {
			return a.name.split(' ').pop().toLowerCase();
		});

		// Handle "legacy" content - content authored outside of the learn site
		// and attributed with metadata in the file,
		// push those contributors to the front of the list
		if ( post.attribution ) {
			post.attribution.forEach(function(str, idx) {
				var contrib, m;

				// Handling specifically for articles originally from jQuery Fundamentals
				if (str == "jQuery Fundamentals") {
					contribs.unshift({
						name: str,
						// Use the jQuery Gravatar
						email: "github@jquery.com",
						source: post.source
					});
				} else {
					m = parseRE.exec(str);
					if ( m ) {
						contrib = { name: m[1].trim(), email: m[2] };
					}
					else {
						contrib = { name: str };
					}
					if ( post.source ) {
						contrib.source = post.source;
					}
				}
			});
		}

		if ( post.customFields ) {
			post.customFields.push({
				key: "contributors",
				value: JSON.stringify( contribs )
			});

		} else {
			post.customFields = [{
				key: "contributors",
				value: JSON.stringify( contribs )
			}];
		}

		fn();
	});

});

grunt.registerHelper( "build-pages-preprocess", (function() {
	var orderMap = grunt.helper( "read-order", "order.yml" );

	return function( post, fileName, done ) {
		grunt.utils.async.series([
			function applyOrder( fn ) {
				var slug = fileName.replace( /^.+?\/(.+)\.\w+$/, "$1" ),
				menuOrder = orderMap[ slug ];
				if ( menuOrder ) {
					post.menuOrder = menuOrder;
				}
				fn();
			},

			function applyContribs( fn ) {
				grunt.helper( "contributor-attribution", post, fileName, fn );
			}
		], done );
	};
})());

grunt.registerTask( "default", "wordpress-deploy" );
grunt.registerTask( "build-wordpress", "check-modules clean lint build-pages build-resources");
grunt.registerTask( "deploy", "wordpress-deploy" );

};

<|endoftext|>


<commit_before>import Vue from 'vue';
import VueResource from 'vue-resource';
import { router } from 'src/main';

const API_BASE = 'http://jsonplaceholder.typicode.com';

Vue.use(VueResource);

Vue.http.options = {
  root: API_BASE
};

Vue.http.interceptors.push((request, next) => {
  next((response) => {
    // Handle global API 404 =>
    if (response.status === 404) {
      router.go('/404');
    }
  });
});

export const postsResource = Vue.resource('posts{/id}');
<commit_msg>New routing methods<commit_after>import Vue from 'vue';
import VueResource from 'vue-resource';
import { router } from 'src/main';

const API_BASE = 'http://jsonplaceholder.typicode.com';

Vue.use(VueResource);

Vue.http.options = {
  root: API_BASE
};

Vue.http.interceptors.push((request, next) => {
  next((response) => {
    // Handle global API 404 =>
    if (response.status === 404) {
      router.push('/404');
    }
  });
});

export const postsResource = Vue.resource('posts{/id}');
<|endoftext|>


<commit_before>var _ = require('underscore'),
    combine = require('./jsCombine'),
    configLoader = require('./config'),
    Context = require('./context'),
    fs = require('fs'),
    fu = require('./fileUtil'),
    plugin = require('./plugin'),
    watch = require('./watcher.js'),
    growl = require('growl');

function logError(err) {
    // TODO : Move this to the executable
    console.error(err.stack || err.message);
    growl(err.message, { title: 'Lumbar error' });
}

/** @type {Object} */
var config;
/** @type {Object} cache */
var configCache;

/**
 *
 * @name loadConfig
 * @function This function loads a JSON config file.
 * @param {string} path to the JSON file we want to load.
 */
function loadConfig(path) {
  try {
    configCache = {};
    config = configLoader.load(path);
  } catch (err) {
    // Do not die a horrible death if this is just a syntax error
    logError(err);
  }
}

exports.fileUtil = fu;
exports.plugin = plugin.plugin;

/**
 *
 * @name init
 * @function This function initializes a Lumbar instance
 * @param {string} lumbarFile The lumbarFile is the main
 *  file. Its responsible to define all the platforms,
 *  packages, modules, and templates for Lumbar to use.
 * @param {Object} options supports the following options:
 *   packageConfigFile (string): name of the package config file.
 *   outdir (string): path to directory of where to output the files.
 *   minimize (boolean): Should we minimize the files?
 * @return {Object.<Function>}
 */
exports.init = function(lumbarFile, options) {
    var outdir = options.outdir,
        minimize = options.minimize,
        plugins = plugin.create(options);

    function initPlatforms() {
      config.platformList().forEach(function(platform) {
        if (!platform) {
          return;
        }
        try {
          console.log("mkdir:\t\033[90mmaking directory\033[0m " + outdir + '/' + platform);
          fs.mkdirSync(outdir + '/' + platform, 0755);
        } catch (err) {
          if (err.code !== 'EEXIST') {
            throw err;
          }
        }
      });
    }

    function buildPlatform(context, callback) {
      buildMode('scripts', context, callback);
      buildMode('styles', context, callback);
    }
    function buildMode(mode, context, callback) {
      var modules = config.moduleList(context.package);

      context = context.clone();
      context.mode = mode;

      plugins.generatedFiles(context).forEach(function(fileConfig) {
        var fileContext = context.clone(true);
        fileContext.fileConfig = fileConfig;
        fileContext.resources = [];
        fileContext.fileCache = fileContext.combined ? {} : undefined;

        if (fileContext.combined) {
          var fileName = plugins.fileName(fileContext);
          fileContext.fileName = outdir + '/' + fileName.path + '.' + fileName.extension;
        }

        var returned = 0;
        modules.forEach(function(module) {
          var moduleContext = fileContext.clone();
          moduleContext.module = module;

          buildModule(moduleContext, function(err, data) {
            if (err) {
              return callback(err);
            }

            returned++;
            if (fileContext.combined) {
              if (returned === modules.length) {
                combineResources(fileContext, fileContext.package, function(err, data) {
                  if (data) {
                    data.package = fileContext.package;
                  }
                  callback(err, data);
                });
              }
            } else if (data) {    // No files for a particular module mode
              callback(err, data);
            }
          });
        });
      });
    }
    function buildModule(context, callback) {
      context.module = config.module(context.module);
      context.options = options;
      context.configCache = configCache;
      context.fileCache = context.combined ? context.fileCache : {};
      context.moduleCache = {};

      if (!context.combined) {
        var fileName = plugins.fileName(context);
        context.fileName = outdir + '/' + fileName.path + '.' + fileName.extension;
      }

      config.fileList(plugins, context, function(err, resources) {
        if (err) {
          return callback(err);
        }

        resources = resources.map(function(resource) {
          var resourceContext = context.clone();
          resourceContext.resource = resource;
          return plugins.resource(resourceContext);
        }).filter(function(resource) { return resource; });

        context.moduleResources = resources;
        plugins.module(context);

        if (!context.combined) {
          context.resources = context.moduleResources;
          context.moduleResources = undefined;
          combineResources(context, context.module.name, function(err, data) {
            if (data) {
              data.package = context.package;
              data.module = context.module.name;
            }
            callback(err, data);
          });
        } else {
          context.resources.push.apply(context.resources, context.moduleResources);
          context.moduleResources = undefined;
          callback();
        }
      });
    }
    function combineResources(context, name, callback) {
      var resources = context.resources;
      if (!resources.length) {
        return;
      }

      plugins.file(context);
      combineFile(resources, context, name, callback);

      context.fileCache = undefined;
    }
    function combineFile(list, context, name, callback) {
      combine.combine(
        list,
        context.fileName,
        options.minimize && context.mode === 'scripts',
        context.mode === 'styles',
        function(err, data) {
          if (err) {
            fs.unlink(context.fileName);
          }

        if (data) {
          data.fileConfig = context.fileConfig;
          data.platform = context.platform;
          data.mode = context.mode;
        }
        callback(err, data)
      });
    }

    loadConfig(lumbarFile);
    initPlatforms();

    return {
      use: function(plugin) {
        plugins.use(plugin);
      },
      /**
       *
       * @name build
       * @function This function builds out the package(s).
       * @param {string} packageName the name of the package listed under
       *  'packages' from the lumbarFile passed in during the call to init().
       * @param {Function} callback the node process Function
       */
      build: function(packageName, callback) {
        var packageNames = packageName ? [packageName] : config.packageList();
        packageNames.forEach(function(pkg) {
          var platforms = config.platformList(pkg);
          platforms.forEach(function(platform) {
            buildPlatform(new Context(
              {
                'package': pkg,
                'platform': platform
              },
              config, plugins), callback);
          });
        });
      },
      watch: function(packageName, callback) {
        if (!fs.watch) {
          throw new Error('Watch requires fs.watch, introduced in Node v0.6.0');
        }

        var packages = packageName ? [packageName] : config.packageList(),
            self = this;

        // Watch for changes in the config file
        // WARN : This isn't really safe for multiple executions, this is a concious design decision
        watch.watchFile(lumbarFile, [], function() {
          loadConfig(lumbarFile);

          // Cleanup what we can, breaking things along the way
          watch.unwatchAll();

          // Give it a chance to cleanup before start recreating. Attempting to avoid the too many files error.
          setTimeout(function() {
            initPlatforms();
            self.watch(packageName, callback);
          }, 1000);
        });

        // Watch the individual components
        packages.forEach(function(name) {
          self.build(name, function(err, status) {
            if (err) {
              logError(err);
              return;
            }

            callback(err, status);

            var context = new Context(status, config, plugins),
                input = status.input.map(function(input) { return fu.resolvePath(input.dir || input); });
            watch.watchFile({ virtual: status.fileName }, input, _.debounce(function() {
              if (context.module) {
                buildModule(context.clone(), callback);
              } else {
                buildMode(context.mode, context.clone(), callback);
              }
            }, 500));
          });
        });
      },
      unwatch: function() {
        watch.unwatchAll();
      }
    };
};
<commit_msg>Fix formatting<commit_after>var _ = require('underscore'),
    combine = require('./jsCombine'),
    configLoader = require('./config'),
    Context = require('./context'),
    fs = require('fs'),
    fu = require('./fileUtil'),
    plugin = require('./plugin'),
    watch = require('./watcher.js'),
    growl = require('growl');

function logError(err) {
    // TODO : Move this to the executable
    console.error(err.stack || err.message);
    growl(err.message, { title: 'Lumbar error' });
}

/** @type {Object} */
var config;
/** @type {Object} cache */
var configCache;

/**
 *
 * @name loadConfig
 * @function This function loads a JSON config file.
 * @param {string} path to the JSON file we want to load.
 */
function loadConfig(path) {
  try {
    configCache = {};
    config = configLoader.load(path);
  } catch (err) {
    // Do not die a horrible death if this is just a syntax error
    logError(err);
  }
}

exports.fileUtil = fu;
exports.plugin = plugin.plugin;

/**
 *
 * @name init
 * @function This function initializes a Lumbar instance
 * @param {string} lumbarFile The lumbarFile is the main
 *  file. Its responsible to define all the platforms,
 *  packages, modules, and templates for Lumbar to use.
 * @param {Object} options supports the following options:
 *   packageConfigFile (string): name of the package config file.
 *   outdir (string): path to directory of where to output the files.
 *   minimize (boolean): Should we minimize the files?
 * @return {Object.<Function>}
 */
exports.init = function(lumbarFile, options) {
    var outdir = options.outdir,
        minimize = options.minimize,
        plugins = plugin.create(options);

    function initPlatforms() {
      config.platformList().forEach(function(platform) {
        if (!platform) {
          return;
        }
        try {
          console.log("mkdir:\t\033[90mmaking directory\033[0m " + outdir + '/' + platform);
          fs.mkdirSync(outdir + '/' + platform, 0755);
        } catch (err) {
          if (err.code !== 'EEXIST') {
            throw err;
          }
        }
      });
    }

    function buildPlatform(context, callback) {
      buildMode('scripts', context, callback);
      buildMode('styles', context, callback);
    }
    function buildMode(mode, context, callback) {
      var modules = config.moduleList(context.package);

      context = context.clone();
      context.mode = mode;

      plugins.generatedFiles(context).forEach(function(fileConfig) {
        var fileContext = context.clone(true);
        fileContext.fileConfig = fileConfig;
        fileContext.resources = [];
        fileContext.fileCache = fileContext.combined ? {} : undefined;

        if (fileContext.combined) {
          var fileName = plugins.fileName(fileContext);
          fileContext.fileName = outdir + '/' + fileName.path + '.' + fileName.extension;
        }

        var returned = 0;
        modules.forEach(function(module) {
          var moduleContext = fileContext.clone();
          moduleContext.module = module;

          buildModule(moduleContext, function(err, data) {
            if (err) {
              return callback(err);
            }

            returned++;
            if (fileContext.combined) {
              if (returned === modules.length) {
                combineResources(fileContext, fileContext.package, function(err, data) {
                  if (data) {
                    data.package = fileContext.package;
                  }
                  callback(err, data);
                });
              }
            } else if (data) {    // No files for a particular module mode
              callback(err, data);
            }
          });
        });
      });
    }
    function buildModule(context, callback) {
      context.module = config.module(context.module);
      context.options = options;
      context.configCache = configCache;
      context.fileCache = context.combined ? context.fileCache : {};
      context.moduleCache = {};

      if (!context.combined) {
        var fileName = plugins.fileName(context);
        context.fileName = outdir + '/' + fileName.path + '.' + fileName.extension;
      }

      config.fileList(plugins, context, function(err, resources) {
        if (err) {
          return callback(err);
        }

        resources = resources.map(function(resource) {
          var resourceContext = context.clone();
          resourceContext.resource = resource;
          return plugins.resource(resourceContext);
        }).filter(function(resource) { return resource; });

        context.moduleResources = resources;
        plugins.module(context);

        if (!context.combined) {
          context.resources = context.moduleResources;
          context.moduleResources = undefined;
          combineResources(context, context.module.name, function(err, data) {
            if (data) {
              data.package = context.package;
              data.module = context.module.name;
            }
            callback(err, data);
          });
        } else {
          context.resources.push.apply(context.resources, context.moduleResources);
          context.moduleResources = undefined;
          callback();
        }
      });
    }
    function combineResources(context, name, callback) {
      var resources = context.resources;
      if (!resources.length) {
        return;
      }

      plugins.file(context);
      combineFile(resources, context, name, callback);

      context.fileCache = undefined;
    }
    function combineFile(list, context, name, callback) {
      combine.combine(
        list,
        context.fileName,
        options.minimize && context.mode === 'scripts',
        context.mode === 'styles',
        function(err, data) {
          if (err) {
            fs.unlink(context.fileName);
          }

          if (data) {
            data.fileConfig = context.fileConfig;
            data.platform = context.platform;
            data.mode = context.mode;
          }
          callback(err, data)
        });
    }

    loadConfig(lumbarFile);
    initPlatforms();

    return {
      use: function(plugin) {
        plugins.use(plugin);
      },
      /**
       *
       * @name build
       * @function This function builds out the package(s).
       * @param {string} packageName the name of the package listed under
       *  'packages' from the lumbarFile passed in during the call to init().
       * @param {Function} callback the node process Function
       */
      build: function(packageName, callback) {
        var packageNames = packageName ? [packageName] : config.packageList();
        packageNames.forEach(function(pkg) {
          var platforms = config.platformList(pkg);
          platforms.forEach(function(platform) {
            buildPlatform(new Context(
              {
                'package': pkg,
                'platform': platform
              },
              config, plugins), callback);
          });
        });
      },
      watch: function(packageName, callback) {
        if (!fs.watch) {
          throw new Error('Watch requires fs.watch, introduced in Node v0.6.0');
        }

        var packages = packageName ? [packageName] : config.packageList(),
            self = this;

        // Watch for changes in the config file
        // WARN : This isn't really safe for multiple executions, this is a concious design decision
        watch.watchFile(lumbarFile, [], function() {
          loadConfig(lumbarFile);

          // Cleanup what we can, breaking things along the way
          watch.unwatchAll();

          // Give it a chance to cleanup before start recreating. Attempting to avoid the too many files error.
          setTimeout(function() {
            initPlatforms();
            self.watch(packageName, callback);
          }, 1000);
        });

        // Watch the individual components
        packages.forEach(function(name) {
          self.build(name, function(err, status) {
            if (err) {
              logError(err);
              return;
            }

            callback(err, status);

            var context = new Context(status, config, plugins),
                input = status.input.map(function(input) { return fu.resolvePath(input.dir || input); });
            watch.watchFile({ virtual: status.fileName }, input, _.debounce(function() {
              if (context.module) {
                buildModule(context.clone(), callback);
              } else {
                buildMode(context.mode, context.clone(), callback);
              }
            }, 500));
          });
        });
      },
      unwatch: function() {
        watch.unwatchAll();
      }
    };
};
<|endoftext|>


<commit_before>'use strict';

/* global $, _, angular, uuid, moment, marked, ace, hljs, CartUtility, CartConst */
module.exports = function($scope, $location, $anchorScroll, $routeParams, $dataService, FileUploader) {
    CartUtility.log('CartBlogEditCtrl');

    var postId = $routeParams.postId;

    $scope.postCategories = $dataService.categoryGetAll();

    $scope.postCategory = null;
    $scope.postTags = [];
    $scope.postAttachments = [];

    var postBaseUrl = CartUtility.getPureAbsUrlFromLocation($location);
    var editElement = $('#markdown-edit');
    var previewElement = $('#markdown-preview');
    var titleElement = $('#markdown-post-title');
    var categoryElement = $('#markdown-post-category');
    var tagsIdentify = '#markdown-post-tags > span';
    var tocIconElement = $('.markdown-toc-icon');
    var tocContentElement = $('.markdown-toc-content');
    var tocFirstLinksIdentify = '.markdown-toc-content > ol > li > a';
    var topBtnElement = $('.page-to-top');
    var spinnerElement = $('#markdown-loading');
    var spinnerNameElement = $('.spinner-markdown-name');

    //-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-
    //-* LOGIC
    //-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-
    $scope.toggleToc = function() {
        CartUtility.toggleToc(tocIconElement, tocContentElement);
    };

    // category related
    $scope.categoryInput = '';
    $scope.selectPostCategory = function(categoryUuid) {
        var category = $dataService.categorySearchById(categoryUuid);

        if (!_.isNull(category)) {
            $scope.postCategory = category;
            $scope.categoryInput = category.title;
        }

        CartUtility.focusEditor(aceEditor);
    };

    // tag related
    $scope.tagInput = '';
    $scope.addPostTag = function(event) {
        event.preventDefault();
        var inputName = $scope.tagInput;
        if (_.isEmpty(inputName)) {
            return; // empty input, ignore it
        }

        // search current post tags
        var currentPostTagsFound = false;
        if (!_.isEmpty($scope.postTags)) {
            _.forEach($scope.postTags, function(tag) {
                if (tag.title === inputName) {
                    currentPostTagsFound = true;
                }
            });
        }
        if (currentPostTagsFound !== false) {
            CartUtility.notify('Warning!', 'Tag with name "' + inputName + '" already exists in this post!', 'notice');
            return; // same tag already exists in post
        }

        var tag = $dataService.tagSearch(inputName);
        if (!_.isEmpty(tag)) {
            // tag already exists
            tag = tag.pop(); // [searchedTag].pop()
            $scope.postTags[tag.uuid] = tag;
        } else {
            // tag not found, create it
            $dataService.tagCreate(inputName).then(function(data) { // data is tag json
                $scope.postTags[data.uuid] = data;
            }, function() {
                CartUtility.notify('Error!', 'Error in creating new tag: ' + inputName, 'error');
            });
        }

        $scope.tagInput = '';
        CartUtility.focusEditor(aceEditor);
    };
    $scope.removePostTag = function(tagUuid) {
        if ($scope.postTags.hasOwnProperty(tagUuid)) {
            delete $scope.postTags[tagUuid];
        }

        CartUtility.focusEditor(aceEditor);
    };

    $scope.$on('$routeChangeStart', function() {
        CartUtility.log('CartBlogNewCtrl $routeChangeStart entered!');
        // anchor link logic
        var prevHash = $location.hash();
        if (_.isString(prevHash) && !_.isEmpty(prevHash)) {
            $location.hash(CartUtility.escapeAnchorName(prevHash));
            $anchorScroll();
            $location.hash(prevHash);
        }
    });

    // post data search logic
    $dataService.postGetTmp($routeParams.postId).then(function(tmpPostData) {
        if (tmpPostData === false) {
            // tmp post data not found
            // FIXME
            // search local post data, found, fetch post data from server, display it
            // not found, redirect to error page
        } else {
            // tmp post data found
            aceEditor.setValue(tmpPostData.md, -1);
            $scope.postCategory = tmpPostData.category;
            $scope.categoryInput = tmpPostData.category.title;
            $scope.postTags = tmpPostData.tags;
            $scope.postAttachments = tmpPostData.attachments;
        }
    });

    //-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-
    //-* EDITOR
    //-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-
    // markd
    var renderer = new marked.Renderer();
    renderer.heading = function(text, level) { // add anchor link
        return '' +
            '<h' + level + '>' +
                '' +
                text +
            '</h' + level + '>';
    };
    marked.setOptions({
        renderer: renderer,
        gfm: true,
        tables: true,
        breaks: true,
        pedantic: false,
        sanitize: true,
        smartLists: true,
        smartypants: false,
        highlight: function (code, lang) {
            // since the highlight class name added with highlight lib itself not working, have to wrap code tag by self
            return '' + hljs.highlightAuto(code, [lang]).value + '';
        }
    });

    var aceEditor = ace.edit(editElement.attr('id'));

    aceEditor.setOption("showPrintMargin", false);
    aceEditor.setTheme('ace/theme/earthsong');
    aceEditor.setHighlightActiveLine(true);
    aceEditor.setBehavioursEnabled(true);

    aceEditor.getSession().setMode('ace/mode/markdown');
    aceEditor.getSession().on('change', function() {
        previewMd();
    });

    aceEditor.session.setUseWrapMode(true);
    aceEditor.session.setNewLineMode("unix");
    aceEditor.session.setScrollTop(false);
    aceEditor.renderer.setShowGutter(true);
    aceEditor.renderer.setPrintMarginColumn(true);
    aceEditor.renderer.setPadding(15);
    aceEditor.renderer.setScrollMargin(20);
    aceEditor.resize(true);

    var collectPostData = function() {
        var title = titleElement.text().trim();

        var category = $dataService.categorySearch(categoryElement.text().trim());
        if (_.isEmpty(category)) {
            category = {};
        } else {
            category = category.pop(); // [searchedCategory].pop()
            category = $dataService.categoryUpdateTime(category);
        }

        var tags = {};
        var tagNames = [];
        var tagElements = $(tagsIdentify);
        if (tagElements.length > 0) {
            _.forEach(tagElements, function(element) {
                tagNames.push($(element).text().trim());
            });
        }
        if (tagNames.length > 0) {
            _.forEach(tagNames, function(tagName) {
                var tag = $dataService.tagSearch(tagName);
                if (!_.isEmpty(tag)) {
                    tag = tag.pop(); // [searchedTag].pop()
                    tag = $dataService.tagUpdateTime(tag);
                    tags[tag.uuid] = tag;
                }
            });
        }

        return {
            "title": title,
            "category": category,
            "tags": tags,
            "attachments": $scope.postAttachments
        };
    };

    var previewMd = function() {
        var content = aceEditor.getSession().getValue();
        var converted = marked(content);

        previewElement.html(converted);

        var editHeight = parseInt(editElement.css('height'));
        var previewHeight = parseInt(previewElement.css('height'));
        if (editHeight > previewHeight) {
            previewElement.css('height', editHeight);
            aceEditor.resize();
        } else if (editHeight < previewHeight) {
            editElement.css('height', previewHeight);
            aceEditor.resize();
        }

        var toc = CartUtility.buildToc(converted, postBaseUrl);
        if (toc !== '') { // find header links
            // apply toc html
            tocContentElement.html(toc);
            // apply post title
            titleElement.text($($(tocFirstLinksIdentify)[0]).text());
        }
    };

    //-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-
    //-* EDITOR COMMANDS
    //-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-
    aceEditor.commands.addCommand({
        "name": "cmdSaveTmp",
        "bindKey": {"win": "Ctrl-S", "mac": "Command-S"},
        "exec": function(editor) {
            CartUtility.log('ACE Ctrl-S triggered: Save post tmp.');

            var postData = collectPostData(editor);

            $dataService.postSaveTmp(
                postId, postData.title,
                editor.getSession().getValue(),
                postData.category, postData.tags, postData.attachments
            ).then(function() {
                CartUtility.notify('Done!', 'Tmp post saved!', 'success');
            });
        }
    });
    aceEditor.commands.addCommand({
        "name": "cmdUpload",
        "bindKey": {"win": "Ctrl-U", "mac": "Command-U"},
        "exec": function(editor) {
            CartUtility.log('ACE Ctrl-U triggered: Upload post.');

            var postData = collectPostData(editor);
        }
    });
    aceEditor.commands.addCommand({
        "name": "cmdDisplayIndex",
        "bindKey": {"win": "Ctrl-I", "mac": "Command-I"},
        "exec": function() {
            CartUtility.log('ACE Ctrl-I triggered: Display post index.');
            CartUtility.toggleToc(tocIconElement, tocContentElement);
        }
    });
    aceEditor.commands.addCommand({
        "name": "cmdGotoTop",
        "bindKey": {"win": "Ctrl-H", "mac": "Command-H"},
        "exec": function() {
            CartUtility.log('ACE Ctrl-H triggered: Goto page top.');
            topBtnElement.click();
        }
    });

    //-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-
    //-* EDITOR FILE UPLOAD
    //-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-
    /* HOT FIX for FileUploader.isUploading status */
    FileUploader.prototype._onCompleteItem = function(item, response, status, headers) {
        var nextItem = this.getReadyItems()[0];
        this.isUploading = !!nextItem;

        item._onComplete(response, status, headers);
        this.onCompleteItem(item, response, status, headers);

        if (angular.isDefined(nextItem)) {
            nextItem.upload();
            return;
        }

        this.onCompleteAll();
        this.progress = this._getTotalProgress();
        this._render();
    };
    /**
     * File {
     *     FilelastModified: 1414117158000,
     *     lastModifiedDate: Fri Oct 24 2014 10:19:18 GMT+0800 (CST),
     *     name: "1697525.jpeg",
     *     size: 9303,
     *     type: "image/jpeg"
     * }
     * FileLikeObject {
     *     lastModifiedDate: Mon Oct 27 2014 12:50:24 GMT+0800 (CST),
     *     size: 269534,
     *     type: "image/png",
     *     name: "6f5ef307gw1elpmcbs6k1j20db0dwmz3.png"
     * }
     */
    var uploader = new FileUploader({
        url: '/api/upload',
        removeAfterUpload: true
    });
    uploader.filters.push({
        name: 'fileTypeFilter',
        fn: function(file) {
            return CartConst.UPLOAD_TYPES.indexOf(file.type) !== -1;
        }
    });
    var uploaderProcessNextUpload = function() {
        if (uploader.queue.length > 0 && !uploader.isUploading) {
            var fileItem = uploader.queue[0];
            uploaderReportUploadProgress(fileItem.file.name, 0);
            fileItem.formData = [{ "postId": postId }];
            fileItem.upload();
        } else if (uploader.queue.length === 0) {
            // no any files to be uploaded, try to hide spinner
            uploaderHideSpinner();
        }
    };
    var uploaderReportUploadProgress = function(name, progress) {
        spinnerNameElement.text('Uploading "' + name + '": ' + progress + '% ...');
    };
    var uploaderHideSpinner = function() {
        if (uploader.queue.length === 0 && !uploader.isUploading) {
            spinnerElement.hide();
            spinnerNameElement.text('');
        }
    };
    uploader.onWhenAddingFileFailed = function(file) {
        CartUtility.notify('Error!', 'File type of "' + file.name + '" is not allowed: ' + file.type, 'error');
        uploaderHideSpinner();
    };
    uploader.onProgressItem = function(fileItem, progress) {
        uploaderReportUploadProgress(fileItem.file.name, progress);
    };
    uploader.onErrorItem = function(fileItem) {
        CartUtility.notify('Upload Error!', 'Error encountered while uploading file "' + fileItem.file.name + '"!', 'error');
        uploaderHideSpinner();
    };
    uploader.onCompleteItem = function(fileItem, response) {
        if (CartUtility.handleResponse(response)) {
            CartUtility.notify('Upload Done!', 'File "' + fileItem.file.name + '" uploaded!', 'success');
        }
        $scope.postAttachments.push({
            created: moment().unix(),
            driveId: null,
            title: fileItem.file.name,
            updated: moment().unix(),
            uuid: uuid.v4()
        });
        uploaderProcessNextUpload();
    };
    if (CartUtility.isDndSupported()) {
        // intercept document body drag & drop event, prevent document drop page redirect
        $(document).on('dragenter', function(event) {
            event.stopPropagation();
            event.preventDefault();
        }).on('dragover', function(event) {
            event.stopPropagation();
            event.preventDefault();
        }).on('drop', function(event) {
            event.stopPropagation();
            event.preventDefault();
        });
        // bind real drop event
        var aceContent = $('.ace_content');
        aceContent.on('dragenter', function(event) {
            // display drag over view effect
            event.stopPropagation();
            event.preventDefault();
            aceContent.addClass('ace_drag_over');
        }).on('dragleave', function(event) {
            // hide drag over view effect
            event.stopPropagation();
            event.preventDefault();
            aceContent.removeClass('ace_drag_over');
        }).on('dragover', function(event) {
            // intercept the dragover event to enable drop event
            event.stopPropagation();
            event.preventDefault();
        }).on('drop', function(event) {
            // drop to upload
            event.stopPropagation();
            event.preventDefault();
            // styles
            aceContent.removeClass('ace_drag_over');
            spinnerElement.show();
            // upload
            var files = event.originalEvent.dataTransfer.files;
            uploader.addToQueue(files);
            uploaderProcessNextUpload();
        });
    } else {
        CartUtility.notify(
            'Warning!',
            'Drag & Drop file upload functionality is not supported in this browser. \n' +
            'You cannot upload file with this browser! \n' +
            'Please use modern browsers like Chrome.',
            'notice',
            100000 // 10s
        );
    }
};<commit_msg>Update comments.<commit_after>'use strict';

/* global $, _, angular, uuid, moment, marked, ace, hljs, CartUtility, CartConst */
module.exports = function($scope, $location, $anchorScroll, $routeParams, $dataService, FileUploader) {
    CartUtility.log('CartBlogEditCtrl');

    var postId = $routeParams.postId;

    $scope.postCategories = $dataService.categoryGetAll();

    $scope.postCategory = null;
    $scope.postTags = [];
    $scope.postAttachments = [];

    var postBaseUrl = CartUtility.getPureAbsUrlFromLocation($location);
    var editElement = $('#markdown-edit');
    var previewElement = $('#markdown-preview');
    var titleElement = $('#markdown-post-title');
    var categoryElement = $('#markdown-post-category');
    var tagsIdentify = '#markdown-post-tags > span';
    var tocIconElement = $('.markdown-toc-icon');
    var tocContentElement = $('.markdown-toc-content');
    var tocFirstLinksIdentify = '.markdown-toc-content > ol > li > a';
    var topBtnElement = $('.page-to-top');
    var spinnerElement = $('#markdown-loading');
    var spinnerNameElement = $('.spinner-markdown-name');

    //-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-
    //-* LOGIC
    //-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-
    $scope.toggleToc = function() {
        CartUtility.toggleToc(tocIconElement, tocContentElement);
    };

    // category related
    $scope.categoryInput = '';
    $scope.selectPostCategory = function(categoryUuid) {
        var category = $dataService.categorySearchById(categoryUuid);

        if (!_.isNull(category)) {
            $scope.postCategory = category;
            $scope.categoryInput = category.title;
        }

        CartUtility.focusEditor(aceEditor);
    };

    // tag related
    // FIXME tag input auto complete
    $scope.tagInput = '';
    $scope.addPostTag = function(event) {
        event.preventDefault();
        var inputName = $scope.tagInput;
        if (_.isEmpty(inputName)) {
            return; // empty input, ignore it
        }

        // search current post tags
        var currentPostTagsFound = false;
        if (!_.isEmpty($scope.postTags)) {
            _.forEach($scope.postTags, function(tag) {
                if (tag.title === inputName) {
                    currentPostTagsFound = true;
                }
            });
        }
        if (currentPostTagsFound !== false) {
            CartUtility.notify('Warning!', 'Tag with name "' + inputName + '" already exists in this post!', 'notice');
            return; // same tag already exists in post
        }

        var tag = $dataService.tagSearch(inputName);
        if (!_.isEmpty(tag)) {
            // tag already exists
            tag = tag.pop(); // [searchedTag].pop()
            $scope.postTags[tag.uuid] = tag;
        } else {
            // tag not found, create it
            $dataService.tagCreate(inputName).then(function(data) { // data is tag json
                $scope.postTags[data.uuid] = data;
            }, function() {
                CartUtility.notify('Error!', 'Error in creating new tag: ' + inputName, 'error');
            });
        }

        $scope.tagInput = '';
        CartUtility.focusEditor(aceEditor);
    };
    $scope.removePostTag = function(tagUuid) {
        if ($scope.postTags.hasOwnProperty(tagUuid)) {
            delete $scope.postTags[tagUuid];
        }

        CartUtility.focusEditor(aceEditor);
    };

    $scope.$on('$routeChangeStart', function() {
        CartUtility.log('CartBlogNewCtrl $routeChangeStart entered!');
        // anchor link logic
        var prevHash = $location.hash();
        if (_.isString(prevHash) && !_.isEmpty(prevHash)) {
            $location.hash(CartUtility.escapeAnchorName(prevHash));
            $anchorScroll();
            $location.hash(prevHash);
        }
    });

    // post data search logic
    $dataService.postGetTmp($routeParams.postId).then(function(tmpPostData) {
        if (tmpPostData === false) {
            // tmp post data not found
            // FIXME
            // search local post data, found, fetch post data from server, display it
            // not found, redirect to error page
        } else {
            // tmp post data found
            aceEditor.setValue(tmpPostData.md, -1);
            $scope.postCategory = tmpPostData.category;
            $scope.categoryInput = tmpPostData.category.title;
            $scope.postTags = tmpPostData.tags;
            $scope.postAttachments = tmpPostData.attachments;
        }
    });

    //-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-
    //-* EDITOR
    //-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-
    // markd
    var renderer = new marked.Renderer();
    renderer.heading = function(text, level) { // add anchor link
        return '' +
            '<h' + level + '>' +
                '' +
                text +
            '</h' + level + '>';
    };
    marked.setOptions({
        renderer: renderer,
        gfm: true,
        tables: true,
        breaks: true,
        pedantic: false,
        sanitize: true,
        smartLists: true,
        smartypants: false,
        highlight: function (code, lang) {
            // since the highlight class name added with highlight lib itself not working, have to wrap code tag by self
            return '' + hljs.highlightAuto(code, [lang]).value + '';
        }
    });

    var aceEditor = ace.edit(editElement.attr('id'));

    aceEditor.setOption("showPrintMargin", false);
    aceEditor.setTheme('ace/theme/earthsong');
    aceEditor.setHighlightActiveLine(true);
    aceEditor.setBehavioursEnabled(true);

    aceEditor.getSession().setMode('ace/mode/markdown');
    aceEditor.getSession().on('change', function() {
        previewMd();
    });

    aceEditor.session.setUseWrapMode(true);
    aceEditor.session.setNewLineMode("unix");
    aceEditor.session.setScrollTop(false);
    aceEditor.renderer.setShowGutter(true);
    aceEditor.renderer.setPrintMarginColumn(true);
    aceEditor.renderer.setPadding(15);
    aceEditor.renderer.setScrollMargin(20);
    aceEditor.resize(true);

    var collectPostData = function() {
        var title = titleElement.text().trim();

        var category = $dataService.categorySearch(categoryElement.text().trim());
        if (_.isEmpty(category)) {
            category = {};
        } else {
            category = category.pop(); // [searchedCategory].pop()
            category = $dataService.categoryUpdateTime(category);
        }

        var tags = {};
        var tagNames = [];
        var tagElements = $(tagsIdentify);
        if (tagElements.length > 0) {
            _.forEach(tagElements, function(element) {
                tagNames.push($(element).text().trim());
            });
        }
        if (tagNames.length > 0) {
            _.forEach(tagNames, function(tagName) {
                var tag = $dataService.tagSearch(tagName);
                if (!_.isEmpty(tag)) {
                    tag = tag.pop(); // [searchedTag].pop()
                    tag = $dataService.tagUpdateTime(tag);
                    tags[tag.uuid] = tag;
                }
            });
        }

        return {
            "title": title,
            "category": category,
            "tags": tags,
            "attachments": $scope.postAttachments
        };
    };

    var previewMd = function() {
        var content = aceEditor.getSession().getValue();
        var converted = marked(content);

        previewElement.html(converted);

        var editHeight = parseInt(editElement.css('height'));
        var previewHeight = parseInt(previewElement.css('height'));
        if (editHeight > previewHeight) {
            previewElement.css('height', editHeight);
            aceEditor.resize();
        } else if (editHeight < previewHeight) {
            editElement.css('height', previewHeight);
            aceEditor.resize();
        }

        var toc = CartUtility.buildToc(converted, postBaseUrl);
        if (toc !== '') { // find header links
            // apply toc html
            tocContentElement.html(toc);
            // apply post title
            titleElement.text($($(tocFirstLinksIdentify)[0]).text());
        }
    };

    //-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-
    //-* EDITOR COMMANDS
    //-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-
    aceEditor.commands.addCommand({
        "name": "cmdSaveTmp",
        "bindKey": {"win": "Ctrl-S", "mac": "Command-S"},
        "exec": function(editor) {
            CartUtility.log('ACE Ctrl-S triggered: Save post tmp.');

            var postData = collectPostData(editor);

            $dataService.postSaveTmp(
                postId, postData.title,
                editor.getSession().getValue(),
                postData.category, postData.tags, postData.attachments
            ).then(function() {
                CartUtility.notify('Done!', 'Tmp post saved!', 'success');
            });
        }
    });
    aceEditor.commands.addCommand({
        "name": "cmdUpload",
        "bindKey": {"win": "Ctrl-U", "mac": "Command-U"},
        "exec": function(editor) {
            CartUtility.log('ACE Ctrl-U triggered: Upload post.');

            var postData = collectPostData(editor);
        }
    });
    aceEditor.commands.addCommand({
        "name": "cmdDisplayIndex",
        "bindKey": {"win": "Ctrl-I", "mac": "Command-I"},
        "exec": function() {
            CartUtility.log('ACE Ctrl-I triggered: Display post index.');
            CartUtility.toggleToc(tocIconElement, tocContentElement);
        }
    });
    aceEditor.commands.addCommand({
        "name": "cmdGotoTop",
        "bindKey": {"win": "Ctrl-H", "mac": "Command-H"},
        "exec": function() {
            CartUtility.log('ACE Ctrl-H triggered: Goto page top.');
            topBtnElement.click();
        }
    });

    //-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-
    //-* EDITOR FILE UPLOAD
    //-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-
    /* HOT FIX for FileUploader.isUploading status */
    FileUploader.prototype._onCompleteItem = function(item, response, status, headers) {
        var nextItem = this.getReadyItems()[0];
        this.isUploading = !!nextItem;

        item._onComplete(response, status, headers);
        this.onCompleteItem(item, response, status, headers);

        if (angular.isDefined(nextItem)) {
            nextItem.upload();
            return;
        }

        this.onCompleteAll();
        this.progress = this._getTotalProgress();
        this._render();
    };
    /**
     * File {
     *     FilelastModified: 1414117158000,
     *     lastModifiedDate: Fri Oct 24 2014 10:19:18 GMT+0800 (CST),
     *     name: "1697525.jpeg",
     *     size: 9303,
     *     type: "image/jpeg"
     * }
     * FileLikeObject {
     *     lastModifiedDate: Mon Oct 27 2014 12:50:24 GMT+0800 (CST),
     *     size: 269534,
     *     type: "image/png",
     *     name: "6f5ef307gw1elpmcbs6k1j20db0dwmz3.png"
     * }
     */
    var uploader = new FileUploader({
        url: '/api/upload',
        removeAfterUpload: true
    });
    uploader.filters.push({
        name: 'fileTypeFilter',
        fn: function(file) {
            return CartConst.UPLOAD_TYPES.indexOf(file.type) !== -1;
        }
    });
    var uploaderProcessNextUpload = function() {
        if (uploader.queue.length > 0 && !uploader.isUploading) {
            var fileItem = uploader.queue[0];
            uploaderReportUploadProgress(fileItem.file.name, 0);
            fileItem.formData = [{ "postId": postId }];
            fileItem.upload();
        } else if (uploader.queue.length === 0) {
            // no any files to be uploaded, try to hide spinner
            uploaderHideSpinner();
        }
    };
    var uploaderReportUploadProgress = function(name, progress) {
        spinnerNameElement.text('Uploading "' + name + '": ' + progress + '% ...');
    };
    var uploaderHideSpinner = function() {
        if (uploader.queue.length === 0 && !uploader.isUploading) {
            spinnerElement.hide();
            spinnerNameElement.text('');
        }
    };
    uploader.onWhenAddingFileFailed = function(file) {
        CartUtility.notify('Error!', 'File type of "' + file.name + '" is not allowed: ' + file.type, 'error');
        uploaderHideSpinner();
    };
    uploader.onProgressItem = function(fileItem, progress) {
        uploaderReportUploadProgress(fileItem.file.name, progress);
    };
    uploader.onErrorItem = function(fileItem) {
        CartUtility.notify('Upload Error!', 'Error encountered while uploading file "' + fileItem.file.name + '"!', 'error');
        uploaderHideSpinner();
    };
    uploader.onCompleteItem = function(fileItem, response) {
        if (CartUtility.handleResponse(response)) {
            CartUtility.notify('Upload Done!', 'File "' + fileItem.file.name + '" uploaded!', 'success');
        }
        $scope.postAttachments.push({
            created: moment().unix(),
            driveId: null,
            title: fileItem.file.name,
            updated: moment().unix(),
            uuid: uuid.v4()
        });
        uploaderProcessNextUpload();
    };
    if (CartUtility.isDndSupported()) {
        // intercept document body drag & drop event, prevent document drop page redirect
        $(document).on('dragenter', function(event) {
            event.stopPropagation();
            event.preventDefault();
        }).on('dragover', function(event) {
            event.stopPropagation();
            event.preventDefault();
        }).on('drop', function(event) {
            event.stopPropagation();
            event.preventDefault();
        });
        // bind real drop event
        var aceContent = $('.ace_content');
        aceContent.on('dragenter', function(event) {
            // display drag over view effect
            event.stopPropagation();
            event.preventDefault();
            aceContent.addClass('ace_drag_over');
        }).on('dragleave', function(event) {
            // hide drag over view effect
            event.stopPropagation();
            event.preventDefault();
            aceContent.removeClass('ace_drag_over');
        }).on('dragover', function(event) {
            // intercept the dragover event to enable drop event
            event.stopPropagation();
            event.preventDefault();
        }).on('drop', function(event) {
            // drop to upload
            event.stopPropagation();
            event.preventDefault();
            // styles
            aceContent.removeClass('ace_drag_over');
            spinnerElement.show();
            // upload
            var files = event.originalEvent.dataTransfer.files;
            uploader.addToQueue(files);
            uploaderProcessNextUpload();
        });
    } else {
        CartUtility.notify(
            'Warning!',
            'Drag & Drop file upload functionality is not supported in this browser. \n' +
            'You cannot upload file with this browser! \n' +
            'Please use modern browsers like Chrome.',
            'notice',
            100000 // 10s
        );
    }
};<|endoftext|>


<commit_before>var fs = require('fs'),
  path = require('path'),
  filepath = path.resolve(__dirname, '../../webpack-stats.json');

module.exports = function writeStats(stats, env) {

  var publicPath = this.options.output.publicPath;

  var json = stats.toJson();

  // get chunks by name and extensions
  function getChunks(name, ext) {
    ext = ext || 'js';
    var chunk = json.assetsByChunkName[name];

    // a chunk could be a string or an array, so make sure it is an array
    if (!(Array.isArray(chunk))) {
      chunk = [chunk];
    }

    return chunk
      // filter by extension
      .filter(function (chunkName) {
        return path.extname(chunkName) === '.' + ext;
      })
      .map(function (chunkName) {
        return publicPath + chunkName;
      });
  }

  var script = getChunks('main', 'js');
  var cssFiles = getChunks('main', 'css');

  var cssModules = {};

  // Is there a way to get this dynamically so it doesn't depend on loader?
  var namePrefix = './~/css-loader?modules&importLoaders=2&localIdentName=[local]___[hash:base64:5]!./~/autoprefixer-loader?browsers=last 2 version!./~/sass-loader?outputStyle=expanded&sourceMap=true&sourceMapContents=true!';

  json.modules.filter(function(m) {
    if (env === 'prod') {
      return /\.scss$/.test(m.name);
    }

    return m.name.slice(0, namePrefix.length) === namePrefix;
  }).forEach(function(m) {
    var name = path.resolve(__dirname, '../../', env === 'prod' ?
      m.name.slice('./src'.length) :
      m.name.slice(namePrefix.length + './src'.length));
    //Resolve the e.g.:"C:\"  issue on windows
    if(name) {
      const i = name.indexOf(":");
      name = name.substring(i > -1 ? i + 1 : 0, name.length + 1);
    }
    //end
    var regex = env === 'prod' ? /module\.exports = ((.|\n)+);/ : /exports\.locals = ((.|\n)+);/;
    var match = m.source.match(regex);
    cssModules[name] = match ? JSON.parse(match[1]) : {};
  });

  var content = {
    script: script,
    css: {
      files: cssFiles,
      modules: cssModules
    }
  };

  fs.writeFileSync(filepath, JSON.stringify(content));

};
<commit_msg>Find compiled images in modules of web pack stats memory<commit_after>var fs = require('fs'),
  path = require('path'),
  filepath = path.resolve(__dirname, '../../webpack-stats.json');

module.exports = function writeStats(stats, env) {

  var publicPath = this.options.output.publicPath;

  var json = stats.toJson();

  // get chunks by name and extensions
  function getChunks(name, ext) {
    ext = ext || 'js';
    var chunk = json.assetsByChunkName[name];

    // a chunk could be a string or an array, so make sure it is an array
    if (!(Array.isArray(chunk))) {
      chunk = [chunk];
    }

    return chunk
      // filter by extension
      .filter(function (chunkName) {
        return path.extname(chunkName) === '.' + ext;
      })
      .map(function (chunkName) {
        return publicPath + chunkName;
      });
  }

  var script = getChunks('main', 'js');
  var cssFiles = getChunks('main', 'css');

  var cssModules = {};

  // Is there a way to get this dynamically so it doesn't depend on loader?
  var namePrefix = './~/css-loader?modules&importLoaders=2&localIdentName=[local]___[hash:base64:5]!./~/autoprefixer-loader?browsers=last 2 version!./~/sass-loader?outputStyle=expanded&sourceMap=true&sourceMapContents=true!';

  json.modules.filter(function(m) {
    if (env === 'prod') {
      return /\.scss$/.test(m.name);
    }

    return m.name.slice(0, namePrefix.length) === namePrefix;
  }).forEach(function(m) {
    var name = path.resolve(__dirname, '../../', env === 'prod' ?
      m.name.slice('./src'.length) :
      m.name.slice(namePrefix.length + './src'.length));
    //Resolve the e.g.:"C:\"  issue on windows
    if(name) {
      const i = name.indexOf(":");
      name = name.substring(i > -1 ? i + 1 : 0, name.length + 1);
    }
    //end
    var regex = env === 'prod' ? /module\.exports = ((.|\n)+);/ : /exports\.locals = ((.|\n)+);/;
    var match = m.source.match(regex);
    cssModules[name] = match ? JSON.parse(match[1]) : {};
  });

  // Find compiled images in modules
  // it will be used to map original filename to the compiled one
  // for server side rendering
  const imagesRegex = /\.(jpe?g|png|gif|svg)$/;
  const images = json.modules
    .filter(function(module) {
      return imagesRegex.test(module.name);
    })
    .map(function(image) {
      return {
        original: image.name,
        compiled: `${publicPath}${image.assets[0]}`
      };
    });

  var content = {
    script: script,
    css: {
      files: cssFiles,
      modules: cssModules
    },
    images: images
  };

  fs.writeFileSync(filepath, JSON.stringify(content));

};
<|endoftext|>


<commit_before>var test = require('tap').test,
    generators = require('../');

test('Create ladder', function(t) {
  var graph = generators.ladder(3);
  // Ladder graph looks like this:
  // *--*--*
  // |  |  |
  // *--*--*
  t.equal(graph.getNodesCount(), 6, "Unexpected number of nodes for ladder graph");
  t.equal(graph.getLinksCount(), 7, "Unexpected number of links for ladder graph");
  t.end();
});

test('Create path', function(t) {
  var size = 5;
  var graph = generators.path(size);
  // Path is simple:
  // *--*--*--*--*
  t.equal(graph.getNodesCount(), size, "Unexpected number of nodes for path graph");
  t.equal(graph.getLinksCount(), (size - 1), "Unexpected number of links for path graph");
  t.end();
});

test('Create complete', function(t) {
  var size = 5;
  var graph = generators.complete(size);
  // Complete graph has all nodes connected with each other.
  t.equal(graph.getNodesCount(), size, "Unexpected number of nodes for complete graph");
  t.equal(graph.getLinksCount(), (size * (size - 1)), "Unexpected number of links for complete graph");
  t.end();
});

test('Create complete', function(t) {
  // see http://en.wikipedia.org/wiki/Complete_bipartite_graph
  var m = 3, n = 4;
  var graph = generators.completeBipartite(m, n);

  t.equal(graph.getNodesCount(), m + n, "Unexpected number of nodes for complete bipartite graph");
  t.equal(graph.getLinksCount(), (m * n), "Unexpected number of links for complete bipartite graph");
  t.end();
});

test('Circular ladder', function(t) {
  var size = 3;
  var graph = generators.circularLadder(size);
  // cricular ladder is like a ladder, but last two and first two
  // nodes are connected
  // ┌-----┐
  // *--*--*
  // |  |  |
  // *--*--*
  // └-----┘
  t.equal(graph.getNodesCount(), 6, "Unexpected number of nodes for circular ladder graph");
  t.equal(graph.getLinksCount(), 9, "Unexpected number of links for circular ladder graph");
  t.end();
});

test('Create grid', function(t) {
  // grid is a grid. This is 3 x 4 grid:
  // *--*--*--*
  // |  |  |  |
  // *--*--*--*
  // |  |  |  |
  // *--*--*--*
  t.test('3x4 grid', function (t) {
    var graph = generators.grid(3, 4);
    t.equal(graph.getNodesCount(), 12, "Unexpected number of nodes for grid graph");
    t.equal(graph.getLinksCount(), 17, "Unexpected number of links for grid graph");
    t.end();
  });

  t.test('1x1 grid', function (t) {
    var graph = generators.grid(1, 1);
    t.equal(graph.getNodesCount(), 1, "Unexpected number of nodes for 1x1 grid graph");
    t.equal(graph.getLinksCount(), 0, "Unexpected number of links for 1x1 grid graph");
    t.end();
  });

  t.test('1x2 grid', function (t) {
    // this is a path of two nodes *--*
    var graph = generators.grid(1, 2);
    t.equal(graph.getNodesCount(), 2, "Unexpected number of nodes for 1x2 grid graph");
    t.equal(graph.getLinksCount(), 1, "Unexpected number of links for 1x2 grid graph");
    t.end();
  });
});

test('Create balanced binary tree', function(t) {
  // This is a binary tree graph, of height 2:
  //      *
  //   *     *
  // *   * *   *
  var graph = generators.balancedBinTree(2);
  t.equal(graph.getNodesCount(), 7, "Unexpected number of nodes for balanced binary tree");
  t.equal(graph.getLinksCount(), 6, "Unexpected number of links for balanced binary tree");
  t.end();
});

test('Create no links', function(t) {
  // no links graph has no links.
  var graph = generators.noLinks(42);
  t.equal(graph.getNodesCount(), 42, "Unexpected number of nodes in noLinks graph");
  t.equal(graph.getLinksCount(), 0, "Unexpected number of links in noLinks graph");
  t.end();
});

test('Create grid3d', function(t) {
  // grid is a grid. This is 3 x 4 x 2 grid:
  //
  //   *---*--*--*
  //  /  /  /  / |
  // *--*--*--*  *
  // |  |  |  | /|
  // *--*--*--*  *
  // |  |  |  | /
  // *--*--*--*
  //
  t.test('3x4x2 grid', function (t) {
    var graph = generators.grid3(3, 4, 2);
    t.equal(graph.getNodesCount(), 24, "Unexpected number of nodes for grid3 graph");
    t.equal(graph.getLinksCount(), 46, "Unexpected number of links for grid3 graph");
    t.end();
  });

  t.test('1x1x1 grid', function (t) {
    var graph = generators.grid3(1, 1, 1);
    t.equal(graph.getNodesCount(), 1, "Unexpected number of nodes for 1x1x1 grid3 graph");
    t.equal(graph.getLinksCount(), 0, "Unexpected number of links for 1x1x1 grid3 graph");
    t.end();
  });

  t.test('1x2 grid', function (t) {
    // this is a path of two nodes *--*
    var graph = generators.grid3(1, 2, 1);
    t.equal(graph.getNodesCount(), 2, "Unexpected number of nodes for 1x2x1 grid3 graph");
    t.equal(graph.getLinksCount(), 1, "Unexpected number of links for 1x2x1 grid3 graph");
    t.end();
  });
});
<commit_msg>Updated test description<commit_after>var test = require('tap').test,
    generators = require('../');

test('Create ladder', function(t) {
  var graph = generators.ladder(3);
  // Ladder graph looks like this:
  // *--*--*
  // |  |  |
  // *--*--*
  t.equal(graph.getNodesCount(), 6, "Unexpected number of nodes for ladder graph");
  t.equal(graph.getLinksCount(), 7, "Unexpected number of links for ladder graph");
  t.end();
});

test('Create path', function(t) {
  var size = 5;
  var graph = generators.path(size);
  // Path is simple:
  // *--*--*--*--*
  t.equal(graph.getNodesCount(), size, "Unexpected number of nodes for path graph");
  t.equal(graph.getLinksCount(), (size - 1), "Unexpected number of links for path graph");
  t.end();
});

test('Create complete', function(t) {
  var size = 5;
  var graph = generators.complete(size);
  // Complete graph has all nodes connected with each other.
  t.equal(graph.getNodesCount(), size, "Unexpected number of nodes for complete graph");
  t.equal(graph.getLinksCount(), (size * (size - 1)), "Unexpected number of links for complete graph");
  t.end();
});

test('Create complete', function(t) {
  // see http://en.wikipedia.org/wiki/Complete_bipartite_graph
  var m = 3, n = 4;
  var graph = generators.completeBipartite(m, n);

  t.equal(graph.getNodesCount(), m + n, "Unexpected number of nodes for complete bipartite graph");
  t.equal(graph.getLinksCount(), (m * n), "Unexpected number of links for complete bipartite graph");
  t.end();
});

test('Circular ladder', function(t) {
  var size = 3;
  var graph = generators.circularLadder(size);
  // cricular ladder is like a ladder, but last two and first two
  // nodes are connected
  // ┌-----┐
  // *--*--*
  // |  |  |
  // *--*--*
  // └-----┘
  t.equal(graph.getNodesCount(), 6, "Unexpected number of nodes for circular ladder graph");
  t.equal(graph.getLinksCount(), 9, "Unexpected number of links for circular ladder graph");
  t.end();
});

test('Create grid', function(t) {
  // grid is a grid. This is 3 x 4 grid:
  // *--*--*--*
  // |  |  |  |
  // *--*--*--*
  // |  |  |  |
  // *--*--*--*
  t.test('3x4 grid', function (t) {
    var graph = generators.grid(3, 4);
    t.equal(graph.getNodesCount(), 12, "Unexpected number of nodes for grid graph");
    t.equal(graph.getLinksCount(), 17, "Unexpected number of links for grid graph");
    t.end();
  });

  t.test('1x1 grid', function (t) {
    var graph = generators.grid(1, 1);
    t.equal(graph.getNodesCount(), 1, "Unexpected number of nodes for 1x1 grid graph");
    t.equal(graph.getLinksCount(), 0, "Unexpected number of links for 1x1 grid graph");
    t.end();
  });

  t.test('1x2 grid', function (t) {
    // this is a path of two nodes *--*
    var graph = generators.grid(1, 2);
    t.equal(graph.getNodesCount(), 2, "Unexpected number of nodes for 1x2 grid graph");
    t.equal(graph.getLinksCount(), 1, "Unexpected number of links for 1x2 grid graph");
    t.end();
  });
});

test('Create balanced binary tree', function(t) {
  // This is a binary tree graph, of height 2:
  //      *
  //   *     *
  // *   * *   *
  var graph = generators.balancedBinTree(2);
  t.equal(graph.getNodesCount(), 7, "Unexpected number of nodes for balanced binary tree");
  t.equal(graph.getLinksCount(), 6, "Unexpected number of links for balanced binary tree");
  t.end();
});

test('Create no links', function(t) {
  // no links graph has no links.
  var graph = generators.noLinks(42);
  t.equal(graph.getNodesCount(), 42, "Unexpected number of nodes in noLinks graph");
  t.equal(graph.getLinksCount(), 0, "Unexpected number of links in noLinks graph");
  t.end();
});

test('Create grid3d', function(t) {
  // grid is a grid. This is 3 x 4 x 2 grid:
  //
  //   *---*--*--*
  //  /  /  /  / |
  // *--*--*--*  *
  // |  |  |  | /|
  // *--*--*--*  *
  // |  |  |  | /
  // *--*--*--*
  //
  t.test('3x4x2 grid', function (t) {
    var graph = generators.grid3(3, 4, 2);
    t.equal(graph.getNodesCount(), 24, "Unexpected number of nodes for grid3 graph");
    t.equal(graph.getLinksCount(), 46, "Unexpected number of links for grid3 graph");
    t.end();
  });

  t.test('1x1x1 grid', function (t) {
    var graph = generators.grid3(1, 1, 1);
    t.equal(graph.getNodesCount(), 1, "Unexpected number of nodes for 1x1x1 grid3 graph");
    t.equal(graph.getLinksCount(), 0, "Unexpected number of links for 1x1x1 grid3 graph");
    t.end();
  });

  t.test('1x2x1 grid', function (t) {
    // this is a path of two nodes *--*
    var graph = generators.grid3(1, 2, 1);
    t.equal(graph.getNodesCount(), 2, "Unexpected number of nodes for 1x2x1 grid3 graph");
    t.equal(graph.getLinksCount(), 1, "Unexpected number of links for 1x2x1 grid3 graph");
    t.end();
  });
});
<|endoftext|>