<commit_before>var d3 = require("d3");

module.exports = function xkcdplot() {

    // Default parameters.
    var width = 600,
        height = 300,
        margin = 20,
        arrowSize = 12,
        arrowAspect = 0.4,
        arrowOffset = 6,
        magnitude = 0.003,
        xlabel = "Time of Day",
        ylabel = "Awesomeness",
        title = "The most important graph ever made",
        xlim,
        ylim;

    // Plot elements.
    var el,
        xscale = d3.scale.linear(),
        yscale = d3.scale.linear();

    // Plotting functions.
    var elements = [];

    // The XKCD object itself.
    var xkcd = function (nm) {
        el = d3.select(nm).append("svg")
                    .attr("width", width + 2 * margin)
                    .attr("height", height + 2 * margin)
                .append("g")
                    .attr("transform", "translate(" + margin + ", "
                                                    + margin + ")");
        return xkcd;
    };

    // Getters and setters.
    xkcd.xlim = function () {
        if (!arguments.length) return xlim;
        xlim = arguments[0];
        return xkcd;
    };

    // Do the render.
    xkcd.draw = function () {
        // Set the axes limits.
        xscale.domain(xlim).range([0, width]);
        yscale.domain(ylim).range([height, 0]);

        // Compute the zero points where the axes will be drawn.
        var x0 = xscale(0),
            y0 = yscale(0);

        // Draw the axes.
        var axis = d3.svg.line().interpolate(xinterp);
        el.selectAll(".axis").remove();
        el.append("svg:path")
            .attr("class", "x axis")
            .attr("d", axis([[0, y0], [width, y0]]));
        el.append("svg:path")
            .attr("class", "y axis")
            .attr("d", axis([[x0, 0], [x0, height]]));

        // Laboriously draw some arrows at the ends of the axes.
        var aa = arrowAspect * arrowSize,
            o = arrowOffset,
            s = arrowSize;
        el.append("svg:path")
            .attr("class", "x axis arrow")
            .attr("d", axis([[width - s + o, y0 + aa], [width + o, y0], [width - s + o, y0 - aa]]));
        el.append("svg:path")
            .attr("class", "x axis arrow")
            .attr("d", axis([[s - o, y0 + aa], [-o, y0], [s - o, y0 - aa]]));
        el.append("svg:path")
            .attr("class", "y axis arrow")
            .attr("d", axis([[x0 + aa, s - o], [x0, -o], [x0 - aa, s - o]]));
        el.append("svg:path")
            .attr("class", "y axis arrow")
            .attr("d", axis([[x0 + aa, height - s + o], [x0, height + o], [x0 - aa, height - s + o]]));

        for (var i = 0, l = elements.length; i < l; ++i) {
            var e = elements[i];
            e.func(e.data, e.x, e.y, e.opts);
        }

        // Add some axes labels.
        el.append("text").attr("class", "x label")
                              .attr("text-anchor", "end")
                              .attr("x", width - s)
                              .attr("y", y0 + aa)
                              .attr("dy", ".75em")
                              .text(xlabel);
        el.append("text").attr("class", "y label")
                              .attr("text-anchor", "end")
                              .attr("x", aa)
                              .attr("y", x0)
                              .attr("dy", "-.75em")
                              .attr("transform", "rotate(-90)")
                              .text(ylabel);

        // And a title.
        el.append("text").attr("class", "title")
                              .attr("text-anchor", "end")
                              .attr("x", width)
                              .attr("y", 0)
                              .text(title);

        return xkcd;
    };

    // Adding plot elements.
    xkcd.plot = function (data, opts) {
        var x = function (d) { return d.x; },
            y = function (d) { return d.y; },
            cx = function (d) { return xscale(x(d)); },
            cy = function (d) { return yscale(y(d)); },
            xl = d3.extent(data, x),
            yl = d3.extent(data, y);

        // Rescale the axes.
        xlim = xlim || xl;
        xlim[0] = Math.min(xlim[0], xl[0]);
        xlim[1] = Math.max(xlim[1], xl[1]);
        ylim = ylim || yl;
        ylim[0] = Math.min(ylim[0], yl[0]);
        ylim[1] = Math.max(ylim[1], yl[1]);

        // Add the plotting function.
        elements.push({
                        data: data,
                        func: lineplot,
                        x: cx,
                        y: cy,
                        opts: opts
                      });

        return xkcd;
    };

    // Plot styles.
    function lineplot(data, x, y, opts) {
        var line = d3.svg.line().x(x).y(y).interpolate(xinterp),
            bgline = d3.svg.line().x(x).y(y),
            strokeWidth = _get(opts, "stroke-width", 3),
            color = _get(opts, "stroke", "steelblue");
        el.append("svg:path").attr("d", bgline(data))
                             .style("stroke", "white")
                             .style("stroke-width", 2 * strokeWidth + "px")
                             .style("fill", "none")
                             .attr("class", "bgline");
        el.append("svg:path").attr("d", line(data))
                             .style("stroke", color)
                             .style("stroke-width", strokeWidth + "px")
                             .style("fill", "none");
    };

    // XKCD-style line interpolation. Roughly based on:
    //    jakevdp.github.com/blog/2012/10/07/xkcd-style-plots-in-matplotlib
    function xinterp (points) {
        // Scale the data.
        var f = [xscale(xlim[1]) - xscale(xlim[0]),
                 yscale(ylim[1]) - yscale(ylim[0])],
            z = [xscale(xlim[0]),
                 yscale(ylim[0])],
            scaled = points.map(function (p) {
                return [(p[0] - z[0]) / f[0], (p[1] - z[1]) / f[1]];
            });

        // Compute the distance along the path using a map-reduce.
        var dists = scaled.map(function (d, i) {
            if (i == 0) return 0.0;
            var dx = d[0] - scaled[i - 1][0],
                dy = d[1] - scaled[i - 1][1];
            return Math.sqrt(dx * dx + dy * dy);
        }),
            dist = dists.reduce(function (curr, d) { return d + curr; }, 0.0);

        // Choose the number of interpolation points based on this distance.
        var N = Math.round(200 * dist);

        // Re-sample the line.
        var resampled = [];
        dists.map(function (d, i) {
            if (i == 0) return;
            var n = Math.max(3, Math.round(d / dist * N)),
                spline = d3.interpolate(scaled[i - 1][1], scaled[i][1]),
                delta = (scaled[i][0] - scaled[i - 1][0]) / (n - 1);
            for (var j = 0, x = scaled[i - 1][0]; j < n; ++j, x += delta)
                resampled.push([x, spline(j / (n - 1))]);
        });

        // Compute the gradients.
        var gradients = resampled.map(function (a, i, d) {
            if (i == 0) return [d[1][0] - d[0][0], d[1][1] - d[0][1]];
            if (i == resampled.length - 1)
                return [d[i][0] - d[i - 1][0], d[i][1] - d[i - 1][1]];
            return [0.5 * (d[i + 1][0] - d[i - 1][0]),
                    0.5 * (d[i + 1][1] - d[i - 1][1])];
        });

        // Normalize the gradient vectors to be unit vectors.
        gradients = gradients.map(function (d) {
            var len = Math.sqrt(d[0] * d[0] + d[1] * d[1]);
            return [d[0] / len, d[1] / len];
        });

        // Generate some perturbations.
        var perturbations = smooth(resampled.map(d3.random.normal()), 3);

        // Add in the perturbations and re-scale the re-sampled curve.
        var result = resampled.map(function (d, i) {
            var p = perturbations[i],
                g = gradients[i];
            return [(d[0] + magnitude * g[1] * p) * f[0] + z[0],
                    (d[1] - magnitude * g[0] * p) * f[1] + z[1]];
        });

        return result.join("L");
    }

    // Smooth some data with a given window size.
    function smooth(d, w) {
        var result = [];
        for (var i = 0, l = d.length; i < l; ++i) {
            var mn = Math.max(0, i - 5 * w),
                mx = Math.min(d.length - 1, i + 5 * w),
                s = 0.0;
            result[i] = 0.0;
            for (var j = mn; j < mx; ++j) {
                var wd = Math.exp(-0.5 * (i - j) * (i - j) / w / w);
                result[i] += wd * d[j];
                s += wd;
            }
            result[i] /= s;
        }
        return result;
    }

    // Get a value from an object or return a default if that doesn't work.
    function _get(d, k, def) {
        if (typeof d === "undefined") return def;
        if (typeof d[k] === "undefined") return def;
        return d[k];
    }

    return xkcd;

}
<commit_msg>Move all params to a 'config' option so we can generate getter/setters for them<commit_after>var d3 = require("d3");

module.exports = function xkcdplot() {

    // Default parameters.
    var config = {
        width: 600,
        height: 300,
        margin: 20,
        arrowSize: 12,
        arrowAspect: 0.4,
        arrowOffset: 6,
        magnitude: 0.003,
        xlabel: "Time of Day",
        ylabel: "Awesomeness",
        title: "The most important graph ever made",
        xlim: null,
        ylim: null
    };

    // Plot elements.
    var el,
        xscale = d3.scale.linear(),
        yscale = d3.scale.linear();

    // Plotting functions.
    var elements = [];

    // The XKCD object itself.
    var xkcd = function (nm) {
        el = d3.select(nm).append("svg")
                    .attr("width", config.width + 2 * config.margin)
                    .attr("height", config.height + 2 * config.margin)
                .append("g")
                    .attr("transform", "translate(" + config.margin + ", "
                                                    + config.margin + ")");
        return xkcd;
    };

    // Getters and setters.
    function makeGetterSetter(name) {
        xkcd[name] = function (value) {
            if (arguments.length === 0) {
                return config[name];
            }
            config[name] = value;
            return xkcd;
        };
    }
    for (var name in config) {
        makeGetterSetter(name);
    }

    // Do the render.
    xkcd.draw = function () {
        // Set the axes limits.
        xscale.domain(config.xlim).range([0, config.width]);
        yscale.domain(config.ylim).range([config.height, 0]);

        // Compute the zero points where the axes will be drawn.
        var x0 = xscale(0),
            y0 = yscale(0);

        // Draw the axes.
        var axis = d3.svg.line().interpolate(xinterp);
        el.selectAll(".axis").remove();
        el.append("svg:path")
            .attr("class", "x axis")
            .attr("d", axis([[0, y0], [config.width, y0]]));
        el.append("svg:path")
            .attr("class", "y axis")
            .attr("d", axis([[x0, 0], [x0, config.height]]));

        // Laboriously draw some arrows at the ends of the axes.
        var aa = config.arrowAspect * config.arrowSize,
            o = config.arrowOffset,
            s = config.arrowSize;
        el.append("svg:path")
            .attr("class", "x axis arrow")
            .attr("d", axis([[config.width - s + o, y0 + aa], [config.width + o, y0], [config.width - s + o, y0 - aa]]));
        el.append("svg:path")
            .attr("class", "x axis arrow")
            .attr("d", axis([[s - o, y0 + aa], [-o, y0], [s - o, y0 - aa]]));
        el.append("svg:path")
            .attr("class", "y axis arrow")
            .attr("d", axis([[x0 + aa, s - o], [x0, -o], [x0 - aa, s - o]]));
        el.append("svg:path")
            .attr("class", "y axis arrow")
            .attr("d", axis([[x0 + aa, config.height - s + o], [x0, config.height + o], [x0 - aa, config.height - s + o]]));

        for (var i = 0, l = elements.length; i < l; ++i) {
            var e = elements[i];
            e.func(e.data, e.x, e.y, e.opts);
        }

        // Add some axes labels.
        el.append("text").attr("class", "x label")
                              .attr("text-anchor", "end")
                              .attr("x", config.width - s)
                              .attr("y", y0 + aa)
                              .attr("dy", ".75em")
                              .text(config.xlabel);
        el.append("text").attr("class", "y label")
                              .attr("text-anchor", "end")
                              .attr("x", aa)
                              .attr("y", x0)
                              .attr("dy", "-.75em")
                              .attr("transform", "rotate(-90)")
                              .text(config.ylabel);

        // And a title.
        el.append("text").attr("class", "title")
                              .attr("text-anchor", "end")
                              .attr("x", config.width)
                              .attr("y", 0)
                              .text(config.title);

        return xkcd;
    };

    // Adding plot elements.
    xkcd.plot = function (data, opts) {
        var x = function (d) { return d.x; },
            y = function (d) { return d.y; },
            cx = function (d) { return xscale(x(d)); },
            cy = function (d) { return yscale(y(d)); },
            xl = d3.extent(data, x),
            yl = d3.extent(data, y);

        // Rescale the axes.
        config.xlim = config.xlim || xl;
        config.xlim[0] = Math.min(config.xlim[0], xl[0]);
        config.xlim[1] = Math.max(config.xlim[1], xl[1]);
        config.ylim = config.ylim || yl;
        config.ylim[0] = Math.min(config.ylim[0], yl[0]);
        config.ylim[1] = Math.max(config.ylim[1], yl[1]);

        // Add the plotting function.
        elements.push({
                        data: data,
                        func: lineplot,
                        x: cx,
                        y: cy,
                        opts: opts
                      });

        return xkcd;
    };

    // Plot styles.
    function lineplot(data, x, y, opts) {
        var line = d3.svg.line().x(x).y(y).interpolate(xinterp),
            bgline = d3.svg.line().x(x).y(y),
            strokeWidth = _get(opts, "stroke-width", 3),
            color = _get(opts, "stroke", "steelblue");
        el.append("svg:path").attr("d", bgline(data))
                             .style("stroke", "white")
                             .style("stroke-width", 2 * strokeWidth + "px")
                             .style("fill", "none")
                             .attr("class", "bgline");
        el.append("svg:path").attr("d", line(data))
                             .style("stroke", color)
                             .style("stroke-width", strokeWidth + "px")
                             .style("fill", "none");
    };

    // XKCD-style line interpolation. Roughly based on:
    //    jakevdp.github.com/blog/2012/10/07/xkcd-style-plots-in-matplotlib
    function xinterp (points) {
        // Scale the data.
        var f = [xscale(config.xlim[1]) - xscale(config.xlim[0]),
                 yscale(config.ylim[1]) - yscale(config.ylim[0])],
            z = [xscale(config.xlim[0]),
                 yscale(config.ylim[0])],
            scaled = points.map(function (p) {
                return [(p[0] - z[0]) / f[0], (p[1] - z[1]) / f[1]];
            });

        // Compute the distance along the path using a map-reduce.
        var dists = scaled.map(function (d, i) {
            if (i == 0) return 0.0;
            var dx = d[0] - scaled[i - 1][0],
                dy = d[1] - scaled[i - 1][1];
            return Math.sqrt(dx * dx + dy * dy);
        }),
            dist = dists.reduce(function (curr, d) { return d + curr; }, 0.0);

        // Choose the number of interpolation points based on this distance.
        var N = Math.round(200 * dist);

        // Re-sample the line.
        var resampled = [];
        dists.map(function (d, i) {
            if (i == 0) return;
            var n = Math.max(3, Math.round(d / dist * N)),
                spline = d3.interpolate(scaled[i - 1][1], scaled[i][1]),
                delta = (scaled[i][0] - scaled[i - 1][0]) / (n - 1);
            for (var j = 0, x = scaled[i - 1][0]; j < n; ++j, x += delta)
                resampled.push([x, spline(j / (n - 1))]);
        });

        // Compute the gradients.
        var gradients = resampled.map(function (a, i, d) {
            if (i == 0) return [d[1][0] - d[0][0], d[1][1] - d[0][1]];
            if (i == resampled.length - 1)
                return [d[i][0] - d[i - 1][0], d[i][1] - d[i - 1][1]];
            return [0.5 * (d[i + 1][0] - d[i - 1][0]),
                    0.5 * (d[i + 1][1] - d[i - 1][1])];
        });

        // Normalize the gradient vectors to be unit vectors.
        gradients = gradients.map(function (d) {
            var len = Math.sqrt(d[0] * d[0] + d[1] * d[1]);
            return [d[0] / len, d[1] / len];
        });

        // Generate some perturbations.
        var perturbations = smooth(resampled.map(d3.random.normal()), 3);

        // Add in the perturbations and re-scale the re-sampled curve.
        var result = resampled.map(function (d, i) {
            var p = perturbations[i],
                g = gradients[i];
            return [(d[0] + config.magnitude * g[1] * p) * f[0] + z[0],
                    (d[1] - config.magnitude * g[0] * p) * f[1] + z[1]];
        });

        return result.join("L");
    }

    // Smooth some data with a given window size.
    function smooth(d, w) {
        var result = [];
        for (var i = 0, l = d.length; i < l; ++i) {
            var mn = Math.max(0, i - 5 * w),
                mx = Math.min(d.length - 1, i + 5 * w),
                s = 0.0;
            result[i] = 0.0;
            for (var j = mn; j < mx; ++j) {
                var wd = Math.exp(-0.5 * (i - j) * (i - j) / w / w);
                result[i] += wd * d[j];
                s += wd;
            }
            result[i] /= s;
        }
        return result;
    }

    // Get a value from an object or return a default if that doesn't work.
    function _get(d, k, def) {
        if (typeof d === "undefined") return def;
        if (typeof d[k] === "undefined") return def;
        return d[k];
    }

    return xkcd;

}
<|endoftext|>


<commit_before>// YAML.node, © 2011 Stéphan Kochen
// MIT-licensed. (See the included LICENSE file.)

var fs = require('fs'),
    EventEmitter = require('events').EventEmitter,
    binding = require('./build/Release/binding');

// Low-level YAML stream exports.
exports.stream = {};

// Create a raw event stream from YAML input.
//
//     var output = yaml.stream.parse(input, handler);
//
// The handler can be an object that exposes methods for each LibYAML parser event. These are
// named `onScalar`, `onSequenceStart`, etc. All of these methods take an event object that is
// similar in structure to a flattened `yaml_event_t`.
//
// Alternatively, a single function can be passed in to handle all events.
exports.stream.parse = function(input, handler) {
  if (typeof(handler) !== 'function') {
    var orig = handler;
    handler = function(event) {
      var type = event.type;
      var method = 'on' + type.charAt(0).toUpperCase() + type.slice(1);
      if (typeof(orig[method]) === 'function')
        orig[method](event);
    };
  }

  binding.parse(input, handler);
};

// Create a YAML data stream from raw events.
//
//     var emitter = yaml.stream.createEmitter(function(data) { /* ... */ });
//     emitter.stream(function() {
//       emitter.document(function() {
//         emitter.scalar("foobar");
//       });
//     });
//
// YAML stream events are exposed as methods on the emitter, e.g. 'streamStart', 'scalar', etc.
// Additionally, events that come in start/end pairs are exposed as single methods taking a
// function to wrap with the start and end events.
//
// As libYAML produces output, `data` events are emitted. The `createEmitter` factory can take
// a function argument which is immediately installed as a `data` event listener.
var YamlStreamEmitter = function() {
  var callback = this.emit.bind(this, 'data');
  this.emitter_ = new binding.Emitter(callback);
};
YamlStreamEmitter.prototype = new EventEmitter();

YamlStreamEmitter.prototype.event = function(obj) {
  if (typeof(obj) === 'string')
    obj = { type: obj };
  this.emitter_.event(obj);
};

['stream', 'document', 'sequence', 'mapping'].forEach(function(pre) {
  var startEvent = pre + 'Start';
  YamlStreamEmitter.prototype[startEvent] = function() {
    this.emitter_.event({ type: startEvent });
  };

  var endEvent = pre + 'End';
  YamlStreamEmitter.prototype[endEvent] = function() {
    this.emitter_.event({ type: endEvent });
  };

  YamlStreamEmitter.prototype[pre] = function(block) {
    this.emitter_.event({ type: startEvent });
    var result = block();
    this.emitter_.event({ type: endEvent });
    return result;
  };
});

YamlStreamEmitter.prototype.alias = function(anchor) {
  this.emitter_.event({ type: 'alias', anchor: anchor });
};

YamlStreamEmitter.prototype.scalar = function(value) {
  this.emitter_.event({ type: 'scalar', value: value });
};

exports.stream.createEmitter = function(handler) {
  var result = new YamlStreamEmitter();
  if (handler) result.on('data', handler);
  return result;
};

// Most of these were derived from: http://yaml.org/type/
// Also borrows from tenderlove's `Psych::ScalarScanner`. (MIT-licensed)
var definitelyNonNumericRe = /^[a-z~]/i,
    definitelyNonBooleanRe = /^[^ytonf~]/i,
    canParseIntRe   = /^[-+]?(0x[0-9a-fA-F_]+|[\d_]+)$/,
    canParseFloatRe = /^[-+]?(\d[\d_]*)?\.[\d_]*(e[-+]\d+)?$/i,
    nullRe   = /^(~|null)$/i,
    trueRe   = /^(y|yes|true|on)$/i,
    falseRe  = /^(n|no|false|off)$/i,
    posInfRe = /^\+?\.inf$/i,
    negInfRe = /^-\.inf$/i,
    nanRe    = /^\.nan$/i,
    binRe    = /^([-+])?0b([01_]+)$/,
    timeRe   = /^([-+]?)([0-9][\d_]*(?::[0-5]?\d)+(?:\.[\d_]*)?)$/,
    dateRe   = /^\d{4}-\d\d?-\d\d?$/,
    timestampRe = /^(\d{4}-\d\d?-\d\d?(?:[Tt]|\s+)\d\d?:\d\d:\d\d(?:\.\d*)?)(?:\s*(Z|[-+]\d\d?(?::\d\d)?))?$/,
    underscoresRe = /_/g;

// Tiny helper to pad a string.
var pad = function(s, num, ch) {
  while (s.length < num) s = ch + s;
  return s;
};

// Helper function that converts a string scalar to its actual type.
var parseScalar = function(v) {
  if (!v) return null;

  // Simple keywords.
  if (definitelyNonNumericRe.test(v)) {
    if (v.length > 5 || definitelyNonBooleanRe.test(v)) return v;
    if (nullRe.test(v)) return null;
    if (trueRe.test(v)) return true;
    if (falseRe.test(v)) return false;
    return v;
  }
  if (posInfRe.test(v)) return  1/0;
  if (negInfRe.test(v)) return -1/0;
  if (nanRe.test(v))    return  0/0;

  // JavaScript's `parseInt` does not support binary numbers.
  var m = binRe.exec(v);
  if (m) {
    var s = m[2],
        length = s.length,
        result = 0;
    for (var i = 0; i < length; i++) {
      if (s[i] == '_')
        continue;
      result *= 2;
      if (s[i] == '1')
        result++;
    }
    if (m[1] == '-')
      result *= -1;
    return result;
  }

  // JavaScript's datetime parsing is subtly different from YAML's.
  var m = timestampRe.exec(v);
  if (m) {
    var dateTimePart = m[1].replace('t', 'T'),
        offset = 0;
    if (m[2] && m[2] !== 'Z') {
      var parts = m[2].split(':');
      offset = parseInt(parts[0], 10) * 100;
      if (parts.length == 2)
        offset += parseInt(parts[1], 10);
    }
    if (offset >= 0)
      offset = "+" + pad(String(offset), 4, '0');
    else
      offset = "-" + pad(String(Math.abs(offset)), 4, '0');
    return new Date(dateTimePart + offset);
  }
  if (dateRe.test(v))
    return new Date(v + "T00:00:00Z");

  // Regular numbers.
  if (canParseIntRe.test(v))
    return parseInt(v.replace(underscoresRe, ''), 0);
  if (canParseFloatRe.test(v))
    return parseFloat(v.replace(underscoresRe, ''));

  // Times.
  var m = timeRe.exec(v);
  if (m) {
    var parts = m[2].split(':'),
        length = parts.length,
        result = 0;
    for (var i = 0; i < length; i++) {
      result *= 60;
      if (length == i + 1)
        result += parseFloat(parts[i]);
      else
        result += parseInt(parts[i], 10);
    }
    if (m[1] == '-')
      result *= -1;
    return result;
  }

  return v;
};

// The `load` function reads all documents from the given string input. The return value is an
// array of documents found represented as plain JavaScript objects, arrays and primitives.
exports.load = function(input, tagHandlers) {
  if (typeof tagHandlers !== 'object')
    tagHandlers = {};

  var parserHandler, handlerStack = [];

  // Capture all values between two parser events. Because we can nest in YAML, we need a stack of
  // these value handlers, and we need to ensure we can deal with `until` blocks that are nested
  // for the same kinds of events (ie. restore the old event handler afterwards).
  var until = function(method, valueHandler) {
    var oldMethod = parserHandler[method];
    handlerStack.unshift(valueHandler);
    parserHandler[method] = function(e) {
      parserHandler[method] = oldMethod;
      var oldHandler = handlerStack.shift();
      oldHandler.after();
    };
  };

  // Dispatch a value. At this point, the value is a JavaScript primitive, ie. sequences are
  // `Array`s, numeric scalars are `Number`s, etc. If a tag was specified, the tag handler
  // function is asked to post process the value. The value is then sent to the current value
  // handler in the stack.
  var dispatch = function(e, value) {
    if (e.tag !== null) {
      var tagHandler = tagHandlers[e.tag];
      // FIXME: Deal with standard tags
      if (tagHandler)
        value = tagHandler(value);
    }
    handlerStack[0].handle(value);
  };

  // Call into the parser and build the documents.
  var documents = [];
  exports.stream.parse(input, parserHandler = {
    onDocumentEnd: null,
    onDocumentStart: function(e) {
      var document;
      until('onDocumentEnd', {
        handle: function(value) {
          document = value;
        },
        after: function() {
          documents.push(document);
        }
      });
    },

    onAlias: function(e) {
      // FIXME
    },

    onScalar: function(e) {
      dispatch(e, parseScalar(e.value));
    },

    onSequenceEnd: null,
    onSequenceStart: function(e) {
      var sequence = [];
      until('onSequenceEnd', {
        handle: function(value) {
          sequence.push(value);
        },
        after: function() {
          dispatch(e, sequence);
        }
      });
    },

    onMappingEnd: null,
    onMappingStart: function(e) {
      var mapping = {}, key = undefined;
      until('onMappingEnd', {
        handle: function(value) {
          if (key === undefined) {
            key = value;
          }
          else {
            mapping[key] = value;
            key = undefined;
          }
        },
        after: function() {
          dispatch(e, mapping);
        }
      });
    }
  });

  return documents;
};

// Helper for quickly reading in a file.
exports.loadFile = function(filename, tagHandlers, callback) {
  if (typeof tagHandlers === 'function') {
    callback = tagHandlers;
    tagHandlers = {};
  }

  fs.readFile(filename, 'utf-8', function(err, data) {
    if (err) callback(err, null);
    else callback(null, exports.load(data, tagHandlers));
  });
};

// Synchronous version of loadFile.
exports.loadFileSync = function(filename, tagHandlers) {
  var data = fs.readFileSync(filename, 'utf-8');
  return exports.load(data, tagHandlers);
};

// Allow direct requiring of YAML files.
require.extensions[".yaml"] = require.extensions[".yml"] = function (module) {
   module.exports = exports.loadFileSync(module.filename);
};

// Helper function that emits a serialized version of the given item.
var serialize = function(emitter, item) {
  // FIXME: throw on circulars
  switch (typeof item) {
    case "string":
      emitter.scalar(item);
      break;
    case "object":
      if (!item) {
        emitter.scalar('~');
      }
      else if (item instanceof Date) {
        emitter.scalar(item.toISOString());
      }
      else if (item.length) {
        emitter.sequence(function() {
          var length = item.length;
          for (var i = 0; i < length; i++)
            serialize(emitter, item[i]);
        });
      }
      else {
        emitter.mapping(function() {
          for (var p in item) {
            if (Object.hasOwnProperty.call(item, p)) {
              emitter.scalar(p);
              serialize(emitter, item[p]);
            }
          }
        });
      }
      break;
    case "number":
      if (item === Infinity)
        emitter.scalar(".inf");
      else if (item === -Infinity)
        emitter.scalar("-.inf");
      else if (isNaN(item))
        emitter.scalar(".NaN");
      else
        emitter.scalar(String(item));
      break;
    default:
      emitter.scalar(String(item));
      break;
  }
};

// The `dump` function serializes its arguments to YAML. Any number of arguments may be provided,
// and the arguments should be plain JavaScript objects, arrays or primitives. Each argument is
// treated as a single document to serialize. The return value is a string.
exports.dump = function() {
  var documents = arguments, chunks = [], emitter;
  emitter = new exports.stream.createEmitter(function(chunk) {
    chunks.push(chunk);
  });
  emitter.stream(function() {
    var length = documents.length;
    for (var i = 0; i < length; i++) {
      var document = documents[i];
      emitter.document(function() {
        serialize(emitter, document);
      });
    }
  });
  return chunks.join('');
};

// Helper for quickly writing out a file.
exports.dumpFile = function(filename) {
  var documents = Array.prototype.slice.call(arguments, 1),
      numDocuments = documents.length,
      callback;
  if (numDocuments !== 0 && typeof documents[numDocuments - 1] === 'function')
    callback = documents.pop();

  var data = exports.dump.apply(this, documents);
  fs.writeFile(filename, data, callback);
};

// Synchronous version of dumpFile.
exports.dumpFileSync = function(filename) {
  var documents = Array.prototype.slice.call(arguments, 1);

  var data = exports.dump.apply(this, documents);
  fs.writeFileSync(filename, data);
};
<commit_msg>Use util.inherits.<commit_after>// YAML.node, © 2011 Stéphan Kochen
// MIT-licensed. (See the included LICENSE file.)

var fs = require('fs'),
    util = require('util'),
    events = require('events'),
    binding = require('./build/Release/binding');

// Low-level YAML stream exports.
exports.stream = {};

// Create a raw event stream from YAML input.
//
//     var output = yaml.stream.parse(input, handler);
//
// The handler can be an object that exposes methods for each LibYAML parser event. These are
// named `onScalar`, `onSequenceStart`, etc. All of these methods take an event object that is
// similar in structure to a flattened `yaml_event_t`.
//
// Alternatively, a single function can be passed in to handle all events.
exports.stream.parse = function(input, handler) {
  if (typeof(handler) !== 'function') {
    var orig = handler;
    handler = function(event) {
      var type = event.type;
      var method = 'on' + type.charAt(0).toUpperCase() + type.slice(1);
      if (typeof(orig[method]) === 'function')
        orig[method](event);
    };
  }

  binding.parse(input, handler);
};

// Create a YAML data stream from raw events.
//
//     var emitter = yaml.stream.createEmitter(function(data) { /* ... */ });
//     emitter.stream(function() {
//       emitter.document(function() {
//         emitter.scalar("foobar");
//       });
//     });
//
// YAML stream events are exposed as methods on the emitter, e.g. 'streamStart', 'scalar', etc.
// Additionally, events that come in start/end pairs are exposed as single methods taking a
// function to wrap with the start and end events.
//
// As libYAML produces output, `data` events are emitted. The `createEmitter` factory can take
// a function argument which is immediately installed as a `data` event listener.
var YamlStreamEmitter = function() {
  var callback = this.emit.bind(this, 'data');
  this.emitter_ = new binding.Emitter(callback);
};
util.inherits(YamlStreamEmitter, events.EventEmitter);

YamlStreamEmitter.prototype.event = function(obj) {
  if (typeof(obj) === 'string')
    obj = { type: obj };
  this.emitter_.event(obj);
};

['stream', 'document', 'sequence', 'mapping'].forEach(function(pre) {
  var startEvent = pre + 'Start';
  YamlStreamEmitter.prototype[startEvent] = function() {
    this.emitter_.event({ type: startEvent });
  };

  var endEvent = pre + 'End';
  YamlStreamEmitter.prototype[endEvent] = function() {
    this.emitter_.event({ type: endEvent });
  };

  YamlStreamEmitter.prototype[pre] = function(block) {
    this.emitter_.event({ type: startEvent });
    var result = block();
    this.emitter_.event({ type: endEvent });
    return result;
  };
});

YamlStreamEmitter.prototype.alias = function(anchor) {
  this.emitter_.event({ type: 'alias', anchor: anchor });
};

YamlStreamEmitter.prototype.scalar = function(value) {
  this.emitter_.event({ type: 'scalar', value: value });
};

exports.stream.createEmitter = function(handler) {
  var result = new YamlStreamEmitter();
  if (handler) result.on('data', handler);
  return result;
};

// Most of these were derived from: http://yaml.org/type/
// Also borrows from tenderlove's `Psych::ScalarScanner`. (MIT-licensed)
var definitelyNonNumericRe = /^[a-z~]/i,
    definitelyNonBooleanRe = /^[^ytonf~]/i,
    canParseIntRe   = /^[-+]?(0x[0-9a-fA-F_]+|[\d_]+)$/,
    canParseFloatRe = /^[-+]?(\d[\d_]*)?\.[\d_]*(e[-+]\d+)?$/i,
    nullRe   = /^(~|null)$/i,
    trueRe   = /^(y|yes|true|on)$/i,
    falseRe  = /^(n|no|false|off)$/i,
    posInfRe = /^\+?\.inf$/i,
    negInfRe = /^-\.inf$/i,
    nanRe    = /^\.nan$/i,
    binRe    = /^([-+])?0b([01_]+)$/,
    timeRe   = /^([-+]?)([0-9][\d_]*(?::[0-5]?\d)+(?:\.[\d_]*)?)$/,
    dateRe   = /^\d{4}-\d\d?-\d\d?$/,
    timestampRe = /^(\d{4}-\d\d?-\d\d?(?:[Tt]|\s+)\d\d?:\d\d:\d\d(?:\.\d*)?)(?:\s*(Z|[-+]\d\d?(?::\d\d)?))?$/,
    underscoresRe = /_/g;

// Tiny helper to pad a string.
var pad = function(s, num, ch) {
  while (s.length < num) s = ch + s;
  return s;
};

// Helper function that converts a string scalar to its actual type.
var parseScalar = function(v) {
  if (!v) return null;

  // Simple keywords.
  if (definitelyNonNumericRe.test(v)) {
    if (v.length > 5 || definitelyNonBooleanRe.test(v)) return v;
    if (nullRe.test(v)) return null;
    if (trueRe.test(v)) return true;
    if (falseRe.test(v)) return false;
    return v;
  }
  if (posInfRe.test(v)) return  1/0;
  if (negInfRe.test(v)) return -1/0;
  if (nanRe.test(v))    return  0/0;

  // JavaScript's `parseInt` does not support binary numbers.
  var m = binRe.exec(v);
  if (m) {
    var s = m[2],
        length = s.length,
        result = 0;
    for (var i = 0; i < length; i++) {
      if (s[i] == '_')
        continue;
      result *= 2;
      if (s[i] == '1')
        result++;
    }
    if (m[1] == '-')
      result *= -1;
    return result;
  }

  // JavaScript's datetime parsing is subtly different from YAML's.
  var m = timestampRe.exec(v);
  if (m) {
    var dateTimePart = m[1].replace('t', 'T'),
        offset = 0;
    if (m[2] && m[2] !== 'Z') {
      var parts = m[2].split(':');
      offset = parseInt(parts[0], 10) * 100;
      if (parts.length == 2)
        offset += parseInt(parts[1], 10);
    }
    if (offset >= 0)
      offset = "+" + pad(String(offset), 4, '0');
    else
      offset = "-" + pad(String(Math.abs(offset)), 4, '0');
    return new Date(dateTimePart + offset);
  }
  if (dateRe.test(v))
    return new Date(v + "T00:00:00Z");

  // Regular numbers.
  if (canParseIntRe.test(v))
    return parseInt(v.replace(underscoresRe, ''), 0);
  if (canParseFloatRe.test(v))
    return parseFloat(v.replace(underscoresRe, ''));

  // Times.
  var m = timeRe.exec(v);
  if (m) {
    var parts = m[2].split(':'),
        length = parts.length,
        result = 0;
    for (var i = 0; i < length; i++) {
      result *= 60;
      if (length == i + 1)
        result += parseFloat(parts[i]);
      else
        result += parseInt(parts[i], 10);
    }
    if (m[1] == '-')
      result *= -1;
    return result;
  }

  return v;
};

// The `load` function reads all documents from the given string input. The return value is an
// array of documents found represented as plain JavaScript objects, arrays and primitives.
exports.load = function(input, tagHandlers) {
  if (typeof tagHandlers !== 'object')
    tagHandlers = {};

  var parserHandler, handlerStack = [];

  // Capture all values between two parser events. Because we can nest in YAML, we need a stack of
  // these value handlers, and we need to ensure we can deal with `until` blocks that are nested
  // for the same kinds of events (ie. restore the old event handler afterwards).
  var until = function(method, valueHandler) {
    var oldMethod = parserHandler[method];
    handlerStack.unshift(valueHandler);
    parserHandler[method] = function(e) {
      parserHandler[method] = oldMethod;
      var oldHandler = handlerStack.shift();
      oldHandler.after();
    };
  };

  // Dispatch a value. At this point, the value is a JavaScript primitive, ie. sequences are
  // `Array`s, numeric scalars are `Number`s, etc. If a tag was specified, the tag handler
  // function is asked to post process the value. The value is then sent to the current value
  // handler in the stack.
  var dispatch = function(e, value) {
    if (e.tag !== null) {
      var tagHandler = tagHandlers[e.tag];
      // FIXME: Deal with standard tags
      if (tagHandler)
        value = tagHandler(value);
    }
    handlerStack[0].handle(value);
  };

  // Call into the parser and build the documents.
  var documents = [];
  exports.stream.parse(input, parserHandler = {
    onDocumentEnd: null,
    onDocumentStart: function(e) {
      var document;
      until('onDocumentEnd', {
        handle: function(value) {
          document = value;
        },
        after: function() {
          documents.push(document);
        }
      });
    },

    onAlias: function(e) {
      // FIXME
    },

    onScalar: function(e) {
      dispatch(e, parseScalar(e.value));
    },

    onSequenceEnd: null,
    onSequenceStart: function(e) {
      var sequence = [];
      until('onSequenceEnd', {
        handle: function(value) {
          sequence.push(value);
        },
        after: function() {
          dispatch(e, sequence);
        }
      });
    },

    onMappingEnd: null,
    onMappingStart: function(e) {
      var mapping = {}, key = undefined;
      until('onMappingEnd', {
        handle: function(value) {
          if (key === undefined) {
            key = value;
          }
          else {
            mapping[key] = value;
            key = undefined;
          }
        },
        after: function() {
          dispatch(e, mapping);
        }
      });
    }
  });

  return documents;
};

// Helper for quickly reading in a file.
exports.loadFile = function(filename, tagHandlers, callback) {
  if (typeof tagHandlers === 'function') {
    callback = tagHandlers;
    tagHandlers = {};
  }

  fs.readFile(filename, 'utf-8', function(err, data) {
    if (err) callback(err, null);
    else callback(null, exports.load(data, tagHandlers));
  });
};

// Synchronous version of loadFile.
exports.loadFileSync = function(filename, tagHandlers) {
  var data = fs.readFileSync(filename, 'utf-8');
  return exports.load(data, tagHandlers);
};

// Allow direct requiring of YAML files.
require.extensions[".yaml"] = require.extensions[".yml"] = function (module) {
   module.exports = exports.loadFileSync(module.filename);
};

// Helper function that emits a serialized version of the given item.
var serialize = function(emitter, item) {
  // FIXME: throw on circulars
  switch (typeof item) {
    case "string":
      emitter.scalar(item);
      break;
    case "object":
      if (!item) {
        emitter.scalar('~');
      }
      else if (item instanceof Date) {
        emitter.scalar(item.toISOString());
      }
      else if (item.length) {
        emitter.sequence(function() {
          var length = item.length;
          for (var i = 0; i < length; i++)
            serialize(emitter, item[i]);
        });
      }
      else {
        emitter.mapping(function() {
          for (var p in item) {
            if (Object.hasOwnProperty.call(item, p)) {
              emitter.scalar(p);
              serialize(emitter, item[p]);
            }
          }
        });
      }
      break;
    case "number":
      if (item === Infinity)
        emitter.scalar(".inf");
      else if (item === -Infinity)
        emitter.scalar("-.inf");
      else if (isNaN(item))
        emitter.scalar(".NaN");
      else
        emitter.scalar(String(item));
      break;
    default:
      emitter.scalar(String(item));
      break;
  }
};

// The `dump` function serializes its arguments to YAML. Any number of arguments may be provided,
// and the arguments should be plain JavaScript objects, arrays or primitives. Each argument is
// treated as a single document to serialize. The return value is a string.
exports.dump = function() {
  var documents = arguments, chunks = [], emitter;
  emitter = new exports.stream.createEmitter(function(chunk) {
    chunks.push(chunk);
  });
  emitter.stream(function() {
    var length = documents.length;
    for (var i = 0; i < length; i++) {
      var document = documents[i];
      emitter.document(function() {
        serialize(emitter, document);
      });
    }
  });
  return chunks.join('');
};

// Helper for quickly writing out a file.
exports.dumpFile = function(filename) {
  var documents = Array.prototype.slice.call(arguments, 1),
      numDocuments = documents.length,
      callback;
  if (numDocuments !== 0 && typeof documents[numDocuments - 1] === 'function')
    callback = documents.pop();

  var data = exports.dump.apply(this, documents);
  fs.writeFile(filename, data, callback);
};

// Synchronous version of dumpFile.
exports.dumpFileSync = function(filename) {
  var documents = Array.prototype.slice.call(arguments, 1);

  var data = exports.dump.apply(this, documents);
  fs.writeFileSync(filename, data);
};
<|endoftext|>


<commit_before>/* jshint onevar:false */

'use strict';

var async = require('async');
var opn = require('opn');
var yo = require('yeoman-generator');
var yosay = require('yosay');
var util = require('util');
var path = require('path');
var updateNotifier = require('update-notifier');
var chalk = require('chalk');
var findup = require('findup');
var fullname = require('fullname');

// The `yo yo` generator provides users with a few common, helpful commands.
var yoyo = module.exports = function yoyo(args, options) {
  yo.Base.apply(this, arguments);
  this.insight = options.insight;

  this.insight.track('yoyo', 'init');
  process.once('exit', this._exit.bind(this));
};

util.inherits(yoyo, yo.Base);

// Runs parallel `npm update -g`s for each selected generator.
yoyo.prototype._updateGenerators = function _updateGenerators(pkgs) {
  var self = this;

  var resolveGenerators = function (pkg) {
    return function (next) {
      self.spawnCommand('npm', ['update', '-g', pkg])
        .on('error', next)
        .on('exit', next);
    };
  };

  self.insight.track('yoyo', 'update');
  async.parallel(self._.map(pkgs, resolveGenerators), function (err) {
    if (err) {
      self.insight.track('yoyo:err', 'update');
      return self.emit('error', err);
    }

    self.insight.track('yoyo', 'updated');
    self.home({
      refresh: true,
      message:
        'I\'ve just updated your generators. Remember, you can update' +
        '\na specific generator with npm by running:\n' +
        chalk.magenta('\n    npm update -g generator-_______')
    });
  });
};

// Prompts the user to select which generators to update
yoyo.prototype._promptToUpdateGenerators = function _promptToUpdateGenerators() {
  this.prompt([{
    name: '_updateSelectedGenerators',
    message: 'Generators to update',
    type: 'checkbox',
    choices: this._.map(this.pkgs, function (generator) {
      return {name: generator.name, checked: true};
    })
  }], function (answer) {
    this._updateGenerators.call(this, answer._updateSelectedGenerators);
  }.bind(this));
};

// Initializes a generator.
//
// - generator - (string) The generator to initialize.
yoyo.prototype._initGenerator = function _initGenerator(generator, done) {
  console.log(
    chalk.yellow('\nMake sure you are in the directory you want to scaffold into.\n') +
    'This generator can also be run with: ' +
    chalk.blue.bold('yo ' + generator.split(':')[0])
  );

  this.insight.track('yoyo', 'run', generator);
  this.composeWith(generator);
  done();
};

// Serves as the response prompt for "Install a generator" as well as simply
// installs a generator if a string is passed in.
//
// - pkgName - (optional) A string that matches the NPM package name.
yoyo.prototype._installGenerator = function _installGenerator(pkgName) {
  if (this._.isString(pkgName)) {
    this.insight.track('yoyo', 'install', pkgName);

    // We know what generator we want to install
    return this.spawnCommand('npm', ['install', '-g', pkgName])
      .on('error', function (err) {
        this.insight.track('yoyo:err', 'install', pkgName);
        this.emit('error', err);
      }.bind(this))
      .on('exit', function () {
        this.insight.track('yoyo', 'installed', pkgName);
        this.home({
          refresh: true,
          message:
            '\nI just installed your generator by running:\n' +
            chalk.blue.bold('\n    npm install -g ' + pkgName)
        });
      }.bind(this));
  }

  this.insight.track('yoyo', 'install');

  this.prompt([{
    name: 'searchTerm',
    message: 'Search NPM for generators'
  }], this._searchNpm.bind(this));
};

// Grabs all of the packages with a `yeoman-generator` keyword on NPM.
//
// - term - (object) Contains the search term & gets passed back to callback().
// - cb   - Callback to execute once generators have been found.
yoyo.prototype._findAllNpmGenerators = function _findAllNpmGenerators(term, cb) {
  var url = 'http://isaacs.iriscouch.com/registry/_design/' +
    'app/_view/byKeyword?startkey=[%22yeoman-generator%22]' +
    '&endkey=[%22yeoman-generator%22,{}]&group_level=3';

  this.request(url, function (err, res, body) {
    if (err) {
      return this.emit('error', err);
    }

    try {
      this.npmGenerators = JSON.parse(body);
    } catch (err) {
      return this.emit('error', new Error(chalk.bold(
        'A problem occurred contacting the registry.' +
        '\nUnable to parse response: not valid JSON.'
      )));
    }

    cb(term);
  }.bind(this));
};

// Takes a search term, looks it up in the registry, prompts the user with the
// results, allowing them to choose to install it, or go back home.
//
// - term - Object with a 'searchTerm' property containing the term to search
//          NPM for.
yoyo.prototype._searchNpm = function _searchNpm(term) {
  if (!this.npmGenerators) {
    return this._findAllNpmGenerators(term, this._searchNpm.bind(this));
  }

  // Find any matches from NPM.
  var choices = this._.chain(this.npmGenerators.rows).map(function (generator) {
    // Make sure it's not already installed.
    if (
      !this.pkgs[generator.key[1]] &&
      generator.key.join(' ').indexOf(term.searchTerm) > -1
    ) {
      return {
        name: generator.key[1],
        value: generator.key[1]
      };
    }
  }.bind(this)).compact().value();

  var resultsPrompt = [{
    name: '_installGenerator',
    type: 'list',
    message: choices.length > 0 ?
      'Here\'s what I found. Install one?' : 'Sorry, nothing was found',
    choices: this._.union(choices, [{
      name: 'Search again',
      value: '_installGenerator'
    }, {
      name: 'Return home',
      value: 'home'
    }])
  }];

  this.prompt(resultsPrompt, function (answer) {
    if (this._.isFunction(this[answer._installGenerator])) {
      return this[answer._installGenerator]();
    }

    this._installGenerator(answer._installGenerator);
  }.bind(this));
};

// Prompts user with a few helpful resources, then opens it in their browser.
yoyo.prototype._findHelp = function _findHelp() {
  this.insight.track('yoyo', 'help');
  this.prompt([{
    name: 'whereTo',
    type: 'list',
    message:
      'Here are a few helpful resources.\n' +
      '\nI will open the link you select in your browser for you',
    choices: [{
      name: 'Take me to the documentation',
      value: 'http://yeoman.io/learning/index.html'
    }, {
      name: 'View Frequently Asked Questions',
      value: 'http://yeoman.io/learning/faq.html'
    }, {
      name: 'File an issue on GitHub',
      value: 'http://yeoman.io/contributing/opening-issues.html'
    }, {
      name: 'Take me back home, Yo!',
      value: {
        method: 'home',
        args: {
          message: 'I get it, you like learning on your own. I respect that.'
        }
      }
    }]
  }], function (answer) {
    this.insight.track('yoyo', 'help', answer);

    if (this._.isFunction(this[answer.whereTo.method])) {
      this[answer.whereTo.method](answer.whereTo.args);
    } else {
      opn(answer.whereTo);
    }
  }.bind(this));
};

// Serves as a quick escape from the `yo yo` prompts.
yoyo.prototype._exit = function _exit() {
  this.insight.track('yoyo', 'exit');

  var url = 'https://github.com/yeoman/yeoman#team';
  var maxLength = url.length;
  var newLine = new Array(maxLength).join(' ');

  console.log(
    '\n' +
    yosay(
      'Bye from us! Chat soon.' +
      newLine +
      newLine +
      'The Yeoman Team ' + url,
      { maxLength: maxLength }
    )
  );
};

// I'm sorry...
yoyo.prototype._noop = function _noop() {};

// Rolls through all of the generators provided by `env.generators`, finding
// their `package.json` files, then storing them internally in `this.pkgs`.
yoyo.prototype.findGenerators = function findGenerators() {
  this.pkgs = {};

  var resolveGenerators = function (generator) {
    if (!/(app|all)$/.test(generator.namespace)) {
      return;
    }

    var dir = findup.sync(generator.resolved, 'package.json');
    if (!dir) {
      return;
    }

    var pkg = yo.file.readJSON(path.join(dir, 'package.json'));
    pkg.namespace = generator.namespace;
    pkg.appGenerator = true;
    pkg.prettyName = generator.namespace.replace(/(\w+):\w+/, '$1');
    pkg.prettyName = pkg.prettyName.charAt(0).toUpperCase() + pkg.prettyName.slice(1);

    pkg.update = updateNotifier({
      packageName: pkg.name,
      packageVersion: pkg.version
    }).update;

    if (pkg.update && pkg.version !== pkg.update.latest) {
      pkg.updateAvailable = true;
    }

    this.pkgs[pkg.name] = pkg;
  };

  this._.each(this.env.getGeneratorsMeta(), resolveGenerators, this);
};

// Display the home `yo` screen, with the intial set of options.
//
// - options - (optional)
//           - message (string) - String to print before prompt.
//           - refresh (bool) - Spawn a new `yo` command.
yoyo.prototype.home = function home(options) {
  var done = this.async();

  options = options || {};

  if (options.refresh) {
    this.env.lookup();
    this.findGenerators();
  }

  if (options.message) {
    console.log('\n' + chalk.cyan(options.message) + '\n');
  }

  var defaultChoices = [{
    name: 'Install a generator',
    value: {
      method: '_installGenerator'
    }
  }, {
    name: 'Find some help',
    value: {
      method: '_findHelp'
    }
  }, {
    name: 'Get me out of here!',
    value: {
      method: '_noop'
    }
  }];

  var generatorList = this._.chain(this.pkgs).map(function (generator) {
    if (!generator.appGenerator) {
      return;
    }

    var versionInfo = chalk.dim.reset('(' + generator.version + ')');

    if (generator.updateAvailable) {
      versionInfo += chalk.yellow(' Update Available! ') +
        chalk.red('(' + generator.update.latest + ')');
    }

    return {
      name: 'Run the ' + generator.prettyName + ' generator ' + versionInfo,
      value: {
        method: '_initGenerator',
        args: generator.namespace
      }
    };
  }).compact().value();

  if (generatorList.length) {
    defaultChoices.unshift({
      name: 'Update your generators',
      value: {
        method: '_promptToUpdateGenerators'
      }
    });
  }

  this.insight.track('yoyo', 'home');

  fullname(function (err, name) {
    if (err) {
      return done(err);
    }

    var allo = name ? ('\'Allo ' + name.split(' ')[0] + '! ') : '\'Allo! ';

    this.prompt([{
      name: 'whatNext',
      type: 'list',
      message: allo + 'What would you like to do?',
      choices: this._.union(generatorList, new yo.inquirer.Separator(), defaultChoices)
    }], function (answer) {
      this[answer.whatNext.method](answer.whatNext.args, done);
    }.bind(this));
  }.bind(this));
};
<commit_msg>prettify the main menu<commit_after>/* jshint onevar:false */

'use strict';

var async = require('async');
var opn = require('opn');
var yo = require('yeoman-generator');
var yosay = require('yosay');
var util = require('util');
var path = require('path');
var updateNotifier = require('update-notifier');
var chalk = require('chalk');
var findup = require('findup');
var fullname = require('fullname');

// The `yo yo` generator provides users with a few common, helpful commands.
var yoyo = module.exports = function yoyo(args, options) {
  yo.Base.apply(this, arguments);
  this.insight = options.insight;

  this.insight.track('yoyo', 'init');
  process.once('exit', this._exit.bind(this));
};

util.inherits(yoyo, yo.Base);

// Runs parallel `npm update -g`s for each selected generator.
yoyo.prototype._updateGenerators = function _updateGenerators(pkgs) {
  var self = this;

  var resolveGenerators = function (pkg) {
    return function (next) {
      self.spawnCommand('npm', ['update', '-g', pkg])
        .on('error', next)
        .on('exit', next);
    };
  };

  self.insight.track('yoyo', 'update');
  async.parallel(self._.map(pkgs, resolveGenerators), function (err) {
    if (err) {
      self.insight.track('yoyo:err', 'update');
      return self.emit('error', err);
    }

    self.insight.track('yoyo', 'updated');
    self.home({
      refresh: true,
      message:
        'I\'ve just updated your generators. Remember, you can update' +
        '\na specific generator with npm by running:\n' +
        chalk.magenta('\n    npm update -g generator-_______')
    });
  });
};

// Prompts the user to select which generators to update
yoyo.prototype._promptToUpdateGenerators = function _promptToUpdateGenerators() {
  this.prompt([{
    name: '_updateSelectedGenerators',
    message: 'Generators to update',
    type: 'checkbox',
    choices: this._.map(this.pkgs, function (generator) {
      return {name: generator.name, checked: true};
    })
  }], function (answer) {
    this._updateGenerators.call(this, answer._updateSelectedGenerators);
  }.bind(this));
};

// Initializes a generator.
//
// - generator - (string) The generator to initialize.
yoyo.prototype._initGenerator = function _initGenerator(generator, done) {
  console.log(
    chalk.yellow('\nMake sure you are in the directory you want to scaffold into.\n') +
    'This generator can also be run with: ' +
    chalk.blue.bold('yo ' + generator.split(':')[0])
  );

  this.insight.track('yoyo', 'run', generator);
  this.composeWith(generator);
  done();
};

// Serves as the response prompt for "Install a generator" as well as simply
// installs a generator if a string is passed in.
//
// - pkgName - (optional) A string that matches the NPM package name.
yoyo.prototype._installGenerator = function _installGenerator(pkgName) {
  if (this._.isString(pkgName)) {
    this.insight.track('yoyo', 'install', pkgName);

    // We know what generator we want to install
    return this.spawnCommand('npm', ['install', '-g', pkgName])
      .on('error', function (err) {
        this.insight.track('yoyo:err', 'install', pkgName);
        this.emit('error', err);
      }.bind(this))
      .on('exit', function () {
        this.insight.track('yoyo', 'installed', pkgName);
        this.home({
          refresh: true,
          message:
            '\nI just installed your generator by running:\n' +
            chalk.blue.bold('\n    npm install -g ' + pkgName)
        });
      }.bind(this));
  }

  this.insight.track('yoyo', 'install');

  this.prompt([{
    name: 'searchTerm',
    message: 'Search NPM for generators'
  }], this._searchNpm.bind(this));
};

// Grabs all of the packages with a `yeoman-generator` keyword on NPM.
//
// - term - (object) Contains the search term & gets passed back to callback().
// - cb   - Callback to execute once generators have been found.
yoyo.prototype._findAllNpmGenerators = function _findAllNpmGenerators(term, cb) {
  var url = 'http://isaacs.iriscouch.com/registry/_design/' +
    'app/_view/byKeyword?startkey=[%22yeoman-generator%22]' +
    '&endkey=[%22yeoman-generator%22,{}]&group_level=3';

  this.request(url, function (err, res, body) {
    if (err) {
      return this.emit('error', err);
    }

    try {
      this.npmGenerators = JSON.parse(body);
    } catch (err) {
      return this.emit('error', new Error(chalk.bold(
        'A problem occurred contacting the registry.' +
        '\nUnable to parse response: not valid JSON.'
      )));
    }

    cb(term);
  }.bind(this));
};

// Takes a search term, looks it up in the registry, prompts the user with the
// results, allowing them to choose to install it, or go back home.
//
// - term - Object with a 'searchTerm' property containing the term to search
//          NPM for.
yoyo.prototype._searchNpm = function _searchNpm(term) {
  if (!this.npmGenerators) {
    return this._findAllNpmGenerators(term, this._searchNpm.bind(this));
  }

  // Find any matches from NPM.
  var choices = this._.chain(this.npmGenerators.rows).map(function (generator) {
    // Make sure it's not already installed.
    if (
      !this.pkgs[generator.key[1]] &&
      generator.key.join(' ').indexOf(term.searchTerm) > -1
    ) {
      return {
        name: generator.key[1],
        value: generator.key[1]
      };
    }
  }.bind(this)).compact().value();

  var resultsPrompt = [{
    name: '_installGenerator',
    type: 'list',
    message: choices.length > 0 ?
      'Here\'s what I found. Install one?' : 'Sorry, nothing was found',
    choices: this._.union(choices, [{
      name: 'Search again',
      value: '_installGenerator'
    }, {
      name: 'Return home',
      value: 'home'
    }])
  }];

  this.prompt(resultsPrompt, function (answer) {
    if (this._.isFunction(this[answer._installGenerator])) {
      return this[answer._installGenerator]();
    }

    this._installGenerator(answer._installGenerator);
  }.bind(this));
};

// Prompts user with a few helpful resources, then opens it in their browser.
yoyo.prototype._findHelp = function _findHelp() {
  this.insight.track('yoyo', 'help');
  this.prompt([{
    name: 'whereTo',
    type: 'list',
    message:
      'Here are a few helpful resources.\n' +
      '\nI will open the link you select in your browser for you',
    choices: [{
      name: 'Take me to the documentation',
      value: 'http://yeoman.io/learning/index.html'
    }, {
      name: 'View Frequently Asked Questions',
      value: 'http://yeoman.io/learning/faq.html'
    }, {
      name: 'File an issue on GitHub',
      value: 'http://yeoman.io/contributing/opening-issues.html'
    }, {
      name: 'Take me back home, Yo!',
      value: {
        method: 'home',
        args: {
          message: 'I get it, you like learning on your own. I respect that.'
        }
      }
    }]
  }], function (answer) {
    this.insight.track('yoyo', 'help', answer);

    if (this._.isFunction(this[answer.whereTo.method])) {
      this[answer.whereTo.method](answer.whereTo.args);
    } else {
      opn(answer.whereTo);
    }
  }.bind(this));
};

// Serves as a quick escape from the `yo yo` prompts.
yoyo.prototype._exit = function _exit() {
  this.insight.track('yoyo', 'exit');

  var url = 'https://github.com/yeoman/yeoman#team';
  var maxLength = url.length;
  var newLine = new Array(maxLength).join(' ');

  console.log(
    '\n' +
    yosay(
      'Bye from us! Chat soon.' +
      newLine +
      newLine +
      'The Yeoman Team ' + url,
      { maxLength: maxLength }
    )
  );
};

// I'm sorry...
yoyo.prototype._noop = function _noop() {};

// Rolls through all of the generators provided by `env.generators`, finding
// their `package.json` files, then storing them internally in `this.pkgs`.
yoyo.prototype.findGenerators = function findGenerators() {
  this.pkgs = {};

  var resolveGenerators = function (generator) {
    if (!/(app|all)$/.test(generator.namespace)) {
      return;
    }

    var dir = findup.sync(generator.resolved, 'package.json');
    if (!dir) {
      return;
    }

    var pkg = yo.file.readJSON(path.join(dir, 'package.json'));
    pkg.namespace = generator.namespace;
    pkg.appGenerator = true;
    pkg.prettyName = generator.namespace.replace(/(\w+):\w+/, '$1');
    pkg.prettyName = pkg.prettyName.charAt(0).toUpperCase() + pkg.prettyName.slice(1);

    pkg.update = updateNotifier({
      packageName: pkg.name,
      packageVersion: pkg.version
    }).update;

    if (pkg.update && pkg.version !== pkg.update.latest) {
      pkg.updateAvailable = true;
    }

    this.pkgs[pkg.name] = pkg;
  };

  this._.each(this.env.getGeneratorsMeta(), resolveGenerators, this);
};

// Display the home `yo` screen, with the intial set of options.
//
// - options - (optional)
//           - message (string) - String to print before prompt.
//           - refresh (bool) - Spawn a new `yo` command.
yoyo.prototype.home = function home(options) {
  var done = this.async();

  options = options || {};

  if (options.refresh) {
    this.env.lookup();
    this.findGenerators();
  }

  if (options.message) {
    console.log('\n' + chalk.cyan(options.message) + '\n');
  }

  var defaultChoices = [{
    name: 'Install a generator',
    value: {
      method: '_installGenerator'
    }
  }, {
    name: 'Find some help',
    value: {
      method: '_findHelp'
    }
  }, {
    name: 'Get me out of here!',
    value: {
      method: '_noop'
    }
  }];

  var generatorList = this._.chain(this.pkgs).map(function (generator) {
    if (!generator.appGenerator) {
      return;
    }

    var versionInfo = chalk.dim.reset('(' + generator.version + ')');

    if (generator.updateAvailable) {
      versionInfo += chalk.yellow(' Update Available! ') +
        chalk.red('(' + generator.update.latest + ')');
    }

    return {
      name: generator.prettyName + ' ' + versionInfo,
      value: {
        method: '_initGenerator',
        args: generator.namespace
      }
    };
  }).compact().value();

  if (generatorList.length) {
    defaultChoices.unshift({
      name: 'Update your generators',
      value: {
        method: '_promptToUpdateGenerators'
      }
    });
  }

  this.insight.track('yoyo', 'home');

  fullname(function (err, name) {
    if (err) {
      return done(err);
    }

    var allo = name ? ('\'Allo ' + name.split(' ')[0] + '! ') : '\'Allo! ';

    this.prompt([{
      name: 'whatNext',
      type: 'list',
      message: allo + 'What would you like to do?',
      choices: this._.flatten([
        new yo.inquirer.Separator('Run a generator'),
        generatorList,
        new yo.inquirer.Separator(),
        defaultChoices,
        new yo.inquirer.Separator()
      ])
    }], function (answer) {
      this[answer.whatNext.method](answer.whatNext.args, done);
    }.bind(this));
  }.bind(this));
};
<|endoftext|>


<commit_before>/* YRSS 1.1.4 */
/* Copyright (c) 2018 Mark Hillard - MIT License */

(function ($) {

    // use strict mode
    'use strict';

    $.fn.yrss = function (url, options, fn) {

        // plugin defaults
        var defaults = {
            ssl: false,
            limit: 10,
            reverse: false,
            cache: true,
            maxage: 3600,
            showerror: true,
            errormsg: '',
            tags: false,
            date: true,
            dateformat: 'default',
            titletag: 'h4',
            content: true,
            image: false,
            snippet: true,
            snippetlimit: 120,
            linktarget: '_self',
            logging: false
        };

        // extend options
        options = $.extend(defaults, options);

        // return function
        return this.each(function (i, e) {
            // check for ssl protocol
            var s = '';
            if (options.ssl) { s = 's'; }

            // add class to container
            if (!$(e).hasClass('rss-feed')) { $(e).addClass('rss-feed'); }

            // check for valid url
            if (!url) { return false; }

            // create yql query
            var query = 'http' + s + '://query.yahooapis.com/v1/public/yql?q=' + encodeURIComponent('select * from feed where url="' + url + '"');

            // set limit
            if (!!options.limit) { query += ' limit ' + options.limit; }

            // reverse feed order
            if (options.reverse) { query += ' | reverse()'; }

            // set maximum cache age
            if (options.cache) { query += '&_maxage=' + options.maxage; }

            // specify format
            query += '&format=json';

            // send request
            $.getJSON(query, function (data, status, errorThrown) {
                // if successful... *
                if (status === 'success') {
                    // * run function to create html result
                    process(e, data, options);

                    // * optional callback function
                    if ($.isFunction(fn)) { fn.call(this, $(e)); }

                // if there's an error... *
                } else if (status === 'error' || status === 'parsererror') {
                    // if showerror option is true... *
                    if (options.showerror) {
                        // variable scoping (error)
                        var msg;

                        // if errormsg option is not empty... *
                        if (options.errormsg !== '') {
                            // * assign custom error message
                            msg = options.errormsg;

                        // if errormsg option is empty... *
                        } else {
                            // * assign default error message
                            msg = errorThrown;
                        }

                        // * display error message
                        $(e).html('

' + msg + '

');

                    // if showerror option is false... *
                    } else {
                        // * abort
                        return false;
                    }
                }
            });
        });
    };

    // create html result
    var process = function (e, data, options) {
        // feed data (entries)
        var entries = data.query.results.item;

        // check if entries are not inside an array (only 1 entry)
        if (!$.isArray(entries)) { entries = [entries]; }

        // log object data to console if logging is true
        if (options.logging) {
            console.log('Object Data:');
            console.log(data);
            console.log('Entry Array:');
            console.log(entries);
        }

        // abort if no entries exist
        if (!entries) { return false; }

        // html variables
        var html = '';
        var htmlObject;

        // for each entry... *
        $.each(entries, function (i) {
            // * assign entry variable
            var entry = entries[i];

            // * variable scoping (tags)
            var tags;

            // if entry tags exist... *
            if (!!entry.category) {
                // * check for data type
                if ($.isArray(entry.category)) {
                    var arr = Object.keys(entry.category).map(function(key) {
                        if (entry.category[key].domain !== '') {
                            return entry.category[key].domain + ':' + entry.category[key].content;
                        } else {
                            return entry.category[key].content;
                        }
                    });
                    tags = arr;
                } else if ($.type(entry.category) === 'object') {
                    var domain = entry.category.domain,
                        obj = entry.category.content;
                    if (entry.category.domain !== '') {
                        tags = domain + ':' + obj;
                    } else {
                        tags = obj;
                    }
                } else {
                    tags = entry.category;
                }

                // * arrange entry tags
                tags = tags.toString().toLowerCase().replace(/ /g, '-').replace(/,/g, ' ');
            }

            // * variable scoping (date)
            var pubDate;

            // if date option is true... *
            if (entry.pubDate) {
                // * create date object
                var entryDate = new Date(entry.pubDate);

                // * select date format
                if (options.dateformat === 'default') {
                    pubDate = (entryDate.getMonth() + 1).toString() + '/' + entryDate.getDate().toString() + '/' + entryDate.getFullYear();
                } else if (options.dateformat === 'spellmonth') {
                    var months = ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'];
                    pubDate = months[entryDate.getMonth()] + ' ' + entryDate.getDate().toString() + ', ' + entryDate.getFullYear();
                } else if (options.dateformat === 'localedate') {
                    pubDate = entryDate.toLocaleDateString();
                } else if (options.dateformat === 'localedatetime') {
                    pubDate = entryDate.toLocaleDateString() + ' ' + entryDate.toLocaleTimeString();
                }
            }

            // * build entry
            html += '

<' + options.titletag + '>' + entry.title + '</' + options.titletag + '>
';
            if (options.date && pubDate) { html += '
' + pubDate + '
'; }

            // if content option is true... *
            if (options.content) {
                // * check for rss description/encoded value
                var content;
                if (!!entry.description) {
                    content = $.trim(entry.description);
                } else {
                    content = $.trim(entry.encoded);
                }

                // * build content
                html += '
' + content + '
';
            }

            html += '
';
        });

        // provisional html result
        htmlObject = $(html);

        // if content option is true... *
        if (options.content) {
            // for each entry... *
            $.each(htmlObject, function () {
                // if image option is true... *
                if (options.image) {
                    // * check for first image
                    var image = $(this).find('img').first();

                    // if image exists... *
                    if (image.length !== 0) {
                        // * create image wrapper
                        $(this).prepend('
');

                        // * append first image in image wrapper and wrap all textual elements after it
                        $(this).find('.entry-image').append(image).nextAll().wrapAll('

');
                    } else {
                        $(this).children().wrapAll('

');
                    }
                }

                // if snippet option is true... *
                if (options.snippet) {
                    // * set character limit
                    var content = $(this).find('.entry-content');
                    var contentLength = $(content).text().length;
                    content.text(function (i, v) {
                        if (contentLength === 0) {
                            return '';
                        } else if (contentLength !== 0 && contentLength <= options.snippetlimit) {
                            return v;
                        } else {
                            return v.substring(0, options.snippetlimit) + ' ...';
                        }
                    });
                }
            });
        }

        // append final html result
        $(e).append(htmlObject);

        // apply target to links
        $('a', e).attr('target', options.linktarget);
    };

})(jQuery);
<commit_msg>Remove all images from content if image option is false<commit_after>/* YRSS 1.1.4 */
/* Copyright (c) 2018 Mark Hillard - MIT License */

(function ($) {

    // use strict mode
    'use strict';

    $.fn.yrss = function (url, options, fn) {

        // plugin defaults
        var defaults = {
            ssl: false,
            limit: 10,
            reverse: false,
            cache: true,
            maxage: 3600,
            showerror: true,
            errormsg: '',
            tags: false,
            date: true,
            dateformat: 'default',
            titletag: 'h4',
            content: true,
            image: false,
            snippet: true,
            snippetlimit: 120,
            linktarget: '_self',
            logging: false
        };

        // extend options
        options = $.extend(defaults, options);

        // return function
        return this.each(function (i, e) {
            // check for ssl protocol
            var s = '';
            if (options.ssl) { s = 's'; }

            // add class to container
            if (!$(e).hasClass('rss-feed')) { $(e).addClass('rss-feed'); }

            // check for valid url
            if (!url) { return false; }

            // create yql query
            var query = 'http' + s + '://query.yahooapis.com/v1/public/yql?q=' + encodeURIComponent('select * from feed where url="' + url + '"');

            // set limit
            if (!!options.limit) { query += ' limit ' + options.limit; }

            // reverse feed order
            if (options.reverse) { query += ' | reverse()'; }

            // set maximum cache age
            if (options.cache) { query += '&_maxage=' + options.maxage; }

            // specify format
            query += '&format=json';

            // send request
            $.getJSON(query, function (data, status, errorThrown) {
                // if successful... *
                if (status === 'success') {
                    // * run function to create html result
                    process(e, data, options);

                    // * optional callback function
                    if ($.isFunction(fn)) { fn.call(this, $(e)); }

                // if there's an error... *
                } else if (status === 'error' || status === 'parsererror') {
                    // if showerror option is true... *
                    if (options.showerror) {
                        // variable scoping (error)
                        var msg;

                        // if errormsg option is not empty... *
                        if (options.errormsg !== '') {
                            // * assign custom error message
                            msg = options.errormsg;

                        // if errormsg option is empty... *
                        } else {
                            // * assign default error message
                            msg = errorThrown;
                        }

                        // * display error message
                        $(e).html('

' + msg + '

');

                    // if showerror option is false... *
                    } else {
                        // * abort
                        return false;
                    }
                }
            });
        });
    };

    // create html result
    var process = function (e, data, options) {
        // feed data (entries)
        var entries = data.query.results.item;

        // check if entries are not inside an array (only 1 entry)
        if (!$.isArray(entries)) { entries = [entries]; }

        // log object data to console if logging is true
        if (options.logging) {
            console.log('Object Data:');
            console.log(data);
            console.log('Entry Array:');
            console.log(entries);
        }

        // abort if no entries exist
        if (!entries) { return false; }

        // html variables
        var html = '';
        var htmlObject;

        // for each entry... *
        $.each(entries, function (i) {
            // * assign entry variable
            var entry = entries[i];

            // * variable scoping (tags)
            var tags;

            // if entry tags exist... *
            if (!!entry.category) {
                // * check for data type
                if ($.isArray(entry.category)) {
                    var arr = Object.keys(entry.category).map(function(key) {
                        if (entry.category[key].domain !== '') {
                            return entry.category[key].domain + ':' + entry.category[key].content;
                        } else {
                            return entry.category[key].content;
                        }
                    });
                    tags = arr;
                } else if ($.type(entry.category) === 'object') {
                    var domain = entry.category.domain,
                        obj = entry.category.content;
                    if (entry.category.domain !== '') {
                        tags = domain + ':' + obj;
                    } else {
                        tags = obj;
                    }
                } else {
                    tags = entry.category;
                }

                // * arrange entry tags
                tags = tags.toString().toLowerCase().replace(/ /g, '-').replace(/,/g, ' ');
            }

            // * variable scoping (date)
            var pubDate;

            // if date option is true... *
            if (entry.pubDate) {
                // * create date object
                var entryDate = new Date(entry.pubDate);

                // * select date format
                if (options.dateformat === 'default') {
                    pubDate = (entryDate.getMonth() + 1).toString() + '/' + entryDate.getDate().toString() + '/' + entryDate.getFullYear();
                } else if (options.dateformat === 'spellmonth') {
                    var months = ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'];
                    pubDate = months[entryDate.getMonth()] + ' ' + entryDate.getDate().toString() + ', ' + entryDate.getFullYear();
                } else if (options.dateformat === 'localedate') {
                    pubDate = entryDate.toLocaleDateString();
                } else if (options.dateformat === 'localedatetime') {
                    pubDate = entryDate.toLocaleDateString() + ' ' + entryDate.toLocaleTimeString();
                }
            }

            // * build entry
            html += '

<' + options.titletag + '>' + entry.title + '</' + options.titletag + '>
';
            if (options.date && pubDate) { html += '
' + pubDate + '
'; }

            // if content option is true... *
            if (options.content) {
                // * check for rss description/encoded value
                var content;
                if (!!entry.description) {
                    content = $.trim(entry.description);
                } else {
                    content = $.trim(entry.encoded);
                }

                // * build content
                html += '
' + content + '
';
            }

            html += '
';
        });

        // provisional html result
        htmlObject = $(html);

        // if content option is true... *
        if (options.content) {
            // for each entry... *
            $.each(htmlObject, function () {
                // if image option is true... *
                if (options.image) {
                    // * check for first image
                    var image = $(this).find('img').first();

                    // if image exists... *
                    if (image.length !== 0) {
                        // * create image wrapper
                        $(this).prepend('
');

                        // * append first image in image wrapper and wrap all textual elements after it
                        $(this).find('.entry-image').append(image).nextAll().wrapAll('

');
                    } else {
                        $(this).children().wrapAll('

');
                    }

                // if image option is false... *
                } else {
                    // * remove all images from content
                    $(this).find('img').remove();
                }

                // if snippet option is true... *
                if (options.snippet) {
                    // * set character limit
                    var content = $(this).find('.entry-content');
                    var contentLength = $(content).text().length;
                    content.text(function (i, v) {
                        if (contentLength === 0) {
                            return '';
                        } else if (contentLength !== 0 && contentLength <= options.snippetlimit) {
                            return v;
                        } else {
                            return v.substring(0, options.snippetlimit) + ' ...';
                        }
                    });
                }
            });
        }

        // append final html result
        $(e).append(htmlObject);

        // apply target to links
        $('a', e).attr('target', options.linktarget);
    };

})(jQuery);
<|endoftext|>


<commit_before>/**
 * Code for ascii-game
 * @author KeyboardFire
 * @license MIT
 */

var ASCIIGame = {
	init: function(el, options) {
		if (!options) options = {};

		// game is the object that stores all the game-related information (data, internal metadata, player, etc.)
		var game = {
				// DOM
				el: el, elData: [], info: null,
				// internals
				data: [], eventData: {frame: []},
				w: options.w || options.width || 80,
				h: options.h || options.height || 24,
				player: {jumpDeltas: [2, 2, 2, 1, 2, 1, 1, 2, 1, 1, 1, 2, 1, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 0, 1, 0, 0, 0], jumpIndex: -1},
				lastFrame: 0,
				mainLoop: function() {
					// stuff that happens every frame
					if (new Date() - game.lastFrame > 50/*ms*/) {
						game.lastFrame = new Date();

						// handle player input
						if (tools.keysDown[65]) { // A (go left)
							if (data.get(game.player.x - 1, game.player.y).id === 'empty') {
								data.move(game.player.x, game.player.y, --game.player.x, game.player.y);
							}
						}
						if (tools.keysDown[68]) { // D (go right)
							if (data.get(game.player.x + 1, game.player.y).id === 'empty') {
								data.move(game.player.x, game.player.y, ++game.player.x, game.player.y);
							}
						}
						if (tools.keysDown[87]) { // W (jump)
							if (data.get(game.player.x, game.player.y + 1).id !== 'empty') {
								game.player.jumpIndex = 0;
							}
						}
						if (game.player.jumpIndex !== -1) { // jumping
							var dy = game.player.jumpDeltas[game.player.jumpIndex++];
							if (data.get(game.player.x, game.player.y - dy).id === 'empty') {
								data.move(game.player.x, game.player.y, game.player.x, game.player.y -= dy);
							}
							if (game.player.jumpIndex >= game.player.jumpDeltas.length) game.player.jumpIndex = -1;
						}
						if (game.player.y < game.h - 1) { // gravity
							if (data.get(game.player.x, game.player.y + 1).id === 'empty') {
								data.move(game.player.x, game.player.y, game.player.x, ++game.player.y);
							}
						}

						// handle "each frame" events
						for (var i = 0; i < game.eventData.frame.length; ++i) {
							var c = tools.unpack(game.eventData.frame[i]), x = c[0], y = c[1];
							data.get(x, y).events.frame(data.get(x, y), x, y);
						}

						data.render();
					}

					tools.reqAnimFrame(game.mainLoop);
				}
			// tools stores all the stuff not directly related to the game (DOM tools, I/O, shims, etc.)
			}, tools = {
				// simple requestAnimationFrame shim
				reqAnimFrame: function(f){window.requestAnimationFrame(f);} ||
					function(f){window.webkitRequestAnimationFrame(f);} ||
					function(f){window.mozRequestAnimationFrame(f);} ||
					function(f){window.msRequestAnimationFrame(f);} ||
					function(f){window.oRequestAnimationFrame(f);} ||
					function(f){setTimeout(f, 5);},
				keysDown: {},
				// for packing X/Y coords into a single number
				// for both values, 0 <= v < pow(2, 16)
				pack: function(a, b) {
					return (a << 16) | b;
				},
				unpack: function(n) {
					return [(n >> 16) & 0xFFFF, n & 0xFFFF];
				},
				// helper constants
				direction: {
					RIGHT: 1, LEFT: -1, UP: 2, DOWN: -2
				}
			// data is a helper object to manipulate game.data and game.elData
			}, data = {
				// types of squares / tiles
				tile: function(type) {
					var o = ({
						empty: {color: '#000', chr: '.'},
						player: {color: '#00F', chr: '@'},
						block: {color: '#F00', chr: '#'},
						goomba: {color: '#B73', chr: 'O', events: {
							frame: function(self, x, y) {
								if (++self.counter !== 4) return;
								self.counter = 0;
								if (data.get(x - 1, y).id === 'empty') {
									data.move(x, y, x - 1, y);
								}
							},
							collision: function(self, x, y, other, direction) {
								if (direction == tools.direction.UP) {
									data.set(x, y, data.tile('empty'));
								}
							}
						}, counter: 0}
					})[type] || {};
					o.id = type;
					if (!o.events) o.events = {};
					return o;
				},
				get: function(x, y) {
					if (x < 0 || y < 0 || x >= game.data[0].length || y >= game.data.length) {
						return {id: 'outofbounds'};
					}
					return game.data[y][x];
				},
				set: function(x, y, val) {
					for (evtType in game.eventData) {
						if (game.data[y][x].events[evtType]) {
							game.eventData[evtType].splice(game.eventData[evtType].indexOf(tools.pack(x, y)));
						}
						if (val.events[evtType]) {
							game.eventData[evtType].push(tools.pack(x, y));
						}
					}
					game.data[y][x] = val;
					data.modified.push(tools.pack(x, y));
				},
				move: function(x1, y1, x2, y2) {
					var old = data.get(x1, y1);
					data.set(x1, y1, data.tile('empty'));
					data.set(x2, y2, old);
				},
				moveWithCollision: function(x1, y1, x2, y2) {
					var d1 = data.get(x1, y1), d2 = data.get(x2, y2);
					if (d2.id === 'empty') {
						data.move(x1, y1, x2, y2);
					} else {
						// this line of code is terrifying
						var dir = x1 < x2 ? tools.direction.RIGHT : (x1 > x2 ? tools.direction.LEFT : (y1 < y2 ? tools.direction.DOWN : tools.direction.UP));
						if (d1.events.collision) {
							d1.events.collision(d1, x1, y1, d2, dir);
						}
						if (d2.events.collision) {
							d2.events.collision(d2, x2, y2, d1, -dir);
						}
					}
				},
				// render and modified exist so that the whole DOM doesn't have to be updated each frame
				render: function() {
					for (var i = 0; i < data.modified.length; ++i) {
						var c = tools.unpack(data.modified[i]), x = c[0], y = c[1];
						game.elData[y][x].style.color = game.data[y][x].color;
						game.elData[y][x].firstChild.nodeValue = game.data[y][x].chr;
					}
					data.modified = [];
				},
				modified: []
			};

		// this is the only object from init that will be exposed to outsiders
		var asciiGame = {
			play: function() {
				// clear loading message, etc.
				while (game.el.firstChild) game.el.removeChild(game.el.firstChild);

				// set up / initialize internal data and DOM
				for (var y = 0; y < game.h; ++y) {
					var row = [];
					var elRow = [];
					for (var x = 0; x < game.w; ++x) {
						row.push(data.tile('empty'));

						var node = document.createElement('span');
						node.style.color = data.tile('empty').color;
						node.appendChild(document.createTextNode(data.tile('empty').chr));
						elRow.push(node);
						game.el.appendChild(node);
					}
					game.data.push(row);
					game.elData.push(elRow);
					game.el.appendChild(document.createTextNode('\n'));
				}

				// set up info display
				game.info = document.createElement('div');
				game.info.appendChild(document.createTextNode('info goes here'));
				game.el.appendChild(document.createTextNode('\n'));
				game.el.appendChild(game.info);

				// set up event listeners
				var _firsttime = true;
				window.onkeydown = function(e) { if (_firsttime) console.log(e.which, e.keyCode); _firsttime = false; tools.keysDown[e.which || e.keyCode] = true; };
				window.onkeyup = function(e) { tools.keysDown[e.which || e.keyCode] = false; };

				// you!
				game.player.x = 1;
				game.player.y = game.h - 1;
				data.set(game.player.x, game.player.y, data.tile('player'));

				// a random wall (for testing)
				var wx = (Math.random() * (game.w-3) | 0) + 3;
				for (var i = 1; i < 6; ++i) data.set(wx, game.h - i, data.tile('block'));

				// a random goomba (also for testing)
				var gx = (Math.random() * (game.w-6) | 0) + 6;
				if (wx === gx) gx += (gx > game.w/2 ? 1 : -1);
				data.set(gx, game.h - 1, data.tile('goomba'));

				// start playing!
				data.render();
				game.mainLoop();
			}
		};
		return asciiGame;
	}
};

window.onload = function() {
	var asciiGame = ASCIIGame.init(document.getElementById('ascii'));
	asciiGame.play();
};
<commit_msg>no more passing around x/y values<commit_after>/**
 * Code for ascii-game
 * @author KeyboardFire
 * @license MIT
 */

var ASCIIGame = {
	init: function(el, options) {
		if (!options) options = {};

		// game is the object that stores all the game-related information (data, internal metadata, player, etc.)
		var game = {
				// DOM
				el: el, elData: [], info: null,
				// internals
				data: [], eventData: {frame: []},
				w: options.w || options.width || 80,
				h: options.h || options.height || 24,
				player: {jumpDeltas: [2, 2, 2, 1, 2, 1, 1, 2, 1, 1, 1, 2, 1, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 0, 1, 0, 0, 0], jumpIndex: -1},
				lastFrame: 0,
				mainLoop: function() {
					// stuff that happens every frame
					if (new Date() - game.lastFrame > 50/*ms*/) {
						game.lastFrame = new Date();

						// handle player input
						if (tools.keysDown[65]) { // A (go left)
							if (data.get(game.player.x - 1, game.player.y).id === 'empty') {
								data.move(game.player.x, game.player.y, --game.player.x, game.player.y);
							}
						}
						if (tools.keysDown[68]) { // D (go right)
							if (data.get(game.player.x + 1, game.player.y).id === 'empty') {
								data.move(game.player.x, game.player.y, ++game.player.x, game.player.y);
							}
						}
						if (tools.keysDown[87]) { // W (jump)
							if (data.get(game.player.x, game.player.y + 1).id !== 'empty') {
								game.player.jumpIndex = 0;
							}
						}
						if (game.player.jumpIndex !== -1) { // jumping
							var dy = game.player.jumpDeltas[game.player.jumpIndex++];
							if (data.get(game.player.x, game.player.y - dy).id === 'empty') {
								data.move(game.player.x, game.player.y, game.player.x, game.player.y -= dy);
							}
							if (game.player.jumpIndex >= game.player.jumpDeltas.length) game.player.jumpIndex = -1;
						}
						if (game.player.y < game.h - 1) { // gravity
							if (data.get(game.player.x, game.player.y + 1).id === 'empty') {
								data.move(game.player.x, game.player.y, game.player.x, ++game.player.y);
							}
						}

						// handle "each frame" events
						for (var i = 0; i < game.eventData.frame.length; ++i) {
							var c = tools.unpack(game.eventData.frame[i]), x = c[0], y = c[1];
							data.get(x, y).events.frame(data.get(x, y));
						}

						data.render();
					}

					tools.reqAnimFrame(game.mainLoop);
				}
			// tools stores all the stuff not directly related to the game (DOM tools, I/O, shims, etc.)
			}, tools = {
				// simple requestAnimationFrame shim
				reqAnimFrame: function(f){window.requestAnimationFrame(f);} ||
					function(f){window.webkitRequestAnimationFrame(f);} ||
					function(f){window.mozRequestAnimationFrame(f);} ||
					function(f){window.msRequestAnimationFrame(f);} ||
					function(f){window.oRequestAnimationFrame(f);} ||
					function(f){setTimeout(f, 5);},
				keysDown: {},
				// for packing X/Y coords into a single number
				// for both values, 0 <= v < pow(2, 16)
				pack: function(a, b) {
					return (a << 16) | b;
				},
				unpack: function(n) {
					return [(n >> 16) & 0xFFFF, n & 0xFFFF];
				},
				// helper constants
				direction: {
					RIGHT: 1, LEFT: -1, UP: 2, DOWN: -2
				}
			// data is a helper object to manipulate game.data and game.elData
			}, data = {
				// types of squares / tiles
				tile: function(type) {
					var o = ({
						empty: {color: '#000', chr: '.'},
						player: {color: '#00F', chr: '@'},
						block: {color: '#F00', chr: '#'},
						goomba: {color: '#B73', chr: 'O', events: {
							frame: function(self) {
								if (++self.counter !== 4) return;
								self.counter = 0;
								if (data.get(self.x - 1, self.y).id === 'empty') {
									data.move(self.x, self.y, self.x - 1, self.y);
								}
							},
							collision: function(self, other, direction) {
								if (direction == tools.direction.UP) {
									data.set(self.x, self.y, data.tile('empty'));
								}
							}
						}, counter: 0}
					})[type] || {};
					o.id = type;
					if (!o.events) o.events = {};
					return o;
				},
				get: function(x, y) {
					if (x < 0 || y < 0 || x >= game.data[0].length || y >= game.data.length) {
						return {id: 'outofbounds'};
					}
					return game.data[y][x];
				},
				set: function(x, y, val) {
					val.x = x;
					val.y = y;
					for (evtType in game.eventData) {
						if (game.data[y][x].events[evtType]) {
							game.eventData[evtType].splice(game.eventData[evtType].indexOf(tools.pack(x, y)));
						}
						if (val.events[evtType]) {
							game.eventData[evtType].push(tools.pack(x, y));
						}
					}
					game.data[y][x] = val;
					data.modified.push(tools.pack(x, y));
				},
				move: function(x1, y1, x2, y2) {
					var old = data.get(x1, y1);
					data.set(x1, y1, data.tile('empty'));
					data.set(x2, y2, old);
				},
				moveWithCollision: function(x1, y1, x2, y2) {
					var d1 = data.get(x1, y1), d2 = data.get(x2, y2);
					if (d2.id === 'empty') {
						data.move(x1, y1, x2, y2);
					} else {
						// this line of code is terrifying
						var dir = x1 < x2 ? tools.direction.RIGHT : (x1 > x2 ? tools.direction.LEFT : (y1 < y2 ? tools.direction.DOWN : tools.direction.UP));
						if (d1.events.collision) {
							d1.events.collision(d1, d2, dir);
						}
						if (d2.events.collision) {
							d2.events.collision(d2, d1, -dir);
						}
					}
				},
				// render and modified exist so that the whole DOM doesn't have to be updated each frame
				render: function() {
					for (var i = 0; i < data.modified.length; ++i) {
						var c = tools.unpack(data.modified[i]), x = c[0], y = c[1];
						game.elData[y][x].style.color = game.data[y][x].color;
						game.elData[y][x].firstChild.nodeValue = game.data[y][x].chr;
					}
					data.modified = [];
				},
				modified: []
			};

		// this is the only object from init that will be exposed to outsiders
		var asciiGame = {
			play: function() {
				// clear loading message, etc.
				while (game.el.firstChild) game.el.removeChild(game.el.firstChild);

				// set up / initialize internal data and DOM
				for (var y = 0; y < game.h; ++y) {
					var row = [];
					var elRow = [];
					for (var x = 0; x < game.w; ++x) {
						row.push(data.tile('empty'));

						var node = document.createElement('span');
						node.style.color = data.tile('empty').color;
						node.appendChild(document.createTextNode(data.tile('empty').chr));
						elRow.push(node);
						game.el.appendChild(node);
					}
					game.data.push(row);
					game.elData.push(elRow);
					game.el.appendChild(document.createTextNode('\n'));
				}

				// set up info display
				game.info = document.createElement('div');
				game.info.appendChild(document.createTextNode('info goes here'));
				game.el.appendChild(document.createTextNode('\n'));
				game.el.appendChild(game.info);

				// set up event listeners
				var _firsttime = true;
				window.onkeydown = function(e) { if (_firsttime) console.log(e.which, e.keyCode); _firsttime = false; tools.keysDown[e.which || e.keyCode] = true; };
				window.onkeyup = function(e) { tools.keysDown[e.which || e.keyCode] = false; };

				// you!
				game.player.x = 1;
				game.player.y = game.h - 1;
				data.set(game.player.x, game.player.y, data.tile('player'));

				// a random wall (for testing)
				var wx = (Math.random() * (game.w-3) | 0) + 3;
				for (var i = 1; i < 6; ++i) data.set(wx, game.h - i, data.tile('block'));

				// a random goomba (also for testing)
				var gx = (Math.random() * (game.w-6) | 0) + 6;
				if (wx === gx) gx += (gx > game.w/2 ? 1 : -1);
				data.set(gx, game.h - 1, data.tile('goomba'));

				// start playing!
				data.render();
				game.mainLoop();
			}
		};
		return asciiGame;
	}
};

window.onload = function() {
	var asciiGame = ASCIIGame.init(document.getElementById('ascii'));
	asciiGame.play();
};
<|endoftext|>


<commit_before>/*
	Glean.js - the final product from the ShopDown.js tests.
	proposed use.
  ----
  	var Glean 	= $("thiselement.section").Glean(),
  			link 		= Glean.get("link");
  	console.log(link);
	----
	output: http://danieltamkin.com
*/
<commit_msg>Added basic structure<commit_after>/*
	Glean.js - the final product from the ShopDown.js tests.
	proposed use.
  ----
  	var Glean 	= $("thiselement.section").Glean(),
  			link 		= Glean.get("link");
  	console.log(link);
	----
	output: http://danieltamkin.com
*/
$.fn.ShopDown = function(data) {
	$this = $(this);
	$data = [];
	$data.settings = datacheck(data);
	/*data*/
	function datacheck(data){
	  return datascrub(data);
	}
	function datatemplate(){
	  return {
			leftovers: false,
	    content: $this,
	    sanitize: true,
			cull: true,
			images: true,
			compile: true
	  }
	}
	function datascrub(data){
	  template = datatemplate();
		if(data == undefined || $.isEmptyObject(data) == true){
			data = template;
		}
		else{
		  $.each(template,function(key,value){
		    $.each(data,function(bravokey,bravovalue){
		      if(bravokey == key){
		        if(bravovalue.length === -1){
		          value = value;
		        }
		        else{
		          // do nothing, bravovalue is in bravokey
		        }
		      }
		      else{
		        if(!(key in data)){// that key doesnt exist? make it.
		          data[key] = value;
		        }
		      }
		    });
		  });
		}
		return data;
	}
/*> app*/
	function app(){
	  window.htmlGot = false;// yes your allowed to grab HTML!
		appCalculation();
		appCleanup();
	}
	app();
/*> objective functions*/
	return {
	  output: function(data) {
	    return appOutput(data);
	  },
		reload: function(){
			app();// just reloads the app model to get new info.
		}
	}
};
<|endoftext|>