<commit_before>'use strict';

var compilerPrototype,
    table;

table = require('markdown-table');

function repeat(times, character) {
    return Array(times + 1).join(character);
}

function getLongestRepetition(value, character) {
    var index,
        length,
        currentCount,
        highestCount,
        currentCharacter;

    highestCount = 0;

    index = -1;
    length = value.length;

    while (++index < length) {
        currentCharacter = value.charAt(index);

        if (currentCharacter === character) {
            currentCount++;
        } else {
            if (currentCount > highestCount) {
                highestCount = currentCount;
            }

            currentCount = 0;
        }
    }

    return highestCount;
}

function pad(value, level) {
    var index,
        padding;

    value = value.split('\n');

    index = value.length;
    padding = repeat(level * 4, ' ');

    while (index--) {
        if (value[index].length !== 0) {
            value[index] = padding + value[index];
        }
    }

    return value.join('\n');
}

function Compiler(options) {
    this.options = options || {};
    this.footnoteCounter = 0;
}

compilerPrototype = Compiler.prototype;

compilerPrototype.visit = function (token, parent, level) {
    var result;

    if (!level) {
        level = 0;
    }

    level += 1;

    result = this[token.type](token, parent, level);

    return result;
};

compilerPrototype.visitAll = function (token, tokens, level) {
    var self = this,
        values = [],
        iterator = -1,
        length = tokens.length;

    while (++iterator < length) {
        values[iterator] = self.visit(tokens[iterator], token, level);
    }

    return values;
};

compilerPrototype.visitOrderedItems = function (token, tokens, level) {
    var self = this,
        values = [],
        iterator = -1,
        length = tokens.length,
        indent;

    level = level + 1;

    while (++iterator < length) {
        indent = (iterator + 1) + '. ';

        values[iterator] = indent +
            self.listItem(tokens[iterator], token, level, indent.length);
    }

    return values;
};

compilerPrototype.visitUnorderedItems = function (token, tokens, level) {
    var self = this,
        values = [],
        iterator = -1,
        length = tokens.length,
        indent;

    level = level + 1;

    indent = '- ';

    while (++iterator < length) {
        values[iterator] = indent +
            self.listItem(tokens[iterator], token, level, indent.length);
    }

    return values;
};

compilerPrototype.root = function (token, parent, level) {
    var self = this,
        values = [],
        iterator = -1,
        tokens = token.children,
        length = tokens.length,
        child,
        prevType;

    while (++iterator < length) {
        child = tokens[iterator];

        /**
         * `html` tokens include their ending new lines.
         */

        if (prevType && prevType !== 'html') {
            /**
             * Duplicate tokens, such as a list
             * directly following another list,
             * often need multiple new lines.
             */

            if (child.type === prevType) {
                values.push('\n\n\n');
            } else {
                values.push('\n\n');
            }
        }

        values.push(self.visit(child, token, level));

        prevType = child.type;
    }

    return values.join('');
};

compilerPrototype.heading = function (token, parent, level) {
    return repeat(token.depth, '#') + ' ' +
        this.visitAll(token, token.children, level).join('');
};

compilerPrototype.text = function (token) {
    return token.value.replace(/[\\`*{}\[\]()#+\-.!_>]/g, '\\$&');
};

compilerPrototype.paragraph = function (token, parent, level) {
    return this.visitAll(token, token.children, level).join('');
};

compilerPrototype.blockquote = function (token, parent, level) {
    var result;

    result = this.visitAll(token, token.children, level);

    result = '> ' + result.join('\n\n').split('\n').join('\n> ');

    return result;
};

compilerPrototype.link = function (token, parent, level) {
    var value = '[' +
        this.visitAll(token, token.children, level).join('') +
    ']';

    value += '(' + token.href;

    if (token.title) {
        value += ' "' + token.title + '"';
    }

    value += ')';

    return value;
};

compilerPrototype.list = function (token, parent, level) {
    var methodName;

    methodName = token.ordered ? 'visitOrderedItems' : 'visitUnorderedItems';

    return this[methodName](token, token.children, level).join('\n');
  };

compilerPrototype.listItem = function (token, parent, level, padding) {
    var self,
        tokens,
        values,
        iterator,
        length,
        value;

    self = this;

    tokens = token.children;

    values = [];

    iterator = -1;
    length = tokens.length;

    while (++iterator < length) {
        values[iterator] = self.visit(tokens[iterator], token, level);
    }

    value = values.join(repeat(token.loose ? 2 : 1, '\n'));

    if (token.loose) {
        value += '\n';
    }

    value = pad(value, padding / 4);

    return value.trimLeft();
};

compilerPrototype.inlineCode = function (token) {
    var value,
        ticks,
        start,
        end;

    value = token.value;

    ticks = repeat(getLongestRepetition(value, '`') + 2, '`');

    start = ticks;
    end = ticks;

    if (value.charAt(0) === '`') {
        start += ' ';
    }

    if (value.charAt(value.length - 1) === '`') {
        end = ' ' + end;
    }

    return start + token.value + end;
};

compilerPrototype.code = function (token) {
    var value,
        ticks;

    value = token.value;

    /**
     * Probably pedantic.
     */

    if (!token.lang) {
        return pad(value, 1);
    }

    ticks = Math.max(getLongestRepetition(value, '`') + 1, 3);

    ticks = repeat(ticks, '`');

    return ticks + token.lang + '\n' + value + '\n' + ticks;
};

compilerPrototype.html = function (token) {
    return token.value;
};

compilerPrototype.horizontalRule = function () {
    return '* * *';
};

compilerPrototype.strong = function (token, parent, level) {
    return '**' + this.visitAll(token, token.children, level).join('') + '**';
};

compilerPrototype.emphasis = function (token, parent, level) {
    return '*' + this.visitAll(token, token.children, level).join('') + '*';
};

compilerPrototype.break = function () {
    return '\n';
};

compilerPrototype.delete = function (token, parent, level) {
    return '~~' + this.visitAll(token, token.children, level).join('') + '~~';
};

compilerPrototype.image = function (token) {
    var value = '![' + (token.alt || '') + ']';

    value += '(' + token.src;

    if (token.title) {
        value += ' "' + token.title + '"';
    }

    value += ')';

    return value;
};

compilerPrototype.footnote = function (token) {
    return '[^' + token.id + ']';
};

compilerPrototype.table = function (token, parent, level) {
    var align,
        self,
        cells;

    align = token.align.map(function (alignment) {
        return alignment.charAt(0);
    });

    self = this;

    cells = token.children.map(function (row) {
        return self.visitAll(row, row.children, level);
    });

    return table(cells, {
        'align': align
    });
};

compilerPrototype.tableCell = function (token, parent, level) {
    return this.visitAll(token, token.children, level).join('');
};

function stringify(token, options) {
    var compiler,
        footnotes,
        value;

    compiler = new Compiler(options);

    if (token.footnotes) {
        compiler.footnotes = token.footnotes;
        compiler.footnoteMap = {};
    }

    value = compiler.visit(token);

    if (token.footnotes) {
        footnotes = '';

        footnotes = '\n\n' + Object.keys(token.footnotes).map(function (key) {
            var footnote;

            footnote = token.footnotes[key];

            return '[^' + key + ']: ' + compiler.visitAll(
                null, footnote
            ).join('\n\n' + repeat(4, ' '));
        }).join('\n\n');

        value += footnotes;
    }

    return value;
}

module.exports = stringify;
<commit_msg>Add missing `new` operator to `lib/stringify.js`<commit_after>'use strict';

var compilerPrototype,
    table;

table = require('markdown-table');

function repeat(times, character) {
    return new Array(times + 1).join(character);
}

function getLongestRepetition(value, character) {
    var index,
        length,
        currentCount,
        highestCount,
        currentCharacter;

    highestCount = 0;

    index = -1;
    length = value.length;

    while (++index < length) {
        currentCharacter = value.charAt(index);

        if (currentCharacter === character) {
            currentCount++;
        } else {
            if (currentCount > highestCount) {
                highestCount = currentCount;
            }

            currentCount = 0;
        }
    }

    return highestCount;
}

function pad(value, level) {
    var index,
        padding;

    value = value.split('\n');

    index = value.length;
    padding = repeat(level * 4, ' ');

    while (index--) {
        if (value[index].length !== 0) {
            value[index] = padding + value[index];
        }
    }

    return value.join('\n');
}

function Compiler(options) {
    this.options = options || {};
    this.footnoteCounter = 0;
}

compilerPrototype = Compiler.prototype;

compilerPrototype.visit = function (token, parent, level) {
    var result;

    if (!level) {
        level = 0;
    }

    level += 1;

    result = this[token.type](token, parent, level);

    return result;
};

compilerPrototype.visitAll = function (token, tokens, level) {
    var self = this,
        values = [],
        iterator = -1,
        length = tokens.length;

    while (++iterator < length) {
        values[iterator] = self.visit(tokens[iterator], token, level);
    }

    return values;
};

compilerPrototype.visitOrderedItems = function (token, tokens, level) {
    var self = this,
        values = [],
        iterator = -1,
        length = tokens.length,
        indent;

    level = level + 1;

    while (++iterator < length) {
        indent = (iterator + 1) + '. ';

        values[iterator] = indent +
            self.listItem(tokens[iterator], token, level, indent.length);
    }

    return values;
};

compilerPrototype.visitUnorderedItems = function (token, tokens, level) {
    var self = this,
        values = [],
        iterator = -1,
        length = tokens.length,
        indent;

    level = level + 1;

    indent = '- ';

    while (++iterator < length) {
        values[iterator] = indent +
            self.listItem(tokens[iterator], token, level, indent.length);
    }

    return values;
};

compilerPrototype.root = function (token, parent, level) {
    var self = this,
        values = [],
        iterator = -1,
        tokens = token.children,
        length = tokens.length,
        child,
        prevType;

    while (++iterator < length) {
        child = tokens[iterator];

        /**
         * `html` tokens include their ending new lines.
         */

        if (prevType && prevType !== 'html') {
            /**
             * Duplicate tokens, such as a list
             * directly following another list,
             * often need multiple new lines.
             */

            if (child.type === prevType) {
                values.push('\n\n\n');
            } else {
                values.push('\n\n');
            }
        }

        values.push(self.visit(child, token, level));

        prevType = child.type;
    }

    return values.join('');
};

compilerPrototype.heading = function (token, parent, level) {
    return repeat(token.depth, '#') + ' ' +
        this.visitAll(token, token.children, level).join('');
};

compilerPrototype.text = function (token) {
    return token.value.replace(/[\\`*{}\[\]()#+\-.!_>]/g, '\\$&');
};

compilerPrototype.paragraph = function (token, parent, level) {
    return this.visitAll(token, token.children, level).join('');
};

compilerPrototype.blockquote = function (token, parent, level) {
    var result;

    result = this.visitAll(token, token.children, level);

    result = '> ' + result.join('\n\n').split('\n').join('\n> ');

    return result;
};

compilerPrototype.link = function (token, parent, level) {
    var value = '[' +
        this.visitAll(token, token.children, level).join('') +
    ']';

    value += '(' + token.href;

    if (token.title) {
        value += ' "' + token.title + '"';
    }

    value += ')';

    return value;
};

compilerPrototype.list = function (token, parent, level) {
    var methodName;

    methodName = token.ordered ? 'visitOrderedItems' : 'visitUnorderedItems';

    return this[methodName](token, token.children, level).join('\n');
  };

compilerPrototype.listItem = function (token, parent, level, padding) {
    var self,
        tokens,
        values,
        iterator,
        length,
        value;

    self = this;

    tokens = token.children;

    values = [];

    iterator = -1;
    length = tokens.length;

    while (++iterator < length) {
        values[iterator] = self.visit(tokens[iterator], token, level);
    }

    value = values.join(repeat(token.loose ? 2 : 1, '\n'));

    if (token.loose) {
        value += '\n';
    }

    value = pad(value, padding / 4);

    return value.trimLeft();
};

compilerPrototype.inlineCode = function (token) {
    var value,
        ticks,
        start,
        end;

    value = token.value;

    ticks = repeat(getLongestRepetition(value, '`') + 2, '`');

    start = ticks;
    end = ticks;

    if (value.charAt(0) === '`') {
        start += ' ';
    }

    if (value.charAt(value.length - 1) === '`') {
        end = ' ' + end;
    }

    return start + token.value + end;
};

compilerPrototype.code = function (token) {
    var value,
        ticks;

    value = token.value;

    /**
     * Probably pedantic.
     */

    if (!token.lang) {
        return pad(value, 1);
    }

    ticks = Math.max(getLongestRepetition(value, '`') + 1, 3);

    ticks = repeat(ticks, '`');

    return ticks + token.lang + '\n' + value + '\n' + ticks;
};

compilerPrototype.html = function (token) {
    return token.value;
};

compilerPrototype.horizontalRule = function () {
    return '* * *';
};

compilerPrototype.strong = function (token, parent, level) {
    return '**' + this.visitAll(token, token.children, level).join('') + '**';
};

compilerPrototype.emphasis = function (token, parent, level) {
    return '*' + this.visitAll(token, token.children, level).join('') + '*';
};

compilerPrototype.break = function () {
    return '\n';
};

compilerPrototype.delete = function (token, parent, level) {
    return '~~' + this.visitAll(token, token.children, level).join('') + '~~';
};

compilerPrototype.image = function (token) {
    var value = '![' + (token.alt || '') + ']';

    value += '(' + token.src;

    if (token.title) {
        value += ' "' + token.title + '"';
    }

    value += ')';

    return value;
};

compilerPrototype.footnote = function (token) {
    return '[^' + token.id + ']';
};

compilerPrototype.table = function (token, parent, level) {
    var align,
        self,
        cells;

    align = token.align.map(function (alignment) {
        return alignment.charAt(0);
    });

    self = this;

    cells = token.children.map(function (row) {
        return self.visitAll(row, row.children, level);
    });

    return table(cells, {
        'align': align
    });
};

compilerPrototype.tableCell = function (token, parent, level) {
    return this.visitAll(token, token.children, level).join('');
};

function stringify(token, options) {
    var compiler,
        footnotes,
        value;

    compiler = new Compiler(options);

    if (token.footnotes) {
        compiler.footnotes = token.footnotes;
        compiler.footnoteMap = {};
    }

    value = compiler.visit(token);

    if (token.footnotes) {
        footnotes = '';

        footnotes = '\n\n' + Object.keys(token.footnotes).map(function (key) {
            var footnote;

            footnote = token.footnotes[key];

            return '[^' + key + ']: ' + compiler.visitAll(
                null, footnote
            ).join('\n\n' + repeat(4, ' '));
        }).join('\n\n');

        value += footnotes;
    }

    return value;
}

module.exports = stringify;
<|endoftext|>


<commit_before>'use strict';

var Utils = require('./utils');

var arrayPrefixGenerators = {
    brackets: function brackets(prefix) {
        return prefix + '[]';
    },
    indices: function indices(prefix, key) {
        return prefix + '[' + key + ']';
    },
    repeat: function repeat(prefix) {
        return prefix;
    }
};

var defaults = {
    delimiter: '&',
    strictNullHandling: false,
    skipNulls: false,
    encode: true,
    encoder: Utils.encode
};

var isArray = Array.isArray;

var stringify = function stringify(object, prefix, generateArrayPrefix, strictNullHandling, skipNulls, encoder, filter, sort, allowDots) {
    var obj = object;
    if (typeof filter === 'function') {
        obj = filter(prefix, obj);
    } else if (obj instanceof Date) {
        obj = obj.toISOString();
    } else if (obj === null) {
        if (strictNullHandling) {
            return encoder ? encoder(prefix) : prefix;
        }

        obj = '';
    }

    if (typeof obj === 'string' || typeof obj === 'number' || typeof obj === 'boolean' || Utils.isBuffer(obj)) {
        if (encoder) {
            return [encoder(prefix) + '=' + encoder(obj)];
        }
        return [prefix + '=' + String(obj)];
    }

    var values = [];

    if (typeof obj === 'undefined') {
        return values;
    }

    var objKeys;
    if (isArray(filter)) {
        objKeys = filter;
    } else {
        var keys = Object.keys(obj);
        objKeys = sort ? keys.sort(sort) : keys;
    }

    for (var i = 0; i < objKeys.length; ++i) {
        var key = objKeys[i];

        if (skipNulls && obj[key] === null) {
            continue;
        }

        if (isArray(obj)) {
            values = values.concat(stringify(obj[key], generateArrayPrefix(prefix, key), generateArrayPrefix, strictNullHandling, skipNulls, encoder, filter, sort, allowDots));
        } else {
            values = values.concat(stringify(obj[key], prefix + (allowDots ? '.' + key : '[' + key + ']'), generateArrayPrefix, strictNullHandling, skipNulls, encoder, filter, sort, allowDots));
        }
    }

    return values;
};

module.exports = function (object, opts) {
    var obj = object;
    var options = opts || {};
    var delimiter = typeof options.delimiter === 'undefined' ? defaults.delimiter : options.delimiter;
    var strictNullHandling = typeof options.strictNullHandling === 'boolean' ? options.strictNullHandling : defaults.strictNullHandling;
    var skipNulls = typeof options.skipNulls === 'boolean' ? options.skipNulls : defaults.skipNulls;
    var encode = typeof options.encode === 'boolean' ? options.encode : defaults.encode;
    var encoder = encode ? typeof options.encoder === 'function' ? options.encoder : defaults.encoder : null;
    var sort = typeof options.sort === 'function' ? options.sort : null;
    var allowDots = typeof options.allowDots === 'undefined' ? false : options.allowDots;
    var objKeys;
    var filter;

    if (options.encoder !== null && options.encoder !== undefined && typeof options.encoder !== 'function') {
        throw new TypeError('Encoder has to be a function.');
    }

    if (typeof options.filter === 'function') {
        filter = options.filter;
        obj = filter('', obj);
    } else if (isArray(options.filter)) {
        objKeys = options.filter;
        filter = options.filter;
    }

    var keys = [];

    if (typeof obj !== 'object' || obj === null) {
        return '';
    }

    var arrayFormat;
    if (options.arrayFormat in arrayPrefixGenerators) {
        arrayFormat = options.arrayFormat;
    } else if ('indices' in options) {
        arrayFormat = options.indices ? 'indices' : 'repeat';
    } else {
        arrayFormat = 'indices';
    }

    var generateArrayPrefix = arrayPrefixGenerators[arrayFormat];

    if (!objKeys) {
        objKeys = Object.keys(obj);
    }

    if (sort) {
        objKeys.sort(sort);
    }

    for (var i = 0; i < objKeys.length; ++i) {
        var key = objKeys[i];

        if (skipNulls && obj[key] === null) {
            continue;
        }

        keys = keys.concat(stringify(obj[key], key, generateArrayPrefix, strictNullHandling, skipNulls, encoder, filter, sort, allowDots));
    }

    return keys.join(delimiter);
};
<commit_msg>[Robustness] `stringify`: avoid relying on a global `undefined` (#427)<commit_after>'use strict';

var Utils = require('./utils');

var arrayPrefixGenerators = {
    brackets: function brackets(prefix) {
        return prefix + '[]';
    },
    indices: function indices(prefix, key) {
        return prefix + '[' + key + ']';
    },
    repeat: function repeat(prefix) {
        return prefix;
    }
};

var defaults = {
    delimiter: '&',
    strictNullHandling: false,
    skipNulls: false,
    encode: true,
    encoder: Utils.encode
};

var isArray = Array.isArray;

var stringify = function stringify(object, prefix, generateArrayPrefix, strictNullHandling, skipNulls, encoder, filter, sort, allowDots) {
    var obj = object;
    if (typeof filter === 'function') {
        obj = filter(prefix, obj);
    } else if (obj instanceof Date) {
        obj = obj.toISOString();
    } else if (obj === null) {
        if (strictNullHandling) {
            return encoder ? encoder(prefix) : prefix;
        }

        obj = '';
    }

    if (typeof obj === 'string' || typeof obj === 'number' || typeof obj === 'boolean' || Utils.isBuffer(obj)) {
        if (encoder) {
            return [encoder(prefix) + '=' + encoder(obj)];
        }
        return [prefix + '=' + String(obj)];
    }

    var values = [];

    if (typeof obj === 'undefined') {
        return values;
    }

    var objKeys;
    if (isArray(filter)) {
        objKeys = filter;
    } else {
        var keys = Object.keys(obj);
        objKeys = sort ? keys.sort(sort) : keys;
    }

    for (var i = 0; i < objKeys.length; ++i) {
        var key = objKeys[i];

        if (skipNulls && obj[key] === null) {
            continue;
        }

        if (isArray(obj)) {
            values = values.concat(stringify(obj[key], generateArrayPrefix(prefix, key), generateArrayPrefix, strictNullHandling, skipNulls, encoder, filter, sort, allowDots));
        } else {
            values = values.concat(stringify(obj[key], prefix + (allowDots ? '.' + key : '[' + key + ']'), generateArrayPrefix, strictNullHandling, skipNulls, encoder, filter, sort, allowDots));
        }
    }

    return values;
};

module.exports = function (object, opts) {
    var obj = object;
    var options = opts || {};
    var delimiter = typeof options.delimiter === 'undefined' ? defaults.delimiter : options.delimiter;
    var strictNullHandling = typeof options.strictNullHandling === 'boolean' ? options.strictNullHandling : defaults.strictNullHandling;
    var skipNulls = typeof options.skipNulls === 'boolean' ? options.skipNulls : defaults.skipNulls;
    var encode = typeof options.encode === 'boolean' ? options.encode : defaults.encode;
    var encoder = encode ? typeof options.encoder === 'function' ? options.encoder : defaults.encoder : null;
    var sort = typeof options.sort === 'function' ? options.sort : null;
    var allowDots = typeof options.allowDots === 'undefined' ? false : options.allowDots;
    var objKeys;
    var filter;

    if (options.encoder !== null && typeof options.encoder !== 'undefined' && typeof options.encoder !== 'function') {
        throw new TypeError('Encoder has to be a function.');
    }

    if (typeof options.filter === 'function') {
        filter = options.filter;
        obj = filter('', obj);
    } else if (isArray(options.filter)) {
        objKeys = options.filter;
        filter = options.filter;
    }

    var keys = [];

    if (typeof obj !== 'object' || obj === null) {
        return '';
    }

    var arrayFormat;
    if (options.arrayFormat in arrayPrefixGenerators) {
        arrayFormat = options.arrayFormat;
    } else if ('indices' in options) {
        arrayFormat = options.indices ? 'indices' : 'repeat';
    } else {
        arrayFormat = 'indices';
    }

    var generateArrayPrefix = arrayPrefixGenerators[arrayFormat];

    if (!objKeys) {
        objKeys = Object.keys(obj);
    }

    if (sort) {
        objKeys.sort(sort);
    }

    for (var i = 0; i < objKeys.length; ++i) {
        var key = objKeys[i];

        if (skipNulls && obj[key] === null) {
            continue;
        }

        keys = keys.concat(stringify(obj[key], key, generateArrayPrefix, strictNullHandling, skipNulls, encoder, filter, sort, allowDots));
    }

    return keys.join(delimiter);
};
<|endoftext|>


<commit_before>var _ = require('underscore')
var marked = require('marked')
var urlParser = require('url')
var flat = require('flat')
var path = require('path')

var isPublic = module.exports.isPublic = function (doclet) {
  if (doclet.access && doclet.access !== 'public') {
    return false
  } else if (doclet.tags) {
    var apiTag = _.findWhere(doclet.tags, {
      title: 'api'
    })
    if (apiTag && apiTag.value !== 'public') {
      return false
    }
  }

  return true
}

var isIgnored = module.exports.isIgnored = function (doclets) {
  return function (doclet) {
    if (doclet.ignore) {
      return true
    }
    var moduleName = doclet.longname.match(/module:[^~\.#]+/)
    if (moduleName) {
      var module = _.findWhere(doclets, {longname: moduleName})
      if (module) {
        return module.ignore
      }
    }
    var parent = _.findWhere(doclets, {longname: doclet.memberof})
    if (parent) {
      return parent.ignore
    }
  }
}

var fileRootFromGitHubUrl = function (branch) {
  return '/blob/' + branch + '/'
}

var rawRootFromGitHubUrl = function (branch) {
  return '/raw/' + branch + '/'
}

module.exports.getPlaygrounds = function (doclet) {
  var playgrounds = _.filter(doclet.tags, {title: 'playground'})
  if (playgrounds.length > 0) {
    return playgrounds
  }
}

module.exports.hasLiveExamples = function (doclet) {
  if (doclet.tags) {
    return _.findWhere(doclet.tags, {title: 'live'}) !== undefined
  }
}

module.exports.addUrlToDoclets = function (doclets, githubUrl, branch) {
  var fileBaseUrl = githubUrl + fileRootFromGitHubUrl(branch)
  _.each(doclets, function (doclet) {
    try {
      doclet.meta.url = fileBaseUrl + doclet.meta.filename + '#L' + doclet.meta.lineno
    } catch (err) {
      console.log('no URL for', doclet.longname, err)
    }
  })
}

var clearAnonymous = module.exports.clearAnonymous = function (doclets) {
  _.each(doclets, function (doclet) {
    var match = doclet.longname.match(/<anonymous>(~|\.|#)(.+)/)
    if (match) {
      doclet.longname = match[2]
    }
  })
}

var isRelativeLink = module.exports.isRelativeLink = function (href) {
  if (urlParser.parse(href).protocol || href.indexOf('//') === 0) {
    return false
  }
  return true
}

var idFromLongname = module.exports.idFromLongname = function (longname) {
  var escaped = 'dl-' + longname.replace(/(\.|:|#|~|\s|\$)/g, '-')
  return escaped
}

var shortLinkName = function (parts) {
  if (parts.pathname.substr(-1) === '/') {
    parts.pathname = parts.pathname.substr(0, parts.pathname.length - 1)
  }
  return parts.host + parts.pathname
}

var createLink = module.exports.createLink = function (link, text) {
  var parts = urlParser.parse(link)
  if (parts.protocol && (parts.protocol === 'http:' || parts.protocol === 'https:')) {
    text = text || shortLinkName(parts)
  } else {
    text = text || link
    link = '#' + idFromLongname(link)
  }
  return "" + text + ''
}

var replaceInlineLinks = module.exports.replaceInlineLink = function (str) {
  str = str.replace(/\[(.+?)\]\s*{@link(?:plain|code)?\s([^}]+?)}/g, function (match, text, link) {
    return createLink(link, text)
  })
  str = str.replace(/{@link(?:plain|code)?\s([^\s}]+?)[\s|\|]([^}]+?)}/g, function (match, link, text) {
    return createLink(link, text)
  })
  str = str.replace(/{@link(?:plain|code)?\s([^\s}]+?)}/g, function (match, link) {
    return createLink(link)
  })
  return str
}

var normalizeSeeTag = module.exports.normalizeSeeTag = function (see) {
  if (!see.match(/{@link}/) && see.match(/^[^\s]+$/)) {
    return '{@link ' + see + '}'
  }
  return see
}

var processInlineTags = module.exports.processInlineTags = function (doclet) {
  if (doclet.description) {
    doclet.description = replaceInlineLinks(doclet.description)
  }
  if (doclet.see) {
    doclet.see = doclet.see.map(normalizeSeeTag)
    doclet.see = doclet.see.map(replaceInlineLinks)
  }
}

var createMarkdownRenderer = module.exports.createMarkdownRenderer = function (githubUrl, branch) {
  var fileBaseUrl = fileRootFromGitHubUrl(branch)
  var rawBaseUrl = rawRootFromGitHubUrl(branch)
  var linkFixer = new marked.Renderer()
  var renderLink = linkFixer.link
  var renderImage = linkFixer.image
  linkFixer.link = function (href, title, text) {
    if (isRelativeLink(href)) {
      href = githubUrl + path.join(fileBaseUrl, href)
    }
    return renderLink.call(this, href, title, text)
  }
  linkFixer.image = function (href, title, text) {
    if (isRelativeLink(href)) {
      href = githubUrl + path.join(rawBaseUrl, href)
    }
    return renderImage.call(this, href, title, text)
  }
  return function (markdown) {
    return marked(markdown, {
      renderer: linkFixer
    })
  }
}

module.exports.buildArticleHtml = function (article, githubUrl, branch) {
  var linkFixer = createMarkdownRenderer(githubUrl, branch)
  return linkFixer(article.markdown)
}

var Node = function (doclet, module) {
  if (module) {
    var shortName = doclet.longname.split(module.longname)[1] || '___self'
    this.__id = shortName.substr(1, shortName.length - 1)
  } else {
    this.__id = doclet.longname
  }
  this.__doclet = doclet
}

var childs = module.exports.childs = function (node) {
  return _.chain(node)
    .pick(function (val, key) {
      return key.indexOf('__') !== 0
    })
    .mapObject(function (value, key) {
      return {
        name: key,
        node: value
      }
    })
    .toArray()
    .sortBy('name')
    .value()
}

var unflattenParams = module.exports.unflattenParams = function (doclet) {
  if (doclet.params) {
    var tmp = doclet.params.map(function (param) {
      param.name = param.name || 'unnamed'
      return {
        __name: param.name,
        __content: param
      }
    })
    tmp = _.indexBy(tmp, '__name')
    var nestedParams = flat.unflatten(tmp)
    var sortedNested = []
    doclet.params.forEach(function (param) {
      if (param.name.indexOf('.') === -1) {
        sortedNested.push(nestedParams[param.name])
      }
    })
    doclet.nestedParams = sortedNested
  }
}

module.exports.flat = function (doclets) {
  if (!doclets._flat) {
    doclets._flat = _.chain(doclets)
      .filter(isPublic)
      .reject(isIgnored(doclets))
      .sortBy('longname')
      .each(processInlineTags)
      .each(unflattenParams)
      .value()
  }
  return doclets._flat
}

module.exports.tree = function (doclets) {
  clearAnonymous(doclets)
  var modules = _.chain(doclets)
    .filter(isPublic)
    .reject(isIgnored(doclets))
    .where({kind: 'module'})
    .map(function (module) {
      var flatTree = _.chain(doclets)
        .filter(function (doclet) {
          return doclet.kind !== 'module'
        })
        .filter(function (doclet) {
          var regexp = new RegExp('^(' + module.longname + ')(~|\\.|#)')
          return doclet.longname.match(regexp) || doclet.longname === module.longname
        })
        .map(function (doclet) {
          return new Node(doclet, module)
        })
        .indexBy('__id')
        .value()

      module.childs = flat.unflatten(flatTree, {delimiter: /~|\.|#/})
      return module
    })
    .sortBy('longname')
    .value()

  var globalTree = _.chain(doclets)
    .filter(isPublic)
    .reject(isIgnored(doclets))
    .filter(function (doclet) {
      return doclet.longname.indexOf('module:') === -1
    })
    .map(function (doclet) {
      return new Node(doclet)
    })
    .indexBy('__id')
    .value()

  if (_.keys(globalTree).length > 0) {
    modules.unshift({
      longname: 'globals',
      childs: flat.unflatten(globalTree, {delimiter: /~|\.|#/})
    })
  }

  return modules
}
<commit_msg>remove unused var<commit_after>var _ = require('underscore')
var marked = require('marked')
var urlParser = require('url')
var flat = require('flat')
var path = require('path')

var isPublic = module.exports.isPublic = function (doclet) {
  if (doclet.access && doclet.access !== 'public') {
    return false
  } else if (doclet.tags) {
    var apiTag = _.findWhere(doclet.tags, {
      title: 'api'
    })
    if (apiTag && apiTag.value !== 'public') {
      return false
    }
  }

  return true
}

var isIgnored = module.exports.isIgnored = function (doclets) {
  return function (doclet) {
    if (doclet.ignore) {
      return true
    }
    var moduleName = doclet.longname.match(/module:[^~\.#]+/)
    if (moduleName) {
      var module = _.findWhere(doclets, {longname: moduleName})
      if (module) {
        return module.ignore
      }
    }
    var parent = _.findWhere(doclets, {longname: doclet.memberof})
    if (parent) {
      return parent.ignore
    }
  }
}

var fileRootFromGitHubUrl = function (branch) {
  return '/blob/' + branch + '/'
}

var rawRootFromGitHubUrl = function (branch) {
  return '/raw/' + branch + '/'
}

module.exports.getPlaygrounds = function (doclet) {
  var playgrounds = _.filter(doclet.tags, {title: 'playground'})
  if (playgrounds.length > 0) {
    return playgrounds
  }
}

module.exports.hasLiveExamples = function (doclet) {
  if (doclet.tags) {
    return _.findWhere(doclet.tags, {title: 'live'}) !== undefined
  }
}

module.exports.addUrlToDoclets = function (doclets, githubUrl, branch) {
  var fileBaseUrl = githubUrl + fileRootFromGitHubUrl(branch)
  _.each(doclets, function (doclet) {
    try {
      doclet.meta.url = fileBaseUrl + doclet.meta.filename + '#L' + doclet.meta.lineno
    } catch (err) {
      console.log('no URL for', doclet.longname, err)
    }
  })
}

var clearAnonymous = module.exports.clearAnonymous = function (doclets) {
  _.each(doclets, function (doclet) {
    var match = doclet.longname.match(/<anonymous>(~|\.|#)(.+)/)
    if (match) {
      doclet.longname = match[2]
    }
  })
}

var isRelativeLink = module.exports.isRelativeLink = function (href) {
  if (urlParser.parse(href).protocol || href.indexOf('//') === 0) {
    return false
  }
  return true
}

var idFromLongname = module.exports.idFromLongname = function (longname) {
  var escaped = 'dl-' + longname.replace(/(\.|:|#|~|\s|\$)/g, '-')
  return escaped
}

var shortLinkName = function (parts) {
  if (parts.pathname.substr(-1) === '/') {
    parts.pathname = parts.pathname.substr(0, parts.pathname.length - 1)
  }
  return parts.host + parts.pathname
}

var createLink = module.exports.createLink = function (link, text) {
  var parts = urlParser.parse(link)
  if (parts.protocol && (parts.protocol === 'http:' || parts.protocol === 'https:')) {
    text = text || shortLinkName(parts)
  } else {
    text = text || link
    link = '#' + idFromLongname(link)
  }
  return "" + text + ''
}

var replaceInlineLinks = module.exports.replaceInlineLink = function (str) {
  str = str.replace(/\[(.+?)\]\s*{@link(?:plain|code)?\s([^}]+?)}/g, function (match, text, link) {
    return createLink(link, text)
  })
  str = str.replace(/{@link(?:plain|code)?\s([^\s}]+?)[\s|\|]([^}]+?)}/g, function (match, link, text) {
    return createLink(link, text)
  })
  str = str.replace(/{@link(?:plain|code)?\s([^\s}]+?)}/g, function (match, link) {
    return createLink(link)
  })
  return str
}

var normalizeSeeTag = module.exports.normalizeSeeTag = function (see) {
  if (!see.match(/{@link}/) && see.match(/^[^\s]+$/)) {
    return '{@link ' + see + '}'
  }
  return see
}

var processInlineTags = module.exports.processInlineTags = function (doclet) {
  if (doclet.description) {
    doclet.description = replaceInlineLinks(doclet.description)
  }
  if (doclet.see) {
    doclet.see = doclet.see.map(normalizeSeeTag)
    doclet.see = doclet.see.map(replaceInlineLinks)
  }
}

var createMarkdownRenderer = module.exports.createMarkdownRenderer = function (githubUrl, branch) {
  var fileBaseUrl = fileRootFromGitHubUrl(branch)
  var rawBaseUrl = rawRootFromGitHubUrl(branch)
  var linkFixer = new marked.Renderer()
  var renderLink = linkFixer.link
  var renderImage = linkFixer.image
  linkFixer.link = function (href, title, text) {
    if (isRelativeLink(href)) {
      href = githubUrl + path.join(fileBaseUrl, href)
    }
    return renderLink.call(this, href, title, text)
  }
  linkFixer.image = function (href, title, text) {
    if (isRelativeLink(href)) {
      href = githubUrl + path.join(rawBaseUrl, href)
    }
    return renderImage.call(this, href, title, text)
  }
  return function (markdown) {
    return marked(markdown, {
      renderer: linkFixer
    })
  }
}

module.exports.buildArticleHtml = function (article, githubUrl, branch) {
  var linkFixer = createMarkdownRenderer(githubUrl, branch)
  return linkFixer(article.markdown)
}

var Node = function (doclet, module) {
  if (module) {
    var shortName = doclet.longname.split(module.longname)[1] || '___self'
    this.__id = shortName.substr(1, shortName.length - 1)
  } else {
    this.__id = doclet.longname
  }
  this.__doclet = doclet
}

module.exports.childs = function (node) {
  return _.chain(node)
    .pick(function (val, key) {
      return key.indexOf('__') !== 0
    })
    .mapObject(function (value, key) {
      return {
        name: key,
        node: value
      }
    })
    .toArray()
    .sortBy('name')
    .value()
}

var unflattenParams = module.exports.unflattenParams = function (doclet) {
  if (doclet.params) {
    var tmp = doclet.params.map(function (param) {
      param.name = param.name || 'unnamed'
      return {
        __name: param.name,
        __content: param
      }
    })
    tmp = _.indexBy(tmp, '__name')
    var nestedParams = flat.unflatten(tmp)
    var sortedNested = []
    doclet.params.forEach(function (param) {
      if (param.name.indexOf('.') === -1) {
        sortedNested.push(nestedParams[param.name])
      }
    })
    doclet.nestedParams = sortedNested
  }
}

module.exports.flat = function (doclets) {
  if (!doclets._flat) {
    doclets._flat = _.chain(doclets)
      .filter(isPublic)
      .reject(isIgnored(doclets))
      .sortBy('longname')
      .each(processInlineTags)
      .each(unflattenParams)
      .value()
  }
  return doclets._flat
}

module.exports.tree = function (doclets) {
  clearAnonymous(doclets)
  var modules = _.chain(doclets)
    .filter(isPublic)
    .reject(isIgnored(doclets))
    .where({kind: 'module'})
    .map(function (module) {
      var flatTree = _.chain(doclets)
        .filter(function (doclet) {
          return doclet.kind !== 'module'
        })
        .filter(function (doclet) {
          var regexp = new RegExp('^(' + module.longname + ')(~|\\.|#)')
          return doclet.longname.match(regexp) || doclet.longname === module.longname
        })
        .map(function (doclet) {
          return new Node(doclet, module)
        })
        .indexBy('__id')
        .value()

      module.childs = flat.unflatten(flatTree, {delimiter: /~|\.|#/})
      return module
    })
    .sortBy('longname')
    .value()

  var globalTree = _.chain(doclets)
    .filter(isPublic)
    .reject(isIgnored(doclets))
    .filter(function (doclet) {
      return doclet.longname.indexOf('module:') === -1
    })
    .map(function (doclet) {
      return new Node(doclet)
    })
    .indexBy('__id')
    .value()

  if (_.keys(globalTree).length > 0) {
    modules.unshift({
      longname: 'globals',
      childs: flat.unflatten(globalTree, {delimiter: /~|\.|#/})
    })
  }

  return modules
}
<|endoftext|>


<commit_before>var _ = require('underscore'),
    fs = require('fs'),
    hl = require('highlight.js'),
    jade = require('jade'),
    marked = require('marked'),
    stylus = require('stylus');

hl.LANGUAGES['html'] = hl.LANGUAGES['htm'] = hl.LANGUAGES['xml'];

const DEFAULT_TEMPLATE = fs.readFileSync(__dirname + '/../resources/template.jade');

function escapeJavascript(string) {
  return string.replace(/"/g, '\\"').replace(/\n/g, '\\n');
}

module.exports = function(input, options) {
  var highlight = !options || options.highlight === undefined || options.highlight,
      template = options && options.template !== undefined ? options.template : DEFAULT_TEMPLATE;

  template = template ? jade.compile(template) : function(data) { return data.content; };

  marked.setOptions({
    gfm: true,
    pedantic: false,
    sanitize: false,
    highlight: function(code, lang) {
      if (!highlight) {
        return code;
      }

      if (lang && hl.LANGUAGES[lang]) {
        var ret = hl.highlight(lang, code).value;

        // Work around a rendering bug in highlight js where gt are not being escaped.
        if (lang === 'html') {
          ret = ret.replace(/>>/g, '>>');
        }

        return ret;
      } else {
        return hl.highlightAuto(code).value;
      }
    }
  });

  var ast = marked.lexer(input),
      updated = [],
      content = [],
      sections = [],
      id = 0;

  function finishSection() {
    if (updated.length) {
      if (updated[0].section) {
        updated.push({
          section: true,
          type: 'html',
          pre: true,
          text: '
\n'
        });
      }
      content.push(marked.parser(updated));
      updated = [];
    }
  }

  for (var i = 0, len = ast.length; i < len; i++) {
    var token = ast[i];
    if (token.type === 'heading' && token.depth === 2) {
      finishSection();

      var sectionId = 'section-' +  ++id;
      sections.push({
        id: sectionId,
        name: token.text
      });

      updated.push({
        section: true,
        type: 'html',
        pre: true,
        text: '
\n'
      });
      token.depth = 1;
    }

    updated.push(token);

    if (token.type === 'code') {
      if (token.lang === 'htm') {
        token.lang = 'html';
      }

      if (token.lang === 'html') {
        updated.push({
          type: 'html',
          pre: true,
          text: '
' + token.text + '
'
        });
      } else if (token.lang === 'javascript') {
        updated.push({
          type: 'html',
          pre: true,
          text: ''
        });
      } else if (token.lang === 'handlebars') {
        var uniqueId = ++id;

        updated.push({
          type: 'html',
          pre: true,
          text: '

\n'
            + ''
        });
      } else if (token.lang === 'css') {
        updated.push({
          type: 'html',
          pre: true,
          text: ''
        });
      } else if (token.lang === 'stylus') {
        stylus.render(token.text, function(err, style){
          if (err) {
            throw err;
          }

          // WARN: This assumes that stylus never goes async. UT coverage should handle this case should that happen
          updated.push({
            type: 'html',
            pre: true,
            text: ''
          });
        })
      }
    }
  }
  finishSection();

  // Cleanup any template data if necessary
  var templates = {};
  if (options && options.templates) {
    _.each(options.templates, function(value, key) {
      templates[escapeJavascript(key)] = escapeJavascript(value);
    });
  }

  return template({
    sections: sections,
    scripts: (options && options.scripts) || [],
    templates: templates,
    sheets: (options && options.sheets) || [],
    content: content.join('')
  });
};

module.exports.findTemplates = function(input) {
  var templates = [];

  var extractTemplate = /\{\{\s*#?\s*template\s*"((?:[^"]|\\")+)"/g,
      match;
  while (match = extractTemplate.exec(input)) {
    templates.push(match[1]);
  }

  return _.uniq(templates);
};
<commit_msg>Fix exec under marked 0.2.7<commit_after>var _ = require('underscore'),
    fs = require('fs'),
    hl = require('highlight.js'),
    jade = require('jade'),
    marked = require('marked'),
    stylus = require('stylus');

hl.LANGUAGES['html'] = hl.LANGUAGES['htm'] = hl.LANGUAGES['xml'];

const DEFAULT_TEMPLATE = fs.readFileSync(__dirname + '/../resources/template.jade');

function escapeJavascript(string) {
  return string.replace(/"/g, '\\"').replace(/\n/g, '\\n');
}

module.exports = function(input, options) {
  var highlight = !options || options.highlight === undefined || options.highlight,
      template = options && options.template !== undefined ? options.template : DEFAULT_TEMPLATE;

  template = template ? jade.compile(template) : function(data) { return data.content; };

  marked.setOptions({
    gfm: true,
    pedantic: false,
    sanitize: false,
    highlight: function(code, lang) {
      if (!highlight) {
        return code;
      }

      if (lang && hl.LANGUAGES[lang]) {
        var ret = hl.highlight(lang, code).value;

        // Work around a rendering bug in highlight js where gt are not being escaped.
        if (lang === 'html') {
          ret = ret.replace(/>>/g, '>>');
        }

        return ret;
      } else {
        return hl.highlightAuto(code).value;
      }
    }
  });

  var ast = marked.lexer(input),
      updated = [],
      content = [],
      sections = [],
      id = 0;

  function finishSection() {
    if (updated.length) {
      if (updated[0].section) {
        updated.push({
          section: true,
          type: 'html',
          pre: true,
          text: '
\n'
        });
      }
      updated.links = {};   // Required with marked 0.2.6+ For some reason...
      content.push(marked.parser(updated));
      updated = [];
    }
  }

  for (var i = 0, len = ast.length; i < len; i++) {
    var token = ast[i];
    if (token.type === 'heading' && token.depth === 2) {
      finishSection();

      var sectionId = 'section-' +  ++id;
      sections.push({
        id: sectionId,
        name: token.text
      });

      updated.push({
        section: true,
        type: 'html',
        pre: true,
        text: '
\n'
      });
      token.depth = 1;
    }

    updated.push(token);

    if (token.type === 'code') {
      if (token.lang === 'htm') {
        token.lang = 'html';
      }

      if (token.lang === 'html') {
        updated.push({
          type: 'html',
          pre: true,
          text: '
' + token.text + '
'
        });
      } else if (token.lang === 'javascript') {
        updated.push({
          type: 'html',
          pre: true,
          text: ''
        });
      } else if (token.lang === 'handlebars') {
        var uniqueId = ++id;

        updated.push({
          type: 'html',
          pre: true,
          text: '

\n'
            + ''
        });
      } else if (token.lang === 'css') {
        updated.push({
          type: 'html',
          pre: true,
          text: ''
        });
      } else if (token.lang === 'stylus') {
        stylus.render(token.text, function(err, style){
          if (err) {
            throw err;
          }

          // WARN: This assumes that stylus never goes async. UT coverage should handle this case should that happen
          updated.push({
            type: 'html',
            pre: true,
            text: ''
          });
        })
      }
    }
  }
  finishSection();

  // Cleanup any template data if necessary
  var templates = {};
  if (options && options.templates) {
    _.each(options.templates, function(value, key) {
      templates[escapeJavascript(key)] = escapeJavascript(value);
    });
  }

  return template({
    sections: sections,
    scripts: (options && options.scripts) || [],
    templates: templates,
    sheets: (options && options.sheets) || [],
    content: content.join('')
  });
};

module.exports.findTemplates = function(input) {
  var templates = [];

  var extractTemplate = /\{\{\s*#?\s*template\s*"((?:[^"]|\\")+)"/g,
      match;
  while (match = extractTemplate.exec(input)) {
    templates.push(match[1]);
  }

  return _.uniq(templates);
};
<|endoftext|>


<commit_before>var request = require('superagent'),
    Q = require('q');

var fixture = require('../apps/api/fixture');

var METHOD_MAP = {
  'create': 'post',
  'update': 'put',
  'delete': 'del',
  'read': 'get',
  'patch': 'patch'
};

// Main exported sync function. Returns a Q promise to mimic Backbone + jQuery,
// if using the built in cache support it will check the cache for data or send
// off the HTTP request.
//
// @param {Function} method
// @param {Object} model
// @param {Object} options
// @return {Object} A Q promise to mimc how Backbone work with jQuery

module.exports = function(method, model, options) {

  // return setTimeout(function() {
  //   success(options, { body: fixture() }, function(){})
  // }, 500 + Math.random() * 500);

  var url = (options.url ||
    (typeof model.url == 'function' ? model.url() : model.url));
  request.get(url).timeout(2000).end(function(err, r) {
    if (err || !r.ok) {
      error(options, err || r, function(){});
    } else {
      success(options, r, function(){});
    }
  });
  return

  var cacheKey = urlDataCacheKey(method, model, options)[2];
  return Q.promise(function(resolve, reject) {
    if (options.cache && module.exports.cacheClient) {
      module.exports.cacheClient.get(cacheKey, function(err, cachedJSON) {
        if (err || cachedJSON) model.trigger('request', model, {}, options);
        if (err) {
          error(options, err, reject);
        } else if (cachedJSON) {
          success(options, JSON.parse(cachedJSON), resolve);
        } else {
          send(method, model, options, resolve, reject);
        }
      });
    } else {
      send(method, model, options, resolve, reject);
    }
  });
};

// Helper to compute the requesting url, params, and cache key based off those
// two. Used when sending a new http request or determing the cache key to read.
//
// @param {Function} method
// @param {Object} model
// @param {Object} options
// @return {Array} Array of [url, data, cacheKey]

var urlDataCacheKey = function(method, model, options) {
  var url = (options.url ||
    (typeof model.url == 'function' ? model.url() : model.url));
  var data = (options.data ||
    (method === 'create' || method === 'update' ? model.toJSON(options) : {}));
  var cacheKey = url + JSON.stringify(data);
  return [url, data, cacheKey];
}

// Builds and sends the actual http request. This reflects the guts of the
// default Backbone.sync logic.
//
// @param {Function} method
// @param {Object} model
// @param {Object} options
// @param {Function} resolve Deferred resolve
// @param {Function} reject  Deferred reject

var send = function (method, model, options, resolve, reject) {
  var url = urlDataCacheKey(method, model, options)[0];
  var data = urlDataCacheKey(method, model, options)[1];
  var cacheKey = urlDataCacheKey(method, model, options)[2];
  var req = request[METHOD_MAP[method]](url);

  // Allow intercepting of the request object to inject sync-wide things like
  // an oAuth token.
  module.exports.editRequest(req, method, model, options);

  // Inject POST/PUT data in body or GET data in querystring
  if (method == 'create' || method == 'update') {
    req.send(data);
  } else {
    req.query(data);
  }

  // Add common Backbone options like `headers`
  if (options.headers) {
    for(key in options.headers) req.set(key, options.headers[key]);
  }

  // Send the actual http request with a timeout to ensure long hanging
  // requests don't leak memory.
  req.timeout(options.timeout || module.exports.timeout).end(function(err, res) {
    if (err || !res.ok) return error(options, (err || res), reject);
    if (options.cache && module.exports.cacheClient) {
      module.exports.cacheClient.set(cacheKey, JSON.stringify({
        body: res.body,
        headers: res.headers
      }));
      module.exports.cacheClient.expire(cacheKey,
        (options.cacheTime || module.exports.defaultCacheTime));
    }
    success(options, res, resolve);
  });
  model.trigger('request', model, req);
}

// DRYs up resolving the callbacks and deferreds for a successful response,
// whether that's from the cache or a resolved http request.
//
// @param {Object} options Backbone options
// @param {Object} res A response object with { body: {}, headers: {} }
// @param {Function} resolve Deferred resolve

var success = function(options, res, resolve) {
  options.res = { headers: res.headers };
  if (options.success) options.success(res.body);
  if (options.complete) options.complete(res.body);
  resolve(res.body);
}

// DRYs up resolving the callbacks and deferreds for an unsuccessful response,
// whether that's from the cache or a resolved http request.
//
// @param {Object} options Backbone options
// @param {Object} err
// @param {Function} reject  Deferred reject

var error = function(options, err, reject) {
  if (options.error) options.error(err);
  if (options.complete) options.complete(err);
  reject(err);
}

// Configuration that can be overwritten by the user. Includes being able
// to modify the request, a cache client library, default cache expiry, and
// the default timeout for a sent http request.

module.exports.editRequest = function(req) {};
module.exports.cacheClient = null;
module.exports.defaultCacheTime = 3600;
module.exports.timeout = 2000;
<commit_msg>latest<commit_after>var request = require('superagent'),
    Q = require('q');

var fixture = require('../apps/api/fixture');

var METHOD_MAP = {
  'create': 'post',
  'update': 'put',
  'delete': 'del',
  'read': 'get',
  'patch': 'patch'
};

// Main exported sync function. Returns a Q promise to mimic Backbone + jQuery,
// if using the built in cache support it will check the cache for data or send
// off the HTTP request.
//
// @param {Function} method
// @param {Object} model
// @param {Object} options
// @return {Object} A Q promise to mimc how Backbone work with jQuery

module.exports = function(method, model, options) {

  // return setTimeout(function() {
  //   success(options, { body: fixture() }, function(){})
  // }, 500 + Math.random() * 500);

  // var url = (options.url ||
  //   (typeof model.url == 'function' ? model.url() : model.url));
  // request.get(url).timeout(2000).end(function(err, r) {
  //   if (err || !r.ok) {
  //     error(options, err || r, function(){});
  //   } else {
  //     success(options, r, function(){});
  //   }
  // });
  // return

  var cacheKey = urlDataCacheKey(method, model, options)[2];
  return Q.promise(function(resolve, reject) {
    var url = (options.url ||
      (typeof model.url == 'function' ? model.url() : model.url));
    request.get(url).timeout(2000).end(function(err, r) {
      if (err || !r.ok) {
        error(options, err || r, reject);
      } else {
        success(options, r, resolve);
      }
    });

    // if (options.cache && module.exports.cacheClient) {
    //   module.exports.cacheClient.get(cacheKey, function(err, cachedJSON) {
    //     if (err || cachedJSON) model.trigger('request', model, {}, options);
    //     if (err) {
    //       error(options, err, reject);
    //     } else if (cachedJSON) {
    //       success(options, JSON.parse(cachedJSON), resolve);
    //     } else {
    //       send(method, model, options, resolve, reject);
    //     }
    //   });
    // } else {
    //   send(method, model, options, resolve, reject);
    // }
  });
};

// Helper to compute the requesting url, params, and cache key based off those
// two. Used when sending a new http request or determing the cache key to read.
//
// @param {Function} method
// @param {Object} model
// @param {Object} options
// @return {Array} Array of [url, data, cacheKey]

var urlDataCacheKey = function(method, model, options) {
  var url = (options.url ||
    (typeof model.url == 'function' ? model.url() : model.url));
  var data = (options.data ||
    (method === 'create' || method === 'update' ? model.toJSON(options) : {}));
  var cacheKey = url + JSON.stringify(data);
  return [url, data, cacheKey];
}

// Builds and sends the actual http request. This reflects the guts of the
// default Backbone.sync logic.
//
// @param {Function} method
// @param {Object} model
// @param {Object} options
// @param {Function} resolve Deferred resolve
// @param {Function} reject  Deferred reject

var send = function (method, model, options, resolve, reject) {
  var url = urlDataCacheKey(method, model, options)[0];
  var data = urlDataCacheKey(method, model, options)[1];
  var cacheKey = urlDataCacheKey(method, model, options)[2];
  var req = request[METHOD_MAP[method]](url);

  // Allow intercepting of the request object to inject sync-wide things like
  // an oAuth token.
  module.exports.editRequest(req, method, model, options);

  // Inject POST/PUT data in body or GET data in querystring
  if (method == 'create' || method == 'update') {
    req.send(data);
  } else {
    req.query(data);
  }

  // Add common Backbone options like `headers`
  if (options.headers) {
    for(key in options.headers) req.set(key, options.headers[key]);
  }

  // Send the actual http request with a timeout to ensure long hanging
  // requests don't leak memory.
  req.timeout(options.timeout || module.exports.timeout).end(function(err, res) {
    if (err || !res.ok) return error(options, (err || res), reject);
    if (options.cache && module.exports.cacheClient) {
      module.exports.cacheClient.set(cacheKey, JSON.stringify({
        body: res.body,
        headers: res.headers
      }));
      module.exports.cacheClient.expire(cacheKey,
        (options.cacheTime || module.exports.defaultCacheTime));
    }
    success(options, res, resolve);
  });
  model.trigger('request', model, req);
}

// DRYs up resolving the callbacks and deferreds for a successful response,
// whether that's from the cache or a resolved http request.
//
// @param {Object} options Backbone options
// @param {Object} res A response object with { body: {}, headers: {} }
// @param {Function} resolve Deferred resolve

var success = function(options, res, resolve) {
  options.res = { headers: res.headers };
  if (options.success) options.success(res.body);
  if (options.complete) options.complete(res.body);
  resolve(res.body);
}

// DRYs up resolving the callbacks and deferreds for an unsuccessful response,
// whether that's from the cache or a resolved http request.
//
// @param {Object} options Backbone options
// @param {Object} err
// @param {Function} reject  Deferred reject

var error = function(options, err, reject) {
  if (options.error) options.error(err);
  if (options.complete) options.complete(err);
  reject(err);
}

// Configuration that can be overwritten by the user. Includes being able
// to modify the request, a cache client library, default cache expiry, and
// the default timeout for a sent http request.

module.exports.editRequest = function(req) {};
module.exports.cacheClient = null;
module.exports.defaultCacheTime = 3600;
module.exports.timeout = 2000;
<|endoftext|>


<commit_before>var nodemiral = require('nodemiral');
var fs = require('fs');
var path = require('path');

var SCRIPT_DIR = path.resolve(__dirname, '../scripts');
var TEMPLATES_DIR = path.resolve(__dirname, '../templates');

exports.setup = function(installMongo, setupNode, nodeVersion, setupPhantom, appName) {
  var taskList = nodemiral.taskList('Setting Up');

  // Installation
  if (setupNode) {
    taskList.executeScript('installing node', {
      script: path.resolve(SCRIPT_DIR, 'install-node.sh'),
      vars: {
        nodeVersion: nodeVersion
      },
    });
  }

  if (setupPhantom) {
    taskList.executeScript('installing phantomjs', {
      script: path.resolve(SCRIPT_DIR, 'install-phantomjs.sh')
    });
  }

  taskList.executeScript('setting up environment', {
    script: path.resolve(SCRIPT_DIR, 'setup-env.sh'),
    vars: {
      appName: appName
    }
  });

  if(installMongo) {
    taskList.copy('copy /etc/mongodb.conf', {
      src: path.resolve(TEMPLATES_DIR, 'mongodb.conf'),
      dest: '/etc/mongodb.conf'
    });

    taskList.executeScript('installing mongodb', {
      script: path.resolve(SCRIPT_DIR, 'install-mongodb.sh')
    });
  }

  //Configurations
  taskList.copy('configure upstart', {
    src: path.resolve(TEMPLATES_DIR, 'meteor.conf'),
    dest: '/etc/init/' + appName + '.conf',
    vars: {
      appName: appName
    }
  });

  return taskList;
};

exports.deploy = function(bundlePath, env, deployCheckWaitTime, appName, binaryNpmModules) {
  var taskList = nodemiral.taskList("Deploying " + appName + " App");

  taskList.copy('uploading bundle', {
    src: bundlePath,
    dest: '/opt/' + appName + '/tmp/bundle.tar.gz'
  });

  taskList.copy('setting up env vars', {
    src: path.resolve(TEMPLATES_DIR, 'env.sh'),
    dest: '/opt/' + appName + '/config/env.sh',
    vars: {
      env: env || {},
      appName: appName,
    }
  });

  //deploying
  taskList.executeScript('invoking deployment process', {
    script: path.resolve(TEMPLATES_DIR, 'deploy.sh'),
    vars: {
      deployCheckWaitTime: deployCheckWaitTime || 10,
      appName: appName,
      binaryNpmModules: binaryNpmModules
    }
  });

  return taskList;
};

exports.reconfig = function(env, appName) {
  var taskList = nodemiral.taskList("Updating Configurations");

  taskList.copy('setting up env vars', {
    src: path.resolve(TEMPLATES_DIR, 'env.sh'),
    dest: '/opt/' + appName + '/config/env.sh',
    vars: {
      env: env || {},
      appName: appName
    }
  });

  //deploying
  taskList.execute('restarting the app', {
    command: '(sudo stop meteor || :) && (sudo start meteor)'
  });

  return taskList;
};
<commit_msg>possible fix for #45<commit_after>var nodemiral = require('nodemiral');
var fs = require('fs');
var path = require('path');

var SCRIPT_DIR = path.resolve(__dirname, '../scripts');
var TEMPLATES_DIR = path.resolve(__dirname, '../templates');

exports.setup = function(installMongo, setupNode, nodeVersion, setupPhantom, appName) {
  var taskList = nodemiral.taskList('Setting Up');

  // Installation
  if (setupNode) {
    taskList.executeScript('installing node', {
      script: path.resolve(SCRIPT_DIR, 'install-node.sh'),
      vars: {
        nodeVersion: nodeVersion
      },
    });
  }

  if (setupPhantom) {
    taskList.executeScript('installing phantomjs', {
      script: path.resolve(SCRIPT_DIR, 'install-phantomjs.sh')
    });
  }

  taskList.executeScript('setting up environment', {
    script: path.resolve(SCRIPT_DIR, 'setup-env.sh'),
    vars: {
      appName: appName
    }
  });

  if(installMongo) {
    taskList.copy('copy /etc/mongodb.conf', {
      src: path.resolve(TEMPLATES_DIR, 'mongodb.conf'),
      dest: '/etc/mongodb.conf'
    });

    taskList.executeScript('installing mongodb', {
      script: path.resolve(SCRIPT_DIR, 'install-mongodb.sh')
    });
  }

  //Configurations
  taskList.copy('configure upstart', {
    src: path.resolve(TEMPLATES_DIR, 'meteor.conf'),
    dest: '/etc/init/' + appName + '.conf',
    vars: {
      appName: appName
    }
  });

  return taskList;
};

exports.deploy = function(bundlePath, env, deployCheckWaitTime, appName, binaryNpmModules) {
  var taskList = nodemiral.taskList("Deploying " + appName + " App");

  taskList.copy('uploading bundle', {
    src: bundlePath,
    dest: '/opt/' + appName + '/tmp/bundle.tar.gz'
  });

  taskList.copy('setting up env vars', {
    src: path.resolve(TEMPLATES_DIR, 'env.sh'),
    dest: '/opt/' + appName + '/config/env.sh',
    vars: {
      env: env || {},
      appName: appName,
    }
  });

  //deploying
  taskList.executeScript('invoking deployment process', {
    script: path.resolve(TEMPLATES_DIR, 'deploy.sh'),
    vars: {
      deployCheckWaitTime: deployCheckWaitTime || 10,
      appName: appName,
      binaryNpmModules: binaryNpmModules
    }
  });

  return taskList;
};

exports.reconfig = function(env, appName) {
  var taskList = nodemiral.taskList("Updating Configurations");

  taskList.copy('setting up env vars', {
    src: path.resolve(TEMPLATES_DIR, 'env.sh'),
    dest: '/opt/' + appName + '/config/env.sh',
    vars: {
      env: env || {},
      appName: appName
    }
  });

  //deploying
  taskList.execute('restarting the app', {
    command: '(sudo stop ' + appName + ' || :) && (sudo start ' + appName + ')'
  });

  return taskList;
};
<|endoftext|>


<commit_before>var EventEmitter = require('events').EventEmitter;
var util = require('util');
var urlResolve = require('url').resolve;
var _ = require('underscore');
var request = require('superagent');
var ga = require('../lib/googleanalytics.js');

var STORAGE_KEY = 'TEACH_API_LOGIN_INFO';

function autobind(obj) {
  var prototypes = [].slice.call(arguments, 1);
  prototypes.forEach(function(prototype) {
    Object.keys(prototype).filter(function(propName) {
      return typeof obj[propName] == 'function';
    }).forEach(function(methodName) {
      obj[methodName] = obj[methodName].bind(obj);
    });
  });
}

function TeachAPI(options) {
  options = options || {};

  autobind(this, TeachAPI.prototype, EventEmitter.prototype);

  this.baseURL = (options.baseURL ||
                  process.env.TEACH_API_URL ||
                  'https://teach-api.herokuapp.com');
  this.storage = options.storage || (
    process.browser ? window.sessionStorage : {}
  );
  this._clubs = [];
}

util.inherits(TeachAPI, EventEmitter);

_.extend(TeachAPI.prototype, {
  logout: function() {
    delete this.storage[STORAGE_KEY];
    this.emit('username:change', null);
    this.emit('logout');
  },
  getLoginInfo: function() {
    try {
      return JSON.parse(this.storage[STORAGE_KEY]);
    } catch (e) {
      return null;
    }
  },
  getUsername: function() {
    var info = this.getLoginInfo();
    return info && info.username;
  },
  startLogin: function() {
    if (!(process.browser && window.navigator.id)) {
      return this.emit('login:error',
                       new Error('navigator.id does not exist'));
    }
    window.navigator.id.get(function(assertion) {
      if (!assertion) {
        return this.emit('login:cancel');
      }

      request
        .post(this.baseURL + '/auth/persona')
        .type('form')
        .send({ assertion: assertion })
        .end(function(err, res) {
          if (err) {
            err.hasNoWebmakerAccount = (
              err.response && err.response.forbidden &&
              err.response.text == 'invalid assertion or email'
            );
            return this.emit('login:error', err);
          }

          // TODO: Handle a thrown exception here.
          this.storage[STORAGE_KEY] = JSON.stringify(res.body);

          this.emit('username:change', res.body.username);
          this.emit('login:success', res.body);
        }.bind(this));
    }.bind(this));
  },
  request: function(method, path) {
    var info = this.getLoginInfo();
    var url = urlResolve(this.baseURL, path);
    var req = request(method, url);

    if (info && info.token) {
      if (url.indexOf(this.baseURL + '/') == 0) {
        req.set('Authorization', 'Token ' + info.token);
      } else {
        console.warn('Teach API base URL is ' + this.baseURL +
                     ' which is at a different origin from ' +
                     url + '. Not sending auth token.');
      }
    }

    return req;
  },
  getClubs: function() {
    return this._clubs;
  },
  updateClubs: function(callback) {
    callback = callback || function () {};
    return this.request('get', '/api/clubs/')
      .accept('json')
      .end(function(err, res) {
        if (err) {
          return callback(err);
        }
        this._clubs = res.body;
        this.emit('clubs:change', res.body);
        callback(null, res.body);
      }.bind(this));
  },
  addClub: function(club, callback) {
    callback = callback || function () {};
    return this.request('post', '/api/clubs/')
      .send(club)
      .accept('json')
      .end(function(err, res) {
        if (err) {
          return callback(err);
        }
        this.updateClubs();
        ga.event({ category: 'Clubs', action: 'Added a Club' });
        callback(null, res.body);
      }.bind(this));
  },
  changeClub: function(club, callback) {
    callback = callback || function () {};
    return this.request('put', club.url)
      .send(club)
      .accept('json')
      .end(function(err, res) {
        if (err) {
          return callback(err);
        }
        this.updateClubs();
        ga.event({ category: 'Clubs', action: 'Edited a Club' });
        callback(null, res.body);
      }.bind(this));
  },
  deleteClub: function(url, callback) {
    callback = callback || function () {};
    return this.request('delete', url)
      .accept('json')
      .end(function(err, res) {
        if (err) {
          return callback(err);
        }
        this.updateClubs();
        ga.event({ category: 'Clubs', action: 'Deleted a Club' });
        callback(null);
      }.bind(this));
  }
});

module.exports = TeachAPI;
<commit_msg>fix == to ===.<commit_after>var EventEmitter = require('events').EventEmitter;
var util = require('util');
var urlResolve = require('url').resolve;
var _ = require('underscore');
var request = require('superagent');
var ga = require('../lib/googleanalytics.js');

var STORAGE_KEY = 'TEACH_API_LOGIN_INFO';

function autobind(obj) {
  var prototypes = [].slice.call(arguments, 1);
  prototypes.forEach(function(prototype) {
    Object.keys(prototype).filter(function(propName) {
      return typeof obj[propName] == 'function';
    }).forEach(function(methodName) {
      obj[methodName] = obj[methodName].bind(obj);
    });
  });
}

function TeachAPI(options) {
  options = options || {};

  autobind(this, TeachAPI.prototype, EventEmitter.prototype);

  this.baseURL = (options.baseURL ||
                  process.env.TEACH_API_URL ||
                  'https://teach-api.herokuapp.com');
  this.storage = options.storage || (
    process.browser ? window.sessionStorage : {}
  );
  this._clubs = [];
}

util.inherits(TeachAPI, EventEmitter);

_.extend(TeachAPI.prototype, {
  logout: function() {
    delete this.storage[STORAGE_KEY];
    this.emit('username:change', null);
    this.emit('logout');
  },
  getLoginInfo: function() {
    try {
      return JSON.parse(this.storage[STORAGE_KEY]);
    } catch (e) {
      return null;
    }
  },
  getUsername: function() {
    var info = this.getLoginInfo();
    return info && info.username;
  },
  startLogin: function() {
    if (!(process.browser && window.navigator.id)) {
      return this.emit('login:error',
                       new Error('navigator.id does not exist'));
    }
    window.navigator.id.get(function(assertion) {
      if (!assertion) {
        return this.emit('login:cancel');
      }

      request
        .post(this.baseURL + '/auth/persona')
        .type('form')
        .send({ assertion: assertion })
        .end(function(err, res) {
          if (err) {
            err.hasNoWebmakerAccount = (
              err.response && err.response.forbidden &&
              err.response.text == 'invalid assertion or email'
            );
            return this.emit('login:error', err);
          }

          // TODO: Handle a thrown exception here.
          this.storage[STORAGE_KEY] = JSON.stringify(res.body);

          this.emit('username:change', res.body.username);
          this.emit('login:success', res.body);
        }.bind(this));
    }.bind(this));
  },
  request: function(method, path) {
    var info = this.getLoginInfo();
    var url = urlResolve(this.baseURL, path);
    var req = request(method, url);

    if (info && info.token) {
      if (url.indexOf(this.baseURL + '/') === 0) {
        req.set('Authorization', 'Token ' + info.token);
      } else {
        console.warn('Teach API base URL is ' + this.baseURL +
                     ' which is at a different origin from ' +
                     url + '. Not sending auth token.');
      }
    }

    return req;
  },
  getClubs: function() {
    return this._clubs;
  },
  updateClubs: function(callback) {
    callback = callback || function () {};
    return this.request('get', '/api/clubs/')
      .accept('json')
      .end(function(err, res) {
        if (err) {
          return callback(err);
        }
        this._clubs = res.body;
        this.emit('clubs:change', res.body);
        callback(null, res.body);
      }.bind(this));
  },
  addClub: function(club, callback) {
    callback = callback || function () {};
    return this.request('post', '/api/clubs/')
      .send(club)
      .accept('json')
      .end(function(err, res) {
        if (err) {
          return callback(err);
        }
        this.updateClubs();
        ga.event({ category: 'Clubs', action: 'Added a Club' });
        callback(null, res.body);
      }.bind(this));
  },
  changeClub: function(club, callback) {
    callback = callback || function () {};
    return this.request('put', club.url)
      .send(club)
      .accept('json')
      .end(function(err, res) {
        if (err) {
          return callback(err);
        }
        this.updateClubs();
        ga.event({ category: 'Clubs', action: 'Edited a Club' });
        callback(null, res.body);
      }.bind(this));
  },
  deleteClub: function(url, callback) {
    callback = callback || function () {};
    return this.request('delete', url)
      .accept('json')
      .end(function(err, res) {
        if (err) {
          return callback(err);
        }
        this.updateClubs();
        ga.event({ category: 'Clubs', action: 'Deleted a Club' });
        callback(null);
      }.bind(this));
  }
});

module.exports = TeachAPI;
<|endoftext|>


<commit_before>/**
* Log requests for build HAR output
*
* @see: https://dvcs.w3.org/hg/webperf/raw-file/tip/specs/HAR/Overview.html
*/
'use strict';

exports.version = '0.1';

var fs = require('fs');

/**
 * From phantomHAR
 * @author: Christopher Van (@cvan)
 * @homepage: https://github.com/cvan/phantomHAR
 * @original: https://github.com/cvan/phantomHAR/blob/master/phantomhar.js
 */

function getErrorString(error) {
    // According to http://qt-project.org/doc/qt-4.8/qnetworkreply.html
    switch (error.errorCode) {
        case 1:
            return '(refused)';
        case 2:
            return '(closed)';
        case 3:
            return '(host not found)';
        case 4:
            return '(timeout)';
        case 5:
            return '(canceled)';
        case 6:
            return '(ssl failure)';
        case 7:
            return '(net failure)';
        default:
            return '(unknown error)';
    }
}

function createHAR(page, creator) {
    var address = page.address;
    var title = page.title;
    var startTime = page.startTime;
    var resources = page.resources;

    var entries = [];

    resources.forEach(function(resource) {
        var request = resource.request;
        var startReply = resource.startReply;
        var endReply = resource.endReply;
        var error = resource.error;

        if (!request || !startReply || !endReply) {
            return;
        }

        // Exclude data URIs from the HAR because they aren't
        // included in the spec.
        if (request.url.substring(0, 5).toLowerCase() === 'data:') {
            return;
        }

        if (error) {
            startReply.bodySize = 0;
            startReply.time = 0;
            endReply.time = 0;
            endReply.content = {};
            endReply.contentType = null;
            endReply.headers = [];
            endReply.statusText = getErrorString(error);
            endReply.status = null;
        }

        entries.push({
            cache: {},
            pageref: address,
            request: {
                // Accurate bodySize blocked on https://github.com/ariya/phantomjs/pull/11484
                // bodySize: -1,
                bodySize: startReply.contentLength,
                cookies: [],
                headers: request.headers,
                // Accurate headersSize blocked on https://github.com/ariya/phantomjs/pull/11484
                // headersSize: -1,
                headersSize: 0,
                httpVersion: 'HTTP/1.1',
                method: request.method,
                queryString: [],
                url: request.url,
            },
            response: {
                bodySize: startReply.contentLength,
                cookies: [],
                headers: endReply.headers,
                headersSize: -1,
                httpVersion: 'HTTP/1.1',
                redirectURL: '',
                status: endReply.status,
                statusText: endReply.statusText,
                content: {
                    mimeType: endReply.contentType || '',
                    size: startReply.bodySize, // uncompressed
                    text: startReply.content || ''
                }
            },
            startedDateTime: request.time.toISOString(),
            time: endReply.time - request.time,
            timings: {
                blocked: 0,
                dns: -1,
                connect: -1,
                send: 0,
                wait: startReply.time - request.time,
                receive: endReply.time - startReply.time,
                ssl: -1
            }
        });
    });

    return {
        log: {
            creator: creator,
            entries: entries,
            pages: [
                {
                    startedDateTime: startTime.toISOString(),
                    id: address,
                    title: title,
                    pageTimings: {
                        onLoad: page.onLoad || -1,
                        onContentLoad: page.onContentLoad || -1
                    }
                }
            ],
            version: '1.2',
        }
    };
}
/** End **/

exports.module = function(phantomas) {

    var param = phantomas.getParam('har'),
        path = '';

    var page = {
        origin: undefined,
        resources: [],
        title: undefined,
        address: undefined,
        startTime: undefined,
        endTime: undefined,
        onDOMReadyTime: undefined,
        windowOnLoadTime: undefined,
        timeToLastByte: undefined,
        onLoad: undefined,
        onContentLoad: undefined
    };

    var creator = {
        name: "Phantomas - (using phantomHAR)",
        version: phantomas.getVersion()
    };

    if (typeof param === 'undefined') {
        phantomas.log('No HAR path specified, use --har <path>');
        return;
    }

    // --har
    if (param === true) {
        // defaults to "2013-12-07T20:15:01.521Z.har"
        path = (new Date()).toJSON() + '.har';
    }
    // --har [file name]
    else {
        path = param;
    }

    phantomas.log('HAR path: %s', path);

    phantomas.on('pageBeforeOpen', function(p) {
        page.origin = p;
    });

    phantomas.on('pageOpen', function() {
        page.startTime = new Date();
    });

    phantomas.on('loadFinished', function() {
        page.endTime = new Date();
    });

    phantomas.on('onResourceRequested', function(res, req) {
        page.resources[res.id] = {
            request: res,
            startReply: null,
            endReply: null
        };
    });

    phantomas.on('onResourceReceived', function(res) {
        switch (res.stage) {
            case 'start':
                page.resources[res.id].startReply = res;
                break;
            case 'end':
                page.resources[res.id].endReply = res;
                break;
        }
    });

    phantomas.on('metric', function(name, value) {
        switch (name) {
            case 'onDOMReadyTime':
                page.onDOMReadyTime = value;
                break;
            case 'windowOnLoadTime':
                page.windowOnLoadTime = value;
                break;
            case 'timeToLastByte':
                page.timeToLastByte = value;
                break;
        }
    });

    phantomas.on('report', function() {
        // Set endTime if page was not finished correctly
        if (! page.endTime)
            page.endTime = new Date();

        // If metric 'windowOnLoadTime' hasn't been fired, compute it
        if (! page.windowOnLoadTime)
            page.windowOnLoadTime = page.endTime.getTime() - page.startTime.getTime();

        page.address = page.origin.url;
        page.title = page.origin.title;

        // Times (windowOnLoadTime, onDOMReadyTime) are relative to responseEnd entry
        // in NavigationTiming (represented by timeToLastByte metric)
        page.onLoad = page.timeToLastByte + page.windowOnLoadTime;
        page.onContentLoad = page.timeToLastByte + page.onDOMReadyTime;

        phantomas.log('Create HAR: %s ("%s")', page.address, page.title);

        var har,
            dump;

        try {
            har = createHAR(page, creator);
        } catch (e) {
            phantomas.log('Impossible to build HAR: %s', e);
            return;
        }

        phantomas.log('Convert HAR to JSON');
        try {
            dump = JSON.stringify(har);
        } catch (e) {
            phantomas.log('Impossible stringify HAR on JSON format: %s', e);
            return;
        }

        phantomas.log("Write HAR in '%s'", path);
        try {
            fs.write(path, dump);
        } catch (e) {
            phantomas.log('Impossible write HAR file: %s', e);
            return;
        }

        phantomas.log('HAR Done !');
    });
};
<commit_msg>Use 'send' and 'recv' events instead 'onResource*'<commit_after>/**
* Log requests for build HAR output
*
* @see: https://dvcs.w3.org/hg/webperf/raw-file/tip/specs/HAR/Overview.html
*/
'use strict';

exports.version = '0.1';

var fs = require('fs');

/**
 * Inspired by phantomHAR
 * @author: Christopher Van (@cvan)
 * @homepage: https://github.com/cvan/phantomHAR
 * @original: https://github.com/cvan/phantomHAR/blob/master/phantomhar.js
 */

function createHAR(page, creator) {
    var address = page.address;
    var title = page.title;
    var startTime = page.startTime;
    var resources = page.resources;

    var entries = [];

    resources.forEach(function(resource) {
        var request = resource.request;
        var response = resource.response;
        var entry = resource.entry;
        var contentType = entry.headers['content-type'];

        if (!request || !response || !entry) {
            return;
        }

        // Exclude data URIs from the HAR because they aren't
        // included in the spec.
        if (request.url.substring(0, 5).toLowerCase() === 'data:') {
            return;
        }

        entries.push({
            cache: {},
            pageref: address,
            request: {
                // Accurate bodySize blocked on https://github.com/ariya/phantomjs/pull/11484
                bodySize: -1,
                cookies: [],
                headers: request.headers,
                // Accurate headersSize blocked on https://github.com/ariya/phantomjs/pull/11484
                headersSize: -1,
                httpVersion: 'HTTP/1.1',
                method: request.method,
                queryString: [],
                url: request.url,
            },
            response: {
                bodySize: entry.contentLength,
                cookies: [],
                headers: response.headers,
                headersSize: -1,
                httpVersion: 'HTTP/1.1',
                redirectURL: '',
                status: entry.status,
                statusText: entry.statusText,
                content: {
                    mimeType: contentType || '',
                    size: entry.bodySize, // uncompressed
                    text: entry.content || ''
                }
            },
            startedDateTime: entry.sendTime.toISOString(),
            time: entry.timeToLastByte,
            timings: {
                blocked: 0,
                dns: -1,
                connect: -1,
                send: 0,
                wait: entry.timeToFirstByte || 0,
                receive: entry.receiveTime,
                ssl: -1
            }
        });
    });

    return {
        log: {
            creator: creator,
            entries: entries,
            pages: [
                {
                    startedDateTime: startTime.toISOString(),
                    id: address,
                    title: title,
                    pageTimings: {
                        onLoad: page.onLoad || -1,
                        onContentLoad: page.onContentLoad || -1
                    }
                }
            ],
            version: '1.2',
        }
    };
}
/** End **/

exports.module = function(phantomas) {

    var param = phantomas.getParam('har'),
        path = '';

    var page = {
        origin: undefined,
        resources: [],
        title: undefined,
        address: undefined,
        startTime: undefined,
        endTime: undefined,
        onDOMReadyTime: undefined,
        windowOnLoadTime: undefined,
        timeToLastByte: undefined,
        onLoad: undefined,
        onContentLoad: undefined
    };

    var creator = {
        name: "Phantomas - (using phantomHAR)",
        version: phantomas.getVersion()
    };

    if (typeof param === 'undefined') {
        phantomas.log('No HAR path specified, use --har <path>');
        return;
    }

    // --har
    if (param === true) {
        // defaults to "2013-12-07T20:15:01.521Z.har"
        path = (new Date()).toJSON() + '.har';
    }
    // --har [file name]
    else {
        path = param;
    }

    phantomas.log('HAR path: %s', path);

    phantomas.on('pageBeforeOpen', function(p) {
        page.origin = p;
    });

    phantomas.on('pageOpen', function() {
        page.startTime = new Date();
    });

    phantomas.on('loadFinished', function() {
        page.endTime = new Date();
    });

    phantomas.on('send', function(entry, res) {
        page.resources[res.id] = {
            request: res,
            response: null,
            entry: null
        };
    });

    phantomas.on('recv', function(entry, res) {
        page.resources[res.id].response = res;
        page.resources[res.id].entry = entry;
    });

    phantomas.on('metric', function(name, value) {
        switch (name) {
            case 'onDOMReadyTime':
                page.onDOMReadyTime = value;
                break;
            case 'windowOnLoadTime':
                page.windowOnLoadTime = value;
                break;
            case 'timeToLastByte':
                page.timeToLastByte = value;
                break;
        }
    });

    phantomas.on('report', function() {
        // Set endTime if page was not finished correctly
        if (! page.endTime)
            page.endTime = new Date();

        // If metric 'windowOnLoadTime' hasn't been fired, compute it
        if (! page.windowOnLoadTime)
            page.windowOnLoadTime = page.endTime.getTime() - page.startTime.getTime();

        page.address = page.origin.url;
        page.title = page.origin.title;

        // Times (windowOnLoadTime, onDOMReadyTime) are relative to responseEnd entry
        // in NavigationTiming (represented by timeToLastByte metric)
        page.onLoad = page.timeToLastByte + page.windowOnLoadTime;
        page.onContentLoad = page.timeToLastByte + page.onDOMReadyTime;

        phantomas.log('Create HAR: %s ("%s")', page.address, page.title);

        var har,
            dump;

        try {
            har = createHAR(page, creator);
        } catch (e) {
            phantomas.log('Impossible to build HAR: %s', e);
            return;
        }

        phantomas.log('Convert HAR to JSON');
        try {
            dump = JSON.stringify(har);
        } catch (e) {
            phantomas.log('Impossible stringify HAR on JSON format: %s', e);
            return;
        }

        phantomas.log("Write HAR in '%s'", path);
        try {
            fs.write(path, dump);
        } catch (e) {
            phantomas.log('Impossible write HAR file: %s', e);
            return;
        }

        phantomas.log('HAR Done !');
    });
};
<|endoftext|>


<commit_before>var dust = require('../deps/dustjs/lib/dust'),
    async = require('../deps/async'),
    path = require('path'),
    utils = require('./utils'),
    fs = require('fs');

exports.load = function (dir, doc, callback) {
    var paths = doc.settings.templates;
    if (!Array.isArray(paths)) {
        paths = [paths];
    }
    async.concat(paths, function (p, cb) {
        exports.find(path.join(dir, p), cb);
    },
    function (err, files) {
        if (err) return callback(err);
        exports.addFiles(dir, files, doc, callback);
    });
};

exports.addFiles = function (dir, files, doc, callback) {
    dir = utils.abspath(dir);
    fs.readFile(__dirname + '/../deps/dustjs/lib/dust.js', function (err, src) {
        if (err) return callback(err);
        doc.templates = src.toString();
        async.forEach(files, function (f, cb) {
            exports.addFile(dir, utils.abspath(f), doc, cb);
        }, callback);
    });
};

exports.addFile = function (dir, file, doc, callback) {
    fs.readFile(file, function (err, content) {
        if (err) return callback(err);
        var rel = utils.relpath(file, path.join(dir, doc.settings.templates));
        doc.templates += dust.compile(content.toString(), rel);
        callback();
    });
};

exports.find = function (p, callback) {
    utils.find(p, /^[^\.]+(?!\/\.)*(?:\.\w+)?$/, callback);
};
<commit_msg>don't compress whitespace in dust templates<commit_after>var dust = require('../deps/dustjs/lib/dust'),
    async = require('../deps/async'),
    path = require('path'),
    utils = require('./utils'),
    fs = require('fs');

// disable whitespace compression
dust.optimizers.format = function(ctx, node) {
    return node
};

exports.load = function (dir, doc, callback) {
    var paths = doc.settings.templates;
    if (!Array.isArray(paths)) {
        paths = [paths];
    }
    async.concat(paths, function (p, cb) {
        exports.find(path.join(dir, p), cb);
    },
    function (err, files) {
        if (err) return callback(err);
        exports.addFiles(dir, files, doc, callback);
    });
};

exports.addFiles = function (dir, files, doc, callback) {
    dir = utils.abspath(dir);
    fs.readFile(__dirname + '/../deps/dustjs/lib/dust.js', function (err, src) {
        if (err) return callback(err);
        doc.templates = src.toString();
        async.forEach(files, function (f, cb) {
            exports.addFile(dir, utils.abspath(f), doc, cb);
        }, callback);
    });
};

exports.addFile = function (dir, file, doc, callback) {
    fs.readFile(file, function (err, content) {
        if (err) return callback(err);
        var rel = utils.relpath(file, path.join(dir, doc.settings.templates));
        doc.templates += dust.compile(content.toString(), rel);
        callback();
    });
};

exports.find = function (p, callback) {
    utils.find(p, /^[^\.]+(?!\/\.)*(?:\.\w+)?$/, callback);
};
<|endoftext|>