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

const fs = require('fs');
const path = require('path');

const Monitor = module.exports = { // eslint-disable-line no-unused-vars

	/*********************************************************
	 * Logging
	 *********************************************************/

	log: function (text) {
		this.notice(text);
		if (Rooms('staff')) {
			Rooms('staff').add('|c|~|' + text).update();
		}
	},
	adminlog: function (text) {
		this.notice(text);
		if (Rooms('upperstaff')) {
			Rooms('upperstaff').add('|c|~|' + text).update();
		}
	},
	logHTML: function (text) {
		this.notice(text);
		if (Rooms('staff')) {
			Rooms('staff').add('|html|' + text).update();
		}
	},
	debug: function (text) {
		// console.log(text);
	},
	warn: function (text) {
		console.log(text);
	},
	notice: function (text) {
		console.log(text);
	},

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

	clean: function () {
		Monitor.networkCount = {};
		Monitor.networkUse = {};
		Monitor.battlePrepTimes = {};
		Monitor.battlePreps = {};
		Monitor.battleTimes = {};
		Monitor.battles = {};
		Monitor.connectionTimes = {};
		Monitor.connections = {};
		Dnsbl.cache.clear();
	},
	connections: {},
	connectionTimes: {},
	battles: {},
	battleTimes: {},
	battlePreps: {},
	battlePrepTimes: {},
	groupChats: {},
	groupChatTimes: {},
	networkUse: {},
	networkCount: {},
	cmds: {},
	cmdsTimes: {},
	cmdsTotal: {lastCleanup: Date.now(), count: 0},
	teamValidatorChanged: 0,
	teamValidatorUnchanged: 0,
	hotpatchLock: false,
	/**
	 * Counts a connection. Returns true if the connection should be terminated for abuse.
	 */
	countConnection: function (ip, name) {
		let now = Date.now();
		let duration = now - this.connectionTimes[ip];
		name = (name ? ': ' + name : '');
		if (ip in this.connections && duration < 30 * 60 * 1000) {
			this.connections[ip]++;
			if (this.connections[ip] === 500) {
				this.adminlog('[ResourceMonitor] IP ' + ip + ' has been banned for connection flooding (' + this.connections[ip] + ' times in the last ' + duration.duration() + name + ')');
				return true;
			} else if (this.connections[ip] > 500) {
				if (this.connections[ip] % 500 === 0) {
					let c = this.connections[ip] / 500;
					if (c === 2 || c === 4 || c === 10 || c === 20 || c % 40 === 0) {
						this.adminlog('[ResourceMonitor] Banned IP ' + ip + ' has connected ' + this.connections[ip] + ' times in the last ' + duration.duration() + name);
					}
				}
				return true;
			}
		} else {
			this.connections[ip] = 1;
			this.connectionTimes[ip] = now;
		}
	},
	/**
	 * Counts a battle. Returns true if the connection should be terminated for abuse.
	 */
	countBattle: function (ip, name) {
		let now = Date.now();
		let duration = now - this.battleTimes[ip];
		name = (name ? ': ' + name : '');
		if (ip in this.battles && duration < 30 * 60 * 1000) {
			this.battles[ip]++;
			if (duration < 5 * 60 * 1000 && this.battles[ip] % 15 === 0) {
				this.log('[ResourceMonitor] IP ' + ip + ' has battled ' + this.battles[ip] + ' times in the last ' + duration.duration() + name);
			} else if (this.battles[ip] % 75 === 0) {
				this.log('[ResourceMonitor] IP ' + ip + ' has battled ' + this.battles[ip] + ' times in the last ' + duration.duration() + name);
			}
		} else {
			this.battles[ip] = 1;
			this.battleTimes[ip] = now;
		}
	},
	/**
	 * Counts battle prep. Returns true if too much
	 */
	countPrepBattle: function (ip) {
		let now = Date.now();
		let duration = now - this.battlePrepTimes[ip];
		if (ip in this.battlePreps && duration < 3 * 60 * 1000) {
			this.battlePreps[ip]++;
			if (this.battlePreps[ip] > 6) {
				return true;
			}
		} else {
			this.battlePreps[ip] = 1;
			this.battlePrepTimes[ip] = now;
		}
	},
	/**
	 * Counts group chat creation. Returns true if too much.
	 */
	countGroupChat: function (ip) {
		let now = Date.now();
		let duration = now - this.groupChatTimes[ip];
		if (ip in this.groupChats && duration < 60 * 60 * 1000) {
			this.groupChats[ip]++;
			if (this.groupChats[ip] > 4) {
				return true;
			}
		} else {
			this.groupChats[ip] = 1;
			this.groupChatTimes[ip] = now;
		}
	},
	/**
	 * data
	 */
	countNetworkUse: function (size) {
		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: function () {
		let buf = '';
		for (let i in this.networkUse) {
			buf += '' + this.networkUse[i] + '\t' + this.networkCount[i] + '\t' + i + '\n';
		}
		fs.writeFile(path.resolve(__dirname, 'logs/networkuse.tsv'), buf);
	},
	clearNetworkUse: function () {
		this.networkUse = {};
		this.networkCount = {};
	},
	/**
	 * Counts roughly the size of an object to have an idea of the server load.
	 */
	sizeOfObject: function (object) {
		let objectList = [];
		let stack = [object];
		let bytes = 0;

		while (stack.length) {
			let value = stack.pop();
			if (typeof value === 'boolean') {
				bytes += 4;
			} else if (typeof value === 'string') {
				bytes += value.length * 2;
			} else if (typeof value === 'number') {
				bytes += 8;
			} else if (typeof value === 'object' && objectList.indexOf(value) < 0) {
				objectList.push(value);
				for (let i in value) stack.push(value[i]);
			}
		}

		return bytes;
	},
	/**
	 * Controls the amount of times a cmd command is used
	 */
	countCmd: function (ip, name) {
		let now = Date.now();
		let duration = now - this.cmdsTimes[ip];
		name = (name ? ': ' + name : '');
		if (!this.cmdsTotal) this.cmdsTotal = {lastCleanup: 0, count: 0};
		if (now - this.cmdsTotal.lastCleanup > 60 * 1000) {
			this.cmdsTotal.count = 0;
			this.cmdsTotal.lastCleanup = now;
		}
		this.cmdsTotal.count++;
		if (ip in this.cmds && duration < 60 * 1000) {
			this.cmds[ip]++;
			if (duration < 60 * 1000 && this.cmds[ip] % 5 === 0) {
				if (this.cmds[ip] >= 3) {
					if (this.cmds[ip] % 30 === 0) this.log('CMD command from ' + ip + ' blocked for ' + this.cmds[ip] + 'th use in the last ' + duration.duration() + name);
					return true;
				}
				this.log('[ResourceMonitor] IP ' + ip + ' has used CMD command ' + this.cmds[ip] + ' times in the last ' + duration.duration() + name);
			} else if (this.cmds[ip] % 15 === 0) {
				this.log('CMD command from ' + ip + ' blocked for ' + this.cmds[ip] + 'th use in the last ' + duration.duration() + name);
				return true;
			}
		} else if (this.cmdsTotal.count > 8000) {
			// One CMD check per user per minute on average (to-do: make this better)
			this.log('CMD command for ' + ip + ' blocked because CMD has been used ' + this.cmdsTotal.count + ' times in the last minute.');
			return true;
		} else {
			this.cmds[ip] = 1;
			this.cmdsTimes[ip] = now;
		}
	},
};

Monitor.cleanInterval = setInterval(Monitor.clean, 6 * 60 * 60 * 1000);
<commit_msg>Update Monitor.cleanInterval to new style<commit_after>/**
 * Monitor
 * Pokemon Showdown - http://pokemonshowdown.com/
 *
 * Various utility functions to make sure PS is running healthy.
 *
 * @license MIT license
 */
'use strict';

const fs = require('fs');
const path = require('path');

const Monitor = module.exports = { // eslint-disable-line no-unused-vars

	/*********************************************************
	 * Logging
	 *********************************************************/

	log: function (text) {
		this.notice(text);
		if (Rooms('staff')) {
			Rooms('staff').add('|c|~|' + text).update();
		}
	},
	adminlog: function (text) {
		this.notice(text);
		if (Rooms('upperstaff')) {
			Rooms('upperstaff').add('|c|~|' + text).update();
		}
	},
	logHTML: function (text) {
		this.notice(text);
		if (Rooms('staff')) {
			Rooms('staff').add('|html|' + text).update();
		}
	},
	debug: function (text) {
		// console.log(text);
	},
	warn: function (text) {
		console.log(text);
	},
	notice: function (text) {
		console.log(text);
	},

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

	clean: function () {
		Monitor.networkCount = {};
		Monitor.networkUse = {};
		Monitor.battlePrepTimes = {};
		Monitor.battlePreps = {};
		Monitor.battleTimes = {};
		Monitor.battles = {};
		Monitor.connectionTimes = {};
		Monitor.connections = {};
		Dnsbl.cache.clear();
	},
	connections: {},
	connectionTimes: {},
	battles: {},
	battleTimes: {},
	battlePreps: {},
	battlePrepTimes: {},
	groupChats: {},
	groupChatTimes: {},
	networkUse: {},
	networkCount: {},
	cmds: {},
	cmdsTimes: {},
	cmdsTotal: {lastCleanup: Date.now(), count: 0},
	teamValidatorChanged: 0,
	teamValidatorUnchanged: 0,
	hotpatchLock: false,
	/**
	 * Counts a connection. Returns true if the connection should be terminated for abuse.
	 */
	countConnection: function (ip, name) {
		let now = Date.now();
		let duration = now - this.connectionTimes[ip];
		name = (name ? ': ' + name : '');
		if (ip in this.connections && duration < 30 * 60 * 1000) {
			this.connections[ip]++;
			if (this.connections[ip] === 500) {
				this.adminlog('[ResourceMonitor] IP ' + ip + ' has been banned for connection flooding (' + this.connections[ip] + ' times in the last ' + duration.duration() + name + ')');
				return true;
			} else if (this.connections[ip] > 500) {
				if (this.connections[ip] % 500 === 0) {
					let c = this.connections[ip] / 500;
					if (c === 2 || c === 4 || c === 10 || c === 20 || c % 40 === 0) {
						this.adminlog('[ResourceMonitor] Banned IP ' + ip + ' has connected ' + this.connections[ip] + ' times in the last ' + duration.duration() + name);
					}
				}
				return true;
			}
		} else {
			this.connections[ip] = 1;
			this.connectionTimes[ip] = now;
		}
	},
	/**
	 * Counts a battle. Returns true if the connection should be terminated for abuse.
	 */
	countBattle: function (ip, name) {
		let now = Date.now();
		let duration = now - this.battleTimes[ip];
		name = (name ? ': ' + name : '');
		if (ip in this.battles && duration < 30 * 60 * 1000) {
			this.battles[ip]++;
			if (duration < 5 * 60 * 1000 && this.battles[ip] % 15 === 0) {
				this.log('[ResourceMonitor] IP ' + ip + ' has battled ' + this.battles[ip] + ' times in the last ' + duration.duration() + name);
			} else if (this.battles[ip] % 75 === 0) {
				this.log('[ResourceMonitor] IP ' + ip + ' has battled ' + this.battles[ip] + ' times in the last ' + duration.duration() + name);
			}
		} else {
			this.battles[ip] = 1;
			this.battleTimes[ip] = now;
		}
	},
	/**
	 * Counts battle prep. Returns true if too much
	 */
	countPrepBattle: function (ip) {
		let now = Date.now();
		let duration = now - this.battlePrepTimes[ip];
		if (ip in this.battlePreps && duration < 3 * 60 * 1000) {
			this.battlePreps[ip]++;
			if (this.battlePreps[ip] > 6) {
				return true;
			}
		} else {
			this.battlePreps[ip] = 1;
			this.battlePrepTimes[ip] = now;
		}
	},
	/**
	 * Counts group chat creation. Returns true if too much.
	 */
	countGroupChat: function (ip) {
		let now = Date.now();
		let duration = now - this.groupChatTimes[ip];
		if (ip in this.groupChats && duration < 60 * 60 * 1000) {
			this.groupChats[ip]++;
			if (this.groupChats[ip] > 4) {
				return true;
			}
		} else {
			this.groupChats[ip] = 1;
			this.groupChatTimes[ip] = now;
		}
	},
	/**
	 * data
	 */
	countNetworkUse: function (size) {
		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: function () {
		let buf = '';
		for (let i in this.networkUse) {
			buf += '' + this.networkUse[i] + '\t' + this.networkCount[i] + '\t' + i + '\n';
		}
		fs.writeFile(path.resolve(__dirname, 'logs/networkuse.tsv'), buf);
	},
	clearNetworkUse: function () {
		this.networkUse = {};
		this.networkCount = {};
	},
	/**
	 * Counts roughly the size of an object to have an idea of the server load.
	 */
	sizeOfObject: function (object) {
		let objectList = [];
		let stack = [object];
		let bytes = 0;

		while (stack.length) {
			let value = stack.pop();
			if (typeof value === 'boolean') {
				bytes += 4;
			} else if (typeof value === 'string') {
				bytes += value.length * 2;
			} else if (typeof value === 'number') {
				bytes += 8;
			} else if (typeof value === 'object' && objectList.indexOf(value) < 0) {
				objectList.push(value);
				for (let i in value) stack.push(value[i]);
			}
		}

		return bytes;
	},
	/**
	 * Controls the amount of times a cmd command is used
	 */
	countCmd: function (ip, name) {
		let now = Date.now();
		let duration = now - this.cmdsTimes[ip];
		name = (name ? ': ' + name : '');
		if (!this.cmdsTotal) this.cmdsTotal = {lastCleanup: 0, count: 0};
		if (now - this.cmdsTotal.lastCleanup > 60 * 1000) {
			this.cmdsTotal.count = 0;
			this.cmdsTotal.lastCleanup = now;
		}
		this.cmdsTotal.count++;
		if (ip in this.cmds && duration < 60 * 1000) {
			this.cmds[ip]++;
			if (duration < 60 * 1000 && this.cmds[ip] % 5 === 0) {
				if (this.cmds[ip] >= 3) {
					if (this.cmds[ip] % 30 === 0) this.log('CMD command from ' + ip + ' blocked for ' + this.cmds[ip] + 'th use in the last ' + duration.duration() + name);
					return true;
				}
				this.log('[ResourceMonitor] IP ' + ip + ' has used CMD command ' + this.cmds[ip] + ' times in the last ' + duration.duration() + name);
			} else if (this.cmds[ip] % 15 === 0) {
				this.log('CMD command from ' + ip + ' blocked for ' + this.cmds[ip] + 'th use in the last ' + duration.duration() + name);
				return true;
			}
		} else if (this.cmdsTotal.count > 8000) {
			// One CMD check per user per minute on average (to-do: make this better)
			this.log('CMD command for ' + ip + ' blocked because CMD has been used ' + this.cmdsTotal.count + ' times in the last minute.');
			return true;
		} else {
			this.cmds[ip] = 1;
			this.cmdsTimes[ip] = now;
		}
	},
};

Monitor.cleanInterval = setInterval(() => Monitor.clean(), 6 * 60 * 60 * 1000);
<|endoftext|>


<commit_before>#!/usr/bin/env node
'use strict';

var exec = require('child_process').exec;
var express = require('express');
var app = express();

var speak = require('./controllers/speak');
var joke = require('./controllers/joke');
var sentences = require('./data/sentences');

// setup API and client
app.use('/', express.static(__dirname + '/public'));

var bodyParser = require('body-parser');
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({
	extended: true
}));

app.get('/api/ping', function (req, res) {
	res.send(speak.isSpeaking());
});

app.post('/api/joke', function (req, res) {
	joke(function () {
		res.sendStatus(200);
	});
});

app.post('/api/fun', function (req, res) {
	speak.say('Comme ma bite', function () {
		res.sendStatus(200);
	});
});

app.listen(3000, function () {
	console.log('MrRobot has started on port 3000');
	setTimeout(function () {
		var command = 'chromium-browser --kiosk http://localhost:3000';
		exec(command, function (error, stdout, stderr) {
			console.error(stderr);
			console.log(stdout);
		});
	}, 10000);
});

setTimeout(function() {
	// say startup fun
	speak.say(sentences.getStartup());
}, 3000);
<commit_msg>pouet<commit_after>#!/usr/bin/env node
'use strict';

var exec = require('child_process').exec;
var express = require('express');
var app = express();

var speak = require('./controllers/speak');
var joke = require('./controllers/joke');
var sentences = require('./data/sentences');

// setup API and client
app.use('/', express.static(__dirname + '/public'));

var bodyParser = require('body-parser');
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({
	extended: true
}));

app.get('/api/ping', function (req, res) {
	res.send(speak.isSpeaking());
});

app.post('/api/joke', function (req, res) {
	joke(function () {
		res.sendStatus(200);
	});
});

app.post('/api/fun', function (req, res) {
	speak.say('Comme ma bite', function () {
		res.sendStatus(200);
	});
});

app.listen(3000, function () {
	console.log('MrRobot has started on port 3000');
	// setTimeout(function () {
	// 	var command = 'chromium-browser --kiosk http://localhost:3000';
	// 	exec(command, function (error, stdout, stderr) {
	// 		console.error(stderr);
	// 		console.log(stdout);
	// 	});
	// }, 10000);
});

setTimeout(function() {
	// say startup fun
	speak.say(sentences.getStartup());
}, 3000);
<|endoftext|>


<commit_before>var Util   = require("util");
var Bot    = require("./lib/irc");
var moment = require('moment')
var boo    = require('boo')
var spice  = require('spice')
var profile = require('./nanoprofile')

function ensure_not_active(bot, cx) {
  if (!bot.current_ww.active)
    return cx.channel.send_reply(cx.sender, "No WordWar active.")
  else
    return true
}

function string_to_time(hhmm) {
  var start = moment(hhmm, 'H:m')
  if(!start.isValid())
    return false

  if(start.isBefore(new Date()))
    start.add('days', 1)

  return start
}

var WordWar = boo.Base.derive({
  init:
  function _init() {
    this.open   = false
    this.active = false
    this.timers = []
  }

, activate:
  function _activate(sender, minutes, start_at) {
    this.participants = [sender]
    this.starter      = sender
    this.open         = true
    this.active       = true
    this.time         = minutes
    this.start_at     = start_at
  }

, stop:
  function _stop() {
    this.open   = false
    this.active = false
    this.timers.forEach(clearTimeout)
    this.timers = []
  }

, join:
  function _add(name) {
    this.participants.push(name)
  }

, part:
  function _part(name) {
    this.participants = this.participants.filter(function(a){ return a !== name })
  }

, is_participating:
  function _is_participating(name) {
    return this.participants.some(function(a){ return a === name })
  }

, notify:
  function _notify() {
    var start_at      = this.start_at
    var start_display = start_at? this.start_at.format('HH:mm') : ''
    return spice('{:sender} is asking for a {:minutes} minutes WordWar{:start}!'
                +' Type "!join" to participate. Type "!start" to begin{:now}.'
                , { start   : start_at? ' to start at ' + start_display : ' '
                  , now     : start_at? ' now' : ''
                  , sender  : this.starter
                  , minutes : this.time })
  }

, notify_start:
  function _notify_start() {
    this.open       = false
    this.start_time = moment(new Date)
    this.end_time   = moment(new Date).add('minutes', this.time)
    return spice('WordWar from {:start} to {:end} starting. Go {:participants}!'
                , { participants: this.participants.join(', ')
                  , start:        this.start_time.format('HH:mm')
                  , end:          this.end_time.format('HH:mm')
                  })
  }

, notify_end:
  function _notify_end() {
    return spice('WordWar ended, {:participants}.'
                , { participants: this.participants.join(', ') })
  }

, notify_status:
  function _notify_status() {
    return spice('WordWar started on {:start} and will end on {:end}'
                +' ({:minutes} minutes left).'
                +' {:participants} nanowriters are in.'
                , { participants: this.participants.length
                  , start:        this.start_time.format('HH:mm')
                  , end:          this.end_time.format('HH:mm')
                  , minutes:      this.end_time.diff(new Date, 'minutes')
                  })
  }
})

Util.inherits(NanoBot, Bot)
function NanoBot(profile) {
  Bot.call(this, profile)
  this.set_log_level(this.LOG_ALL)
  this.set_trigger("!")
};

NanoBot.prototype.init = function() {
  Bot.prototype.init.call(this)

  this.current_ww = WordWar.make()

  this.register_command("ww", this.ww, {
    allow_intentions: false,
    help: 'Asks for a new WordWar in the channel. You can schedule the WordWar to have a particular duration, and start on a specific time. The default is a 20 minutes WordWar with manual start (see "!start"). Usage: !ww [minutes] [time] — e.g.: !ww 30 12:34'
  })
  this.register_command("stop", this.stop_ww, {
    allow_intentions: false,
    help: 'Stops an on-going WordWar, or a previously scheduled but not yet started one. Usage: !stop'
  })
  this.register_command("join", this.join_ww, {
    allow_intentions: false,
    help: 'Joins a scheduled or on-going WordWar. People who schedule the WordWar are considered to be already participating. To leave a WordWar, see !part. Usage: !join'
  })
  this.register_command("start", this.start_ww, {
    allow_intentions: false,
    help: 'Starts a scheduled WordWar. People who are participating will be notified and the bot will notify all participants when the WordWar ends. Usage: !start'
  })
  this.register_command("part", this.part_ww, {
    allow_intentions: false,
    help: 'Leaves a WordWar you\'re participating. Usage: !part. (aliases: !quit, !leave)'
  })
  this.register_command("quit", "part")
  this.register_command("leave", "part")
  this.register_command("status", this.status_ww, {
    help: 'Tells you if there\'s any WordWar going on in the channel, or if a WordWar has been scheduled. You can append "@ user" to your message to direct the information to someone in the channel. Usage: !status'
  })
  this.register_command("help", this.help)
  this.register_command("commands", this.commands)

  this.on('command_not_found', this.unrecognized)

};

NanoBot.prototype.ww = function(cx, text) {
  var args = text.split(/\s+/)
  var minutes = Number(args[0])
  var start_at = false;
  if (this.current_ww.active)
    return cx.channel.send_reply(cx.sender, "There's a WordWar going on already!")
  if (isNaN(minutes) || minutes < 0)
    return cx.channel.send_reply(cx.sender,this.get_command_help("ww"))

  if(args.length >= 2) {
    start_at = string_to_time(args[1])
    if (start_at === false)
      return cx.channel.send_reply(cx.sender, this.get_command_help("ww"))
    else
      this.current_ww.timers.push(setTimeout(function() {
        this.start_ww(cx, '')
      }.bind(this), start_at.diff(new Date())))
  }

  this.current_ww.activate(cx.sender, minutes || 20, start_at)
  cx.channel.send(this.current_ww.notify(start_at))
};

NanoBot.prototype.start_ww = function(cx, text) {
  if (!ensure_not_active(this, cx)) return
  if (!this.current_ww.open)
    return cx.channel.send_reply(cx.sender, "There's a WordWar going on already. " + this.current_ww.notify_status())

  this.current_ww.open = false
  this.current_ww.timers.push(setTimeout(function() {
      cx.channel.send(this.current_ww.notify_end())
      this.current_ww.stop()
  }.bind(this), this.current_ww.time * 60 * 1000))
  cx.channel.send(this.current_ww.notify_start())
}

NanoBot.prototype.stop_ww = function(cx, text) {
  if (!ensure_not_active(this, cx))  return

  this.current_ww.stop()
  cx.channel.send_reply(cx.sender, "WordWar stopped.")
}

NanoBot.prototype.join_ww = function(cx, text) {
  if (!ensure_not_active(this, cx))  return
  if (this.current_ww.is_participating(cx.sender))
    return cx.channel.send_reply(cx.sender, "You're already in.")

  this.current_ww.join(cx.sender)
  cx.channel.send(cx.sender + " joined the WordWar")
}

NanoBot.prototype.part_ww = function(cx, text) {
  if (!ensure_not_active(this, cx))  return
  if (this.current_ww.is_participating(cx.sender)) {
    this.current_ww.part(cx.sender)
    cx.channel.send(cx.sender + " left the WordWar")
  }
}

NanoBot.prototype.status_ww = function(cx, text) {
  if (!ensure_not_active(this, cx))  return
  if (this.current_ww.open) {
    cx.channel.send_reply(cx.intent, 'There\'s a ' + this.current_ww.time + ' minutes word war, but it hasn\'t started yet. '
    + (this.current_ww.start_at ? 'It\'ll start at ' + this.current_ww.start_at.format('HH:mm') + '. ' : '')
    + (this.current_ww.is_participating(cx.intent) ? 'You\'re already in.' : 'You can type "!join" to participate.'))
  }
  else
    cx.channel.send_reply(cx.intent, this.current_ww.notify_status() + (this.current_ww.is_participating(cx.intent) ? ' You\'re already in.' : ' You can type "!join" to participate.'))
}

NanoBot.prototype.help = function(cx, text) {
  try {
    if (!text) return this.unrecognized(cx, text)
    cx.channel.send_reply(cx.intent, this.get_command_help(text))
  } catch (e) {
    cx.channel.send_reply(cx.sender, e)
  }
}

NanoBot.prototype.commands = function(cx, text) {
  var trigger  = this.__trigger
  var commands = this.get_commands().map(function(a){ return trigger + a })
  cx.channel.send_reply(cx.intent, "Valid commands are: " + commands.join(', '))
}

NanoBot.prototype.unrecognized = function(cx, text) {
	cx.channel.send_reply(cx.sender, "There is no command: "+text)
}

;(new NanoBot(profile)).init()<commit_msg>Sends the information to the correct in the intent.<commit_after>var Util   = require("util");
var Bot    = require("./lib/irc");
var moment = require('moment')
var boo    = require('boo')
var spice  = require('spice')
var profile = require('./nanoprofile')

function ensure_not_active(bot, cx) {
  if (!bot.current_ww.active)
    return cx.channel.send_reply(cx.intent || cx.sender, "No WordWar active.")
  else
    return true
}

function string_to_time(hhmm) {
  var start = moment(hhmm, 'H:m')
  if(!start.isValid())
    return false

  if(start.isBefore(new Date()))
    start.add('days', 1)

  return start
}

var WordWar = boo.Base.derive({
  init:
  function _init() {
    this.open   = false
    this.active = false
    this.timers = []
  }

, activate:
  function _activate(sender, minutes, start_at) {
    this.participants = [sender]
    this.starter      = sender
    this.open         = true
    this.active       = true
    this.time         = minutes
    this.start_at     = start_at
  }

, stop:
  function _stop() {
    this.open   = false
    this.active = false
    this.timers.forEach(clearTimeout)
    this.timers = []
  }

, join:
  function _add(name) {
    this.participants.push(name)
  }

, part:
  function _part(name) {
    this.participants = this.participants.filter(function(a){ return a !== name })
  }

, is_participating:
  function _is_participating(name) {
    return this.participants.some(function(a){ return a === name })
  }

, notify:
  function _notify() {
    var start_at      = this.start_at
    var start_display = start_at? this.start_at.format('HH:mm') : ''
    return spice('{:sender} is asking for a {:minutes} minutes WordWar{:start}!'
                +' Type "!join" to participate. Type "!start" to begin{:now}.'
                , { start   : start_at? ' to start at ' + start_display : ' '
                  , now     : start_at? ' now' : ''
                  , sender  : this.starter
                  , minutes : this.time })
  }

, notify_start:
  function _notify_start() {
    this.open       = false
    this.start_time = moment(new Date)
    this.end_time   = moment(new Date).add('minutes', this.time)
    return spice('WordWar from {:start} to {:end} starting. Go {:participants}!'
                , { participants: this.participants.join(', ')
                  , start:        this.start_time.format('HH:mm')
                  , end:          this.end_time.format('HH:mm')
                  })
  }

, notify_end:
  function _notify_end() {
    return spice('WordWar ended, {:participants}.'
                , { participants: this.participants.join(', ') })
  }

, notify_status:
  function _notify_status() {
    return spice('WordWar started on {:start} and will end on {:end}'
                +' ({:minutes} minutes left).'
                +' {:participants} nanowriters are in.'
                , { participants: this.participants.length
                  , start:        this.start_time.format('HH:mm')
                  , end:          this.end_time.format('HH:mm')
                  , minutes:      this.end_time.diff(new Date, 'minutes')
                  })
  }
})

Util.inherits(NanoBot, Bot)
function NanoBot(profile) {
  Bot.call(this, profile)
  this.set_log_level(this.LOG_ALL)
  this.set_trigger("!")
};

NanoBot.prototype.init = function() {
  Bot.prototype.init.call(this)

  this.current_ww = WordWar.make()

  this.register_command("ww", this.ww, {
    allow_intentions: false,
    help: 'Asks for a new WordWar in the channel. You can schedule the WordWar to have a particular duration, and start on a specific time. The default is a 20 minutes WordWar with manual start (see "!start"). Usage: !ww [minutes] [time] — e.g.: !ww 30 12:34'
  })
  this.register_command("stop", this.stop_ww, {
    allow_intentions: false,
    help: 'Stops an on-going WordWar, or a previously scheduled but not yet started one. Usage: !stop'
  })
  this.register_command("join", this.join_ww, {
    allow_intentions: false,
    help: 'Joins a scheduled or on-going WordWar. People who schedule the WordWar are considered to be already participating. To leave a WordWar, see !part. Usage: !join'
  })
  this.register_command("start", this.start_ww, {
    allow_intentions: false,
    help: 'Starts a scheduled WordWar. People who are participating will be notified and the bot will notify all participants when the WordWar ends. Usage: !start'
  })
  this.register_command("part", this.part_ww, {
    allow_intentions: false,
    help: 'Leaves a WordWar you\'re participating. Usage: !part. (aliases: !quit, !leave)'
  })
  this.register_command("quit", "part")
  this.register_command("leave", "part")
  this.register_command("status", this.status_ww, {
    help: 'Tells you if there\'s any WordWar going on in the channel, or if a WordWar has been scheduled. You can append "@ user" to your message to direct the information to someone in the channel. Usage: !status'
  })
  this.register_command("help", this.help)
  this.register_command("commands", this.commands)

  this.on('command_not_found', this.unrecognized)

};

NanoBot.prototype.ww = function(cx, text) {
  var args = text.split(/\s+/)
  var minutes = Number(args[0])
  var start_at = false;
  if (this.current_ww.active)
    return cx.channel.send_reply(cx.sender, "There's a WordWar going on already!")
  if (isNaN(minutes) || minutes < 0)
    return cx.channel.send_reply(cx.sender,this.get_command_help("ww"))

  if(args.length >= 2) {
    start_at = string_to_time(args[1])
    if (start_at === false)
      return cx.channel.send_reply(cx.sender, this.get_command_help("ww"))
    else
      this.current_ww.timers.push(setTimeout(function() {
        this.start_ww(cx, '')
      }.bind(this), start_at.diff(new Date())))
  }

  this.current_ww.activate(cx.sender, minutes || 20, start_at)
  cx.channel.send(this.current_ww.notify(start_at))
};

NanoBot.prototype.start_ww = function(cx, text) {
  if (!ensure_not_active(this, cx)) return
  if (!this.current_ww.open)
    return cx.channel.send_reply(cx.sender, "There's a WordWar going on already. " + this.current_ww.notify_status())

  this.current_ww.open = false
  this.current_ww.timers.push(setTimeout(function() {
      cx.channel.send(this.current_ww.notify_end())
      this.current_ww.stop()
  }.bind(this), this.current_ww.time * 60 * 1000))
  cx.channel.send(this.current_ww.notify_start())
}

NanoBot.prototype.stop_ww = function(cx, text) {
  if (!ensure_not_active(this, cx))  return

  this.current_ww.stop()
  cx.channel.send_reply(cx.sender, "WordWar stopped.")
}

NanoBot.prototype.join_ww = function(cx, text) {
  if (!ensure_not_active(this, cx))  return
  if (this.current_ww.is_participating(cx.sender))
    return cx.channel.send_reply(cx.sender, "You're already in.")

  this.current_ww.join(cx.sender)
  cx.channel.send(cx.sender + " joined the WordWar")
}

NanoBot.prototype.part_ww = function(cx, text) {
  if (!ensure_not_active(this, cx))  return
  if (this.current_ww.is_participating(cx.sender)) {
    this.current_ww.part(cx.sender)
    cx.channel.send(cx.sender + " left the WordWar")
  }
}

NanoBot.prototype.status_ww = function(cx, text) {
  if (!ensure_not_active(this, cx))  return
  if (this.current_ww.open) {
    cx.channel.send_reply(cx.intent, 'There\'s a ' + this.current_ww.time + ' minutes word war, but it hasn\'t started yet. '
    + (this.current_ww.start_at ? 'It\'ll start at ' + this.current_ww.start_at.format('HH:mm') + '. ' : '')
    + (this.current_ww.is_participating(cx.intent) ? 'You\'re already in.' : 'You can type "!join" to participate.'))
  }
  else
    cx.channel.send_reply(cx.intent, this.current_ww.notify_status() + (this.current_ww.is_participating(cx.intent) ? ' You\'re already in.' : ' You can type "!join" to participate.'))
}

NanoBot.prototype.help = function(cx, text) {
  try {
    if (!text) return this.unrecognized(cx, text)
    cx.channel.send_reply(cx.intent, this.get_command_help(text))
  } catch (e) {
    cx.channel.send_reply(cx.sender, e)
  }
}

NanoBot.prototype.commands = function(cx, text) {
  var trigger  = this.__trigger
  var commands = this.get_commands().map(function(a){ return trigger + a })
  cx.channel.send_reply(cx.intent, "Valid commands are: " + commands.join(', '))
}

NanoBot.prototype.unrecognized = function(cx, text) {
	cx.channel.send_reply(cx.sender, "There is no command: "+text)
}

;(new NanoBot(profile)).init()<|endoftext|>


<commit_before>// Inform the background page that
// this tab should have a page-action

chrome.runtime.sendMessage({
  from:    'content',
  subject: 'showPageAction'
});

// Listen for messages from the popup
chrome.runtime.onMessage.addListener(function (msg, sender, response) {
  // First, validate the message's structure
  if ((msg.from === 'popup') && (msg.subject === 'DOMInfo')) {
    // Collect the necessary data
    var pageData = ""

    //For students who HAVE applied for graduation
    try{
      pageData = document.getElementsByClassName("pagebodydiv")[0].children[1].innerText;

      if(pageData.includes("Subj")){
        //Add valid text to domInfo
        var domInfo = {
          data: pageData
        }
        // Directly respond to the sender (popup),
        // through the specified callback
        response(domInfo);
        return;
      }
    }catch (e){
      console.log("Caught error from student applied block (content.js)");
    }
    //For advisers
    try{
      pageData = document.getElementsByClassName("pagebodydiv")[0].children[2].innerText;

      if(pageData.includes("Subj")){
        //Add valid text to domInfo
        var domInfo = {
          data: pageData
        }
        // Directly respond to the sender (popup),
        // through the specified callback
        response(domInfo);
        return;
      }
    }catch (f){
      console.log("Caught error from advisor block (content.js)");
    }
    //For students who have NOT applied for graduation
    try{
      pageData = document.getElementsByClassName("pagebodydiv")[0].children[3].innerText;

      if(pageData.includes("Subj")){
        //Add valid text to domInfo
        var domInfo = {
          data: pageData
        }
        // Directly respond to the sender (popup),
        // through the specified callback
        response(domInfo);
        return;
      }
    }catch (g){
      console.log("Caught error from student not applied block (content.js)");
    }

  }
});

function formatForPrint(){

  console.log("running improved print function");

  var printWindow = window.open('', 'PRINT', 'height=800,width=700');

  // Setup the title and header for the print document
  printWindow.document.write('<link rel="stylesheet" href="../css/print.css" type="text/css" media="all">');
  printWindow.document.write('<title>Unofficial GPA</title>');
  printWindow.document.write('
');

  //Create necessary variables
  var classList = [];
  var mode = 'UNSET';
  var gpa = 0.0;
  var credits = 0;
  var name = 'UNSET';

  // get name from chrome storage
  chrome.storage.sync.get('studentName', function(result){
    name = result.studentName;
    console.log('name: ' + name);
  });

  //Get mode and store it in variable
  //console.log("getting mode");
  chrome.storage.sync.get('mode', function(result){
  //Check if mode has been set
    if(typeof(result.mode) != "undefined"){
      //Set mode variable
      if(result.mode === 'true'){
        mode = 'CBE';
      }else{
        mode = 'MSCM';
      }
      console.debug("Mode: " + mode);
    }
  });

  // Get gpa
  console.log("Getting GPA");
  chrome.storage.sync.get('gradeInfo', function(result){

    console.log(result.gradeInfo);
    //Check if mode has been set
    if(typeof(result.gradeInfo) != "undefined"){
      console.log("adding header info");

      //Append mode  and title to print page
      //printWindow.document.write("
Academic History
");

      printWindow.document.write('

');
      printWindow.document.write('
Name: ');
      printWindow.document.write(name[1].replace(',', '') + ' ' + name[0].replace(',', ''));
      printWindow.document.write('
');
      printWindow.document.write('
Program: ' + mode + '
');
      printWindow.document.write('
Progam GPA: ' + result.gradeInfo[0] + '
');
      printWindow.document.write('
Total Credits: ' + result.gradeInfo[1] + '
');
      printWindow.document.write('

');
    }

  });

  //Get classes and store them in variable
  console.log("getting CBE classes");
  chrome.storage.sync.get('CBEclasses', function(result){
    //Check to see if there are classes saved in storage
    console.log("checking class list");

    // add classes to page if there are any to add
    if((typeof(result.CBEclasses) != "undefined") && (result.CBEclasses.length > 0)){

      //Set classList variable
      console.log("setting class list");
      classList = result.CBEclasses;
      console.debug(classList);

      // setup class table
      printWindow.document.write('
');
      printWindow.document.write('
Class	Class GPA	Credits
');

      //Loop to append each class to page
      for(var i = 0 ; i < classList.length ; i++){
        console.log("adding class");
        printWindow.document.write('
' + classList[i].name + '	');
        printWindow.document.write('' + classList[i].gpa + '	');
        printWindow.document.write('' + classList[i].credits + '
');
      }

      printWindow.document.write( "
");
      printWindow.document.write('');
      printWindow.document.close();
      printWindow.focus();

      //print the output
      setTimeout(function(){printWindow.print();},1000);
      setTimeout(function(){printWindow.close();},1000);
    }

  });
}

chrome.runtime.onMessage.addListener(
  function(message, sender, sendResponse) {
    console.log(sender.tab ?
                "from a content script:" + sender.tab.url :
                "from the extension");
    //console.log(sender + message + sendResponse);
    if (message.action == "print"){
        console.log("print message recieved");
      console.debug("Message received");
      formatForPrint();
    }
  }
);
<commit_msg>changed the print output to print the grade letter instead of the numeric value<commit_after>// Inform the background page that
// this tab should have a page-action

chrome.runtime.sendMessage({
  from:    'content',
  subject: 'showPageAction'
});

// Listen for messages from the popup
chrome.runtime.onMessage.addListener(function (msg, sender, response) {
  // First, validate the message's structure
  if ((msg.from === 'popup') && (msg.subject === 'DOMInfo')) {
    // Collect the necessary data
    var pageData = ""

    //For students who HAVE applied for graduation
    try{
      pageData = document.getElementsByClassName("pagebodydiv")[0].children[1].innerText;

      if(pageData.includes("Subj")){
        //Add valid text to domInfo
        var domInfo = {
          data: pageData
        }
        // Directly respond to the sender (popup),
        // through the specified callback
        response(domInfo);
        return;
      }
    }catch (e){
      console.log("Caught error from student applied block (content.js)");
    }
    //For advisers
    try{
      pageData = document.getElementsByClassName("pagebodydiv")[0].children[2].innerText;

      if(pageData.includes("Subj")){
        //Add valid text to domInfo
        var domInfo = {
          data: pageData
        }
        // Directly respond to the sender (popup),
        // through the specified callback
        response(domInfo);
        return;
      }
    }catch (f){
      console.log("Caught error from advisor block (content.js)");
    }
    //For students who have NOT applied for graduation
    try{
      pageData = document.getElementsByClassName("pagebodydiv")[0].children[3].innerText;

      if(pageData.includes("Subj")){
        //Add valid text to domInfo
        var domInfo = {
          data: pageData
        }
        // Directly respond to the sender (popup),
        // through the specified callback
        response(domInfo);
        return;
      }
    }catch (g){
      console.log("Caught error from student not applied block (content.js)");
    }

  }
});

function formatForPrint(){

  console.log("running improved print function");

  var printWindow = window.open('', 'PRINT', 'height=800,width=700');

  // Setup the title and header for the print document
  printWindow.document.write('<link rel="stylesheet" href="../css/print.css" type="text/css" media="all">');
  printWindow.document.write('<title>Unofficial GPA</title>');
  printWindow.document.write('
');

  //Create necessary variables
  var classList = [];
  var mode = 'UNSET';
  var gpa = 0.0;
  var credits = 0;
  var name = 'UNSET';

  // get name from chrome storage
  chrome.storage.sync.get('studentName', function(result){
    name = result.studentName;
    console.log('name: ' + name);
  });

  //Get mode and store it in variable
  //console.log("getting mode");
  chrome.storage.sync.get('mode', function(result){
  //Check if mode has been set
    if(typeof(result.mode) != "undefined"){
      //Set mode variable
      if(result.mode === 'true'){
        mode = 'CBE';
      }else{
        mode = 'MSCM';
      }
      console.debug("Mode: " + mode);
    }
  });

  // Get gpa
  console.log("Getting GPA");
  chrome.storage.sync.get('gradeInfo', function(result){

    console.log(result.gradeInfo);
    //Check if mode has been set
    if(typeof(result.gradeInfo) != "undefined"){
      console.log("adding header info");

      //Append mode  and title to print page
      //printWindow.document.write("
Academic History
");

      printWindow.document.write('

');
      printWindow.document.write('
Name: ');
      printWindow.document.write(name[1].replace(',', '') + ' ' + name[0].replace(',', ''));
      printWindow.document.write('
');
      printWindow.document.write('
Program: ' + mode + '
');
      printWindow.document.write('
Progam GPA: ' + result.gradeInfo[0] + '
');
      printWindow.document.write('
Total Credits: ' + result.gradeInfo[1] + '
');
      printWindow.document.write('

');
    }

  });

  //Get classes and store them in variable
  console.log("getting CBE classes");
  chrome.storage.sync.get('CBEclasses', function(result){
    //Check to see if there are classes saved in storage
    console.log("checking class list");

    // add classes to page if there are any to add
    if((typeof(result.CBEclasses) != "undefined") && (result.CBEclasses.length > 0)){

      //Set classList variable
      console.log("setting class list");
      classList = result.CBEclasses;
      console.debug(classList);

      // setup class table
      printWindow.document.write('
');
      printWindow.document.write('
Class	Class GPA	Credits
');

      //Loop to append each class to page
      for(var i = 0 ; i < classList.length ; i++){

        //find the letter for the gpa of a give class
        var gpa = classList[i].gpa;
        var gpa_letter = "";

        switch(gpa){
            case("4.0"):
                gpa_letter = "A";
                break;
            case("3.7"):
                gpa_letter = "A-";
                break;
            case("3.3"):
                gpa_letter = "B+";
                break;
            case("3.0"):
                gpa_letter = "B";
                break;
            case("2.7"):
                gpa_letter = "B-";
                break;
            case("2.3"):
                gpa_letter = "C-";
                break;
            case("2.0"):
                gpa_letter = "C";
                break;
            case("1.7"):
                gpa_letter = "C-";
                break;
            case("1.3"):
                gpa_letter = "D+";
                break;
            case("1.0"):
                gpa_letter = "D";
                break;
            case("0.0"):
                gpa_letter = "F";
                break;
            case("-1.0"):
                gpa_letter = "K";
                break;
            default:
                gpa_letter = "N/A";

        }

        console.log("adding class");
        printWindow.document.write('
' + classList[i].name + '	');

        //printWindow.document.write('' + classList[i].gpa + '	');
        printWindow.document.write('' + gpa_letter + '	');

        printWindow.document.write('' + classList[i].credits + '
');
      }

      printWindow.document.write( "
");
      printWindow.document.write('');
      printWindow.document.close();
      printWindow.focus();

      //print the output
      setTimeout(function(){printWindow.print();},1000);
      setTimeout(function(){printWindow.close();},1000);
    }

  });
}

chrome.runtime.onMessage.addListener(
  function(message, sender, sendResponse) {
    console.log(sender.tab ?
                "from a content script:" + sender.tab.url :
                "from the extension");
    //console.log(sender + message + sendResponse);
    if (message.action == "print"){
        console.log("print message recieved");
      console.debug("Message received");
      formatForPrint();
    }
  }
);
<|endoftext|>


<commit_before>'use strict';

var path = require('path');
var webpack = require('webpack');
var ExtractTextPlugin = require("extract-text-webpack-plugin");

var config = {
  entry: path.resolve(__dirname, 'demo.js'),

  output: {
    path: path.resolve(__dirname, '../tmp'),
    filename: 'bundle.js'
  },

  resolve: {
    extensions: ['', '.js'],
  },

  module: {
    loaders: [
      {
        test: /\.js?$/,
        exclude: /node_modules/,
        loader: 'babel',
      },
      {
        test: /\.css$/,
        exclude: /node_modules/,
        loader: ExtractTextPlugin.extract("style-loader", "css-loader")
      }
    ]
  },

  plugins: [
    new ExtractTextPlugin("../tmp/bundle.css"),
    new webpack.optimize.OccurenceOrderPlugin(),
    new webpack.optimize.UglifyJsPlugin({
      compress: {
        warnings: false
      }
    })
  ]
};

module.exports = config;
<commit_msg>fix tmp path, changed to website<commit_after>'use strict';

var path = require('path');
var webpack = require('webpack');
var ExtractTextPlugin = require("extract-text-webpack-plugin");

var config = {
  entry: path.resolve(__dirname, 'demo.js'),

  output: {
    path: path.resolve(__dirname, '../website'),
    filename: 'bundle.js'
  },

  resolve: {
    extensions: ['', '.js'],
  },

  module: {
    loaders: [
      {
        test: /\.js?$/,
        exclude: /node_modules/,
        loader: 'babel',
      },
      {
        test: /\.css$/,
        exclude: /node_modules/,
        loader: ExtractTextPlugin.extract("style-loader", "css-loader")
      }
    ]
  },

  plugins: [
    new ExtractTextPlugin("../website/bundle.css"),
    new webpack.optimize.OccurenceOrderPlugin(),
    new webpack.optimize.UglifyJsPlugin({
      compress: {
        warnings: false
      }
    })
  ]
};

module.exports = config;
<|endoftext|>


<commit_before>define(["dojo/_base/declare", "./PersistentObject", "dojo/date"],
    function(declare, PersistentObject, dojoDate) {

      function internalReload(/*AuditableObject*/ self, /*Object*/ json) {
        if (json && json.createdAt && json.createdBy && json.lastModifiedAt && json.lastModifiedBy /* TODO json.... undefined, but not null */) {
          // all data or nothing;
          // TODO add precondition for this
          if ((self.createdAt || self.createdBy) &&
              ((self.createdAt && (dojoDate.compare(json.createdAt, self.createdAt) !== 0)) ||
               (self.createdBy && json.createdBy != self.createdBy))) {
            throw "ERROR cannot change from existing created information"; // TODO precondition
          }
          // MUDO HACK see below
          if (json.lastModifiedAt.indexOf("+00:00") < 0) {
            // there is no timezone information in the string
            json.lastModifiedAt += "+00:00";
          }
          // MUDO end hack
          var jsonLastModifiedAt = new Date(json.lastModifiedAt);
          if (self.lastModifiedAt && 1000 < self.lastModifiedAt.getTime() - jsonLastModifiedAt.getTime()) {
            // We use < 1000 (1s) instead of < 0, because the server stores dates only to the second.
            // With the current server implementation, we see that if we retrieve quickly after an
            // update or create, the retrieve lastModified at is later than the one in the response
            // of the action. The reason is, that the data in the action response comes from RAM,
            // and has values up to the nanosecond or so (23/6/2012 15:45:32.424242242), while the
            // data in the retrieve comes from the DB, which only stores up to the second
            // (23/6/2012 15:45:32). So, it seems that retrieve-date is earlier than the action
            // response date, which is impossible. By giving our comparison a 1 second leeway,
            // this is resolved.
            // IDEA solve in the server
            // MUDO There is a much worse problem: a RAM-created server Date is in the local time of the server
            //      While the time in the DB has no timezone. A RAM-created server Date that is sent over JSON
            //      contains timezone information. A date that is retrieved from the DB does not contain
            //      timezone in RAM, and thus does also not contain timezone information when it is sent over
            //      JSON.
            //      When JavaScript parses a string with timezone information, it takes it into account.
            //      When JavaScript parses a string without timezone information, it assumes the local
            //      timezone.
            //      When server and client are in a different timezone, when the JSON contains timezone information,
            //      the string is interpreted in the timezone of the server. When the JSON does not contain
            //      timezone information, the string is interpreted in the timezone of the client.
            //      For webapplications, this in unacceptable.
            // MUDO for now we work around this by NOT SENDING THE DATA TO THE SERVER IN THE FIRST PLACE
            //      that was another workaround; the effect of this is that we will not see this data after update
            // MUDO but it is obviously wrong
            throw "ERROR cannot become an earlier modified version"; // TODO precondition
          }
          if (self.lastModifiedBy && !json.lastModifiedBy) {
            throw "ERROR cannot change to lastModifiedBy == null"; // TODO precondition
          }
          // this will happen with the JSON response from a creation or update, and during construction
          if (! self.createdBy) {
            // MUDO HACK see above
            if (json.createdAt.indexOf("+00:00") < 0) {
              // there is no timezone information in the string
              json.createdAt += "+00:00";
            }
            // MUDO end hack
            //noinspection JSUnresolvedFunction
            self._changeAttrValue("createdAt", new Date(json.createdAt));
            //noinspection JSUnresolvedFunction
            self._changeAttrValue("createdBy", json.createdBy);
          }
          //noinspection JSUnresolvedFunction
          self._changeAttrValue("lastModifiedAt", jsonLastModifiedAt);
          //noinspection JSUnresolvedFunction
          self._changeAttrValue("lastModifiedBy", json.lastModifiedBy);
        }
      }

      var AuditableObject = declare([PersistentObject], {
        // created.. can change from null to value, but then no more
        // lastModified.. can change all the time, but ..At can only become bigger

        _c_invar: [
          function() {return this.hasOwnProperty("createdAt");},
          function() {return this.hasOwnProperty("createdBy");},
          function() {return this.hasOwnProperty("lastModifiedAt");},
          function() {return this.hasOwnProperty("lastModifiedBy");}
          // TODO should be strings and times
        ],

        constructor: function(/*Object*/ props) {
          /* we don't care about the format of the data here; we just keep it, and return it to the server
           like we got it. */

          this.createdAt = null;
          this.createdBy = null;
          this.lastModifiedAt = null;
          this.lastModifiedBy = null;
          internalReload(this, props);
        },

        reload: function(/*Object*/ json) {
          // created.. can change from null to an actual date and username number after create,
          // lastModified.. to, but then again with each update
          internalReload(this, json);
        },

        // getters are implicit; create when needed (for documentation)
        // TODO documentation

        _createdAtSetter: function() {
          // createdAt is read-only
          throw "ERROR";
        },

        _createdBySetter: function() {
          // createdBy is read-only
          throw "ERROR";
        },

        _lastModifiedAtSetter: function() {
          // lastModifiedAt is read-only
          throw "ERROR";
        },

        _lastModifiedBySetter: function() {
          // lastModifiedBy is read-only
          throw "ERROR";
        },

        _extendJsonObject: function(/*Object*/ json) {
          // it makes no senses whatsoever to send this data back to the back-end

          // HOWEVER due to an idiosyncrasy in our current server, it is much nicer
          //         if we do send back the created-attributes
          // In any case, it should be in nobody's way.
          json.createdBy = this.createdBy;
          json.createdAt = this.createdAt;
          // IDEA resolve this issue in the server
        },

        _stateToString: function(/*Array of String*/ toStrings) {
          toStrings.push("createdAt: " + this.createdAt);
          toStrings.push("createdBy: " + this.createdBy);
          toStrings.push("lastModifiedAt: " + this.lastModifiedAt);
          toStrings.push("lastModifiedBy: " + this.lastModifiedBy);
        }
      });

      return AuditableObject;
    }
);
<commit_msg>sometimes we provide something that is already a Date, and not a string; deal with it<commit_after>define(["dojo/_base/declare", "./PersistentObject", "dojo/date", "dojo/_base/lang"],
    function(declare, PersistentObject, dojoDate, lang) {

      function internalReload(/*AuditableObject*/ self, /*Object*/ json) {
        if (json && json.createdAt && json.createdBy && json.lastModifiedAt && json.lastModifiedBy /* TODO json.... undefined, but not null */) {
          // all data or nothing;
          // TODO add precondition for this
          if ((self.createdAt || self.createdBy) &&
              ((self.createdAt && (dojoDate.compare(json.createdAt, self.createdAt) !== 0)) ||
               (self.createdBy && json.createdBy != self.createdBy))) {
            throw "ERROR cannot change from existing created information"; // TODO precondition
          }
          var jsonLastModifiedAt = null;
          if (lang.isString(json.lastModifiedAt)) {
            // MUDO HACK see below
            if (json.lastModifiedAt.indexOf("+00:00") < 0) {
              // there is no timezone information in the string
              json.lastModifiedAt += "+00:00";
            }
            // MUDO end hack
            jsonLastModifiedAt = new Date(json.lastModifiedAt);
          }
          else {
            jsonLastModifiedAt = json.lastModifiedAt; // presumed to be a data already
          }
          if (self.lastModifiedAt && 1000 < self.lastModifiedAt.getTime() - jsonLastModifiedAt.getTime()) {
            // We use < 1000 (1s) instead of < 0, because the server stores dates only to the second.
            // With the current server implementation, we see that if we retrieve quickly after an
            // update or create, the retrieve lastModified at is later than the one in the response
            // of the action. The reason is, that the data in the action response comes from RAM,
            // and has values up to the nanosecond or so (23/6/2012 15:45:32.424242242), while the
            // data in the retrieve comes from the DB, which only stores up to the second
            // (23/6/2012 15:45:32). So, it seems that retrieve-date is earlier than the action
            // response date, which is impossible. By giving our comparison a 1 second leeway,
            // this is resolved.
            // IDEA solve in the server
            // MUDO There is a much worse problem: a RAM-created server Date is in the local time of the server
            //      While the time in the DB has no timezone. A RAM-created server Date that is sent over JSON
            //      contains timezone information. A date that is retrieved from the DB does not contain
            //      timezone in RAM, and thus does also not contain timezone information when it is sent over
            //      JSON.
            //      When JavaScript parses a string with timezone information, it takes it into account.
            //      When JavaScript parses a string without timezone information, it assumes the local
            //      timezone.
            //      When server and client are in a different timezone, when the JSON contains timezone information,
            //      the string is interpreted in the timezone of the server. When the JSON does not contain
            //      timezone information, the string is interpreted in the timezone of the client.
            //      For webapplications, this in unacceptable.
            // MUDO for now we work around this by NOT SENDING THE DATA TO THE SERVER IN THE FIRST PLACE
            //      that was another workaround; the effect of this is that we will not see this data after update
            // MUDO but it is obviously wrong
            throw "ERROR cannot become an earlier modified version"; // TODO precondition
          }
          if (self.lastModifiedBy && !json.lastModifiedBy) {
            throw "ERROR cannot change to lastModifiedBy == null"; // TODO precondition
          }
          // this will happen with the JSON response from a creation or update, and during construction
          if (! self.createdBy) {
            var jsonCreatedAt = null;
            if (lang.isString(json.createdAt)) {
              // MUDO HACK see above
              if (json.createdAt.indexOf("+00:00") < 0) {
                // there is no timezone information in the string
                json.createdAt += "+00:00";
              }
              // MUDO end hack
              jsonCreatedAt = new Date(json.createdAt);
            }
            else {
              jsonCreatedAt = json.createdAt; // already is presumed a Date
            }
            //noinspection JSUnresolvedFunction
            self._changeAttrValue("createdAt", jsonCreatedAt);
            //noinspection JSUnresolvedFunction
            self._changeAttrValue("createdBy", json.createdBy);
          }
          //noinspection JSUnresolvedFunction
          self._changeAttrValue("lastModifiedAt", jsonLastModifiedAt);
          //noinspection JSUnresolvedFunction
          self._changeAttrValue("lastModifiedBy", json.lastModifiedBy);
        }
      }

      var AuditableObject = declare([PersistentObject], {
        // created.. can change from null to value, but then no more
        // lastModified.. can change all the time, but ..At can only become bigger

        _c_invar: [
          function() {return this.hasOwnProperty("createdAt");},
          function() {return this.hasOwnProperty("createdBy");},
          function() {return this.hasOwnProperty("lastModifiedAt");},
          function() {return this.hasOwnProperty("lastModifiedBy");}
          // TODO should be strings and times
        ],

        constructor: function(/*Object*/ props) {
          /* we don't care about the format of the data here; we just keep it, and return it to the server
           like we got it. */

          this.createdAt = null;
          this.createdBy = null;
          this.lastModifiedAt = null;
          this.lastModifiedBy = null;
          internalReload(this, props);
        },

        reload: function(/*Object*/ json) {
          // created.. can change from null to an actual date and username number after create,
          // lastModified.. to, but then again with each update
          internalReload(this, json);
        },

        // getters are implicit; create when needed (for documentation)
        // TODO documentation

        _createdAtSetter: function() {
          // createdAt is read-only
          throw "ERROR";
        },

        _createdBySetter: function() {
          // createdBy is read-only
          throw "ERROR";
        },

        _lastModifiedAtSetter: function() {
          // lastModifiedAt is read-only
          throw "ERROR";
        },

        _lastModifiedBySetter: function() {
          // lastModifiedBy is read-only
          throw "ERROR";
        },

        _extendJsonObject: function(/*Object*/ json) {
          // it makes no senses whatsoever to send this data back to the back-end

          // HOWEVER due to an idiosyncrasy in our current server, it is much nicer
          //         if we do send back the created-attributes
          // In any case, it should be in nobody's way.
          json.createdBy = this.createdBy;
          json.createdAt = this.createdAt;
          // IDEA resolve this issue in the server
        },

        _stateToString: function(/*Array of String*/ toStrings) {
          toStrings.push("createdAt: " + this.createdAt);
          toStrings.push("createdBy: " + this.createdBy);
          toStrings.push("lastModifiedAt: " + this.lastModifiedAt);
          toStrings.push("lastModifiedBy: " + this.lastModifiedBy);
        }
      });

      return AuditableObject;
    }
);
<|endoftext|>


<commit_before>import PropTypes from 'prop-types';
import React, { Component } from 'react';
import _ from 'lodash';
import SortableHeader from './SortableHeader';
import SelectAllHeader from './SelectAllHeader';
import TableRow from './TableRow';
import TableNoData from './TableNoData';

const propTypes = {
  data: PropTypes.array.isRequired,
  headers: PropTypes.object.isRequired,
  columns: PropTypes.array.isRequired,
  filter: PropTypes.array.isRequired,
  primaryKey: PropTypes.string.isRequired,
  selectable: PropTypes.bool,
  selectEnabled: PropTypes.func,
  className: PropTypes.string,
  styleName: PropTypes.string,
  CheckboxComponent: PropTypes.func,
  NoDataComponent: PropTypes.func,
};

const resolveHeaderCondition = (col, tableProps) => {
  if (!col) {
    return false;
  } else if (_.isFunction(col.headerComponentVisible)) {
    return col.headerComponentVisible(tableProps);
  } else if (_.isBoolean(col.headerComponentVisible)) {
    return col.headerComponentVisible;
  }
  throw new Error('Header\'s headerComponentVisible should be a function or a boolean!');
};

const omitHeaderProps = props => _.omit(props, [
  'data',
  'filter',
  'primaryKey',
  'selectable',
  'selectEnabled',
  'className',
  'styleName',
  'CheckboxComponent',
  'NoDataComponent',
]);

class Table extends Component {
  render() {
    const {
      selectable,
      data,
      headers,
      columns,
      filter,
      primaryKey,
      CheckboxComponent,
      NoDataComponent,
    } = this.props;

    const className = this.props.className || 'table-sm table-striped table-hover';
    const visibleColumns = columns.filter((c) => !c.hidden);
    const visibleColumnsLength = visibleColumns.length;

    const NoDataContent = NoDataComponent || TableNoData;

    return (
      <table className={`table ${className}`}>

            {selectable &&
              <SelectAllHeader
                {...headers.select}
                CheckboxComponent={CheckboxComponent}
              />
            }
            {visibleColumns.map((col) => {
              const { hidden, HeaderComponent, key, title } = col;
              if (!hidden && HeaderComponent && resolveHeaderCondition(col, this.props)) {
                return (
                  <col.HeaderComponent
                    {...omitHeaderProps(this.props)}
                    key={key}
                    title={title}
                  />
                	);
              }
              if (col.sortable && !col.hidden) {
                return (
                  <SortableHeader
                    key={col.key}
                    title={col.title}
                    {...headers[col.key]}
                  />);
              }
              return (

                  {col.header}

              );
            })}

          {data.map((row) => (
            <TableRow key={_.get(row, primaryKey)} {...this.props} row={row} />
          ))}
          {!data.length &&

                <NoDataContent filter={filter} />

          }

    );
  }
}

Table.propTypes = propTypes;
export default Table;
<commit_msg>Don't throw if header condition is not yet present<commit_after>import PropTypes from 'prop-types';
import React, { Component } from 'react';
import _ from 'lodash';
import SortableHeader from './SortableHeader';
import SelectAllHeader from './SelectAllHeader';
import TableRow from './TableRow';
import TableNoData from './TableNoData';

const propTypes = {
  data: PropTypes.array.isRequired,
  headers: PropTypes.object.isRequired,
  columns: PropTypes.array.isRequired,
  filter: PropTypes.array.isRequired,
  primaryKey: PropTypes.string.isRequired,
  selectable: PropTypes.bool,
  selectEnabled: PropTypes.func,
  className: PropTypes.string,
  styleName: PropTypes.string,
  CheckboxComponent: PropTypes.func,
  NoDataComponent: PropTypes.func,
};

const resolveHeaderCondition = (col, tableProps) => {
  if (!col || !col.headerComponentVisible) {
    return false;
  } else if (_.isFunction(col.headerComponentVisible)) {
    return col.headerComponentVisible(tableProps);
  } else if (_.isBoolean(col.headerComponentVisible)) {
    return col.headerComponentVisible;
  }
  throw new Error('Header\'s headerComponentVisible should be a function or a boolean!');
};

const omitHeaderProps = props => _.omit(props, [
  'data',
  'filter',
  'primaryKey',
  'selectable',
  'selectEnabled',
  'className',
  'styleName',
  'CheckboxComponent',
  'NoDataComponent',
]);

class Table extends Component {
  render() {
    const {
      selectable,
      data,
      headers,
      columns,
      filter,
      primaryKey,
      CheckboxComponent,
      NoDataComponent,
    } = this.props;

    const className = this.props.className || 'table-sm table-striped table-hover';
    const visibleColumns = columns.filter((c) => !c.hidden);
    const visibleColumnsLength = visibleColumns.length;

    const NoDataContent = NoDataComponent || TableNoData;

    return (
      <table className={`table ${className}`}>

            {selectable &&
              <SelectAllHeader
                {...headers.select}
                CheckboxComponent={CheckboxComponent}
              />
            }
            {visibleColumns.map((col) => {
              const { hidden, HeaderComponent, key, title } = col;
              if (!hidden && HeaderComponent && resolveHeaderCondition(col, this.props)) {
                return (
                  <col.HeaderComponent
                    {...omitHeaderProps(this.props)}
                    key={key}
                    title={title}
                  />
                	);
              }
              if (col.sortable && !col.hidden) {
                return (
                  <SortableHeader
                    key={col.key}
                    title={col.title}
                    {...headers[col.key]}
                  />);
              }
              return (

                  {col.header}

              );
            })}

          {data.map((row) => (
            <TableRow key={_.get(row, primaryKey)} {...this.props} row={row} />
          ))}
          {!data.length &&

                <NoDataContent filter={filter} />

          }

    );
  }
}

Table.propTypes = propTypes;
export default Table;
<|endoftext|>


<commit_before>'use strict';

var webpack = require('webpack');
var CommonsChunkPlugin = webpack.optimize.CommonsChunkPlugin;

module.exports = {

    entry: {
        mainpage: './index.js',
        reacttagpage: './js/reactTagsInputPage.js',
        reactselectpage: './js/reactSelectPage.js',
        reactgeomiconspage: './js/reactGeomIconsPage.js'
    },
    output: {
        path: __dirname + '/assets',
        publicPath: __dirname + "/assets/",
        filename: '[name]-bundle.js'
    },
    plugins: [
        new webpack.NoErrorsPlugin(),
        new CommonsChunkPlugin({
            name: "mainpage"
        })
    ],
    module: {
        loaders: [
            {
                test: /\.css$/,
                loader: 'style-loader!css-loader'
            },

            {
                test: /\.(png|jpg|svg)$/,
                loaders: [
                    'url-loader?limit=8192',
                    'image-webpack?optimizationLevel=7&progressive=true']
            } // inline base64 URLs for <=8k images, direct URLs for the rest
        ]
    }

};<commit_msg>inline images webpack<commit_after>'use strict';

var webpack = require('webpack');
var CommonsChunkPlugin = webpack.optimize.CommonsChunkPlugin;

module.exports = {

    entry: {
        mainpage: './index.js',
        reacttagpage: './js/reactTagsInputPage.js',
        reactselectpage: './js/reactSelectPage.js',
        reactgeomiconspage: './js/reactGeomIconsPage.js'
    },
    output: {
        path: __dirname + '/assets',
        publicPath: __dirname + "/assets/",
        filename: '[name]-bundle.js'
    },
    plugins: [
        new webpack.NoErrorsPlugin(),
        new CommonsChunkPlugin({
            name: "mainpage"
        })
    ],
    module: {
        loaders: [
            {
                test: /\.css$/,
                loader: 'style-loader!css-loader'
            },

            {
                test: /\.(png|jpg|svg)$/,
                loaders: [
                    'url-loader?limit=81920',
                    'image-webpack?optimizationLevel=7&progressive=true']
            } // inline base64 URLs for <=8k images, direct URLs for the rest
        ]
    }

};<|endoftext|>


<commit_before>/**
 * @author       Richard Davey <rich@photonstorm.com>
 * @copyright    2020 Photon Storm Ltd.
 * @license      {@link https://opensource.org/licenses/MIT|MIT License}
 */

var ArcadeSprite = require('./ArcadeSprite');
var Class = require('../../utils/Class');
var CONST = require('./const');
var GetFastValue = require('../../utils/object/GetFastValue');
var Group = require('../../gameobjects/group/Group');
var IsPlainObject = require('../../utils/object/IsPlainObject');

/**
 * @classdesc
 * An Arcade Physics Group object.
 *
 * All Game Objects created by or added to this Group will automatically be given dynamic Arcade Physics bodies (if they have no body)
 * and the bodies will receive the Group's {@link Phaser.Physics.Arcade.Group#defaults default values}.
 *
 * Its static counterpart is {@link Phaser.Physics.Arcade.StaticGroup}.
 *
 * @class Group
 * @extends Phaser.GameObjects.Group
 * @memberof Phaser.Physics.Arcade
 * @constructor
 * @since 3.0.0
 *
 * @param {Phaser.Physics.Arcade.World} world - The physics simulation.
 * @param {Phaser.Scene} scene - The scene this group belongs to.
 * @param {(Phaser.GameObjects.GameObject[]|Phaser.Types.Physics.Arcade.PhysicsGroupConfig|Phaser.Types.GameObjects.Group.GroupCreateConfig)} [children] - Game Objects to add to this group; or the `config` argument.
 * @param {Phaser.Types.Physics.Arcade.PhysicsGroupConfig|Phaser.Types.GameObjects.Group.GroupCreateConfig} [config] - Settings for this group.
 */
var PhysicsGroup = new Class({

    Extends: Group,

    initialize:

    function PhysicsGroup (world, scene, children, config)
    {
        if (!children && !config)
        {
            config = {
                internalCreateCallback: this.createCallbackHandler,
                internalRemoveCallback: this.removeCallbackHandler
            };
        }
        else if (IsPlainObject(children))
        {
            //  children is a plain object, so swizzle them:
            config = children;
            children = null;

            config.internalCreateCallback = this.createCallbackHandler;
            config.internalRemoveCallback = this.removeCallbackHandler;
        }
        else if (Array.isArray(children) && IsPlainObject(children[0]))
        {
            //  children is an array of plain objects (i.e., configs)
            config = children[0];

            var _this = this;

            children.forEach(function (singleConfig)
            {
                singleConfig.internalCreateCallback = _this.createCallbackHandler;
                singleConfig.internalRemoveCallback = _this.removeCallbackHandler;
            });

            children = null;
        }
        else
        {
            // config is not defined and children is not a plain object nor an array of plain objects
            config = {
                internalCreateCallback: this.createCallbackHandler,
                internalRemoveCallback: this.removeCallbackHandler
            };
        }

        /**
         * The physics simulation.
         *
         * @name Phaser.Physics.Arcade.Group#world
         * @type {Phaser.Physics.Arcade.World}
         * @since 3.0.0
         */
        this.world = world;

        /**
         * The class to create new Group members from.
         *
         * This should be either `Phaser.Physics.Arcade.Image`, `Phaser.Physics.Arcade.Sprite`, or a class extending one of those.
         *
         * @name Phaser.Physics.Arcade.Group#classType
         * @type {Function}
         * @default ArcadeSprite
         * @since 3.0.0
         */
        config.classType = GetFastValue(config, 'classType', ArcadeSprite);

        /**
         * The physics type of the Group's members.
         *
         * @name Phaser.Physics.Arcade.Group#physicsType
         * @type {integer}
         * @default Phaser.Physics.Arcade.DYNAMIC_BODY
         * @since 3.0.0
         */
        this.physicsType = CONST.DYNAMIC_BODY;

        /**
         * Default physics properties applied to Game Objects added to the Group or created by the Group. Derived from the `config` argument.
         *
         * You can remove the default values by setting this property to `{}`.
         *
         * @name Phaser.Physics.Arcade.Group#defaults
         * @type {Phaser.Types.Physics.Arcade.PhysicsGroupDefaults}
         * @since 3.0.0
         */
        this.defaults = {
            setCollideWorldBounds: GetFastValue(config, 'collideWorldBounds', false),
            setBoundsRectangle: GetFastValue(config, 'customBoundsRectangle', null),
            setAccelerationX: GetFastValue(config, 'accelerationX', 0),
            setAccelerationY: GetFastValue(config, 'accelerationY', 0),
            setAllowDrag: GetFastValue(config, 'allowDrag', true),
            setAllowGravity: GetFastValue(config, 'allowGravity', true),
            setAllowRotation: GetFastValue(config, 'allowRotation', true),
            setBounceX: GetFastValue(config, 'bounceX', 0),
            setBounceY: GetFastValue(config, 'bounceY', 0),
            setDragX: GetFastValue(config, 'dragX', 0),
            setDragY: GetFastValue(config, 'dragY', 0),
            setEnable: GetFastValue(config, 'enable', true),
            setGravityX: GetFastValue(config, 'gravityX', 0),
            setGravityY: GetFastValue(config, 'gravityY', 0),
            setFrictionX: GetFastValue(config, 'frictionX', 0),
            setFrictionY: GetFastValue(config, 'frictionY', 0),
            setVelocityX: GetFastValue(config, 'velocityX', 0),
            setVelocityY: GetFastValue(config, 'velocityY', 0),
            setAngularVelocity: GetFastValue(config, 'angularVelocity', 0),
            setAngularAcceleration: GetFastValue(config, 'angularAcceleration', 0),
            setAngularDrag: GetFastValue(config, 'angularDrag', 0),
            setMass: GetFastValue(config, 'mass', 1),
            setImmovable: GetFastValue(config, 'immovable', false)
        };

        Group.call(this, scene, children, config);

        /**
         * A textual representation of this Game Object.
         * Used internally by Phaser but is available for your own custom classes to populate.
         *
         * @name Phaser.Physics.Arcade.Group#type
         * @type {string}
         * @default 'PhysicsGroup'
         * @since 3.21.0
         */
        this.type = 'PhysicsGroup';
    },

    /**
     * Enables a Game Object's Body and assigns `defaults`. Called when a Group member is added or created.
     *
     * @method Phaser.Physics.Arcade.Group#createCallbackHandler
     * @since 3.0.0
     *
     * @param {Phaser.GameObjects.GameObject} child - The Game Object being added.
     */
    createCallbackHandler: function (child)
    {
        if (!child.body)
        {
            this.world.enableBody(child, CONST.DYNAMIC_BODY);
        }

        var body = child.body;

        for (var key in this.defaults)
        {
            body[key](this.defaults[key]);
        }
    },

    /**
     * Disables a Game Object's Body. Called when a Group member is removed.
     *
     * @method Phaser.Physics.Arcade.Group#removeCallbackHandler
     * @since 3.0.0
     *
     * @param {Phaser.GameObjects.GameObject} child - The Game Object being removed.
     */
    removeCallbackHandler: function (child)
    {
        if (child.body)
        {
            this.world.disableBody(child);
        }
    },

    /**
     * Sets the velocity of each Group member.
     *
     * @method Phaser.Physics.Arcade.Group#setVelocity
     * @since 3.0.0
     *
     * @param {number} x - The horizontal velocity.
     * @param {number} y - The vertical velocity.
     * @param {number} [step=0] - The velocity increment. When set, the first member receives velocity (x, y), the second (x + step, y + step), and so on.
     *
     * @return {Phaser.Physics.Arcade.Group} This Physics Group object.
     */
    setVelocity: function (x, y, step)
    {
        if (step === undefined) { step = 0; }

        var items = this.getChildren();

        for (var i = 0; i < items.length; i++)
        {
            items[i].body.velocity.set(x + (i * step), y + (i * step));
        }

        return this;
    },

    /**
     * Sets the horizontal velocity of each Group member.
     *
     * @method Phaser.Physics.Arcade.Group#setVelocityX
     * @since 3.0.0
     *
     * @param {number} value - The velocity value.
     * @param {number} [step=0] - The velocity increment. When set, the first member receives velocity (x), the second (x + step), and so on.
     *
     * @return {Phaser.Physics.Arcade.Group} This Physics Group object.
     */
    setVelocityX: function (value, step)
    {
        if (step === undefined) { step = 0; }

        var items = this.getChildren();

        for (var i = 0; i < items.length; i++)
        {
            items[i].body.velocity.x = value + (i * step);
        }

        return this;
    },

    /**
     * Sets the vertical velocity of each Group member.
     *
     * @method Phaser.Physics.Arcade.Group#setVelocityY
     * @since 3.0.0
     *
     * @param {number} value - The velocity value.
     * @param {number} [step=0] - The velocity increment. When set, the first member receives velocity (y), the second (y + step), and so on.
     *
     * @return {Phaser.Physics.Arcade.Group} This Physics Group object.
     */
    setVelocityY: function (value, step)
    {
        if (step === undefined) { step = 0; }

        var items = this.getChildren();

        for (var i = 0; i < items.length; i++)
        {
            items[i].body.velocity.y = value + (i * step);
        }

        return this;
    }

});

module.exports = PhysicsGroup;
<commit_msg>Better Group docs. Fix #5011<commit_after>/**
 * @author       Richard Davey <rich@photonstorm.com>
 * @copyright    2020 Photon Storm Ltd.
 * @license      {@link https://opensource.org/licenses/MIT|MIT License}
 */

var ArcadeSprite = require('./ArcadeSprite');
var Class = require('../../utils/Class');
var CONST = require('./const');
var GetFastValue = require('../../utils/object/GetFastValue');
var Group = require('../../gameobjects/group/Group');
var IsPlainObject = require('../../utils/object/IsPlainObject');

/**
 * @classdesc
 * An Arcade Physics Group object.
 *
 * The primary use of a Physics Group is a way to collect together physics enable objects
 * that share the same intrinsic structure into a single pool. They can they be easily
 * compared against other Groups, or Game Objects.
 *
 * All Game Objects created by, or added to this Group will automatically be given **dynamic**
 * Arcade Physics bodies (if they have no body already) and the bodies will receive the
 * Groups {@link Phaser.Physics.Arcade.Group#defaults default values}.
 *
 * You should not pass objects into this Group that should not receive a body. For example,
 * do not add basic Geometry or Tilemap Layers into a Group, as they will not behave in the
 * way you may expect. Groups should all ideally have objects of the same type in them.
 *
 * If you wish to create a Group filled with Static Bodies, please see {@link Phaser.Physics.Arcade.StaticGroup}.
 *
 * @class Group
 * @extends Phaser.GameObjects.Group
 * @memberof Phaser.Physics.Arcade
 * @constructor
 * @since 3.0.0
 *
 * @param {Phaser.Physics.Arcade.World} world - The physics simulation.
 * @param {Phaser.Scene} scene - The scene this group belongs to.
 * @param {(Phaser.GameObjects.GameObject[]|Phaser.Types.Physics.Arcade.PhysicsGroupConfig|Phaser.Types.GameObjects.Group.GroupCreateConfig)} [children] - Game Objects to add to this group; or the `config` argument.
 * @param {Phaser.Types.Physics.Arcade.PhysicsGroupConfig|Phaser.Types.GameObjects.Group.GroupCreateConfig} [config] - Settings for this group.
 */
var PhysicsGroup = new Class({

    Extends: Group,

    initialize:

    function PhysicsGroup (world, scene, children, config)
    {
        if (!children && !config)
        {
            config = {
                internalCreateCallback: this.createCallbackHandler,
                internalRemoveCallback: this.removeCallbackHandler
            };
        }
        else if (IsPlainObject(children))
        {
            //  children is a plain object, so swizzle them:
            config = children;
            children = null;

            config.internalCreateCallback = this.createCallbackHandler;
            config.internalRemoveCallback = this.removeCallbackHandler;
        }
        else if (Array.isArray(children) && IsPlainObject(children[0]))
        {
            //  children is an array of plain objects (i.e., configs)
            config = children[0];

            var _this = this;

            children.forEach(function (singleConfig)
            {
                singleConfig.internalCreateCallback = _this.createCallbackHandler;
                singleConfig.internalRemoveCallback = _this.removeCallbackHandler;
            });

            children = null;
        }
        else
        {
            // config is not defined and children is not a plain object nor an array of plain objects
            config = {
                internalCreateCallback: this.createCallbackHandler,
                internalRemoveCallback: this.removeCallbackHandler
            };
        }

        /**
         * The physics simulation.
         *
         * @name Phaser.Physics.Arcade.Group#world
         * @type {Phaser.Physics.Arcade.World}
         * @since 3.0.0
         */
        this.world = world;

        /**
         * The class to create new Group members from.
         *
         * This should be either `Phaser.Physics.Arcade.Image`, `Phaser.Physics.Arcade.Sprite`, or a class extending one of those.
         *
         * @name Phaser.Physics.Arcade.Group#classType
         * @type {Function}
         * @default ArcadeSprite
         * @since 3.0.0
         */
        config.classType = GetFastValue(config, 'classType', ArcadeSprite);

        /**
         * The physics type of the Group's members.
         *
         * @name Phaser.Physics.Arcade.Group#physicsType
         * @type {integer}
         * @default Phaser.Physics.Arcade.DYNAMIC_BODY
         * @since 3.0.0
         */
        this.physicsType = CONST.DYNAMIC_BODY;

        /**
         * Default physics properties applied to Game Objects added to the Group or created by the Group. Derived from the `config` argument.
         *
         * You can remove the default values by setting this property to `{}`.
         *
         * @name Phaser.Physics.Arcade.Group#defaults
         * @type {Phaser.Types.Physics.Arcade.PhysicsGroupDefaults}
         * @since 3.0.0
         */
        this.defaults = {
            setCollideWorldBounds: GetFastValue(config, 'collideWorldBounds', false),
            setBoundsRectangle: GetFastValue(config, 'customBoundsRectangle', null),
            setAccelerationX: GetFastValue(config, 'accelerationX', 0),
            setAccelerationY: GetFastValue(config, 'accelerationY', 0),
            setAllowDrag: GetFastValue(config, 'allowDrag', true),
            setAllowGravity: GetFastValue(config, 'allowGravity', true),
            setAllowRotation: GetFastValue(config, 'allowRotation', true),
            setBounceX: GetFastValue(config, 'bounceX', 0),
            setBounceY: GetFastValue(config, 'bounceY', 0),
            setDragX: GetFastValue(config, 'dragX', 0),
            setDragY: GetFastValue(config, 'dragY', 0),
            setEnable: GetFastValue(config, 'enable', true),
            setGravityX: GetFastValue(config, 'gravityX', 0),
            setGravityY: GetFastValue(config, 'gravityY', 0),
            setFrictionX: GetFastValue(config, 'frictionX', 0),
            setFrictionY: GetFastValue(config, 'frictionY', 0),
            setVelocityX: GetFastValue(config, 'velocityX', 0),
            setVelocityY: GetFastValue(config, 'velocityY', 0),
            setAngularVelocity: GetFastValue(config, 'angularVelocity', 0),
            setAngularAcceleration: GetFastValue(config, 'angularAcceleration', 0),
            setAngularDrag: GetFastValue(config, 'angularDrag', 0),
            setMass: GetFastValue(config, 'mass', 1),
            setImmovable: GetFastValue(config, 'immovable', false)
        };

        Group.call(this, scene, children, config);

        /**
         * A textual representation of this Game Object.
         * Used internally by Phaser but is available for your own custom classes to populate.
         *
         * @name Phaser.Physics.Arcade.Group#type
         * @type {string}
         * @default 'PhysicsGroup'
         * @since 3.21.0
         */
        this.type = 'PhysicsGroup';
    },

    /**
     * Enables a Game Object's Body and assigns `defaults`. Called when a Group member is added or created.
     *
     * @method Phaser.Physics.Arcade.Group#createCallbackHandler
     * @since 3.0.0
     *
     * @param {Phaser.GameObjects.GameObject} child - The Game Object being added.
     */
    createCallbackHandler: function (child)
    {
        if (!child.body)
        {
            this.world.enableBody(child, CONST.DYNAMIC_BODY);
        }

        var body = child.body;

        for (var key in this.defaults)
        {
            body[key](this.defaults[key]);
        }
    },

    /**
     * Disables a Game Object's Body. Called when a Group member is removed.
     *
     * @method Phaser.Physics.Arcade.Group#removeCallbackHandler
     * @since 3.0.0
     *
     * @param {Phaser.GameObjects.GameObject} child - The Game Object being removed.
     */
    removeCallbackHandler: function (child)
    {
        if (child.body)
        {
            this.world.disableBody(child);
        }
    },

    /**
     * Sets the velocity of each Group member.
     *
     * @method Phaser.Physics.Arcade.Group#setVelocity
     * @since 3.0.0
     *
     * @param {number} x - The horizontal velocity.
     * @param {number} y - The vertical velocity.
     * @param {number} [step=0] - The velocity increment. When set, the first member receives velocity (x, y), the second (x + step, y + step), and so on.
     *
     * @return {Phaser.Physics.Arcade.Group} This Physics Group object.
     */
    setVelocity: function (x, y, step)
    {
        if (step === undefined) { step = 0; }

        var items = this.getChildren();

        for (var i = 0; i < items.length; i++)
        {
            items[i].body.velocity.set(x + (i * step), y + (i * step));
        }

        return this;
    },

    /**
     * Sets the horizontal velocity of each Group member.
     *
     * @method Phaser.Physics.Arcade.Group#setVelocityX
     * @since 3.0.0
     *
     * @param {number} value - The velocity value.
     * @param {number} [step=0] - The velocity increment. When set, the first member receives velocity (x), the second (x + step), and so on.
     *
     * @return {Phaser.Physics.Arcade.Group} This Physics Group object.
     */
    setVelocityX: function (value, step)
    {
        if (step === undefined) { step = 0; }

        var items = this.getChildren();

        for (var i = 0; i < items.length; i++)
        {
            items[i].body.velocity.x = value + (i * step);
        }

        return this;
    },

    /**
     * Sets the vertical velocity of each Group member.
     *
     * @method Phaser.Physics.Arcade.Group#setVelocityY
     * @since 3.0.0
     *
     * @param {number} value - The velocity value.
     * @param {number} [step=0] - The velocity increment. When set, the first member receives velocity (y), the second (y + step), and so on.
     *
     * @return {Phaser.Physics.Arcade.Group} This Physics Group object.
     */
    setVelocityY: function (value, step)
    {
        if (step === undefined) { step = 0; }

        var items = this.getChildren();

        for (var i = 0; i < items.length; i++)
        {
            items[i].body.velocity.y = value + (i * step);
        }

        return this;
    }

});

module.exports = PhysicsGroup;
<|endoftext|>


<commit_before>define(
    [
        'grobo/lib',
        'grobo/state',
        'grobo/ui/button',
        'grobo/ui/panel',
        'grobo/logger'
    ],
    function (lib, refState, refButton, refPanel, logger) {

        var refMenuState = lib.create(refState, {

            name: 'Menu',
            isAnimating: false,

            init: function (config) {

                this.modality = this.EXCLUSIVE;
                this._initState(config);

                var view = lib.create(refPanel).init({
                    canvas: this.canvas,
                    style: {
                        width: '100%',
                        height: '100%',
                        background: 'green'
                    }
                });

                var panel = lib.create(refPanel).init({
                    style: {
                        width: '90%', height: '90%',
                        background: 'red'
                    }
                });

                var button1 = lib.create(refButton).init({
                    style: {
                        top: 0,
                        left: 0,
                        width: '50%',
                        height: '50%',
                        spacing: 10
                    },
                    label: 'Button 1 (TL)'
                }),
                button2 = lib.create(refButton).init({
                    style: {
                        top: 0,
                        right: 0,
                        width: '50%',
                        height: '50%',
                        spacing: 10
                    },
                    label: 'Button 2 (TR)'
                }),
                button3 = lib.create(refButton).init({
                    style: {
                        bottom: 0,
                        left: 0,
                        width: '50%',
                        height: '50%',
                        spacing: 10
                    },
                    label: 'Button 3 (BL)'
                }),
                button4 = lib.create(refButton).init({
                    style: {
                        bottom: 0,
                        right: 0,
                        width: '50%',
                        height: '50%',
                        spacing: 10
                    },
                    label: 'Button 4 (BR)'
                });

                view.addChild(panel);
                panel.addChild(button1).addChild(button2).addChild(button3).addChild(button4);

                this.view = view;

                return this;
            },

            entered: function () {
                this.isAnimating = true;
                this.view.x = this.canvas.width;
            },

            update: function (delta) {
                if (this.isAnimating) {
                    if (this.view.x > 0) {
                        this.view.x -= delta;
                        if (this.view.x <= 0) {
                            this.view.x = 0;
                            this.isAnimating = false;
                        }
                    }
                }
            },

            draw: function () {
                this.view.draw();
            },

            handleInput: function (event) {
                this._handleInputState(event);
                if (event.name === 'click' && !event.isConsumed) {
                    event.consume();
                    this.stateManager.push(this.stateFactory.getHelp());
                }
            }
        });

        return refMenuState;
    }
);<commit_msg>Change menu spacing.<commit_after>define(
    [
        'grobo/lib',
        'grobo/state',
        'grobo/ui/button',
        'grobo/ui/panel',
        'grobo/logger'
    ],
    function (lib, refState, refButton, refPanel, logger) {

        var refMenuState = lib.create(refState, {

            name: 'Menu',
            isAnimating: false,

            init: function (config) {

                this.modality = this.EXCLUSIVE;
                this._initState(config);

                var view = lib.create(refPanel).init({
                    canvas: this.canvas,
                    style: {
                        width: '100%',
                        height: '100%',
                        background: 'green'
                    }
                });

                var panel = lib.create(refPanel).init({
                    style: {
                        spacing: 10,
                        background: 'red'
                    }
                });

                var button1 = lib.create(refButton).init({
                    style: {
                        top: 0,
                        left: 0,
                        width: '50%',
                        height: '50%',
                        spacing: 10
                    },
                    label: 'Button 1 (TL)'
                }),
                button2 = lib.create(refButton).init({
                    style: {
                        top: 0,
                        right: 0,
                        width: '50%',
                        height: '50%',
                        spacing: 10
                    },
                    label: 'Button 2 (TR)'
                }),
                button3 = lib.create(refButton).init({
                    style: {
                        bottom: 0,
                        left: 0,
                        width: '50%',
                        height: '50%',
                        spacing: 10
                    },
                    label: 'Button 3 (BL)'
                }),
                button4 = lib.create(refButton).init({
                    style: {
                        bottom: 0,
                        right: 0,
                        width: '50%',
                        height: '50%',
                        spacing: 10
                    },
                    label: 'Button 4 (BR)'
                });

                view.addChild(panel);
                panel.addChild(button1).addChild(button2).addChild(button3).addChild(button4);

                this.view = view;

                return this;
            },

            entered: function () {
                this.isAnimating = true;
                this.view.x = this.canvas.width;
            },

            update: function (delta) {
                if (this.isAnimating) {
                    if (this.view.x > 0) {
                        this.view.x -= delta;
                        if (this.view.x <= 0) {
                            this.view.x = 0;
                            this.isAnimating = false;
                        }
                    }
                }
            },

            draw: function () {
                this.view.draw();
            },

            handleInput: function (event) {
                this._handleInputState(event);
                if (event.name === 'click' && !event.isConsumed) {
                    event.consume();
                    this.stateManager.push(this.stateFactory.getHelp());
                }
            }
        });

        return refMenuState;
    }
);<|endoftext|>


<commit_before><commit_msg>generate code doesnt cover workspace scrollbars<commit_after><|endoftext|>