<commit_before>(function() {

  var arcToSegmentsCache = { },
      segmentToBezierCache = { },
      boundsOfCurveCache = { }
      _join = Array.prototype.join;

  /* Adapted from http://dxr.mozilla.org/mozilla-central/source/content/svg/content/src/nsSVGPathDataParser.cpp
   * by Andrea Bogazzi code is under MPL. if you don't have a copy of the license you can take it here
   * http://mozilla.org/MPL/2.0/
   */
  function arcToSegments(toX, toY, rx, ry, large, sweep, rotateX) {
    var argsString = _join.call(arguments);
    if (arcToSegmentsCache[argsString]) {
      return arcToSegmentsCache[argsString];
    }

    var PI = Math.PI, th = rotateX * PI / 180,
        sinTh = Math.sin(th),
        cosTh = Math.cos(th),
        fromX = 0, fromY = 0;

    rx = Math.abs(rx);
    ry = Math.abs(ry);

    var px = -cosTh * toX * 0.5 - sinTh * toY * 0.5,
        py = -cosTh * toY * 0.5 + sinTh * toX * 0.5,
        rx2 = rx * rx, ry2 = ry * ry, py2 = py * py, px2 = px * px,
        pl = rx2 * ry2 - rx2 * py2 - ry2 * px2,
        root = 0;

    if (pl < 0) {
      var s = Math.sqrt(1 - pl/(rx2 * ry2));
      rx *= s;
      ry *= s;
    }
    else {
      root = (large === sweep ? -1.0 : 1.0) *
              Math.sqrt( pl /(rx2 * py2 + ry2 * px2));
    }

    var cx = root * rx * py / ry,
        cy = -root * ry * px / rx,
        cx1 = cosTh * cx - sinTh * cy + toX * 0.5,
        cy1 = sinTh * cx + cosTh * cy + toY * 0.5,
        mTheta = calcVectorAngle(1, 0, (px - cx) / rx, (py - cy) / ry),
        dtheta = calcVectorAngle((px - cx) / rx, (py - cy) / ry, (-px - cx) / rx, (-py - cy) / ry);

    if (sweep === 0 && dtheta > 0) {
      dtheta -= 2 * PI;
    }
    else if (sweep === 1 && dtheta < 0) {
      dtheta += 2 * PI;
    }

    // Convert into cubic bezier segments <= 90deg
    var segments = Math.ceil(Math.abs(dtheta / PI * 2)),
        result = [], mDelta = dtheta / segments,
        mT = 8 / 3 * Math.sin(mDelta / 4) * Math.sin(mDelta / 4) / Math.sin(mDelta / 2),
        th3 = mTheta + mDelta;

    for (var i = 0; i < segments; i++) {
      result[i] = segmentToBezier(mTheta, th3, cosTh, sinTh, rx, ry, cx1, cy1, mT, fromX, fromY);
      fromX = result[i][4];
      fromY = result[i][5];
      mTheta = th3;
      th3 += mDelta;
    }
    arcToSegmentsCache[argsString] = result;
    return result;
  }

  function segmentToBezier(th2, th3, cosTh, sinTh, rx, ry, cx1, cy1, mT, fromX, fromY) {
    var argsString2 = _join.call(arguments);
    if (segmentToBezierCache[argsString2]) {
      return segmentToBezierCache[argsString2];
    }

    var costh2 = Math.cos(th2),
        sinth2 = Math.sin(th2),
        costh3 = Math.cos(th3),
        sinth3 = Math.sin(th3),
        toX = cosTh * rx * costh3 - sinTh * ry * sinth3 + cx1,
        toY = sinTh * rx * costh3 + cosTh * ry * sinth3 + cy1,
        cp1X = fromX + mT * ( - cosTh * rx * sinth2 - sinTh * ry * costh2),
        cp1Y = fromY + mT * ( - sinTh * rx * sinth2 + cosTh * ry * costh2),
        cp2X = toX + mT * ( cosTh * rx * sinth3 + sinTh * ry * costh3),
        cp2Y = toY + mT * ( sinTh * rx * sinth3 - cosTh * ry * costh3);

    segmentToBezierCache[argsString2] = [
      cp1X, cp1Y,
      cp2X, cp2Y,
      toX, toY
    ];
    return segmentToBezierCache[argsString2];
  }

  /*
  * Private
  */
  function calcVectorAngle(ux, uy, vx, vy) {
    var ta = Math.atan2(uy, ux),
        tb = Math.atan2(vy, vx);
    if (tb >= ta) {
      return tb - ta;
    }
    else {
      return 2 * Math.PI - (ta - tb);
    }
  }

  /**
   * Draws arc
   * @param {CanvasRenderingContext2D} ctx
   * @param {Number} fx
   * @param {Number} fy
   * @param {Array} coords
   */
  fabric.util.drawArc = function(ctx, fx, fy, coords) {
    var rx = coords[0],
        ry = coords[1],
        rot = coords[2],
        large = coords[3],
        sweep = coords[4],
        tx = coords[5],
        ty = coords[6],
        segs = [[ ], [ ], [ ], [ ]],
        segsNorm = arcToSegments(tx - fx, ty - fy, rx, ry, large, sweep, rot);

    for (var i = 0, len = segsNorm.length; i < len; i++) {
      segs[i][0] = segsNorm[i][0] + fx;
      segs[i][1] = segsNorm[i][1] + fy;
      segs[i][2] = segsNorm[i][2] + fx;
      segs[i][3] = segsNorm[i][3] + fy;
      segs[i][4] = segsNorm[i][4] + fx;
      segs[i][5] = segsNorm[i][5] + fy;
      ctx.bezierCurveTo.apply(ctx, segs[i]);
    }
  };

  /**
   * Calculate bounding box of a elliptic-arc
   * @param {Number} fx start point of arc
   * @param {Number} fy
   * @param {Number} rx horizontal radius
   * @param {Number} ry vertical radius
   * @param {Number} rot angle of horizontal axe
   * @param {Number} large 1 or 0, whatever the arc is the big or the small on the 2 points
   * @param {Number} sweep 1 or 0, 1 clockwise or counterclockwise direction
   * @param {Number} tx end point of arc
   * @param {Number} ty
   */
  fabric.util.getBoundsOfArc = function(fx, fy, rx, ry, rot, large, sweep, tx, ty) {

    var fromX = 0, fromY = 0, bound = [ ], bounds = [ ],
    segs = arcToSegments(tx - fx, ty - fy, rx, ry, large, sweep, rot);

    for (var i = 0, len = segs.length; i < len; i++) {
      bound = getBoundsOfCurve(fromX, fromY, segs[i][0], segs[i][1], segs[i][2], segs[i][3], segs[i][4], segs[i][5]);
      bound[0].x += fx;
      bound[0].y += fy;
      bound[1].x += fx;
      bound[1].y += fy;
      bounds.push(bound[0]);
      bounds.push(bound[1]);
      fromX = segs[i][4];
      fromY = segs[i][5];
    }
    return bounds;
  };

  /**
   * Calculate bounding box of a beziercurve
   * @param {Number} x0 starting point
   * @param {Number} y0
   * @param {Number} x1 first control point
   * @param {Number} y1
   * @param {Number} x2 secondo control point
   * @param {Number} y2
   * @param {Number} x3 end of beizer
   * @param {Number} y3
   */
  // taken from http://jsbin.com/ivomiq/56/edit  no credits available for that.
  function getBoundsOfCurve(x0, y0, x1, y1, x2, y2, x3, y3) {
    var argsString = _join.call(arguments);
    if (boundsOfCurveCache[argsString]) {
      return boundsOfCurveCache[argsString];
    }

    var pow = Math.pow, sqrt = Math.sqrt,
        min = Math.min, max = Math.max,
        abs = Math.abs, tvalues = [ ],
        bounds = [[ ], [ ]], points = [ ],
        a, b, c, t, t1, t2, b2ac, sqrtb2ac;

    b = 6 * x0 - 12 * x1 + 6 * x2;
    a = -3 * x0 + 9 * x1 - 9 * x2 + 3 * x3;
    c = 3 * x1 - 3 * x0;

    for (var i = 0; i < 2; ++i) {
      if (i > 0) {
        b = 6 * y0 - 12 * y1 + 6 * y2;
        a = -3 * y0 + 9 * y1 - 9 * y2 + 3 * y3;
        c = 3 * y1 - 3 * y0;
      }

      if (abs(a) < 1e-12) {
        if (abs(b) < 1e-12) {
          continue;
        }
        t = -c / b;
        if (0 < t && t < 1) {
          tvalues.push(t);
        }
        continue;
      }
      b2ac = b * b - 4 * c * a;
      if (b2ac < 0) {
        continue;
      }
      sqrtb2ac = sqrt(b2ac);
      t1 = (-b + sqrtb2ac) / (2 * a);
      if (0 < t1 && t1 < 1) {
        tvalues.push(t1);
      }
      t2 = (-b - sqrtb2ac) / (2 * a);
      if (0 < t2 && t2 < 1) {
        tvalues.push(t2);
      }
    }

    var x, y, j = tvalues.length, jlen = j, mt;
    while(j--) {
      t = tvalues[j];
      mt = 1 - t;
      x = (mt * mt * mt * x0) + (3 * mt * mt * t * x1) + (3 * mt * t * t * x2) + (t * t * t * x3);
      bounds[0][j] = x;

      y = (mt * mt * mt * y0) + (3 * mt * mt * t * y1) + (3 * mt * t * t * y2) + (t * t * t * y3);
      bounds[1][j] = y;
    }

    bounds[0][jlen] = x0;
    bounds[1][jlen] = y0;
    bounds[0][jlen + 1] = x3;
    bounds[1][jlen + 1] = y3;
    var result = [
      {
        x: min.apply(null, bounds[0]),
        y: min.apply(null, bounds[1])
      },
      {
        x: max.apply(null, bounds[0]),
        y: max.apply(null, bounds[1])
      }
    ];
    boundsOfCurveCache[argsString] = result;
    return result;
  };

  fabric.util.getBoundsOfCurve = getBoundsOfCurve;

})();
<commit_msg>Update arc.js<commit_after>(function() {

  var arcToSegmentsCache = { },
      segmentToBezierCache = { },
      boundsOfCurveCache = { },
      _join = Array.prototype.join;

  /* Adapted from http://dxr.mozilla.org/mozilla-central/source/content/svg/content/src/nsSVGPathDataParser.cpp
   * by Andrea Bogazzi code is under MPL. if you don't have a copy of the license you can take it here
   * http://mozilla.org/MPL/2.0/
   */
  function arcToSegments(toX, toY, rx, ry, large, sweep, rotateX) {
    var argsString = _join.call(arguments);
    if (arcToSegmentsCache[argsString]) {
      return arcToSegmentsCache[argsString];
    }

    var PI = Math.PI, th = rotateX * PI / 180,
        sinTh = Math.sin(th),
        cosTh = Math.cos(th),
        fromX = 0, fromY = 0;

    rx = Math.abs(rx);
    ry = Math.abs(ry);

    var px = -cosTh * toX * 0.5 - sinTh * toY * 0.5,
        py = -cosTh * toY * 0.5 + sinTh * toX * 0.5,
        rx2 = rx * rx, ry2 = ry * ry, py2 = py * py, px2 = px * px,
        pl = rx2 * ry2 - rx2 * py2 - ry2 * px2,
        root = 0;

    if (pl < 0) {
      var s = Math.sqrt(1 - pl/(rx2 * ry2));
      rx *= s;
      ry *= s;
    }
    else {
      root = (large === sweep ? -1.0 : 1.0) *
              Math.sqrt( pl /(rx2 * py2 + ry2 * px2));
    }

    var cx = root * rx * py / ry,
        cy = -root * ry * px / rx,
        cx1 = cosTh * cx - sinTh * cy + toX * 0.5,
        cy1 = sinTh * cx + cosTh * cy + toY * 0.5,
        mTheta = calcVectorAngle(1, 0, (px - cx) / rx, (py - cy) / ry),
        dtheta = calcVectorAngle((px - cx) / rx, (py - cy) / ry, (-px - cx) / rx, (-py - cy) / ry);

    if (sweep === 0 && dtheta > 0) {
      dtheta -= 2 * PI;
    }
    else if (sweep === 1 && dtheta < 0) {
      dtheta += 2 * PI;
    }

    // Convert into cubic bezier segments <= 90deg
    var segments = Math.ceil(Math.abs(dtheta / PI * 2)),
        result = [], mDelta = dtheta / segments,
        mT = 8 / 3 * Math.sin(mDelta / 4) * Math.sin(mDelta / 4) / Math.sin(mDelta / 2),
        th3 = mTheta + mDelta;

    for (var i = 0; i < segments; i++) {
      result[i] = segmentToBezier(mTheta, th3, cosTh, sinTh, rx, ry, cx1, cy1, mT, fromX, fromY);
      fromX = result[i][4];
      fromY = result[i][5];
      mTheta = th3;
      th3 += mDelta;
    }
    arcToSegmentsCache[argsString] = result;
    return result;
  }

  function segmentToBezier(th2, th3, cosTh, sinTh, rx, ry, cx1, cy1, mT, fromX, fromY) {
    var argsString2 = _join.call(arguments);
    if (segmentToBezierCache[argsString2]) {
      return segmentToBezierCache[argsString2];
    }

    var costh2 = Math.cos(th2),
        sinth2 = Math.sin(th2),
        costh3 = Math.cos(th3),
        sinth3 = Math.sin(th3),
        toX = cosTh * rx * costh3 - sinTh * ry * sinth3 + cx1,
        toY = sinTh * rx * costh3 + cosTh * ry * sinth3 + cy1,
        cp1X = fromX + mT * ( - cosTh * rx * sinth2 - sinTh * ry * costh2),
        cp1Y = fromY + mT * ( - sinTh * rx * sinth2 + cosTh * ry * costh2),
        cp2X = toX + mT * ( cosTh * rx * sinth3 + sinTh * ry * costh3),
        cp2Y = toY + mT * ( sinTh * rx * sinth3 - cosTh * ry * costh3);

    segmentToBezierCache[argsString2] = [
      cp1X, cp1Y,
      cp2X, cp2Y,
      toX, toY
    ];
    return segmentToBezierCache[argsString2];
  }

  /*
  * Private
  */
  function calcVectorAngle(ux, uy, vx, vy) {
    var ta = Math.atan2(uy, ux),
        tb = Math.atan2(vy, vx);
    if (tb >= ta) {
      return tb - ta;
    }
    else {
      return 2 * Math.PI - (ta - tb);
    }
  }

  /**
   * Draws arc
   * @param {CanvasRenderingContext2D} ctx
   * @param {Number} fx
   * @param {Number} fy
   * @param {Array} coords
   */
  fabric.util.drawArc = function(ctx, fx, fy, coords) {
    var rx = coords[0],
        ry = coords[1],
        rot = coords[2],
        large = coords[3],
        sweep = coords[4],
        tx = coords[5],
        ty = coords[6],
        segs = [[ ], [ ], [ ], [ ]],
        segsNorm = arcToSegments(tx - fx, ty - fy, rx, ry, large, sweep, rot);

    for (var i = 0, len = segsNorm.length; i < len; i++) {
      segs[i][0] = segsNorm[i][0] + fx;
      segs[i][1] = segsNorm[i][1] + fy;
      segs[i][2] = segsNorm[i][2] + fx;
      segs[i][3] = segsNorm[i][3] + fy;
      segs[i][4] = segsNorm[i][4] + fx;
      segs[i][5] = segsNorm[i][5] + fy;
      ctx.bezierCurveTo.apply(ctx, segs[i]);
    }
  };

  /**
   * Calculate bounding box of a elliptic-arc
   * @param {Number} fx start point of arc
   * @param {Number} fy
   * @param {Number} rx horizontal radius
   * @param {Number} ry vertical radius
   * @param {Number} rot angle of horizontal axe
   * @param {Number} large 1 or 0, whatever the arc is the big or the small on the 2 points
   * @param {Number} sweep 1 or 0, 1 clockwise or counterclockwise direction
   * @param {Number} tx end point of arc
   * @param {Number} ty
   */
  fabric.util.getBoundsOfArc = function(fx, fy, rx, ry, rot, large, sweep, tx, ty) {

    var fromX = 0, fromY = 0, bound = [ ], bounds = [ ],
    segs = arcToSegments(tx - fx, ty - fy, rx, ry, large, sweep, rot);

    for (var i = 0, len = segs.length; i < len; i++) {
      bound = getBoundsOfCurve(fromX, fromY, segs[i][0], segs[i][1], segs[i][2], segs[i][3], segs[i][4], segs[i][5]);
      bound[0].x += fx;
      bound[0].y += fy;
      bound[1].x += fx;
      bound[1].y += fy;
      bounds.push(bound[0]);
      bounds.push(bound[1]);
      fromX = segs[i][4];
      fromY = segs[i][5];
    }
    return bounds;
  };

  /**
   * Calculate bounding box of a beziercurve
   * @param {Number} x0 starting point
   * @param {Number} y0
   * @param {Number} x1 first control point
   * @param {Number} y1
   * @param {Number} x2 secondo control point
   * @param {Number} y2
   * @param {Number} x3 end of beizer
   * @param {Number} y3
   */
  // taken from http://jsbin.com/ivomiq/56/edit  no credits available for that.
  function getBoundsOfCurve(x0, y0, x1, y1, x2, y2, x3, y3) {
    var argsString = _join.call(arguments);
    if (boundsOfCurveCache[argsString]) {
      return boundsOfCurveCache[argsString];
    }

    var sqrt = Math.sqrt,
        min = Math.min, max = Math.max,
        abs = Math.abs, tvalues = [ ],
        bounds = [[ ], [ ]],
        a, b, c, t, t1, t2, b2ac, sqrtb2ac;

    b = 6 * x0 - 12 * x1 + 6 * x2;
    a = -3 * x0 + 9 * x1 - 9 * x2 + 3 * x3;
    c = 3 * x1 - 3 * x0;

    for (var i = 0; i < 2; ++i) {
      if (i > 0) {
        b = 6 * y0 - 12 * y1 + 6 * y2;
        a = -3 * y0 + 9 * y1 - 9 * y2 + 3 * y3;
        c = 3 * y1 - 3 * y0;
      }

      if (abs(a) < 1e-12) {
        if (abs(b) < 1e-12) {
          continue;
        }
        t = -c / b;
        if (0 < t && t < 1) {
          tvalues.push(t);
        }
        continue;
      }
      b2ac = b * b - 4 * c * a;
      if (b2ac < 0) {
        continue;
      }
      sqrtb2ac = sqrt(b2ac);
      t1 = (-b + sqrtb2ac) / (2 * a);
      if (0 < t1 && t1 < 1) {
        tvalues.push(t1);
      }
      t2 = (-b - sqrtb2ac) / (2 * a);
      if (0 < t2 && t2 < 1) {
        tvalues.push(t2);
      }
    }

    var x, y, j = tvalues.length, jlen = j, mt;
    while(j--) {
      t = tvalues[j];
      mt = 1 - t;
      x = (mt * mt * mt * x0) + (3 * mt * mt * t * x1) + (3 * mt * t * t * x2) + (t * t * t * x3);
      bounds[0][j] = x;

      y = (mt * mt * mt * y0) + (3 * mt * mt * t * y1) + (3 * mt * t * t * y2) + (t * t * t * y3);
      bounds[1][j] = y;
    }

    bounds[0][jlen] = x0;
    bounds[1][jlen] = y0;
    bounds[0][jlen + 1] = x3;
    bounds[1][jlen + 1] = y3;
    var result = [
      {
        x: min.apply(null, bounds[0]),
        y: min.apply(null, bounds[1])
      },
      {
        x: max.apply(null, bounds[0]),
        y: max.apply(null, bounds[1])
      }
    ];
    boundsOfCurveCache[argsString] = result;
    return result;
  }

  fabric.util.getBoundsOfCurve = getBoundsOfCurve;

})();
<|endoftext|>


<commit_before>(function (root, main) {
  // AMD
  if (typeof define === 'function' && define.amd) {
    define(['backbone', 'mustache', 'bloodhound', 'underscore'], main);
  // CommonJS
  } else if (typeof exports !== 'undefined' && typeof require !== 'undefined') {
    module.exports = main(require('backbone'), require('mustache'),
      require('bloodhound'), require('underscore'));
  // Globals
  } else {
    /* eslint-disable no-param-reassign */
    root.Tags = main(root.Backbone, root.Mustache, root.Bloodhound, root._);
    /* eslint-enable no-param-reassign */
  }
})(this, function (Backbone, Mustache, Bloodhound, _) {
  'use strict';

  // TODO: options.fetch / options.create / options.sync allow to provide them
  //  for each backbone method used
  // TODO: cancel, save, edit events on view on "Backbone.View.events"
  // TODO: Tags-init options tags collection url (and options.url => .libraryUrl)

  var Tags = Object.create(null);

  Tags.Model = Backbone.Model.extend({
    /**
     * checks if only has id and no tag data
     * @return {Boolean} true if it has fetched data
     */
    hasData: function () {
      return Object.keys(this.attributes).length > 1 && this.get('id');
    }
  });

  /**
   * Backbone Model containing the tag Id used as reference
   */
  Tags.Reference = Backbone.Model.extend({
    /**
     * Retrieves the corresponding tag
     *
     * @return {Backbone.Model} tag model instance
     */
    getTag: function () {
      return Tags.Library.get(this.get('id') || this.get('tagId'));
    },
    /*
     * Overwrites set to add tag reference to main library
     */
    set: function (attrs, opts) {
      // if there's a new tag id that is not in library add it
      if (attrs.id && !Tags.Library.get(attrs.id)) {
        Tags.Library.add({
          id: attrs.id
        });
      }
      return Backbone.Model.prototype.set.call(this, attrs, opts);
    }
  });
  /**
   * Collection of references to Tags associated with a parent Model
   */
  Tags.Collection = Backbone.Collection.extend({
    model: Tags.Reference,
    args: {},
    initialize: function (models, options) {
      this.args = options;
      this.parentModel = options.parentModel;
      this._url = options.url;
    },
    /**
     * Associate collection url to the parent Model
     * @return {string | undefined}
     */
    url: function () {
      return this.parentModel ? this.parentModel.url() + '/' +
        (this._url || 'tags') : (this._url || 'tags');
    },
    /**
     * Overrides the clone() method to also clone the collection parameters
     * @return {Backbone.Collection}
     */
    clone: function () {
      return new this.constructor(this.models, this.args);
    }
  });

  /**
   * Store of all the tags models
   * @type {Backbone.Collection}
   */
  Tags.LibraryCollection = Backbone.Collection.extend({
    model: Tags.Model,
    url: 'tags',
    initialize: function (options) {
      var self = this;

      this.url = options.url || this.url;
      /**
       * Used as a suggestion engine for typeahead.js to fetch tags from the
       *  server as the user inputs them.
       * Set rateLimitWait to an obscene number of requests being made to the
       * remote endpoint.
       * Set the url to the Tags.Library endpoint.
       * See more about Bloodhound in the link bellow
       * {@link https://github.com/twitter/typeahead.js/blob/master/doc/bloodhound.md}
       */
      this.bloodhound = options.bloodhound || new Bloodhound({
        datumTokenizer: Bloodhound.tokenizers.whitespace,
        queryTokenizer: Bloodhound.tokenizers.whitespace,
        identify: function (obj) {
          return obj.id;
        },
        remote: {
          rateLimitBy: options.rateLimitBy || 'throttle',
          rateLimitWait: options.rateLimitWait || 1000,
          url: self.url + '?search=%QUERY',
          wildcard: '%QUERY'
        }
      });
    },
  });

  /**
   * Templates that will be used to render the content into the web page.
   * view - template for the view mode
   * edit - template for the edit mode
   * suggestion - object containing the templates for the suggestions
   *   text - the template for the text suggestion
   *   notFound - template for when suggestions are not found
   *   pending - template for when suggestions are being fetched
   *   header - template for the header of the suggestions menu
   *   footer - template for the footer of the suggestions menu
   * @type {Object}
   */
  Tags.Templates = {
    view: '
No template for view, please add one.
',
    edit: '
No template for edit, please add one.
',
    suggestion: {
      text: '
No template for suggestions, please add one.
',
      notFound: '

',
      pending: '

',
      header: '',
      footer: ''
    }
  };

  Tags.Views = Object.create(null);

  /**
   * View for the tags
   * @param  {Object} options
   * @return {Backbone.View}
   */
  Tags.Views.List = Backbone.View.extend({
    typeahead: '[data-role="typeahead"]',
    textName: 'text',
    mode: 'view',
    oldModels: {},
    /**
     * Set the parent model.
     * Fetch the tags related with this user and render them.
     * @param  {object} options Contains the parameters such as parentModel
     * @param  {string} options.textName
     * @return {void}
     */
    initialize: function (options) {
      this.textName = options.textName || this.textName;
      this.collection = options.collection;
      this.bloodhound = options.bloodhound || Tags.Library.bloodhound;
      this.templates = options.templates || Tags.Templates;
    },
    /**
     * Get the user tags from the server
     * @return {void}
     */
    fetch: function (options) {
      var self = this;
      this.collection.fetch({
        wait: true,
        success: function () {
          var ids = self.collection.models.map(function (model) {
            return model.id;
          });
          self.oldModels = self.collection.clone();
          Tags.Library.fetch({
            wait: true,
            data: {
              ids: ids
            },
            success: function () {
              if (options.render) {
                self.render();
              }
            }
          });
        }
      });
    },
    /**
     * Set the view mode to edit.
     * Initialize bootstrap-tagsinput with typeahead.
     * {@link https://github.com/bootstrap-tagsinput/bootstrap-tagsinput}
     * {@link https://github.com/twitter/typeahead.js/blob/master/doc/jquery_typeahead.md}
     * @return {void}
     */
    editTags: function () {
      var self = this;
      if (this.mode === 'view') {
        this.oldModels = this.collection.clone();
        this.$el.html(self.templates.edit);
        this.$(this.typeahead).tagsinput({
          typeaheadjs: [{
            hint: true,
            highlight: true,
            minLength: 3
          }, {
            name: 'tags',
            displayKey: self.textName,
            source: function (query, syncResults, asyncResults) {
              self.bloodhound.search(query, syncResults, function (suggestions) {
                var filtered = suggestions.filter(function (suggestion) {
                  return suggestion[self.textName]
                    .toLowerCase()
                    .indexOf(query.toLowerCase()) >= 0;
                });
                asyncResults(filtered);
              });
            },
            templates: {
              notFound: self.templates.suggestion.notFound || '',
              pending: self.templates.suggestion.pending || '',
              header: self.templates.suggestion.header || '',
              footer: self.templates.suggestion.footer || '',
              suggestion: function (context) {
                return Mustache.render(self.templates.suggestion.text, context);
              }
            }
          }],
          itemValue: function (item) {
            return item[self.textName];
          }
        });
        // Detect tag addition event and add it to the collection if not there
        this.$(this.typeahead).on('itemAdded', function (event) {
          var exists = _(self.collection.models).some(function (model) {
            return model.tagId === event.item.id;
          });
          Tags.Library.add(event.item);
          if (!exists) {
            self.collection.add({
              tagId: event.item.id
            });
            self.trigger('tag:attach');
          }
        });
        // Detect tag remove event and remove it from the collection
        this.$(this.typeahead).on('itemRemoved', function (event) {
          self.collection.remove(event.item.cid);
          self.trigger('tag:detach');
        });
        // Add existing tags to the tagsinput for editing
        this.collection.each(function (item) {
          var model = item.getTag().toJSON();
          model.cid = item.cid;
          self.$(self.typeahead).tagsinput('add', model);
        });
        this.$(this.typeahead).tagsinput('focus');
      }
      this.mode = 'edit';
    },
    /**
     * Save the edited tags by sending them to the server
     * @return {void}
     */
    saveTags: function () {
      var self = this;
      if (this.mode === 'edit') {
        this.$el.html('');
        Backbone.sync('create', this.collection, {
          wait: true,
          success: function () {
            self.oldModels = self.collection.clone();
            self.render();
            self.trigger('tag:save');
          },
          error: function () {
            self.collection = self.oldModels;
            self.render();
          }
        });
        this.mode = 'view';
      }
    },
    /**
     * Cancel edition and restore collection to state before being edited.
     * @return {void}
     */
    cancelEdit: function () {
      if (this.mode === 'edit') {
        this.$el.html('');
        this.collection = this.oldModels;
        this.render();
        this.mode = 'view';
        this.trigger('tag:cancel');
      }
    },
    /**
     * Render a tag to web page using the template
     * @param  {Backbone.Model} item The tag object
     * @return {void}
     */
    renderTag: function (item) {
      this.$el.append(Mustache.render(this.templates.view, item.toJSON()));
    },
    /**
     * Render tags collection by rendering each tag it contains with the view
     * template and appends all the tags to the corresponding element
     * @return {void}
     */
    render: function () {
      this.collection.each(function (item) {
        this.renderTag(item.getTag());
      }, this);
    }
  });

  /**
   * Initializes the tags plugin with the inputted options
   * @param  {Object} options Contains the options to initialize the plugin
   * @return {Backbone.View}
   */
  Tags.init = function (options) {
    var opts = _.defaults(options, {
      render: true,
      fetch: true
    });
    var instance = {
      view: {}
    };

    if (!Tags.Library) {
      Tags.Library = new Tags.LibraryCollection({
        bloodhound: opts.bloodhound,
        url: opts.url
      });
    }

    instance.collection = opts.collection || new Tags.Collection([], {
      parentModel: options.parentModel
    });

    if (options.tagsElement) {
      instance.view.list = new Tags.Views.List({
        el: options.tagsElement,
        collection: instance.collection,
        bloodhound: options.bloodhound,
        templates: options.templates
      });
    }

    if (opts.fetch && instance.view.list) {
      // fetch tags from server and render them
      instance.view.list.fetch({
        render: opts.render
      });
    }

    if (opts.render && instance.view.list) {
      instance.view.list.render();
    }

    return instance;
  };

  return Tags;
});
<commit_msg>no required tagelement<commit_after>(function (root, main) {
  // AMD
  if (typeof define === 'function' && define.amd) {
    define(['backbone', 'mustache', 'bloodhound', 'underscore'], main);
  // CommonJS
  } else if (typeof exports !== 'undefined' && typeof require !== 'undefined') {
    module.exports = main(require('backbone'), require('mustache'),
      require('bloodhound'), require('underscore'));
  // Globals
  } else {
    /* eslint-disable no-param-reassign */
    root.Tags = main(root.Backbone, root.Mustache, root.Bloodhound, root._);
    /* eslint-enable no-param-reassign */
  }
})(this, function (Backbone, Mustache, Bloodhound, _) {
  'use strict';

  // TODO: options.fetch / options.create / options.sync allow to provide them
  //  for each backbone method used
  // TODO: cancel, save, edit events on view on "Backbone.View.events"
  // TODO: Tags-init options tags collection url (and options.url => .libraryUrl)

  var Tags = Object.create(null);

  Tags.Model = Backbone.Model.extend({
    /**
     * checks if only has id and no tag data
     * @return {Boolean} true if it has fetched data
     */
    hasData: function () {
      return Object.keys(this.attributes).length > 1 && this.get('id');
    }
  });

  /**
   * Backbone Model containing the tag Id used as reference
   */
  Tags.Reference = Backbone.Model.extend({
    /**
     * Retrieves the corresponding tag
     *
     * @return {Backbone.Model} tag model instance
     */
    getTag: function () {
      return Tags.Library.get(this.get('id') || this.get('tagId'));
    },
    /*
     * Overwrites set to add tag reference to main library
     */
    set: function (attrs, opts) {
      // if there's a new tag id that is not in library add it
      if (attrs.id && !Tags.Library.get(attrs.id)) {
        Tags.Library.add({
          id: attrs.id
        });
      }
      return Backbone.Model.prototype.set.call(this, attrs, opts);
    }
  });
  /**
   * Collection of references to Tags associated with a parent Model
   */
  Tags.Collection = Backbone.Collection.extend({
    model: Tags.Reference,
    args: {},
    initialize: function (models, options) {
      this.args = options;
      this.parentModel = options.parentModel;
      this._url = options.url;
    },
    /**
     * Associate collection url to the parent Model
     * @return {string | undefined}
     */
    url: function () {
      return this.parentModel ? this.parentModel.url() + '/' +
        (this._url || 'tags') : (this._url || 'tags');
    },
    /**
     * Overrides the clone() method to also clone the collection parameters
     * @return {Backbone.Collection}
     */
    clone: function () {
      return new this.constructor(this.models, this.args);
    }
  });

  /**
   * Store of all the tags models
   * @type {Backbone.Collection}
   */
  Tags.LibraryCollection = Backbone.Collection.extend({
    model: Tags.Model,
    url: 'tags',
    initialize: function (options) {
      var self = this;

      this.url = options.url || this.url;
      /**
       * Used as a suggestion engine for typeahead.js to fetch tags from the
       *  server as the user inputs them.
       * Set rateLimitWait to an obscene number of requests being made to the
       * remote endpoint.
       * Set the url to the Tags.Library endpoint.
       * See more about Bloodhound in the link bellow
       * {@link https://github.com/twitter/typeahead.js/blob/master/doc/bloodhound.md}
       */
      this.bloodhound = options.bloodhound || new Bloodhound({
        datumTokenizer: Bloodhound.tokenizers.whitespace,
        queryTokenizer: Bloodhound.tokenizers.whitespace,
        identify: function (obj) {
          return obj.id;
        },
        remote: {
          rateLimitBy: options.rateLimitBy || 'throttle',
          rateLimitWait: options.rateLimitWait || 1000,
          url: self.url + '?search=%QUERY',
          wildcard: '%QUERY'
        }
      });
    },
  });

  /**
   * Templates that will be used to render the content into the web page.
   * view - template for the view mode
   * edit - template for the edit mode
   * suggestion - object containing the templates for the suggestions
   *   text - the template for the text suggestion
   *   notFound - template for when suggestions are not found
   *   pending - template for when suggestions are being fetched
   *   header - template for the header of the suggestions menu
   *   footer - template for the footer of the suggestions menu
   * @type {Object}
   */
  Tags.Templates = {
    view: '
No template for view, please add one.
',
    edit: '
No template for edit, please add one.
',
    suggestion: {
      text: '
No template for suggestions, please add one.
',
      notFound: '

',
      pending: '

',
      header: '',
      footer: ''
    }
  };

  Tags.Views = Object.create(null);

  /**
   * View for the tags
   * @param  {Object} options
   * @return {Backbone.View}
   */
  Tags.Views.List = Backbone.View.extend({
    typeahead: '[data-role="typeahead"]',
    textName: 'text',
    mode: 'view',
    oldModels: {},
    /**
     * Set the parent model.
     * Fetch the tags related with this user and render them.
     * @param  {object} options Contains the parameters such as parentModel
     * @param  {string} options.textName
     * @return {void}
     */
    initialize: function (options) {
      this.textName = options.textName || this.textName;
      this.collection = options.collection;
      this.bloodhound = options.bloodhound || Tags.Library.bloodhound;
      this.templates = options.templates || Tags.Templates;
    },
    /**
     * Get the user tags from the server
     * @return {void}
     */
    fetch: function (options) {
      var self = this;
      this.collection.fetch({
        wait: true,
        success: function () {
          var ids = self.collection.models.map(function (model) {
            return model.id;
          });
          self.oldModels = self.collection.clone();
          Tags.Library.fetch({
            wait: true,
            data: {
              ids: ids
            },
            success: function () {
              if (options.render) {
                self.render();
              }
            }
          });
        }
      });
    },
    /**
     * Set the view mode to edit.
     * Initialize bootstrap-tagsinput with typeahead.
     * {@link https://github.com/bootstrap-tagsinput/bootstrap-tagsinput}
     * {@link https://github.com/twitter/typeahead.js/blob/master/doc/jquery_typeahead.md}
     * @return {void}
     */
    editTags: function () {
      var self = this;
      if (this.mode === 'view') {
        this.oldModels = this.collection.clone();
        this.$el.html(self.templates.edit);
        this.$(this.typeahead).tagsinput({
          typeaheadjs: [{
            hint: true,
            highlight: true,
            minLength: 3
          }, {
            name: 'tags',
            displayKey: self.textName,
            source: function (query, syncResults, asyncResults) {
              self.bloodhound.search(query, syncResults, function (suggestions) {
                var filtered = suggestions.filter(function (suggestion) {
                  return suggestion[self.textName]
                    .toLowerCase()
                    .indexOf(query.toLowerCase()) >= 0;
                });
                asyncResults(filtered);
              });
            },
            templates: {
              notFound: self.templates.suggestion.notFound || '',
              pending: self.templates.suggestion.pending || '',
              header: self.templates.suggestion.header || '',
              footer: self.templates.suggestion.footer || '',
              suggestion: function (context) {
                return Mustache.render(self.templates.suggestion.text, context);
              }
            }
          }],
          itemValue: function (item) {
            return item[self.textName];
          }
        });
        // Detect tag addition event and add it to the collection if not there
        this.$(this.typeahead).on('itemAdded', function (event) {
          var exists = _(self.collection.models).some(function (model) {
            return model.tagId === event.item.id;
          });
          Tags.Library.add(event.item);
          if (!exists) {
            self.collection.add({
              tagId: event.item.id
            });
            self.trigger('tag:attach');
          }
        });
        // Detect tag remove event and remove it from the collection
        this.$(this.typeahead).on('itemRemoved', function (event) {
          self.collection.remove(event.item.cid);
          self.trigger('tag:detach');
        });
        // Add existing tags to the tagsinput for editing
        this.collection.each(function (item) {
          var model = item.getTag().toJSON();
          model.cid = item.cid;
          self.$(self.typeahead).tagsinput('add', model);
        });
        this.$(this.typeahead).tagsinput('focus');
      }
      this.mode = 'edit';
    },
    /**
     * Save the edited tags by sending them to the server
     * @return {void}
     */
    saveTags: function () {
      var self = this;
      if (this.mode === 'edit') {
        this.$el.html('');
        Backbone.sync('create', this.collection, {
          wait: true,
          success: function () {
            self.oldModels = self.collection.clone();
            self.render();
            self.trigger('tag:save');
          },
          error: function () {
            self.collection = self.oldModels;
            self.render();
          }
        });
        this.mode = 'view';
      }
    },
    /**
     * Cancel edition and restore collection to state before being edited.
     * @return {void}
     */
    cancelEdit: function () {
      if (this.mode === 'edit') {
        this.$el.html('');
        this.collection = this.oldModels;
        this.render();
        this.mode = 'view';
        this.trigger('tag:cancel');
      }
    },
    /**
     * Render a tag to web page using the template
     * @param  {Backbone.Model} item The tag object
     * @return {void}
     */
    renderTag: function (item) {
      this.$el.append(Mustache.render(this.templates.view, item.toJSON()));
    },
    /**
     * Render tags collection by rendering each tag it contains with the view
     * template and appends all the tags to the corresponding element
     * @return {void}
     */
    render: function () {
      this.collection.each(function (item) {
        this.renderTag(item.getTag());
      }, this);
    }
  });

  /**
   * Initializes the tags plugin with the inputted options
   * @param  {Object} options Contains the options to initialize the plugin
   * @return {Backbone.View}
   */
  Tags.init = function (options) {
    var opts = _.defaults(options, {
      render: true,
      fetch: true
    });
    var instance = {
      view: {}
    };

    if (!Tags.Library) {
      Tags.Library = new Tags.LibraryCollection({
        bloodhound: opts.bloodhound,
        url: opts.url
      });
    }

    instance.collection = opts.collection || new Tags.Collection([], {
      parentModel: options.parentModel
    });

    var listInit =  {
      collection: instance.collection,
      bloodhound: options.bloodhound,
      templates: options.templates
    };

    if (options.tagsElement) {
      listInit.el = options.tagsElement;
    }

    instance.view.list = new Tags.Views.List(listInit);

    if (opts.fetch && instance.view.list) {
      // fetch tags from server and render them
      instance.view.list.fetch({
        render: opts.render
      });
    }

    if (opts.render && instance.view.list) {
      instance.view.list.render();
    }

    return instance;
  };

  return Tags;
});
<|endoftext|>


<commit_before>/**
 * @file Argument validation procedures.
 * @module util/arg
 */

'use strict'

// Imports
const ethereumAddress = require('ethereum-address')

// Type imports
const type = {
  BigNumber: require('bignumber.js'),
  Address: require('../primitives/address'),
  Amount: require('../primitives/amount'),
  Block: require('../primitives/block'),
  Taint: require('../primitives/taint'),
  Transaction: require('../primitives/transaction')
}

/**
 * Confirm argument is string literal.
 * @param {string|*} arg - Argument.
 * @return {undefined}
 * @throws {Error} If arg is not a string literal.
 */
exports.string = function string (arg) {
  if (!(typeof arg === 'string')) {
    throw new Error('Argument must be string literal')
  }
}

/**
 * Confirm argument is number literal.
 * @param {number|*} arg - Argument.
 * @return {undefined}
 * @throws {Error} If arg is not a number literal.
 */
exports.number = function number (arg) {
  if (!(typeof arg === 'number')) {
    throw new Error('Argument must be number literal')
  }
  if (Number.isNaN(arg)) {
    throw new Error('Argument must not be NaN')
  }
  if (!Number.isFinite(arg)) {
    throw new Error('Argument must be finite')
  }
}

/**
 * Confirm argument is integer literal.
 * @param {number|*} arg - Argument.
 * @return {undefined}
 * @throws {Error} If arg is not an integer literal.
 */
exports.integer = function integer (arg) {
  if (!Number.isInteger(arg)) {
    throw new Error('Argument must be integer literal')
  }
}

/**
 * Confirm argument is address hex.
 * @param {string|*} arg - Argument.
 * @return {undefined}
 * @throws {Error} If arg is not an address hex.
 */
exports.addressHex = function addressHex (arg) {
  if (!ethereumAddress.isAddress(arg)) {
    throw new Error('Argument must be an Ethereum address')
  }
}

/**
 * Confirm argument is transaction hash.
 * TODO: Validate transaction hash format.
 * @param {string|*} arg - Argument.
 * @return {undefined}
 * @throws {Error} If arg is not a string literal.
 */
exports.transactionHash = function transactionHash (arg) {
  exports.string(arg)
}

/**
 * Confirm argument is BigNumber.
 * @param {BigNumber|*} arg - Argument.
 * @return {undefined}
 * @throws {Error} If arg is not a BigNumber.
 */
exports.BigNumber = function BigNumber (arg) {
  if (!(arg instanceof type.BigNumber)) {
    throw new Error('Argument must be BigNumber')
  }
}

/**
 * Confirm argument is Address.
 * @param {module:primitives/address.Address|*} arg - Argument.
 * @return {undefined}
 * @throws {Error} If arg is not an Address.
 */
exports.Address = function Address (arg) {
  if (!(arg instanceof type.Address)) {
    throw new Error('Argument must be Address')
  }
}

/**
 * Confirm argument is Amount.
 * @param {module:primitives/amount.Amount|*} arg - Argument.
 * @return {undefined}
 * @throws {Error} If arg is not an Amount.
 */
exports.Amount = function Amount (arg) {
  if (!(arg instanceof type.Amount)) {
    throw new Error('Argument must be Amount')
  }
}

/**
 * Confirm argument is Block.
 * @param {module:primitives/block.Block|*} arg - Argument.
 * @return {undefined}
 * @throws {Error} If arg is not a Block.
 */
exports.Block = function Block (arg) {
  if (!(arg instanceof type.Block)) {
    throw new Error('Argument must be Block')
  }
}

/**
 * Confirm argument is Taint.
 * @param {module:primitives/taint.Taint|*} arg - Argument.
 * @return {undefined}
 * @throws {Error} If arg is not a Taint.
 */
exports.Taint = function Taint (arg) {
  if (!(arg instanceof type.Taint)) {
    throw new Error('Argument must be Taint')
  }
}

/**
 * Confirm argument is Transaction.
 * @param {module:primitives/transaction.Transaction|*} arg
 *     Argument.
 * @return {undefined}
 * @throws {Error} If arg is not a Transaction.
 */
exports.Transaction = function Transaction (arg) {
  if (!(arg instanceof type.Transaction)) {
    throw new Error('Argument must be Transaction')
  }
}
<commit_msg>Move arg.transactionHash todo into jsdoc tag<commit_after>/**
 * @file Argument validation procedures.
 * @module util/arg
 */

'use strict'

// Imports
const ethereumAddress = require('ethereum-address')

// Type imports
const type = {
  BigNumber: require('bignumber.js'),
  Address: require('../primitives/address'),
  Amount: require('../primitives/amount'),
  Block: require('../primitives/block'),
  Taint: require('../primitives/taint'),
  Transaction: require('../primitives/transaction')
}

/**
 * Confirm argument is string literal.
 * @param {string|*} arg - Argument.
 * @return {undefined}
 * @throws {Error} If arg is not a string literal.
 */
exports.string = function string (arg) {
  if (!(typeof arg === 'string')) {
    throw new Error('Argument must be string literal')
  }
}

/**
 * Confirm argument is number literal.
 * @param {number|*} arg - Argument.
 * @return {undefined}
 * @throws {Error} If arg is not a number literal.
 */
exports.number = function number (arg) {
  if (!(typeof arg === 'number')) {
    throw new Error('Argument must be number literal')
  }
  if (Number.isNaN(arg)) {
    throw new Error('Argument must not be NaN')
  }
  if (!Number.isFinite(arg)) {
    throw new Error('Argument must be finite')
  }
}

/**
 * Confirm argument is integer literal.
 * @param {number|*} arg - Argument.
 * @return {undefined}
 * @throws {Error} If arg is not an integer literal.
 */
exports.integer = function integer (arg) {
  if (!Number.isInteger(arg)) {
    throw new Error('Argument must be integer literal')
  }
}

/**
 * Confirm argument is address hex.
 * @param {string|*} arg - Argument.
 * @return {undefined}
 * @throws {Error} If arg is not an address hex.
 */
exports.addressHex = function addressHex (arg) {
  if (!ethereumAddress.isAddress(arg)) {
    throw new Error('Argument must be an Ethereum address')
  }
}

/**
 * Confirm argument is transaction hash.
 * @todo Validate transaction hash format.
 * @param {string|*} arg - Argument.
 * @return {undefined}
 * @throws {Error} If arg is not a string literal.
 */
exports.transactionHash = function transactionHash (arg) {
  exports.string(arg)
}

/**
 * Confirm argument is BigNumber.
 * @param {BigNumber|*} arg - Argument.
 * @return {undefined}
 * @throws {Error} If arg is not a BigNumber.
 */
exports.BigNumber = function BigNumber (arg) {
  if (!(arg instanceof type.BigNumber)) {
    throw new Error('Argument must be BigNumber')
  }
}

/**
 * Confirm argument is Address.
 * @param {module:primitives/address.Address|*} arg - Argument.
 * @return {undefined}
 * @throws {Error} If arg is not an Address.
 */
exports.Address = function Address (arg) {
  if (!(arg instanceof type.Address)) {
    throw new Error('Argument must be Address')
  }
}

/**
 * Confirm argument is Amount.
 * @param {module:primitives/amount.Amount|*} arg - Argument.
 * @return {undefined}
 * @throws {Error} If arg is not an Amount.
 */
exports.Amount = function Amount (arg) {
  if (!(arg instanceof type.Amount)) {
    throw new Error('Argument must be Amount')
  }
}

/**
 * Confirm argument is Block.
 * @param {module:primitives/block.Block|*} arg - Argument.
 * @return {undefined}
 * @throws {Error} If arg is not a Block.
 */
exports.Block = function Block (arg) {
  if (!(arg instanceof type.Block)) {
    throw new Error('Argument must be Block')
  }
}

/**
 * Confirm argument is Taint.
 * @param {module:primitives/taint.Taint|*} arg - Argument.
 * @return {undefined}
 * @throws {Error} If arg is not a Taint.
 */
exports.Taint = function Taint (arg) {
  if (!(arg instanceof type.Taint)) {
    throw new Error('Argument must be Taint')
  }
}

/**
 * Confirm argument is Transaction.
 * @param {module:primitives/transaction.Transaction|*} arg
 *     Argument.
 * @return {undefined}
 * @throws {Error} If arg is not a Transaction.
 */
exports.Transaction = function Transaction (arg) {
  if (!(arg instanceof type.Transaction)) {
    throw new Error('Argument must be Transaction')
  }
}
<|endoftext|>


<commit_before>"use strict";

var Docblock = require('./Docblock');

var assign = require('lodash.assign');
var consumers = require('./consumers');
var defaultConfig = require('./defaultConfig');

var DOCBLOCK_PATTERN = /^\s*\/\*\*[^]*\*\/\s*$/;
var TAG_PATTERN = /^\s*@([^\s]+)\s?/;

function docblockParser(config={}) {
  if (!config.text) {
    config.text = defaultConfig.text;
  }
  if (!config.default) {
    config.default = defaultConfig.default;
  }

  function parse(docstring) {
    if (!docstring || !DOCBLOCK_PATTERN.test(docstring)) {
      throw new TypeError('Argument is not a valid docstring (/** ... */).');
    }

    var text = [];
    var tags = {};
    var tag;

    var docblock = new Docblock(docstring.split('\n'));
    while (!docblock.isExhausted()) {
      consumers.emptyLines(docblock);
      var line = docblock.peek();
      if (!line) { continue;}
      var match = line.match(TAG_PATTERN);
      if (match) {
        docblock.replace(line.replace(TAG_PATTERN, ''));
        tag = match[1];
        var consumer = config.tags[tag] || config.default;
        var tagValue = consumer(docblock);
        if (!tags.hasOwnProperty(tag)) {
          tags[tag] = [];
        }
        tags[tag].push(tagValue);
      }
      else {
        text.push(config.text(docblock));
      }
    }

    // simplify entries with single values
    for (tag in tags) {
      if (tags.hasOwnProperty(tag) && tags[tag].length === 1) {
        tags[tag] = tags[tag][0];
      }
    }
    // simplify text
    if (text.length === 1) {
      text = text[0];
    }
    else if (text.length === 0) {
      text = '';
    }

    return {text: text, tags: tags};
  }

  return {parse: parse};
}

docblockParser.parse = docblockParser(defaultConfig).parse;
docblockParser.consumeTil = require('./consumeTil');
assign(docblockParser, consumers);

module.exports = docblockParser;
<commit_msg>Relax arguments test<commit_after>"use strict";

var Docblock = require('./Docblock');

var assign = require('lodash.assign');
var consumers = require('./consumers');
var defaultConfig = require('./defaultConfig');

var DOCBLOCK_PATTERN = /^\/\*\*|^\s*\* ?/m;
var TAG_PATTERN = /^\s*@([^\s]+)\s?/;

function docblockParser(config={}) {
  if (!config.text) {
    config.text = defaultConfig.text;
  }
  if (!config.default) {
    config.default = defaultConfig.default;
  }

  function parse(docstring) {
    if (!docstring || !DOCBLOCK_PATTERN.test(docstring)) {
      throw new TypeError(
        "Argument does not appear to be a valid docstring. A docstring " +
        "usually starts with /**, ends with */ and every line in between " +
        "starts with a single *. This is what I got instead:\n\n" +
        docstring
      );
    }

    var text = [];
    var tags = {};
    var tag;

    var docblock = new Docblock(docstring.split('\n'));
    while (!docblock.isExhausted()) {
      consumers.emptyLines(docblock);
      var line = docblock.peek();
      if (!line) { continue;}
      var match = line.match(TAG_PATTERN);
      if (match) {
        docblock.replace(line.replace(TAG_PATTERN, ''));
        tag = match[1];
        var consumer = config.tags[tag] || config.default;
        var tagValue = consumer(docblock);
        if (!tags.hasOwnProperty(tag)) {
          tags[tag] = [];
        }
        tags[tag].push(tagValue);
      }
      else {
        text.push(config.text(docblock));
      }
    }

    // simplify entries with single values
    for (tag in tags) {
      if (tags.hasOwnProperty(tag) && tags[tag].length === 1) {
        tags[tag] = tags[tag][0];
      }
    }
    // simplify text
    if (text.length === 1) {
      text = text[0];
    }
    else if (text.length === 0) {
      text = '';
    }

    return {text: text, tags: tags};
  }

  return {parse: parse};
}

docblockParser.parse = docblockParser(defaultConfig).parse;
docblockParser.consumeTil = require('./consumeTil');
assign(docblockParser, consumers);

module.exports = docblockParser;
<|endoftext|>


<commit_before>var express = require('express')
	, bodyParser = require('body-parser')
	, expressValidator = require('express-validator')
	, log = require('npmlog')

	// , Cloud = require('vigour-js/browser/network/cloud')
	// 	.inject(require('vigour-js/browser/network/cloud/datacloud'))
	// , Data = require('vigour-js/data')
	, fs = require('vigour-fs')

	// , spriteMaker = require('./spriteMaker')
	, imgManip = require('./imgManip')
	, util = require('./util')
	, setHeaders = require('./setHeaders')

	, config = require('./config')

	// , cloud = new Cloud('ws://' + config.cloudHost + ':' + config.cloudPort)
	// , data = new Data(cloud.data.get(config.mtvCloudDataFieldName))

	// , subscribeObj = {}

console.log('starting')

Object.defineProperty(Error.prototype, 'toJSON', {
    value: function () {
        var alt = {}

        Object.getOwnPropertyNames(this).forEach(function (key) {
            alt[key] = this[key]
        }, this)

        return alt
    },
    configurable: true
})

app = express();

app.use(function (req, res, next) {
	console.log(req.method, req.originalUrl)
	next()
})

app.use(bodyParser.urlencoded({
	extended: true
}))

app.use(expressValidator())

app.get('/:image/:width/:height'
	, validateDimensions
	, validateEffects
	, cacheForever(true)
	, makeOut
	, serveCached
	, prepare
	, function (req, res, next) {
		var url = req.query.url
		req.pathToOriginal = config.originalsPath + "/" + req.query.url.slice(req.query.url.lastIndexOf('/') + 1)
		fs.exists(req.pathToOriginal, function (exists) {
			if (exists) {
				next()
			} else {
				console.log("Downloading original image")
				fs.writeFile(req.pathToOriginal
					, url
					, {
						maxTries: config.maxTries
						, retryOn404: true	// MTV's image server sometimes returns 404 even if image does exists, i.e. retrying may work
					}
					, function (err) {
						if (err) {
							err.details = 'vigour-fs.write (download) error'
							err.path = req.pathToOriginal
							err.data = url
							log.error(err.details, err)
							res.status(500).end(JSON.stringify(err, null, " "))
							util.cleanup(req.tmpDir)
						} else {
							next()
						}
					})
				}
			})
	}
	, function (req, res, next) {
		console.log("Transforming image")
		imgManip.effect(req.query
			, req.pathToOriginal
			, req.dimensions
			, req.out
			, function (err, newPath) {
				if (err) {
					err.details = "imgManip.effect error"
					err.query = req.query
					err.path = req.pathToOriginal
					err.dimensions = req.dimensions
					err.out = req.out
					res.status(500).end(JSON.stringify(err, null, " "))
					util.cleanup(req.tmpDir)
				} else {
					console.log("Serving image")
					serve(res, newPath, req.cacheForever, function (err) {
						util.cleanup(req.tmpDir)
					})
				}
			})
	})

app.get('/image/:id/:width/:height'
	, validateDimensions
	, validateImgId
	, validateEffects
	, cacheForever(true)
	, makeOut
	, serveCached
	, prepare
	, function (req, res, next) {
		var url = util.urlFromId(req.params.id)
		req.pathToOriginal = config.originalsPath + '/' + req.params.id
		fs.exists(req.pathToOriginal, function (exists) {
			if (exists) {
				next()
			} else {
				console.log("Downloading original image")
				fs.writeFile(req.pathToOriginal
					, url
					, {
						maxTries: config.maxTries
						, retryOn404: true	// MTV's image server sometimes returns 404 even if image does exists, i.e. retrying may work
					}
					, function (err) {
						if (err) {
							err.details = 'vigour-fs.write (download) error'
							err.path = req.pathToOriginal
							err.data = url
							log.error(err.details, err)
							res.status(500).end(JSON.stringify(err, null, " "))
							util.cleanup(req.tmpDir)
						} else {
							next()
						}
					})
				}
			})
	}
	, function (req, res, next) {
		console.log("Transforming image")
		imgManip.effect(req.query
			, req.pathToOriginal
			, req.dimensions
			, req.out
			, function (err, newPath) {
				if (err) {
					err.details = "imgManip.effect error"
					err.query = req.query
					err.path = req.pathToOriginal
					err.dimensions = req.dimensions
					err.out = req.out
					res.status(500).end(JSON.stringify(err, null, " "))
					util.cleanup(req.tmpDir)
				} else {
					console.log("Serving image")
					serve(res, newPath, req.cacheForever, function (err) {
						util.cleanup(req.tmpDir)
					})
				}
			})
	})

// app.get('/sprite/:country/:lang/shows/:width/:height'
// 	, validateDimensions
// 	, cacheForever(false)
// 	, makeOut
// 	, serveCached
// 	, prepare
// 	, function (req, res, next) {
// 		var p = req.params
// 		req.pathToSpriteData = [p.country
// 			, p.lang
// 			, 'shows'
// 		]
// 		next()
// 	}
// 	, requestSprite)

// app.get('/sprite/:country/:lang/episodes/:showId/:seasonId/:width/:height'
// 	, validateDimensions
// 	, cacheForever(false)
// 	, makeOut
// 	, serveCached
// 	, prepare
// 	, function (req, res, next) {
// 		var p = req.params
// 		req.pathToSpriteData = [p.country
// 			, p.lang
// 			, 'shows'
// 			, p.showId
// 			, 'seasons'
// 			, p.seasonId
// 			, 'episodes'
// 		]
// 		next()
// 	}
// 	, requestSprite)

app.get('*'
	, function (req, res, next) {
		invalidRequest(res)
	})

// cloud.on('welcome', function (err) {
// 	console.log('cloud welcome')
// })

// cloud.on('error', function (err) {
// 	log.error('cloud error', err)
// })

// subscribeObj[config.mtvCloudDataFieldName] =  {
// 	$: {
// 		$: {
// 			shows: {
// 				$: {
// 					img: true
// 					, number: true
// 					, seasons: {
// 						$: {
// 							number: true
// 							, episodes: {
// 								$: {
// 									img: true
// 									, number: true
// 								}
// 							}
// 						}
// 					}
// 				}
// 			},
// 			channels: {
// 				$: {
// 					img: true
// 					, number: true
// 				}
// 			}
// 		}
// 	}
// }

// cloud.subscribe(subscribeObj)

// data.addListener(listen)

// function listen () {
// 	console.log("Listen called")
	app.listen(config.port);
	console.log('Listening on port ', config.port);
// 	this.removeListener(listen)
// }

function serveCached (req, res, next) {
	var filePath = req.out + '.jpg'
	serveIfExists(filePath
		, req.cacheForever
		, res
		, function (err) {
			if (err) {
				filePath = req.out + '.png'
				serveIfExists(filePath
					, req.cachedForever
					, res
					, function (err) {
						if (err) {
							next()
						}
					})
			}
		})
}

function serveIfExists (path, cacheForever, res, cb) {
	fs.exists(path, function (exists) {
		if (exists) {
			console.log('Serving existing file')
			serve(res, path, cacheForever)
			cb(null)
		} else {
			cb(true)
		}
	})
}

function cacheForever (bool) {
	return function (req, res, next) {
		req.cacheForever = bool
		next()
	}
}

function serve (res, path, cacheForever, cb) {
	setHeaders(res, cacheForever)
	res.sendFile(path
		, {
			root: __dirname
		}
		, function (err) {
			if (err) {
				err.message += ": sendFile error"
				if (err.code === "ECONNABORT" && res.statusCode === 304) {
					log.info('sent 304 for', path)
				} else {
					log.error('Error sending file', err)
					// TODO Warn dev
				}
			} else {
				console.log("sendFile succeeds", path)
			}
			if (cb) {
				cb(err)
			}
		})
}

function makeOut (req, res, next) {
	console.log('making out')
	try {
		req.out = config.outDir + '/' + encodeURIComponent(req.originalUrl)
		next()
	} catch (e) {
		invalidRequest(res)
	}
}

function invalidRequest (res) {
	console.log('Serving 400')
	res.status(400).end(config.invalidRequestMessage)
}

function prepare (req, res, next) {
	req.tmpDir = config.tmpDir + '/' + Math.random().toString().slice(1)
	console.log('creating temp directory')
	fs.mkdir(req.tmpDir, function (err) {
		if (err) {
			err.detail = 'fs.mkdir error'
			err.path = req.tmpDir
			res.status(500).end(JSON.stringify(err, null, " "))
		} else {
			req.dimensions = {
				width: req.params.width
				, height: req.params.height
			}
			next()
		}
	})
}

function validateEffects (req, res, next) {
	var errors
		, validEffects = [
			'composite'
			, 'mask'
			, 'overlay'
			, 'tMask'
			, 'blur'
			, 'overlayBlur'
			, 'smartResize'
		]
		, fileNameRE = /^[a-zA-Z][\w\.-]*$/
	console.log('validating effects')
	if (req.query.effect) {
		req.checkQuery('effect', 'effect should be a valid effect').isIn(validEffects)
		if (!errors) {
			if (~['mask', 'tMask'].indexOf(req.query.effect)) {
				req.checkQuery('mask', "mask should be a valid file name, without the extension").matches(fileNameRE)
				if (req.query.effect === 'mask') {
					req.checkQuery('fillColor', "fillColor should be a valid hexadecimal color").isHexColor()
					req.sanitize('fillColor').blacklist('#')
				}
			} else if (~['overlayBlur', 'overlay', 'composite'].indexOf(req.query.effect)) {
				req.checkQuery('overlay', '').matches(fileNameRE)
			}
			if (~['overlayBlur', 'blur'].indexOf(req.query.effect)) {
				req.checkQuery('radius', "radius should be an integer").isInt()
				req.checkQuery('sigma', "sigma should be an integer").isInt()
			}
		}
	}
	errors = req.validationErrors()
	if (errors) {
		res.status(400).end(config.invalidRequestMessage + '\n' + JSON.stringify(errors))
	} else {
		next()
	}
}

function validateImgId (req, res, next) {
	var errors
	console.log('validating image id')
	req.checkParams('id', "id should be alphanumeric").isAlphanumeric()
	errors = req.validationErrors()
	if (errors) {
		res.status(400).end(config.invalidRequestMessage + '\n' + JSON.stringify(errors))
	} else {
		next()
	}
}

function validateDimensions (req, res, next) {
	var errors
		, widthError = false
		, heightError = false
	console.log('validating dimensions')
	req.checkParams('width', 'width should be an integer').isInt()
	req.checkParams('height', 'height should be an integer').isInt()
	errors = req.validationErrors()

	if (parseInt(req.params.width, 10) > config.maxWidth) {
		widthError = true
	}
	if (parseInt(req.params.height, 10) > config.maxHeight) {
		heightError = true
	}

	if (errors || widthError || heightError) {
		res.status(400).end(config.invalidRequestMessage + '\n' + JSON.stringify(errors))
	} else {
		next()
	}
}

// function requestSprite (req, res, next) {
// 	console.log('requesting sprite')
// 	spriteMaker.requestSprite(req.pathToSpriteData
// 		, data
// 		, req.params
// 		, req.tmpDir
// 		, req.dimensions
// 		, req.out
// 		, function (err, spritePath, cb) {
// 			if (err) {
// 				err.pathToSpriteData = req.pathToSpriteData
// 				err.params = err.params
// 				log.error('spriteMaker.requestSprite error', err)
// 				res.status(err.status).end(JSON.stringify(err, null, " "))
// 			} else {
// 				console.log("Serving sprite")
// 				serve(res, spritePath, req.cacheForever, function (err) {
// 					util.cleanup(req.tmpDir)
// 				})
// 			}
// 		})
// }<commit_msg>validate that dimensions are both above 0<commit_after>var express = require('express')
	, bodyParser = require('body-parser')
	, expressValidator = require('express-validator')
	, log = require('npmlog')

	// , Cloud = require('vigour-js/browser/network/cloud')
	// 	.inject(require('vigour-js/browser/network/cloud/datacloud'))
	// , Data = require('vigour-js/data')
	, fs = require('vigour-fs')

	// , spriteMaker = require('./spriteMaker')
	, imgManip = require('./imgManip')
	, util = require('./util')
	, setHeaders = require('./setHeaders')

	, config = require('./config')

	// , cloud = new Cloud('ws://' + config.cloudHost + ':' + config.cloudPort)
	// , data = new Data(cloud.data.get(config.mtvCloudDataFieldName))

	// , subscribeObj = {}

console.log('starting')

Object.defineProperty(Error.prototype, 'toJSON', {
    value: function () {
        var alt = {}

        Object.getOwnPropertyNames(this).forEach(function (key) {
            alt[key] = this[key]
        }, this)

        return alt
    },
    configurable: true
})

app = express();

app.use(function (req, res, next) {
	console.log(req.method, req.originalUrl)
	next()
})

app.use(bodyParser.urlencoded({
	extended: true
}))

app.use(expressValidator())

app.get('/:image/:width/:height'
	, validateDimensions
	, validateEffects
	, cacheForever(true)
	, makeOut
	, serveCached
	, prepare
	, function (req, res, next) {
		var url = req.query.url
		req.pathToOriginal = config.originalsPath + "/" + req.query.url.slice(req.query.url.lastIndexOf('/') + 1)
		fs.exists(req.pathToOriginal, function (exists) {
			if (exists) {
				next()
			} else {
				console.log("Downloading original image")
				fs.writeFile(req.pathToOriginal
					, url
					, {
						maxTries: config.maxTries
						, retryOn404: true	// MTV's image server sometimes returns 404 even if image does exists, i.e. retrying may work
					}
					, function (err) {
						if (err) {
							err.details = 'vigour-fs.write (download) error'
							err.path = req.pathToOriginal
							err.data = url
							log.error(err.details, err)
							res.status(500).end(JSON.stringify(err, null, " "))
							util.cleanup(req.tmpDir)
						} else {
							next()
						}
					})
				}
			})
	}
	, function (req, res, next) {
		console.log("Transforming image")
		imgManip.effect(req.query
			, req.pathToOriginal
			, req.dimensions
			, req.out
			, function (err, newPath) {
				if (err) {
					err.details = "imgManip.effect error"
					err.query = req.query
					err.path = req.pathToOriginal
					err.dimensions = req.dimensions
					err.out = req.out
					res.status(500).end(JSON.stringify(err, null, " "))
					util.cleanup(req.tmpDir)
				} else {
					console.log("Serving image")
					serve(res, newPath, req.cacheForever, function (err) {
						util.cleanup(req.tmpDir)
					})
				}
			})
	})

app.get('/image/:id/:width/:height'
	, validateDimensions
	, validateImgId
	, validateEffects
	, cacheForever(true)
	, makeOut
	, serveCached
	, prepare
	, function (req, res, next) {
		var url = util.urlFromId(req.params.id)
		req.pathToOriginal = config.originalsPath + '/' + req.params.id
		fs.exists(req.pathToOriginal, function (exists) {
			if (exists) {
				next()
			} else {
				console.log("Downloading original image")
				fs.writeFile(req.pathToOriginal
					, url
					, {
						maxTries: config.maxTries
						, retryOn404: true	// MTV's image server sometimes returns 404 even if image does exists, i.e. retrying may work
					}
					, function (err) {
						if (err) {
							err.details = 'vigour-fs.write (download) error'
							err.path = req.pathToOriginal
							err.data = url
							log.error(err.details, err)
							res.status(500).end(JSON.stringify(err, null, " "))
							util.cleanup(req.tmpDir)
						} else {
							next()
						}
					})
				}
			})
	}
	, function (req, res, next) {
		console.log("Transforming image")
		imgManip.effect(req.query
			, req.pathToOriginal
			, req.dimensions
			, req.out
			, function (err, newPath) {
				if (err) {
					err.details = "imgManip.effect error"
					err.query = req.query
					err.path = req.pathToOriginal
					err.dimensions = req.dimensions
					err.out = req.out
					res.status(500).end(JSON.stringify(err, null, " "))
					util.cleanup(req.tmpDir)
				} else {
					console.log("Serving image")
					serve(res, newPath, req.cacheForever, function (err) {
						util.cleanup(req.tmpDir)
					})
				}
			})
	})

// app.get('/sprite/:country/:lang/shows/:width/:height'
// 	, validateDimensions
// 	, cacheForever(false)
// 	, makeOut
// 	, serveCached
// 	, prepare
// 	, function (req, res, next) {
// 		var p = req.params
// 		req.pathToSpriteData = [p.country
// 			, p.lang
// 			, 'shows'
// 		]
// 		next()
// 	}
// 	, requestSprite)

// app.get('/sprite/:country/:lang/episodes/:showId/:seasonId/:width/:height'
// 	, validateDimensions
// 	, cacheForever(false)
// 	, makeOut
// 	, serveCached
// 	, prepare
// 	, function (req, res, next) {
// 		var p = req.params
// 		req.pathToSpriteData = [p.country
// 			, p.lang
// 			, 'shows'
// 			, p.showId
// 			, 'seasons'
// 			, p.seasonId
// 			, 'episodes'
// 		]
// 		next()
// 	}
// 	, requestSprite)

app.get('*'
	, function (req, res, next) {
		invalidRequest(res)
	})

// cloud.on('welcome', function (err) {
// 	console.log('cloud welcome')
// })

// cloud.on('error', function (err) {
// 	log.error('cloud error', err)
// })

// subscribeObj[config.mtvCloudDataFieldName] =  {
// 	$: {
// 		$: {
// 			shows: {
// 				$: {
// 					img: true
// 					, number: true
// 					, seasons: {
// 						$: {
// 							number: true
// 							, episodes: {
// 								$: {
// 									img: true
// 									, number: true
// 								}
// 							}
// 						}
// 					}
// 				}
// 			},
// 			channels: {
// 				$: {
// 					img: true
// 					, number: true
// 				}
// 			}
// 		}
// 	}
// }

// cloud.subscribe(subscribeObj)

// data.addListener(listen)

// function listen () {
// 	console.log("Listen called")
	app.listen(config.port);
	console.log('Listening on port ', config.port);
// 	this.removeListener(listen)
// }

function serveCached (req, res, next) {
	var filePath = req.out + '.jpg'
	serveIfExists(filePath
		, req.cacheForever
		, res
		, function (err) {
			if (err) {
				filePath = req.out + '.png'
				serveIfExists(filePath
					, req.cachedForever
					, res
					, function (err) {
						if (err) {
							next()
						}
					})
			}
		})
}

function serveIfExists (path, cacheForever, res, cb) {
	fs.exists(path, function (exists) {
		if (exists) {
			console.log('Serving existing file')
			serve(res, path, cacheForever)
			cb(null)
		} else {
			cb(true)
		}
	})
}

function cacheForever (bool) {
	return function (req, res, next) {
		req.cacheForever = bool
		next()
	}
}

function serve (res, path, cacheForever, cb) {
	setHeaders(res, cacheForever)
	res.sendFile(path
		, {
			root: __dirname
		}
		, function (err) {
			if (err) {
				err.message += ": sendFile error"
				if (err.code === "ECONNABORT" && res.statusCode === 304) {
					log.info('sent 304 for', path)
				} else {
					log.error('Error sending file', err)
					// TODO Warn dev
				}
			} else {
				console.log("sendFile succeeds", path)
			}
			if (cb) {
				cb(err)
			}
		})
}

function makeOut (req, res, next) {
	console.log('making out')
	try {
		req.out = config.outDir + '/' + encodeURIComponent(req.originalUrl)
		next()
	} catch (e) {
		invalidRequest(res)
	}
}

function invalidRequest (res) {
	console.log('Serving 400')
	res.status(400).end(config.invalidRequestMessage)
}

function prepare (req, res, next) {
	req.tmpDir = config.tmpDir + '/' + Math.random().toString().slice(1)
	console.log('creating temp directory')
	fs.mkdir(req.tmpDir, function (err) {
		if (err) {
			err.detail = 'fs.mkdir error'
			err.path = req.tmpDir
			res.status(500).end(JSON.stringify(err, null, " "))
		} else {
			req.dimensions = {
				width: req.params.width
				, height: req.params.height
			}
			next()
		}
	})
}

function validateEffects (req, res, next) {
	var errors
		, validEffects = [
			'composite'
			, 'mask'
			, 'overlay'
			, 'tMask'
			, 'blur'
			, 'overlayBlur'
			, 'smartResize'
		]
		, fileNameRE = /^[a-zA-Z][\w\.-]*$/
	console.log('validating effects')
	if (req.query.effect) {
		req.checkQuery('effect', 'effect should be a valid effect').isIn(validEffects)
		if (!errors) {
			if (~['mask', 'tMask'].indexOf(req.query.effect)) {
				req.checkQuery('mask', "mask should be a valid file name, without the extension").matches(fileNameRE)
				if (req.query.effect === 'mask') {
					req.checkQuery('fillColor', "fillColor should be a valid hexadecimal color").isHexColor()
					req.sanitize('fillColor').blacklist('#')
				}
			} else if (~['overlayBlur', 'overlay', 'composite'].indexOf(req.query.effect)) {
				req.checkQuery('overlay', '').matches(fileNameRE)
			}
			if (~['overlayBlur', 'blur'].indexOf(req.query.effect)) {
				req.checkQuery('radius', "radius should be an integer").isInt()
				req.checkQuery('sigma', "sigma should be an integer").isInt()
			}
		}
	}
	errors = req.validationErrors()
	if (errors) {
		res.status(400).end(config.invalidRequestMessage + '\n' + JSON.stringify(errors))
	} else {
		next()
	}
}

function validateImgId (req, res, next) {
	var errors
	console.log('validating image id')
	req.checkParams('id', "id should be alphanumeric").isAlphanumeric()
	errors = req.validationErrors()
	if (errors) {
		res.status(400).end(config.invalidRequestMessage + '\n' + JSON.stringify(errors))
	} else {
		next()
	}
}

function validateDimensions (req, res, next) {
	var errors
		, width
		, height
		, widthError = false
		, heightError = false
	console.log('validating dimensions')
	req.checkParams('width', 'width should be an integer').isInt()
	req.checkParams('height', 'height should be an integer').isInt()
	errors = req.validationErrors()
	width = parseInt(req.params.width, 10)
	if (width > config.maxWidth || width < 1) {
		widthError = true
	}
	height = parseInt(req.params.height, 10)
	if (height > config.maxHeight || height < 1) {
		heightError = true
	}

	if (errors || widthError || heightError) {
		res.status(400).end(config.invalidRequestMessage + '\n' + JSON.stringify(errors))
	} else {
		next()
	}
}

// function requestSprite (req, res, next) {
// 	console.log('requesting sprite')
// 	spriteMaker.requestSprite(req.pathToSpriteData
// 		, data
// 		, req.params
// 		, req.tmpDir
// 		, req.dimensions
// 		, req.out
// 		, function (err, spritePath, cb) {
// 			if (err) {
// 				err.pathToSpriteData = req.pathToSpriteData
// 				err.params = err.params
// 				log.error('spriteMaker.requestSprite error', err)
// 				res.status(err.status).end(JSON.stringify(err, null, " "))
// 			} else {
// 				console.log("Serving sprite")
// 				serve(res, spritePath, req.cacheForever, function (err) {
// 					util.cleanup(req.tmpDir)
// 				})
// 			}
// 		})
// }<|endoftext|>


<commit_before>'use strict'

module.exports = [
  '0.0.1'
, '0.0.2'
, '0.0.3'
, '0.0.4'
, '0.0.5'
, '0.0.6'
, '0.1.0'
, '0.1.1'
, '0.1.2'
, '0.1.3'
, '0.1.4'
, '0.1.5'
, '0.1.6'
, '0.1.7'
, '0.1.8'
, '0.1.9'
, '0.1.10'
, '0.1.11'
, '0.1.12'
, '0.1.13'
, '0.1.14'
, '0.1.15'
, '0.1.16'
, '0.1.17'
, '0.1.18'
, '0.1.19'
, '0.1.20'
, '0.1.21'
, '0.1.22'
, '0.1.23'
, '0.1.24'
, '0.1.25'
, '0.1.26'
, '0.1.27'
, '0.1.28'
, '0.1.29'
, '0.1.30'
, '0.1.31'
, '0.1.32'
, '0.1.33'
, '0.1.90'
, '0.1.91'
, '0.1.92'
, '0.1.93'
, '0.1.94'
, '0.1.95'
, '0.1.96'
, '0.1.97'
, '0.1.98'
, '0.1.99'
, '0.1.100'
, '0.1.101'
, '0.1.102'
, '0.1.103'
, '0.1.104'
, '0.2.0'
, '0.2.1'
, '0.2.2'
, '0.2.3'
, '0.2.4'
, '0.2.5'
, '0.2.6'
, '0.3.0'
, '0.3.1'
, '0.3.2'
, '0.3.3'
, '0.3.4'
, '0.3.5'
, '0.3.6'
, '0.3.7'
, '0.3.8'
, '0.4.0'
, '0.4.1'
, '0.4.2'
, '0.4.3'
, '0.4.4'
, '0.4.5'
, '0.4.6'
, '0.4.7'
, '0.4.8'
, '0.4.9'
, '0.4.10'
, '0.4.11'
, '0.4.12'
, '0.5.0'
, '0.5.1'
, '0.5.2'
, '0.5.3'
, '0.5.4'
, '0.5.5'
, '0.5.6'
, '0.5.7'
, '0.5.8'
, '0.5.9'
, '0.5.10'
, '0.6.0'
, '0.6.1'
, '0.6.2'
, '0.6.3'
, '0.6.4'
, '0.6.5'
, '0.6.6'
, '0.6.7'
, '0.6.8'
, '0.6.9'
, '0.6.10'
, '0.6.11'
, '0.6.12'
, '0.6.13'
, '0.6.14'
, '0.6.15'
, '0.6.16'
, '0.6.17'
, '0.6.18'
, '0.6.19'
, '0.6.20'
, '0.6.21'
, '0.7.0'
, '0.7.1'
, '0.7.2'
, '0.7.3'
, '0.7.4'
, '0.7.5'
, '0.7.6'
, '0.7.7'
, '0.7.8'
, '0.7.9'
, '0.7.10'
, '0.7.11'
, '0.7.12'
, '0.8.0'
, '0.8.1'
, '0.8.2'
, '0.8.3'
, '0.8.4'
, '0.8.5'
, '0.8.6'
, '0.8.7'
, '0.8.8'
, '0.8.9'
, '0.8.10'
, '0.8.11'
, '0.8.12'
, '0.8.13'
, '0.8.14'
, '0.8.15'
, '0.8.16'
, '0.8.17'
, '0.8.18'
, '0.8.19'
, '0.8.20'
, '0.8.21'
, '0.8.22'
, '0.8.23'
, '0.8.24'
, '0.8.25'
, '0.8.26'
, '0.8.27'
, '0.8.28'
, '0.9.0'
, '0.9.1'
, '0.9.2'
, '0.9.3'
, '0.9.4'
, '0.9.5'
, '0.9.6'
, '0.9.7'
, '0.9.8'
, '0.9.9'
, '0.9.10'
, '0.9.11'
, '0.9.12'
, '0.10.0'
, '0.10.1'
, '0.10.2'
, '0.10.3'
, '0.10.4'
, '0.10.5'
, '0.10.6'
, '0.10.7'
, '0.10.8'
, '0.10.9'
, '0.10.10'
, '0.10.11'
, '0.10.12'
, '0.10.13'
, '0.10.14'
, '0.10.15'
, '0.10.16'
, '0.10.17'
, '0.10.18'
, '0.10.19'
, '0.10.20'
, '0.10.21'
, '0.10.22'
, '0.10.23'
, '0.10.24'
, '0.10.25'
, '0.10.26'
, '0.10.27'
, '0.10.28'
, '0.10.29'
, '0.10.30'
, '0.10.31'
, '0.10.32'
, '0.10.33'
, '0.10.34'
, '0.10.35'
, '0.10.36'
, '0.10.37'
, '0.10.38'
, '0.10.39'
, '0.10.40'
, '0.10.41'
, '0.10.42'
, '0.10.43'
, '0.10.44'
, '0.11.0'
, '0.11.1'
, '0.11.2'
, '0.11.3'
, '0.11.4'
, '0.11.5'
, '0.11.6'
, '0.11.7'
, '0.11.8'
, '0.11.9'
, '0.11.10'
, '0.11.11'
, '0.11.12'
, '0.11.13'
, '0.11.14'
, '0.11.15'
, '0.11.16'
, '0.12.0'
, '0.12.1'
, '0.12.2'
, '0.12.3'
, '0.12.4'
, '0.12.5'
, '0.12.6'
, '0.12.7'
, '0.12.8'
, '0.12.9'
, '0.12.10'
, '0.12.11'
, '0.12.12'
, '1.0.0'
, '1.0.1'
, '1.0.2'
, '1.0.3'
, '1.0.4'
, '1.1.0'
, '1.2.0'
, '1.3.0'
, '1.4.1'
, '1.4.2'
, '1.4.3'
, '1.5.0'
, '1.5.1'
, '1.6.0'
, '1.6.1'
, '1.6.2'
, '1.6.3'
, '1.6.4'
, '1.7.0'
, '1.7.1'
, '1.8.1'
, '1.8.2'
, '1.8.3'
, '1.8.4'
, '2.0.0'
, '2.0.1'
, '2.0.2'
, '2.1.0'
, '2.2.0'
, '2.2.1'
, '2.3.0'
, '2.3.1'
, '2.3.2'
, '2.3.3'
, '2.3.4'
, '2.4.0'
, '2.5.0'
, '3.0.0'
, '3.1.0'
, '3.2.0'
, '3.3.0'
, '3.3.1'
, '4.0.0'
, '4.1.0'
, '4.1.1'
, '4.1.2'
, '4.2.0'
, '4.2.1'
, '4.2.2'
, '4.2.3'
, '4.2.4'
, '4.2.5'
, '4.2.6'
, '4.3.0'
, '4.3.1'
, '4.3.2'
, '4.4.0'
, '4.4.1'
, '4.4.2'
, '5.0.0'
, '5.1.0'
, '5.1.1'
, '5.2.0'
, '5.3.0'
, '5.4.0'
, '5.4.1'
, '5.5.0'
, '5.6.0'
, '5.7.0'
, '5.7.1'
, '5.8.0'
, '5.9.0'
, '5.9.1'
]
<commit_msg>add version 0.12.13<commit_after>'use strict'

module.exports = [
  '0.0.1'
, '0.0.2'
, '0.0.3'
, '0.0.4'
, '0.0.5'
, '0.0.6'
, '0.1.0'
, '0.1.1'
, '0.1.2'
, '0.1.3'
, '0.1.4'
, '0.1.5'
, '0.1.6'
, '0.1.7'
, '0.1.8'
, '0.1.9'
, '0.1.10'
, '0.1.11'
, '0.1.12'
, '0.1.13'
, '0.1.14'
, '0.1.15'
, '0.1.16'
, '0.1.17'
, '0.1.18'
, '0.1.19'
, '0.1.20'
, '0.1.21'
, '0.1.22'
, '0.1.23'
, '0.1.24'
, '0.1.25'
, '0.1.26'
, '0.1.27'
, '0.1.28'
, '0.1.29'
, '0.1.30'
, '0.1.31'
, '0.1.32'
, '0.1.33'
, '0.1.90'
, '0.1.91'
, '0.1.92'
, '0.1.93'
, '0.1.94'
, '0.1.95'
, '0.1.96'
, '0.1.97'
, '0.1.98'
, '0.1.99'
, '0.1.100'
, '0.1.101'
, '0.1.102'
, '0.1.103'
, '0.1.104'
, '0.2.0'
, '0.2.1'
, '0.2.2'
, '0.2.3'
, '0.2.4'
, '0.2.5'
, '0.2.6'
, '0.3.0'
, '0.3.1'
, '0.3.2'
, '0.3.3'
, '0.3.4'
, '0.3.5'
, '0.3.6'
, '0.3.7'
, '0.3.8'
, '0.4.0'
, '0.4.1'
, '0.4.2'
, '0.4.3'
, '0.4.4'
, '0.4.5'
, '0.4.6'
, '0.4.7'
, '0.4.8'
, '0.4.9'
, '0.4.10'
, '0.4.11'
, '0.4.12'
, '0.5.0'
, '0.5.1'
, '0.5.2'
, '0.5.3'
, '0.5.4'
, '0.5.5'
, '0.5.6'
, '0.5.7'
, '0.5.8'
, '0.5.9'
, '0.5.10'
, '0.6.0'
, '0.6.1'
, '0.6.2'
, '0.6.3'
, '0.6.4'
, '0.6.5'
, '0.6.6'
, '0.6.7'
, '0.6.8'
, '0.6.9'
, '0.6.10'
, '0.6.11'
, '0.6.12'
, '0.6.13'
, '0.6.14'
, '0.6.15'
, '0.6.16'
, '0.6.17'
, '0.6.18'
, '0.6.19'
, '0.6.20'
, '0.6.21'
, '0.7.0'
, '0.7.1'
, '0.7.2'
, '0.7.3'
, '0.7.4'
, '0.7.5'
, '0.7.6'
, '0.7.7'
, '0.7.8'
, '0.7.9'
, '0.7.10'
, '0.7.11'
, '0.7.12'
, '0.8.0'
, '0.8.1'
, '0.8.2'
, '0.8.3'
, '0.8.4'
, '0.8.5'
, '0.8.6'
, '0.8.7'
, '0.8.8'
, '0.8.9'
, '0.8.10'
, '0.8.11'
, '0.8.12'
, '0.8.13'
, '0.8.14'
, '0.8.15'
, '0.8.16'
, '0.8.17'
, '0.8.18'
, '0.8.19'
, '0.8.20'
, '0.8.21'
, '0.8.22'
, '0.8.23'
, '0.8.24'
, '0.8.25'
, '0.8.26'
, '0.8.27'
, '0.8.28'
, '0.9.0'
, '0.9.1'
, '0.9.2'
, '0.9.3'
, '0.9.4'
, '0.9.5'
, '0.9.6'
, '0.9.7'
, '0.9.8'
, '0.9.9'
, '0.9.10'
, '0.9.11'
, '0.9.12'
, '0.10.0'
, '0.10.1'
, '0.10.2'
, '0.10.3'
, '0.10.4'
, '0.10.5'
, '0.10.6'
, '0.10.7'
, '0.10.8'
, '0.10.9'
, '0.10.10'
, '0.10.11'
, '0.10.12'
, '0.10.13'
, '0.10.14'
, '0.10.15'
, '0.10.16'
, '0.10.17'
, '0.10.18'
, '0.10.19'
, '0.10.20'
, '0.10.21'
, '0.10.22'
, '0.10.23'
, '0.10.24'
, '0.10.25'
, '0.10.26'
, '0.10.27'
, '0.10.28'
, '0.10.29'
, '0.10.30'
, '0.10.31'
, '0.10.32'
, '0.10.33'
, '0.10.34'
, '0.10.35'
, '0.10.36'
, '0.10.37'
, '0.10.38'
, '0.10.39'
, '0.10.40'
, '0.10.41'
, '0.10.42'
, '0.10.43'
, '0.10.44'
, '0.11.0'
, '0.11.1'
, '0.11.2'
, '0.11.3'
, '0.11.4'
, '0.11.5'
, '0.11.6'
, '0.11.7'
, '0.11.8'
, '0.11.9'
, '0.11.10'
, '0.11.11'
, '0.11.12'
, '0.11.13'
, '0.11.14'
, '0.11.15'
, '0.11.16'
, '0.12.0'
, '0.12.1'
, '0.12.2'
, '0.12.3'
, '0.12.4'
, '0.12.5'
, '0.12.6'
, '0.12.7'
, '0.12.8'
, '0.12.9'
, '0.12.10'
, '0.12.11'
, '0.12.12'
, '0.12.13'
, '1.0.0'
, '1.0.1'
, '1.0.2'
, '1.0.3'
, '1.0.4'
, '1.1.0'
, '1.2.0'
, '1.3.0'
, '1.4.1'
, '1.4.2'
, '1.4.3'
, '1.5.0'
, '1.5.1'
, '1.6.0'
, '1.6.1'
, '1.6.2'
, '1.6.3'
, '1.6.4'
, '1.7.0'
, '1.7.1'
, '1.8.1'
, '1.8.2'
, '1.8.3'
, '1.8.4'
, '2.0.0'
, '2.0.1'
, '2.0.2'
, '2.1.0'
, '2.2.0'
, '2.2.1'
, '2.3.0'
, '2.3.1'
, '2.3.2'
, '2.3.3'
, '2.3.4'
, '2.4.0'
, '2.5.0'
, '3.0.0'
, '3.1.0'
, '3.2.0'
, '3.3.0'
, '3.3.1'
, '4.0.0'
, '4.1.0'
, '4.1.1'
, '4.1.2'
, '4.2.0'
, '4.2.1'
, '4.2.2'
, '4.2.3'
, '4.2.4'
, '4.2.5'
, '4.2.6'
, '4.3.0'
, '4.3.1'
, '4.3.2'
, '4.4.0'
, '4.4.1'
, '4.4.2'
, '5.0.0'
, '5.1.0'
, '5.1.1'
, '5.2.0'
, '5.3.0'
, '5.4.0'
, '5.4.1'
, '5.5.0'
, '5.6.0'
, '5.7.0'
, '5.7.1'
, '5.8.0'
, '5.9.0'
, '5.9.1'
]
<|endoftext|>


<commit_before>var Path = require("path");
var Dust = require("dustjs-linkedin");
var LoaderUtils = require("loader-utils");
var Async = require("Async");
var FS = require("fs");

// Use the `paths` option to generate a template name based on the
// path of the .dust file.
//
// E.g. if paths is ["C:\MyApp\dust"] and resourcePath is
// "C:\MyApp\dust\home\widgets\welcome.dust" then the template name
// will be "home-widgets-welcome".
var uniqueNumber = 1;
var moduleMapping = {};

function getTemplateName(paths, resourcePath) {
    // We want to loop through each path and see if it matches part of the file path.
    for (var i = 0; i < paths.length; i++) {
        var path = paths[i];

        if (resourcePath.indexOf(path) == 0) {
            if (Path.basename(resourcePath) == "index.dust") {
                resourcePath = Path.dirname(resourcePath);
            } else {
                resourcePath = resourcePath.replace(/\.dust$/i, "");
            }

            return resourcePath
                .replace(path + Path.sep, "")
                // If the path includes slashes or spaces, replace them with hyphens.
                .replace(/[\\\/\s]+/g, "-");
        }
    }

    resourcePath = Path.resolve(resourcePath);
    if (resourcePath in moduleMapping)
        return moduleMapping[resourcePath];

    // XXX what to do here?
    // If it's not in a known path, does that mean it's just not a partial, and
    // therefore doesn't need a name? (E.g. we could just return "anonymous123"
    // as long as each .dust file gets a different number?)
    return moduleMapping[resourcePath] = "anonymousTemplate" + (uniqueNumber++);
}

function resolveDependency(ctx, dep, paths, callback) {
    if (dep.partialName) {
        resolvePartialName(dep.partialName, paths, callback);
    } else {
        ctx.resolve(ctx.context, dep.moduleName + ".dust", function(err, path) {
            if (err)
                return ctx.resolve(ctx.context, dep.moduleName + "/index.dust", callback);

            return callback(null, path);
        });
    }
}

function resolvePartialNameInPath(name, path, callback) {
    console.log("Looking for", name, "in", path)

    if (name.length === 0)
        name = ["index"];

    var head = name[0],
        dustFile = Path.join(path, (head || "index") + ".dust"),
        subPath = Path.join(path, head);

    FS.exists(dustFile, function(hasDust) {
        // Found it!
        if (hasDust)
            return callback(null, dustFile);

        if (!head)
            return callback(null, false);

        FS.exists(subPath, function(folderExists) {
            // Found a matching folder! We must go deeper.
            if (folderExists) {
                return resolvePartialNameInPath(name.slice(1), subPath, callback);
            }

            // Not found. However, we might just need to try a longer path component.
            // E.g. framework-item-grading-list could be framework/item/grading-list
            // or framework-item/grading/list...
            if (name.length > 1) {
                var newName = name.slice(2);
                newName.unshift(name[0] + "-" + name[1]);
                //name = (name[0] + "-" + name[1]).concat(name.slice(2));
                return resolvePartialNameInPath(newName, path, callback);
            }

            // Not found, or possible to find.
            return callback(null, false);
        });
    });
}

function resolvePartialName(name, paths, callback) {
    if (typeof name === "string")
        return resolvePartialName(name.split(/-/), paths, callback);

    Async.eachSeries(paths, function(path, cb) {
        resolvePartialNameInPath(name, path, function(err, result) {
            if (err)
                return cb(err);

            // Found it (early return)
            if (result)
                return callback(null, result);

            // Try the next path
            return cb();
        });
    }, function(err) {
        if (err)
            return callback(err);

        // If we get here, we didn't find it.
        return callback(undefined);
    });
}

// Find {>".path/to/partial"/} references using a regular expression, and
// record where it was found (so that we can rewrite it later).
//
// This cannot possibly support interpolated template names, because the
// partial name isn't actually known until runtime.
//
// (A solution to this involving `require.context` is left as an exercise
// to the reader.)
function findDependencies(content) {
    var partials = [];
    var partialRegExp = /\{>\s*(?:([\w\d\-]+)|[^"]*"([^"]*)")/g;
    var match;

    while ((match = partialRegExp.exec(content))) {
        var partialName = match[1],
            moduleName = match[2];

        // Ignore interpolated template names, which have to use quotes
        if (moduleName && (moduleName.indexOf('{') > -1))
            continue;

        partials.push({
            partialName: partialName,
            moduleName: moduleName,
            index: match.index,
            length: match[0].length
        });
    }

    return partials;
}

// Replaces {>"./path/to/template"/} references with whatever the template's name
// should be (taking the `paths` option into account).
function rewriteDependencies(content, deps) {
    var index = 0, // Current position in the content string
        chunks = [];

    deps.forEach(function(dep) {
        chunks.push(content.substring(index, dep.index));
        chunks.push('{>' + dep.newName);
        index = dep.index + dep.length;
    });

    if (index < content.length)
        chunks.push(content.substring(index));

    return chunks.join("");
}

// Generate some require statements, so that the partials get loaded when the template
// is required.
function getDependenciesJS(deps) {
    return deps.map(function(dep) {
        return "require(" + JSON.stringify(dep.rawPath) + ");";
    }).join("\n");
}

module.exports = function(content) {
    var query = LoaderUtils.parseQuery(this.query);

    this.cacheable && this.cacheable();

    // Resolving module references is asynchronous.
    var cb = this.async();

    // Partial paths
    var paths = [];
    if (query.path)
        paths.push(query.path);

    if (query.paths)
        paths.push.apply(paths, query.paths);

    var deps = findDependencies(content);

    Async.eachSeries(deps, function(dep, callback) {
        resolveDependency(this, dep, paths, function(err, rawPath) {
            if (err)
                return callback(err);

            if (!rawPath)
                return callback(new Error("Unable to resolve dust partial: " + (dep.partialName || dep.moduleName)));

            console.log("Resolved", (dep.partialName || dep.moduleName), "to", rawPath);

            dep.rawPath = rawPath;
            this.addDependency(rawPath);

            var newName = getTemplateName(paths, rawPath);
            if (dep.partialName && dep.partialName != newName)
                return callback(new Error("Partial name doesn't resolve to itself: " + dep.partialName + " -> " + newName));
            dep.newName = newName;

            return callback();
        }.bind(this));
    }.bind(this), function(err) {
        if (err)
            return cb(err);

        content = rewriteDependencies(content, deps);
        var templateName = getTemplateName(paths, this.resourcePath);

        var compiled;
        try {
            compiled = Dust.compile(content, templateName);
        } catch(e) {
            return cb(e);
        }

        var output = [
            // The output of the compile function requires that the variable 'dust' exists. Without a module system, 'dust'
            // would exist on the window, making it a global variable.
            "var dust = require('dustjs-linkedin');\n",

            // Any require() statements for partials.
            getDependenciesJS(deps),

            // Compile the template returning an stringified IIFE registering the template under the name in 'templateName'.
            compiled + "\n",

            // Return the template name to make the require statement more meaningful.
            "module.exports = " + JSON.stringify(templateName) + ";"
        ].join("");

        return cb(null, output);
    }.bind(this));

};
<commit_msg>Less verbose loading (except when query.verbose is true)<commit_after>var Path = require("path");
var Dust = require("dustjs-linkedin");
var LoaderUtils = require("loader-utils");
var Async = require("Async");
var FS = require("fs");

// Use the `paths` option to generate a template name based on the
// path of the .dust file.
//
// E.g. if paths is ["C:\MyApp\dust"] and resourcePath is
// "C:\MyApp\dust\home\widgets\welcome.dust" then the template name
// will be "home-widgets-welcome".
var uniqueNumber = 1;
var moduleMapping = {};

function getTemplateName(paths, resourcePath) {
    // We want to loop through each path and see if it matches part of the file path.
    for (var i = 0; i < paths.length; i++) {
        var path = paths[i];

        if (resourcePath.indexOf(path) == 0) {
            if (Path.basename(resourcePath) == "index.dust")
                resourcePath = Path.dirname(resourcePath);
            else
                resourcePath = resourcePath.replace(/\.dust$/i, "");

            return resourcePath
                .replace(path + Path.sep, "")
                // If the path includes slashes or spaces, replace them with hyphens.
                .replace(/[\\\/\s]+/g, "-");
        }
    }

    // If it's not in a known path, which means it's just not a partial, and
    // therefore doesn't need a name. (We just return "anonymous123", where
    // each .dust file gets a different number.)

    // Don't generate different template names for the same template.
    resourcePath = Path.resolve(resourcePath);
    if (resourcePath in moduleMapping)
        return moduleMapping[resourcePath];

    return moduleMapping[resourcePath] = "anonymousTemplate" + (uniqueNumber++);
}

function resolveDependency(ctx, dep, paths, callback) {
    if (dep.partialName) {
        resolvePartialName(dep.partialName, paths, callback);
    } else {
        ctx.resolve(ctx.context, dep.moduleName + ".dust", function(err, path) {
            if (err)
                return ctx.resolve(ctx.context, dep.moduleName + "/index.dust", callback);

            return callback(null, path);
        });
    }
}

function resolvePartialNameInPath(name, path, callback) {
    //console.log("Looking for", name, "in", path)

    if (name.length === 0)
        name = ["index"];

    var head = name[0],
        dustFile = Path.join(path, (head || "index") + ".dust"),
        subPath = Path.join(path, head);

    FS.exists(dustFile, function(hasDust) {
        // Found it!
        if (hasDust)
            return callback(null, dustFile);

        if (!head)
            return callback(null, false);

        FS.exists(subPath, function(folderExists) {
            // Found a matching folder! We must go deeper.
            if (folderExists) {
                return resolvePartialNameInPath(name.slice(1), subPath, callback);
            }

            // Not found. However, we might just need to try a longer path component.
            // E.g. framework-item-grading-list could be framework/item/grading-list
            // or framework-item/grading/list...
            if (name.length > 1) {
                var newName = name.slice(2);
                newName.unshift(name[0] + "-" + name[1]);
                //name = (name[0] + "-" + name[1]).concat(name.slice(2));
                return resolvePartialNameInPath(newName, path, callback);
            }

            // Not found, or possible to find.
            return callback(null, false);
        });
    });
}

function resolvePartialName(name, paths, callback) {
    if (typeof name === "string")
        return resolvePartialName(name.split(/-/), paths, callback);

    Async.eachSeries(paths, function(path, cb) {
        resolvePartialNameInPath(name, path, function(err, result) {
            if (err)
                return cb(err);

            // Found it (early return)
            if (result)
                return callback(null, result);

            // Try the next path
            return cb();
        });
    }, function(err) {
        if (err)
            return callback(err);

        // If we get here, we didn't find it.
        return callback(undefined);
    });
}

// Find {>".path/to/partial"/} references using a regular expression, and
// record where it was found (so that we can rewrite it later).
//
// This cannot possibly support interpolated template names, because the
// partial name isn't actually known until runtime.
//
// (A solution to this involving `require.context` is left as an exercise
// to the reader.)
function findDependencies(content) {
    var partials = [];
    var partialRegExp = /\{>\s*(?:([\w\d\-]+)|[^"]*"([^"]*)")/g;
    var match;

    while ((match = partialRegExp.exec(content))) {
        var partialName = match[1],
            moduleName = match[2];

        // Ignore interpolated template names, which have to use quotes
        if (moduleName && (moduleName.indexOf('{') > -1))
            continue;

        partials.push({
            partialName: partialName,
            moduleName: moduleName,
            index: match.index,
            length: match[0].length
        });
    }

    return partials;
}

// Replaces {>"./path/to/template"/} references with whatever the template's name
// should be (taking the `paths` option into account).
function rewriteDependencies(content, deps) {
    var index = 0, // Current position in the content string
        chunks = [];

    deps.forEach(function(dep) {
        chunks.push(content.substring(index, dep.index));
        chunks.push('{>' + dep.newName);
        index = dep.index + dep.length;
    });

    if (index < content.length)
        chunks.push(content.substring(index));

    return chunks.join("");
}

// Generate some require statements, so that the partials get loaded when the template
// is required.
function getDependenciesJS(deps) {
    return deps.map(function(dep) {
        return "require(" + JSON.stringify(dep.rawPath) + ");";
    }).join("\n");
}

module.exports = function(content) {
    var query = LoaderUtils.parseQuery(this.query);

    this.cacheable && this.cacheable();

    // Resolving module references is asynchronous.
    var cb = this.async();

    // Partial paths
    var paths = [];
    if (query.path)
        paths.push(query.path);

    if (query.paths)
        paths.push.apply(paths, query.paths);

    var deps = findDependencies(content);

    Async.eachSeries(deps, function(dep, callback) {
        resolveDependency(this, dep, paths, function(err, rawPath) {
            if (err)
                return callback(err);

            if (!rawPath)
                return callback(new Error("Unable to resolve dust partial: " + (dep.partialName || dep.moduleName)));

            if (query.verbose)
                console.log("Resolved", (dep.partialName || dep.moduleName), "to", rawPath);

            dep.rawPath = rawPath;
            this.addDependency(rawPath);

            var newName = getTemplateName(paths, rawPath);
            if (dep.partialName && dep.partialName != newName)
                return callback(new Error("Partial name doesn't resolve to itself: " + dep.partialName + " -> " + newName));
            dep.newName = newName;

            return callback();
        }.bind(this));
    }.bind(this), function(err) {
        if (err)
            return cb(err);

        content = rewriteDependencies(content, deps);
        var templateName = getTemplateName(paths, this.resourcePath);

        var compiled;
        try {
            compiled = Dust.compile(content, templateName);
        } catch(e) {
            return cb(e);
        }

        var output = [
            // The output of the compile function requires that the variable 'dust' exists. Without a module system, 'dust'
            // would exist on the window, making it a global variable.
            "var dust = require('dustjs-linkedin');\n",

            // Any require() statements for partials.
            getDependenciesJS(deps),

            // Compile the template returning an stringified IIFE registering the template under the name in 'templateName'.
            "\n" + compiled + "\n",

            // Return the template name to make the require statement more meaningful.
            "module.exports = " + JSON.stringify(templateName) + ";"
        ].join("");

        return cb(null, output);
    }.bind(this));

};
<|endoftext|>


<commit_before>import { BrowserWindow } from 'electron';
import path from 'path';

export const showDesktopSettings = () => {
  if (WindowManager.getAll('settings').length > 0) {
    WindowManager.getAll('settings')[0].show();
    return;
  }
  const desktopSettings = new BrowserWindow({
    width: 800,
    height: 400,
    frame: false,
    show: false,
    nodeIntegration: true,
    icon: path.resolve(`${__dirname}/../../../assets/img/main.png`),
    title: 'Settings',
  });
  desktopSettings.loadURL(`file://${__dirname}/../../../public_html/desktop_settings.html`);

  WindowManager.add(desktopSettings, 'settings');
  WindowManager.forceFocus(desktopSettings);
};

Emitter.on('window:settings', () => {
  // const mainWindow = WindowManager.getAll('main')[0];
  showDesktopSettings();
});

Emitter.on('settings:set', (event, details) => {
  Settings.set(details.key, details.value);
  // DEV: React to settings change
  switch (details.key) {
    case 'miniAlwaysShowSongInfo':
      Emitter.sendToGooglePlayMusic('miniAlwaysShowSongInfo', { state: details.value });
      break;
    case 'miniAlwaysOnTop':
      Emitter.sendToGooglePlayMusic('miniAlwaysOnTop', { state: details.value });
      break;
    case 'speechRecognition':
      Emitter.sendToGooglePlayMusic('speech:toggle', { state: details.value });
      break;
    default:
      break;
  }
});
<commit_msg>Increase settings window height to fit contents.<commit_after>import { BrowserWindow } from 'electron';
import path from 'path';

export const showDesktopSettings = () => {
  if (WindowManager.getAll('settings').length > 0) {
    WindowManager.getAll('settings')[0].show();
    return;
  }
  const desktopSettings = new BrowserWindow({
    width: 800,
    height: 480,
    frame: false,
    show: false,
    nodeIntegration: true,
    icon: path.resolve(`${__dirname}/../../../assets/img/main.png`),
    title: 'Settings',
  });
  desktopSettings.loadURL(`file://${__dirname}/../../../public_html/desktop_settings.html`);

  WindowManager.add(desktopSettings, 'settings');
  WindowManager.forceFocus(desktopSettings);
};

Emitter.on('window:settings', () => {
  // const mainWindow = WindowManager.getAll('main')[0];
  showDesktopSettings();
});

Emitter.on('settings:set', (event, details) => {
  Settings.set(details.key, details.value);
  // DEV: React to settings change
  switch (details.key) {
    case 'miniAlwaysShowSongInfo':
      Emitter.sendToGooglePlayMusic('miniAlwaysShowSongInfo', { state: details.value });
      break;
    case 'miniAlwaysOnTop':
      Emitter.sendToGooglePlayMusic('miniAlwaysOnTop', { state: details.value });
      break;
    case 'speechRecognition':
      Emitter.sendToGooglePlayMusic('speech:toggle', { state: details.value });
      break;
    default:
      break;
  }
});
<|endoftext|>