<commit_before>const OFI = 'bfred-it:object-fit-images';
const propRegex = /(object-fit|object-position)\s*:\s*([-\w\s%]+)/g;
const testImg = new Image();
const supportsObjectFit = 'object-fit' in testImg.style;
const supportsObjectPosition = 'object-position' in testImg.style;
const supportsOFI = 'background-size' in testImg.style;
const supportsCurrentSrc = typeof testImg.currentSrc === 'string';
const nativeGetAttribute = testImg.getAttribute;
const nativeSetAttribute = testImg.setAttribute;
let autoModeEnabled = false;

function createPlaceholder(w, h) {
	return `data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='${w}' height='${h}'%3E%3C/svg%3E`;
}

function polyfillCurrentSrc(el) {
	if (el.srcset && !supportsCurrentSrc && window.picturefill) {
		const pf = window.picturefill._;
		// parse srcset with picturefill where currentSrc isn't available
		if (!el[pf.ns] || !el[pf.ns].evaled) {
			// force synchronous srcset parsing
			pf.fillImg(el, {reselect: true});
		}

		if (!el[pf.ns].curSrc) {
			// force picturefill to parse srcset
			el[pf.ns].supported = false;
			pf.fillImg(el, {reselect: true});
		}

		// retrieve parsed currentSrc, if any
		el.currentSrc = el[pf.ns].curSrc || el.src;
	}
}

function getStyle(el) {
	const style = getComputedStyle(el).fontFamily;
	let parsed;
	const props = {};
	while ((parsed = propRegex.exec(style)) !== null) {
		props[parsed[1]] = parsed[2];
	}
	return props;
}

function setPlaceholder(img, width, height) {
	// Default: fill width, no height
	const placeholder = createPlaceholder(width || 1, height || 0);

	// Only set placeholder if it's different
	if (nativeGetAttribute.call(img, 'src') !== placeholder) {
		nativeSetAttribute.call(img, 'src', placeholder);
	}
}

function onImageReady(img, callback) {
	// naturalWidth is only available when the image headers are loaded,
	// this loop will poll it every 100ms.
	if (img.naturalWidth) {
		callback(img);
	} else {
		setTimeout(onImageReady, 100, img, callback);
	}
}

function fixOne(el) {
	const style = getStyle(el);
	const ofi = el[OFI];
	style['object-fit'] = style['object-fit'] || 'fill'; // default value

	// Avoid running where unnecessary, unless OFI had already done its deed
	if (!ofi.img) {
		// fill is the default behavior so no action is necessary
		if (style['object-fit'] === 'fill') {
			return;
		}

		// Where object-fit is supported and object-position isn't (Safari < 10)
		if (
			!ofi.skipTest && // unless user wants to apply regardless of browser support
			supportsObjectFit && // if browser already supports object-fit
			!style['object-position'] // unless object-position is used
		) {
			return;
		}
	}

	// keep a clone in memory while resetting the original to a blank
	if (!ofi.img) {
		ofi.img = new Image(el.width, el.height);
		ofi.img.srcset = nativeGetAttribute.call(el, `data-ofi-srcset`) || el.srcset;
		ofi.img.src = nativeGetAttribute.call(el, `data-ofi-src`) || el.src;

		// preserve for any future cloneNode calls
		// https://github.com/bfred-it/object-fit-images/issues/53
		nativeSetAttribute.call(el, `data-ofi-src`, el.src);
		nativeSetAttribute.call(el, `data-ofi-srcset`, el.srcset);

		setPlaceholder(el, el.naturalWidth || el.width, el.naturalHeight || el.height);

		// remove srcset because it overrides src
		if (el.srcset) {
			el.srcset = '';
		}
		try {
			keepSrcUsable(el);
		} catch (err) {
			if (window.console) {
				console.log('http://bit.ly/ofi-old-browser');
			}
		}
	}

	polyfillCurrentSrc(ofi.img);

	el.style.backgroundImage = `url(${(ofi.img.currentSrc || ofi.img.src).replace('(', '%28').replace(')', '%29')})`;
	el.style.backgroundPosition = style['object-position'] || 'center';
	el.style.backgroundRepeat = 'no-repeat';

	if (/scale-down/.test(style['object-fit'])) {
		onImageReady(ofi.img, () => {
			if (ofi.img.naturalWidth > el.width || ofi.img.naturalHeight > el.height) {
				el.style.backgroundSize = 'contain';
			} else {
				el.style.backgroundSize = 'auto';
			}
		});
	} else {
		el.style.backgroundSize = style['object-fit'].replace('none', 'auto').replace('fill', '100% 100%');
	}

	onImageReady(ofi.img, img => {
		setPlaceholder(el, img.naturalWidth, img.naturalHeight);
	});
}

function keepSrcUsable(el) {
	const descriptors = {
		get(prop) {
			return el[OFI].img[prop ? prop : 'src'];
		},
		set(value, prop) {
			el[OFI].img[prop ? prop : 'src'] = value;
			nativeSetAttribute.call(el, `data-ofi-${prop}`, value); // preserve for any future cloneNode
			fixOne(el);
			return value;
		}
	};
	Object.defineProperty(el, 'src', descriptors);
	Object.defineProperty(el, 'currentSrc', {
		get: () => descriptors.get('currentSrc')
	});
	Object.defineProperty(el, 'srcset', {
		get: () => descriptors.get('srcset'),
		set: ss => descriptors.set(ss, 'srcset')
	});
}

function hijackAttributes() {
	function getOfiImageMaybe(el, name) {
		return el[OFI] && el[OFI].img && (name === 'src' || name === 'srcset') ? el[OFI].img : el;
	}
	if (!supportsObjectPosition) {
		HTMLImageElement.prototype.getAttribute = function (name) {
			return nativeGetAttribute.call(getOfiImageMaybe(this, name), name);
		};

		HTMLImageElement.prototype.setAttribute = function (name, value) {
			return nativeSetAttribute.call(getOfiImageMaybe(this, name), name, String(value));
		};
	}
}

export default function fix(imgs, opts) {
	const startAutoMode = !autoModeEnabled && !imgs;
	opts = opts || {};
	imgs = imgs || 'img';
	if ((supportsObjectPosition && !opts.skipTest) || !supportsOFI) {
		return false;
	}

	// use imgs as a selector or just select all images
	if (typeof imgs === 'string') {
		imgs = document.querySelectorAll(imgs);
	} else if (!('length' in imgs)) {
		imgs = [imgs];
	}

	// apply fix to all
	for (let i = 0; i < imgs.length; i++) {
		imgs[i][OFI] = imgs[i][OFI] || {
			skipTest: opts.skipTest
		};
		fixOne(imgs[i]);
	}

	if (startAutoMode) {
		document.body.addEventListener('load', e => {
			if (e.target.tagName === 'IMG') {
				fix(e.target, {
					skipTest: opts.skipTest
				});
			}
		}, true);
		autoModeEnabled = true;
		imgs = 'img'; // reset to a generic selector for watchMQ
	}

	// if requested, watch media queries for object-fit change
	if (opts.watchMQ) {
		window.addEventListener('resize', fix.bind(null, imgs, {
			skipTest: opts.skipTest
		}));
	}
}

fix.supportsObjectFit = supportsObjectFit;
fix.supportsObjectPosition = supportsObjectPosition;

hijackAttributes();
<commit_msg>Don't save the srcset into the attributes if it was never defined<commit_after>const OFI = 'bfred-it:object-fit-images';
const propRegex = /(object-fit|object-position)\s*:\s*([-\w\s%]+)/g;
const testImg = new Image();
const supportsObjectFit = 'object-fit' in testImg.style;
const supportsObjectPosition = 'object-position' in testImg.style;
const supportsOFI = 'background-size' in testImg.style;
const supportsCurrentSrc = typeof testImg.currentSrc === 'string';
const nativeGetAttribute = testImg.getAttribute;
const nativeSetAttribute = testImg.setAttribute;
let autoModeEnabled = false;

function createPlaceholder(w, h) {
	return `data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='${w}' height='${h}'%3E%3C/svg%3E`;
}

function polyfillCurrentSrc(el) {
	if (el.srcset && !supportsCurrentSrc && window.picturefill) {
		const pf = window.picturefill._;
		// parse srcset with picturefill where currentSrc isn't available
		if (!el[pf.ns] || !el[pf.ns].evaled) {
			// force synchronous srcset parsing
			pf.fillImg(el, {reselect: true});
		}

		if (!el[pf.ns].curSrc) {
			// force picturefill to parse srcset
			el[pf.ns].supported = false;
			pf.fillImg(el, {reselect: true});
		}

		// retrieve parsed currentSrc, if any
		el.currentSrc = el[pf.ns].curSrc || el.src;
	}
}

function getStyle(el) {
	const style = getComputedStyle(el).fontFamily;
	let parsed;
	const props = {};
	while ((parsed = propRegex.exec(style)) !== null) {
		props[parsed[1]] = parsed[2];
	}
	return props;
}

function setPlaceholder(img, width, height) {
	// Default: fill width, no height
	const placeholder = createPlaceholder(width || 1, height || 0);

	// Only set placeholder if it's different
	if (nativeGetAttribute.call(img, 'src') !== placeholder) {
		nativeSetAttribute.call(img, 'src', placeholder);
	}
}

function onImageReady(img, callback) {
	// naturalWidth is only available when the image headers are loaded,
	// this loop will poll it every 100ms.
	if (img.naturalWidth) {
		callback(img);
	} else {
		setTimeout(onImageReady, 100, img, callback);
	}
}

function fixOne(el) {
	const style = getStyle(el);
	const ofi = el[OFI];
	style['object-fit'] = style['object-fit'] || 'fill'; // default value

	// Avoid running where unnecessary, unless OFI had already done its deed
	if (!ofi.img) {
		// fill is the default behavior so no action is necessary
		if (style['object-fit'] === 'fill') {
			return;
		}

		// Where object-fit is supported and object-position isn't (Safari < 10)
		if (
			!ofi.skipTest && // unless user wants to apply regardless of browser support
			supportsObjectFit && // if browser already supports object-fit
			!style['object-position'] // unless object-position is used
		) {
			return;
		}
	}

	// keep a clone in memory while resetting the original to a blank
	if (!ofi.img) {
		ofi.img = new Image(el.width, el.height);
		ofi.img.srcset = nativeGetAttribute.call(el, `data-ofi-srcset`) || el.srcset;
		ofi.img.src = nativeGetAttribute.call(el, `data-ofi-src`) || el.src;

		// preserve for any future cloneNode calls
		// https://github.com/bfred-it/object-fit-images/issues/53
		nativeSetAttribute.call(el, `data-ofi-src`, el.src);
		if (el.srcset) {
			nativeSetAttribute.call(el, `data-ofi-srcset`, el.srcset);
		}

		setPlaceholder(el, el.naturalWidth || el.width, el.naturalHeight || el.height);

		// remove srcset because it overrides src
		if (el.srcset) {
			el.srcset = '';
		}
		try {
			keepSrcUsable(el);
		} catch (err) {
			if (window.console) {
				console.log('http://bit.ly/ofi-old-browser');
			}
		}
	}

	polyfillCurrentSrc(ofi.img);

	el.style.backgroundImage = `url(${(ofi.img.currentSrc || ofi.img.src).replace('(', '%28').replace(')', '%29')})`;
	el.style.backgroundPosition = style['object-position'] || 'center';
	el.style.backgroundRepeat = 'no-repeat';

	if (/scale-down/.test(style['object-fit'])) {
		onImageReady(ofi.img, () => {
			if (ofi.img.naturalWidth > el.width || ofi.img.naturalHeight > el.height) {
				el.style.backgroundSize = 'contain';
			} else {
				el.style.backgroundSize = 'auto';
			}
		});
	} else {
		el.style.backgroundSize = style['object-fit'].replace('none', 'auto').replace('fill', '100% 100%');
	}

	onImageReady(ofi.img, img => {
		setPlaceholder(el, img.naturalWidth, img.naturalHeight);
	});
}

function keepSrcUsable(el) {
	const descriptors = {
		get(prop) {
			return el[OFI].img[prop ? prop : 'src'];
		},
		set(value, prop) {
			el[OFI].img[prop ? prop : 'src'] = value;
			nativeSetAttribute.call(el, `data-ofi-${prop}`, value); // preserve for any future cloneNode
			fixOne(el);
			return value;
		}
	};
	Object.defineProperty(el, 'src', descriptors);
	Object.defineProperty(el, 'currentSrc', {
		get: () => descriptors.get('currentSrc')
	});
	Object.defineProperty(el, 'srcset', {
		get: () => descriptors.get('srcset'),
		set: ss => descriptors.set(ss, 'srcset')
	});
}

function hijackAttributes() {
	function getOfiImageMaybe(el, name) {
		return el[OFI] && el[OFI].img && (name === 'src' || name === 'srcset') ? el[OFI].img : el;
	}
	if (!supportsObjectPosition) {
		HTMLImageElement.prototype.getAttribute = function (name) {
			return nativeGetAttribute.call(getOfiImageMaybe(this, name), name);
		};

		HTMLImageElement.prototype.setAttribute = function (name, value) {
			return nativeSetAttribute.call(getOfiImageMaybe(this, name), name, String(value));
		};
	}
}

export default function fix(imgs, opts) {
	const startAutoMode = !autoModeEnabled && !imgs;
	opts = opts || {};
	imgs = imgs || 'img';

	if ((supportsObjectPosition && !opts.skipTest) || !supportsOFI) {
		return false;
	}

	// use imgs as a selector or just select all images
	if (typeof imgs === 'string') {
		imgs = document.querySelectorAll(imgs);
	} else if (!('length' in imgs)) {
		imgs = [imgs];
	}

	// apply fix to all
	for (let i = 0; i < imgs.length; i++) {
		imgs[i][OFI] = imgs[i][OFI] || {
			skipTest: opts.skipTest
		};
		fixOne(imgs[i]);
	}

	if (startAutoMode) {
		document.body.addEventListener('load', e => {
			if (e.target.tagName === 'IMG') {
				fix(e.target, {
					skipTest: opts.skipTest
				});
			}
		}, true);
		autoModeEnabled = true;
		imgs = 'img'; // reset to a generic selector for watchMQ
	}

	// if requested, watch media queries for object-fit change
	if (opts.watchMQ) {
		window.addEventListener('resize', fix.bind(null, imgs, {
			skipTest: opts.skipTest
		}));
	}
}

fix.supportsObjectFit = supportsObjectFit;
fix.supportsObjectPosition = supportsObjectPosition;

hijackAttributes();
<|endoftext|>


<commit_before>var request = require('request');
var config = require('config');

// Load configuration
confEvent = config.get('event');
confINat = config.get('iNaturalist');

// URL for project observations
var projectObsURL = confINat.api + 'observations/project/' + confEvent.slug + '.json';

// Callback function for project observations
function observation_callback(error, response, body) {
    if (!error && response.statusCode == 200) {
        var project_observations = JSON.parse(body)
        console.log(project_observations)
    }
}

// Get the list of observations
request(projectObsURL, observation_callback)

<commit_msg>inner loop to get a single observation and print lat/lon<commit_after>var request = require('request');
var config = require('config');

// Load configuration
confEvent = config.get('event');
confINat = config.get('iNaturalist');

// URL for project observations
var projectObsURL = confINat.api + 'observations/project/' + confEvent.slug + '.json';

// Callback function for project observations
function observation_callback(error, response, body) {
    if (!error && response.statusCode == 200) {
        var projectObservations = JSON.parse(body)
        for(var i in projectObservations) {
            var singleObservation = projectObservations[i]
            console.log(singleObservation.latitude, singleObservation.longitude)
        }
    }
}

// Get the list of observations
request(projectObsURL, observation_callback)

<|endoftext|>


<commit_before>/*
 * gulp-bootlint
 * https://github.com/tschortsch/gulp-bootlint
 *
 * Copyright (c) 2014 Juerg Hunziker
 * Licensed under the MIT license.
 */

'use strict';
var gutil = require('gulp-util');
var PluginError = gutil.PluginError;
var through = require('through2');
var chalk = require('chalk');
var bootlint = require('bootlint');

// consts
var PLUGIN_NAME = 'gulp-bootlint';

function gulpBootlint(options) {
    options = options || {
        stoponerror: false,
        relaxerror: []
    };

    var hardfail = false;

    // creating a stream through which each file will pass
    var stream = through.obj(function (file, enc, cb) {
        if (file.isNull()) {
            cb(null, file);
            return;
        }

        if (file.isStream()) {
            cb(new PluginError(PLUGIN_NAME, 'Streaming not supported'));
            return;
        }

        var reporter = function (lint) {
            var lintId = (lint.id[0] === 'E') ? chalk.bgGreen.white(lint.id) : chalk.bgRed.white(lint.id);
            var output = false;
            if (lint.elements) {
                lint.elements.each(function (_, element) {
                    var loc = element.startLocation;
                    gutil.log(file + ":" + (loc.line + 1) + ":" + (loc.column + 1), lintId, lint.message);
                    output = true;
                });
            }
            if (!output) {
                gutil.log(file + ":", lintId, lint.message);
                if (options.stoponerror) {
                    hardfail = true;
                }
            }
        };

        bootlint.lintHtml(file, reporter, options.relaxerror);

        return cb(null, file);
    });

    return stream;
};

// exporting the plugin
module.exports = gulpBootlint;<commit_msg>pass source instead of filepath<commit_after>/*
 * gulp-bootlint
 * https://github.com/tschortsch/gulp-bootlint
 *
 * Copyright (c) 2014 Juerg Hunziker
 * Licensed under the MIT license.
 */

'use strict';
var gutil = require('gulp-util');
var PluginError = gutil.PluginError;
var through = require('through2');
var chalk = require('chalk');
var bootlint = require('bootlint');

// consts
var PLUGIN_NAME = 'gulp-bootlint';

function gulpBootlint(options) {
    options = options || {
        relaxerror: []
    };

    var stream;

    // creating a stream through which each file will pass
    stream = through.obj(function (file, enc, cb) {
        if (file.isNull()) {
            cb(null, file);
            return;
        }

        if (file.isStream()) {
            cb(new PluginError(PLUGIN_NAME, 'Streaming not supported'));
            return;
        }

        var reporter = function (lint) {
            var lintId = (lint.id[0] === 'E') ? chalk.bgRed.white(lint.id) : chalk.bgYellow.white(lint.id);
            var errorElementsAvailable = false;
            if (lint.elements) {
                lint.elements.each(function (_, element) {
                    var errorLocation = element.startLocation;
                    gutil.log(file.path + ":" + (errorLocation.line + 1) + ":" + (errorLocation.column + 1), lintId, lint.message);
                    errorElementsAvailable = true;
                });
            }
            if (!errorElementsAvailable) {
                gutil.log(file.path + ":", lintId, lint.message);
            }
        };

        bootlint.lintHtml(file.contents.toString(), reporter, options.relaxerror);

        return cb(null, file);
    });

    return stream;
};

// exporting the plugin
module.exports = gulpBootlint;<|endoftext|>


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

var fs = require('fs');
var crypto = require('crypto');
var express = require('express');
var bodyParser = require('body-parser');
var redis = require('./redis.js');
var webPush = require('web-push');
var exphbs  = require('express-handlebars');

var app = express();
app.engine('handlebars', exphbs({defaultLayout: 'main'}));
app.set('view engine', 'handlebars');

app.use(bodyParser.json());

app.use(function(req, res, next) {
  var host = req.get('Host');

  if (host.indexOf('localhost') !== 0 && host.indexOf('127.0.0.1') !== 0) {
    // https://developer.mozilla.org/en-US/docs/Web/Security/HTTP_strict_transport_security
    res.header('Strict-Transport-Security', 'max-age=15768000');
    // https://github.com/rangle/force-ssl-heroku/blob/master/force-ssl-heroku.js
    if (req.headers['x-forwarded-proto'] !== 'https') {
      return res.redirect('https://' + host + req.url);
    }
  }
  return next();
});

app.use(function(req, res, next) {
  // http://enable-cors.org/server_expressjs.html
  res.header('Access-Control-Allow-Origin', '*');
  res.header('Access-Control-Allow-Headers', 'Origin, Content-Type, Accept');
  next();
});

if (!fs.existsSync('./dist')) {
  throw new Error('Missing `dist` folder, execute `npm run build` first.');
}
app.use(express.static('./dist'));

// load current data for
app.get('/', function(req, res) {
  res.render('index');
});

app.get('/devices/:token', function(req, res) {
  return sendMachines(req, res, req.params.token);
});

// get machines for the token and send them along with the token
function sendMachines(req, res, token) {
  if (!token) {
    throw('No token provided');
  }
  var machines = {};
  var machineId;
  function machinePromise(machineId) {
    return new Promise(function(resolve, reject) {
      redis.hgetall(machineId)
        .then(function(machine) {
          machines[machineId] = machine;
          resolve();
        });
    });
  }
  redis.smembers(token)
    .then(function(ids) {
      if (ids.length === 0) {
        res.sendStatus(404);
        return;
      }
      var promises = ids.map(machinePromise);
      Promise.all(promises)
        .then(() => res.send({
          token: token,
          machines: machines
        }));
    });
}

// adds a new machine to a token set
// creates a new token set if needed
app.post('/register', function(req, res) {
  // add/update machine in database
  var machineId = req.body.machineId;
  new Promise(function(resolve, reject) {
    if (!req.body.token) {
      return resolve();
    }

    redis.exists(req.body.token).then(function(result) {
      if (!result) {
        reject();
      } else {
        resolve();
      }
    }, reject);
  }).then(function() {
    redis.hmset(machineId, {
      endpoint: req.body.endpoint,
      key: req.body.key,
      name: req.body.name,
      active: true
    }).then(function() {
      // check if token provided
      return new Promise(function(resolve, reject) {
        if (req.body.token) {
          console.log('DEBUG: Registering machine ' + machineId + ' using existing token');
          resolve(req.body.token);
          return;
        }
        // creating a new token
        console.log('DEBUG: Creating a new token for machine ' + machineId);
        crypto.randomBytes(32, function(ex, buf) {
          resolve(buf.toString('hex'));
        });
      }).then(function(token) {
        // add to the token set only if not there already (multiple
        // notifications!)
        return redis.sismember(token, machineId)
        .then(function(isMember) {
          if (isMember) {
            sendMachines(req, res, token);
            return;
          }

          return redis.sadd(token, req.body.machineId)
          .then(() => sendMachines(req, res, token));
        });
      });
    })
    .catch(function(err) {
      console.error(err);
      res.sendStatus(500);
    });
  }, function() {
    console.log('DEBUG: Attempt to use a non existing token');
    res.sendStatus(404);
  });
});

// remove entire token set and all its machines
app.post('/unregister', function(req, res) {
  var token = req.body.token;

  redis.exists(token)
  .then(function(result) {
    if (!result) {
      res.sendStatus(404);
      return;
    }

    return redis.smembers(token)
    .then(function(machines) {
      var promises = machines.map(function(machine) {
        return redis.del(machine);
      });

      return Promise.all(promises)
      .then(() => redis.del(token))
      .then(() => res.sendStatus(200));
    });
  })
  .catch(function(err) {
    console.error(err);
    res.sendStatus(500);
  });
});

// remove machine hash and its id from token set
app.post('/unregisterMachine', function(req, res) {
  var token = req.body.token;
  var machineId = req.body.machineId;

  console.log('Unregistering machine: ' + machineId);

  redis.exists(token)
  .then(function(result) {
    if (!result) {
      res.sendStatus(404);
      return;
    }

    return redis.exists(machineId)
    .then(function(result) {
      if (!result) {
        res.sendStatus(404);
        return;
      }

      return redis.del(machineId)
      .then(function() {
        return redis.srem(token, machineId)
        .then(() => res.sendStatus(200));
      });
    });
  })
  .catch(function(err) {
    console.error(err);
    res.sendStatus(500);
  });
});

// used only if registration is expired
// it's needed as happens on request from service worker which doesn't
// have any meta data
app.post('/updateRegistration', function(req, res) {
  var token = req.body.token;
  var machineId = req.body.machineId;

  redis.sismember(token, machineId)
  .then(function(isMember) {
    if (!isMember) {
      res.sendStatus(404);
      return;
    }

    return redis.exists(machineId)
    .then(function(exists) {
      if (!exists) {
        res.sendStatus(404);
        return;
      }

      return redis.hmset(machineId, {
        "endpoint": req.body.endpoint,
        "key": req.body.key
      })
      .then(() => res.sendStatus(200));
    });
  })
  .catch(function(err) {
    console.error(err);
    res.sendStatus(500);
  });
});

// used only if metadata updated
// this happens on request to update meta data from front-end site
app.post('/updateMeta', function(req, res) {
  var token = req.body.token;
  var machineId = req.body.machineId;

  redis.sismember(token, machineId)
  .then(function(isMember) {
    if (!isMember) {
      res.sendStatus(404);
      return;
    }

    return redis.exists(machineId)
    .then(function(exists) {
      if (!exists) {
        res.sendStatus(404);
        return;
      }

      return redis.hmset(machineId, {
        "name": req.body.name,
        "active": req.body.active
      })
      .then(() => res.sendStatus(200));
    });
  })
  .catch(function(err) {
    console.error(err);
    res.sendStatus(500);
  });
});

app.post('/notify', function(req, res) {
  var token = req.body.token;

  redis.exists(token)
  .then(function(exists) {
    if (!exists) {
      res.sendStatus(404);
      return;
    }

    return redis.smembers(token)
    .then(function(machines) {
      // send notification to all machines assigned to `token`
      if (!machines) {
        // XXX: Are we being too aggressive here?
        return redis.del(token)
        .then(() => { throw new Error('Broken token.'); });
      }

      var promises = machines.map(function(machine) {
        return redis.hgetall(machine)
        .then(function(registration) {
          console.log('DEBUG: sending notification to: ' + registration.endpoint);

          if (registration.endpoint.indexOf('https://android.googleapis.com/gcm/send') === 0) {
            return redis.set(token + '-payload', JSON.stringify(req.body.payload))
            .then(() => webPush.sendNotification(registration.endpoint, req.body.ttl));
          } else {
            return webPush.sendNotification(registration.endpoint, req.body.ttl, registration.key, JSON.stringify(req.body.payload));
          }
        });
      });

      return Promise.all(promises)
      .then(() => res.sendStatus(200));
    });
  })
  .catch(function(err) {
    console.error('Error in sending notification: ' + err);
    res.sendStatus(500);
  });
});

app.get('/getPayload', function(req, res) {
  var hash = req.body.token + '-payload';

  redis.exists(hash)
  .then(function(exists) {
    if (!exists) {
      res.sendStatus(404);
      return;
    }

    return redis.get(hash)
    .then(function(payload) {
      if (!payload) {
        res.sendStatus(404);
        return;
      }

      res.send(payload);
    });
  })
  .catch(function(err) {
    console.error(err);
    res.sendStatus(500);
  });
});

if (!process.env.GCM_API_KEY) {
  console.warn('Set the GCM_API_KEY environment variable to support GCM');
}

webPush.setGCMAPIKey(process.env.GCM_API_KEY);

var port = process.env.PORT || 4000;
var ready = new Promise(function(resolve, reject) {
  app.listen(port, function(err) {
    if (err) {
      reject(err);
      return;
    }

    console.log('Mercurius listening on http://localhost:%d', port);

    resolve();
  });
});

module.exports = {
  app: app,
  ready: ready,
};
<commit_msg>unnecessary promise removed throw new error<commit_after>#! /usr/bin/env node

var fs = require('fs');
var crypto = require('crypto');
var express = require('express');
var bodyParser = require('body-parser');
var redis = require('./redis.js');
var webPush = require('web-push');
var exphbs  = require('express-handlebars');

var app = express();
app.engine('handlebars', exphbs({defaultLayout: 'main'}));
app.set('view engine', 'handlebars');

app.use(bodyParser.json());

app.use(function(req, res, next) {
  var host = req.get('Host');

  if (host.indexOf('localhost') !== 0 && host.indexOf('127.0.0.1') !== 0) {
    // https://developer.mozilla.org/en-US/docs/Web/Security/HTTP_strict_transport_security
    res.header('Strict-Transport-Security', 'max-age=15768000');
    // https://github.com/rangle/force-ssl-heroku/blob/master/force-ssl-heroku.js
    if (req.headers['x-forwarded-proto'] !== 'https') {
      return res.redirect('https://' + host + req.url);
    }
  }
  return next();
});

app.use(function(req, res, next) {
  // http://enable-cors.org/server_expressjs.html
  res.header('Access-Control-Allow-Origin', '*');
  res.header('Access-Control-Allow-Headers', 'Origin, Content-Type, Accept');
  next();
});

if (!fs.existsSync('./dist')) {
  throw new Error('Missing `dist` folder, execute `npm run build` first.');
}
app.use(express.static('./dist'));

// load current data for
app.get('/', function(req, res) {
  res.render('index');
});

app.get('/devices/:token', function(req, res) {
  return sendMachines(req, res, req.params.token);
});

// get machines for the token and send them along with the token
function sendMachines(req, res, token) {
  if (!token) {
    throw new Error('No token provided');
  }
  var machines = {};
  var machineId;
  function machinePromise(machineId) {
    return redis.hgetall(machineId)
    .then(function(machine) {
      machines[machineId] = machine;
    });
  }
  redis.smembers(token)
  .then(function(ids) {
    if (ids.length === 0) {
      res.sendStatus(404);
      return;
    }
    var promises = ids.map(machinePromise);
    Promise.all(promises)
    .then(() => res.send({
      token: token,
      machines: machines
    }));
  });
}

// adds a new machine to a token set
// creates a new token set if needed
app.post('/register', function(req, res) {
  // add/update machine in database
  var machineId = req.body.machineId;
  new Promise(function(resolve, reject) {
    if (!req.body.token) {
      return resolve();
    }

    redis.exists(req.body.token).then(function(result) {
      if (!result) {
        reject();
      } else {
        resolve();
      }
    }, reject);
  }).then(function() {
    redis.hmset(machineId, {
      endpoint: req.body.endpoint,
      key: req.body.key,
      name: req.body.name,
      active: true
    }).then(function() {
      // check if token provided
      return new Promise(function(resolve, reject) {
        if (req.body.token) {
          console.log('DEBUG: Registering machine ' + machineId + ' using existing token');
          resolve(req.body.token);
          return;
        }
        // creating a new token
        console.log('DEBUG: Creating a new token for machine ' + machineId);
        crypto.randomBytes(32, function(ex, buf) {
          resolve(buf.toString('hex'));
        });
      }).then(function(token) {
        // add to the token set only if not there already (multiple
        // notifications!)
        return redis.sismember(token, machineId)
        .then(function(isMember) {
          if (isMember) {
            sendMachines(req, res, token);
            return;
          }

          return redis.sadd(token, req.body.machineId)
          .then(() => sendMachines(req, res, token));
        });
      });
    })
    .catch(function(err) {
      console.error(err);
      res.sendStatus(500);
    });
  }, function() {
    console.log('DEBUG: Attempt to use a non existing token');
    res.sendStatus(404);
  });
});

// remove entire token set and all its machines
app.post('/unregister', function(req, res) {
  var token = req.body.token;

  redis.exists(token)
  .then(function(result) {
    if (!result) {
      res.sendStatus(404);
      return;
    }

    return redis.smembers(token)
    .then(function(machines) {
      var promises = machines.map(function(machine) {
        return redis.del(machine);
      });

      return Promise.all(promises)
      .then(() => redis.del(token))
      .then(() => res.sendStatus(200));
    });
  })
  .catch(function(err) {
    console.error(err);
    res.sendStatus(500);
  });
});

// remove machine hash and its id from token set
app.post('/unregisterMachine', function(req, res) {
  var token = req.body.token;
  var machineId = req.body.machineId;

  console.log('Unregistering machine: ' + machineId);

  redis.exists(token)
  .then(function(result) {
    if (!result) {
      res.sendStatus(404);
      return;
    }

    return redis.exists(machineId)
    .then(function(result) {
      if (!result) {
        res.sendStatus(404);
        return;
      }

      return redis.del(machineId)
      .then(function() {
        return redis.srem(token, machineId)
        .then(() => res.sendStatus(200));
      });
    });
  })
  .catch(function(err) {
    console.error(err);
    res.sendStatus(500);
  });
});

// used only if registration is expired
// it's needed as happens on request from service worker which doesn't
// have any meta data
app.post('/updateRegistration', function(req, res) {
  var token = req.body.token;
  var machineId = req.body.machineId;

  redis.sismember(token, machineId)
  .then(function(isMember) {
    if (!isMember) {
      res.sendStatus(404);
      return;
    }

    return redis.exists(machineId)
    .then(function(exists) {
      if (!exists) {
        res.sendStatus(404);
        return;
      }

      return redis.hmset(machineId, {
        "endpoint": req.body.endpoint,
        "key": req.body.key
      })
      .then(() => res.sendStatus(200));
    });
  })
  .catch(function(err) {
    console.error(err);
    res.sendStatus(500);
  });
});

// used only if metadata updated
// this happens on request to update meta data from front-end site
app.post('/updateMeta', function(req, res) {
  var token = req.body.token;
  var machineId = req.body.machineId;

  redis.sismember(token, machineId)
  .then(function(isMember) {
    if (!isMember) {
      res.sendStatus(404);
      return;
    }

    return redis.exists(machineId)
    .then(function(exists) {
      if (!exists) {
        res.sendStatus(404);
        return;
      }

      return redis.hmset(machineId, {
        "name": req.body.name,
        "active": req.body.active
      })
      .then(() => res.sendStatus(200));
    });
  })
  .catch(function(err) {
    console.error(err);
    res.sendStatus(500);
  });
});

app.post('/notify', function(req, res) {
  var token = req.body.token;

  redis.exists(token)
  .then(function(exists) {
    if (!exists) {
      res.sendStatus(404);
      return;
    }

    return redis.smembers(token)
    .then(function(machines) {
      // send notification to all machines assigned to `token`
      if (!machines) {
        // XXX: Are we being too aggressive here?
        return redis.del(token)
        .then(() => { throw new Error('Broken token.'); });
      }

      var promises = machines.map(function(machine) {
        return redis.hgetall(machine)
        .then(function(registration) {
          console.log('DEBUG: sending notification to: ' + registration.endpoint);

          if (registration.endpoint.indexOf('https://android.googleapis.com/gcm/send') === 0) {
            return redis.set(token + '-payload', JSON.stringify(req.body.payload))
            .then(() => webPush.sendNotification(registration.endpoint, req.body.ttl));
          } else {
            return webPush.sendNotification(registration.endpoint, req.body.ttl, registration.key, JSON.stringify(req.body.payload));
          }
        });
      });

      return Promise.all(promises)
      .then(() => res.sendStatus(200));
    });
  })
  .catch(function(err) {
    console.error('Error in sending notification: ' + err);
    res.sendStatus(500);
  });
});

app.get('/getPayload', function(req, res) {
  var hash = req.body.token + '-payload';

  redis.exists(hash)
  .then(function(exists) {
    if (!exists) {
      res.sendStatus(404);
      return;
    }

    return redis.get(hash)
    .then(function(payload) {
      if (!payload) {
        res.sendStatus(404);
        return;
      }

      res.send(payload);
    });
  })
  .catch(function(err) {
    console.error(err);
    res.sendStatus(500);
  });
});

if (!process.env.GCM_API_KEY) {
  console.warn('Set the GCM_API_KEY environment variable to support GCM');
}

webPush.setGCMAPIKey(process.env.GCM_API_KEY);

var port = process.env.PORT || 4000;
var ready = new Promise(function(resolve, reject) {
  app.listen(port, function(err) {
    if (err) {
      reject(err);
      return;
    }

    console.log('Mercurius listening on http://localhost:%d', port);

    resolve();
  });
});

module.exports = {
  app: app,
  ready: ready,
};
<|endoftext|>


<commit_before>var EventEmitter = require('events').EventEmitter;
var util = require('util');

function Shop(name, autoload){
	EventEmitter.call(this);
	this.name = name;
	this.items = [];
	if(autoload){
		this.load();
	}
}

util.inherits(Shop, EventEmitter);

module.exports = Shop;

Shop.prototype.addItem = function(item){
	this.items.push(item);
}

Shop.prototype.getItem = function(id){
	var items = this.items.filter(function(item){
		return item.id==id;
	})
	return items.length>0 ? items[0] : null;
}

Shop.prototype.removeItem = function(id){
	this.items = this.items.filter(function(item){
		return item.id!=id;
	})
}

Shop.prototype.getTotal = function(){
	var total = 0;
	this.items.forEach(function(item){
		total+=(item.price||0)*(item.qty||0);
	})
	return total;
}

Shop.prototype.load = function(){
	var string = localStorage != null ? localStorage[this.name + "_cornershop"] : null;
	if(string){
		try {
      this.items = JSON.parse(string);
    }
    catch (err) {

    }
	}
}

Shop.prototype.save = function(){
	if (localStorage != null){
    localStorage[this.name + "_cornershop"] = JSON.stringify(this.items);
  }
}

module.exports = function(name){
	return new Shop(name);
}

module.exports.Class = Shop;<commit_msg>no export of node modules into component land<commit_after>function Shop(name, autoload){
	this.name = name;
	this.items = [];
	if(autoload){
		this.load();
	}
}

module.exports = Shop;

Shop.prototype.addItem = function(item){
	this.items.push(item);
}

Shop.prototype.getItem = function(id){
	var items = this.items.filter(function(item){
		return item.id==id;
	})
	return items.length>0 ? items[0] : null;
}

Shop.prototype.removeItem = function(id){
	this.items = this.items.filter(function(item){
		return item.id!=id;
	})
}

Shop.prototype.getTotal = function(){
	var total = 0;
	this.items.forEach(function(item){
		total+=(item.price||0)*(item.qty||0);
	})
	return total;
}

Shop.prototype.load = function(){
	var string = localStorage != null ? localStorage[this.name + "_cornershop"] : null;
	if(string){
		try {
      this.items = JSON.parse(string);
    }
    catch (err) {

    }
	}
}

Shop.prototype.save = function(){
	if (localStorage != null){
    localStorage[this.name + "_cornershop"] = JSON.stringify(this.items);
  }
}

module.exports = function(name){
	return new Shop(name);
}

module.exports.Class = Shop;<|endoftext|>


<commit_before>'use strict';

var debug = require('debug')('express-sharp')
var etag = require('etag');
var express = require('express');
var http = require('http');
var sharp = require('sharp');
var url = require('url');
var validator = require('validator');

var router = express.Router();

var options = {};

var validateWith = function(validator) {
  return function(req, res, next, val) {
    if (validator(val)) {
      return next();
    }
    res.sendStatus(400);
  }
};

router.param('height', validateWith(validator.isNumeric));
router.param('width', validateWith(validator.isNumeric));

router.get('/resize/:width/:height?', function(req, res) {
  var format = req.query.format;
  var quality = parseInt(req.query.quality, 10);

  if (
    !validator.isURL(req.query.url) ||
    (format && !sharp.format.hasOwnProperty(format)) ||
    (quality && !validator.isNumeric(quality))
  ) {
    return res.sendStatus(400);
  }

  var imageUrl = url.parse(req.query.url);
  imageUrl.host = options.baseHost;
  if (imageUrl.port !== null) {
    imageUrl.host += ':' + imageUrl.port;
  }
  imageUrl = url.format(imageUrl);

  var width = parseInt(req.params.width, 10);
  var height = parseInt(req.params.height, 10);

  var transformer = sharp()
    .resize(width, height)
    .withoutEnlargement()
    .on('error', function(err) {
      console.error(err);
      res.sendStatus(500);
    });

  if (req.query.progressive) {
    transformer.progressive();
  }

  if (format) {
    transformer.toFormat(format);
  }

  if (quality) {
    transformer.quality(quality)
  }

  var etagBuffer = new Buffer([imageUrl, width, height, format, quality]);
  res.setHeader('ETag', etag(etagBuffer, {weak: true}))
  if (req.fresh) {
    return res.sendStatus(304);
  }

  debug('Requesting:', imageUrl);
  http
    .get(imageUrl, function(result) {
      debug(imageUrl, 'requested');
      res.status(result.statusCode)
      res.type(format || result.headers['content-type']);
      result.pipe(transformer).pipe(res);
    })
    .on('error', function(err) {
      console.error('Error while fetching ' + imageUrl + ':', err);
      res.sendStatus(500);
    });
});

module.exports = function(o) {
  options = o;
  return router;
};
<commit_msg>extract transformer function<commit_after>'use strict';

var debug = require('debug')('express-sharp')
var etag = require('etag');
var express = require('express');
var http = require('http');
var sharp = require('sharp');
var url = require('url');
var validator = require('validator');

var router = express.Router();

var options = {};

var validateWith = function(validator) {
  return function(req, res, next, val) {
    if (validator(val)) {
      return next();
    }
    res.sendStatus(400);
  }
};

var transform = function(width, height) {
  return sharp()
    .resize(width, height)
    .withoutEnlargement()
};

router.param('height', validateWith(validator.isNumeric));
router.param('width', validateWith(validator.isNumeric));

router.get('/resize/:width/:height?', function(req, res) {
  var format = req.query.format;
  var quality = parseInt(req.query.quality, 10);

  if (
    !validator.isURL(req.query.url) ||
    (format && !sharp.format.hasOwnProperty(format)) ||
    (quality && !validator.isNumeric(quality))
  ) {
    return res.sendStatus(400);
  }

  var imageUrl = url.parse(req.query.url);
  imageUrl.host = options.baseHost;
  if (imageUrl.port !== null) {
    imageUrl.host += ':' + imageUrl.port;
  }
  imageUrl = url.format(imageUrl);

  var width = parseInt(req.params.width, 10);
  var height = parseInt(req.params.height, 10);

  var transformer = transform(width, height)
    .on('error', function sharpError(err) {
      res.status(500);
      next(new Error(err));
    });

  if (req.query.progressive) {
    transformer.progressive();
  }

  if (format) {
    transformer.toFormat(format);
  }

  if (quality) {
    transformer.quality(quality)
  }

  var etagBuffer = new Buffer([imageUrl, width, height, format, quality]);
  res.setHeader('ETag', etag(etagBuffer, {weak: true}))
  if (req.fresh) {
    return res.sendStatus(304);
  }

  debug('Requesting:', imageUrl);
  http
    .get(imageUrl, function(result) {
      debug(imageUrl, 'requested');
      res.status(result.statusCode)
      res.type(format || result.headers['content-type']);
      result.pipe(transformer).pipe(res);
    })
    .on('error', function(err) {
      console.error('Error while fetching ' + imageUrl + ':', err);
      res.sendStatus(500);
    });
});

module.exports = function(o) {
  options = o;
  return router;
};
<|endoftext|>


<commit_before>var BasePlugin = require('ember-cli-deploy-plugin');
var Rsync = require('rsync');

module.exports = {
  name: 'ember-cli-deploy-scp',

  createDeployPlugin: function(options) {
    var DeployPlugin = BasePlugin.extend({
      name: options.name,

      defaultConfig: {
        port: '22',
        directory: 'tmp/deploy-dist/*'
      },

      requiredConfig: ['username', 'path', 'host'],

      build: function(context) {
        this.log('Building...');
      },

      upload: function(context) {
        this.log('Uploading...');
        var MyDate = new Date();
        var MyDateString;
        MyDate.setDate(MyDate.getDate());
        MyDateString = ('0' + MyDate.getDate()).slice(-2) + ('0' + (MyDate.getMonth()+1)).slice(-2) + MyDate.getFullYear() + ('0' + MyDate.getHours()).slice(-2) + ('0' + MyDate.getMinutes()).slice(-2);

        var rsync = new Rsync()
          .shell('ssh')
          .flags('rtvu')
          .source(this.readConfig('directory'))
          .destination(this.readConfig('username') + '@' + this.readConfig('host') + ':' + this.readConfig('path') + '/' + MyDateString);

        rsync.execute(function(error, code, cmd) {
            this.log('Done !');
        });

        var rsync_current = new Rsync()
          .shell('ssh')
          .flags('rtvu')
          .source(this.readConfig('directory'))
          .destination(this.readConfig('username') + '@' + this.readConfig('host') + ':' + this.readConfig('path') + '/current/web');

        rsync_current.execute(function(error, code, cmd) {
            this.log('Done !');
        });

        this.log('Done !');
      }
    });

    return new DeployPlugin();
  }
};<commit_msg>Remote dir is now now configurable<commit_after>var BasePlugin = require('ember-cli-deploy-plugin');
var Rsync = require('rsync');

module.exports = {
  name: 'ember-cli-deploy-scp',

  createDeployPlugin: function(options) {
    var DeployPlugin = BasePlugin.extend({
      name: options.name,

      defaultConfig: {
        port: '22',
        directory: 'tmp/deploy-dist/*',
        remoteDir: '/current/web'
      },

      requiredConfig: ['username', 'path', 'host'],

      build: function(context) {
        this.log('Building...');
      },

      upload: function(context) {
        this.log('Uploading...');
        var MyDate = new Date();
        var MyDateString;
        MyDate.setDate(MyDate.getDate());
        MyDateString = ('0' + MyDate.getDate()).slice(-2) + ('0' + (MyDate.getMonth()+1)).slice(-2) + MyDate.getFullYear() + ('0' + MyDate.getHours()).slice(-2) + ('0' + MyDate.getMinutes()).slice(-2);

        var rsync = new Rsync()
          .shell('ssh')
          .flags('rtvu')
          .source(this.readConfig('directory'))
          .destination(this.readConfig('username') + '@' + this.readConfig('host') + ':' + this.readConfig('path') + '/' + MyDateString);

        rsync.execute(function(error, code, cmd) {
            this.log('Done !');
        });

        var rsync_current = new Rsync()
          .shell('ssh')
          .flags('rtvu')
          .source(this.readConfig('directory'))
          .destination(this.readConfig('username') + '@' + this.readConfig('host') + ':' + this.readConfig('path') + this.readConfig('remoteDir'));

        rsync_current.execute(function(error, code, cmd) {
            this.log('Done !');
        });

        this.log('Done !');
      }
    });

    return new DeployPlugin();
  }
};
<|endoftext|>


<commit_before>'use strict';

/**
 * serverless-mocha-plugin
 * - a plugin for TDD with serverless
 */

const path  = require('path'),
  fs        = require('fs'),
  lambdaWrapper = require('lambda-wrapper'),
  Mocha = require('mocha'),
  chai = require('chai'),
  Path = require('path'),
  BbPromise = require('bluebird'); // Serverless uses Bluebird Promises and we recommend you do to because they provide more than your average Promise :)

const testFolder = 'test'; // Folder used my mocha for tests

module.exports = function(S) { // Always pass in the ServerlessPlugin Class

  /**
   * Adding/Manipulating Serverless classes
   * - You can add or manipulate Serverless classes like this
   */

  S.classes.Project.newStaticMethod     = function() { console.log("A new method!"); };
  S.classes.Project.prototype.newMethod = function() { S.classes.Project.newStaticMethod(); };

  /**
   * Extending the Plugin Class
   * - Here is how you can add custom Actions and Hooks to Serverless.
   * - This class is only required if you want to add Actions and Hooks.
   */

  class PluginBoilerplate extends S.classes.Plugin {

    /**
     * Constructor
     * - Keep this and don't touch it unless you know what you're doing.
     */

    constructor() {
      super();
      this.name = 'io.sc5.mocha';
    }

    /**
     * Register Actions
     * - function mocha-create
     */

    registerActions() {

      S.addAction(this._createAction.bind(this), {
        handler:       '_createAction',
        description:   'Create mocha test for function',
        context:       'function',
        contextAction: 'mocha-create',
        options:       [{ // These must be specified in the CLI like this "-option true" or "-o true"
        }],
        parameters: [ // Use paths when you multiple values need to be input (like an array).  Input looks like this: "serverless custom run module1/function1 module1/function2 module1/function3.  Serverless will automatically turn this into an array and attach it to evt.options within your plugin
          {
            parameter: 'paths',
            description: 'Path to function to test. If not defined, test all functions.',
            position: '0->' // Can be: 0, 0-2, 0->  This tells Serverless which params are which.  3-> Means that number and infinite values after it.
          }
        ]
      });

      S.addAction(this._runAction.bind(this), {
        handler:       '_runAction',
        description:   'Create mocha test for function',
        context:       'function',
        contextAction: 'mocha-run',
        options:       [          {
            option:      'region',
            shortcut:    'r',
            description: 'region you want to run your function in'
          },
          {
            option:      'stage',
            shortcut:    's',
            description: 'stage you want to run your function in'
          },
          {
            option:      'reporter',
            shortcut:    'R',
            description: 'specify the reporter to use'
          },
          {
            option:      'reporter-options',
            shortcut:    'O',
            description: 'reporter-specific options'
          }],
        parameters: [ // Use paths when you multiple values need to be input (like an array).  Input looks like this: "serverless custom run module1/function1 module1/function2 module1/function3.  Serverless will automatically turn this into an array and attach it to evt.options within your plugin
          {
            parameter: 'paths',
            description: 'Path to function to test. If not defined, test all functions.',
            position: '0->' // Can be: 0, 0-2, 0->  This tells Serverless which params are which.  3-> Means that number and infinite values after it.
          }
        ]
      });

      return BbPromise.resolve();
    }

    /**
     * Register Hooks
     * -  function create (post) creates mocha test file
     */

    registerHooks() {
      S.addHook(this._hookPostFuncCreate.bind(this), {
        action: 'functionCreate',
        event:  'post'
      });

      return BbPromise.resolve();
    }

    /**
     * Custom action serverless function mocha-create functioName
     */

    _createAction(evt) {
      if (S.getProject().getFunction(evt.options.paths[0]) === undefined) {
        return new BbPromise(function(resolve, reject) {
          reject(`MochaPluginError: Function ${evt.options.paths[0]} does not exist in your project`);
        });
      }

      return createTest(evt.options.paths[0]);
    }

    /**
     * Custom action serverless function mocha-create functioName
     */

    _runAction(evt) {
      return new BbPromise(function(resolve, reject) {
          let functions = evt.options.paths;
          let mocha = new Mocha();
          //This could pose as an issue if several functions share a common ENV name but different values.

          let stage = evt.options.stage || S.getProject().getAllStages()[0].name;
          let region = evt.options.region || S.getProject().getAllRegions(stage)[0].name;

          getFunctions(functions)
          .then(getFilePaths)
          .then(function(paths) {
            if (paths.length === 0) {
              return reject('No tests to run.');
            }
            paths.forEach(function(func,idx) {
              SetEnvVars(func, {
                stage: stage,
                region: region
              });
              mocha.addFile(func.mochaPlugin.testPath);
            })
            var reporter = evt.options.reporter;
            if ( reporter !== null) {
              var reporterOptions = {};
              if (evt.options["reporter-options"] !== null) {
                evt.options["reporter-options"].split(",").forEach(function(opt) {
                  var L = opt.split("=");
                  if (L.length > 2 || L.length === 0) {
                    throw new Error("invalid reporter option '" + opt + "'");
                  } else if (L.length === 2) {
                    reporterOptions[L[0]] = L[1];
                  } else {
                    reporterOptions[L[0]] = true;
                  }
                });
              }
              mocha.reporter(reporter, reporterOptions)
            }
            mocha.run(function(failures){
              process.on('exit', function () {
                process.exit(failures);  // exit with non-zero status if there were failures
              });
            });
          }, function(error) {

            return reject(error);
          });
      });
    }
    /**
     * Hook for creating the mocha test placeholder after function creation
     */

    _hookPostFuncCreate(evt) {
      // TODO: only run with runtime node4.3
      if (evt.options.runtime != 'nodejs4.3') {
        return;
      }
      let parsedPath = path.parse(evt.options.path);
      let funcName = parsedPath.base;

      return createTest(funcName);
    }
  }

  //Set environment variables
  function SetEnvVars(func, config) {
    var envVars = func.toObjectPopulated(config);
    var fields = Object.keys(envVars);

    for (var key in fields) {
      process.env[fields[key]] = envVars[fields[key]];
    }
    return process.env;
  }

  // Create the test folder
  function createTestFolder() {
      return new BbPromise(function(resolve, reject) {

        fs.exists(testFolder, function(exists) {
            if (exists) {
                return resolve(testFolder);
            }
            fs.mkdir(testFolder, function(err) {
                if (err) {
                    return reject(err);
                }
                return resolve(testFolder);
            })
        })
      });
  }

  // Create the test file (and test folder)

  function createTest(funcName) {
    return createTestFolder().then(function(testFolder) {
      return new BbPromise(function(resolve, reject) {
        let funcFilePath = testFilePath(funcName);
        let projectPath = S.getProject().getRootPath();
        let funcFullPath = S.getProject().getFunction(funcName).getRootPath();
        let funcPath = path.relative(projectPath, funcFullPath).replace(/\\/g, "/");

        fs.exists(funcFilePath, function (exists) {
           if (exists) {
               return reject(new Error(`File ${funcFilePath} already exists`));
           }
           fs.writeFile(funcFilePath, newTestFile(funcName, funcPath), function(err) {
               if (err) {
                   return reject(new Error(`Creating file ${funcFilePath} failed: ${err}`));
               }
               console.log(`serverless-mocha-plugin: created ${funcFilePath}`);
               return resolve(funcFilePath);
           })
        });
      });
    });
  }

  // getFunctions. If no functions provided, returns all files
  function getFunctions(funcNames) {
    return new BbPromise(function(resolve, reject) {
      let funcObjs = [];
      if (funcNames.length === 0) {
        let sFuncs = S.getProject().getAllFunctions();

        return resolve(sFuncs);
      }

      let func;
      funcNames.forEach(function(funcName, idx) {
        func = S.getProject().getFunction(funcName);
        if (func) {
          funcObjs.push(func);
        } else {
          console.log(`Warning: Could not find function '${funcName}'.`);
        }
      });
      resolve(funcObjs);
    });
  };

  // getTestFiles. If no functions provided, returns all files
  function getFilePaths(funcs) {
   // console.log(funcs);
    return new BbPromise(function(resolve, reject) {
        var paths = [];

        if (funcs && (funcs.length > 0)) {
            funcs.forEach(function(val, idx) {
              val.mochaPlugin = {
                testPath: testFilePath(val.name)
              };
              paths.push(val);
            });
            return resolve(paths);
        }
        return resolve([]);
    });
  };

  // Returns the path to a function's test file
  function testFilePath(funcName) {
      return path.join(testFolder, `${funcName.replace(/.*\//g, '')}.js`);
  }

  function newTestFile(funcName, funcPath) {
      return `'use strict';
// tests for ${funcName}
// Generated by serverless-mocha-plugin

const mod         = require('../${funcPath}/handler.js');
const mochaPlugin = require('serverless-mocha-plugin');
const wrapper     = mochaPlugin.lambdaWrapper;
const expect      = mochaPlugin.chai.expect;

describe('${funcName}', () => {
  before(function (done) {
    wrapper.init(mod);

    done()
  })

  it('implement tests here', (done) => {
    wrapper.init(mod);
    wrapper.run({}, (err, response) => {
      done('no tests implemented');
    });
  });
});
`;

  }
  // Export Plugin Class
  return PluginBoilerplate;
};

module.exports.lambdaWrapper = lambdaWrapper;
module.exports.chai = chai;
<commit_msg>Fixed bug of environment variables not populating<commit_after>'use strict';

/**
 * serverless-mocha-plugin
 * - a plugin for TDD with serverless
 */

const path  = require('path'),
  fs        = require('fs'),
  lambdaWrapper = require('lambda-wrapper'),
  Mocha = require('mocha'),
  chai = require('chai'),
  Path = require('path'),
  BbPromise = require('bluebird'); // Serverless uses Bluebird Promises and we recommend you do to because they provide more than your average Promise :)

const testFolder = 'test'; // Folder used my mocha for tests

module.exports = function(S) { // Always pass in the ServerlessPlugin Class

  /**
   * Adding/Manipulating Serverless classes
   * - You can add or manipulate Serverless classes like this
   */

  S.classes.Project.newStaticMethod     = function() { console.log("A new method!"); };
  S.classes.Project.prototype.newMethod = function() { S.classes.Project.newStaticMethod(); };

  /**
   * Extending the Plugin Class
   * - Here is how you can add custom Actions and Hooks to Serverless.
   * - This class is only required if you want to add Actions and Hooks.
   */

  class PluginBoilerplate extends S.classes.Plugin {

    /**
     * Constructor
     * - Keep this and don't touch it unless you know what you're doing.
     */

    constructor() {
      super();
      this.name = 'io.sc5.mocha';
    }

    /**
     * Register Actions
     * - function mocha-create
     */

    registerActions() {

      S.addAction(this._createAction.bind(this), {
        handler:       '_createAction',
        description:   'Create mocha test for function',
        context:       'function',
        contextAction: 'mocha-create',
        options:       [{ // These must be specified in the CLI like this "-option true" or "-o true"
        }],
        parameters: [ // Use paths when you multiple values need to be input (like an array).  Input looks like this: "serverless custom run module1/function1 module1/function2 module1/function3.  Serverless will automatically turn this into an array and attach it to evt.options within your plugin
          {
            parameter: 'paths',
            description: 'Path to function to test. If not defined, test all functions.',
            position: '0->' // Can be: 0, 0-2, 0->  This tells Serverless which params are which.  3-> Means that number and infinite values after it.
          }
        ]
      });

      S.addAction(this._runAction.bind(this), {
        handler:       '_runAction',
        description:   'Create mocha test for function',
        context:       'function',
        contextAction: 'mocha-run',
        options:       [          {
            option:      'region',
            shortcut:    'r',
            description: 'region you want to run your function in'
          },
          {
            option:      'stage',
            shortcut:    's',
            description: 'stage you want to run your function in'
          },
          {
            option:      'reporter',
            shortcut:    'R',
            description: 'specify the reporter to use'
          },
          {
            option:      'reporter-options',
            shortcut:    'O',
            description: 'reporter-specific options'
          }],
        parameters: [ // Use paths when you multiple values need to be input (like an array).  Input looks like this: "serverless custom run module1/function1 module1/function2 module1/function3.  Serverless will automatically turn this into an array and attach it to evt.options within your plugin
          {
            parameter: 'paths',
            description: 'Path to function to test. If not defined, test all functions.',
            position: '0->' // Can be: 0, 0-2, 0->  This tells Serverless which params are which.  3-> Means that number and infinite values after it.
          }
        ]
      });

      return BbPromise.resolve();
    }

    /**
     * Register Hooks
     * -  function create (post) creates mocha test file
     */

    registerHooks() {
      S.addHook(this._hookPostFuncCreate.bind(this), {
        action: 'functionCreate',
        event:  'post'
      });

      return BbPromise.resolve();
    }

    /**
     * Custom action serverless function mocha-create functioName
     */

    _createAction(evt) {
      if (S.getProject().getFunction(evt.options.paths[0]) === undefined) {
        return new BbPromise(function(resolve, reject) {
          reject(`MochaPluginError: Function ${evt.options.paths[0]} does not exist in your project`);
        });
      }

      return createTest(evt.options.paths[0]);
    }

    /**
     * Custom action serverless function mocha-create functioName
     */

    _runAction(evt) {
      return new BbPromise(function(resolve, reject) {
          let functions = evt.options.paths;
          let mocha = new Mocha();
          //This could pose as an issue if several functions share a common ENV name but different values.

          let stage = evt.options.stage || S.getProject().getAllStages()[0].name;
          let region = evt.options.region || S.getProject().getAllRegions(stage)[0].name;

          getFunctions(functions)
          .then(getFilePaths)
          .then(function(paths) {
            if (paths.length === 0) {
              return reject('No tests to run.');
            }
            paths.forEach(function(func,idx) {
              SetEnvVars(func, {
                stage: stage,
                region: region
              });
              mocha.addFile(func.mochaPlugin.testPath);
            })
            var reporter = evt.options.reporter;
            if ( reporter !== null) {
              var reporterOptions = {};
              if (evt.options["reporter-options"] !== null) {
                evt.options["reporter-options"].split(",").forEach(function(opt) {
                  var L = opt.split("=");
                  if (L.length > 2 || L.length === 0) {
                    throw new Error("invalid reporter option '" + opt + "'");
                  } else if (L.length === 2) {
                    reporterOptions[L[0]] = L[1];
                  } else {
                    reporterOptions[L[0]] = true;
                  }
                });
              }
              mocha.reporter(reporter, reporterOptions)
            }
            mocha.run(function(failures){
              process.on('exit', function () {
                process.exit(failures);  // exit with non-zero status if there were failures
              });
            });
          }, function(error) {

            return reject(error);
          });
      });
    }
    /**
     * Hook for creating the mocha test placeholder after function creation
     */

    _hookPostFuncCreate(evt) {
      // TODO: only run with runtime node4.3
      if (evt.options.runtime != 'nodejs4.3') {
        return;
      }
      let parsedPath = path.parse(evt.options.path);
      let funcName = parsedPath.base;

      return createTest(funcName);
    }
  }

  //Set environment variables
  function SetEnvVars(func, config) {
    var envVars = func.toObjectPopulated(config).environment;
    var fields = Object.keys(envVars);

    for (var key in fields) {
      process.env[fields[key]] = envVars[fields[key]];
    }
    return process.env;
  }

  // Create the test folder
  function createTestFolder() {
      return new BbPromise(function(resolve, reject) {

        fs.exists(testFolder, function(exists) {
            if (exists) {
                return resolve(testFolder);
            }
            fs.mkdir(testFolder, function(err) {
                if (err) {
                    return reject(err);
                }
                return resolve(testFolder);
            })
        })
      });
  }

  // Create the test file (and test folder)

  function createTest(funcName) {
    return createTestFolder().then(function(testFolder) {
      return new BbPromise(function(resolve, reject) {
        let funcFilePath = testFilePath(funcName);
        let projectPath = S.getProject().getRootPath();
        let funcFullPath = S.getProject().getFunction(funcName).getRootPath();
        let funcPath = path.relative(projectPath, funcFullPath).replace(/\\/g, "/");

        fs.exists(funcFilePath, function (exists) {
           if (exists) {
               return reject(new Error(`File ${funcFilePath} already exists`));
           }
           fs.writeFile(funcFilePath, newTestFile(funcName, funcPath), function(err) {
               if (err) {
                   return reject(new Error(`Creating file ${funcFilePath} failed: ${err}`));
               }
               console.log(`serverless-mocha-plugin: created ${funcFilePath}`);
               return resolve(funcFilePath);
           })
        });
      });
    });
  }

  // getFunctions. If no functions provided, returns all files
  function getFunctions(funcNames) {
    return new BbPromise(function(resolve, reject) {
      let funcObjs = [];
      if (funcNames.length === 0) {
        let sFuncs = S.getProject().getAllFunctions();

        return resolve(sFuncs);
      }

      let func;
      funcNames.forEach(function(funcName, idx) {
        func = S.getProject().getFunction(funcName);
        if (func) {
          funcObjs.push(func);
        } else {
          console.log(`Warning: Could not find function '${funcName}'.`);
        }
      });
      resolve(funcObjs);
    });
  };

  // getTestFiles. If no functions provided, returns all files
  function getFilePaths(funcs) {
   // console.log(funcs);
    return new BbPromise(function(resolve, reject) {
        var paths = [];

        if (funcs && (funcs.length > 0)) {
            funcs.forEach(function(val, idx) {
              val.mochaPlugin = {
                testPath: testFilePath(val.name)
              };
              paths.push(val);
            });
            return resolve(paths);
        }
        return resolve([]);
    });
  };

  // Returns the path to a function's test file
  function testFilePath(funcName) {
      return path.join(testFolder, `${funcName.replace(/.*\//g, '')}.js`);
  }

  function newTestFile(funcName, funcPath) {
      return `'use strict';
// tests for ${funcName}
// Generated by serverless-mocha-plugin

const mod         = require('../${funcPath}/handler.js');
const mochaPlugin = require('serverless-mocha-plugin');
const wrapper     = mochaPlugin.lambdaWrapper;
const expect      = mochaPlugin.chai.expect;

describe('${funcName}', () => {
  before(function (done) {
    wrapper.init(mod);

    done()
  })

  it('implement tests here', (done) => {
    wrapper.init(mod);
    wrapper.run({}, (err, response) => {
      done('no tests implemented');
    });
  });
});
`;

  }
  // Export Plugin Class
  return PluginBoilerplate;
};

module.exports.lambdaWrapper = lambdaWrapper;
module.exports.chai = chai;
<|endoftext|>


<commit_before>var chalk = require('chalk');
var gutil = require('gulp-util');
var toposort = require('toposort');

module.exports = {
    init: preprocessTopoInitializor,
    merger: preprocessTopoMerger
};

/**
 * Initializes preprocess options and sorts dependencies.
 * This function adds a new property called `preprocessFeatures` holding an array of first level preprocess option keys.
 *
 * **Note:** Dependencies are read from `preprocessDependencies` property.
 *
 * @param {Object} config - The default or custom config to be initialized.
 */
function preprocessTopoInitializor(config) {
    var preprocess = config.preprocess;
    var dependencies = config.preprocessDependencies;
    var features = Object.keys(preprocess);
    var edges;

    if(dependencies) {
        edges = hashToEdges(dependencies, preprocess);
        features = toposort.array(features, edges).reverse();
    }

    config.preprocessFeatures = features;

    gutil.log(chalk.green('Init preprocess features.'));
}

/**
 * Merges preprocess options through complementing with a default config file and sorts dependencies.
 * This function adds a new property called `preprocessFeatures` holding an array of first level preprocess option keys.
 *
 * **Note:** Dependencies are read from `preprocessDependencies` property.
 *
 * @param {Object} config - The new config to be merged with defaultConfig.
 * @param {Object} defaultConfig - The default config used to complement the new config.
 */
function preprocessTopoMerger(config, defaultConfig) {
    var preprocess = config.preprocess;
    var preprocessDefault = defaultConfig.preprocess;
    var dependencies = defaultConfig.preprocessDependencies;
    var features;
    var edges;
    var edge;
    var node;
    var dep;
    var i, l;
    var resolved = [];

    // resolve dependant properties
    if(dependencies) {
        edges = hashToEdges(dependencies, preprocess);

        for (i = 0, l = edges.length; i < l; i++) {
            edge = edges[i];
            node = edge[0];
            dep = edge[1];

            if (preprocess[node] && !preprocess[dep]) {
                preprocess[dep] = preprocessDefault[dep];

                resolved.push(dep);

                edges.slice(i, 1);
                l--;
                i = -1;
            }
        }
    }

    // set features
    features = Object.keys(preprocess);
    if(dependencies) {
        edges = hashToEdges(dependencies, preprocess);
        features = toposort.array(features, edges).reverse();
    }

    config.preprocessFeatures = features;

    gutil.log(chalk.green('Resolved ') + chalk.yellow(resolved.length) + chalk.green(' Dependencies: ') + chalk.yellow(resolved.join(chalk.green(', '))));
}

/**
 * A hash containing features and their respective dependencies.
 *
 * **Note:** This has also build in support for conditional dependencies.
 *
 * @typedef {Object.<string, Array.<string|{condition: string, value: string}>>} DependenciesHash
 */

/**
 * Turns a preprocessDependencies hash in an array of edges, consumable by toposort algorithm.
 *
 * **Note:** This has also build in support for conditional dependencies.
 *
 * @param {DependenciesHash} hash - The preprocessDependencies hash.
 * @param {Object} preprocess - The preprocess config object.
 * @returns {Array}
 */
function hashToEdges(hash, preprocess) {
    if(!hash) return;

    var keys = Object.keys(hash);
    var edges = [];
    var key;
    var deps;
    var dep;
    var condition;
    var i, l, j, k;

    for(i=0, l=keys.length; i<l; i++) {
        key = keys[i];
        deps = hash[key];

        for(j=0, k=deps.length; j<k; j++) {
            dep = deps[j];

            // check conditional dependency
            if(typeof dep === 'object') {
                condition = dep.condition;

                // if feature is disabled -> continue
                if(!preprocess || !preprocess[key] || !preprocess[key][condition])
                    continue;

                // else get dependency
                dep = dep.value;
            }

            edges.push([key, dep]);
        }
    }

    return edges;
}<commit_msg>fixed #3<commit_after>var chalk = require('chalk');
var gutil = require('gulp-util');
var toposort = require('toposort');

module.exports = {
    init: preprocessTopoInitializor,
    merger: preprocessTopoMerger
};

/**
 * Initializes preprocess options and sorts dependencies.
 * This function adds a new property called `preprocessFeatures` holding an array of first level preprocess option keys.
 *
 * **Note:** Dependencies are read from `preprocessDependencies` property.
 *
 * @param {Object} config - The default or custom config to be initialized.
 */
function preprocessTopoInitializor(config) {
    var preprocess = config.preprocess;
    var dependencies = config.preprocessDependencies;
    var features = Object.keys(preprocess);
    var edges;

    if(dependencies) {
        edges = hashToEdges(dependencies, preprocess);
        features = toposort.array(features, edges).reverse();
    }

    config.preprocessFeatures = features;

    gutil.log(chalk.green('Init preprocess features.'));
}

/**
 * Merges preprocess options through complementing with a default config file and sorts dependencies.
 * This function adds a new property called `preprocessFeatures` holding an array of first level preprocess option keys.
 *
 * **Note:** Dependencies are read from `preprocessDependencies` property.
 *
 * @param {Object} config - The new config to be merged with defaultConfig.
 * @param {Object} defaultConfig - The default config used to complement the new config.
 */
function preprocessTopoMerger(config, defaultConfig) {
    var preprocess = config.preprocess;
    var preprocessDefault = defaultConfig.preprocess;
    var dependencies = defaultConfig.preprocessDependencies;
    var features;
    var edges;
    var edge;
    var node;
    var dep;
    var i, l;
    var resolved = [];

    // resolve dependant properties
    if(dependencies) {
        edges = hashToEdges(dependencies, preprocess);

        for (i = 0, l = edges.length; i < l; i++) {
            edge = edges[i];
            node = edge[0];
            dep = edge[1];

            if (preprocess[node] && !preprocess[dep]) {
                preprocess[dep] = preprocessDefault[dep];

                resolved.push(dep);

                edges.slice(i, 1);
                l = edges.length;
                i = -1;
            }
        }
    }

    // set features
    features = Object.keys(preprocess);
    if(dependencies) {
        edges = hashToEdges(dependencies, preprocess);
        features = toposort.array(features, edges).reverse();
    }

    config.preprocessFeatures = features;

    gutil.log(chalk.green('Resolved ') + chalk.yellow(resolved.length) + chalk.green(' Dependencies: ') + chalk.yellow(resolved.join(chalk.green(', '))));
}

/**
 * A hash containing features and their respective dependencies.
 *
 * **Note:** This has also build in support for conditional dependencies.
 *
 * @typedef {Object.<string, Array.<string|{condition: string, value: string}>>} DependenciesHash
 */

/**
 * Turns a preprocessDependencies hash in an array of edges, consumable by toposort algorithm.
 *
 * **Note:** This has also build in support for conditional dependencies.
 *
 * @param {DependenciesHash} hash - The preprocessDependencies hash.
 * @param {Object} preprocess - The preprocess config object.
 * @returns {Array}
 */
function hashToEdges(hash, preprocess) {
    if(!hash) return;

    var keys = Object.keys(hash);
    var edges = [];
    var key;
    var deps;
    var dep;
    var condition;
    var i, l, j, k;

    for(i=0, l=keys.length; i<l; i++) {
        key = keys[i];
        deps = hash[key];

        for(j=0, k=deps.length; j<k; j++) {
            dep = deps[j];

            // check conditional dependency
            if(typeof dep === 'object') {
                condition = dep.condition;

                // if feature is disabled -> continue
                if(!preprocess || !preprocess[key] || !preprocess[key][condition])
                    continue;

                // else get dependency
                dep = dep.value;
            }

            edges.push([key, dep]);
        }
    }

    return edges;
}<|endoftext|>


<commit_before>var myVersion = "0.46";

var http = require ("http");
var AWS = require ("aws-sdk");
var s3 = new AWS.S3 ();
var urlpack = require ("url");
var dns = require ("dns");

var myPort, flRunningOnHeroku = false;

function consoleLog (s) {
	if (flRunningOnHeroku) { //they include the time in their console, no need to repeat
		console.log (s);
		}
	else {
		console.log (new Date ().toLocaleTimeString () + " -- " + s);
		}
	}
function tcpGetMyIpAddress () {
	var interfaces = require ("os").networkInterfaces ();
	for (var devName in interfaces) {
		var iface = interfaces [devName];
		for (var i = 0; i < iface.length; i++) {
			var alias = iface [i];
			if (alias.family === 'IPv4' && alias.address !== '127.0.0.1' && !alias.internal)
				return (alias.address);
				}
		}
	return ("0.0.0.0");
	}
function isAlpha (ch) {
	return (((ch >= 'a') && (ch <= 'z')) || ((ch >= 'A') && (ch <= 'Z')));
	}
function isNumeric (ch) {
	return ((ch >= '0') && (ch <= '9'));
	}
function sameDay (d1, d2) {
	//returns true if the two dates are on the same day
	d1 = new Date (d1);
	d2 = new Date (d2);
	return ((d1.getFullYear () == d2.getFullYear ()) && (d1.getMonth () == d2.getMonth ()) && (d1.getDate () == d2.getDate ()));
	}
function padWithZeros (num, ctplaces) {
	var s = num.toString ();
	while (s.length < ctplaces) {
		s = "0" + s;
		}
	return (s);
	}
function getDatePath (theDate, flLastSeparator) {
	if (theDate == undefined) {
		theDate = new Date ();
		}
	if (flLastSeparator == undefined) {
		flLastSeparator = true;
		}

	var month = padWithZeros (theDate.getMonth () + 1, 2);
	var day = padWithZeros (theDate.getDate (), 2);
	var year = theDate.getFullYear ();

	if (flLastSeparator) {
		return (year + "/" + month + "/" + day + "/");
		}
	else {
		return (year + "/" + month + "/" + day);
		}
	}
function cleanName (name) {
	var s = "";
	if (name == undefined) {
		return ("");
		}
	for (var i = 0; i < name.length; i++) {
		var ch = name [i];
		if (isAlpha (ch) || isNumeric (ch)) {
			s += ch;
			}
		}
	return (s.toLowerCase (s));
	}
function okparam (param) {
	return ((param != undefined) && (param.length > 0));
	}
function s3SplitPath (path) { //split path like this: /tmp.scripting.com/testing/one.txt -- into bucketname and path.
	var bucketname = "";
	if (path.length > 0) {
		if (path [0] == "/") { //delete the slash
			path = path.substr (1);
			}
		var ix = path.indexOf ("/");
		bucketname = path.substr (0, ix);
		path = path.substr (ix + 1);
		}
	return ({Bucket: bucketname, Key: path});
	}
function s3GetObject (path, callback) {
	var params = s3SplitPath (path);
	s3.getObject (params, function (err, data) {
		callback (data);
		});
	}
function s3NewObject (path, data, type, acl, callback) {
	var splitpath = s3SplitPath (path);
	if (type == undefined) {
		type = s3defaultType;
		}
	if (acl == undefined) {
		acl = s3defaultAcl;
		}
	var params = {
		ACL: acl,
		ContentType: type,
		Body: data,
		Bucket: splitpath.Bucket,
		Key: splitpath.Key
		};
	s3.putObject (params, function (err, data) {
		if (callback != undefined) {
			callback (err, data);
			}
		});
	}
function s3getJsonObject (path, callback) {
	s3GetObject (path, function (data) {
		if (data == null) {
			callback (new Object ());
			}
		else {
			callback (JSON.parse (data.Body));
			}
		});
	}
function s3putJsonObject (path, obj, callback) {
	var jsontext = JSON.stringify (obj, undefined, 3);
	s3NewObject (path, jsontext, "text/plain", "public-read", function (err, data) {
		if (err != null) {
			consoleLog ("s3putJsonObject: error: " + err.message);
			}
		else {
			if (callback != undefined) {
				callback (err, data);
				}
			}
		});
	}

var myIpAddress = tcpGetMyIpAddress ();
var myServerName = process.env.counterServerName;

if (myServerName == undefined) {
	myServerName = myIpAddress;
	}

var s3appPath = "/static.scripting.com/counters/";

var s3folderpath = s3appPath + myServerName + "/";

var server = http.createServer (function (httpRequest, httpResponse) {
	var parsedUrl = urlpack.parse (httpRequest.url, true);

	consoleLog (httpRequest.url);

	switch (parsedUrl.pathname.toLowerCase ()) {
		case "/version":
			httpResponse.writeHead (200, {"Content-Type": "text/plain"});
			httpResponse.end (myVersion);
			return;
		case "/counter":
			var url = parsedUrl.query.referer, now = new Date ();

			if (okparam (url) && okparam (parsedUrl.query.group)) {
				var group = cleanName (parsedUrl.query.group);
				var s3groupPath = s3appPath + group + "/";
				var s3todayPath = s3groupPath + "today.json";
				var s3archivePath = s3groupPath + getDatePath (now, false) + ".json";
				s3getJsonObject (s3todayPath, function (obj) {
					var lowerurl = url.toLowerCase ();

					//stats
						if (obj.ctUpdates == undefined) {
							obj.ctUpdates = 0;
							}
						if (obj.ctUpdatesToday == undefined) {
							obj.ctUpdatesToday = 0;
							}
						obj.ctUpdates++;
						obj.ctUpdatesToday++;
					//date rollover
						var nowstring = now.toUTCString ();
						if (obj.theDate == undefined) {
							obj.theDate = nowstring;
							}
						else {
							if (!sameDay (obj.theDate, now)) {
								obj = new Object ();
								obj.theDate = nowstring;
								obj.ctUpdatesToday = 0;
								consoleLog ("Rollover.");
								}
							}
						obj.whenLastUpdate = nowstring;

					if (obj.urls == undefined) {
						obj.urls = new Array ();
						}
					var flfound = false, len = obj.urls.length;
					for (var i = 0; i < len; i++) {
						if (obj.urls [i].url == url) {
							obj.urls [i].ct++;
							flfound = true;
							break;
							}
						}
					if (!flfound) {
						var newobj = new Object ();
						newobj.ct = 1;
						newobj.url = url;
						obj.urls [len] = newobj;
						}

					consoleLog ("Counter: referrer == " + url);

					s3putJsonObject (s3todayPath, obj, function (err, data) {
						s3putJsonObject (s3archivePath, obj);
						});
					});
				}

			//set up the http response
				httpResponse.writeHead (200, {"Content-Type": "application/json"});
				var x = {"message": "We got your ping on " + now};
				var s = "getData (" + JSON.stringify (x) + ")";
				httpResponse.end (s);

			return
		}
	});

if (process.env.PORT == undefined) { //it's not Heroku
	if (process.env.fpServerPort == undefined) {
		myPort = 5337;
		}
	else {
		myPort = process.env.fpServerPort;
		}
	}
else {
	myPort = process.env.PORT;
	flRunningOnHeroku = true;
	}

consoleLog ("Counter server v" + myVersion + " running on port " + myPort + ".");

server.listen (myPort);
<commit_msg>Update<commit_after>var myVersion = "0.47";

var http = require ("http");
var AWS = require ("aws-sdk");
var s3 = new AWS.S3 ();
var urlpack = require ("url");
var dns = require ("dns");

var myPort, flRunningOnHeroku = false;

function consoleLog (s) {
	if (flRunningOnHeroku) { //they include the time in their console, no need to repeat
		console.log (s);
		}
	else {
		console.log (new Date ().toLocaleTimeString () + " -- " + s);
		}
	}
function tcpGetMyIpAddress () {
	var interfaces = require ("os").networkInterfaces ();
	for (var devName in interfaces) {
		var iface = interfaces [devName];
		for (var i = 0; i < iface.length; i++) {
			var alias = iface [i];
			if (alias.family === 'IPv4' && alias.address !== '127.0.0.1' && !alias.internal)
				return (alias.address);
				}
		}
	return ("0.0.0.0");
	}
function isAlpha (ch) {
	return (((ch >= 'a') && (ch <= 'z')) || ((ch >= 'A') && (ch <= 'Z')));
	}
function isNumeric (ch) {
	return ((ch >= '0') && (ch <= '9'));
	}
function sameDay (d1, d2) {
	//returns true if the two dates are on the same day
	d1 = new Date (d1);
	d2 = new Date (d2);
	return ((d1.getFullYear () == d2.getFullYear ()) && (d1.getMonth () == d2.getMonth ()) && (d1.getDate () == d2.getDate ()));
	}
function padWithZeros (num, ctplaces) {
	var s = num.toString ();
	while (s.length < ctplaces) {
		s = "0" + s;
		}
	return (s);
	}
function getDatePath (theDate, flLastSeparator) {
	if (theDate == undefined) {
		theDate = new Date ();
		}
	if (flLastSeparator == undefined) {
		flLastSeparator = true;
		}

	var month = padWithZeros (theDate.getMonth () + 1, 2);
	var day = padWithZeros (theDate.getDate (), 2);
	var year = theDate.getFullYear ();

	if (flLastSeparator) {
		return (year + "/" + month + "/" + day + "/");
		}
	else {
		return (year + "/" + month + "/" + day);
		}
	}
function cleanName (name) {
	var s = "";
	if (name == undefined) {
		return ("");
		}
	for (var i = 0; i < name.length; i++) {
		var ch = name [i];
		if (isAlpha (ch) || isNumeric (ch)) {
			s += ch;
			}
		}
	return (s.toLowerCase (s));
	}
function okparam (param) {
	return ((param != undefined) && (param.length > 0));
	}
function s3SplitPath (path) { //split path like this: /tmp.scripting.com/testing/one.txt -- into bucketname and path.
	var bucketname = "";
	if (path.length > 0) {
		if (path [0] == "/") { //delete the slash
			path = path.substr (1);
			}
		var ix = path.indexOf ("/");
		bucketname = path.substr (0, ix);
		path = path.substr (ix + 1);
		}
	return ({Bucket: bucketname, Key: path});
	}
function s3GetObject (path, callback) {
	var params = s3SplitPath (path);
	s3.getObject (params, function (err, data) {
		callback (data);
		});
	}
function s3NewObject (path, data, type, acl, callback) {
	var splitpath = s3SplitPath (path);
	if (type == undefined) {
		type = s3defaultType;
		}
	if (acl == undefined) {
		acl = s3defaultAcl;
		}
	var params = {
		ACL: acl,
		ContentType: type,
		Body: data,
		Bucket: splitpath.Bucket,
		Key: splitpath.Key
		};
	s3.putObject (params, function (err, data) {
		if (callback != undefined) {
			callback (err, data);
			}
		});
	}
function s3getJsonObject (path, callback) {
	s3GetObject (path, function (data) {
		if (data == null) {
			callback (new Object ());
			}
		else {
			callback (JSON.parse (data.Body));
			}
		});
	}
function s3putJsonObject (path, obj, callback) {
	var jsontext = JSON.stringify (obj, undefined, 3);
	s3NewObject (path, jsontext, "text/plain", "public-read", function (err, data) {
		if (err != null) {
			consoleLog ("s3putJsonObject: error: " + err.message);
			}
		else {
			if (callback != undefined) {
				callback (err, data);
				}
			}
		});
	}

var myIpAddress = tcpGetMyIpAddress ();
var myServerName = process.env.counterServerName;

if (myServerName == undefined) {
	myServerName = myIpAddress;
	}

var s3appPath = "/static.scripting.com/counters/";

var s3folderpath = s3appPath + myServerName + "/";

if (process.env.PORT == undefined) { //it's not Heroku
	if (process.env.fpServerPort == undefined) {
		myPort = 5337;
		}
	else {
		myPort = process.env.fpServerPort;
		}
	}
else {
	myPort = process.env.PORT;
	flRunningOnHeroku = true;
	}

consoleLog ("Counter server v" + myVersion + " running on port " + myPort + ".");

http.createServer (function (httpRequest, httpResponse) {
	var parsedUrl = urlpack.parse (httpRequest.url, true);

	consoleLog (httpRequest.url);

	switch (parsedUrl.pathname.toLowerCase ()) {
		case "/version":
			httpResponse.writeHead (200, {"Content-Type": "text/plain"});
			httpResponse.end (myVersion);
			return;
		case "/counter":
			var url = parsedUrl.query.referer, now = new Date ();

			if (okparam (url) && okparam (parsedUrl.query.group)) {
				var group = cleanName (parsedUrl.query.group);
				var s3groupPath = s3appPath + group + "/";
				var s3todayPath = s3groupPath + "today.json";
				var s3archivePath = s3groupPath + getDatePath (now, false) + ".json";
				s3getJsonObject (s3todayPath, function (obj) {
					var lowerurl = url.toLowerCase ();

					//stats
						if (obj.ctUpdates == undefined) {
							obj.ctUpdates = 0;
							}
						if (obj.ctUpdatesToday == undefined) {
							obj.ctUpdatesToday = 0;
							}
						obj.ctUpdates++;
						obj.ctUpdatesToday++;
					//date rollover
						var nowstring = now.toUTCString ();
						if (obj.theDate == undefined) {
							obj.theDate = nowstring;
							}
						else {
							if (!sameDay (obj.theDate, now)) {
								obj = new Object ();
								obj.theDate = nowstring;
								obj.ctUpdatesToday = 0;
								consoleLog ("Rollover.");
								}
							}
						obj.whenLastUpdate = nowstring;

					if (obj.urls == undefined) {
						obj.urls = new Array ();
						}
					var flfound = false, len = obj.urls.length;
					for (var i = 0; i < len; i++) {
						if (obj.urls [i].url == url) {
							obj.urls [i].ct++;
							flfound = true;
							break;
							}
						}
					if (!flfound) {
						var newobj = new Object ();
						newobj.ct = 1;
						newobj.url = url;
						obj.urls [len] = newobj;
						}

					consoleLog ("Counter: referrer == " + url);

					s3putJsonObject (s3todayPath, obj, function (err, data) {
						s3putJsonObject (s3archivePath, obj);
						});
					});
				}

			//set up the http response
				httpResponse.writeHead (200, {"Content-Type": "application/json"});
				var x = {"message": "We got your ping on " + now};
				var s = "getData (" + JSON.stringify (x) + ")";
				httpResponse.end (s);

			return
		}
	}).listen (myPort);

<|endoftext|>