<commit_before>/**
* GeoJSON Schemas for Mongoose
*
* rough GeoJSON schemas for use with mongoose schema creation
*
* Based on GeoJSON Spec @ http://geojson.org/geojson-spec.html
*
* Created by Ben Dalton (ben@rideamigos) on 3/27/14.
* Copyright RideAmigos (http://rideamigos.com)
**/

var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var crs = {};

function validateCrs(crs) {
  if (typeof crs !== 'object' && crs !== null) {
    throw new mongoose.Error('Crs must be an object or null');
  }
  if (crs === null) {
    return;
  }
  if (!crs.type) {
    throw new mongoose.Error('Crs must have a type');
  }
  if (crs.type !== 'name' && crs.type !== 'link') {
    throw new mongoose.Error('Crs must be either a name or link');
  }
  if (!crs.properties) {
    throw new mongoose.Error('Crs must contain a properties object');
  }
  if (crs.type === 'name' && !crs.properties.name) {
    throw new mongoose.Error('Crs specified by name must have a name property');
  }
  if (crs.type === 'link' && !crs.properties.href || crs.type === 'link' && !crs.properties.type) {
    throw new mongoose.Error('Crs specified by link must have a name and href property');
  }
}

/**
* @SchemaType GeoJSON
*
*/

function GeoJSON(key, options) {
  mongoose.SchemaType.call(this, key, options, 'GeoJSON');
}

GeoJSON.prototype = Object.create(mongoose.SchemaType.prototype);

GeoJSON.prototype.cast = function(geojson) {
  if (!geojson.type) {
    throw new mongoose.Error('GeoJSON objects must have a type');
  }
  switch (geojson.type) {
    case 'Point':
      validatePointObject(geojson);
      break;
    case 'MultiPoint':
      validateMultiPointObject(geojson);
      break;
    case 'LineString':
      validateLineStringObject(geojson);
      break;
    case 'MultiLineString':
      validateMultiLineStringObject(geojson);
      break;
    case 'Polygon':
      validatePolygonObject(geojson);
      break;
    case 'MultiPolygon':
      validateMultiPolygonObject(geojson);
      break;
    case 'Geometry':
      validateGeometryObject(geojson);
      break;
    case 'GeometryCollection':
      validateGeometryCollectionObject(geojson);
      break;
    case 'Feature':
      validateFeatureObject(geojson);
      break;
    case 'FeatureCollection':
      validateFeatureCollectionObject(geojson);
      break;
    default:
      throw new mongoose.Error(geojson.type + ' is not a valid GeoJSON type');

  }
  return geojson;
};

Schema.Types.GeoJSON = GeoJSON;

/**
* @SchemaType Point
*
*/

function Point(key, options) {
  mongoose.SchemaType.call(this, key, options, 'Point');
}

function validatePoint(coordinates) {
  // must be an array (object)
  if (typeof coordinates !== 'object') {
    throw new mongoose.Error('Point ' + coordinates + ' must be an array');
  }
  // must have 2/3 points
  if (coordinates.length < 2 || coordinates.length > 3) {
    throw new mongoose.Error('Point' + coordinates + ' must contain two or three coordinates');
  }
  // must have two numbers
  if (typeof coordinates[0] !== 'number' || typeof coordinates[1] !== 'number') {
    throw new mongoose.Error('Point must have two numbers');
  }
  if (!crs) {
    // longitude must be within bounds
    if (coordinates[0] > 180 || coordinates[0] < -180) {
      throw new mongoose.Error('Point' + coordinates[0] + ' should be within the boundaries of latitude');
    }
    // latitude must be within bounds
    if (coordinates[1] > 90 || coordinates[1] < -90) {
      throw new mongoose.Error('Point' + coordinates[1] + ' should be within the boundaries of latitude');
    }
  }
}

Point.prototype = Object.create(mongoose.SchemaType.prototype);

Point.prototype.cast = function(point) {
  if (!point.type) {
    throw new mongoose.Error('Point', point.type, 'point.type');
  }
  // type must be Point
  if (point.type !== 'Point') {
    throw new mongoose.Error(point.type + ' is not a valid GeoJSON type');
  }
  // check for crs
  if (point.crs) {
    crs = point.crs;
    validateCrs(crs);
  } else {
    crs = undefined;
  }
  validatePoint(point.coordinates);
  return point;
};

mongoose.Schema.Types.Point = Point;

/**
* @SchemaType MultiPoint
*
*/

function MultiPoint(key, options) {
  mongoose.SchemaType.call(this, key, options, 'MultiPoint');
}

function validateMultiPoint(coordinates) {
  for (var i = 0; i < coordinates.length; i++) {
    validatePoint(coordinates[i]);
  }
}

MultiPoint.prototype = Object.create(mongoose.SchemaType.prototype);

MultiPoint.prototype.cast = function(multipoint) {
  // must be an array (object)
  if (typeof multipoint.coordinates !== 'object') {
    throw new mongoose.Error('MultiPoint must be an array');
  }
  if (!multipoint.type) {
    throw new mongoose.Error('MultiPoint must have a type');
  }
  // type must be MultiPoint
  if (multipoint.type !== 'MultiPoint') {
    throw new mongoose.Error(multipoint.type + ' is not a valid GeoJSON type');
  }
  // check for crs
  if (multipoint.crs) {
    crs = multipoint.crs;
    validateCrs(crs);
  }
  validateMultiPoint(multipoint.coordinates);
  return multipoint;
};

mongoose.Schema.Types.MultiPoint = MultiPoint;

/**
* @SchemaType LineString
*
*/

function LineString(key, options) {
  mongoose.SchemaType.call(this, key, options, 'LineString');
}

function validateLineString(coordinates) {
  for (var i = 0; i < coordinates.length; i++) {
    validatePoint(coordinates[i]);
  }
}

LineString.prototype = Object.create(mongoose.SchemaType.prototype);

LineString.prototype.cast = function(linestring) {
  if (!linestring.type) {
    throw new mongoose.Error('LineString must have a type');
  }
  // type must be LineString
  if (linestring.type !== 'LineString') {
    throw new mongoose.Error(linestring.type + ' is not a valid GeoJSON type');
  }
  // must have at least two Points
  if (linestring.coordinates.length < 2) {
    throw new mongoose.Error('LineString type must have at least two Points');
  }
  // check for crs
  if (linestring.crs) {
    crs = linestring.crs;
    validateCrs(crs);
  }
  validateLineString(linestring.coordinates);
  return linestring;
};

mongoose.Schema.Types.LineString = LineString;

/**
* @SchemaType MultiLineString
*
*/

function MultiLineString(key, options) {
  mongoose.SchemaType.call(this, key, options, 'MultiLineString');
}

function validateMultiLineString(coordinates) {
  for (var i = 0; i < coordinates.length; i++) {
    validateLineString(coordinates[i]);
  }
}

MultiLineString.prototype = Object.create(mongoose.SchemaType.prototype);

MultiLineString.prototype.cast = function(multilinestring) {
  // must be an array (object)
  if (typeof multilinestring.coordinates !== 'object') {
    throw new mongoose.Error('MultiLineString must be an array');
  }
  if (!multilinestring.type) {
    throw new mongoose.Error('MultiLineString', multilinestring.type + ' must have a type');
  }
  // type must be MultiLineString
  if (multilinestring.type !== 'MultiLineString') {
    throw new mongoose.Error(multilinestring.type + ' is not a valid GeoJSON type');
  }
  validateMultiLineString(multilinestring.coordinates);
  return multilinestring;
};

mongoose.Schema.Types.MultiLineString = MultiLineString;

/**
* @SchemaType Polygon
*
*/

function Polygon(key, options) {
  mongoose.SchemaType.call(this, key, options, 'Polygon');
}

function arraysEqual(arr1, arr2) {
  if(arr1.length !== arr2.length) return false;
  for(var i = arr1.length; i--;) {
    if(arr1[i] !== arr2[i])
    return false;
  }
  return true;
}

function validatePolygon(coordinates) {
  for (var i = 0; i < coordinates.length; i++) {
    // The LinearRing elements must have at least four Points
    if (coordinates[i].length < 4) {
      throw new mongoose.Error('Each Polygon LinearRing must have at least four elements');
    }
    // the LinearRing objects must have identical start and end values
    if (!arraysEqual(coordinates[i][0], coordinates[i][coordinates[i].length-1])) {
      throw new mongoose.Error('Each Polygon LinearRing must have an identical first and last point');
    }
    // otherwise the LinearRings must correspond to a LineString
    validateLineString(coordinates[i]);
  }
}

Polygon.prototype = Object.create(mongoose.SchemaType.prototype);

Polygon.prototype.cast = function(polygon) {
  if (!polygon.type) {
    throw new mongoose.Error('Polygon', polygon.type + ' must have a type');
  }
  // type must be Polygon
  if (polygon.type !== 'Polygon') {
    throw new mongoose.Error(polygon.type + ' is not a valid GeoJSON type');
  }
  // check for crs
  if (polygon.crs) {
    crs = polygon.crs;
    validateCrs(crs);
  }
  validatePolygon(polygon.coordinates);
  return polygon;
};

mongoose.Schema.Types.Polygon = Polygon;

/**
* @SchemaType MultiPolygon
*
*/

function MultiPolygon(key, options) {
  mongoose.SchemaType.call(this, key, options, 'MultiPolygon');
}

function validateMultiPolygon(coordinates) {
  for (var i = 0; i < coordinates.length; i++) {
    validatePolygon(coordinates[i]);
  }
}

MultiPolygon.prototype = Object.create(mongoose.SchemaType.prototype);

MultiPolygon.prototype.cast = function(multipolygon) {
  // must be an array (object)
  if (typeof multipolygon.coordinates !== 'object') {
    throw new mongoose.Error('MultiPolygon must be an array');
  }
  if (!multipolygon.type) {
    throw new mongoose.Error('MultiPolygon must have a type');
  }
  // type must be Polygon
  if (multipolygon.type !== 'MultiPolygon') {
    throw new mongoose.Error(multipolygon.type + ' is not a valid GeoJSON type');
  }
  // check for crs
  if (multipolygon.crs) {
    crs = multipolygon.crs;
    validateCrs(crs);
  }
  validateMultiPolygon(multipolygon.coordinates);
  return multipolygon;
};

mongoose.Schema.Types.MultiPolygon = MultiPolygon;

/**
* @SchemaType Geometry
*
*/

function Geometry(key, options) {
  mongoose.SchemaType.call(this, key, options, 'Geometry');
}

function validateGeometry(geometry) {
  switch (geometry.type) {
    case 'Point':
    validatePoint(geometry.coordinates);
    break;
    case 'MultiPoint':
    validateMultiPoint(geometry.coordinates);
    break;
    case 'LineString':
    validateLineString(geometry.coordinates);
    break;
    case 'MultiLineString':
    validateMultiLineString(geometry.coordinates);
    break;
    case 'Polygon':
    validatePolygon(geometry.coordinates);
    break;
    case 'MultiPolygon':
    validateMultiPolygon(geometry.coordinates);
    break;
    default:
    throw new mongoose.Error('Geometry must have a valid type');
  }
}

Geometry.prototype = Object.create(mongoose.SchemaType.prototype);

Geometry.prototype.cast = function(geometry) {
  // console.log(geometry);
  // must be an array (object)
  if (!geometry.type) {
    throw new mongoose.Error('Geometry must must have a type');
  }
  // check for crs
  if (geometry.crs) {
    crs = geometry.crs;
    validateCrs(crs);
  }
  validateGeometry(geometry);
  return geometry;
};

mongoose.Schema.Types.Geometry = Geometry;

/**
* @SchemaType GeometryCollection
*
*/

function GeometryCollection(key, options) {
  mongoose.SchemaType.call(this, key, options, 'GeometryCollection');
}

function validateGeometries(geometries) {
  for (var i = 0; i < geometries.length; i++) {
    validateGeometry(geometries[i]);
  }
}

GeometryCollection.prototype = Object.create(mongoose.SchemaType.prototype);

GeometryCollection.prototype.cast = function(geometrycollection) {
  // must be an array (object)
  if (typeof geometrycollection.geometries !== 'object') {
    throw new mongoose.Error('GeometryCollection must be an array');
  }
  // check for crs
  if (geometrycollection.crs) {
    crs = geometrycollection.crs;
    validateCrs(crs);
  }
  validateGeometries(geometrycollection.geometries);
  return geometrycollection;
};

mongoose.Schema.Types.GeometryCollection = GeometryCollection;

/**
* @SchemaType Feature
*
*/

function Feature(key, options) {
  mongoose.SchemaType.call(this, key, options, 'Feature');
}

function validateFeature(feature) {
  if (!feature.type) {
    throw new mongoose.Error('Feature must have a type');
  }
  // type must be Feature
  if (feature.type !== 'Feature') {
    throw new mongoose.Error(feature.type + ' is not a valid GeoJSON type');
  }
  if (!feature.geometry) {
    throw new mongoose.Error('Feature must have a geometry');
  }
  // check for crs
  if (feature.crs) {
    crs = feature.crs;
    validateCrs(crs);
  }
  validateGeometry(feature.geometry);
}

Feature.prototype = Object.create(mongoose.SchemaType.prototype);

Feature.prototype.cast = function(feature) {
  validateFeature(feature);
  return feature;
};

mongoose.Schema.Types.Feature = Feature;

/**
* @SchemaType FeatureCollection
*
*/

function FeatureCollection(key, options) {
  mongoose.SchemaType.call(this, key, options, 'FeatureCollection');
}

function validateFeatureCollection(featurecollection) {
  for (var i = 0; i < featurecollection.features.length; i++) {
    validateFeature(featurecollection.features[i]);
  }
  return featurecollection;
}

FeatureCollection.prototype = Object.create(mongoose.SchemaType.prototype);

FeatureCollection.prototype.cast = function(featurecollection) {
  if (!featurecollection.type) {
    throw new mongoose.Error('FeatureCollection must have a type');
  }
  // type must be Polygon
  if (featurecollection.type !== 'FeatureCollection') {
    throw new mongoose.Error(featurecollection.type + ' is not a valid GeoJSON type');
  }
  if (!featurecollection.features) {
    throw new mongoose.Error('FeatureCollections must have a features object');
  }
  // check for crs
  if (featurecollection.crs) {
    crs = featurecollection.crs;
    validateCrs(crs);
  }
  validateFeatureCollection(featurecollection);
  return featurecollection;
};

mongoose.Schema.Types.FeatureCollection = FeatureCollection;
<commit_msg>Fix incorrect error message<commit_after>/**
* GeoJSON Schemas for Mongoose
*
* rough GeoJSON schemas for use with mongoose schema creation
*
* Based on GeoJSON Spec @ http://geojson.org/geojson-spec.html
*
* Created by Ben Dalton (ben@rideamigos) on 3/27/14.
* Copyright RideAmigos (http://rideamigos.com)
**/

var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var crs = {};

function validateCrs(crs) {
  if (typeof crs !== 'object' && crs !== null) {
    throw new mongoose.Error('Crs must be an object or null');
  }
  if (crs === null) {
    return;
  }
  if (!crs.type) {
    throw new mongoose.Error('Crs must have a type');
  }
  if (crs.type !== 'name' && crs.type !== 'link') {
    throw new mongoose.Error('Crs must be either a name or link');
  }
  if (!crs.properties) {
    throw new mongoose.Error('Crs must contain a properties object');
  }
  if (crs.type === 'name' && !crs.properties.name) {
    throw new mongoose.Error('Crs specified by name must have a name property');
  }
  if (crs.type === 'link' && !crs.properties.href || crs.type === 'link' && !crs.properties.type) {
    throw new mongoose.Error('Crs specified by link must have a name and href property');
  }
}

/**
* @SchemaType GeoJSON
*
*/

function GeoJSON(key, options) {
  mongoose.SchemaType.call(this, key, options, 'GeoJSON');
}

GeoJSON.prototype = Object.create(mongoose.SchemaType.prototype);

GeoJSON.prototype.cast = function(geojson) {
  if (!geojson.type) {
    throw new mongoose.Error('GeoJSON objects must have a type');
  }
  switch (geojson.type) {
    case 'Point':
      validatePointObject(geojson);
      break;
    case 'MultiPoint':
      validateMultiPointObject(geojson);
      break;
    case 'LineString':
      validateLineStringObject(geojson);
      break;
    case 'MultiLineString':
      validateMultiLineStringObject(geojson);
      break;
    case 'Polygon':
      validatePolygonObject(geojson);
      break;
    case 'MultiPolygon':
      validateMultiPolygonObject(geojson);
      break;
    case 'Geometry':
      validateGeometryObject(geojson);
      break;
    case 'GeometryCollection':
      validateGeometryCollectionObject(geojson);
      break;
    case 'Feature':
      validateFeatureObject(geojson);
      break;
    case 'FeatureCollection':
      validateFeatureCollectionObject(geojson);
      break;
    default:
      throw new mongoose.Error(geojson.type + ' is not a valid GeoJSON type');

  }
  return geojson;
};

Schema.Types.GeoJSON = GeoJSON;

/**
* @SchemaType Point
*
*/

function Point(key, options) {
  mongoose.SchemaType.call(this, key, options, 'Point');
}

function validatePoint(coordinates) {
  // must be an array (object)
  if (typeof coordinates !== 'object') {
    throw new mongoose.Error('Point ' + coordinates + ' must be an array');
  }
  // must have 2/3 points
  if (coordinates.length < 2 || coordinates.length > 3) {
    throw new mongoose.Error('Point' + coordinates + ' must contain two or three coordinates');
  }
  // must have two numbers
  if (typeof coordinates[0] !== 'number' || typeof coordinates[1] !== 'number') {
    throw new mongoose.Error('Point must have two numbers');
  }
  if (!crs) {
    // longitude must be within bounds
    if (coordinates[0] > 180 || coordinates[0] < -180) {
      throw new mongoose.Error('Point' + coordinates[0] + ' should be within the boundaries of longitude');
    }
    // latitude must be within bounds
    if (coordinates[1] > 90 || coordinates[1] < -90) {
      throw new mongoose.Error('Point' + coordinates[1] + ' should be within the boundaries of latitude');
    }
  }
}

Point.prototype = Object.create(mongoose.SchemaType.prototype);

Point.prototype.cast = function(point) {
  if (!point.type) {
    throw new mongoose.Error('Point', point.type, 'point.type');
  }
  // type must be Point
  if (point.type !== 'Point') {
    throw new mongoose.Error(point.type + ' is not a valid GeoJSON type');
  }
  // check for crs
  if (point.crs) {
    crs = point.crs;
    validateCrs(crs);
  } else {
    crs = undefined;
  }
  validatePoint(point.coordinates);
  return point;
};

mongoose.Schema.Types.Point = Point;

/**
* @SchemaType MultiPoint
*
*/

function MultiPoint(key, options) {
  mongoose.SchemaType.call(this, key, options, 'MultiPoint');
}

function validateMultiPoint(coordinates) {
  for (var i = 0; i < coordinates.length; i++) {
    validatePoint(coordinates[i]);
  }
}

MultiPoint.prototype = Object.create(mongoose.SchemaType.prototype);

MultiPoint.prototype.cast = function(multipoint) {
  // must be an array (object)
  if (typeof multipoint.coordinates !== 'object') {
    throw new mongoose.Error('MultiPoint must be an array');
  }
  if (!multipoint.type) {
    throw new mongoose.Error('MultiPoint must have a type');
  }
  // type must be MultiPoint
  if (multipoint.type !== 'MultiPoint') {
    throw new mongoose.Error(multipoint.type + ' is not a valid GeoJSON type');
  }
  // check for crs
  if (multipoint.crs) {
    crs = multipoint.crs;
    validateCrs(crs);
  }
  validateMultiPoint(multipoint.coordinates);
  return multipoint;
};

mongoose.Schema.Types.MultiPoint = MultiPoint;

/**
* @SchemaType LineString
*
*/

function LineString(key, options) {
  mongoose.SchemaType.call(this, key, options, 'LineString');
}

function validateLineString(coordinates) {
  for (var i = 0; i < coordinates.length; i++) {
    validatePoint(coordinates[i]);
  }
}

LineString.prototype = Object.create(mongoose.SchemaType.prototype);

LineString.prototype.cast = function(linestring) {
  if (!linestring.type) {
    throw new mongoose.Error('LineString must have a type');
  }
  // type must be LineString
  if (linestring.type !== 'LineString') {
    throw new mongoose.Error(linestring.type + ' is not a valid GeoJSON type');
  }
  // must have at least two Points
  if (linestring.coordinates.length < 2) {
    throw new mongoose.Error('LineString type must have at least two Points');
  }
  // check for crs
  if (linestring.crs) {
    crs = linestring.crs;
    validateCrs(crs);
  }
  validateLineString(linestring.coordinates);
  return linestring;
};

mongoose.Schema.Types.LineString = LineString;

/**
* @SchemaType MultiLineString
*
*/

function MultiLineString(key, options) {
  mongoose.SchemaType.call(this, key, options, 'MultiLineString');
}

function validateMultiLineString(coordinates) {
  for (var i = 0; i < coordinates.length; i++) {
    validateLineString(coordinates[i]);
  }
}

MultiLineString.prototype = Object.create(mongoose.SchemaType.prototype);

MultiLineString.prototype.cast = function(multilinestring) {
  // must be an array (object)
  if (typeof multilinestring.coordinates !== 'object') {
    throw new mongoose.Error('MultiLineString must be an array');
  }
  if (!multilinestring.type) {
    throw new mongoose.Error('MultiLineString', multilinestring.type + ' must have a type');
  }
  // type must be MultiLineString
  if (multilinestring.type !== 'MultiLineString') {
    throw new mongoose.Error(multilinestring.type + ' is not a valid GeoJSON type');
  }
  validateMultiLineString(multilinestring.coordinates);
  return multilinestring;
};

mongoose.Schema.Types.MultiLineString = MultiLineString;

/**
* @SchemaType Polygon
*
*/

function Polygon(key, options) {
  mongoose.SchemaType.call(this, key, options, 'Polygon');
}

function arraysEqual(arr1, arr2) {
  if(arr1.length !== arr2.length) return false;
  for(var i = arr1.length; i--;) {
    if(arr1[i] !== arr2[i])
    return false;
  }
  return true;
}

function validatePolygon(coordinates) {
  for (var i = 0; i < coordinates.length; i++) {
    // The LinearRing elements must have at least four Points
    if (coordinates[i].length < 4) {
      throw new mongoose.Error('Each Polygon LinearRing must have at least four elements');
    }
    // the LinearRing objects must have identical start and end values
    if (!arraysEqual(coordinates[i][0], coordinates[i][coordinates[i].length-1])) {
      throw new mongoose.Error('Each Polygon LinearRing must have an identical first and last point');
    }
    // otherwise the LinearRings must correspond to a LineString
    validateLineString(coordinates[i]);
  }
}

Polygon.prototype = Object.create(mongoose.SchemaType.prototype);

Polygon.prototype.cast = function(polygon) {
  if (!polygon.type) {
    throw new mongoose.Error('Polygon', polygon.type + ' must have a type');
  }
  // type must be Polygon
  if (polygon.type !== 'Polygon') {
    throw new mongoose.Error(polygon.type + ' is not a valid GeoJSON type');
  }
  // check for crs
  if (polygon.crs) {
    crs = polygon.crs;
    validateCrs(crs);
  }
  validatePolygon(polygon.coordinates);
  return polygon;
};

mongoose.Schema.Types.Polygon = Polygon;

/**
* @SchemaType MultiPolygon
*
*/

function MultiPolygon(key, options) {
  mongoose.SchemaType.call(this, key, options, 'MultiPolygon');
}

function validateMultiPolygon(coordinates) {
  for (var i = 0; i < coordinates.length; i++) {
    validatePolygon(coordinates[i]);
  }
}

MultiPolygon.prototype = Object.create(mongoose.SchemaType.prototype);

MultiPolygon.prototype.cast = function(multipolygon) {
  // must be an array (object)
  if (typeof multipolygon.coordinates !== 'object') {
    throw new mongoose.Error('MultiPolygon must be an array');
  }
  if (!multipolygon.type) {
    throw new mongoose.Error('MultiPolygon must have a type');
  }
  // type must be Polygon
  if (multipolygon.type !== 'MultiPolygon') {
    throw new mongoose.Error(multipolygon.type + ' is not a valid GeoJSON type');
  }
  // check for crs
  if (multipolygon.crs) {
    crs = multipolygon.crs;
    validateCrs(crs);
  }
  validateMultiPolygon(multipolygon.coordinates);
  return multipolygon;
};

mongoose.Schema.Types.MultiPolygon = MultiPolygon;

/**
* @SchemaType Geometry
*
*/

function Geometry(key, options) {
  mongoose.SchemaType.call(this, key, options, 'Geometry');
}

function validateGeometry(geometry) {
  switch (geometry.type) {
    case 'Point':
    validatePoint(geometry.coordinates);
    break;
    case 'MultiPoint':
    validateMultiPoint(geometry.coordinates);
    break;
    case 'LineString':
    validateLineString(geometry.coordinates);
    break;
    case 'MultiLineString':
    validateMultiLineString(geometry.coordinates);
    break;
    case 'Polygon':
    validatePolygon(geometry.coordinates);
    break;
    case 'MultiPolygon':
    validateMultiPolygon(geometry.coordinates);
    break;
    default:
    throw new mongoose.Error('Geometry must have a valid type');
  }
}

Geometry.prototype = Object.create(mongoose.SchemaType.prototype);

Geometry.prototype.cast = function(geometry) {
  // console.log(geometry);
  // must be an array (object)
  if (!geometry.type) {
    throw new mongoose.Error('Geometry must must have a type');
  }
  // check for crs
  if (geometry.crs) {
    crs = geometry.crs;
    validateCrs(crs);
  }
  validateGeometry(geometry);
  return geometry;
};

mongoose.Schema.Types.Geometry = Geometry;

/**
* @SchemaType GeometryCollection
*
*/

function GeometryCollection(key, options) {
  mongoose.SchemaType.call(this, key, options, 'GeometryCollection');
}

function validateGeometries(geometries) {
  for (var i = 0; i < geometries.length; i++) {
    validateGeometry(geometries[i]);
  }
}

GeometryCollection.prototype = Object.create(mongoose.SchemaType.prototype);

GeometryCollection.prototype.cast = function(geometrycollection) {
  // must be an array (object)
  if (typeof geometrycollection.geometries !== 'object') {
    throw new mongoose.Error('GeometryCollection must be an array');
  }
  // check for crs
  if (geometrycollection.crs) {
    crs = geometrycollection.crs;
    validateCrs(crs);
  }
  validateGeometries(geometrycollection.geometries);
  return geometrycollection;
};

mongoose.Schema.Types.GeometryCollection = GeometryCollection;

/**
* @SchemaType Feature
*
*/

function Feature(key, options) {
  mongoose.SchemaType.call(this, key, options, 'Feature');
}

function validateFeature(feature) {
  if (!feature.type) {
    throw new mongoose.Error('Feature must have a type');
  }
  // type must be Feature
  if (feature.type !== 'Feature') {
    throw new mongoose.Error(feature.type + ' is not a valid GeoJSON type');
  }
  if (!feature.geometry) {
    throw new mongoose.Error('Feature must have a geometry');
  }
  // check for crs
  if (feature.crs) {
    crs = feature.crs;
    validateCrs(crs);
  }
  validateGeometry(feature.geometry);
}

Feature.prototype = Object.create(mongoose.SchemaType.prototype);

Feature.prototype.cast = function(feature) {
  validateFeature(feature);
  return feature;
};

mongoose.Schema.Types.Feature = Feature;

/**
* @SchemaType FeatureCollection
*
*/

function FeatureCollection(key, options) {
  mongoose.SchemaType.call(this, key, options, 'FeatureCollection');
}

function validateFeatureCollection(featurecollection) {
  for (var i = 0; i < featurecollection.features.length; i++) {
    validateFeature(featurecollection.features[i]);
  }
  return featurecollection;
}

FeatureCollection.prototype = Object.create(mongoose.SchemaType.prototype);

FeatureCollection.prototype.cast = function(featurecollection) {
  if (!featurecollection.type) {
    throw new mongoose.Error('FeatureCollection must have a type');
  }
  // type must be Polygon
  if (featurecollection.type !== 'FeatureCollection') {
    throw new mongoose.Error(featurecollection.type + ' is not a valid GeoJSON type');
  }
  if (!featurecollection.features) {
    throw new mongoose.Error('FeatureCollections must have a features object');
  }
  // check for crs
  if (featurecollection.crs) {
    crs = featurecollection.crs;
    validateCrs(crs);
  }
  validateFeatureCollection(featurecollection);
  return featurecollection;
};

mongoose.Schema.Types.FeatureCollection = FeatureCollection;
<|endoftext|>


<commit_before>/**
 * Base URL utilities for the Ajax.org Cloud IDE
 *
 * Access via require("c9/urls").
 *
 * @copyright 2015, Ajax.org B.V.
 */

var plugin = module.exports = main;
var assert = require("assert");

plugin.consumes = ["error.logger"];
plugin.provides = ["urls"];

var errorLogger = mockErrorLogger;

function main(options, imports, register) {
    errorLogger = imports["error.logger"];

    register(null, {
        urls: plugin
    });
}

/**
 * Get a desired base URL, given some context.
 *
 * @param req                             The current request object or URL
 * @param {String} [sourceUrlPattern]     The source URL pattern, e.g. https://ide.$DOMAIN
 * @param {String} targetBaseUrlPattern   The target URL pattern, e.g. https://$DOMAIN
 */
plugin.getBaseUrl = function(req, sourceBaseUrlPattern, targetBaseUrlPattern) {
    var sourceHost = req.headers && req.headers.host
        || req.host
        || req.url && req.url.replace(/^https?:\/\/([^/]*).*/, "$1")
        || req;
    if (typeof sourceHost !== "string")
        throw new Error("Not a valid request object: " + req);
    if (!sourceBaseUrlPattern)
        throw new Error("getBaseUrls() requires at least two arguments");

    var sourceHostMatcher = sourceBaseUrlPattern
        .replace(/^https?:\/\//, "")
        .replace(/\/.*/, "")
        .replace(/\./, "\\.")
        .replace("$DOMAIN", "([^/]+)");
    var hostMatch = sourceHost.match(sourceHostMatcher);
    var targetHost;
    if (hostMatch) {
        targetHost = hostMatch[1];
    }
    else {
        errorLogger.log(new Error("Could not construct URL: request host "
            + sourceHost + " should match " + sourceBaseUrlPattern));
        targetHost = "c9.io";
    }

    if (targetHost.match(/^(ide|vfs)./) && !targetBaseUrlPattern)
        errorLogger.log(new Error("Warning: no targetBaseUrlPattern specified, will stay at " + targetHost), { sourceBaseUrlPattern: sourceBaseUrlPattern });
    if (targetHost.match(/^(ide|vfs)./))
        console.trace("Warning: possibly incorrect baseUrl constructed, with 'ide.' in the hostname: " + targetHost);
    return replaceDomain(targetBaseUrlPattern || sourceBaseUrlPattern, targetHost)
        .replace(/\/$/, "");
};

plugin.replaceDomains = function(settings, domains) {
    domains = Array.isArray(domains) ? domains : domains.split(",");
    var primaryDomain = domains[0];
    settings.domains = domains;
    settings.primaryDomain = replaceDomain(settings.primaryDomain || "$DOMAIN", primaryDomain);
    settings.primaryBaseUrl = replaceDomain(settings.primaryBaseUrl || "https://$DOMAIN", primaryDomain);
    for (var s in settings) {
        if (!settings[s])
            continue;
        if (settings[s].baseUrl)
            settings[s].baseUrl = replaceDomain(settings[s].baseUrl, primaryDomain);
        if (settings[s].localBaseUrl)
            settings[s].localBaseUrl = replaceDomain(settings[s].localBaseUrl, primaryDomain);
        if (settings[s].primaryBaseUrl)
            settings[s].primaryBaseUrl = replaceDomain(settings[s].primaryBaseUrl, primaryDomain);
        if (settings[s].baseUrls) {
            assert(settings[s].baseUrls.length === 1);
            settings[s].baseUrls = domains.map(function(d) {
                return replaceDomain(settings[s].baseUrls[0], d);
            });
        }
    }
};

function replaceDomain(url, domain) {
    return url.replace("$DOMAIN", domain);
}

function mockErrorLogger() {}
mockErrorLogger.log = function(err) {
    console.error(err.stack);
};
plugin.mockErrorLogger = mockErrorLogger;
<commit_msg>Only show git login/signup for supported domains<commit_after>/**
 * Base URL utilities for the Ajax.org Cloud IDE
 *
 * Access via require("c9/urls").
 *
 * @copyright 2015, Ajax.org B.V.
 */

var plugin = module.exports = main;
var assert = require("assert");

plugin.consumes = ["error.logger"];
plugin.provides = ["urls"];

var errorLogger = mockErrorLogger;

function main(options, imports, register) {
    errorLogger = imports["error.logger"];

    register(null, {
        urls: plugin
    });
}

/**
 * Get a desired base URL, given some context.
 *
 * @param req                             The current request object or URL
 * @param {String} [sourceUrlPattern]     The source URL pattern, e.g. https://ide.$DOMAIN
 * @param {String} targetBaseUrlPattern   The target URL pattern, e.g. https://$DOMAIN
 */
plugin.getBaseUrl = function(req, sourceBaseUrlPattern, targetBaseUrlPattern) {
    var sourceHost = req.headers && req.headers.host
        || req.host
        || req.url && req.url.replace(/^https?:\/\/([^/]*).*/, "$1")
        || req;
    if (typeof sourceHost !== "string")
        throw new Error("Not a valid request object: " + req);
    if (!sourceBaseUrlPattern)
        throw new Error("getBaseUrl() requires at least two arguments");

    var sourceHostMatcher = sourceBaseUrlPattern
        .replace(/^https?:\/\//, "")
        .replace(/\/.*/, "")
        .replace(/\./, "\\.")
        .replace("$DOMAIN", "([^/]+)");
    var hostMatch = sourceHost.match(sourceHostMatcher);
    var targetHost;
    if (hostMatch) {
        targetHost = hostMatch[1];
    }
    else {
        errorLogger.log(new Error("Could not construct URL: request host "
            + sourceHost + " should match " + sourceBaseUrlPattern));
        targetHost = "c9.io";
    }

    if (targetHost.match(/^(ide|vfs)./) && !targetBaseUrlPattern)
        errorLogger.log(new Error("Warning: no targetBaseUrlPattern specified, will stay at " + targetHost), { sourceBaseUrlPattern: sourceBaseUrlPattern });
    if (targetHost.match(/^(ide|vfs)./))
        console.trace("Warning: possibly incorrect baseUrl constructed, with 'ide.' in the hostname: " + targetHost);
    return replaceDomain(targetBaseUrlPattern || sourceBaseUrlPattern, targetHost)
        .replace(/\/$/, "");
};

plugin.replaceDomains = function(settings, domains) {
    domains = Array.isArray(domains) ? domains : domains.split(",");
    var primaryDomain = domains[0];
    settings.domains = domains;
    settings.primaryDomain = replaceDomain(settings.primaryDomain || "$DOMAIN", primaryDomain);
    settings.primaryBaseUrl = replaceDomain(settings.primaryBaseUrl || "https://$DOMAIN", primaryDomain);
    for (var s in settings) {
        if (!settings[s])
            continue;
        if (settings[s].baseUrl)
            settings[s].baseUrl = replaceDomain(settings[s].baseUrl, primaryDomain);
        if (settings[s].localBaseUrl)
            settings[s].localBaseUrl = replaceDomain(settings[s].localBaseUrl, primaryDomain);
        if (settings[s].primaryBaseUrl)
            settings[s].primaryBaseUrl = replaceDomain(settings[s].primaryBaseUrl, primaryDomain);
        if (settings[s].baseUrls) {
            assert(settings[s].baseUrls.length === 1);
            settings[s].baseUrls = domains.map(function(d) {
                return replaceDomain(settings[s].baseUrls[0], d);
            });
        }
    }
};

function replaceDomain(url, domain) {
    return url.replace("$DOMAIN", domain);
}

function mockErrorLogger() {}
mockErrorLogger.log = function(err) {
    console.error(err.stack);
};
plugin.mockErrorLogger = mockErrorLogger;
<|endoftext|>


<commit_before>var qs = require('querystring');
var iconv = require('iconv-lite');
var vm = require('vm');
var util = require('../util');
var extend = require('util')._extend;
var Transform = require('pipestream').Transform;
var WhistleTransform = util.WhistleTransform;
var MAX_REQ_SIZE = 256 * 1024;
var REQ_TYPE = {
		urlencoded: 'application/x-www-form-urlencoded',
		form: 'application/x-www-form-urlencoded',
		json: 'application/json',
		xml: 'text/xml',
		text: 'text/plain',
		upload: 'multipart/form-data',
		multipart: 'multipart/form-data',
		defaultType: 'application/octet-stream'
};

/**
 * 处理请求数据
 *
 * @param req：method、body、headers，top，bottom，speed、delay，charset,timeout
 * @param data
 */
function handleReq(req, data) {
	req.method = data.method || req.method;
	req.timeout = parseInt(data.timeout, 10);
	extend(req.headers, data.headers);
	var method = req.method = typeof req.method != 'string' ? 'GET' : req.method.toUpperCase();
	if (!util.hasRequestBody(method)) {
		delete data.top;
		delete data.bottom;
		delete data.body;
	} else if (data.top || data.bottom || data.body) {
		delete req.headers['content-length'];
	}

	!util.isEmptyObject(data) && req.add(new WhistleTransform(data));
}

function handleCors(data, cors) {
	if (!cors) {
		return;
	}

	if (cors.origin !== undefined) {
		util.setHeader(data, 'origin', cors.origin);
	}

	if (cors.method !== undefined) {
		util.setHeader(data, 'access-control-request-method', cors.method);
	}

	if (cors.headers !== undefined) {
		util.setHeader(data, 'access-control-request-headers', cors.headers);
	}
}

function handleAuth(data, auth) {
	if (!auth) {
		return;
	}

	var basic = [];
	auth.username != null && basic.push(auth.username);
	auth.password != null && basic.push(auth.password);
	if (basic = basic.join(':')) {
		util.setHeader(data, 'authorization', 'Basic ' + new Buffer(basic).toString('base64'));
	}
}

function handleCookies(data, cookies, curCookies) {
	var list = cookies && Object.keys(cookies);
	if (list && list.length) {
		var result = {};
		if (curCookies && typeof curCookies == 'string') {
			curCookies.split(/;\s*/g)
					.forEach(function(cookie) {
						var index = cookie.indexOf('=');
						if (index == -1) {
							result[cookie] = null;
						} else {
							result[cookie.substring(0, index)] = cookie.substring(index + 1);
						}
					});
		}

		list.forEach(function(name) {
			var value = cookies[name];
			value = value && typeof value == 'object' ? value.value : value;
			result[util.encodeURIComponent(name)] = value ? util.encodeURIComponent(value) : value;
		});

		cookies = Object.keys(result).map(function(name) {
			var value = result[name];
			return name + (value == null ? '' : '=' + value);
		}).join('; ');
		util.setHeader(data, 'cookie', cookies);
	}
}

function handleParams(req, params) {
	if (!(params = params && qs.stringify(params))) {
		return;
	}

	if (!req.method || /^GET$/i.test(req.method)) {
		req.options.search = util.replaceUrlQueryString(req.options.search, params);
		req.options.path = util.replaceUrlQueryString(req.options.path, params);
		req.options.href = util.replaceUrlQueryString(req.options.href, params);
	} else if (util.isUrlEncoded(req)) {
		delete req.headers['content-length'];
		var transform = new Transform();
		var buffer, interrupt;
		transform._transform = function(chunk, encoding, callback) {
			if (chunk) {
				if (!interrupt) {
					buffer = buffer ? Buffer.concat([buffer, chunk]) : chunk;
					chunk = null;
					if (buffer.length > MAX_REQ_SIZE) {
						interrupt = true;
						chunk = buffer;
						buffer = null;
					}
				}
			} else if (buffer) {
				var body = buffer + '';
				var isGBK = body.indexOf('�') == -1;
				if (isGBK) {
					body = iconv.decode(buffer, 'GB18030');
					body = util.replaceQueryString(body, params);
					chunk = iconv.encode(body, 'GB18030');
				} else {
					chunk = new Buffer(body);
				}
				buffer = null;
			} else {
				chunk = new Buffer(params);
			}

			callback(null, chunk);
		};
		req.add(transform);
	}
}

module.exports = function(req, res, next) {
	var reqRules = req.rules;
	util.parseRuleJson([reqRules.req, reqRules.reqHeaders, reqRules.reqCookies,
	                    reqRules.auth, reqRules.params, reqRules.reqCors],
			function(data, headers, cookies, auth, params, cors) {
		if (reqRules.head && reqRules.head.req) {
			data = extend(reqRules.head.req, data);
		}
		data = data || {};
		if (headers) {
			data.headers =  extend(data.headers || {}, headers);
		}

		if (data.headers) {
			data.headers = util.lowerCaseify(data.headers);
		}

		var method = util.getMatcherValue(reqRules.method);
		if (method) {
			data.method = method;
		}

		var type = util.getMatcherValue(reqRules.reqType);
		if (type) {
			type = type.indexOf('/') != -1 ? type : (REQ_TYPE[type] || REQ_TYPE.defaultType);
			util.setHeader(data, 'content-type', type);
		}

		var referer = util.getMatcherValue(reqRules.referer);
		if (referer) {
			util.setHeader(data, 'referer', referer);
		}

		var ua = util.getMatcherValue(reqRules.ua);
		if (ua) {
			util.setHeader(data, 'user-agent', ua);
		}

		var reqDelay = util.getMatcherValue(reqRules.reqDelay);
		reqDelay = reqDelay && parseInt(reqDelay, 10);
		if (reqDelay > 0) {
			data.delay = reqDelay;
		}

		var reqSpeed = util.getMatcherValue(reqRules.reqSpeed);
		reqSpeed = reqSpeed && parseFloat(reqSpeed);
		if (reqSpeed > 0) {
			data.speed = reqSpeed;
		}

		handleCookies(data, cookies, req.headers.cookie);
		handleAuth(data, auth);
		handleCors(data, cors);

		util.readInjectFiles(data, function(data) {
			util.getRuleValue([reqRules.reqBody, reqRules.reqPrepend, reqRules.reqAppend],
				function(reqBody, reqPrepend, reqAppend) {
					if (reqBody) {
						data.body = reqBody;
					}

					if (reqPrepend) {
						data.top = reqPrepend;
					}

					if (reqAppend) {
						data.append = reqAppend;
					}

					handleReq(req, data);
					handleParams(req, params);
					util.removeUnsupportsHeaders(req.headers);

					next();
				});
		});
	});
};<commit_msg>disable req<commit_after>var qs = require('querystring');
var iconv = require('iconv-lite');
var vm = require('vm');
var util = require('../util');
var extend = require('util')._extend;
var Transform = require('pipestream').Transform;
var WhistleTransform = util.WhistleTransform;
var MAX_REQ_SIZE = 256 * 1024;
var REQ_TYPE = {
		urlencoded: 'application/x-www-form-urlencoded',
		form: 'application/x-www-form-urlencoded',
		json: 'application/json',
		xml: 'text/xml',
		text: 'text/plain',
		upload: 'multipart/form-data',
		multipart: 'multipart/form-data',
		defaultType: 'application/octet-stream'
};

/**
 * 处理请求数据
 *
 * @param req：method、body、headers，top，bottom，speed、delay，charset,timeout
 * @param data
 */
function handleReq(req, data) {
	req.method = data.method || req.method;
	req.timeout = parseInt(data.timeout, 10);
	extend(req.headers, data.headers);
	var method = req.method = typeof req.method != 'string' ? 'GET' : req.method.toUpperCase();
	if (!util.hasRequestBody(method)) {
		delete data.top;
		delete data.bottom;
		delete data.body;
	} else if (data.top || data.bottom || data.body) {
		delete req.headers['content-length'];
	}

	!util.isEmptyObject(data) && req.add(new WhistleTransform(data));
}

function handleCors(data, cors) {
	if (!cors) {
		return;
	}

	if (cors.origin !== undefined) {
		util.setHeader(data, 'origin', cors.origin);
	}

	if (cors.method !== undefined) {
		util.setHeader(data, 'access-control-request-method', cors.method);
	}

	if (cors.headers !== undefined) {
		util.setHeader(data, 'access-control-request-headers', cors.headers);
	}
}

function handleAuth(data, auth) {
	if (!auth) {
		return;
	}

	var basic = [];
	auth.username != null && basic.push(auth.username);
	auth.password != null && basic.push(auth.password);
	if (basic = basic.join(':')) {
		util.setHeader(data, 'authorization', 'Basic ' + new Buffer(basic).toString('base64'));
	}
}

function handleCookies(data, cookies, curCookies) {
	var list = cookies && Object.keys(cookies);
	if (list && list.length) {
		var result = {};
		if (curCookies && typeof curCookies == 'string') {
			curCookies.split(/;\s*/g)
					.forEach(function(cookie) {
						var index = cookie.indexOf('=');
						if (index == -1) {
							result[cookie] = null;
						} else {
							result[cookie.substring(0, index)] = cookie.substring(index + 1);
						}
					});
		}

		list.forEach(function(name) {
			var value = cookies[name];
			value = value && typeof value == 'object' ? value.value : value;
			result[util.encodeURIComponent(name)] = value ? util.encodeURIComponent(value) : value;
		});

		cookies = Object.keys(result).map(function(name) {
			var value = result[name];
			return name + (value == null ? '' : '=' + value);
		}).join('; ');
		util.setHeader(data, 'cookie', cookies);
	}
}

function handleParams(req, params) {
	if (!(params = params && qs.stringify(params))) {
		return;
	}

	if (!req.method || /^GET$/i.test(req.method)) {
		req.options.search = util.replaceUrlQueryString(req.options.search, params);
		req.options.path = util.replaceUrlQueryString(req.options.path, params);
		req.options.href = util.replaceUrlQueryString(req.options.href, params);
	} else if (util.isUrlEncoded(req)) {
		delete req.headers['content-length'];
		var transform = new Transform();
		var buffer, interrupt;
		transform._transform = function(chunk, encoding, callback) {
			if (chunk) {
				if (!interrupt) {
					buffer = buffer ? Buffer.concat([buffer, chunk]) : chunk;
					chunk = null;
					if (buffer.length > MAX_REQ_SIZE) {
						interrupt = true;
						chunk = buffer;
						buffer = null;
					}
				}
			} else if (buffer) {
				var body = buffer + '';
				var isGBK = body.indexOf('�') == -1;
				if (isGBK) {
					body = iconv.decode(buffer, 'GB18030');
					body = util.replaceQueryString(body, params);
					chunk = iconv.encode(body, 'GB18030');
				} else {
					chunk = new Buffer(body);
				}
				buffer = null;
			} else {
				chunk = new Buffer(params);
			}

			callback(null, chunk);
		};
		req.add(transform);
	}
}

function removeDisableProps(req) {
	var disable = req.disable;
	var headers = req.headers;

	if (disable.ua) {
		delete headers['user-agent'];
	}

	if (disable.cookie || disable.cookies ||
			disable.reqCookie || disable.reqCookies) {
		delete headers.cookie;
	}

	if (disable.referer) {
		delete headers.referer;
	}

	if (disable.cache) {
		delete headers['if-modified-since'];
		delete headers['if-none-match'];
		delete headers['last-modified'];
		delete headers.etag;

		headers['pragma'] = 'no-cache';
		headers['cache-control'] = 'no-cache';
	}
}

module.exports = function(req, res, next) {
	var reqRules = req.rules;
	util.parseRuleJson([reqRules.req, reqRules.reqHeaders, reqRules.reqCookies,
	                    reqRules.auth, reqRules.params, reqRules.reqCors],
			function(data, headers, cookies, auth, params, cors) {
		if (reqRules.head && reqRules.head.req) {
			data = extend(reqRules.head.req, data);
		}
		data = data || {};
		if (headers) {
			data.headers =  extend(data.headers || {}, headers);
		}

		if (data.headers) {
			data.headers = util.lowerCaseify(data.headers);
		}

		var method = util.getMatcherValue(reqRules.method);
		if (method) {
			data.method = method;
		}

		var type = util.getMatcherValue(reqRules.reqType);
		if (type) {
			type = type.indexOf('/') != -1 ? type : (REQ_TYPE[type] || REQ_TYPE.defaultType);
			util.setHeader(data, 'content-type', type);
		}

		var referer = util.getMatcherValue(reqRules.referer);
		if (referer) {
			util.setHeader(data, 'referer', referer);
		}

		var ua = util.getMatcherValue(reqRules.ua);
		if (ua) {
			util.setHeader(data, 'user-agent', ua);
		}

		var reqDelay = util.getMatcherValue(reqRules.reqDelay);
		reqDelay = reqDelay && parseInt(reqDelay, 10);
		if (reqDelay > 0) {
			data.delay = reqDelay;
		}

		var reqSpeed = util.getMatcherValue(reqRules.reqSpeed);
		reqSpeed = reqSpeed && parseFloat(reqSpeed);
		if (reqSpeed > 0) {
			data.speed = reqSpeed;
		}

		handleCookies(data, cookies, req.headers.cookie);
		handleAuth(data, auth);
		handleCors(data, cors);

		util.readInjectFiles(data, function(data) {
			util.getRuleValue([reqRules.reqBody, reqRules.reqPrepend, reqRules.reqAppend],
				function(reqBody, reqPrepend, reqAppend) {
					if (reqBody) {
						data.body = reqBody;
					}

					if (reqPrepend) {
						data.top = reqPrepend;
					}

					if (reqAppend) {
						data.append = reqAppend;
					}

					handleReq(req, data);
					handleParams(req, params);
					util.removeUnsupportsHeaders(req.headers);
					removeDisableProps(req);

					next();
				});
		});
	});
};<|endoftext|>


<commit_before>#!/usr/bin/env node

var fs = require('fs'),
    path = require('path'),
    utils = require('./lib/utils'),
    serial = require('./lib/serial');

// Helper: Determine if an error should just be logged.
function triageError(err) {
    if (err && err.name == 'ProjectMill') {
        console.warn(err.toString());
        err = null;
    }
    return err;
}

// Helper: Process a file.
function fileprocess(source, dest, processor, callback) {
    fs.readFile(source, 'utf8', function(err, data){
        data = processor(data);
        fs.writeFile(dest, data, 'utf8', callback);
    });
}

// Processor: MML
function processMML(config) {

    // add 'b' to 'a'
    function assign(a, b, k) {
        switch (typeof b[k]) {
            case 'object':
                iterObject(a[k], b[k]);
                break;
            case 'array':
                iterArray(a[k], b[k]);
                break;
            case 'number':
            case 'string':
            case 'boolean':
                a[k] = b[k];
                break;
        }
    }

    function iterObject(a, b) {
        for (var k in b) {
            assign(a, b, k);
        }
    }

    function iterArray(a, b) {
        b.forEach(function(v, i) {
            assign(a, b, i);
        });
    }

    return function(o) {
        o = JSON.parse(o);
        iterObject(o, config.mml);
        return JSON.stringify(o, null, 2);
    }
}

// Processor: MSS
function processMSS(config) {
    var varMatch = /^@([\w-]+):[\W]?([^;]+);$/;

    return function(o) {
        var lines = o.split("\n");
        for (var i = 0; i < lines.length; i++) {
            lines[i] = lines[i].replace(varMatch, function(m, n, v, o, s) {
                if (config.cartoVars[n] != undefined) {
                    return '@' + n +': '+ config.cartoVars[n] +';';
                }
                return s;
            });
        }
        return lines.join("\n");
    }
}

var usage = 'Usage: ./index.js <command> [-c ./config.json] [-t /path/to/tilemill]';

// Closure vars
var config = {},
    tilemill = '',
    argv = require('optimist').usage(usage).argv,
    fileDir = argv.p || path.join(process.env.HOME, 'Documents', 'MapBox'),
    replaceExisting = argv.f || false;

// If no command was issued bail.
if (!argv.mill && !argv.render && !argv.upload) {
    console.warn('Error: missing command. Available commands are `--mill`, `--render`, `--upload`');
    console.warn(usage);
    process.exit(1);
}

// Try to locate TileMill
var tilemillPath = argv.t || 'tilemill';
try {
    tilemillPath = require.resolve(tilemillPath);
}
catch(err) {
    console.warn('Error: could not locate TileMill');
    console.warn(usage);
    process.exit(1);
}

// Assemble the main actions.
var actions = [];

// Get our configuration
actions.push(function(next, err) {
    if (err) return next(err);

    var configFile = argv.c || 'config.json';
    fs.readFile(path.join(process.cwd(), configFile), 'utf8', next);
});
actions.push(function(next, err, data) {
    if (err) return next(err);

    try {
        data = JSON.parse(data);
    }
    catch(err) {
        return next(err);
    }

    data.forEach(function(v) {
        // TODO check for other required elements.
        if (v.source && v.destination) {
            config[v.destination] = v;
            v.source = path.join(fileDir, 'project', v.source);
            v.destination = path.join(fileDir, 'project', v.destination);
        }
        else {
            console.warn("Error: project missing required elements >> " + JSON.stringify(v));
        }
    });
    next();
});

// Mill projects defined in configuration.
if (argv.mill) {

    // Validate source paths.
    actions.push(function(next, err) {
        if (err) return next(err);

        var projectDir = path.join(fileDir, 'project');
        fs.readdir(projectDir, next);
    });
    actions.push(function(next, err, files) {
        if (err) return next(err);

        var paths = {};
        files.forEach(function(v) {
            if (v[0] == '.') return;
            paths[path.join(fileDir, 'project', v)] = path.join(fileDir, 'project', v);
        });
        for (var i in config) {
            if (paths[config[i].source] == undefined) {
                console.warn("Error: source project doesn't exist >> " + JSON.stringify(config[i]));
                delete config[i];
            }
        }
        next();
    });

    actions.push(function(next, err) {
        if (err) return next(err);

        var mill = [];
        Object.keys(config).forEach(function(i) {

            mill.push(function(cb, err) {
                err = triageError(err);
                if (err) return cb(err);

                path.exists(config[i].destination, cb);
            });
            mill.push(function(cb, exists) {
                if (!exists) return cb();

                if (replaceExisting) {
                    console.log('Notice: removing project '+ config[i].destination);
                    utils.recursiveDelete(config[i].destination, cb);
                }
                else {
                    var e = new Error('Skipping project '+ i);
                    e.name = "ProjectMill";
                    cb(e);
                }
            });
            mill.push(function(cb, err) {
                if (err) return cb(err);

                utils.readdirr(config[i].source, cb);
            });
            mill.push(function(cb, err, files) {
                if (err) return cb(err);

                var setup = [];
                files.forEach(function(filename) {
                    // Handle symlinks which come in as an object.
                    var linkSource = '';
                    if (typeof filename != 'string') {
                         linkSource = filename.source;
                         filename = filename.target
                    }

                    var destfile = path.join(config[i].destination, filename),
                        destdir = path.dirname(destfile),
                        sourcefile = path.join(config[i].source, filename);

                    // In the future the 'mml' file will always be called
                    // 'project.mml', but currently this isn't the case.
                    // TODO delete this when https://github.com/mapbox/tilemill/pull/970
                    //      is merged.
                    if (path.extname(filename) == '.mml' && filename != 'project.mml') {
                        destfile = path.join(config[i].destination, i +'.mml');
                    }

                    setup.push(function(next) {
                        path.exists(destdir, next);
                    });
                    setup.push(function(next, err, exists) {
                        if (exists) return next();

                        utils.mkdirp(destdir, '0777', next);
                    });
                    setup.push(function(next, err) {
                        if (err) next(err);

                        if (linkSource) {
                            return fs.symlink(linkSource, destfile, next);
                        }
                        else if (config[i].mml && path.extname(filename) == '.mml') {
                            return fileprocess(sourcefile, destfile, processMML(config[i]), next);
                        }
                        else if (config[i].cartoVars && path.extname(filename) == '.mss') {
                            return fileprocess(sourcefile, destfile, processMSS(config[i]), next);
                        }
                        else {
                            return utils.filecopy(sourcefile, destfile, next);
                        }
                    });
                })
                serial(setup, function(err) {
                    if (err) console.warn(err);
                    cb();
                });
            });
            mill.push(function(cb, err) {
                if (!err) console.log('Notice: created project '+ config[i].destination);
                cb(err);
            });
        });
        serial(mill, function(err) {
            next(triageError(err));
        });
    });
}

// Render all available projects.
if (argv.render) {
    var spawn = require('child_process').spawn,
        sqlite3 = require('sqlite3');

    actions.push(function(next, err) {
        if (err) return next(err);

        var render = [];
        Object.keys(config).forEach(function(i) {
            var data = config[i],
                destfile = path.join(fileDir, 'export', i + '.' + data.format);

            render.push(function(cb, err) {
                err = triageError(err);
                if (err) return cb(err);

                path.exists(destfile, cb);
            });
            render.push(function(cb, exists) {
                if (!exists) return cb();

                if (replaceExisting) {
                    console.log("Notice: deleting " + destfile);
                    fs.unlink(destfile, cb);
                }
                else {
                    var e = new Error('Skipping export ' + i);
                    e.name = "ProjectMill";
                    cb(e);
                }
            });
            render.push(function(cb, err) {
                if (err) return cb(err);

                if (data.format == undefined) {
                    var err = new Error('export format not specified for `'+i+'`');
                    return cb(err);
                }

                var args = [];
                // nice the export process.
                args.push('-n19');
                // node command
                args.push(process.execPath);
                // tilemill index.js
                args.push(tilemillPath);
                // export command
                args.push('export');
                // datasource
                args.push(i);
                // filepath
                args.push(destfile);
                // format, don't try to guess extension based on filepath
                args.push('--format=' + data.format);

                if (data.bbox) args.push('--bbox=' + data.bbox.join(','));
                if (data.width) args.push('--width=' + data.width);
                if (data.height) args.push('--height=' + data.height);
                if (data.minzoom) args.push('--minzoom=' + data.minzoom);
                if (data.maxzoom) args.push('--maxzoom=' + data.maxzoom);
                if (data.maxzoom) args.push('--files=' + fileDir);

                console.log('Notice: spawning nice ' + args.join(' '));

                // todo get the actual name of the database written to.
                var nice = spawn('nice', args);
                nice.stdout.on('data', function(data) {
                    console.log(data.toString());
                });
                nice.stderr.on('data',  function(data) {
                    console.warn(data.toString());
                });
                nice.on('exit', function(code, signal) {
                    var err = null;
                    if (code) {
                        err = new Error('Render failed: '+ i);
                        err.name = 'ProjectMill';
                    }
                    else {
                        console.log('Notice: rendered ' + destfile);
                    }
                    cb(err);
                });
            });

            // If this isn't mbtile, or we don't have meta data to add to it
            // skip the remainder of the render steps.
            if (data.format != 'mbtiles' || data.MBmeta == undefined) return;

            render.push(function(cb, err) {
                if (err) return cb(err);

                var db = new sqlite3.Database(destfile, sqlite3.OPEN_READWRITE, function(err) {
                    cb(err, db);
                });
            });
            render.push(function(cb, err, db) {
                if (err) return cb(err);

                var rows = [];
                Object.keys(data.MBmeta).forEach(function(k) {
                    if (typeof data.MBmeta[k] != 'string') return;

                    rows.push(function(nextRow, err) {
                        if (err) return console.warn(err.toString());

                        var sql = 'REPLACE INTO metadata (name, value) VALUES (?, ?)';
                        var stmt = db.prepare(sql, function(err) {
                            nextRow(err, stmt);
                        });
                    });
                    rows.push(function(nextRow, err, stmt) {
                        if (err) return nextRow(err);

                        stmt.run(k, data.MBmeta[k], function(err){
                            if (err) console.warn(err);
                            stmt.finalize(nextRow);
                        })
                    });
                });
                serial(rows, function(err) {
                    delete db;
                    cb(err, db);
                });
            });
            render.push(function(cb, err) {
                if (!err) console.log('Notice: added metadata to ' + destfile);
                cb(err);
            });
        });
        serial(render, function(err) {
            next(triageError(err));
        });
    });
}

// Upload available mbtiles files.
if (argv.upload) {
    var spawn = require('child_process').spawn;
    actions.push(function(next, err) {
        if (err) return next(err);

        var upload = [];
        Object.keys(config).forEach(function(i) {
            var data = config[i];
            if (data.syncAccount && data.syncAccessToken) {
                upload.push(function(cb, err) {
                    err = triageError(err);
                    if (err) return next(err);

                    // todo - mbtilesFile name is guess work.
                    var args = [],
                        mbtilesFile = path.join(fileDir, 'export', i + '.mbtiles');

                    // tilemill index.js
                    args.push(tilemillPath);
                    // export command
                    args.push('export');
                    // datasource
                    args.push(i);
                    // file
                    args.push(mbtilesFile);
                    // signal upload
                    args.push('--format=upload');
                    // OAuth config.
                    args.push('--syncAccount=' + data.syncAccount);
                    args.push('--syncAccessToken=' + data.syncAccessToken);

                    // todo get more output from the child process.
                    var retries = 0;
                    var upload = function(callback) {
                        var retry = false;
                        console.log('Notice: spawning ' + process.execPath + ' ' + args.join(' '));
                        var tilemill = spawn(process.execPath, args);
                        tilemill.stdout.on('data', function(data) {
                            console.log(data.toString());
                        });
                        tilemill.stderr.on('data', function(data) {
                            var error = data.toString();
                            console.warn(error);
                            retry = error.match(/.*S3 is not available.*/) && (++retries < 4);
                        });
                        tilemill.on('exit', function(code, signal) {
                            var err = null;
                            if (code && retry) {
                                console.log('Retrying upload in ' + retries + ' second(s) (attempt ' + retries + ')');
                                return setTimeout(function() {
                                    upload(callback);
                                }, 1000 * retries);
                            }
                            if (code) {
                                err = new Error('Upload failed: '+ i);
                                err.name = 'ProjectMill';
                            }
                            else {
                                console.log('Notice: uploaded ' + mbtilesFile + ' to ' + data.syncAccount);
                            }
                            callback(err);
                        });
                    }
                    upload(cb);
                });
            }
        });
        serial(upload, function(err) {
            next(triageError(err));
        });
    });
}

// Run the main actions.
serial(actions, function(err) {
    if (err) console.warn(err.toString());
    console.log('Done.');
});
<commit_msg>More reasonable default TileMill paths.<commit_after>#!/usr/bin/env node

var fs = require('fs'),
    path = require('path'),
    utils = require('./lib/utils'),
    serial = require('./lib/serial');

// Helper: Determine if an error should just be logged.
function triageError(err) {
    if (err && err.name == 'ProjectMill') {
        console.warn(err.toString());
        err = null;
    }
    return err;
}

// Helper: Process a file.
function fileprocess(source, dest, processor, callback) {
    fs.readFile(source, 'utf8', function(err, data){
        data = processor(data);
        fs.writeFile(dest, data, 'utf8', callback);
    });
}

// Processor: MML
function processMML(config) {

    // add 'b' to 'a'
    function assign(a, b, k) {
        switch (typeof b[k]) {
            case 'object':
                iterObject(a[k], b[k]);
                break;
            case 'array':
                iterArray(a[k], b[k]);
                break;
            case 'number':
            case 'string':
            case 'boolean':
                a[k] = b[k];
                break;
        }
    }

    function iterObject(a, b) {
        for (var k in b) {
            assign(a, b, k);
        }
    }

    function iterArray(a, b) {
        b.forEach(function(v, i) {
            assign(a, b, i);
        });
    }

    return function(o) {
        o = JSON.parse(o);
        iterObject(o, config.mml);
        return JSON.stringify(o, null, 2);
    }
}

// Processor: MSS
function processMSS(config) {
    var varMatch = /^@([\w-]+):[\W]?([^;]+);$/;

    return function(o) {
        var lines = o.split("\n");
        for (var i = 0; i < lines.length; i++) {
            lines[i] = lines[i].replace(varMatch, function(m, n, v, o, s) {
                if (config.cartoVars[n] != undefined) {
                    return '@' + n +': '+ config.cartoVars[n] +';';
                }
                return s;
            });
        }
        return lines.join("\n");
    }
}

var usage = 'Usage: ./index.js <command> [-c ./config.json] [-t /path/to/tilemill]';

// Closure vars
var config = {},
    tilemill = '',
    argv = require('optimist').usage(usage).argv,
    fileDir = argv.p || path.join(process.env.HOME, 'Documents', 'MapBox'),
    replaceExisting = argv.f || false;

// If no command was issued bail.
if (!argv.mill && !argv.render && !argv.upload) {
    console.warn('Error: missing command. Available commands are `--mill`, `--render`, `--upload`');
    console.warn(usage);
    process.exit(1);
}

// Try to locate TileMill
var tilemillPath = argv.t;
if (!tilemillPath && require('os').type() == 'Darwin') {
    tilemillPath = '/Applications/TileMill.app/Contents/Resources';
}
else if (!tilemillPath) {
    tilemillPath = '/usr/share/tilemill';
}

try {
    tilemillPath = require.resolve(tilemillPath);
    if (!argv.t) {
        console.warn("Notice: using TileMill from '"+ tilemillPath +"'");
    }
}
catch(err) {
    console.warn("Error: could not locate TileMill. Looking in '"+ tilemillPath +"'");
    console.warn(usage);
    process.exit(1);
}

// Assemble the main actions.
var actions = [];

// Get our configuration
actions.push(function(next, err) {
    if (err) return next(err);

    var configFile = argv.c || 'config.json';
    fs.readFile(path.join(process.cwd(), configFile), 'utf8', next);
});
actions.push(function(next, err, data) {
    if (err) return next(err);

    try {
        data = JSON.parse(data);
    }
    catch(err) {
        return next(err);
    }

    data.forEach(function(v) {
        // TODO check for other required elements.
        if (v.source && v.destination) {
            config[v.destination] = v;
            v.source = path.join(fileDir, 'project', v.source);
            v.destination = path.join(fileDir, 'project', v.destination);
        }
        else {
            console.warn("Error: project missing required elements >> " + JSON.stringify(v));
        }
    });
    next();
});

// Mill projects defined in configuration.
if (argv.mill) {

    // Validate source paths.
    actions.push(function(next, err) {
        if (err) return next(err);

        var projectDir = path.join(fileDir, 'project');
        fs.readdir(projectDir, next);
    });
    actions.push(function(next, err, files) {
        if (err) return next(err);

        var paths = {};
        files.forEach(function(v) {
            if (v[0] == '.') return;
            paths[path.join(fileDir, 'project', v)] = path.join(fileDir, 'project', v);
        });
        for (var i in config) {
            if (paths[config[i].source] == undefined) {
                console.warn("Error: source project doesn't exist >> " + JSON.stringify(config[i]));
                delete config[i];
            }
        }
        next();
    });

    actions.push(function(next, err) {
        if (err) return next(err);

        var mill = [];
        Object.keys(config).forEach(function(i) {

            mill.push(function(cb, err) {
                err = triageError(err);
                if (err) return cb(err);

                path.exists(config[i].destination, cb);
            });
            mill.push(function(cb, exists) {
                if (!exists) return cb();

                if (replaceExisting) {
                    console.log('Notice: removing project '+ config[i].destination);
                    utils.recursiveDelete(config[i].destination, cb);
                }
                else {
                    var e = new Error('Skipping project '+ i);
                    e.name = "ProjectMill";
                    cb(e);
                }
            });
            mill.push(function(cb, err) {
                if (err) return cb(err);

                utils.readdirr(config[i].source, cb);
            });
            mill.push(function(cb, err, files) {
                if (err) return cb(err);

                var setup = [];
                files.forEach(function(filename) {
                    // Handle symlinks which come in as an object.
                    var linkSource = '';
                    if (typeof filename != 'string') {
                         linkSource = filename.source;
                         filename = filename.target
                    }

                    var destfile = path.join(config[i].destination, filename),
                        destdir = path.dirname(destfile),
                        sourcefile = path.join(config[i].source, filename);

                    // In the future the 'mml' file will always be called
                    // 'project.mml', but currently this isn't the case.
                    // TODO delete this when https://github.com/mapbox/tilemill/pull/970
                    //      is merged.
                    if (path.extname(filename) == '.mml' && filename != 'project.mml') {
                        destfile = path.join(config[i].destination, i +'.mml');
                    }

                    setup.push(function(next) {
                        path.exists(destdir, next);
                    });
                    setup.push(function(next, err, exists) {
                        if (exists) return next();

                        utils.mkdirp(destdir, '0777', next);
                    });
                    setup.push(function(next, err) {
                        if (err) next(err);

                        if (linkSource) {
                            return fs.symlink(linkSource, destfile, next);
                        }
                        else if (config[i].mml && path.extname(filename) == '.mml') {
                            return fileprocess(sourcefile, destfile, processMML(config[i]), next);
                        }
                        else if (config[i].cartoVars && path.extname(filename) == '.mss') {
                            return fileprocess(sourcefile, destfile, processMSS(config[i]), next);
                        }
                        else {
                            return utils.filecopy(sourcefile, destfile, next);
                        }
                    });
                })
                serial(setup, function(err) {
                    if (err) console.warn(err);
                    cb();
                });
            });
            mill.push(function(cb, err) {
                if (!err) console.log('Notice: created project '+ config[i].destination);
                cb(err);
            });
        });
        serial(mill, function(err) {
            next(triageError(err));
        });
    });
}

// Render all available projects.
if (argv.render) {
    var spawn = require('child_process').spawn,
        sqlite3 = require('sqlite3');

    actions.push(function(next, err) {
        if (err) return next(err);

        var render = [];
        Object.keys(config).forEach(function(i) {
            var data = config[i],
                destfile = path.join(fileDir, 'export', i + '.' + data.format);

            render.push(function(cb, err) {
                err = triageError(err);
                if (err) return cb(err);

                path.exists(destfile, cb);
            });
            render.push(function(cb, exists) {
                if (!exists) return cb();

                if (replaceExisting) {
                    console.log("Notice: deleting " + destfile);
                    fs.unlink(destfile, cb);
                }
                else {
                    var e = new Error('Skipping export ' + i);
                    e.name = "ProjectMill";
                    cb(e);
                }
            });
            render.push(function(cb, err) {
                if (err) return cb(err);

                if (data.format == undefined) {
                    var err = new Error('export format not specified for `'+i+'`');
                    return cb(err);
                }

                var args = [];
                // nice the export process.
                args.push('-n19');
                // node command
                args.push(process.execPath);
                // tilemill index.js
                args.push(tilemillPath);
                // export command
                args.push('export');
                // datasource
                args.push(i);
                // filepath
                args.push(destfile);
                // format, don't try to guess extension based on filepath
                args.push('--format=' + data.format);

                if (data.bbox) args.push('--bbox=' + data.bbox.join(','));
                if (data.width) args.push('--width=' + data.width);
                if (data.height) args.push('--height=' + data.height);
                if (data.minzoom) args.push('--minzoom=' + data.minzoom);
                if (data.maxzoom) args.push('--maxzoom=' + data.maxzoom);
                if (data.maxzoom) args.push('--files=' + fileDir);

                console.log('Notice: spawning nice ' + args.join(' '));

                // todo get the actual name of the database written to.
                var nice = spawn('nice', args);
                nice.stdout.on('data', function(data) {
                    console.log(data.toString());
                });
                nice.stderr.on('data',  function(data) {
                    console.warn(data.toString());
                });
                nice.on('exit', function(code, signal) {
                    var err = null;
                    if (code) {
                        err = new Error('Render failed: '+ i);
                        err.name = 'ProjectMill';
                    }
                    else {
                        console.log('Notice: rendered ' + destfile);
                    }
                    cb(err);
                });
            });

            // If this isn't mbtile, or we don't have meta data to add to it
            // skip the remainder of the render steps.
            if (data.format != 'mbtiles' || data.MBmeta == undefined) return;

            render.push(function(cb, err) {
                if (err) return cb(err);

                var db = new sqlite3.Database(destfile, sqlite3.OPEN_READWRITE, function(err) {
                    cb(err, db);
                });
            });
            render.push(function(cb, err, db) {
                if (err) return cb(err);

                var rows = [];
                Object.keys(data.MBmeta).forEach(function(k) {
                    if (typeof data.MBmeta[k] != 'string') return;

                    rows.push(function(nextRow, err) {
                        if (err) return console.warn(err.toString());

                        var sql = 'REPLACE INTO metadata (name, value) VALUES (?, ?)';
                        var stmt = db.prepare(sql, function(err) {
                            nextRow(err, stmt);
                        });
                    });
                    rows.push(function(nextRow, err, stmt) {
                        if (err) return nextRow(err);

                        stmt.run(k, data.MBmeta[k], function(err){
                            if (err) console.warn(err);
                            stmt.finalize(nextRow);
                        })
                    });
                });
                serial(rows, function(err) {
                    delete db;
                    cb(err, db);
                });
            });
            render.push(function(cb, err) {
                if (!err) console.log('Notice: added metadata to ' + destfile);
                cb(err);
            });
        });
        serial(render, function(err) {
            next(triageError(err));
        });
    });
}

// Upload available mbtiles files.
if (argv.upload) {
    var spawn = require('child_process').spawn;
    actions.push(function(next, err) {
        if (err) return next(err);

        var upload = [];
        Object.keys(config).forEach(function(i) {
            var data = config[i];
            if (data.syncAccount && data.syncAccessToken) {
                upload.push(function(cb, err) {
                    err = triageError(err);
                    if (err) return next(err);

                    // todo - mbtilesFile name is guess work.
                    var args = [],
                        mbtilesFile = path.join(fileDir, 'export', i + '.mbtiles');

                    // tilemill index.js
                    args.push(tilemillPath);
                    // export command
                    args.push('export');
                    // datasource
                    args.push(i);
                    // file
                    args.push(mbtilesFile);
                    // signal upload
                    args.push('--format=upload');
                    // OAuth config.
                    args.push('--syncAccount=' + data.syncAccount);
                    args.push('--syncAccessToken=' + data.syncAccessToken);

                    // todo get more output from the child process.
                    var retries = 0;
                    var upload = function(callback) {
                        var retry = false;
                        console.log('Notice: spawning ' + process.execPath + ' ' + args.join(' '));
                        var tilemill = spawn(process.execPath, args);
                        tilemill.stdout.on('data', function(data) {
                            console.log(data.toString());
                        });
                        tilemill.stderr.on('data', function(data) {
                            var error = data.toString();
                            console.warn(error);
                            retry = error.match(/.*S3 is not available.*/) && (++retries < 4);
                        });
                        tilemill.on('exit', function(code, signal) {
                            var err = null;
                            if (code && retry) {
                                console.log('Retrying upload in ' + retries + ' second(s) (attempt ' + retries + ')');
                                return setTimeout(function() {
                                    upload(callback);
                                }, 1000 * retries);
                            }
                            if (code) {
                                err = new Error('Upload failed: '+ i);
                                err.name = 'ProjectMill';
                            }
                            else {
                                console.log('Notice: uploaded ' + mbtilesFile + ' to ' + data.syncAccount);
                            }
                            callback(err);
                        });
                    }
                    upload(cb);
                });
            }
        });
        serial(upload, function(err) {
            next(triageError(err));
        });
    });
}

// Run the main actions.
serial(actions, function(err) {
    if (err) console.warn(err.toString());
    console.log('Done.');
});
<|endoftext|>


<commit_before>var fs = require('fs');
var loaderUtils = require("loader-utils");

var loadedResources = {};

module.exports = function(content) {
	var self = this;

	var query = loaderUtils.parseQuery(this.query);
	var outputName = (query.o || 'vendor');

	var inputFile = this.resourcePath;
	var outputFile = this.options.output.path + '/' + outputName + '.js';

	var fsOptions = { encoding : 'utf8' };

	this.cacheable && this.cacheable();
	if(!this.emitFile) throw new Error("emitFile is required from module system");

	// If no resources have been loaded yet it means the file has to be emptied
	if(!(outputName in loadedResources)) {
		loadedResources[outputName] = {};
		fs.writeFileSync(outputFile, content, fsOptions);
	}

	// If module not loaded yet
	else if(!(inputFile in loadedResources[outputName])) {
		fs.appendFileSync(outputFile, content, fsOptions);
	}
	else {
		this.emitWarning(inputFile);
	}

	// Save module as loaded so we don't have to load it again
	loadedResources[outputName][inputFile] = true;

	return null;
}<commit_msg>Fix return value<commit_after>var fs = require('fs');
var loaderUtils = require("loader-utils");

var loadedResources = {};

module.exports = function(content) {
	var self = this;

	var query = loaderUtils.parseQuery(this.query);
	var outputName = (query.o || 'vendor');

	var inputFile = this.resourcePath;
	var outputFile = this.options.output.path + '/' + outputName + '.js';

	var fsOptions = { encoding : 'utf8' };

	this.cacheable && this.cacheable();
	if(!this.emitFile) throw new Error("emitFile is required from module system");

	// If no resources have been loaded yet it means the file has to be emptied
	if(!(outputName in loadedResources)) {
		loadedResources[outputName] = {};
		fs.writeFileSync(outputFile, content, fsOptions);
	}

	// If module not loaded yet
	else if(!(inputFile in loadedResources[outputName])) {
		fs.appendFileSync(outputFile, content, fsOptions);
	}
	else {
		this.emitWarning(inputFile);
	}

	// Save module as loaded so we don't have to load it again
	loadedResources[outputName][inputFile] = true;

	return '';
}<|endoftext|>


<commit_before>/*
 * partial-extract
 * https://github.com/tilmanjusten/partial-extract
 *
 * Copyright (c) 2015 Tilman Justen
 * Licensed under the MIT license.
 */

'use strict';

var _ = require('lodash');
var path = require('path');
var fs = require('fs-extra');
//var chalk = require('chalk');
var options = {};
var InventoryObject = require('inventory-object');

var PartialExtract = function (files, _options) {
    // Merge task-specific and/or target-specific options with these defaults.
    options = _.assign({
        // Find partials by pattern:
        //
        //
        //   partial
        //
        patternExtract: new RegExp(//g),
        // Wrap partial in template element and add options as data attributes
        templateWrap: {
            before: '<template id="partial" {{wrapData}}>',
            after: '</template>'
        },
        // Wrap component for viewing purposes: e.g. add production context
        //
        //
        //   partial
        //
        //
        // results in
        //
        //

        //   partial
        //

        viewWrap: {
            before: '',
            after: ''
        },
        // Base directory
        base: './dist',
        // Partial directory where individual partial files will be stored (relative to base)
        partials: './partials',
        // Store inventory data as JSON file or `false` if not
        storage: './dist/partial-extract.json',
        // Enable storing partials as individual files
        storePartials: false,
        // Set indent value of partial code
        indent: '    '
    }, _options);

    //console.log(chalk.white('Destination: ' + options.base));
    //console.log(chalk.white('Files: ' + files.length));
    //console.log('');

    // Create destination dir if not exist
    var baseAbsPath = path.resolve(options.base);
    fs.ensureDir(baseAbsPath);

    var processedBlocks = {
        options: options,
        length: 0,
        items: []
    };
    var uniqueBlocks = [];

    // Iterate over all specified file groups.
    files.forEach(function (file) {
        var content = fs.readFileSync(file, 'utf8');

        if (!options.patternExtract.test(content)) {
            //console.log(chalk.red('No partials in file ' + file));
            //console.log('');

            return;
        }

        var blocks = getPartials(content);
        var resources = getResources(content);

        // put resources to the options
        options.resources = options.resources ? _.assign({}, resources, options.resources) : resources;

        //console.log(chalk.green('Found ' + blocks.length + ' partials in file ' + file));

        // Write blocks to separate files
        blocks.map(function (block) {
            // init inventory object
            var opts = _.assign({}, options);
            var processed = new InventoryObject();
            var isDuplicate = false;

            // process block
            processed.parseData(block, opts);
            processed.setProperty('origin', file);

            processedBlocks.items.push(processed);

            if (uniqueBlocks.indexOf(processed.id) < 0) {
                uniqueBlocks.push(processed.id);
            } else {
                isDuplicate = true;
            }

            // store partial if not already happen
            if (options.storePartials && !isDuplicate) {
                fs.writeFileSync(path.resolve(options.base, options.partials, processed.id), processed.template, 'utf8');
            }
        });

        //console.log('');
    });

    processedBlocks.lengthUnique = uniqueBlocks.length;
    processedBlocks.lengthTotal = processedBlocks.items.length;

    if (options.storage && typeof options.storage === 'string') {
        fs.ensureDir(fs.dirname(options.storage));
    fs.writeJsonSync(options.storage, processedBlocks);
    }

    //console.log('');

    //console.log(chalk.green('Extracted ' + processedBlocks.length + ' partials, ' + uniqueBlocks.length + ' unique.'));

    return processedBlocks;
};

/**
 * extract partials
 *
 * @param src
 * @returns {Array}
 */
function getPartials(src) {
    return src.match(options.patternExtract);
}

/**
 * extract resource path of
 * - javascript resources in  and
 * - stylesheet resources in
 * -