<commit_before>const Promise = require('bluebird');
const _ = require('lodash');
const rp = require('request-promise');
const StellarSdk = require('stellar-sdk');
const niceRound = require('./utils/niceRound');
const tradeWalker = require('./utils/tradeWalker');

const PQueue = require('p-queue');
const queue = new PQueue({concurrency: 20});
const run = queue.add;

const directory = require('../directory.json');

Server = new StellarSdk.Server('https://horizon.stellar.org');
StellarSdk.Network.usePublicNetwork();

function tickerGenerator() {
  let finish;
  let ticker = {
    _meta: {
      start: Math.floor(Date.now()/1000),
      startISO: Date(),
      apiLicense: 'Apache-2.0',
    },
  };

  let tickerPromise = Promise.resolve()
    .then(() => phase1(ticker))
    .then(() => loadAssets(ticker))
    .then(() => phase3(ticker))
    .then(() => {
      return {
        'v1/ticker.json': JSON.stringify(ticker, null, 2)
      };
    })

  return tickerPromise;
}

function phase1(ticker) {
  return Promise.all([
    getHorizonMain()
      .then(main => {
        ticker._meta.horizon = {
          core_latest_ledger: main.core_latest_ledger,
          network_passphrase: main.network_passphrase,
        }
      })
    ,
    getExternalPrices()
      .then(externalPrices => {
        ticker._meta.externalPrices = externalPrices;
      })
  ])
}

function loadAssets(ticker) {
  ticker.assets = [];

  ticker.assets.push({
    id: 'XLM-native',
    code: 'XLM',
    issuer: null,
    domain: 'native',
    slug: 'XLM-native',
    website: 'https://www.stellar.org/lumens/',
    price_XLM: 1, // LOL 1 dogecoin = 1 dogecoin; 1 lumen = 1 lumen
    price_USD: ticker._meta.externalPrices.USD_XLM,
  })
  _.each(directory.assets, (asset, id) => {
    let r = {};
    r.id = id;
    r.code = asset.code;
    r.issuer = asset.issuer;
    r.domain = asset.domain;
    r.slug = asset.code + '-' + asset.domain;
    r.website = directory.anchors[asset.domain].website;
    ticker.assets.push(r)
  });
}

function phase3(ticker) {
  ticker.pairs = {};
  _.each(directory.pairs, (pairData, id) => {
    ticker.pairs[id] = {
      baseBuying: pairData.baseBuying,
      counterSelling: pairData.counterSelling,
    };
  });

  return Promise.all(_.map(ticker.pairs, pair => {
    let baseBuying     = new StellarSdk.Asset(pair.baseBuying.code, pair.baseBuying.issuer);
    let counterSelling = new StellarSdk.Asset(pair.counterSelling.code, pair.counterSelling.issuer);
    return Server.orderbook(baseBuying, counterSelling).call()
      .then((res) => {
        if (res.bids.length === 0 || res.asks.length === 0) {
          return;
        }
        pair.bid = _.round(res.bids[0].price, 7);
        pair.ask = _.round(res.asks[0].price, 7);
        // pair.spread = _.round(1 - pair.bid/pair.ask, 4);
        pair.price = _.round((parseFloat(pair.bid) + parseFloat(pair.ask))/2, 7);
        if (baseBuying.isNative()) {
          asset = _.find(ticker.assets, {
            code: pair.counterSelling.code,
            issuer: pair.counterSelling.issuer,
          });
          asset.price_XLM = niceRound(1/pair.price);
          asset.price_USD = niceRound(1/pair.price * ticker._meta.externalPrices.USD_XLM);
        } else if (counterSelling.isNative()) {
          asset = _.find(ticker.assets, {
            code: pair.baseBuying.code,
            issuer: pair.baseBuying.issuer,
          });
          asset.price_XLM = niceRound(pair.price);
          asset.price_USD = niceRound(pair.price * ticker._meta.externalPrices.USD_XLM);
        }

        return tradeWalker.walkUntil(Server, pair.baseBuying, pair.counterSelling, 86400)
          .then(tradesList => {
            pair.numTrades24h = tradesList.length;
          })
      })
  }));
}

function getExternalPrices() {
  return Promise.all([
    getBtcPrice(),
    getLumenPrice(),
  ])
  .then(externalData => {
    return {
      USD_BTC: externalData[0],
      BTC_XLM: externalData[1],
      USD_XLM: _.round(externalData[0]*externalData[1],6),
    }
  })
}

function getBtcPrice() {
  return Promise.all([
    rp('https://api.coindesk.com/v1/bpi/currentprice.json')
      .then(data => {
        return _.round(JSON.parse(data).bpi.USD.rate_float, 3);
      })
      .catch(() => {
        return null;
      })
    ,
    rp('https://api.bitfinex.com/v2/ticker/tBTCUSD')
      .then(data => {
        return _.round(JSON.parse(data)[2], 3);
      })
      .catch(() => {
        return null;
      })
    ,
    rp('https://api.coinbase.com/v2/prices/spot?currency=USD')
      .then(data => {
        return _.round(JSON.parse(data).data.amount, 3);
      })
      .catch(() => {
        return null;
      })
    ,
    rp('https://api.kraken.com/0/public/Ticker?pair=XBTUSD')
      .then(data => {
        return _.round(JSON.parse(data).result.XXBTZUSD.c[0], 3);
      })
      .catch(() => {
        return null;
      })
  ])
  .then(allPrices => {
    return _.round(_.mean(_.filter(allPrices, price => price !== null)), 2);
  })
}

// Get lumen price in terms of btc
function getLumenPrice() {
  return Promise.all([
    rp('https://poloniex.com/public?command=returnTicker')
      .then(data => {
        return parseFloat(JSON.parse(data).BTC_STR.last);
      })
      .catch(() => {
        return null;
      })
    ,
    rp('https://bittrex.com/api/v1.1/public/getticker?market=BTC-XLM')
      .then(data => {
        return parseFloat(JSON.parse(data).result.Last);
      })
      .catch(() => {
        return null;
      })
    ,
    rp('https://api.kraken.com/0/public/Ticker?pair=XLMXBT')
      .then(data => {
        return parseFloat(JSON.parse(data).result.XXLMXXBT.c[0]);
      })
      .catch(() => {
        return null;
      })
  ])
  .then(allPrices => {
    return _.round(_.mean(_.filter(allPrices, price => price !== null)), 8);
  })
}

function getHorizonMain() {
  return rp('https://horizon.stellar.org/')
    .then(horizonMain => {
      return JSON.parse(horizonMain);
    })
}

module.exports = tickerGenerator;
<commit_msg>Add 24h volume calculator to ticker<commit_after>const Promise = require('bluebird');
const _ = require('lodash');
const rp = require('request-promise');
const StellarSdk = require('stellar-sdk');
const niceRound = require('./utils/niceRound');
const tradeWalker = require('./utils/tradeWalker');

const PQueue = require('p-queue');
const queue = new PQueue({concurrency: 20});
const run = queue.add;

const directory = require('../directory.json');

Server = new StellarSdk.Server('https://horizon.stellar.org');
StellarSdk.Network.usePublicNetwork();

function tickerGenerator() {
  let finish;
  let ticker = {
    _meta: {
      start: Math.floor(Date.now()/1000),
      startISO: Date(),
      apiLicense: 'Apache-2.0',
    },
  };

  let tickerPromise = Promise.resolve()
    .then(() => phase1(ticker))
    .then(() => loadAssets(ticker))
    .then(() => phase3(ticker))
    .then(() => {
      return {
        'v1/ticker.json': JSON.stringify(ticker, null, 2)
      };
    })

  return tickerPromise;
}

function phase1(ticker) {
  return Promise.all([
    getHorizonMain()
      .then(main => {
        ticker._meta.horizon = {
          core_latest_ledger: main.core_latest_ledger,
          network_passphrase: main.network_passphrase,
        }
      })
    ,
    getExternalPrices()
      .then(externalPrices => {
        ticker._meta.externalPrices = externalPrices;
      })
  ])
}

function loadAssets(ticker) {
  ticker.assets = [];

  ticker.assets.push({
    id: 'XLM-native',
    code: 'XLM',
    issuer: null,
    domain: 'native',
    slug: 'XLM-native',
    website: 'https://www.stellar.org/lumens/',
    price_XLM: 1, // LOL 1 dogecoin = 1 dogecoin; 1 lumen = 1 lumen
    price_USD: ticker._meta.externalPrices.USD_XLM,
  })
  _.each(directory.assets, (asset, id) => {
    let r = {};
    r.id = id;
    r.code = asset.code;
    r.issuer = asset.issuer;
    r.domain = asset.domain;
    r.slug = asset.code + '-' + asset.domain;
    r.website = directory.anchors[asset.domain].website;
    ticker.assets.push(r)
  });
}

function phase3(ticker) {
  ticker.pairs = {};
  _.each(directory.pairs, (pairData, id) => {
    ticker.pairs[id] = {
      baseBuying: pairData.baseBuying,
      counterSelling: pairData.counterSelling,
    };
  });

  return Promise.all(_.map(ticker.pairs, pair => {
    let baseBuying     = new StellarSdk.Asset(pair.baseBuying.code, pair.baseBuying.issuer);
    let counterSelling = new StellarSdk.Asset(pair.counterSelling.code, pair.counterSelling.issuer);
    return Server.orderbook(baseBuying, counterSelling).call()
      .then((res) => {
        if (res.bids.length === 0 || res.asks.length === 0) {
          return;
        }
        pair.bid = _.round(res.bids[0].price, 7);
        pair.ask = _.round(res.asks[0].price, 7);
        // pair.spread = _.round(1 - pair.bid/pair.ask, 4);
        pair.price = _.round((parseFloat(pair.bid) + parseFloat(pair.ask))/2, 7);

        return tradeWalker.walkUntil(Server, pair.baseBuying, pair.counterSelling, 86400)
          .then(tradesList => {
            pair.numTrades24h = tradesList.length;

            if (baseBuying.isNative()) {
              asset = _.find(ticker.assets, {
                code: pair.counterSelling.code,
                issuer: pair.counterSelling.issuer,
              });
              asset.price_XLM = niceRound(1/pair.price);
              asset.price_USD = niceRound(1/pair.price * ticker._meta.externalPrices.USD_XLM);
              pair.volume24h_XLM = niceRound(_.sumBy(tradesList, 'boughtAmount'));
              asset.volume24h_XLM = pair.volume_XLM;
              asset.volume24h_USD = niceRound(pair.volume_XLM * ticker._meta.externalPrices.USD_XLM);
            } else if (counterSelling.isNative()) {
              asset = _.find(ticker.assets, {
                code: pair.baseBuying.code,
                issuer: pair.baseBuying.issuer,
              });
              asset.price_XLM = niceRound(pair.price);
              asset.price_USD = niceRound(pair.price * ticker._meta.externalPrices.USD_XLM);
              pair.volume24h_XLM = niceRound(_.sumBy(tradesList, 'soldAmount'));
              asset.volume24h_XLM = pair.volume_XLM;
              asset.volume24h_USD = niceRound(pair.volume_XLM * ticker._meta.externalPrices.USD_XLM);
            }
          })
      })
  }));
}

function getExternalPrices() {
  return Promise.all([
    getBtcPrice(),
    getLumenPrice(),
  ])
  .then(externalData => {
    return {
      USD_BTC: externalData[0],
      BTC_XLM: externalData[1],
      USD_XLM: _.round(externalData[0]*externalData[1],6),
    }
  })
}

function getBtcPrice() {
  return Promise.all([
    rp('https://api.coindesk.com/v1/bpi/currentprice.json')
      .then(data => {
        return _.round(JSON.parse(data).bpi.USD.rate_float, 3);
      })
      .catch(() => {
        return null;
      })
    ,
    rp('https://api.bitfinex.com/v2/ticker/tBTCUSD')
      .then(data => {
        return _.round(JSON.parse(data)[2], 3);
      })
      .catch(() => {
        return null;
      })
    ,
    rp('https://api.coinbase.com/v2/prices/spot?currency=USD')
      .then(data => {
        return _.round(JSON.parse(data).data.amount, 3);
      })
      .catch(() => {
        return null;
      })
    ,
    rp('https://api.kraken.com/0/public/Ticker?pair=XBTUSD')
      .then(data => {
        return _.round(JSON.parse(data).result.XXBTZUSD.c[0], 3);
      })
      .catch(() => {
        return null;
      })
  ])
  .then(allPrices => {
    return _.round(_.mean(_.filter(allPrices, price => price !== null)), 2);
  })
}

// Get lumen price in terms of btc
function getLumenPrice() {
  return Promise.all([
    rp('https://poloniex.com/public?command=returnTicker')
      .then(data => {
        return parseFloat(JSON.parse(data).BTC_STR.last);
      })
      .catch(() => {
        return null;
      })
    ,
    rp('https://bittrex.com/api/v1.1/public/getticker?market=BTC-XLM')
      .then(data => {
        return parseFloat(JSON.parse(data).result.Last);
      })
      .catch(() => {
        return null;
      })
    ,
    rp('https://api.kraken.com/0/public/Ticker?pair=XLMXBT')
      .then(data => {
        return parseFloat(JSON.parse(data).result.XXLMXXBT.c[0]);
      })
      .catch(() => {
        return null;
      })
  ])
  .then(allPrices => {
    return _.round(_.mean(_.filter(allPrices, price => price !== null)), 8);
  })
}

function getHorizonMain() {
  return rp('https://horizon.stellar.org/')
    .then(horizonMain => {
      return JSON.parse(horizonMain);
    })
}

module.exports = tickerGenerator;
<|endoftext|>


<commit_before>/**
 * Monitor
 * Pokemon Showdown - http://pokemonshowdown.com/
 *
 * Various utility functions to make sure PS is running healthily.
 *
 * @license MIT license
 */
'use strict';

const FS = require('./lib/fs');

const MONITOR_CLEAN_TIMEOUT = 2 * 60 * 60 * 1000;

/**
 * This counts the number of times an action has been committed, and tracks the
 * delta of time since the last time it was committed. Actions include
 * connecting to the server, starting a battle, validating a team, and
 * sending/receiving data over a connection's socket.
 * @augments {Map<string, [number, number]>}
 */
// @ts-ignore TypeScript bug
class TimedCounter extends Map {
	/**
	 * Increments the number of times an action has been committed by one, and
	 * updates the delta of time since it was last committed.
	 *
	 * @param {string} key
	 * @param {number} timeLimit
	 * @return {[number, number]} - [action count, time delta]
	 */
	increment(key, timeLimit) {
		let val = this.get(key);
		let now = Date.now();
		if (!val || now > val[1] + timeLimit) {
			this.set(key, [1, Date.now()]);
			return [1, 0];
		} else {
			val[0]++;
			return [val[0], now - val[1]];
		}
	}
}

// Config.loglevel is:
// 0 = everything
// 1 = debug (same as 0 for now)
// 2 = notice (default)
// 3 = warning
// (4 is currently unused)
// 5 = supposedly completely silent, but for now a lot of PS output doesn't respect loglevel
if (('Config' in global) &&
		(typeof Config.loglevel !== 'number' || Config.loglevel < 0 || Config.loglevel > 5)) {
	Config.loglevel = 2;
}

// @ts-ignore
const Monitor = module.exports = {
	/*********************************************************
	 * Logging
	 *********************************************************/

	/** @param {Error} error */
	crashlog(error, source = 'The main process') {
		require('./lib/crashlogger')(error, source);
	},

	/**
	 * @param {string} text
	 */
	log(text) {
		this.notice(text);
		if (Rooms('staff')) {
			Rooms('staff').add(`|c|~|${text}`).update();
		}
	},

	/**
	 * @param {string} text
	 */
	adminlog(text) {
		this.notice(text);
		if (Rooms('upperstaff')) {
			Rooms('upperstaff').add(`|c|~|${text}`).update();
		}
	},

	/**
	 * @param {string} text
	 */
	logHTML(text) {
		this.notice(text);
		if (Rooms('staff')) {
			Rooms('staff').add(`|html|${text}`).update();
		}
	},

	/**
	 * @param {string} text
	 */
	debug(text) {
		if (Config.loglevel <= 1) console.log(text);
	},

	/**
	 * @param {string} text
	 */
	warn(text) {
		if (Config.loglevel <= 3) console.log(text);
	},

	/**
	 * @param {string} text
	 */
	notice(text) {
		if (Config.loglevel <= 2) console.log(text);
	},

	/*********************************************************
	 * Resource Monitor
	 *********************************************************/

	clean() {
		this.clearNetworkUse();
		this.battlePreps.clear();
		this.battles.clear();
		this.connections.clear();
		Dnsbl.cache.clear();
	},

	connections: new TimedCounter(),
	battles: new TimedCounter(),
	battlePreps: new TimedCounter(),
	groupChats: new TimedCounter(),
	tickets: new TimedCounter(),

	/** @type {string | null} */
	activeIp: null,
	networkUse: {},
	networkCount: {},
	hotpatchLock: {},

	/**
	 * Counts a connection. Returns true if the connection should be terminated for abuse.
	 *
	 * @param {string} ip
	 * @param {string} [name = '']
	 * @return {boolean}
	 */
	countConnection(ip, name = '') {
		let [count, duration] = this.connections.increment(ip, 30 * 60 * 1000);
		if (count === 500) {
			this.adminlog(`[ResourceMonitor] IP ${ip} banned for cflooding (${count} times in ${Chat.toDurationString(duration)}${name ? ': ' + name : ''})`);
			return true;
		}

		if (count > 500) {
			if (count % 500 === 0) {
				let c = count / 500;
				if (c === 2 || c === 4 || c === 10 || c === 20 || c % 40 === 0) {
					this.adminlog(`[ResourceMonitor] IP ${ip} still cflooding (${count} times in ${Chat.toDurationString(duration)}${name ? ': ' + name : ''})`);
				}
			}
			return true;
		}

		return false;
	},

	/**
	 * Counts battles created. Returns true if the connection should be
	 * terminated for abuse.
	 *
	 * @param {string} ip
	 * @param {string} [name = '']
	 * @return {boolean}
	 */
	countBattle(ip, name = '') {
		let [count, duration] = this.battles.increment(ip, 30 * 60 * 1000);
		if (duration < 5 * 60 * 1000 && count % 30 === 0) {
			this.adminlog(`[ResourceMonitor] IP ${ip} has battled ${count} times in the last ${Chat.toDurationString(duration)}${name ? ': ' + name : ''})`);
			return true;
		}

		if (count % 150 === 0) {
			this.adminlog(`[ResourceMonitor] IP ${ip} has battled ${count} times in the last ${Chat.toDurationString(duration)}${name ? ': ' + name : ''}`);
			return true;
		}

		return false;
	},

	/**
	 * Counts team validations. Returns true if too many.
	 *
	 * @param {string} ip
	 * @param {Connection} connection
	 * @return {boolean}
	 */
	countPrepBattle(ip, connection) {
		let count = this.battlePreps.increment(ip, 3 * 60 * 1000)[0];
		if (count <= 12) return false;
		if (count < 120 && Punishments.sharedIps.has(ip)) return false;
		connection.popup('Due to high load, you are limited to 12 battles and team validations every 3 minutes.');
		return true;
	},

	/**
	 * Counts concurrent battles. Returns true if too many.
	 *
	 * @param {number} count
	 * @param {Connection} connection
	 * @return {boolean}
	 */
	countConcurrentBattle(count, connection) {
		if (count <= 5) return false;
		connection.popup(`Due to high load, you are limited to 5 games at the same time.`);
		return true;
	},
	/**
	 * Counts group chat creation. Returns true if too much.
	 *
	 * @param {string} ip
	 * @return {boolean}
	 */
	countGroupChat(ip) {
		let count = this.groupChats.increment(ip, 60 * 60 * 1000)[0];
		return count > 4;
	},

	/**
	 * Counts ticket creation. Returns true if too much.
	 *
	 * @param {string} ip
	 * @return {boolean}
	 */
	countTickets(ip) {
		let count = this.tickets.increment(ip, 60 * 60 * 1000)[0];
		if (Punishments.sharedIps.has(ip) && count >= 50) return true;
		if (count >= 5) return true;
		return false;
	},

	/**
	 * Counts the data length received by the last connection to send a
	 * message, as well as the data length in the server's response.
	 *
	 * @param {number} size
	 */
	countNetworkUse(size) {
		if (!Config.emergency || typeof this.activeIp !== 'string') return;
		if (this.activeIp in this.networkUse) {
			this.networkUse[this.activeIp] += size;
			this.networkCount[this.activeIp]++;
		} else {
			this.networkUse[this.activeIp] = size;
			this.networkCount[this.activeIp] = 1;
		}
	},

	writeNetworkUse() {
		let buf = '';
		for (let i in this.networkUse) {
			buf += `${this.networkUse[i]}\t${this.networkCount[i]}\t${i}\n`;
		}
		FS('logs/networkuse.tsv').write(buf);
	},

	clearNetworkUse() {
		if (Config.emergency) {
			this.networkUse = {};
			this.networkCount = {};
		}
	},

	/**
	 * Counts roughly the size of an object to have an idea of the server load.
	 *
	 * @param {any} object
	 * @return {number}
	 */
	sizeOfObject(object) {
		/** @type {Set<(Array | Object)>} */
		let objectCache = new Set();
		let stack = [object];
		let bytes = 0;

		while (stack.length) {
			let value = stack.pop();
			switch (typeof value) {
			case 'boolean':
				bytes += 4;
				break;
			case 'string':
				bytes += value.length * 2;
				break;
			case 'number':
				bytes += 8;
				break;
			case 'object':
				if (!objectCache.has(value)) objectCache.add(value);
				if (Array.isArray(value)) {
					for (let el of value) stack.push(el);
				} else {
					for (let i in value) stack.push(value[i]);
				}
				break;
			}
		}

		return bytes;
	},

	/** @type {{new(entries: [any, [number, number]]): TimedCounter}} */
	TimedCounter,
};

Monitor.cleanInterval = setInterval(() => Monitor.clean(), MONITOR_CLEAN_TIMEOUT);
<commit_msg>Make Monitor#adminLog log to staff room<commit_after>/**
 * Monitor
 * Pokemon Showdown - http://pokemonshowdown.com/
 *
 * Various utility functions to make sure PS is running healthily.
 *
 * @license MIT license
 */
'use strict';

const FS = require('./lib/fs');

const MONITOR_CLEAN_TIMEOUT = 2 * 60 * 60 * 1000;

/**
 * This counts the number of times an action has been committed, and tracks the
 * delta of time since the last time it was committed. Actions include
 * connecting to the server, starting a battle, validating a team, and
 * sending/receiving data over a connection's socket.
 * @augments {Map<string, [number, number]>}
 */
// @ts-ignore TypeScript bug
class TimedCounter extends Map {
	/**
	 * Increments the number of times an action has been committed by one, and
	 * updates the delta of time since it was last committed.
	 *
	 * @param {string} key
	 * @param {number} timeLimit
	 * @return {[number, number]} - [action count, time delta]
	 */
	increment(key, timeLimit) {
		let val = this.get(key);
		let now = Date.now();
		if (!val || now > val[1] + timeLimit) {
			this.set(key, [1, Date.now()]);
			return [1, 0];
		} else {
			val[0]++;
			return [val[0], now - val[1]];
		}
	}
}

// Config.loglevel is:
// 0 = everything
// 1 = debug (same as 0 for now)
// 2 = notice (default)
// 3 = warning
// (4 is currently unused)
// 5 = supposedly completely silent, but for now a lot of PS output doesn't respect loglevel
if (('Config' in global) &&
		(typeof Config.loglevel !== 'number' || Config.loglevel < 0 || Config.loglevel > 5)) {
	Config.loglevel = 2;
}

// @ts-ignore
const Monitor = module.exports = {
	/*********************************************************
	 * Logging
	 *********************************************************/

	/** @param {Error} error */
	crashlog(error, source = 'The main process') {
		require('./lib/crashlogger')(error, source);
	},

	/**
	 * @param {string} text
	 */
	log(text) {
		this.notice(text);
		if (Rooms('staff')) {
			Rooms('staff').add(`|c|~|${text}`).update();
		}
	},

	/**
	 * @param {string} text
	 */
	adminlog(text) {
		this.notice(text);
		if (Rooms('staff')) {
			Rooms('staff').add(`|c|~|${text}`).update();
		}
	},

	/**
	 * @param {string} text
	 */
	logHTML(text) {
		this.notice(text);
		if (Rooms('staff')) {
			Rooms('staff').add(`|html|${text}`).update();
		}
	},

	/**
	 * @param {string} text
	 */
	debug(text) {
		if (Config.loglevel <= 1) console.log(text);
	},

	/**
	 * @param {string} text
	 */
	warn(text) {
		if (Config.loglevel <= 3) console.log(text);
	},

	/**
	 * @param {string} text
	 */
	notice(text) {
		if (Config.loglevel <= 2) console.log(text);
	},

	/*********************************************************
	 * Resource Monitor
	 *********************************************************/

	clean() {
		this.clearNetworkUse();
		this.battlePreps.clear();
		this.battles.clear();
		this.connections.clear();
		Dnsbl.cache.clear();
	},

	connections: new TimedCounter(),
	battles: new TimedCounter(),
	battlePreps: new TimedCounter(),
	groupChats: new TimedCounter(),
	tickets: new TimedCounter(),

	/** @type {string | null} */
	activeIp: null,
	networkUse: {},
	networkCount: {},
	hotpatchLock: {},

	/**
	 * Counts a connection. Returns true if the connection should be terminated for abuse.
	 *
	 * @param {string} ip
	 * @param {string} [name = '']
	 * @return {boolean}
	 */
	countConnection(ip, name = '') {
		let [count, duration] = this.connections.increment(ip, 30 * 60 * 1000);
		if (count === 500) {
			this.adminlog(`[ResourceMonitor] IP ${ip} banned for cflooding (${count} times in ${Chat.toDurationString(duration)}${name ? ': ' + name : ''})`);
			return true;
		}

		if (count > 500) {
			if (count % 500 === 0) {
				let c = count / 500;
				if (c === 2 || c === 4 || c === 10 || c === 20 || c % 40 === 0) {
					this.adminlog(`[ResourceMonitor] IP ${ip} still cflooding (${count} times in ${Chat.toDurationString(duration)}${name ? ': ' + name : ''})`);
				}
			}
			return true;
		}

		return false;
	},

	/**
	 * Counts battles created. Returns true if the connection should be
	 * terminated for abuse.
	 *
	 * @param {string} ip
	 * @param {string} [name = '']
	 * @return {boolean}
	 */
	countBattle(ip, name = '') {
		let [count, duration] = this.battles.increment(ip, 30 * 60 * 1000);
		if (duration < 5 * 60 * 1000 && count % 30 === 0) {
			this.adminlog(`[ResourceMonitor] IP ${ip} has battled ${count} times in the last ${Chat.toDurationString(duration)}${name ? ': ' + name : ''})`);
			return true;
		}

		if (count % 150 === 0) {
			this.adminlog(`[ResourceMonitor] IP ${ip} has battled ${count} times in the last ${Chat.toDurationString(duration)}${name ? ': ' + name : ''}`);
			return true;
		}

		return false;
	},

	/**
	 * Counts team validations. Returns true if too many.
	 *
	 * @param {string} ip
	 * @param {Connection} connection
	 * @return {boolean}
	 */
	countPrepBattle(ip, connection) {
		let count = this.battlePreps.increment(ip, 3 * 60 * 1000)[0];
		if (count <= 12) return false;
		if (count < 120 && Punishments.sharedIps.has(ip)) return false;
		connection.popup('Due to high load, you are limited to 12 battles and team validations every 3 minutes.');
		return true;
	},

	/**
	 * Counts concurrent battles. Returns true if too many.
	 *
	 * @param {number} count
	 * @param {Connection} connection
	 * @return {boolean}
	 */
	countConcurrentBattle(count, connection) {
		if (count <= 5) return false;
		connection.popup(`Due to high load, you are limited to 5 games at the same time.`);
		return true;
	},
	/**
	 * Counts group chat creation. Returns true if too much.
	 *
	 * @param {string} ip
	 * @return {boolean}
	 */
	countGroupChat(ip) {
		let count = this.groupChats.increment(ip, 60 * 60 * 1000)[0];
		return count > 4;
	},

	/**
	 * Counts ticket creation. Returns true if too much.
	 *
	 * @param {string} ip
	 * @return {boolean}
	 */
	countTickets(ip) {
		let count = this.tickets.increment(ip, 60 * 60 * 1000)[0];
		if (Punishments.sharedIps.has(ip) && count >= 50) return true;
		if (count >= 5) return true;
		return false;
	},

	/**
	 * Counts the data length received by the last connection to send a
	 * message, as well as the data length in the server's response.
	 *
	 * @param {number} size
	 */
	countNetworkUse(size) {
		if (!Config.emergency || typeof this.activeIp !== 'string') return;
		if (this.activeIp in this.networkUse) {
			this.networkUse[this.activeIp] += size;
			this.networkCount[this.activeIp]++;
		} else {
			this.networkUse[this.activeIp] = size;
			this.networkCount[this.activeIp] = 1;
		}
	},

	writeNetworkUse() {
		let buf = '';
		for (let i in this.networkUse) {
			buf += `${this.networkUse[i]}\t${this.networkCount[i]}\t${i}\n`;
		}
		FS('logs/networkuse.tsv').write(buf);
	},

	clearNetworkUse() {
		if (Config.emergency) {
			this.networkUse = {};
			this.networkCount = {};
		}
	},

	/**
	 * Counts roughly the size of an object to have an idea of the server load.
	 *
	 * @param {any} object
	 * @return {number}
	 */
	sizeOfObject(object) {
		/** @type {Set<(Array | Object)>} */
		let objectCache = new Set();
		let stack = [object];
		let bytes = 0;

		while (stack.length) {
			let value = stack.pop();
			switch (typeof value) {
			case 'boolean':
				bytes += 4;
				break;
			case 'string':
				bytes += value.length * 2;
				break;
			case 'number':
				bytes += 8;
				break;
			case 'object':
				if (!objectCache.has(value)) objectCache.add(value);
				if (Array.isArray(value)) {
					for (let el of value) stack.push(el);
				} else {
					for (let i in value) stack.push(value[i]);
				}
				break;
			}
		}

		return bytes;
	},

	/** @type {{new(entries: [any, [number, number]]): TimedCounter}} */
	TimedCounter,
};

Monitor.cleanInterval = setInterval(() => Monitor.clean(), MONITOR_CLEAN_TIMEOUT);
<|endoftext|>


<commit_before>var VideoRecircList = function() {
  $videoRecircList = $('#video-list');
  this.videoItem = 'video-item';
  this.bettyUrl = $videoRecircList.data('betty-url');
  this.source = $videoRecircList.data('source');
  this.recircCount = $videoRecircList.data('count');
  this.init();
};

VideoRecircList.prototype.init = function() {
  this.loadVideoRecirc();
};

VideoRecircList.prototype.loadVideoRecirc = function() {
  $.getJSON(this.source, function(data) {
    $.each(data.results.slice(0,this.recircCount), function(index, video) {
      var videoTitle = video.title;
      var videoHref = '/v/' + video.id;
      var posterSource = $bettyUrl + '/' + video.poster.id;
      $('',{
        'class' : videoItem,
        'href' : videoHref,
        'data-track-action' : 'Video: Recirc',
        'data-track-label' : videoHref,
        'html': $('
',{
          'class' : 'content',
          'html' : '

'
        }).add($('
',{
          'html' : videoTitle
        }))
      }).appendTo($videoRecircList);
    });
  });
};

new VideoRecircList();<commit_msg>Rol; this back for now<commit_after>var VideoRecircList = function() {
  $videoItem = 'video-item';
  $videoRecircList = $('#video-list');
  $bettyUrl = $videoRecircList.data('betty-url');
  $source = $videoRecircList.data('source');
  $recircCount = $videoRecircList.data('count');
  this.init();
};

VideoRecircList.prototype.init = function() {
  this.loadVideoRecirc();
};

VideoRecircList.prototype.loadVideoRecirc = function() {
  $.getJSON($source, function(data) {
    $.each(data.results.slice(0,$recircCount), function(index, video) {
      $videoTitle = video.title;
      $videoHref = '/v/' + video.id;
      $posterSource = $bettyUrl + '/' + video.poster.id;
      $('',{
        'class' : $videoItem,
        'href' : $videoHref,
        'data-track-action' : 'Video: Recirc',
        'data-track-label' : $videoHref,
        'html': $('
',{
          'class' : 'content',
          'html' : '

'
        }).add($('
',{
          'html' : $videoTitle
        }))
      }).appendTo($videoRecircList);
    });
  });
};

new VideoRecircList();<|endoftext|>


<commit_before>'use strict';

var mongoose = require('mongoose'),
  Schema = mongoose.Schema,
  archive = require('./archive.js');

var FolderSchema = new Schema({
  created: {
    type: Date,
    default: Date.now
  },
  updated: {
    type: Date
  },
  title: {
    type: String
  },
  office: {
    type: Schema.ObjectId,
    ref: 'Office'
  },
  discussion: {
    type: Schema.ObjectId,
    ref: 'Discussion'
  },
  creator: {
    type: Schema.ObjectId,
    ref: 'User'
  },
  manager: {
    type: Schema.ObjectId,
    ref: 'User'
  },
  signature: {
    circles: {},
    codes: {}
  },
  color: {
    type: String,
    required: true
  },
  status: {
    type: String,
    enum: ['new', 'in-progress', 'canceled', 'completed', 'archived'],
    default: 'new'
  },
  tags: [String],
  description: {
    type: String
  },
  //should we maybe have finer grain control on this
  watchers: [
    {
      type: Schema.ObjectId,
      ref: 'User'
    }
  ],
  permissions: [
    {
      _id: false,
      id: {type: Schema.ObjectId, ref: 'User'},
      level: {
        type: String,
        enum: ['viewer', 'commenter', 'editor'],
        default: 'viewer'
      }
    }
  ],
  parent: {
    type: Schema.ObjectId,
    ref: 'Folder'
  },
  room: {
    type: String
  },
  hasRoom: {
    type: Boolean,
    default: false
  },
  sources: [String],
  circles: {
    type: Schema.Types.Mixed
  },
  WantRoom: {
    type: Boolean,
    default: false
  }
});

var starVirtual = FolderSchema.virtual('star');
starVirtual.get(function() {
  return this._star;
});
starVirtual.set(function(value) {
  this._star = value;
});
FolderSchema.set('toJSON', {
  virtuals: true
});
FolderSchema.set('toObject', {
  virtuals: true
});

/**
 * Validations
 */
FolderSchema.path('color').validate(function(color) {
  return /^([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$/i.test(color);
}, 'Invalid HEX color.');

/**
 * Statics
 */
FolderSchema.statics.load = function(id, cb) {
  this.findOne({
    _id: id
  }).populate('creator', 'name username').exec(cb);
};
FolderSchema.statics.office = function(id, cb) {
  require('./office');
  var Office = mongoose.model('Office');
  Office.findById(id, function(err, office) {
    cb(err, office || {});
  });
};
/**
 * Post middleware
 */
var elasticsearch = require('../controllers/elasticsearch');

FolderSchema.post('save', function(req, next) {
  var task = this;
  FolderSchema.statics.office(this.office, function(err, office) {
    if(err) {
      return err;
    }

    elasticsearch.save(task, 'folder', office.room);
  });
  next();
});

FolderSchema.pre('remove', function(next) {
  var task = this;
  FolderSchema.statics.office(this.office, function(err, office) {
    if(err) {
      return err;
    }
    elasticsearch.delete(task, 'task', office.room, next);
  });
  next();
});

/**
 * middleware
 */

FolderSchema.post('save', function(req, next) {
  elasticsearch.save(this, 'folder', this.room);
  next();
});

FolderSchema.pre('remove', function(next) {
  elasticsearch.delete(this, 'folder', this.room, next);
  next();
});

FolderSchema.plugin(archive, 'folder');

module.exports = mongoose.model('Folder', FolderSchema);
<commit_msg>removed parameters from folder model <commit_after>'use strict';

var mongoose = require('mongoose'),
  Schema = mongoose.Schema,
  archive = require('./archive.js');

var FolderSchema = new Schema({
  created: {
    type: Date,
    default: Date.now
  },
  updated: {
    type: Date
  },
  title: {
    type: String
  },
  office: {
    type: Schema.ObjectId,
    ref: 'Office'
  },
  discussion: {
    type: Schema.ObjectId,
    ref: 'Discussion'
  },
  creator: {
    type: Schema.ObjectId,
    ref: 'User'
  },
  manager: {
    type: Schema.ObjectId,
    ref: 'User'
  },
  signature: {
    circles: {},
    codes: {}
  },
  color: {
    type: String,
    required: true
  },
  status: {
    type: String,
    enum: ['new', 'in-progress', 'canceled', 'completed', 'archived'],
    default: 'new'
  },
  tags: [String],
  description: {
    type: String
  },
  //should we maybe have finer grain control on this
  watchers: [
    {
      type: Schema.ObjectId,
      ref: 'User'
    }
  ],
  permissions: [
    {
      _id: false,
      id: {type: Schema.ObjectId, ref: 'User'},
      level: {
        type: String,
        enum: ['viewer', 'commenter', 'editor'],
        default: 'viewer'
      }
    }
  ],
  parent: {
    type: Schema.ObjectId,
    ref: 'Folder'
  },
  room: {
    type: String
  },
  hasRoom: {
    type: Boolean,
    default: false
  },
  sources: [String],
  circles: {
    type: Schema.Types.Mixed
  },
  WantRoom: {
    type: Boolean,
    default: false
  }
});

var starVirtual = FolderSchema.virtual('star');
starVirtual.get(function() {
  return this._star;
});
starVirtual.set(function(value) {
  this._star = value;
});
FolderSchema.set('toJSON', {
  virtuals: true
});
FolderSchema.set('toObject', {
  virtuals: true
});

/**
 * Validations
 */
FolderSchema.path('color').validate(function(color) {
  return /^([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$/i.test(color);
}, 'Invalid HEX color.');

/**
 * Statics
 */
FolderSchema.statics.load = function(id, cb) {
  this.findOne({
    _id: id
  }).populate('creator', 'name username').exec(cb);
};
FolderSchema.statics.office = function(id, cb) {
  require('./office');
  var Office = mongoose.model('Office');
  Office.findById(id, function(err, office) {
    cb(err, office || {});
  });
};
/**
 * Post middleware
 */
var elasticsearch = require('../controllers/elasticsearch');

FolderSchema.post('save', function(req, next) {
  var task = this;
  FolderSchema.statics.office(this.office, function(err, office) {
    if(err) {
      return err;
    }

    elasticsearch.save(task, 'folder');
  });
  next();
});

FolderSchema.pre('remove', function(next) {
  var task = this;
  FolderSchema.statics.office(this.office, function(err, office) {
    if(err) {
      return err;
    }
    elasticsearch.delete(task, 'task', next);
  });
  next();
});

/**
 * middleware
 */

FolderSchema.post('save', function(req, next) {
  elasticsearch.save(this, 'folder');
  next();
});

FolderSchema.pre('remove', function(next) {
  elasticsearch.delete(this, 'folder', next);
  next();
});

FolderSchema.plugin(archive, 'folder');

module.exports = mongoose.model('Folder', FolderSchema);
<|endoftext|>


<commit_before>/*!
 * mor.js v1.0.1
 * http://forchoon.com/projects/javascript/mor-js/
 *
 * Copyright 2011, Alasdair Mercer
 * Licensed under the GPL Version 3 license.
 */
(function(a){function m(a,b){var c=typeof b==="function";try{var d=a();if(c){return b(null,d)}else{return d}}catch(e){if(c){return b(e)}else{throw e}}}function l(a,b){var c="";for(var d=0;d<b;d++){c+=a}return c}function k(a,b,c,d){if(typeof a!=="string"){throw new TypeError("Invalid value type: "+typeof a)}var e=typeof d==="string",f=a.trim().split(b);for(var g=0;g<f.length;g++){f[g]=f[g].split(c);if(e){for(var h=0;h<f[g].length;h++){f[g][h]=f[g][h].split(d).join("")}}}return f}function j(a,b,c,d,e,f){var g=typeof f==="string",h="";for(var i=0;i<a.length;i++){if(g&&i>0){h+=f}switch(a[i]){case b:h+=c;break;case d:h+=e;break}}return h}function i(a){if(typeof a==="string"){a=a.toLowerCase();for(var b=0;b<e.length;b++){if(e[b][0]===a){return e[b]}}}return i(c)}function h(a,c){a=a.toUpperCase();for(var d=0;d<b.length;d++){if(b[d][c]===a){return b[d]}}}var b=[["A","SL"],["B","LSSS"],["C","LSLS"],["D","LSS"],["E","S"],["F","SSLS"],["G","LLS"],["H","SSSS"],["I","SS"],["J","SLLL"],["K","LSL"],["L","SLSS"],["M","LL"],["N","LS"],["O","LLL"],["P","SLLS"],["Q","LLSL"],["R","SLS"],["S","SSS"],["T","L"],["U","SSL"],["V","SSSL"],["W","SLL"],["X","LSSL"],["Y","LSLL"],["Z","LLSS"],["0","LLLLL"],["1","SLLLL"],["2","SSLLL"],["3","SSSLL"],["4","SSSSL"],["5","SSSSS"],["6","LSSSS"],["7","LLSSS"],["8","LLLSS"],["9","LLLLS"],[".","SLSLSL"],[",","LLSSLL"],["?","SSLLSS"],["'","SLLLLS"],["!","LSLSLL"],["/","LSSLS"],["(","LSLLS"],[")","LSLLSL"],["&","SLSSS"],[":","LLLSSS"],[";","LSLSLS"],["=","LSSSL"],["+","SLSLS"],["-","LSSSSL"],["_","SSLLSL"],['"',"SLSSLS"],["$","SSSLSSL"],["@","SLLSLS"],["Ä","SLSL"],["Æ","SLSL"],["Ą","SLSL"],["À","SLLSL"],["Å","SLLSL"],["Ç","LSLSS"],["Ĉ","LSLSS"],["Ć","LSLSS"],["Š","LLLL"],["Ð","SSLLS"],["Ś","SSSLSSS"],["È","SLSSL"],["Ł","SLSSL"],["É","SSLSS"],["Đ","SSLSS"],["Ę","SSLSS"],["Ĝ","LLSLS"],["Ĥ","LLLL"],["Ĵ","SLLLS"],["Ź","LLSSLS"],["Ñ","LLSLL"],["Ń","LLSLL"],["Ö","LLLS"],["Ø","LLLS"],["Ó","LLLS"],["Ŝ","SSSLS"],["Þ","SLLSS"],["Ü","SSLL"],["Ŭ","SSLL"],["Ż","LLSSL"]],c="classic",d="L",e=[],f=a.morjs,g="S";e.push(["classic",["·","-"," ",l(" ",3),l(" ",7)]]);e.push(["classic-entities",["·","-"," ",l(" ",3),l(" ",7)]]);e.push(["digital",["1",l("1",3),"0",l("0",3),l("0",7)]]);e.push(["simple",["0","1","",","," "]]);var n={decode:function(a,b){return m(function(){a=a||{};var b=i(a.mode)[1],c="",e=k(a.message||"",b[4],b[3],b[2]);if(e.length){for(var f=0;f<e.length;f++){if(f>0){c+=" "}for(var l=0;l<e[f].length;l++){var m=j(e[f][l],b[0],g,b[1],d);if(m){var n=h(m,1);if(n){c+=n[0]}}}}}return c},b)},defineChar:function(a,c,d){return m(function(){if(typeof a!=="string"){throw new TypeError("Invalid character type: "+typeof a)}else if(typeof c!=="string"){throw new TypeError("Invalid pattern type: "+typeof c)}if(a.length>1){throw new Error("Invalid character length: "+a.length)}var d=a.toUpperCase(),e=c.trim().toUpperCase();if(!/^[SL]+$/.test(e)){throw new Error("Invalid pattern: "+c)}var f=h(d,0);if(f){f[1]=e}else{b.push([d,e])}},d)},defineMode:function(a,b,c){return m(function(){if(typeof a!=="string"){throw new TypeError("Invalid name type: "+typeof a)}if(typeof b!=="object"){throw new TypeError("Invalid characters type: "+typeof b)}if(b.length!==5){throw new Error("Invalid characters length: "+b.length)}var c=a.toLowerCase(),f="";for(var h=0;h<b.length;h++){if(typeof b[h]!=="string"){throw new TypeError("Invalid character type at ["+h+"]: "+typeof b[h])}f=b[h].toUpperCase();if(f.indexOf(d)!==-1){throw new Error("Invalid character found at ["+h+"]: "+d)}else if(f.indexOf(g)!==-1){throw new Error("Invalid character found at ["+h+"]: "+g)}}var j=i(c);if(j){j[1]=b}else{e.push([c,b])}},c)},encode:function(a,b){return m(function(){a=a||{};var b=i(a.mode)[1],c="",e=k(a.message||"",/\s+/,"");if(e.length){for(var f=0;f<e.length;f++){if(f>0){c+=b[4]}for(var l=0;l<e[f].length;l++){if(l>0){c+=b[3]}var m=h(e[f][l],0);if(m){c+=j(m[1],g,b[0],d,b[1],b[2])}}}}return c},b)},noConflict:function(){a.morjs=f;return this},VERSION:"1.0.1"};if(typeof exports!=="undefined"){if(typeof module!=="undefined"&&module.exports){exports=module.exports=n}exports.morjs=n}else if(typeof define==="function"&&define.amd){define("morjs",function(){return n})}else{a.morjs=n}})(this)<commit_msg>updated minified version with bug fix<commit_after>/*!
 * mor.js v1.0.2
 * http://forchoon.com/projects/javascript/mor-js/
 *
 * Copyright 2011, Alasdair Mercer
 * Licensed under the GPL Version 3 license.
 */
(function(a){function m(a,b){var c=typeof b==="function";try{var d=a();if(c){return b(null,d)}else{return d}}catch(e){if(c){return b(e)}else{throw e}}}function l(a,b){var c="";for(var d=0;d<b;d++){c+=a}return c}function k(a,b,c,d){if(typeof a!=="string"){throw new TypeError("Invalid value type: "+typeof a)}var e=typeof d!=="undefined",f=a.trim().split(b);for(var g=0;g<f.length;g++){f[g]=f[g].split(c);if(e){for(var h=0;h<f[g].length;h++){f[g][h]=f[g][h].split(d).join("")}}}return f}function j(a,b,c,d,e,f){var g=typeof f==="string",h="";for(var i=0;i<a.length;i++){if(g&&i>0){h+=f}switch(a[i]){case b:h+=c;break;case d:h+=e;break}}return h}function i(a){if(typeof a==="string"){a=a.toLowerCase();for(var b=0;b<e.length;b++){if(e[b][0]===a){return e[b]}}}return i(c)}function h(a,c){a=a.toUpperCase();for(var d=0;d<b.length;d++){if(b[d][c]===a){return b[d]}}}var b=[["A","SL"],["B","LSSS"],["C","LSLS"],["D","LSS"],["E","S"],["F","SSLS"],["G","LLS"],["H","SSSS"],["I","SS"],["J","SLLL"],["K","LSL"],["L","SLSS"],["M","LL"],["N","LS"],["O","LLL"],["P","SLLS"],["Q","LLSL"],["R","SLS"],["S","SSS"],["T","L"],["U","SSL"],["V","SSSL"],["W","SLL"],["X","LSSL"],["Y","LSLL"],["Z","LLSS"],["0","LLLLL"],["1","SLLLL"],["2","SSLLL"],["3","SSSLL"],["4","SSSSL"],["5","SSSSS"],["6","LSSSS"],["7","LLSSS"],["8","LLLSS"],["9","LLLLS"],[".","SLSLSL"],[",","LLSSLL"],["?","SSLLSS"],["'","SLLLLS"],["!","LSLSLL"],["/","LSSLS"],["(","LSLLS"],[")","LSLLSL"],["&","SLSSS"],[":","LLLSSS"],[";","LSLSLS"],["=","LSSSL"],["+","SLSLS"],["-","LSSSSL"],["_","SSLLSL"],['"',"SLSSLS"],["$","SSSLSSL"],["@","SLLSLS"],["Ä","SLSL"],["Æ","SLSL"],["Ą","SLSL"],["À","SLLSL"],["Å","SLLSL"],["Ç","LSLSS"],["Ĉ","LSLSS"],["Ć","LSLSS"],["Š","LLLL"],["Ð","SSLLS"],["Ś","SSSLSSS"],["È","SLSSL"],["Ł","SLSSL"],["É","SSLSS"],["Đ","SSLSS"],["Ę","SSLSS"],["Ĝ","LLSLS"],["Ĥ","LLLL"],["Ĵ","SLLLS"],["Ź","LLSSLS"],["Ñ","LLSLL"],["Ń","LLSLL"],["Ö","LLLS"],["Ø","LLLS"],["Ó","LLLS"],["Ŝ","SSSLS"],["Þ","SLLSS"],["Ü","SSLL"],["Ŭ","SSLL"],["Ż","LLSSL"]],c="classic",d="L",e=[],f=a.morjs,g="S";e.push(["classic",["·","-"," ",l(" ",3),l(" ",7)]]);e.push(["classic-entities",["·","-"," ",l(" ",3),l(" ",7)]]);e.push(["digital",["1",l("1",3),"0",l("0",3),l("0",7)]]);e.push(["simple",["0","1","",","," "]]);var n={decode:function(a,b){return m(function(){a=a||{};var b=i(a.mode)[1],c="",e=k(a.message||"",b[4],b[3],b[2]);if(e.length){for(var f=0;f<e.length;f++){if(f>0){c+=" "}for(var l=0;l<e[f].length;l++){var m=j(e[f][l],b[0],g,b[1],d);if(m){var n=h(m,1);if(n){c+=n[0]}}}}}return c},b)},defineChar:function(a,c,d){return m(function(){if(typeof a!=="string"){throw new TypeError("Invalid character type: "+typeof a)}else if(typeof c!=="string"){throw new TypeError("Invalid pattern type: "+typeof c)}if(a.length>1){throw new Error("Invalid character length: "+a.length)}var d=a.toUpperCase(),e=c.trim().toUpperCase();if(!/^[SL]+$/.test(e)){throw new Error("Invalid pattern: "+c)}var f=h(d,0);if(f){f[1]=e}else{b.push([d,e])}},d)},defineMode:function(a,b,c){return m(function(){if(typeof a!=="string"){throw new TypeError("Invalid name type: "+typeof a)}if(typeof b!=="object"){throw new TypeError("Invalid characters type: "+typeof b)}if(b.length!==5){throw new Error("Invalid characters length: "+b.length)}var c=a.toLowerCase(),f="";for(var h=0;h<b.length;h++){if(typeof b[h]!=="string"){throw new TypeError("Invalid character type at ["+h+"]: "+typeof b[h])}f=b[h].toUpperCase();if(f.indexOf(d)!==-1){throw new Error("Invalid character found at ["+h+"]: "+d)}else if(f.indexOf(g)!==-1){throw new Error("Invalid character found at ["+h+"]: "+g)}}var j=i(c);if(j){j[1]=b}else{e.push([c,b])}},c)},encode:function(a,b){return m(function(){a=a||{};var b=i(a.mode)[1],c="",e=k(a.message||"",/\s+/,"");if(e.length){for(var f=0;f<e.length;f++){if(f>0){c+=b[4]}for(var l=0;l<e[f].length;l++){if(l>0){c+=b[3]}var m=h(e[f][l],0);if(m){c+=j(m[1],g,b[0],d,b[1],b[2])}}}}return c},b)},noConflict:function(){a.morjs=f;return this},VERSION:"1.0.2"};if(typeof exports!=="undefined"){if(typeof module!=="undefined"&&module.exports){exports=module.exports=n}exports.morjs=n}else if(typeof define==="function"&&define.amd){define("morjs",function(){return n})}else{a.morjs=n}})(this)<|endoftext|>


<commit_before>/* dynamic xformat helper for timelines */
var xFormatter = function(xAxis, xextent) {
    var cWidth = document.body.clientWidth;
    //console.log("Xaxis extent diff, client width", xextent[1] - xextent[0], document.body.clientWidth);

    // Reduce numer of ticks if small amount of pixels available
    var widthFactor = 1;
    if(cWidth < 700) {
        widthFactor = 2;
    }else if(cWidth < 500) {
        widthFactor = 3;
    }

    var xdiff = xextent[1] - xextent[0];
    if (xdiff >= 3628800000) { // a monthish
        xAxis.ticks(d3.time.months, 1*widthFactor).tickFormat(d3.time.format('%d.%m'));
    }
    else if (xdiff >= 345600000) { // a weekish
        //xAxis.ticks(d3.time.months, 1).tickFormat(d3.time.format('%b %Y'));
        xAxis.ticks(d3.time.hours, 24*widthFactor).tickFormat(d3.time.format('%d.%m'));
    }
    else if (xdiff >= 100800000) { // 3 days
        xAxis.ticks(d3.time.hours, 6*widthFactor).tickFormat(d3.time.format('%a %H'));
    }
    else {
        xAxis.ticks(d3.time.hours, 1*widthFactor).tickFormat(d3.time.format('%H'));
    }
}

// Helps out with the bars
var bartender = function(target, key, legend, width, height) {
    // Fetch new json data
    d3.json("/api/max?key="+key, function(source) {

        valfmt = function(d) { return d.val }
        if (key == 'temp') {
            valfmt = function(d) {
                return rivets.formatters.temp(d.val)
            }
        }
        else if (key == 'rain') {
            valfmt = function(d) {
                return rivets.formatters.rain(d.val)
            }
        }
        else if (key == 'windspeed') {
            valfmt = function(d) {
                return rivets.formatters.wind(d.val)
            }
        }
        else if (key == 'wind') {
            valfmt = function(d) {
                return rivets.formatters.wind(d.val)
            }
        }
        drawbars(target, source, key, null, valfmt, legend, width, height);
    });

}

var draw = function(source) {
    gsource = source;
    //console.log('Current dataset', source);
    /* First remove any existing svg */
    /* Remove any spinners */
    $('.svgholder').empty();

    // The date format of SQL
    var parseDate = d3.time.format("%Y-%m-%d %H:%M:%S").parse;

    // Add d3 js date for each datum
    source.forEach(function(d) {
        d.date = parseDate(d.datetime);
        // also fix wind speed to be in m/s
        d.windspeed = d.windspeed/3.6;
        d.windgust = d.windgust/3.6;
    });
    var width = $('#main').css('width').split('px')[0];
    var height = width/4;

    /* Line graphs */
    var tempgraph = temprain('#temp', source, width, height);
    // Less height for less important graphs
    var height = width/4;
    /*
    drawlines('#pressure', source, 'barometer','Air pressure (hPa)', width, height);
    drawlines('#wind', source, 'windspeed', 'Wind speed m/s', width, height);
    /* Disable rain graph as it is part of temp graph now
    drawlines('#rain', source, 'rain','Daily rain (mm)', width, height);
    drawlines('#winddir', source, 'winddir','Wind direction (°)', width, height);
    //drawlines('#humidity', source, 'outhumidity','Humidity (%)', width, height);

    */
    var vals = ['outhumidity', 'windspeed', 'winddir', 'windgust', 'rain', 'barometer', 'inhumidity', 'intemp', 'outtemp', 'dewpoint', 'heatindex', 'windchill'];
    d3.select('#graphtabs ul.nav-tabs').selectAll('li')
        .data(vals)
      .enter().append('li')
        .classed('active', function(d,i) {
            if (i == 0) {
                return true;
            }
            return false;
        }
        )
        .html(function(d, i) {
            return ''+d.charAt(0).toUpperCase() + d.substr(1).toLowerCase()+''
        });
    d3.select('#graphtabs .tab-content').selectAll('div')
        .data(vals)
      .enter().append('div')
        .classed('tab-pane', true)
        .classed('active', function(d,i) {
            if (i == 0) {
                return true;
            }
            return false;
        }
        )
        .attr('id', function(d,i) { return 'tab_graph_'+d; })
        .html(function(d, i) {
            return '

';
        });
    vals.forEach(function(k,v) {
        console.log(k, source[0][k]);
        if(source[0][k] != undefined && source[0][k] != NaN && source[0][k] != null) {
            console.log(k);
            d3.select("#graphtabs .svgholder."+k)
            .datum(source)
            .call(timeSeriesChart()
                .width(width)
                .height(height)
                .x(function(d) { return d.date; })
                .y(function(d) { return +d[k]; }));
        }
    });

    /* Bar graphs */
    /*
    bartender('#rain', 'rain', 'Daily Rain', width, height);
    bartender('#temp', 'temp', 'Daily Max Temp', width, height);
    bartender('#wind', 'windspeed', 'Daily Max Wind', width, height);
    */

    // Get the last element and populate rivets bindings with it
    rivets.bind(document.getElementById('main'), {
        current: source.slice(-1)[0],
        first: source.slice(0)[0]
    });
}

/* Container for assorted helpers */
var amatyrlib = function() {
    this.that = this;
    that = this;
    this.addGradient = function(target, w, h) {
        /*
        var gradient = target.append("svg:defs")
          .append("svg:linearGradient")
            .attr("id", "gradient")
            .attr("x1", "0%")
            .attr("y1", "0%")
            .attr("x2", "0%")
            .attr("y2", "100%")
            .attr("spreadMethod", "pad");

        gradient.append("svg:stop")
            .attr("offset", "0%")
            .attr("stop-color", "#fafafa")
            .attr("stop-opacity", 1);

        gradient.append("svg:stop")
            .attr("offset", "100%")
            .attr("stop-color", "#f0f0f0")
            .attr("stop-opacity", 1);
*/

        target.append("svg:rect")
            .attr("width", w)
            .attr("height", h)
            .style("fill", "url(#myLinearGradient1)");
    }
    /* Pad helper */
    Number.prototype.pad = function (len) {
            return (new Array(len+1).join("0") + this).slice(-len);
    }
    /* Rivets formatters */
    rivets.formatters.temp = function(value) {
        if(!value)
            return '';
        if (value != undefined)
            return Number((value).toFixed(1))+ ' °C';
    }
    rivets.formatters.pressure = function(value) {
        if (value)
            return Number((value).toFixed(1)) + ' hPa';
    }
    rivets.formatters.rain = function(value) {
        if(value)
            return Number((value).toFixed(1)) + ' mm';
        return '0 mm';
    }
    rivets.formatters.wind = function(value) {
        if (value)
            return Number(value/3.6).toFixed(1) + ' m/s';
        return '0 m/s'
    }
    rivets.formatters.degree = function(value) {
        if (value)
            return Number((value).toFixed(1)) + ' °';
        return '0 °';
    }
    rivets.formatters.percent = function(value) {
        if (value)
            return Number((value).toFixed(0)) + ' %';
        return '0%'
    }
    rivets.formatters.rotate = function(value) {
        return 'display:inline-block;-o-transform: rotate('+value+'deg);-ms-transform: rotate('+value+'deg);-moz-transform: rotate('+value+'deg);-webkit-transform: rotate('+value+'deg);transform: rotate('+value+'deg);'
    }
    rivets.formatters.date = function(date) {
        var parseDate = d3.time.format("%Y-%m-%d %H:%M:%S").parse;
        var date = parseDate(date);
        return date.getHours().pad(2) + ':' + date.getMinutes().pad(2);
    }

    /* Configure Rivets to work with Watch JS */
    rivets.configure({
        adapter: {
            subscribe: function(obj, keypath, callback) {
                watch(obj, keypath, callback)
            },
            unsubscribe: function(obj, keypath, callback) {
                unwatch(obj, keypath, callback)
            },
            read: function(obj, keypath) {
                return obj[keypath]
            },
            publish: function(obj, keypath, value) {
                obj[keypath] = value
            }
        }
    });

    /* Create a custom rivets binder that looks into data changed and transitions the update in data with colorful updating depending on if the value decreased or increase.

     It expects data in the following form
       float unit
       eg. -2.9 hPa
     and will only look at the value
    */
    rivets.binders.texttransition = function(el, value) {
        var newVal, oldVal, transitonTime = 5*1000, color;
        if (value != null) {
            newVal = parseFloat(value.split(' ')[0]);
            if (el.textContent != null) {
                var val = parseFloat(el.textContent.split(' ')[0]);
                if (val != NaN) {
                    oldVal = val;
                }
            }
        }
        if(oldVal > newVal) {
            // Value decrease, we show this as red
            color = '#b5152b';
        }else if (oldVal < newVal) {
            // Value increase, we show this as green
            color = '#5c8843';
        }
        // Animate if color
        if (color) {
            // There
            d3.select(el.parentNode).transition()
                .style('background-color', color)
                .duration(transitonTime);
            d3.select(el)
                .classed('trans', true)
            // And back again
            d3.select(el.parentNode).transition()
                .style('background-color', 'inherit')
                .duration(transitonTime)
                .delay(transitonTime);
            // remove trans class for font styling
            setTimeout(function() {
                d3.select(el)
                .classed('trans', false)
            }, transitonTime);
        }
        if (el.innerText != null) {
            return el.innerText = value != null ? value : '';
        } else {
            return el.textContent = value != null ? value : '';
        }
    }
    return this;
}
<commit_msg>remove debugging<commit_after>/* dynamic xformat helper for timelines */
var xFormatter = function(xAxis, xextent) {
    var cWidth = document.body.clientWidth;
    //console.log("Xaxis extent diff, client width", xextent[1] - xextent[0], document.body.clientWidth);

    // Reduce numer of ticks if small amount of pixels available
    var widthFactor = 1;
    if(cWidth < 700) {
        widthFactor = 2;
    }else if(cWidth < 500) {
        widthFactor = 3;
    }

    var xdiff = xextent[1] - xextent[0];
    if (xdiff >= 3628800000) { // a monthish
        xAxis.ticks(d3.time.months, 1*widthFactor).tickFormat(d3.time.format('%d.%m'));
    }
    else if (xdiff >= 345600000) { // a weekish
        //xAxis.ticks(d3.time.months, 1).tickFormat(d3.time.format('%b %Y'));
        xAxis.ticks(d3.time.hours, 24*widthFactor).tickFormat(d3.time.format('%d.%m'));
    }
    else if (xdiff >= 100800000) { // 3 days
        xAxis.ticks(d3.time.hours, 6*widthFactor).tickFormat(d3.time.format('%a %H'));
    }
    else {
        xAxis.ticks(d3.time.hours, 1*widthFactor).tickFormat(d3.time.format('%H'));
    }
}

// Helps out with the bars
var bartender = function(target, key, legend, width, height) {
    // Fetch new json data
    d3.json("/api/max?key="+key, function(source) {

        valfmt = function(d) { return d.val }
        if (key == 'temp') {
            valfmt = function(d) {
                return rivets.formatters.temp(d.val)
            }
        }
        else if (key == 'rain') {
            valfmt = function(d) {
                return rivets.formatters.rain(d.val)
            }
        }
        else if (key == 'windspeed') {
            valfmt = function(d) {
                return rivets.formatters.wind(d.val)
            }
        }
        else if (key == 'wind') {
            valfmt = function(d) {
                return rivets.formatters.wind(d.val)
            }
        }
        drawbars(target, source, key, null, valfmt, legend, width, height);
    });

}

var draw = function(source) {
    gsource = source;
    //console.log('Current dataset', source);
    /* First remove any existing svg */
    /* Remove any spinners */
    $('.svgholder').empty();

    // The date format of SQL
    var parseDate = d3.time.format("%Y-%m-%d %H:%M:%S").parse;

    // Add d3 js date for each datum
    source.forEach(function(d) {
        d.date = parseDate(d.datetime);
        // also fix wind speed to be in m/s
        d.windspeed = d.windspeed/3.6;
        d.windgust = d.windgust/3.6;
    });
    var width = $('#main').css('width').split('px')[0];
    var height = width/4;

    /* Line graphs */
    var tempgraph = temprain('#temp', source, width, height);
    // Less height for less important graphs
    var height = width/4;
    /*
    drawlines('#pressure', source, 'barometer','Air pressure (hPa)', width, height);
    drawlines('#wind', source, 'windspeed', 'Wind speed m/s', width, height);
    /* Disable rain graph as it is part of temp graph now
    drawlines('#rain', source, 'rain','Daily rain (mm)', width, height);
    drawlines('#winddir', source, 'winddir','Wind direction (°)', width, height);
    //drawlines('#humidity', source, 'outhumidity','Humidity (%)', width, height);

    */
    var vals = ['outhumidity', 'windspeed', 'winddir', 'windgust', 'rain', 'barometer', 'inhumidity', 'intemp', 'outtemp', 'dewpoint', 'heatindex', 'windchill'];
    d3.select('#graphtabs ul.nav-tabs').selectAll('li')
        .data(vals)
      .enter().append('li')
        .classed('active', function(d,i) {
            if (i == 0) {
                return true;
            }
            return false;
        }
        )
        .html(function(d, i) {
            return ''+d.charAt(0).toUpperCase() + d.substr(1).toLowerCase()+''
        });
    d3.select('#graphtabs .tab-content').selectAll('div')
        .data(vals)
      .enter().append('div')
        .classed('tab-pane', true)
        .classed('active', function(d,i) {
            if (i == 0) {
                return true;
            }
            return false;
        }
        )
        .attr('id', function(d,i) { return 'tab_graph_'+d; })
        .html(function(d, i) {
            return '

';
        });
    vals.forEach(function(k,v) {
        if(source[0][k] != undefined && source[0][k] != NaN && source[0][k] != null) {
            d3.select("#graphtabs .svgholder."+k)
            .datum(source)
            .call(timeSeriesChart()
                .width(width)
                .height(height)
                .x(function(d) { return d.date; })
                .y(function(d) { return +d[k]; }));
        }
    });

    /* Bar graphs */
    /*
    bartender('#rain', 'rain', 'Daily Rain', width, height);
    bartender('#temp', 'temp', 'Daily Max Temp', width, height);
    bartender('#wind', 'windspeed', 'Daily Max Wind', width, height);
    */

    // Get the last element and populate rivets bindings with it
    rivets.bind(document.getElementById('main'), {
        current: source.slice(-1)[0],
        first: source.slice(0)[0]
    });
}

/* Container for assorted helpers */
var amatyrlib = function() {
    this.that = this;
    that = this;
    this.addGradient = function(target, w, h) {
        /*
        var gradient = target.append("svg:defs")
          .append("svg:linearGradient")
            .attr("id", "gradient")
            .attr("x1", "0%")
            .attr("y1", "0%")
            .attr("x2", "0%")
            .attr("y2", "100%")
            .attr("spreadMethod", "pad");

        gradient.append("svg:stop")
            .attr("offset", "0%")
            .attr("stop-color", "#fafafa")
            .attr("stop-opacity", 1);

        gradient.append("svg:stop")
            .attr("offset", "100%")
            .attr("stop-color", "#f0f0f0")
            .attr("stop-opacity", 1);
*/

        target.append("svg:rect")
            .attr("width", w)
            .attr("height", h)
            .style("fill", "url(#myLinearGradient1)");
    }
    /* Pad helper */
    Number.prototype.pad = function (len) {
            return (new Array(len+1).join("0") + this).slice(-len);
    }
    /* Rivets formatters */
    rivets.formatters.temp = function(value) {
        if(!value)
            return '';
        if (value != undefined)
            return Number((value).toFixed(1))+ ' °C';
    }
    rivets.formatters.pressure = function(value) {
        if (value)
            return Number((value).toFixed(1)) + ' hPa';
    }
    rivets.formatters.rain = function(value) {
        if(value)
            return Number((value).toFixed(1)) + ' mm';
        return '0 mm';
    }
    rivets.formatters.wind = function(value) {
        if (value)
            return Number(value/3.6).toFixed(1) + ' m/s';
        return '0 m/s'
    }
    rivets.formatters.degree = function(value) {
        if (value)
            return Number((value).toFixed(1)) + ' °';
        return '0 °';
    }
    rivets.formatters.percent = function(value) {
        if (value)
            return Number((value).toFixed(0)) + ' %';
        return '0%'
    }
    rivets.formatters.rotate = function(value) {
        return 'display:inline-block;-o-transform: rotate('+value+'deg);-ms-transform: rotate('+value+'deg);-moz-transform: rotate('+value+'deg);-webkit-transform: rotate('+value+'deg);transform: rotate('+value+'deg);'
    }
    rivets.formatters.date = function(date) {
        var parseDate = d3.time.format("%Y-%m-%d %H:%M:%S").parse;
        var date = parseDate(date);
        return date.getHours().pad(2) + ':' + date.getMinutes().pad(2);
    }

    /* Configure Rivets to work with Watch JS */
    rivets.configure({
        adapter: {
            subscribe: function(obj, keypath, callback) {
                watch(obj, keypath, callback)
            },
            unsubscribe: function(obj, keypath, callback) {
                unwatch(obj, keypath, callback)
            },
            read: function(obj, keypath) {
                return obj[keypath]
            },
            publish: function(obj, keypath, value) {
                obj[keypath] = value
            }
        }
    });

    /* Create a custom rivets binder that looks into data changed and transitions the update in data with colorful updating depending on if the value decreased or increase.

     It expects data in the following form
       float unit
       eg. -2.9 hPa
     and will only look at the value
    */
    rivets.binders.texttransition = function(el, value) {
        var newVal, oldVal, transitonTime = 5*1000, color;
        if (value != null) {
            newVal = parseFloat(value.split(' ')[0]);
            if (el.textContent != null) {
                var val = parseFloat(el.textContent.split(' ')[0]);
                if (val != NaN) {
                    oldVal = val;
                }
            }
        }
        if(oldVal > newVal) {
            // Value decrease, we show this as red
            color = '#b5152b';
        }else if (oldVal < newVal) {
            // Value increase, we show this as green
            color = '#5c8843';
        }
        // Animate if color
        if (color) {
            // There
            d3.select(el.parentNode).transition()
                .style('background-color', color)
                .duration(transitonTime);
            d3.select(el)
                .classed('trans', true)
            // And back again
            d3.select(el.parentNode).transition()
                .style('background-color', 'inherit')
                .duration(transitonTime)
                .delay(transitonTime);
            // remove trans class for font styling
            setTimeout(function() {
                d3.select(el)
                .classed('trans', false)
            }, transitonTime);
        }
        if (el.innerText != null) {
            return el.innerText = value != null ? value : '';
        } else {
            return el.textContent = value != null ? value : '';
        }
    }
    return this;
}
<|endoftext|>


<commit_before>let ServiceBase = function () {
    let _ = Object.create (null);

    // Helper functions for emitting HTML from Javascript
    let valid = function (value) {
        return (typeof (value) != "undefined") && (value !== null);
    };

    let block = function (block, attributes, content) {
        let result = "<" + block;
        if (valid (attributes)) {
            let attributeNames = Object.keys (attributes);
            for (let attributeName of attributeNames) {
                if (valid (attributes[attributeName])) {
                    result += " " + attributeName + "=\"" + attributes[attributeName] + "\"";
                }
            }
        }
        return result + (valid (content) ? (">" + content + "</" + block + ">") : ("/>"));
    };

    let div = function (cssClass, content) {
        return block ("div", { "class": cssClass }, content);
    };

    let a = function (cssClass, href, content) {
        return block ("a", { "class": cssClass, "href": href, "target": "_top" }, content);
    };

    _.get = function (queryString, onSuccess) {
        let request = new XMLHttpRequest ();
        request.overrideMimeType ("application/json");
        request.open ("GET", queryString, true);
        request.onload = function (event) {
            if (request.status === 200) {
                let response = JSON.parse (this.responseText);
                onSuccess (response);
            }
        };
        request.send ();
    };

    // a little black raincloud, of course
    _.display = function (displayInDivId, inputUrl) {
        let url = (typeof (inputUrl) !== "undefined") ? inputUrl : "api?event=help";
        _.get (url, function (db) {
            // if we retrieved the api.json from the service base, get the actual response
            if (typeof (inputUrl) === "undefined") { db = db.response; }

            // start with an empty build
            let innerHTML = "";

            if ("description" in db) {
                innerHTML += block ("h2", {}, "Description") + div ("description-div", db.description);
            }

            if ("events" in db) {
                innerHTML += block ("h2", {}, "Events");
                let events = db.events;
                let eventNames = Object.keys (events);
                let eventsHTML = "";
                for (let eventName of eventNames) {
                    let event = events[eventName];
                    let eventHTML = "";

                    // if there is an example
                    if ("example" in event) {
                        let url = "api?event=" + eventName;
                        let example = event.example;
                        let exampleKeys = Object.keys (example).sort ();
                        for (let exampleKey of exampleKeys) {
                            url += "&" + exampleKey + "=" + example[exampleKey];
                        }
                        //eventHTML = a ("try-it", url, " (" + url.replace (/&/g, "&") + ")");
                        eventHTML = a ("try-it", url, "[example]");
                    }
                    eventHTML = div ("event-name", eventName + eventHTML);

                    // if there is a description
                    if ("description" in event) {
                        eventHTML += div ("event-description", event.description);
                    }

                    let odd;
                    let evenOdd = function (title, object) {
                        odd = true;
                        let names = Object.keys (object);
                        if (names.length > 0) {
                            eventHTML += div ("even-odd-title", title);
                            for (let name of names) {
                                let element = object[name];
                                let required = ("required" in element) ? element.required : false;
                                eventHTML += div ("even-odd-div" + (odd ? " odd" : ""),
                                    div ("even-odd-name", name) +
                                    div ("even-odd-required", required ? "REQUIRED" : "OPTIONAL") +
                                    div ("even-odd-description", element.description));
                                odd = !odd;
                            }
                        }
                        return odd;
                    };

                    if ("parameters" in event) {
                        evenOdd ("Parameters:", event.parameters);
                    }

                    if (("strict" in event) && (event.strict == "false")) {
                        eventHTML += div ("even-odd-div" + (odd ? " odd" : ""),
                            div ("even-odd-name", "(any)") +
                            div ("even-odd-required", "OPTIONAL") +
                            div ("even-odd-description", "Event allows unspecified parameters."));
                    }

                    if ("returns" in event) {
                        let returns = event.returns;
                        // return specification might be an array, indicating this event returns an
                        // array of something
                        if (Array.isArray(returns)) {
                            // return specification might be an empty array, or an array with a single
                            // proto object
                            if (returns.length > 0) {
                                evenOdd("Returns Array of:", returns[0]);
                            } else {
                                eventHTML += div ("even-odd-title", "Returns: Array");
                            }
                        } else {
                            evenOdd("Returns:", returns);
                        }
                    }

                    eventsHTML += div ("event-div", eventHTML);
                }
                innerHTML += div("events-div", eventsHTML);
            }

            if ("name" in db) {
                document.title = db.name;
                innerHTML = block ("h1", {}, db.name) + div("container-div", innerHTML);
            }
            innerHTML += div ("content-center footer", "Built with " + a ("footer-link", "http://bag-service-base.brettonw.com", "brettonw/BagServiceBase"));

            document.getElementById(displayInDivId).innerHTML = innerHTML;
        });
    };

    _.api = function (onSuccess, baseUrl, apiSource) {
        // convert the names into camel-case names (dashes are removed and the following character is uppercased)
        let makeName = function (input) {
            return input.replace (/-([^-])/g, function replacer (match, p1, offset, string) {
                return p1.toUpperCase();
            });
        };

        // condition the inputs
        baseUrl = ((typeof baseUrl === "undefined") || (baseUrl === "")) ? location.href.substr(0,location.href.lastIndexOf("/")) : baseUrl;
        baseUrl = baseUrl.replace (/\/$/g, "");

        // get the api
        let url = (typeof (apiSource) !== "undefined") ? apiSource : (baseUrl + "/api?event=help");
        _.get (url, function (db) {
            // if we retrieved the api.json from the service base, get the actual response
            if (typeof (apiSource) === "undefined") { db = db.response; }

            // start with an empty build
            let api = Object.create (null);

            // check that we got a response with events
            if ("events" in db) {
                let events = db.events;
                let eventNames = Object.keys (events);
                for (let eventName of eventNames) {
                    let event = events[eventName];

                    // set up the function name and an empty parameter list
                    let functionName = makeName (eventName);
                    let functionParameters = "(";
                    let functionBody = '    let url = "' + baseUrl + '/api?event=' + eventName + '";\n';

                    // if there are parameters, add them
                    let first = true;
                    if ("parameters" in event) {
                        let names = Object.keys (event.parameters);
                        if (names.length > 0) {
                            for (let name of names) {
                                let parameterName = makeName (name);
                                functionParameters += ((first !== true) ? ", " : "") + parameterName;
                                functionBody += '    url += "' + name + '=" + ' + parameterName + ';\n';
                                first = false;
                            }
                        }
                    }
                    functionParameters += ((first !== true) ? ", " : "") + "onSuccess";
                    functionBody += '    ServiceBase.get (url, function (response) {\n';
                    functionBody += '        if (response.status === "ok") {\n';
                    functionBody += '            onSuccess (response.response);\n';
                    functionBody += '        }\n';
                    functionBody += '    });\n';
                    functionParameters += ")";

                    console.log (functionName + " " + functionParameters + ";\n");

                    let functionString = "return function " + functionParameters + " {\n" +functionBody + "};\n";
                    api[functionName] = new Function (functionString) ();
                }
            }

            // call the completion routine
            onSuccess (api);
        });
    };

    return _;
} ();
<commit_msg>translating the response object so the names are valid javascript names<commit_after>let ServiceBase = function () {
    let _ = Object.create (null);

    // Helper functions for emitting HTML from Javascript
    let valid = function (value) {
        return (typeof (value) != "undefined") && (value !== null);
    };

    let block = function (block, attributes, content) {
        let result = "<" + block;
        if (valid (attributes)) {
            let attributeNames = Object.keys (attributes);
            for (let attributeName of attributeNames) {
                if (valid (attributes[attributeName])) {
                    result += " " + attributeName + "=\"" + attributes[attributeName] + "\"";
                }
            }
        }
        return result + (valid (content) ? (">" + content + "</" + block + ">") : ("/>"));
    };

    let div = function (cssClass, content) {
        return block ("div", { "class": cssClass }, content);
    };

    let a = function (cssClass, href, content) {
        return block ("a", { "class": cssClass, "href": href, "target": "_top" }, content);
    };

    _.get = function (queryString, onSuccess) {
        let request = new XMLHttpRequest ();
        request.overrideMimeType ("application/json");
        request.open ("GET", queryString, true);
        request.onload = function (event) {
            if (request.status === 200) {
                let response = JSON.parse (this.responseText);
                onSuccess (response);
            }
        };
        request.send ();
    };

    // a little black raincloud, of course
    _.display = function (displayInDivId, inputUrl) {
        let url = (typeof (inputUrl) !== "undefined") ? inputUrl : "api?event=help";
        _.get (url, function (db) {
            // if we retrieved the api.json from the service base, get the actual response
            if (typeof (inputUrl) === "undefined") { db = db.response; }

            // start with an empty build
            let innerHTML = "";

            if ("description" in db) {
                innerHTML += block ("h2", {}, "Description") + div ("description-div", db.description);
            }

            if ("events" in db) {
                innerHTML += block ("h2", {}, "Events");
                let events = db.events;
                let eventNames = Object.keys (events);
                let eventsHTML = "";
                for (let eventName of eventNames) {
                    let event = events[eventName];
                    let eventHTML = "";

                    // if there is an example
                    if ("example" in event) {
                        let url = "api?event=" + eventName;
                        let example = event.example;
                        let exampleKeys = Object.keys (example).sort ();
                        for (let exampleKey of exampleKeys) {
                            url += "&" + exampleKey + "=" + example[exampleKey];
                        }
                        //eventHTML = a ("try-it", url, " (" + url.replace (/&/g, "&") + ")");
                        eventHTML = a ("try-it", url, "[example]");
                    }
                    eventHTML = div ("event-name", eventName + eventHTML);

                    // if there is a description
                    if ("description" in event) {
                        eventHTML += div ("event-description", event.description);
                    }

                    let odd;
                    let evenOdd = function (title, object) {
                        odd = true;
                        let names = Object.keys (object);
                        if (names.length > 0) {
                            eventHTML += div ("even-odd-title", title);
                            for (let name of names) {
                                let element = object[name];
                                let required = ("required" in element) ? element.required : false;
                                eventHTML += div ("even-odd-div" + (odd ? " odd" : ""),
                                    div ("even-odd-name", name) +
                                    div ("even-odd-required", required ? "REQUIRED" : "OPTIONAL") +
                                    div ("even-odd-description", element.description));
                                odd = !odd;
                            }
                        }
                        return odd;
                    };

                    if ("parameters" in event) {
                        evenOdd ("Parameters:", event.parameters);
                    }

                    if (("strict" in event) && (event.strict == "false")) {
                        eventHTML += div ("even-odd-div" + (odd ? " odd" : ""),
                            div ("even-odd-name", "(any)") +
                            div ("even-odd-required", "OPTIONAL") +
                            div ("even-odd-description", "Event allows unspecified parameters."));
                    }

                    if ("returns" in event) {
                        let returns = event.returns;
                        // return specification might be an array, indicating this event returns an
                        // array of something
                        if (Array.isArray(returns)) {
                            // return specification might be an empty array, or an array with a single
                            // proto object
                            if (returns.length > 0) {
                                evenOdd("Returns Array of:", returns[0]);
                            } else {
                                eventHTML += div ("even-odd-title", "Returns: Array");
                            }
                        } else {
                            evenOdd("Returns:", returns);
                        }
                    }

                    eventsHTML += div ("event-div", eventHTML);
                }
                innerHTML += div("events-div", eventsHTML);
            }

            if ("name" in db) {
                document.title = db.name;
                innerHTML = block ("h1", {}, db.name) + div("container-div", innerHTML);
            }
            innerHTML += div ("content-center footer", "Built with " + a ("footer-link", "http://bag-service-base.brettonw.com", "brettonw/BagServiceBase"));

            document.getElementById(displayInDivId).innerHTML = innerHTML;
        });
    };

    // convert the names into camel-case names (dashes are removed and the following character is uppercased)
    let makeName = function (input) {
        return input.replace (/-([^-])/g, function replacer (match, p1, offset, string) {
            return p1.toUpperCase();
        });
    };

    _.translateResponse = function (response) {
        // internal function to copy the object as a response
        let copyAsResponse = function (object) {
            let copy = {};
            let keys = Object.keys (object);
            for (let key of keys) {
                copy[makeName(key)] = object[key];
            }
            return copy;
        };

        // make a decision based on the type
        if (Array.isArray(response)) {
            let copy = [];
            for (let entry of response) {
                copy.push (copyAsResponse(entry));
            }
            return copy;
        } else {
            return copyAsResponse(response);
        }
    };

    _.api = function (onSuccess, baseUrl, apiSource) {
        // condition the inputs
        baseUrl = ((typeof baseUrl === "undefined") || (baseUrl === "")) ? location.href.substr(0,location.href.lastIndexOf("/")) : baseUrl;
        baseUrl = baseUrl.replace (/\/$/g, "");

        // get the api
        let url = ((typeof (apiSource) === "undefined") || (apiSource === "")) ? (baseUrl + "/api?event=help") : apiSource;
        _.get (url, function (db) {
            // if we retrieved the api.json from the service base, get the actual response
            if (typeof (apiSource) === "undefined") { db = db.response; }

            // start with an empty build
            let api = Object.create (null);

            // check that we got a response with events
            if ("events" in db) {
                let events = db.events;
                let eventNames = Object.keys (events);
                for (let eventName of eventNames) {
                    let event = events[eventName];

                    // set up the function name and an empty parameter list
                    let functionName = makeName (eventName);
                    let functionParameters = "(";
                    let functionBody = '    let url = "' + baseUrl + '/api?event=' + eventName + '";\n';

                    // if there are parameters, add them
                    let first = true;
                    if ("parameters" in event) {
                        let names = Object.keys (event.parameters);
                        if (names.length > 0) {
                            for (let name of names) {
                                let parameterName = makeName (name);
                                functionParameters += ((first !== true) ? ", " : "") + parameterName;
                                functionBody += '    url += "' + name + '=" + ' + parameterName + ';\n';
                                first = false;
                            }
                        }
                    }
                    functionParameters += ((first !== true) ? ", " : "") + "onSuccess";
                    functionBody += '    ServiceBase.get (url, function (response) {\n';
                    functionBody += '        if (response.status === "ok") {\n';
                    functionBody += '            response = ServiceBase.translateResponse (response.response);\n';
                    functionBody += '            onSuccess (response);\n';
                    functionBody += '        }\n';
                    functionBody += '    });\n';
                    functionParameters += ")";

                    console.log (functionName + " " + functionParameters + ";\n");

                    let functionString = "return function " + functionParameters + " {\n" +functionBody + "};\n";
                    api[functionName] = new Function (functionString) ();
                }
            }

            // call the completion routine
            onSuccess (api);
        });
    };

    return _;
} ();
<|endoftext|>


<commit_before>'use strict';

var express = require('express'),
    router = express.Router();

router.get('/', function(req, res, next) {
  res.send('get apis');
});

router.post('/', function(req, res, next) {
  res.send('create apis');
});

router.put('/', function(req, res, next) {
  res.send('update apis');
});

router.delete('/', function(req, res, next) {
  res.send('delete apis');
});

module.exports = router;<commit_msg>Validate ssh key<commit_after>'use strict';

var express = require('express'),
    router = express.Router();

router.get('/', function(req, res, next) {
  res.send('get apis!');
});

router.post('/', function(req, res, next) {
  res.send('create apis');
});

router.put('/', function(req, res, next) {
  res.send('update apis');
});

router.delete('/', function(req, res, next) {
  res.send('delete apis');
});

module.exports = router;
<|endoftext|>


<commit_before>(function(window) {

	'use strict';

	function define_gbcountdown() {
		var GBCountdown = {};
		GBCountdown.end = new Date("03/08/2015 22:00:00");
		GBCountdown._second = 1000;
		GBCountdown._minute = GBCountdown._second * 60;
		GBCountdown._hour = GBCountdown._minute * 60;
		GBCountdown._day = GBCountdown._hour * 24;

		GBCountdown.startUpdating = function() {
			GBCountdown.timer = setInterval(GBCountdown.updateRemaining, 1000);
		}

		GBCountdown.stopUpdating = function() {
			clearInterval(GBCountdown.timer);
		}

		GBCountdown.updateRemaining = function() {
			console.log("updateRemaining");

			var now = new Date();

			console.log(now);

			var distance = GBCountdown.end - now;

			console.log(distance);

			if (distance < 0) {
				GBCountdown.stopUpdating();
				document.getElementById("gbCountdown").innerHTML = "DEADLINE PASSED!";
				return;
			}

			var days = Math.floor(distance / GBCountdown._day);
			var hours = Math.floor((distance % GBCountdown._day) / GBCountdown._hour);
			var minutes = Math.floor((distance % GBCountdown._hour) / GBCountdown._minute);
			var seconds = Math.floor((distance % GBCountdown._minute) / GBCountdown._second);

			document.getElementById("gbCountdown").innerHTML = "deadline: " + days + "days ";
			document.getElementById("gbCountdown").innerHTML += hours + "hrs ";
			document.getElementById("gbCountdown").innerHTML += minutes + "mins ";
			document.getElementById("gbCountdown").innerHTML += seconds + "secs";
		}

		return GBCountdown;
	}

	if (typeof(GBCountdown) === "undefined") {
		window.GBCountdown = define_gbcountdown();
	} else {
		console.log("GBCountdown already defined.");
	}

	GBCountdown.startUpdating();

})(window);<commit_msg>argh, editor didn't put a newline at the end of file<commit_after>(function(window) {

	'use strict';

	function define_gbcountdown() {
		var GBCountdown = {};
		GBCountdown.end = new Date("03/08/2015 22:00:00");
		GBCountdown._second = 1000;
		GBCountdown._minute = GBCountdown._second * 60;
		GBCountdown._hour = GBCountdown._minute * 60;
		GBCountdown._day = GBCountdown._hour * 24;

		GBCountdown.startUpdating = function() {
			GBCountdown.timer = setInterval(GBCountdown.updateRemaining, 1000);
		}

		GBCountdown.stopUpdating = function() {
			clearInterval(GBCountdown.timer);
		}

		GBCountdown.updateRemaining = function() {
			console.log("updateRemaining");

			var now = new Date();

			console.log(now);

			var distance = GBCountdown.end - now;

			console.log(distance);

			if (distance < 0) {
				GBCountdown.stopUpdating();
				document.getElementById("gbCountdown").innerHTML = "DEADLINE PASSED!";
				return;
			}

			var days = Math.floor(distance / GBCountdown._day);
			var hours = Math.floor((distance % GBCountdown._day) / GBCountdown._hour);
			var minutes = Math.floor((distance % GBCountdown._hour) / GBCountdown._minute);
			var seconds = Math.floor((distance % GBCountdown._minute) / GBCountdown._second);

			document.getElementById("gbCountdown").innerHTML = "deadline: " + days + "days ";
			document.getElementById("gbCountdown").innerHTML += hours + "hrs ";
			document.getElementById("gbCountdown").innerHTML += minutes + "mins ";
			document.getElementById("gbCountdown").innerHTML += seconds + "secs";
		}

		return GBCountdown;
	}

	if (typeof(GBCountdown) === "undefined") {
		window.GBCountdown = define_gbcountdown();
	} else {
		console.log("GBCountdown already defined.");
	}

	GBCountdown.startUpdating();

})(window);
<|endoftext|>


<commit_before>const fs = require('fs');
const path = require('path');
const { withDefaultConfig } = require('react-docgen-typescript');

const sourceFile = path.resolve(__dirname, '../../package/src/DayPicker.tsx');
const destFile = path.resolve(
  __dirname,
  '../src/components/PropDetails/prop_types.json'
);

console.log('Reading props from... %s', sourceFile);
const linesRE = /(@[a-z]*) ?(.*)$/gm;

const parseProps = obj => {
  let props = {};
  if (!obj) {
    console.log('Props not found');
    console.log(obj);
    return {};
  }
  Object.entries(obj.props).forEach(([name, details]) => {
    const categories = {};
    let matches;
    while ((matches = linesRE.exec(details.description))) {
      const category = matches[1];
      const description = matches[2] || true;
      categories[category] = description;
    }
    delete details.parent;
    const description = details.description.replace(/@.*/gim, '');
    props[name] = {
      ...details,
      description,
      categories,
    };
  });
  return props;
};

const docs = withDefaultConfig().parse(sourceFile);
const props = parseProps(docs[0]);

const json = JSON.stringify(props, null, 2);

console.log('Writing props to %s', destFile);

fs.writeFile(destFile, json, function(err) {
  if (err) {
    return console.error(err);
  }
  console.log('Done.');
});
<commit_msg>Update build props script<commit_after>const fs = require('fs');
const path = require('path');
const { withDefaultConfig } = require('react-docgen-typescript');

const sourceFile = path.resolve(__dirname, '../../package/src/DayPicker.tsx');
const destFile = path.resolve(
  __dirname,
  '../src/components/PropDetails/prop_types.json'
);

console.log('Reading props from... %s', sourceFile);
const linesRE = /(@[a-z]*) ?(.*)$/gm;

const parseProps = obj => {
  let props = {};
  if (!obj) {
    console.log('Props not found');
    console.log(obj);
    return undefined;
  }
  Object.entries(obj.props).forEach(([name, details]) => {
    const categories = {};
    let matches;
    while ((matches = linesRE.exec(details.description))) {
      const category = matches[1];
      const description = matches[2] || true;
      categories[category] = description;
    }
    delete details.parent;
    const description = details.description.replace(/@.*/gim, '');
    props[name] = {
      ...details,
      description,
      categories,
    };
  });
  return props;
};

const docs = withDefaultConfig().parse(sourceFile);
const props = parseProps(docs[0]);
if (!props) {
  process.exit();
}
const json = JSON.stringify(props, null, 2);

console.log('Writing props to %s', destFile);

fs.writeFile(destFile, json, function(err) {
  if (err) {
    return console.error(err);
  }
  console.log('Done.');
});
<|endoftext|>