<commit_before>var fs = require('fs');
var express = require('express');
var async = require('async');
var topojson = require('topojson');
var pg = require('pg');
var SphericalMercator = require('sphericalmercator');
var Caching = require('caching');
var clone = require('clone');
var connectTimeout = require('connect-timeout');

var pgMiddleware = function(dbOptions){
  return function(req, res, next){
    pg.connect(dbOptions, function(err, client, done){
      if (err) {
        throw err;
      } else {
        req.db = client;
        res.on('finish', done);
        res.on('error', done);
        req.on('timeout', done);

        var timeout = parseInt(process.env.TILESPLASH_STATEMENT_TIMEOUT) || 0;

        if (timeout) {
          client.query('SET statement_timeout=$1', [timeout], function(){});
        }

        next();
      }
    });
  };
};

var Tilesplash = function(dbOptions, cacheType, cacheOptions){
  this.projection = new SphericalMercator({
    size: 256
  });

  this.dbOptions = dbOptions;
  this.dbCacheKey = JSON.stringify(dbOptions);

  this.server = express();

  var timeout = parseInt(process.env.TILESPLASH_REQUEST_TIMEOUT) || 0;

  if (timeout) {
    this.server.use(connectTimeout(timeout));
  }

  var self = this;
  this.server.use(function(req, res, next){
    req.on('timeout', function(){
      self.log('Request timed out', 'error');
    });
    next();
  });

  this.server.use(pgMiddleware(dbOptions));

  this.cacheOptions = cacheOptions || {};
  this._cache = new Caching(cacheType || 'memory', cacheOptions);

  this.logLevel('info');
};

Tilesplash.prototype.logging = function(callback){
  this.log = callback;
};

Tilesplash.prototype.logLevel = function(logLevel){
  var logLevels = ['debug', 'info', 'warn', 'error'];
  logLevel = logLevels.indexOf(logLevel);

  this.logging(function(message, level){
    var messageLevel = logLevels.indexOf(level);
    if (logLevel <= messageLevel) {
      console.log('['+level+']', JSON.stringify(message, null, 2));
    }
  });
};

Tilesplash.prototype.defaultCacheKeyGenerator = function(tile){
  return 'tilesplash_tile:'+this.dbCacheKey+'/'+tile.layer+'/'+tile.z+'/'+tile.x+'/'+tile.y;
};

Tilesplash.prototype.cache = function(cacheKeyGenerator, ttl){
  var self = this;
  if (typeof cacheKeyGenerator == 'number') {
    ttl = cacheKeyGenerator;
    cacheKeyGenerator = undefined;
  }
  this.defaultCacher = cacheKeyGenerator || function(tile){
    return self.defaultCacheKeyGenerator(tile);
  };
  this.defaultTtl = ttl;
};

function stringifyTopojson(layers, tile){
  layers = clone(layers);
  return topojson.topology(layers, {
    'property-transform': function(properties, key, value){
      properties[key] = value;
      return true;
    }
  });
}

Tilesplash.prototype.layer = function(name, ___){
  var callbacks = Array.prototype.slice.call(arguments);
  callbacks.shift();
  var tileRenderer = callbacks.pop();

  var self = this;
  this.server.get('/'+name+'/:z/:x/:y.:ext', function(req, res, throwError){
    var render = function(){
      render.query.apply(render, arguments);
    };
    render.query = function(data){
      gotTile(data);
    };
    render.queryFile = function(file, encoding){
      fs.readFile(file, encoding || 'utf8', function(err, data){
        if (err) {
          render.error(err);
        } else {
          gotTile(data);
        }
      });
    };
    render.raw = function(){
      res.send.apply(res, arguments);
    };
    render.rawFile = function(file, encoding){
      fs.readFile(file, encoding || 'utf8', function(err, data){
        if (err) {
          render.error(err);
        } else {
          render.raw(data);
        }
      });
    };
    render.empty = function(){
      render.raw({});
    };
    render.error = function(msg){
      render.raw(500);
      throwError( (msg instanceof Error) ? msg : new Error(msg) );
    };

    if (req.params.ext != 'topojson') {
      render.error('unsupported extension '+req.params.ext);
      return;
    }

    var stringify = stringifyTopojson;

    var tile = {};
    tile.layer = name;
    tile.x = req.params.x*1;
    tile.y = req.params.y*1;
    tile.z = req.params.z*1;

    tile.bounds = self.projection.bbox(req.params.x, req.params.y, req.params.z, false, '900913');
    tile.bbox = 'ST_SetSRID(ST_MakeBox2D(ST_MakePoint('+tile.bounds[0]+', '+tile.bounds[1]+'), ST_MakePoint('+tile.bounds[2]+', '+tile.bounds[3]+')), 3857)';
    tile.bbox_4326 = 'ST_Transform('+tile.bbox+', 4326)';
    tile.geom_hash = 'Substr(MD5(ST_AsBinary(the_geom)), 1, 10)';

    self.log('Rendering tile '+tile.layer+'/'+tile.z+'/'+tile.x+'/'+tile.y, 'debug');

    var parseSql = function(sql, done){
      if (typeof sql == 'number') {
        done(null, sql);
        return;
      }
      if (typeof sql == 'object') {
        if (Array.isArray(sql)) {
          async.map(sql, function(item, next){
            parseSql(item, function(err, out){
              next(err, out);
            });
          }, function(err, results){
            done(err, results);
          });
          return;
        } else {
          var keys = Object.keys(sql);
          async.map(keys, function(item, next){
            parseSql(sql[item], function(err, out){
              next(err, out);
            });
          }, function(err, results){
            var output = {};
            keys.forEach(function(d, i){
              output[d] = results[i];
            });
            done(err, output);
          });
          return;
        }
      }
      if (typeof sql != 'string') {
        done(['Trying to parse non-string SQL', sql]);
        return;
      }
      var templatePattern = /\!([a-zA-Z_\-]+)\!/g;
      var templateVariables = {};

      sql = sql.replace(templatePattern, function(match){
        match = match.substr(1, match.length-2);
        return tile[match];
      });
      done(null, sql);
    };

    var sqlToGeojson = function(sql, done){
      if (sql === false || sql === null) {
        done(null, {});
        return;
      }
      parseSql(sql, function(parsingError, fullsql){
        if (parsingError) {
          done(parsingError);
          return;
        }
        var args = [];
        if (typeof fullsql == 'object' && Array.isArray(fullsql)) {
          args = fullsql;
          fullsql = args.shift();
        }
        self.log('Running Query', 'debug');
        self.log('  SQL: '+fullsql, 'debug');
        self.log('  Arguments: '+JSON.stringify(args), 'debug');
        req.db.query(fullsql, args, function(sqlError, result){
          if (sqlError) {
            done([sqlError, fullsql, args]);
          } else {
            var geojson = {
              "type": "FeatureCollection",
              "features": []
            };
            result.rows.forEach(function(row){
              var properties = {};
              for (var attribute in row) {
                if (attribute != 'the_geom_geojson') {
                  properties[attribute] = row[attribute];
                }
              }
              geojson.features.push({
                "type": "Feature",
                "geometry": JSON.parse(row.the_geom_geojson),
                "properties": properties
              });
            });

            done(null, geojson);
          }
        });
      });
    };

    var tileContext = {
      cache: function(cacheKeyGenerator, ttl){
        if (typeof cacheKeyGenerator == 'number') {
          ttl = cacheKeyGenerator;
          cacheKeyGenerator = undefined;
        }
        if (typeof cacheKeyGenerator == 'function') {
          this._cacher = cacheKeyGenerator;
        }
        this._ttl = ttl;
      }
    };

    var runQuery = function(structuredSql, done){
      if (structuredSql === false || structuredSql === null) {
        done(null, {});
        return;
      }

      if (typeof structuredSql != 'object' || Array.isArray(structuredSql)) {
        structuredSql = {vectile: structuredSql};
      }

      var geojsonLayers = {};
      async.forEach(Object.keys(structuredSql), function(layer, next){
        sqlToGeojson(structuredSql[layer], function(err, geojson){
          if (err) {
            next(err);
          } else {
            geojsonLayers[layer] = geojson;
            next();
          }
        });
      }, function(err){
        if (err) {
          done(err);
        } else {
          done(null, geojsonLayers);
        }
      });
    };

    var gotTile = function(tileOutput){
      parseSql(tileOutput, function(parsingError, structuredSql){
        if (parsingError) {
          render.error(['SQL Parsing Error', parsingError]);
          return;
        }
        var cacher = tileContext._cacher || self.defaultCacher || false;

        if (cacher) {
          var cacheKey = cacher(tile);
          var ttl = tileContext._ttl || self.defaultTtl || 3600000;
          self._cache(cacheKey, ttl, function(done){
            self.log('cache miss', 'debug');
            runQuery(structuredSql, function(queryError, layers){
              done(queryError, layers);
            });
          }, function(layerError, layers){
            if (layerError) {
              render.error(['layer error', layerError]);
            } else {
              res.send(stringify(layers, tile));
            }
          });
        } else {
          runQuery(structuredSql, function(queryError, layers){
            if (queryError) {
              render.error(['error running query', queryError]);
            } else {
              res.send(stringify(layers, tile));
            }
          });
        }
      });
    };

    var outputData;

    async.eachSeries(callbacks, function(middleware, next){
      middleware(req, res, tile, function(err, data){
        outputData = data;
        next(err);
      });
    }, function(err){
      if (err) {
        render.error(['Middleware error', err]);
      } else {
        tileRenderer.call(tileContext, tile, render);
      }
    });
  });
};

module.exports = Tilesplash;
<commit_msg>templatePattern fixed (digits added)<commit_after>var fs = require('fs');
var express = require('express');
var async = require('async');
var topojson = require('topojson');
var pg = require('pg');
var SphericalMercator = require('sphericalmercator');
var Caching = require('caching');
var clone = require('clone');
var connectTimeout = require('connect-timeout');

var pgMiddleware = function(dbOptions){
  return function(req, res, next){
    pg.connect(dbOptions, function(err, client, done){
      if (err) {
        throw err;
      } else {
        req.db = client;
        res.on('finish', done);
        res.on('error', done);
        req.on('timeout', done);

        var timeout = parseInt(process.env.TILESPLASH_STATEMENT_TIMEOUT) || 0;

        if (timeout) {
          client.query('SET statement_timeout=$1', [timeout], function(){});
        }

        next();
      }
    });
  };
};

var Tilesplash = function(dbOptions, cacheType, cacheOptions){
  this.projection = new SphericalMercator({
    size: 256
  });

  this.dbOptions = dbOptions;
  this.dbCacheKey = JSON.stringify(dbOptions);

  this.server = express();

  var timeout = parseInt(process.env.TILESPLASH_REQUEST_TIMEOUT) || 0;

  if (timeout) {
    this.server.use(connectTimeout(timeout));
  }

  var self = this;
  this.server.use(function(req, res, next){
    req.on('timeout', function(){
      self.log('Request timed out', 'error');
    });
    next();
  });

  this.server.use(pgMiddleware(dbOptions));

  this.cacheOptions = cacheOptions || {};
  this._cache = new Caching(cacheType || 'memory', cacheOptions);

  this.logLevel('info');
};

Tilesplash.prototype.logging = function(callback){
  this.log = callback;
};

Tilesplash.prototype.logLevel = function(logLevel){
  var logLevels = ['debug', 'info', 'warn', 'error'];
  logLevel = logLevels.indexOf(logLevel);

  this.logging(function(message, level){
    var messageLevel = logLevels.indexOf(level);
    if (logLevel <= messageLevel) {
      console.log('['+level+']', JSON.stringify(message, null, 2));
    }
  });
};

Tilesplash.prototype.defaultCacheKeyGenerator = function(tile){
  return 'tilesplash_tile:'+this.dbCacheKey+'/'+tile.layer+'/'+tile.z+'/'+tile.x+'/'+tile.y;
};

Tilesplash.prototype.cache = function(cacheKeyGenerator, ttl){
  var self = this;
  if (typeof cacheKeyGenerator == 'number') {
    ttl = cacheKeyGenerator;
    cacheKeyGenerator = undefined;
  }
  this.defaultCacher = cacheKeyGenerator || function(tile){
    return self.defaultCacheKeyGenerator(tile);
  };
  this.defaultTtl = ttl;
};

function stringifyTopojson(layers, tile){
  layers = clone(layers);
  return topojson.topology(layers, {
    'property-transform': function(properties, key, value){
      properties[key] = value;
      return true;
    }
  });
}

Tilesplash.prototype.layer = function(name, ___){
  var callbacks = Array.prototype.slice.call(arguments);
  callbacks.shift();
  var tileRenderer = callbacks.pop();

  var self = this;
  this.server.get('/'+name+'/:z/:x/:y.:ext', function(req, res, throwError){
    var render = function(){
      render.query.apply(render, arguments);
    };
    render.query = function(data){
      gotTile(data);
    };
    render.queryFile = function(file, encoding){
      fs.readFile(file, encoding || 'utf8', function(err, data){
        if (err) {
          render.error(err);
        } else {
          gotTile(data);
        }
      });
    };
    render.raw = function(){
      res.send.apply(res, arguments);
    };
    render.rawFile = function(file, encoding){
      fs.readFile(file, encoding || 'utf8', function(err, data){
        if (err) {
          render.error(err);
        } else {
          render.raw(data);
        }
      });
    };
    render.empty = function(){
      render.raw({});
    };
    render.error = function(msg){
      render.raw(500);
      throwError( (msg instanceof Error) ? msg : new Error(msg) );
    };

    if (req.params.ext != 'topojson') {
      render.error('unsupported extension '+req.params.ext);
      return;
    }

    var stringify = stringifyTopojson;

    var tile = {};
    tile.layer = name;
    tile.x = req.params.x*1;
    tile.y = req.params.y*1;
    tile.z = req.params.z*1;

    tile.bounds = self.projection.bbox(req.params.x, req.params.y, req.params.z, false, '900913');
    tile.bbox = 'ST_SetSRID(ST_MakeBox2D(ST_MakePoint('+tile.bounds[0]+', '+tile.bounds[1]+'), ST_MakePoint('+tile.bounds[2]+', '+tile.bounds[3]+')), 3857)';
    tile.bbox_4326 = 'ST_Transform('+tile.bbox+', 4326)';
    tile.geom_hash = 'Substr(MD5(ST_AsBinary(the_geom)), 1, 10)';

    self.log('Rendering tile '+tile.layer+'/'+tile.z+'/'+tile.x+'/'+tile.y, 'debug');

    var parseSql = function(sql, done){
      if (typeof sql == 'number') {
        done(null, sql);
        return;
      }
      if (typeof sql == 'object') {
        if (Array.isArray(sql)) {
          async.map(sql, function(item, next){
            parseSql(item, function(err, out){
              next(err, out);
            });
          }, function(err, results){
            done(err, results);
          });
          return;
        } else {
          var keys = Object.keys(sql);
          async.map(keys, function(item, next){
            parseSql(sql[item], function(err, out){
              next(err, out);
            });
          }, function(err, results){
            var output = {};
            keys.forEach(function(d, i){
              output[d] = results[i];
            });
            done(err, output);
          });
          return;
        }
      }
      if (typeof sql != 'string') {
        done(['Trying to parse non-string SQL', sql]);
        return;
      }
      var templatePattern = /\!([0-9a-zA-Z_\-]+)\!/g;
      var templateVariables = {};

      sql = sql.replace(templatePattern, function(match){
        match = match.substr(1, match.length-2);
        return tile[match];
      });
      done(null, sql);
    };

    var sqlToGeojson = function(sql, done){
      if (sql === false || sql === null) {
        done(null, {});
        return;
      }
      parseSql(sql, function(parsingError, fullsql){
        if (parsingError) {
          done(parsingError);
          return;
        }
        var args = [];
        if (typeof fullsql == 'object' && Array.isArray(fullsql)) {
          args = fullsql;
          fullsql = args.shift();
        }
        self.log('Running Query', 'debug');
        self.log('  SQL: '+fullsql, 'debug');
        self.log('  Arguments: '+JSON.stringify(args), 'debug');
        req.db.query(fullsql, args, function(sqlError, result){
          if (sqlError) {
            done([sqlError, fullsql, args]);
          } else {
            var geojson = {
              "type": "FeatureCollection",
              "features": []
            };
            result.rows.forEach(function(row){
              var properties = {};
              for (var attribute in row) {
                if (attribute != 'the_geom_geojson') {
                  properties[attribute] = row[attribute];
                }
              }
              geojson.features.push({
                "type": "Feature",
                "geometry": JSON.parse(row.the_geom_geojson),
                "properties": properties
              });
            });

            done(null, geojson);
          }
        });
      });
    };

    var tileContext = {
      cache: function(cacheKeyGenerator, ttl){
        if (typeof cacheKeyGenerator == 'number') {
          ttl = cacheKeyGenerator;
          cacheKeyGenerator = undefined;
        }
        if (typeof cacheKeyGenerator == 'function') {
          this._cacher = cacheKeyGenerator;
        }
        this._ttl = ttl;
      }
    };

    var runQuery = function(structuredSql, done){
      if (structuredSql === false || structuredSql === null) {
        done(null, {});
        return;
      }

      if (typeof structuredSql != 'object' || Array.isArray(structuredSql)) {
        structuredSql = {vectile: structuredSql};
      }

      var geojsonLayers = {};
      async.forEach(Object.keys(structuredSql), function(layer, next){
        sqlToGeojson(structuredSql[layer], function(err, geojson){
          if (err) {
            next(err);
          } else {
            geojsonLayers[layer] = geojson;
            next();
          }
        });
      }, function(err){
        if (err) {
          done(err);
        } else {
          done(null, geojsonLayers);
        }
      });
    };

    var gotTile = function(tileOutput){
      parseSql(tileOutput, function(parsingError, structuredSql){
        if (parsingError) {
          render.error(['SQL Parsing Error', parsingError]);
          return;
        }
        var cacher = tileContext._cacher || self.defaultCacher || false;

        if (cacher) {
          var cacheKey = cacher(tile);
          var ttl = tileContext._ttl || self.defaultTtl || 3600000;
          self._cache(cacheKey, ttl, function(done){
            self.log('cache miss', 'debug');
            runQuery(structuredSql, function(queryError, layers){
              done(queryError, layers);
            });
          }, function(layerError, layers){
            if (layerError) {
              render.error(['layer error', layerError]);
            } else {
              res.send(stringify(layers, tile));
            }
          });
        } else {
          runQuery(structuredSql, function(queryError, layers){
            if (queryError) {
              render.error(['error running query', queryError]);
            } else {
              res.send(stringify(layers, tile));
            }
          });
        }
      });
    };

    var outputData;

    async.eachSeries(callbacks, function(middleware, next){
      middleware(req, res, tile, function(err, data){
        outputData = data;
        next(err);
      });
    }, function(err){
      if (err) {
        render.error(['Middleware error', err]);
      } else {
        tileRenderer.call(tileContext, tile, render);
      }
    });
  });
};

module.exports = Tilesplash;
<|endoftext|>


<commit_before>'use strict';

const iso3166_data = require('./iso3166Data');
const getISO3166 = require('./getISO3166');
const getIso3166ByPhone = require('./getISO3166ByPhone');
const validatePhoneIso3166 = require('./validatePhoneISO3166');

/**
 *
 * If no "+" sign treat as USA or CAN phone number
 * @param {string} phone
 * @param {string} country
 * @returns []
 */

module.exports = function (phone, country) {
	const result = [];
	let formatPhone = (typeof phone !== 'string') ? '' : phone.trim();
	const formatCountry = (typeof country !== 'string') ? '' : country.trim();

	let plusSign = false;

	if (formatPhone.match(/^\+/)) {
		plusSign = true;
	}

	// remove any non-digit character, included the +
	formatPhone = formatPhone.replace(/\D/g, '');

	// if no country, default is USA
	let iso3166 = getISO3166(formatCountry);

	if (Object.keys(iso3166).length === 0) {
		return result;
	}

	let defaultCountry = false;

	if (formatCountry) {
		// remove leading 0s for all countries except 'GAB', 'CIV', 'COG'
		if (['GAB', 'CIV', 'COG'].indexOf(iso3166.alpha3) === -1) {
			formatPhone = formatPhone.replace(/^0+/, '');
		}

		// if input 89234567890, RUS, remove the 8
		if (iso3166.alpha3 === 'RUS' && formatPhone.length === 11 && formatPhone.match(/^89/) !== null) {
			formatPhone = formatPhone.replace(/^8+/, '');
		}

		if (!plusSign && iso3166.phone_number_lengths.includes(formatPhone.length)) {
			formatPhone = iso3166.country_code + formatPhone;
			// C: have country, no plus sign --->
			//	case 1
			//		check phone_number_length == formatPhone.length
			//		add back the country code
			//	case 2
			//		phone_number_length+phone_country_code.length == formatPhone.length
			//		then go to D
		}
	} else if (plusSign) {
		// A: no country, have plus sign --> lookup country_code, length, and get the iso3166 directly
		// also validation is done here. so, the iso3166 is the matched result.
		let possibleIso3166;
		[iso3166, possibleIso3166] = getIso3166ByPhone(formatPhone);

		if (!iso3166) {
			// for some countries, the phone number usually includes one trunk prefix for local use
			// The UK mobile phone number ‘07911 123456’ in international format is ‘+44 7911 123456’, so without the first zero.
			// 8 (AAA) BBB-BB-BB, 0AA-BBBBBBB
			// the numbers should be omitted in international calls
			if (possibleIso3166) {
				iso3166 = possibleIso3166;
				formatPhone = iso3166.country_code + formatPhone.replace(new RegExp(`^${iso3166.country_code}\\d`),'');
			} else {
				iso3166 = {};
			}
		}
	} else if (iso3166.phone_number_lengths.includes(formatPhone.length)) {
		// B: no country, no plus sign --> treat it as USA
		// 1. check length if == 11, or 10, if 10, add +1, then go go D
		// no plus sign, no country is given. then it must be USA
		// iso3166 = iso3166_data[0]; already assign by the default value
		formatPhone = '1' + formatPhone;
		defaultCountry = true;
	}

	let validateResult = validatePhoneIso3166(formatPhone, iso3166);

	if (validateResult) {
		return ['+' + formatPhone, iso3166.alpha3];
	}

	if (defaultCountry)  {
		// also try to validate against CAN for default country, as CAN is also start with +1
		iso3166 = getISO3166('CAN');
		validateResult = validatePhoneIso3166(formatPhone, iso3166);
		if (validateResult) {
			return ['+' + formatPhone, iso3166.alpha3];
		}
	}

	return result;
};

module.exports.iso3166_data = iso3166_data;
<commit_msg>Add allowLandline parameter allowing non-mobile phone numbers<commit_after>'use strict';

const iso3166_data = require('./iso3166Data');
const getISO3166 = require('./getISO3166');
const getIso3166ByPhone = require('./getISO3166ByPhone');
const validatePhoneIso3166 = require('./validatePhoneISO3166');

/**
 *
 * If no "+" sign treat as USA or CAN phone number
 * @param {string} phone
 * @param {string} country
 * @returns []
 */

module.exports = function (phone, country, allowLandline) {
	const result = [];
	let formatPhone = (typeof phone !== 'string') ? '' : phone.trim();
	const formatCountry = (typeof country !== 'string') ? '' : country.trim();

	let plusSign = false;

	if (formatPhone.match(/^\+/)) {
		plusSign = true;
	}

	// remove any non-digit character, included the +
	formatPhone = formatPhone.replace(/\D/g, '');

	// if no country, default is USA
	let iso3166 = getISO3166(formatCountry);

	if (Object.keys(iso3166).length === 0) {
		return result;
	}

	let defaultCountry = false;

	if (formatCountry) {
		// remove leading 0s for all countries except 'GAB', 'CIV', 'COG'
		if (['GAB', 'CIV', 'COG'].indexOf(iso3166.alpha3) === -1) {
			formatPhone = formatPhone.replace(/^0+/, '');
		}

		// if input 89234567890, RUS, remove the 8
		if (iso3166.alpha3 === 'RUS' && formatPhone.length === 11 && formatPhone.match(/^89/) !== null) {
			formatPhone = formatPhone.replace(/^8+/, '');
		}

		if (!plusSign && iso3166.phone_number_lengths.includes(formatPhone.length)) {
			formatPhone = iso3166.country_code + formatPhone;
			// C: have country, no plus sign --->
			//	case 1
			//		check phone_number_length == formatPhone.length
			//		add back the country code
			//	case 2
			//		phone_number_length+phone_country_code.length == formatPhone.length
			//		then go to D
		}
	} else if (plusSign) {
		// A: no country, have plus sign --> lookup country_code, length, and get the iso3166 directly
		// also validation is done here. so, the iso3166 is the matched result.
		let possibleIso3166;
		[iso3166, possibleIso3166] = getIso3166ByPhone(formatPhone);

		if (!iso3166) {
			// for some countries, the phone number usually includes one trunk prefix for local use
			// The UK mobile phone number ‘07911 123456’ in international format is ‘+44 7911 123456’, so without the first zero.
			// 8 (AAA) BBB-BB-BB, 0AA-BBBBBBB
			// the numbers should be omitted in international calls
			if (possibleIso3166) {
				iso3166 = possibleIso3166;
				formatPhone = iso3166.country_code + formatPhone.replace(new RegExp(`^${iso3166.country_code}\\d`),'');
			} else {
				iso3166 = {};
			}
		}
	} else if (iso3166.phone_number_lengths.includes(formatPhone.length)) {
		// B: no country, no plus sign --> treat it as USA
		// 1. check length if == 11, or 10, if 10, add +1, then go go D
		// no plus sign, no country is given. then it must be USA
		// iso3166 = iso3166_data[0]; already assign by the default value
		formatPhone = '1' + formatPhone;
		defaultCountry = true;
	}

	let validateResult = validatePhoneIso3166(formatPhone, iso3166, allowLandline);

	if (validateResult) {
		return ['+' + formatPhone, iso3166.alpha3];
	}

	if (defaultCountry)  {
		// also try to validate against CAN for default country, as CAN is also start with +1
		iso3166 = getISO3166('CAN');
		validateResult = validatePhoneIso3166(formatPhone, iso3166, allowLandline);
		if (validateResult) {
			return ['+' + formatPhone, iso3166.alpha3];
		}
	}

	return result;
};

module.exports.iso3166_data = iso3166_data;
<|endoftext|>


<commit_before>
var util = require('util')
  , ProductAPI = require('facet-product')
  , CategoryAPI = require('facet-category')
  ;

/**
 * Catalog API constructor
 *
 * @param   {Object}  options   Options object - must contain 'db' (mongoose instance)
 *                              and 'intercom' (EventEmitter instance) keys.
 *
 * @return  {void}
 */
var CatalogAPI = function( options ){

  // set the options
  this.setOptions(options);

  // check the contraints and set the custom modules
  if( this.options.hasOwnProperty("ProductsAPI") && this.checkConstraints( this.options.ProductsAPI ) ) {
    ProductAPI = this.options.ProductsAPI;
  }
  if( this.options.hasOwnProperty("CategoriesAPI") && this.checkConstraints( this.options.CategoriesAPI ) ) {
    CategoryAPI = this.options.CategoriesAPI;
  }

  // instantiate the api modules
  this.Products = new ProductAPI( this.options );
  this.Categories = new CategoryAPI( this.options );

  // register the events
  this.registerEvents();
};

/**
 * Registers Catalog API event listeners
 *
 * @return  {void}
 */
CatalogAPI.prototype.registerEvents = function() {

};

/**
 * Binds product and category routes to the provided router instance.
 *
 * @param   {Object}   router         Router instance (express, koa, custom, etc)
 * @param   {Object}   routeOptions   Options for route setup.
 *
 * @return  {void}
 */
CatalogAPI.prototype.bindRoutes = function( router, routeOptions ) {

  // set the router
  this.router = router;

  // iterate through the routeOptions
  for( var route in routeOptions.routes ) {
    var api = routeOptions.routes[route];

    if( this.hasOwnProperty(api) ) {
      this[api].bindRoutes( this.router, {'route': route} );
    }
    else {
      // TODO: emit or log error about incorrect route binding attempt
    }
  }

  return this.router;

};

// export the main function
exports = module.exports = CatalogAPI;
<commit_msg>Switched the catalog facet to extend Core<commit_after>"use strict";
var util = require('util'),
  Core = require('facet-core').Core,
  ProductAPI = require('facet-product'),
  CategoryAPI = require('facet-category');

/**
 * Catalog API constructor
 *
 * @param   {Object}  options   Options object - must contain 'db' (mongoose instance)
 *                              and 'intercom' (EventEmitter instance) keys.
 *
 * @return  {void}
 */
var CatalogAPI = function( options ){

  // set the options
  this.setCommonAttributes( options );

  // check the contraints and set the custom modules
  if( this.options.hasOwnProperty("ProductsAPI") && this.checkConstraints( this.options.ProductsAPI ) ) {
    ProductAPI = this.options.ProductsAPI;
  }
  if( this.options.hasOwnProperty("CategoriesAPI") && this.checkConstraints( this.options.CategoriesAPI ) ) {
    CategoryAPI = this.options.CategoriesAPI;
  }

  // instantiate the api modules
  this.Products = new ProductAPI( this.options );
  this.Categories = new CategoryAPI( this.options );

  // // register the events
  // this.registerEvents();
};

/**
 * Catalog API inherits from Core
 */
util.inherits(CatalogAPI, Core);

// export the main function
exports = module.exports = CatalogAPI;
<|endoftext|>


<commit_before>var ChildProcess = require('child_process');
var IS_WIN = process.platform === 'win32';
var TableParser = require( 'table-parser' );
/**
 * End of line.
 * Basically, the EOL should be:
 * - windows: \r\n
 * - *nix: \n
 * But i'm trying to get every possibilities covered.
 */
var EOL = /(\r\n)|(\n\r)|\n|\r/;
var SystemEOL = require( 'os' ).EOL;

/**
 * Execute child process
 * @type {Function}
 * @param {String[]} args
 * @param {Function} callback
 * @param {Object=null} callback.err
 * @param {Object[]} callback.stdout
 */

var Exec = module.exports = exports = function( args, callback) {
    if (Array.isArray(args)) {
        args = args.join(' ');
    }

    // windows 下直接通过exec方法来wmic process get没有stdout，因此通过调用CMD的方法来做
    if( IS_WIN ){

        var spawn = ChildProcess.spawn;
        var CMD = spawn('cmd');
        var stdout = [];
        var stderr = null;

        CMD.stdout.on('data', function (data) {
            stdout += data.toString();
        });

        CMD.stderr.on('data', function (data) {

            if( stderr === null ){
                stderr = data.toString();
            }
            else {
                stderr += data.toString();
            }
        });

        CMD.on('exit', function () {

            var beginRow;
            stdout = stdout.split( EOL );

            // 寻找到数据的第一行（标题行）
            stdout.forEach(function( out, index ){
                if( out && typeof beginRow == 'undefined' && out.indexOf( 'CommandLine' ) === 0 ){
                    beginRow = index;
                }
            });

            // 去掉开头和结尾 （开头为CMD的版权等，结尾为当前路径）
            stdout.splice( stdout.length - 1 , 1 );
            stdout.splice( 0 , beginRow );

            callback( stderr, stdout.join( SystemEOL ) || false );
        });

        CMD.stdin.write('wmic process get ProcessId,CommandLine \n');
        CMD.stdin.end();
    }
    else {
        ChildProcess.exec( 'ps ' + args, function( err, stdout, stderr) {

            if (err || stderr) {
                return callback( err || stderr.toString() );
            }
            else {
                stdout = stdout.toString();
                callback(null, stdout || false);
            }
        });
    }
};

/**
 * Query Process: Focus on pid & cmd
 * @param query
 * @param {String|String[]} query.pid
 * @param {String} query.command RegExp String
 * @param {String} query.arguments RegExp String
 * @param {Function} callback
 * @param {Object=null} callback.err
 * @param {Object[]} callback.processList
 * @return {Object}
 */

exports.lookup = function(query, callback) {

    /**
     * add 'l' as default ps arguments, since the default ps output in linux like "ubuntu", wont include command arguments
     */
    var exeArgs = query.psargs || [ 'l' ];
    var filter = {};
    var idList;

    // Lookup by PID
    if ( query.pid ) {

        if( Array.isArray( query.pid ) ){
            idList = query.pid;
        }
        else {
            idList = [ query.pid ];
        }

        // Cast all PIDs as Strings
        idList = idList.map( function( v ){
            return String( v );
        } );

    }

    if( query.command ){
        filter[ 'command' ] = new RegExp( query.command, 'i' );
    }

    if( query.arguments ){
        filter[ 'arguments' ] = new RegExp( query.arguments, 'i' );
    }

    if( query.ppid ){
        filter['ppid'] = new RegExp( query.ppid );
    }

    return Exec( exeArgs, function(err, output) {
        if (err) {
            return callback( err );
        }
        else {
            var processList = parseGrid( output );
            var resultList = [];

            processList.forEach(function( p ){

                var flt;
                var type;
                var result = true;
                // 若限定了id列表
                if( idList && idList.indexOf( String( p.pid ) ) < 0 ){
                    return;
                }

                for( type in filter ){
                    flt = filter[ type ];
                    result = flt.test( p[ type ] ) ? result : false;
                }

                if( result ){
                    resultList.push( p );
                }
            });

            callback( null, resultList );
        }
    });
};

/**
 * Kill process
 * @param pid
 * @param next
 */

exports.kill = function( pid, next ){
    var killCommand = IS_WIN ? 'taskkill ' : 'kill ';
    var command = killCommand + ( IS_WIN ? '/F /PID ' + pid : pid );
    ChildProcess.exec( command, function( err, stdout, stderr) {
        if (!next) return;
        if ((err || stderr)) {
            next( err || stderr.toString() );
        }
        else {
            stdout = stdout.toString();

            // 在windows下，kill完马上查询会出现还能找到刚刚被kill的进程的情况，因此等待200ms，然后再认为kill结束
            if( IS_WIN ){
                setTimeout(function(){
                    next( null, stdout );
                }, 200 );
            }
            else {
                next( null, stdout );
            }
        }
    });
};

/**
 * Parse the stdout into readable object.
 * @param {String} output
 */

function parseGrid( output ) {
    if ( !output ) {
        return output;
    }
    return formatOutput( TableParser.parse( output ) );
}

/**
 * 格式化输出结构，从里面提取出 pid, command, arguments
 * @param data
 * @return {Array}
 */

function formatOutput( data ){
    var formatedData = [];
    data.forEach(function( d ){
        var pid = ( d.PID && d.PID[ 0 ] ) || ( d.ProcessId && d.ProcessId[ 0 ] ) || undefined;
        var cmd = d.CMD || d.CommandLine || d.COMMAND || undefined;
        var ppid = ( d.PPID && d.PPID[ 0 ] ) || undefined;

        if( pid && cmd ){
            var command = cmd[ 0 ];
            var args = '';

            if( cmd.length > 1 ){
                args = cmd.slice( 1 );
            }

            formatedData.push( {
                pid: pid,
                command: command,
                arguments: args,
                ppid: ppid
            });
        }
    });

    return formatedData;
}
<commit_msg>Doing function type check instead of existential check.<commit_after>var ChildProcess = require('child_process');
var IS_WIN = process.platform === 'win32';
var TableParser = require( 'table-parser' );
/**
 * End of line.
 * Basically, the EOL should be:
 * - windows: \r\n
 * - *nix: \n
 * But i'm trying to get every possibilities covered.
 */
var EOL = /(\r\n)|(\n\r)|\n|\r/;
var SystemEOL = require( 'os' ).EOL;

/**
 * Execute child process
 * @type {Function}
 * @param {String[]} args
 * @param {Function} callback
 * @param {Object=null} callback.err
 * @param {Object[]} callback.stdout
 */

var Exec = module.exports = exports = function( args, callback) {
    if (Array.isArray(args)) {
        args = args.join(' ');
    }

    // windows 下直接通过exec方法来wmic process get没有stdout，因此通过调用CMD的方法来做
    if( IS_WIN ){

        var spawn = ChildProcess.spawn;
        var CMD = spawn('cmd');
        var stdout = [];
        var stderr = null;

        CMD.stdout.on('data', function (data) {
            stdout += data.toString();
        });

        CMD.stderr.on('data', function (data) {

            if( stderr === null ){
                stderr = data.toString();
            }
            else {
                stderr += data.toString();
            }
        });

        CMD.on('exit', function () {

            var beginRow;
            stdout = stdout.split( EOL );

            // 寻找到数据的第一行（标题行）
            stdout.forEach(function( out, index ){
                if( out && typeof beginRow == 'undefined' && out.indexOf( 'CommandLine' ) === 0 ){
                    beginRow = index;
                }
            });

            // 去掉开头和结尾 （开头为CMD的版权等，结尾为当前路径）
            stdout.splice( stdout.length - 1 , 1 );
            stdout.splice( 0 , beginRow );

            callback( stderr, stdout.join( SystemEOL ) || false );
        });

        CMD.stdin.write('wmic process get ProcessId,CommandLine \n');
        CMD.stdin.end();
    }
    else {
        ChildProcess.exec( 'ps ' + args, function( err, stdout, stderr) {

            if (err || stderr) {
                return callback( err || stderr.toString() );
            }
            else {
                stdout = stdout.toString();
                callback(null, stdout || false);
            }
        });
    }
};

/**
 * Query Process: Focus on pid & cmd
 * @param query
 * @param {String|String[]} query.pid
 * @param {String} query.command RegExp String
 * @param {String} query.arguments RegExp String
 * @param {Function} callback
 * @param {Object=null} callback.err
 * @param {Object[]} callback.processList
 * @return {Object}
 */

exports.lookup = function(query, callback) {

    /**
     * add 'l' as default ps arguments, since the default ps output in linux like "ubuntu", wont include command arguments
     */
    var exeArgs = query.psargs || [ 'l' ];
    var filter = {};
    var idList;

    // Lookup by PID
    if ( query.pid ) {

        if( Array.isArray( query.pid ) ){
            idList = query.pid;
        }
        else {
            idList = [ query.pid ];
        }

        // Cast all PIDs as Strings
        idList = idList.map( function( v ){
            return String( v );
        } );

    }

    if( query.command ){
        filter[ 'command' ] = new RegExp( query.command, 'i' );
    }

    if( query.arguments ){
        filter[ 'arguments' ] = new RegExp( query.arguments, 'i' );
    }

    if( query.ppid ){
        filter['ppid'] = new RegExp( query.ppid );
    }

    return Exec( exeArgs, function(err, output) {
        if (err) {
            return callback( err );
        }
        else {
            var processList = parseGrid( output );
            var resultList = [];

            processList.forEach(function( p ){

                var flt;
                var type;
                var result = true;
                // 若限定了id列表
                if( idList && idList.indexOf( String( p.pid ) ) < 0 ){
                    return;
                }

                for( type in filter ){
                    flt = filter[ type ];
                    result = flt.test( p[ type ] ) ? result : false;
                }

                if( result ){
                    resultList.push( p );
                }
            });

            callback( null, resultList );
        }
    });
};

/**
 * Kill process
 * @param pid
 * @param next
 */

exports.kill = function( pid, next ){
    var killCommand = IS_WIN ? 'taskkill ' : 'kill ';
    var command = killCommand + ( IS_WIN ? '/F /PID ' + pid : pid );
    ChildProcess.exec( command, function( err, stdout, stderr) {
        if (typeof next !== 'function') return;
        if ((err || stderr)) {
            next( err || stderr.toString() );
        }
        else {
            stdout = stdout.toString();

            // 在windows下，kill完马上查询会出现还能找到刚刚被kill的进程的情况，因此等待200ms，然后再认为kill结束
            if( IS_WIN ){
                setTimeout(function(){
                    next( null, stdout );
                }, 200 );
            }
            else {
                next( null, stdout );
            }
        }
    });
};

/**
 * Parse the stdout into readable object.
 * @param {String} output
 */

function parseGrid( output ) {
    if ( !output ) {
        return output;
    }
    return formatOutput( TableParser.parse( output ) );
}

/**
 * 格式化输出结构，从里面提取出 pid, command, arguments
 * @param data
 * @return {Array}
 */

function formatOutput( data ){
    var formatedData = [];
    data.forEach(function( d ){
        var pid = ( d.PID && d.PID[ 0 ] ) || ( d.ProcessId && d.ProcessId[ 0 ] ) || undefined;
        var cmd = d.CMD || d.CommandLine || d.COMMAND || undefined;
        var ppid = ( d.PPID && d.PPID[ 0 ] ) || undefined;

        if( pid && cmd ){
            var command = cmd[ 0 ];
            var args = '';

            if( cmd.length > 1 ){
                args = cmd.slice( 1 );
            }

            formatedData.push( {
                pid: pid,
                command: command,
                arguments: args,
                ppid: ppid
            });
        }
    });

    return formatedData;
}
<|endoftext|>


<commit_before>// @flow
/**
 * Draftail's main API entry point. Exposes all of the modules people
 * will need to create their own editor instances from Draftail.
 */
export { default as DraftailEditor } from "./components/DraftailEditor";
export { default as Icon } from "./components/Icon";
export { default as ToolbarButton } from "./components/ToolbarButton";
export { default as DraftUtils } from "./api/DraftUtils";
export { BLOCK_TYPE, ENTITY_TYPE, INLINE_STYLE } from "./api/constants";
<commit_msg>Expose draftjs-conductor data conversion APIs through Draftail’s API<commit_after>// @flow
/**
 * Draftail's main API entry point. Exposes all of the modules people
 * will need to create their own editor instances from Draftail.
 */
export { default as DraftailEditor } from "./components/DraftailEditor";
export { default as Icon } from "./components/Icon";
export { default as ToolbarButton } from "./components/ToolbarButton";
export { default as DraftUtils } from "./api/DraftUtils";
export { BLOCK_TYPE, ENTITY_TYPE, INLINE_STYLE } from "./api/constants";
// Expose methods from draftjs-conductor directly for users of Draftail.
export {
  createEditorStateFromRaw,
  serialiseEditorStateToRaw,
} from "draftjs-conductor";
<|endoftext|>


<commit_before>// https://sellercentral.amazon.com/forums/thread.jspa?threadID=369774&tstart=75

const { promisify } = require('util');
const fs = require('fs');
const writeFile = promisify(fs.writeFile);

let MWS = require('mws-simple');

// TODO: add Subscriptions and Recommendations categories
const {
    feeds, finances, inbound, inventory, outbound,
    merchFulfillment, orders, products, sellers, reports,
} = require('./endpoints');

const { isType, validateAndTransformParameters } = require('./validation');
const sleep = require('./sleep');

/* Monkeypatch mws-simple to accept authToken in it's constructor */
const oldMWS = MWS;

function Client(opts) {

    // force 'new' constructor
    if (!(this instanceof Client)) return new Client(opts);

    this.host = opts.host || 'mws.amazonservices.com';
    this.port = opts.port || 443

    if (opts.accessKeyId) this.accessKeyId = opts.accessKeyId;
    if (opts.secretAccessKey) this.secretAccessKey = opts.secretAccessKey;
    if (opts.merchantId) this.merchantId = opts.merchantId;
    if (opts.authToken) this.authToken = opts.authToken;
}

Client.prototype = oldMWS.prototype;

MWS = Client;

/* End Monkeypatch for mws-simple constructor */

/* Begin Monkeypatch mws-simple request() to apply the authToken as MWSAuthToken in the query */
const oldRequestFunction = MWS.prototype.request;

function newRequest(requestData, callback) {
    if (!requestData.query.MWSAuthToken && this.authToken) {
        requestData.query.MWSAuthToken = this.authToken;
    }
    return oldRequestFunction.apply(this, [requestData, callback]);
}

MWS.prototype.request = newRequest;

/* End Monkeypatch */

// TODO: implement Recommendations and Reports and Subscriptions
// http://s3.amazonaws.com/devo.docs.developer.amazonservices.com/en_DE/sellers/Sellers_ListMarketplaceParticipations.html

// TODO: probably don't need the "action" part, so long as "action" is always equal to the string
// passed to callEndpoint .. haven't finished looking through the entire API, so might not be?

// TODO: When specifying a "List of" type to Amazon, such as "IdList" to GetMatchingProductForId,
// apparently the API is expecting it to come in such as:
// 'IdList.Id.1': 'Id1',
// 'IdList.Id.2': 'Id2'
// .. The number part starts at 1 (argh!), but how do we determine what the part between the dots
// should be?  Other examples that also work: MarketplaceIdList.Id.1, MarketplaceId.Id.1,
// OrderStatus.Status.1, etc.  Is that something that is dealt with in mws-simple ? or is it some
// part of the Amazon API that I can't seem to find a bit of documentation about?

// take all the categories and flatten them down into a single flat object
// TODO: THIS MAY BE A BAD IDEA CONSIDERING THAT SOME CATEGORIES SHARE SOME ENDPOINT NAMES
// TODO: WE MAY NEED TO COME UP WITH A WAY TO DEAL WITH THAT.
const endpoints = Object.assign(
    {},
    feeds,
    finances,
    inbound,
    inventory,
    merchFulfillment,
    orders,
    products,
    sellers,
    reports
);

// flatten all 1-element arrays found within a result object into just their values
const flattenResult = (result) => {
    // console.warn('**** flattenResult', result);
    for (const r in result) {
        // console.warn('**** r=', r);
        if (Array.isArray(result[r]) && result[r].length === 1) {
            // console.warn('**** r is single element array');
            result[r] = result[r][0];
        }
        if (typeof result[r] === 'object') {
            // console.warn('**** r is object');
            result[r] = flattenResult(result[r]);
        }
    }
    // console.warn('**** returning ', result);
    return result;
}

function testFlattenResult() {
    const test = {
        test: ["test"],
        test2: ["test2", "test2"],
        test3: [
            {
                test4: ["test4"],
            },
        ],
    };
    console.warn(JSON.stringify(test));
    console.warn(flattenResult(test));
}

let mws = null;

// return a promise for making the desired request, flattening the response out
// to something that makes a little more sense, hopefully.

const requestPromise = (requestData) => {
    return new Promise((resolve, reject) => {
        mws.request(requestData, (err, result) => {
            if (err) {
                reject(err);
            } else {
                const flatResult = flattenResult(result)
                resolve(flatResult);
            }
        });
    });
};

const callEndpoint = async (name, options) => {
    const endpoint = endpoints[name];
    if (!endpoint) {
        console.error('**** callEndpoint did not find an endpoint called', name);
        return null;
    }
    if (endpoint.params) {
        options = validateAndTransformParameters(endpoint.params, options);
    }
    const queryOptions = {
        ...options,
        Action: endpoint.action,
        Version: endpoint.version,
    };

    const params = {
        path: `/${endpoint.category}/${endpoint.version}`,
        query: queryOptions,
    };

    return await requestPromise(params);
};

const init = ({ accessKeyId, secretAccessKey, merchantId, authToken }) => {
    mws = new MWS({
        accessKeyId,
        secretAccessKey,
        merchantId,
        authToken,
    });
    if (mws.authToken)
        mws.authToken = authToken;
};

module.exports = {
    init,
    callEndpoint,
};
<commit_msg>noticed and removed a bunch of out of date TODOs<commit_after>// https://sellercentral.amazon.com/forums/thread.jspa?threadID=369774&tstart=75

const { promisify } = require('util');
const fs = require('fs');
const writeFile = promisify(fs.writeFile);

let MWS = require('mws-simple');

// TODO: add Subscriptions and Recommendations categories
const {
    feeds, finances, inbound, inventory, outbound,
    merchFulfillment, orders, products, sellers, reports,
} = require('./endpoints');

const { isType, validateAndTransformParameters } = require('./validation');
const sleep = require('./sleep');

/* Monkeypatch mws-simple to accept authToken in it's constructor */
const oldMWS = MWS;

function Client(opts) {

    // force 'new' constructor
    if (!(this instanceof Client)) return new Client(opts);

    this.host = opts.host || 'mws.amazonservices.com';
    this.port = opts.port || 443

    if (opts.accessKeyId) this.accessKeyId = opts.accessKeyId;
    if (opts.secretAccessKey) this.secretAccessKey = opts.secretAccessKey;
    if (opts.merchantId) this.merchantId = opts.merchantId;
    if (opts.authToken) this.authToken = opts.authToken;
}

Client.prototype = oldMWS.prototype;

MWS = Client;

/* End Monkeypatch for mws-simple constructor */

/* Begin Monkeypatch mws-simple request() to apply the authToken as MWSAuthToken in the query */
const oldRequestFunction = MWS.prototype.request;

function newRequest(requestData, callback) {
    if (!requestData.query.MWSAuthToken && this.authToken) {
        requestData.query.MWSAuthToken = this.authToken;
    }
    return oldRequestFunction.apply(this, [requestData, callback]);
}

MWS.prototype.request = newRequest;

/* End Monkeypatch */

// http://s3.amazonaws.com/devo.docs.developer.amazonservices.com/en_US/sellers/Sellers_ListMarketplaceParticipations.html

// take all the categories and flatten them down into a single flat object
// TODO: THIS MAY BE A BAD IDEA CONSIDERING THAT SOME CATEGORIES SHARE SOME ENDPOINT NAMES
// TODO: WE MAY NEED TO COME UP WITH A WAY TO DEAL WITH THAT.
const endpoints = Object.assign(
    {},
    feeds,
    finances,
    inbound,
    inventory,
    merchFulfillment,
    orders,
    products,
    sellers,
    reports
);

// flatten all 1-element arrays found within a result object into just their values
const flattenResult = (result) => {
    // console.warn('**** flattenResult', result);
    for (const r in result) {
        // console.warn('**** r=', r);
        if (Array.isArray(result[r]) && result[r].length === 1) {
            // console.warn('**** r is single element array');
            result[r] = result[r][0];
        }
        if (typeof result[r] === 'object') {
            // console.warn('**** r is object');
            result[r] = flattenResult(result[r]);
        }
    }
    // console.warn('**** returning ', result);
    return result;
}

function testFlattenResult() {
    const test = {
        test: ["test"],
        test2: ["test2", "test2"],
        test3: [
            {
                test4: ["test4"],
            },
        ],
    };
    console.warn(JSON.stringify(test));
    console.warn(flattenResult(test));
}

// this will hold the mws reference after init() is called
let mws = null;

// return a promise for making the desired request, flattening the response out
// to something that makes a little more sense, hopefully.

const requestPromise = (requestData) => {
    return new Promise((resolve, reject) => {
        mws.request(requestData, (err, result) => {
            if (err) {
                reject(err);
            } else {
                const flatResult = flattenResult(result)
                resolve(flatResult);
            }
        });
    });
};

const callEndpoint = async (name, options) => {
    const endpoint = endpoints[name];
    if (!endpoint) {
        console.error('**** callEndpoint did not find an endpoint called', name);
        return null;
    }
    if (endpoint.params) {
        options = validateAndTransformParameters(endpoint.params, options);
    }
    const queryOptions = {
        ...options,
        Action: endpoint.action,
        Version: endpoint.version,
    };

    const params = {
        path: `/${endpoint.category}/${endpoint.version}`,
        query: queryOptions,
    };

    return await requestPromise(params);
};

const init = ({ accessKeyId, secretAccessKey, merchantId, authToken }) => {
    mws = new MWS({
        accessKeyId,
        secretAccessKey,
        merchantId,
        authToken,
    });
    if (mws.authToken)
        mws.authToken = authToken;
};

module.exports = {
    init,
    callEndpoint,
};
<|endoftext|>


<commit_before>const refresh = require('./lib/refresh');
const bot = require('./bot.js');
const settings = require('./../settings.json');
const modules = require('./modules.js');
const Message = require('./../orm/Message')
const commands = require('./commands');
const request = require('request-promise-native');
const influx = require('./../influx');

let lock = false;
let ready = false;
bot.on('error', (err) => {
    /**
     * Catch errors here
     */
    console.log("Stack Trace: " + err.stack);
})
process.on('unhandledRejection', (err) => {
    console.log("UNHANDLED REJECTION AT " + err.stack);
    if (err.toString().includes('Request to use token, but token was unavailable to the client'))
        process.exit();//restart
});
process.on('uncaughtException', (err) => console.log("UNHANDLED EXCEPTION AT " + err.stack));
bot.on('message', async function (message) {
    /**
     * if locked, reject everything except dm
     */
    new Message({
        messageID: message.id,
        channel: message.channel.id
    }).save();
    if (message.author.bot) return;
    if (lock) {
        if (!settings.owners.includes(message.author.id))
            //  if (message.channel.guild)
            return;//not DM
    }
    /**
     * Listen to messages and convert into params
     */
    if (message.member && message.member != null) {
        let muted = message.member.roles.find(function (r) {
            if (r.name.toLowerCase().includes('mute')) return r
        })
        if (muted)
            message.delete();
    }
    if (message.content.startsWith(settings.identifier)) {
        /**Extracting params */
        let params = message.content.substring(settings.identifier.length).trim();
        params = params.split(settings.delimiter || ' ');
        let cmd = params.shift().trim();
        commands.execute(cmd.toLowerCase(), params, message)
    }
    else {
        //ignore because normal message
    }
});
bot.on('lock', () => { lock = true; });
bot.on('unlock', () => { lock = false; });
bot.on("guildMemberAdd", async function (member) {
    var user = member.user;
    if (member.guild.id === "184536578654339072") {
        if (member.user.bot) return console.log(member.user.tag + " is a bot who joined " + member.guild.name)
        if (refresh(user, true)) {
            user.send("Welcome to the Revive Network\nBecome a verified member and get exclusive benefits by linking your discord account with your Revive account\nTo link, please follow this link\nhttps://battlelog.co/discord_link.php");
        }
    }
});
bot.on("guildMemberRemove", async function (member) {
    var user = member.user;
});
/**
bot.on('disconnect', function(event) {
    if (event.code === 0) return console.error(event);
    process.exit();//force restart
});*/

bot.on('ready', async function () {
    console.log("ReviveBot Ready");
    let dbs = await influx.getDatabaseNames();
    console.log(dbs);
    if (!dbs || dbs === null)
        console.log("Cant connect to influx")
    if (!dbs.includes('discord')) {
        console.log("Creating Dicord DB");
    }
    ready = true;
});
bot.on("guildMemberUpdate", async function (member, newMem) {
    let user = member.user;
    if (member.guild && (member.guild.id === "184536578654339072")) {
        let oldMem = member;
        if (!oldMem.roles.has("273105185566359562") && newMem.roles.has("273105185566359562")) return;
        if (!oldMem.roles.has("275317218911322112") && newMem.roles.has("275317218911322112")) return;
        if ((!oldMem.roles.has("184684916833779712") && newMem.roles.has("184684916833779712")) || (!oldMem.roles.has("200849956796497920") && newMem.roles.has("200849956796497920")) || (!oldMem.roles.has("184676864630063104") && newMem.roles.has("184676864630063104")) || (!oldMem.roles.has("286646245198528523") && newMem.roles.has("286646245198528523"))) {
            await request({ uri: 'http://localhost/v0/discord/reverse_link/' + user.id, method: "POST" });
        }
    }
});
setInterval(async function () {
    if (!ready) return;
    const guild = bot.guilds.get("184536578654339072");
    influx.writePoints([
        {
            measurement: 'statistics',
            fields: { count: guild.memberCount },
            tags: { type: 'members' }
        }
    ]).catch(console.log);
    let count = await Message.count();
    if (count)
        influx.writePoints([
            {
                measurement: 'statistics',
                fields: { count: count },
                tags: { type: 'messages' }
            }
        ]).catch(console.log);
    guild.roles.map(r => {
        influx.writePoints([
            {
                measurement: 'statistics',
                fields: { count: r.members.size },
                tags: { type: r.id }
            }
        ]).catch(console.log);
    });
    influx.writePoints([
        {
            measurement: 'statistics',
            fields: { count: guild.presences.map(p => p.status != 'offline').length },
            tags: { type: "online" }
        }
    ]).catch(console.log);
}, 1000)
<commit_msg>allow bot to be muted<commit_after>const refresh = require('./lib/refresh');
const bot = require('./bot.js');
const settings = require('./../settings.json');
const modules = require('./modules.js');
const Message = require('./../orm/Message')
const commands = require('./commands');
const request = require('request-promise-native');
const influx = require('./../influx');

let lock = false;
let ready = false;
bot.on('error', (err) => {
    /**
     * Catch errors here
     */
    console.log("Stack Trace: " + err.stack);
})
process.on('unhandledRejection', (err) => {
    console.log("UNHANDLED REJECTION AT " + err.stack);
    if (err.toString().includes('Request to use token, but token was unavailable to the client'))
        process.exit();//restart
});
process.on('uncaughtException', (err) => console.log("UNHANDLED EXCEPTION AT " + err.stack));
bot.on('message', async function (message) {
    /**
     * if locked, reject everything except dm
     */
    new Message({
        messageID: message.id,
        channel: message.channel.id
    }).save();
    if (lock) {
        if (!settings.owners.includes(message.author.id))
            //  if (message.channel.guild)
            return;//not DM
    }
    /**
     * Listen to messages and convert into params
     */
    if (message.member && message.member != null) {
        let muted = message.member.roles.find(function (r) {
            if (r.name.toLowerCase().includes('mute')) return r
        })
        if (muted)
            message.delete();
    }

    if (message.author.bot) return;

    if (message.content.startsWith(settings.identifier)) {
        /**Extracting params */
        let params = message.content.substring(settings.identifier.length).trim();
        params = params.split(settings.delimiter || ' ');
        let cmd = params.shift().trim();
        commands.execute(cmd.toLowerCase(), params, message)
    }
    else {
        //ignore because normal message
    }
});
bot.on('lock', () => { lock = true; });
bot.on('unlock', () => { lock = false; });
bot.on("guildMemberAdd", async function (member) {
    var user = member.user;
    if (member.guild.id === "184536578654339072") {
        if (member.user.bot) return console.log(member.user.tag + " is a bot who joined " + member.guild.name)
        if (refresh(user, true)) {
            user.send("Welcome to the Revive Network\nBecome a verified member and get exclusive benefits by linking your discord account with your Revive account\nTo link, please follow this link\nhttps://battlelog.co/discord_link.php");
        }
    }
});
bot.on("guildMemberRemove", async function (member) {
    var user = member.user;
});
/**
bot.on('disconnect', function(event) {
    if (event.code === 0) return console.error(event);
    process.exit();//force restart
});*/

bot.on('ready', async function () {
    console.log("ReviveBot Ready");
    let dbs = await influx.getDatabaseNames();
    console.log(dbs);
    if (!dbs || dbs === null)
        console.log("Cant connect to influx")
    if (!dbs.includes('discord')) {
        console.log("Creating Dicord DB");
    }
    ready = true;
});
bot.on("guildMemberUpdate", async function (member, newMem) {
    let user = member.user;
    if (member.guild && (member.guild.id === "184536578654339072")) {
        let oldMem = member;
        if (!oldMem.roles.has("273105185566359562") && newMem.roles.has("273105185566359562")) return;
        if (!oldMem.roles.has("275317218911322112") && newMem.roles.has("275317218911322112")) return;
        if ((!oldMem.roles.has("184684916833779712") && newMem.roles.has("184684916833779712")) || (!oldMem.roles.has("200849956796497920") && newMem.roles.has("200849956796497920")) || (!oldMem.roles.has("184676864630063104") && newMem.roles.has("184676864630063104")) || (!oldMem.roles.has("286646245198528523") && newMem.roles.has("286646245198528523"))) {
            await request({ uri: 'http://localhost/v0/discord/reverse_link/' + user.id, method: "POST" });
        }
    }
});
setInterval(async function () {
    if (!ready) return;
    const guild = bot.guilds.get("184536578654339072");
    influx.writePoints([
        {
            measurement: 'statistics',
            fields: { count: guild.memberCount },
            tags: { type: 'members' }
        }
    ]).catch(console.log);
    let count = await Message.count();
    if (count)
        influx.writePoints([
            {
                measurement: 'statistics',
                fields: { count: count },
                tags: { type: 'messages' }
            }
        ]).catch(console.log);
    guild.roles.map(r => {
        influx.writePoints([
            {
                measurement: 'statistics',
                fields: { count: r.members.size },
                tags: { type: r.id }
            }
        ]).catch(console.log);
    });
    influx.writePoints([
        {
            measurement: 'statistics',
            fields: { count: guild.presences.map(p => p.status != 'offline').length },
            tags: { type: "online" }
        }
    ]).catch(console.log);
}, 1000)
<|endoftext|>


<commit_before>'use strict';

const Joi = require('joi');
const Hoek = require('hoek');

const Schema = require('./schema');
const ServerMonitor = require('./serverlog/servermonitor');
const KnexMonitor = require('./knex/knexmonitor');

const internals = {
  initializePackages(options) {

    console.log('proteusjs is checking required packages');

    options.knex && (new KnexMonitor(options.knex, null));

    console.log('proteusjs done with package checking');
  },

  onPostStop(servermonitor) {
    return (server, next) => {
      //servermonitor.stop(next);
      return next();
    };
  },

  onPreStart(servermonitor, options) {
    return (server, next) => {
      const interval = options.ops.interval;
      servermonitor.startOps(interval);
      return next();
    };
  }
};

exports.register = function (server, options, next) {

  const result = Joi.validate(options, Schema.proteusjs);
  Hoek.assert(!result.error, 'Invalid options of proteusjs', result.error);

  internals.initializePackages(result.value);

  const servermonitor = new ServerMonitor(server, result.value);

  server.ext([{
    type: 'onPostStop',
    method: internals.onPostStop(servermonitor)
  }, {
    type: 'onPreStart',
    method: internals.onPreStart(servermonitor, result.value)
  }]);

  return servermonitor.start(next);
};

exports.register.attributes = {
  pkg: require('../package.json')
};
<commit_msg>Fixed knex log reference.<commit_after>'use strict';

const Joi = require('joi');
const Hoek = require('hoek');

const Schema = require('./schema');
const ServerMonitor = require('./serverlog/servermonitor');
const KnexMonitor = require('./knexlog/knexmonitor');

const internals = {
  initializePackages(options) {

    console.log('proteusjs is checking required packages');

    options.knex && (new KnexMonitor(options.knex, null));

    console.log('proteusjs done with package checking');
  },

  onPostStop(servermonitor) {
    return (server, next) => {
      //servermonitor.stop(next);
      return next();
    };
  },

  onPreStart(servermonitor, options) {
    return (server, next) => {
      const interval = options.ops.interval;
      servermonitor.startOps(interval);
      return next();
    };
  }
};

exports.register = function (server, options, next) {

  const result = Joi.validate(options, Schema.proteusjs);
  Hoek.assert(!result.error, 'Invalid options of proteusjs', result.error);

  internals.initializePackages(result.value);

  const servermonitor = new ServerMonitor(server, result.value);

  server.ext([{
    type: 'onPostStop',
    method: internals.onPostStop(servermonitor)
  }, {
    type: 'onPreStart',
    method: internals.onPreStart(servermonitor, result.value)
  }]);

  return servermonitor.start(next);
};

exports.register.attributes = {
  pkg: require('../package.json')
};
<|endoftext|>


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

function cli() {

  var findup = require('findup-sync');
  var path = require('path');
  var spawn = require('child_process').spawn;
  var gruntBin = path.join(__dirname, '../node_modules/.bin/grunt');
  var projectRoot;
  var peonsfile;

  peonsfile = findup('peonsfile.+(js|coffee)', {nocase: true});

  if (!peonsfile) {
    console.error(new Error('Unable to find a peonsfile.'));
    return;
  }

  projectRoot = path.dirname(peonsfile);

  process.argv.splice(0, 2);

  spawn(
    gruntBin,
    [
      '--gruntfile',
      path.join(__dirname, 'mainpeons.js'),
      '--projectDir',
      projectRoot
    ].concat(process.argv),
    {
      stdio: 'inherit'
    }
  ).on('close', function(code) {
    process.exit(code);
  });
}

module.exports = {
  cli: cli
};
<commit_msg>fix camelcase fup<commit_after>#!/usr/bin/env node

function cli() {

  var findup = require('findup-sync');
  var path = require('path');
  var spawn = require('child_process').spawn;
  var gruntBin = path.join(__dirname, '../node_modules/.bin/grunt');
  var projectRoot;
  var peonsfile;

  peonsfile = findup('peonsfile.+(js|coffee)', {nocase: true});

  if (!peonsfile) {
    console.error(new Error('Unable to find a peonsfile.'));
    return;
  }

  projectRoot = path.dirname(peonsfile);

  process.argv.splice(0, 2);

  spawn(
    gruntBin,
    [
      '--gruntfile',
      path.join(__dirname, 'mainPeons.js'),
      '--projectDir',
      projectRoot
    ].concat(process.argv),
    {
      stdio: 'inherit'
    }
  ).on('close', function(code) {
    process.exit(code);
  });
}

module.exports = {
  cli: cli
};
<|endoftext|>


<commit_before>"use strict";

const BloggifyServer = require("bloggify-server")
    , defaultOptions = require("./options")
    , ul = require("ul")
    , mapO = require("map-o")
    ;

module.exports = class Bloggify extends BloggifyServer {

    /**
     * Bloggify
     * Creates a new instance of `Bloggify`.
     *
     * @name Bloggify
     * @function
     * @param {Object} options An object containing the following fields:
     *
     *  - `loadPlugins`: false
     *  - `plugins`: plugins to load
     *  - `pluginConfigs`: plugin configs
     *  - `metadata`:
     *       - `siteTitle`: the site title
     *       - `siteDescription`: the site title
     *       - `domain`: the site domain
     *  - `corePlugins`: `[ "bloggify-plugin-manager", "bloggify-router" ]`
     *  - `server`: The lien settings.
     *  - `theme`:
     *       - `path`: the theme path (default: `"theme"`)
     *       - `public`: the theme public directory (default: `"public"`)
     *  - `adapter`: Adapter settings
     *       - `paths`:
     *              - `articles`: the path to the articles dir (default: `"content/articles"`)
     *              - `pages`: the path to the pages dir (default: `"content/pages"`)
     *
     * @param {BloggifyAdapter} adapter A custom content adapter.
     * @returns {Bloggify} The `Bloggify` instance.
     */
    constructor (options, adapter) {

        options = options || process.cwd();

        if (typeof options === "string") {
            options = { root: options };
        }

        options = ul.deepMerge(options, defaultOptions);

        // The plugin manager will take care of loading the plugins
        // ...but we don't force it
        if (!options.loadPlugins) {
            options.loadPlugins = false;
        }

        super(options);
        options = this.options;

        mapO(options.adapter.paths, value => `${this.paths.root}/${value}`);

        // Normalize the adapter paths
        options.Adapter = options.Adapter || adapter || require("bloggify-markdown-adapter");
        options.viewer = options.viewer || require("bloggify-theme-renderer");

        this.adapter = new options.Adapter(this);
        this.viewer = new options.viewer(this);

        this.viewer.render = this.viewer.render || (lien => {
            lien.end("The renderer plugin should override `bloggify.viewer.render` to be able to render the templates.", 500);
        });

        if (!this.options.metadata.domain) {
            this.log("Warning: Please provide your website domain in metadata.", "warn");
        }

        // Load the core plugins
        this.log("Loading the core plugins", "info");
        this.loadPlugins(options.corePlugins, (err, data) => {
            if (err) {
                this.log("Failed to initialize the plugins.", "error");
                this.log(err, "error");
            } else {
                this.log("Initialized plugins.");
            }
        });
    }

    /**
     * render
     * Renders a template.
     *
     * @name render
     * @function
     * @param {Lien} lien The `lien` object.
     * @param {String} templateName The template name or path.
     * @param {Object} data The template data.
     */
    render (lien, templateName, data) {

        let render500 = true;
        data = data || {};

        let renderTempl = () => {
            data = ul.merge(data, {
                Bloggify: this
              , lien: lien
              , template: templateName
              , siteMetadata: this.options.metadata || {}
              , page: data.page || {}
              , _public: (filePath, absolute) => this.paths.staticFilePath(filePath, absolute)
            });

            if (data.error) {
                data.error.statusCode = data.error.statusCode || (
                    /not found|enoent/i.test(data.error.message) ? 404 : 500
                );
                if (!data.forceTemplateName) {
                    templateName = data.error.statusCode.toString();
                }
            }

            data.title = data.title || data.page.title || templateName;

            return this.viewer.getTemplatePath(templateName, (err, path) => {

                if (err) {
                    if (render500) {
                        data.error = err;
                        err.statusCode = 500;
                        renderTempl(lien, "500", data);
                    } else {
                        return lien.end("Failed to get the template.", 500);
                    }
                    render500 = false;
                    return this.log(err);
                }

                this.viewer.render(lien, path, data, err => {
                    if (err) {
                        if (render500) {
                            data.error = err;
                            err.statusCode = 500;
                            renderTempl(lien, templateName, data);
                        } else {
                            lien.end("An error occured when rendering the page.", 500);
                        }
                        render500 = false;
                        return this.log(err);
                    }
                });
            });
        };

        return renderTempl();
    }

    /**
     * registerRouter
     * Registers a new router.
     *
     * @name registerRouter
     * @function
     * @param {BloggifyRouter} router The Bloggify router to register.
     */
    registerRouter (router) {
        if (this.router) {
            throw new Error("There is already a registered router.");
        }
        this.router = router;
    }

    // Article-related functions
    /**
     * getArticleById
     * Gets a specific article, by id.
     *
     * @name getArticleById
     * @function
     * @param {String} id The article id.
     * @param {Function} cb The callback function.
     */
    getArticleById (id, cb) {
        return this.adapter.getArticleById(id, cb);
    }

    /**
     * getArticles
     * Get multiple articles.
     *
     * @name getArticles
     * @function
     * @param {Object} query The query.
     * @param {Function} cb The callback function.
     */
    getArticles (query, cb) {
        return this.adapter.getArticles(query, cb);
    }

    /**
     * createArticle
     * Create a new article.
     *
     * @name createArticle
     * @function
     * @param {String} title The article title.
     * @param {String} content The article content.
     * @param {Object} custom Custom data.
     * @param {Function} cb The callback function.
     */
    createArticle (title, content, custom, cb) {
        return this.adapter.createArticle(title, content, custom, cb);
    }

    /**
     * saveArticle
     * Saves an existing article.
     *
     * @name saveArticle
     * @function
     * @param {String} id The article id.
     * @param {String} title The article title.
     * @param {String} content The article content.
     * @param {Object} custom Custom data.
     * @param {Function} cb The callback function.
     */
    saveArticle (id, title, content, custom, cb) {
        return this.adapter.saveArticle(id, title, content, custom, cb);
    }

    /**
     * deleteArticle
     * Delete an article.
     *
     * @name deleteArticle
     * @function
     * @param {String} id The article id.
     * @param {Function} cb The callback function.
     */
    deleteArticle (id, cb) {
        return this.adapter.deleteArticle(id, cb);
    }

    /**
     * deleteArticles
     * Delete multiple articles.
     *
     * @name deleteArticles
     * @function
     * @param {Array} ids A list of ids.
     * @param {Function} cb The callback function.
     */
    deleteArticles (ids, cb) {
        return this.adapter.deleteArticles(ids, cb);
    }

    // Pages-related functions
    /**
     * getPageBySlug
     * Get a page by the slug.
     *
     * @name getPageBySlug
     * @function
     * @param {String} slug The page slug.
     * @param {Function} cb The callback function.
     */
    getPageBySlug (slug, cb) {
        return this.adapter.getPageBySlug(slug, cb);
    }

    /**
     * getPages
     * Get multiple pages.
     *
     * @name getPages
     * @function
     * @param {Object} query The query object.
     * @param {Function} cb The callback function.
     */
    getPages (query, cb) {
        return this.adapter.getPages(query, cb);
    }

    /**
     * createPage
     * Create a new page.
     *
     * @name createPage
     * @function
     * @param {String} title The article title.
     * @param {String} content The article content.
     * @param {Object} custom Custom data.
     * @param {Function} cb The callback function.
     */
    createPage (title, content, custom, cb) {
        return this.adapter.createPage(title, content, custom, cb);
    }

    /**
     * savePage
     * Saves a page.
     *
     * @name savePage
     * @function
     * @param {String} title The article title.
     * @param {String} content The article content.
     * @param {Object} custom Custom data.
     * @param {Function} cb The callback function.
     */
    savePage (slug, title, content, custom, cb) {
        return this.adapter.savePage(slug, title, content, custom, cb);
    }

    /**
     * deletePage
     * Delete a page.
     *
     * @name deletePage
     * @function
     * @param {String} slug The page slug.
     * @param {Function} cb The callback function.
     */
    deletePage (slug, cb) {
        return this.adapter.deletePage(slug, cb);
    }

    /**
     * deletePages
     * Delete multiple pages.
     *
     * @name deletePages
     * @function
     * @param {Array} slugs An array of slugs.
     * @param {Function} cb The callback function.
     */
    deletePages (slugs, cb) {
        return this.adapter.deletePages(slugs, cb);
    }
};
<commit_msg>Use uppercase letter<commit_after>"use strict";

const BloggifyServer = require("bloggify-server")
    , defaultOptions = require("./options")
    , ul = require("ul")
    , mapO = require("map-o")
    ;

module.exports = class Bloggify extends BloggifyServer {

    /**
     * Bloggify
     * Creates a new instance of `Bloggify`.
     *
     * @name Bloggify
     * @function
     * @param {Object} options An object containing the following fields:
     *
     *  - `loadPlugins`: false
     *  - `plugins`: plugins to load
     *  - `pluginConfigs`: plugin configs
     *  - `metadata`:
     *       - `siteTitle`: the site title
     *       - `siteDescription`: the site title
     *       - `domain`: the site domain
     *  - `corePlugins`: `[ "bloggify-plugin-manager", "bloggify-router" ]`
     *  - `server`: The lien settings.
     *  - `theme`:
     *       - `path`: the theme path (default: `"theme"`)
     *       - `public`: the theme public directory (default: `"public"`)
     *  - `adapter`: Adapter settings
     *       - `paths`:
     *              - `articles`: the path to the articles dir (default: `"content/articles"`)
     *              - `pages`: the path to the pages dir (default: `"content/pages"`)
     *
     * @param {BloggifyAdapter} adapter A custom content adapter.
     * @returns {Bloggify} The `Bloggify` instance.
     */
    constructor (options, adapter) {

        options = options || process.cwd();

        if (typeof options === "string") {
            options = { root: options };
        }

        options = ul.deepMerge(options, defaultOptions);

        // The plugin manager will take care of loading the plugins
        // ...but we don't force it
        if (!options.loadPlugins) {
            options.loadPlugins = false;
        }

        super(options);
        options = this.options;

        mapO(options.adapter.paths, value => `${this.paths.root}/${value}`);

        // Normalize the adapter paths
        options.Adapter = options.Adapter || adapter || require("bloggify-markdown-adapter");
        options.Viewer = options.viewer || require("bloggify-theme-renderer");

        this.adapter = new options.Adapter(this);
        this.viewer = new options.Viewer(this);

        this.viewer.render = this.viewer.render || (lien => {
            lien.end("The renderer plugin should override `bloggify.viewer.render` to be able to render the templates.", 500);
        });

        if (!this.options.metadata.domain) {
            this.log("Warning: Please provide your website domain in metadata.", "warn");
        }

        // Load the core plugins
        this.log("Loading the core plugins", "info");
        this.loadPlugins(options.corePlugins, (err, data) => {
            if (err) {
                this.log("Failed to initialize the plugins.", "error");
                this.log(err, "error");
            } else {
                this.log("Initialized plugins.");
            }
        });
    }

    /**
     * render
     * Renders a template.
     *
     * @name render
     * @function
     * @param {Lien} lien The `lien` object.
     * @param {String} templateName The template name or path.
     * @param {Object} data The template data.
     */
    render (lien, templateName, data) {

        let render500 = true;
        data = data || {};

        let renderTempl = () => {
            data = ul.merge(data, {
                Bloggify: this
              , lien: lien
              , template: templateName
              , siteMetadata: this.options.metadata || {}
              , page: data.page || {}
              , _public: (filePath, absolute) => this.paths.staticFilePath(filePath, absolute)
            });

            if (data.error) {
                data.error.statusCode = data.error.statusCode || (
                    /not found|enoent/i.test(data.error.message) ? 404 : 500
                );
                if (!data.forceTemplateName) {
                    templateName = data.error.statusCode.toString();
                }
            }

            data.title = data.title || data.page.title || templateName;

            return this.viewer.getTemplatePath(templateName, (err, path) => {

                if (err) {
                    if (render500) {
                        data.error = err;
                        err.statusCode = 500;
                        renderTempl(lien, "500", data);
                    } else {
                        return lien.end("Failed to get the template.", 500);
                    }
                    render500 = false;
                    return this.log(err);
                }

                this.viewer.render(lien, path, data, err => {
                    if (err) {
                        if (render500) {
                            data.error = err;
                            err.statusCode = 500;
                            renderTempl(lien, templateName, data);
                        } else {
                            lien.end("An error occured when rendering the page.", 500);
                        }
                        render500 = false;
                        return this.log(err);
                    }
                });
            });
        };

        return renderTempl();
    }

    /**
     * registerRouter
     * Registers a new router.
     *
     * @name registerRouter
     * @function
     * @param {BloggifyRouter} router The Bloggify router to register.
     */
    registerRouter (router) {
        if (this.router) {
            throw new Error("There is already a registered router.");
        }
        this.router = router;
    }

    // Article-related functions
    /**
     * getArticleById
     * Gets a specific article, by id.
     *
     * @name getArticleById
     * @function
     * @param {String} id The article id.
     * @param {Function} cb The callback function.
     */
    getArticleById (id, cb) {
        return this.adapter.getArticleById(id, cb);
    }

    /**
     * getArticles
     * Get multiple articles.
     *
     * @name getArticles
     * @function
     * @param {Object} query The query.
     * @param {Function} cb The callback function.
     */
    getArticles (query, cb) {
        return this.adapter.getArticles(query, cb);
    }

    /**
     * createArticle
     * Create a new article.
     *
     * @name createArticle
     * @function
     * @param {String} title The article title.
     * @param {String} content The article content.
     * @param {Object} custom Custom data.
     * @param {Function} cb The callback function.
     */
    createArticle (title, content, custom, cb) {
        return this.adapter.createArticle(title, content, custom, cb);
    }

    /**
     * saveArticle
     * Saves an existing article.
     *
     * @name saveArticle
     * @function
     * @param {String} id The article id.
     * @param {String} title The article title.
     * @param {String} content The article content.
     * @param {Object} custom Custom data.
     * @param {Function} cb The callback function.
     */
    saveArticle (id, title, content, custom, cb) {
        return this.adapter.saveArticle(id, title, content, custom, cb);
    }

    /**
     * deleteArticle
     * Delete an article.
     *
     * @name deleteArticle
     * @function
     * @param {String} id The article id.
     * @param {Function} cb The callback function.
     */
    deleteArticle (id, cb) {
        return this.adapter.deleteArticle(id, cb);
    }

    /**
     * deleteArticles
     * Delete multiple articles.
     *
     * @name deleteArticles
     * @function
     * @param {Array} ids A list of ids.
     * @param {Function} cb The callback function.
     */
    deleteArticles (ids, cb) {
        return this.adapter.deleteArticles(ids, cb);
    }

    // Pages-related functions
    /**
     * getPageBySlug
     * Get a page by the slug.
     *
     * @name getPageBySlug
     * @function
     * @param {String} slug The page slug.
     * @param {Function} cb The callback function.
     */
    getPageBySlug (slug, cb) {
        return this.adapter.getPageBySlug(slug, cb);
    }

    /**
     * getPages
     * Get multiple pages.
     *
     * @name getPages
     * @function
     * @param {Object} query The query object.
     * @param {Function} cb The callback function.
     */
    getPages (query, cb) {
        return this.adapter.getPages(query, cb);
    }

    /**
     * createPage
     * Create a new page.
     *
     * @name createPage
     * @function
     * @param {String} title The article title.
     * @param {String} content The article content.
     * @param {Object} custom Custom data.
     * @param {Function} cb The callback function.
     */
    createPage (title, content, custom, cb) {
        return this.adapter.createPage(title, content, custom, cb);
    }

    /**
     * savePage
     * Saves a page.
     *
     * @name savePage
     * @function
     * @param {String} title The article title.
     * @param {String} content The article content.
     * @param {Object} custom Custom data.
     * @param {Function} cb The callback function.
     */
    savePage (slug, title, content, custom, cb) {
        return this.adapter.savePage(slug, title, content, custom, cb);
    }

    /**
     * deletePage
     * Delete a page.
     *
     * @name deletePage
     * @function
     * @param {String} slug The page slug.
     * @param {Function} cb The callback function.
     */
    deletePage (slug, cb) {
        return this.adapter.deletePage(slug, cb);
    }

    /**
     * deletePages
     * Delete multiple pages.
     *
     * @name deletePages
     * @function
     * @param {Array} slugs An array of slugs.
     * @param {Function} cb The callback function.
     */
    deletePages (slugs, cb) {
        return this.adapter.deletePages(slugs, cb);
    }
};
<|endoftext|>


<commit_before>'use strict';
/* global JSON */
/* eslint no-restricted-globals: [0] */

/**
 * Module dependencies.
 */

var integration = require('@segment/analytics.js-integration');
var each = require('component-each');
var is = require('is');
var extend = require('@ndhoule/extend');

/**
 * Expose `Heap` integration.
 */

var Heap = module.exports = integration('Heap')
  .global('heap')
  .option('appId', '')
  .tag('<script src="//cdn.heapanalytics.com/js/heap-{{ appId }}.js">');

/**
 * Initialize.
 *
 * https://heapanalytics.com/docs/installation#web
 *
 * @api public
 */

Heap.prototype.initialize = function() {
  window.heap = window.heap || [];
  window.heap.load = function(appid, config) {
    window.heap.appid = appid;
    window.heap.config = config;

    var methodFactory = function(type) {
      return function() {
        window.heap.push([type].concat(Array.prototype.slice.call(arguments, 0)));
      };
    };

    var heapMethods = ['addEventProperties', 'addUserProperties', 'clearEventProperties', 'identify', 'removeEventProperty', 'setEventProperties', 'track', 'unsetEventProperty'];
    each(heapMethods, function(method) {
      window.heap[method] = methodFactory(method);
    });
  };

  window.heap.load(this.options.appId);
  this.load(this.ready);
};

/**
 * Loaded?
 *
 * @api private
 * @return {boolean}
 */

Heap.prototype.loaded = function() {
  return !!(window.heap && window.heap.appid);
};

/**
 * Identify.
 *
 * https://heapanalytics.com/docs#identify
 *
 * @api public
 * @param {Identify} identify
 */

Heap.prototype.identify = function(identify) {
  var traits = identify.traits({ email: '_email' });
  var id = identify.userId();
  if (id) window.heap.identify(id);
  window.heap.addUserProperties(clean(traits));
};

/**
 * Track.
 *
 * https://heapanalytics.com/docs#track
 *
 * @api public
 * @param {Track} track
 */

Heap.prototype.track = function(track) {
  window.heap.track(track.event(), clean(track.properties()));
};

/**
 * Clean all nested objects and arrays.
 *
 * @param {Object} obj
 * @return {Object}
 * @api private
 */

function clean(obj) {
  var ret = {};

  for (var k in obj) {
    if (obj.hasOwnProperty(k)) {
      var value = obj[k];
      if (value === null) continue;

      // date
      if (is.date(value)) {
        ret[k] = value.toISOString();
        continue;
      }

      // leave boolean as is
      if (is.bool(value)) {
        ret[k] = value;
        continue;
      }

      // leave  numbers as is
      if (is.number(value)) {
        ret[k] = value;
        continue;
      }

      // non objects
      if (value.toString() !== '[object Object]') {
        ret[k] = value.toString();
        continue;
      }

      // json
      // must flatten including the name of the original trait/property
      var nestedObj = {};
      nestedObj[k] = value;
      var flattenedObj = flatten(nestedObj, { safe: true });

      // stringify arrays inside nested object to be consistent with top level behavior of arrays
      for (var key in flattenedObj) {
        if (is.array(flattenedObj[key])) flattenedObj[key] = JSON.stringify(flattenedObj[key]);
      }

      ret = extend(ret, flattenedObj);
      delete ret[k];
    }
  }

  return ret;
}

/**
 * Flatten nested objects
 * taken from https://www.npmjs.com/package/flat
 * @param {Object} obj
 * @return {Object} obj
 * @api public
 */

function flatten(target, opts) {
  opts = opts || {};

  var delimiter = opts.delimiter || '.';
  var maxDepth = opts.maxDepth;
  var currentDepth = 1;
  var output = {};

  function step(object, prev) {
    Object.keys(object).forEach(function(key) {
      var value = object[key];
      var isarray = opts.safe && Array.isArray(value);
      var type = Object.prototype.toString.call(value);
      var isobject = type === '[object Object]' || type === '[object Array]';

      var newKey = prev
        ? prev + delimiter + key
        : key;

      if (!opts.maxDepth) {
        maxDepth = currentDepth + 1;
      }

      if (!isarray && isobject && Object.keys(value).length && currentDepth < maxDepth) {
        ++currentDepth;
        return step(value, newKey);
      }

      output[newKey] = value;
    });
  }

  step(target);

  return output;
}

/**
 * Polyfill Object.keys
 * // From https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/keys
 * Note: Had to do this because for some reason, the above will not work properly without using Object.keys
 */

if (!Object.keys) {
  Object.keys = function(o) {
    if (o !== Object(o)) {
      throw new TypeError('Object.keys called on a non-object');
    }
    var k = [];
    var p;
    for (p in o) if (Object.prototype.hasOwnProperty.call(o, p)) k.push(p);
    return k;
  };
}
<commit_msg>skip undefined<commit_after>'use strict';
/* global JSON */
/* eslint no-restricted-globals: [0] */

/**
 * Module dependencies.
 */

var integration = require('@segment/analytics.js-integration');
var each = require('component-each');
var is = require('is');
var extend = require('@ndhoule/extend');

/**
 * Expose `Heap` integration.
 */

var Heap = module.exports = integration('Heap')
  .global('heap')
  .option('appId', '')
  .tag('<script src="//cdn.heapanalytics.com/js/heap-{{ appId }}.js">');

/**
 * Initialize.
 *
 * https://heapanalytics.com/docs/installation#web
 *
 * @api public
 */

Heap.prototype.initialize = function() {
  window.heap = window.heap || [];
  window.heap.load = function(appid, config) {
    window.heap.appid = appid;
    window.heap.config = config;

    var methodFactory = function(type) {
      return function() {
        window.heap.push([type].concat(Array.prototype.slice.call(arguments, 0)));
      };
    };

    var heapMethods = ['addEventProperties', 'addUserProperties', 'clearEventProperties', 'identify', 'removeEventProperty', 'setEventProperties', 'track', 'unsetEventProperty'];
    each(heapMethods, function(method) {
      window.heap[method] = methodFactory(method);
    });
  };

  window.heap.load(this.options.appId);
  this.load(this.ready);
};

/**
 * Loaded?
 *
 * @api private
 * @return {boolean}
 */

Heap.prototype.loaded = function() {
  return !!(window.heap && window.heap.appid);
};

/**
 * Identify.
 *
 * https://heapanalytics.com/docs#identify
 *
 * @api public
 * @param {Identify} identify
 */

Heap.prototype.identify = function(identify) {
  var traits = identify.traits({ email: '_email' });
  var id = identify.userId();
  if (id) window.heap.identify(id);
  window.heap.addUserProperties(clean(traits));
};

/**
 * Track.
 *
 * https://heapanalytics.com/docs#track
 *
 * @api public
 * @param {Track} track
 */

Heap.prototype.track = function(track) {
  window.heap.track(track.event(), clean(track.properties()));
};

/**
 * Clean all nested objects and arrays.
 *
 * @param {Object} obj
 * @return {Object}
 * @api private
 */

function clean(obj) {
  var ret = {};

  for (var k in obj) {
    if (obj.hasOwnProperty(k)) {
      var value = obj[k];
      if (value === null || value === undefined) continue;

      // date
      if (is.date(value)) {
        ret[k] = value.toISOString();
        continue;
      }

      // leave boolean as is
      if (is.bool(value)) {
        ret[k] = value;
        continue;
      }

      // leave  numbers as is
      if (is.number(value)) {
        ret[k] = value;
        continue;
      }

      // non objects
      if (value.toString() !== '[object Object]') {
        ret[k] = value.toString();
        continue;
      }

      // json
      // must flatten including the name of the original trait/property
      var nestedObj = {};
      nestedObj[k] = value;
      var flattenedObj = flatten(nestedObj, { safe: true });

      // stringify arrays inside nested object to be consistent with top level behavior of arrays
      for (var key in flattenedObj) {
        if (is.array(flattenedObj[key])) flattenedObj[key] = JSON.stringify(flattenedObj[key]);
      }

      ret = extend(ret, flattenedObj);
      delete ret[k];
    }
  }

  return ret;
}

/**
 * Flatten nested objects
 * taken from https://www.npmjs.com/package/flat
 * @param {Object} obj
 * @return {Object} obj
 * @api public
 */

function flatten(target, opts) {
  opts = opts || {};

  var delimiter = opts.delimiter || '.';
  var maxDepth = opts.maxDepth;
  var currentDepth = 1;
  var output = {};

  function step(object, prev) {
    Object.keys(object).forEach(function(key) {
      var value = object[key];
      var isarray = opts.safe && Array.isArray(value);
      var type = Object.prototype.toString.call(value);
      var isobject = type === '[object Object]' || type === '[object Array]';

      var newKey = prev
        ? prev + delimiter + key
        : key;

      if (!opts.maxDepth) {
        maxDepth = currentDepth + 1;
      }

      if (!isarray && isobject && Object.keys(value).length && currentDepth < maxDepth) {
        ++currentDepth;
        return step(value, newKey);
      }

      output[newKey] = value;
    });
  }

  step(target);

  return output;
}

/**
 * Polyfill Object.keys
 * // From https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/keys
 * Note: Had to do this because for some reason, the above will not work properly without using Object.keys
 */

if (!Object.keys) {
  Object.keys = function(o) {
    if (o !== Object(o)) {
      throw new TypeError('Object.keys called on a non-object');
    }
    var k = [];
    var p;
    for (p in o) if (Object.prototype.hasOwnProperty.call(o, p)) k.push(p);
    return k;
  };
}
<|endoftext|>