<commit_before>var microtime = require('microtime');
var SPI       = require('spi-device');

/*
 A node.js library to control a WS2801 RGB LED stripe via SPI with your Raspberry Pi
 @copyright 2013 Jürgen Skrotzky (MIT-License)
*/

function RPiWS2801(){
  this.spiBus = 0;
  this.spiDevice = 0;
  this.numLEDs = 32;
  this.spi = null;
  this.inverted = false;
  this.reversed = false;
  this.gamma = 2.5;
  this.redIndex = 0;
  this.greenIndex = 1;
  this.blueIndex = 2;
  this.gammatable = new Array(256);
  this.bytePerPixel = 3; //RGB
  this.channelCount = this.numLEDs*this.bytePerPixel;
  this.values = new Buffer(this.channelCount);
  this.rowResetTime = 1000; // number of us CLK has to be pulled low (=no writes) for frame reset
    						            // manual of WS2801 says 500 is enough, however we need at least 1000
  this.lastWriteTime = microtime.now()-this.rowResetTime-1; //last time something was written to SPI
    												                                //required for save WS2801 reset
  // SPI max speed
  this.maxSpeedHz = 1000000;
  // clear buffer
  this.values.fill(0);
}

RPiWS2801.prototype = {
  /*
   * connect to SPI port
   */
  connect: function(numLEDs, spiBus, spiDevice, gamma){
    // sanity check for params
    if ((numLEDs !== parseInt(numLEDs)) || (numLEDs<1)) {
      console.error("invalid param for number of LEDs, plz use integer >0");
      return false;
    }
    if (spiBus){
      this.spiBus = spiBus;
    }
	if (spiDevice){
      this.spiDevice = spiDevice;
    }

    try{
      this.spi = SPI.openSync(this.spiBus, this.spiDevice, {'maxSpeedHz' : this.maxSpeedHz});
    } catch (err) {
      console.error("error opening SPI device "+this.spiDevice, err);
      return false;
    }

    this.numLEDs = numLEDs;

    this.channelCount = this.numLEDs*this.bytePerPixel;

    this.values = new Buffer(this.channelCount);
    this.values.fill(0);

    this.gamma = gamma ? gamma : 2.5; //set gamma correction value
    // compute gamma correction table
    for (var i=0; i<256; i++)
      this.gammatable[i] = Math.round(255*Math.pow(i/255, this.gamma));
    //console.log("gammatable" + this.gammatable);
  },

  /*
   * disconnect from SPI port
   */
  disconnect : function(){
    if (this.spi) this.spi.closeSync();
  },

  /*
   * send stored buffer with RGB values to WS2801 stripe
   */
  update: function(){
    if (this.spi) {
      this.sendRgbBuffer(this.values);
    }
  },

  /*
   * invert all colors
   */
  invert: function(){
    this.inverted = !this.inverted;
  }, // end invert

  /*
   * send buffer with RGB values to WS2801 stripe in reverse order
   */
  reverse: function(){
    this.reversed = !this.reversed;
  }, // end reverse

  /*
   * clear RGB stripe
   */
  clear: function(){
    this.fill(0x00, 0x00, 0x00);
  }, // end clear

  /*
   * set new RGB index order
   */
  setColorIndex: function(redIndex, greenIndex, blueIndex){
    this.redIndex = redIndex;
    this.greenIndex = greenIndex;
    this.blueIndex = blueIndex;
  }, // end setColorIndex

  /*
   * send buffer with RGB values to WS2801 stripe
   */
  sendRgbBuffer : function(buffer){
    // checking if enough time passed for resetting stripe
    if (microtime.now() > (this.lastWriteTime + this.rowResetTime)){
      // yes, its o.k., lets write
      // but first do gamma correction
      var adjustedBuffer = new Buffer(buffer.length);
      for (var i=0; i < buffer.length; i++){
        adjustedBuffer[i]=this.gammatable[buffer[i]];
      }

	  let message = [{
		sendBuffer: adjustedBuffer,
		byteLength: adjustedBuffer.length,
		speedHz: this.maxSpeedHz
	  }];

      this.spi.transferSync(message);

      this.lastWriteTime = microtime.now();
      return true;
    }
    console.log('writing too fast, data dropped');
    return false;
  }, // end sendRgbBuffer

  /*
   * fill whole stripe with one color
   */
  fill: function(r,g,b){
    if (this.spi) {
      var colors = this.getRGBArray(r,g,b);
      var colorBuffer = new Buffer(this.channelCount);
      for (var i=0; i<(this.channelCount); i+=3){
        colorBuffer[i+0]=colors[0];
        colorBuffer[i+1]=colors[1];
        colorBuffer[i+2]=colors[2];
      }
      this.sendRgbBuffer(colorBuffer);
    }
  }, //end fill

  /*
   * set color of led index [red, green, blue] from 0 to 255
   */
  setColor: function(ledIndex, color) {
    if (this.spi) {
      var colors = this.getRGBArray(color[0],color[1],color[2]);
      var r, g, b;
      r = colors[0] / 255;
      g = colors[1] / 255;
      b = colors[2] / 255;
      var redChannel = this.getRedChannelIndex(ledIndex);
      this.setChannelPower(redChannel,   r);
      this.setChannelPower(redChannel+1, g);
      this.setChannelPower(redChannel+2, b);
    }
  },

  /*
   * set power of channel from 0 to 1
   */
  setChannelPower: function(channelIndex, powerValue) {
    if (this.spi) {
      if(channelIndex > this.channelCount || channelIndex < 0) {
        return false;
      }
      if(powerValue < 0)
        powerValue = 0;
      if(powerValue > 1)
        powerValue = 1;
      this.values[channelIndex] = Math.floor((Math.pow(16, 2) - 1) * powerValue);
    }
  }, // end setChannelPower

  /*
   * set RGB hexcolor to LED index
   */
  setRGB: function(ledIndex, hexColor) {
    if (this.spi) {
      var rgb = this.getRGBfromHex(hexColor);
      var colors = this.getRGBArray(rgb.r,rgb.g,rgb.b);
      var redChannel = this.getRedChannelIndex(ledIndex);
      this.setChannelPower(redChannel+this.redIndex,   colors[0]);
      this.setChannelPower(redChannel+this.greenIndex, colors[1]);
      this.setChannelPower(redChannel+this.blueIndex,  colors[2]);
    }
  }, // end setRGB

  mySin: function(a, min, max){
    return min + ((max-min)/2.)*(Math.sin(a)+1)
  },

  rainbow: function(a){
    var intense = 255;
    var r = parseInt(this.mySin(a, 0, intense));
    var g = parseInt(this.mySin(a+Math.pi/2, 0, intense));
    var b = parseInt(this.mySin(a + Math.pi, 0, intense));
    var colors = this.getRGBArray(r,g,b);
    return [colors[0],colors[1],colors[2]];
  },

  getChannelCount: function() {
    return this.channelCount;
  },

  getRedChannelIndex: function(ledIndex) {
    return ledIndex * 3;
  },

  getGreenChannelIndex: function(ledIndex) {
    return ledIndex * 3 + 1;
  },

  getBlueChannelIndex: function(ledIndex) {
    return ledIndex * 3 + 2;
  },

  getRGBArray: function(r, g, b){
    var colorArray = new Array(3);
    colorArray[this.redIndex] = r & 0xff;
    colorArray[this.greenIndex] = g & 0xff;
    colorArray[this.blueIndex] = b & 0xff;
    if(this.inverted) {
      colorArray[0] = (1 - colorArray[0]/255)*255;
      colorArray[1] = (1 - colorArray[1]/255)*255;
      colorArray[2] = (1 - colorArray[2]/255)*255;
    }
    return colorArray;
  },

  getRGBfromHex: function(color) {
    var r, g, b;
    if (color.length == 4) {
      r = parseInt(color.replace("#", "").substr(0,1)+color.replace("#", "").substr(0,1),16);
      g = parseInt(color.replace("#", "").substr(1,1)+color.replace("#", "").substr(1,1),16);
      b = parseInt(color.replace("#", "").substr(2,1)+color.replace("#", "").substr(2,1),16);
    }
    if (color.length == 7) {
      r = parseInt(color.replace("#", "").substr(0,2),16);
      g = parseInt(color.replace("#", "").substr(2,2),16);
      b = parseInt(color.replace("#", "").substr(4,2),16);
    }
    var colors = this.getRGBArray(r,g,b);
    r = colors[0] / 255;
    g = colors[1] / 255;
    b = colors[2] / 255;
    if (r>=0 && r<=1 && g>=0 && g<=1 && b>=0 && b<=1) {
      return {r:r,g:g,b:b};
    } else {
      return {r:0,g:0,b:0};
    }
  }
}

module.exports = new RPiWS2801();
<commit_msg>As there is no setter for maxSpeedHz, removed optional speedHz property from SPI message<commit_after>var microtime = require('microtime');
var SPI       = require('spi-device');

/*
 A node.js library to control a WS2801 RGB LED stripe via SPI with your Raspberry Pi
 @copyright 2013 Jürgen Skrotzky (MIT-License)
*/

function RPiWS2801(){
  this.spiBus = 0;
  this.spiDevice = 0;
  this.numLEDs = 32;
  this.spi = null;
  this.inverted = false;
  this.reversed = false;
  this.gamma = 2.5;
  this.redIndex = 0;
  this.greenIndex = 1;
  this.blueIndex = 2;
  this.gammatable = new Array(256);
  this.bytePerPixel = 3; //RGB
  this.channelCount = this.numLEDs*this.bytePerPixel;
  this.values = new Buffer(this.channelCount);
  this.rowResetTime = 1000; // number of us CLK has to be pulled low (=no writes) for frame reset
    						            // manual of WS2801 says 500 is enough, however we need at least 1000
  this.lastWriteTime = microtime.now()-this.rowResetTime-1; //last time something was written to SPI
    												                                //required for save WS2801 reset
  // SPI max speed
  this.maxSpeedHz = 1000000;
  // clear buffer
  this.values.fill(0);
}

RPiWS2801.prototype = {
  /*
   * connect to SPI port
   */
  connect: function(numLEDs, spiBus, spiDevice, gamma){
    // sanity check for params
    if ((numLEDs !== parseInt(numLEDs)) || (numLEDs<1)) {
      console.error("invalid param for number of LEDs, plz use integer >0");
      return false;
    }
    if (spiBus){
      this.spiBus = spiBus;
    }
	if (spiDevice){
      this.spiDevice = spiDevice;
    }

    try{
      this.spi = SPI.openSync(this.spiBus, this.spiDevice, {'maxSpeedHz' : this.maxSpeedHz});
    } catch (err) {
      console.error("error opening SPI device "+this.spiDevice, err);
      return false;
    }

    this.numLEDs = numLEDs;

    this.channelCount = this.numLEDs*this.bytePerPixel;

    this.values = new Buffer(this.channelCount);
    this.values.fill(0);

    this.gamma = gamma ? gamma : 2.5; //set gamma correction value
    // compute gamma correction table
    for (var i=0; i<256; i++)
      this.gammatable[i] = Math.round(255*Math.pow(i/255, this.gamma));
    //console.log("gammatable" + this.gammatable);
  },

  /*
   * disconnect from SPI port
   */
  disconnect : function(){
    if (this.spi) this.spi.closeSync();
  },

  /*
   * send stored buffer with RGB values to WS2801 stripe
   */
  update: function(){
    if (this.spi) {
      this.sendRgbBuffer(this.values);
    }
  },

  /*
   * invert all colors
   */
  invert: function(){
    this.inverted = !this.inverted;
  }, // end invert

  /*
   * send buffer with RGB values to WS2801 stripe in reverse order
   */
  reverse: function(){
    this.reversed = !this.reversed;
  }, // end reverse

  /*
   * clear RGB stripe
   */
  clear: function(){
    this.fill(0x00, 0x00, 0x00);
  }, // end clear

  /*
   * set new RGB index order
   */
  setColorIndex: function(redIndex, greenIndex, blueIndex){
    this.redIndex = redIndex;
    this.greenIndex = greenIndex;
    this.blueIndex = blueIndex;
  }, // end setColorIndex

  /*
   * send buffer with RGB values to WS2801 stripe
   */
  sendRgbBuffer : function(buffer){
    // checking if enough time passed for resetting stripe
    if (microtime.now() > (this.lastWriteTime + this.rowResetTime)){
      // yes, its o.k., lets write
      // but first do gamma correction
      var adjustedBuffer = new Buffer(buffer.length);
      for (var i=0; i < buffer.length; i++){
        adjustedBuffer[i]=this.gammatable[buffer[i]];
      }

	  let message = [{
		sendBuffer: adjustedBuffer,
		byteLength: adjustedBuffer.length,
		//speedHz: this.maxSpeedHz
	  }];

      this.spi.transferSync(message);

      this.lastWriteTime = microtime.now();
      return true;
    }
    console.log('writing too fast, data dropped');
    return false;
  }, // end sendRgbBuffer

  /*
   * fill whole stripe with one color
   */
  fill: function(r,g,b){
    if (this.spi) {
      var colors = this.getRGBArray(r,g,b);
      var colorBuffer = new Buffer(this.channelCount);
      for (var i=0; i<(this.channelCount); i+=3){
        colorBuffer[i+0]=colors[0];
        colorBuffer[i+1]=colors[1];
        colorBuffer[i+2]=colors[2];
      }
      this.sendRgbBuffer(colorBuffer);
    }
  }, //end fill

  /*
   * set color of led index [red, green, blue] from 0 to 255
   */
  setColor: function(ledIndex, color) {
    if (this.spi) {
      var colors = this.getRGBArray(color[0],color[1],color[2]);
      var r, g, b;
      r = colors[0] / 255;
      g = colors[1] / 255;
      b = colors[2] / 255;
      var redChannel = this.getRedChannelIndex(ledIndex);
      this.setChannelPower(redChannel,   r);
      this.setChannelPower(redChannel+1, g);
      this.setChannelPower(redChannel+2, b);
    }
  },

  /*
   * set power of channel from 0 to 1
   */
  setChannelPower: function(channelIndex, powerValue) {
    if (this.spi) {
      if(channelIndex > this.channelCount || channelIndex < 0) {
        return false;
      }
      if(powerValue < 0)
        powerValue = 0;
      if(powerValue > 1)
        powerValue = 1;
      this.values[channelIndex] = Math.floor((Math.pow(16, 2) - 1) * powerValue);
    }
  }, // end setChannelPower

  /*
   * set RGB hexcolor to LED index
   */
  setRGB: function(ledIndex, hexColor) {
    if (this.spi) {
      var rgb = this.getRGBfromHex(hexColor);
      var colors = this.getRGBArray(rgb.r,rgb.g,rgb.b);
      var redChannel = this.getRedChannelIndex(ledIndex);
      this.setChannelPower(redChannel+this.redIndex,   colors[0]);
      this.setChannelPower(redChannel+this.greenIndex, colors[1]);
      this.setChannelPower(redChannel+this.blueIndex,  colors[2]);
    }
  }, // end setRGB

  mySin: function(a, min, max){
    return min + ((max-min)/2.)*(Math.sin(a)+1)
  },

  rainbow: function(a){
    var intense = 255;
    var r = parseInt(this.mySin(a, 0, intense));
    var g = parseInt(this.mySin(a+Math.pi/2, 0, intense));
    var b = parseInt(this.mySin(a + Math.pi, 0, intense));
    var colors = this.getRGBArray(r,g,b);
    return [colors[0],colors[1],colors[2]];
  },

  getChannelCount: function() {
    return this.channelCount;
  },

  getRedChannelIndex: function(ledIndex) {
    return ledIndex * 3;
  },

  getGreenChannelIndex: function(ledIndex) {
    return ledIndex * 3 + 1;
  },

  getBlueChannelIndex: function(ledIndex) {
    return ledIndex * 3 + 2;
  },

  getRGBArray: function(r, g, b){
    var colorArray = new Array(3);
    colorArray[this.redIndex] = r & 0xff;
    colorArray[this.greenIndex] = g & 0xff;
    colorArray[this.blueIndex] = b & 0xff;
    if(this.inverted) {
      colorArray[0] = (1 - colorArray[0]/255)*255;
      colorArray[1] = (1 - colorArray[1]/255)*255;
      colorArray[2] = (1 - colorArray[2]/255)*255;
    }
    return colorArray;
  },

  getRGBfromHex: function(color) {
    var r, g, b;
    if (color.length == 4) {
      r = parseInt(color.replace("#", "").substr(0,1)+color.replace("#", "").substr(0,1),16);
      g = parseInt(color.replace("#", "").substr(1,1)+color.replace("#", "").substr(1,1),16);
      b = parseInt(color.replace("#", "").substr(2,1)+color.replace("#", "").substr(2,1),16);
    }
    if (color.length == 7) {
      r = parseInt(color.replace("#", "").substr(0,2),16);
      g = parseInt(color.replace("#", "").substr(2,2),16);
      b = parseInt(color.replace("#", "").substr(4,2),16);
    }
    var colors = this.getRGBArray(r,g,b);
    r = colors[0] / 255;
    g = colors[1] / 255;
    b = colors[2] / 255;
    if (r>=0 && r<=1 && g>=0 && g<=1 && b>=0 && b<=1) {
      return {r:r,g:g,b:b};
    } else {
      return {r:0,g:0,b:0};
    }
  }
}

module.exports = new RPiWS2801();
<|endoftext|>


<commit_before>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

const express = require('express');
const _ = require('lodash');
const validate = require('jsonschema').validate;

const Group = require('../models/Group');

const router = express.Router();

/**
 * Given a meter or group, return only the name and ID of that meter or group.
 * This exists to control what data we send to the client.
 * @param item group or meter
 * @returns {{id, name}}
 */
function formatToIDandNameOnly(item) {
	return { id: item.id, name: item.name };
}

/**
 * GET IDs and names of all groups
 */
router.get('/', async (req, res) => {
	try {
		const rows = await Group.getAll();
		res.json(rows.map(formatToIDandNameOnly));
	} catch (err) {
		console.error(`Error while preforming GET all groups query: ${err}`); // eslint-disable-line no-console
	}
});

/**
 * GET meters and groups that are immediate children of a given group
 * This will only return IDs because it queries groups_immediate_children and groups_immediate_meters, which store
 * only the IDs of the children.
 * @param int group_id
 * @return {[int], [int]}  child meter IDs and child group IDs
 */
router.get('/children/:group_id', async (req, res) => {
	try {
		const [meters, groups] = await Promise.all([
			Group.getImmediateMetersByGroupID(req.params.group_id),
			Group.getImmediateGroupsByGroupID(req.params.group_id)
		]);
		res.json({ meters, groups });
	} catch (err) {
		console.error(`Error while preforming GET on all immediate children (meters and groups) of specific group: ${err}`); // eslint-disable-line no-console, max-len
	}
});

router.get('/deep/groups/:group_id', async (req, res) => {
	try {
		const [deepGroups] = await Group.getDeepGroupsByGroupID(req.params.group_id);
		res.json({ deepGroups });
	} catch (err) {
		console.error(`Error while preforming GET on all deep child groups of specific group: ${err}`); // eslint-disable-line no-console
	}
});

router.get('/deep/meters/:group_id', async (req, res) => {
	try {
		const [deepMeters] = await Group.getDeepMetersByGroupID(req.params.group_id);
		res.json({ deepMeters });
	} catch (err) {
		console.error(`Error while preforming GET on all deep child meters of specific group: ${err}`); // eslint-disable-line no-console
	}
});

router.post('/create', async (req, res) => {
	const validGroup = {
		type: 'object',
		maxProperties: 3,
		required: ['name', 'childGroups', 'childMeters'],
		properties: {
			name: {
				type: 'string',
				minLength: 1,
			},
			childGroups: {
				type: 'array',
				uniqueItems: true,
				items: {
					type: 'integer'
				},
			},
			childMeters: {
				type: 'array',
				uniqueItems: true,
				items: {
					type: 'integer'
				},
			}
		}
	};

	if (!validate(req.body, validGroup).valid) {
		res.sendStatus(400);
	}

	try {
		const newGroup = new Group(undefined, req.body.name);
		await newGroup.insert();
		await Promise.all(req.body.childGroups.map(gid => newGroup.adoptGroup(gid)));
		await Promise.all(req.body.childMeters.map(mid => newGroup.adoptMeter(mid)));

		res.sendStatus(200);
	} catch (err) {
		console.error(`Error while inserting new group ${err}`); // eslint-disable-line no-console
		res.sendStatus(500);
	}
});

router.put('/edit', async (req, res) => {
	const validGroup = {
		type: 'object',
		maxProperties: 4,
		required: ['id', 'name', 'childGroups', 'childMeters'],
		properties: {
			id: {
				type: 'integer'
			},
			name: {
				type: 'string',
				minLength: 1,
			},
			childGroups: {
				type: 'array',
				uniqueItems: true,
				items: {
					type: 'integer'
				},
			},
			childMeters: {
				type: 'array',
				uniqueItems: true,
				items: {
					type: 'integer'
				},
			}
		}
	};

	if (!validate(req.body, validGroup).valid) {
		res.sendStatus(400);
	}

	try {
		const currentGroup = await Group.getByID(req.body.id);
		if (req.body.name !== currentGroup.name) {
			await currentGroup.rename(req.body.name);
		}

		const currentChildGroups = Group.getImmediateGroupsByGroupID(currentGroup.id);

		const adoptedGroups = _.difference(req.body.childGroups, currentChildGroups);
		if (adoptedGroups.length === 0) {
			await Promise.all(adoptedGroups.map(gid => currentGroup.adoptGroup(gid)));
		}

		const disownedGroups = _.difference(currentChildGroups, req.body.childGroups);
		if (disownedGroups.length === 0) {
			await Promise.all(disownedGroups.map(gid => currentGroup.disownGroup(gid)));
		}

		// Compute meters differences and adopt/disown to make changes
		const currentChildMeters = Group.getImmediateMetersByGroupID(currentGroup.id);

		const adoptedMeters = _.difference(req.body.childMeters, currentChildMeters);
		if (adoptedMeters.length === 0) {
			await Promise.all(adoptedMeters.map(mid => currentGroup.adoptMeter(mid)));
		}

		const disownedMeters = _.difference(currentChildMeters, req.body.childMeters);
		if (disownedMeters.length === 0) {
			await Promise.All(disownedMeters.map(mid => currentGroup.disownMeter(mid)));
		}

		res.sendStatus(200);
	} catch (err) {
		console.error(`Error while editing existing group: ${err}`); // eslint-disable-line no-console
		res.sendStatus(500);
	}
});

router.delete('/delete', async (req, res) => {
	const validParams = {
		type: 'object',
		maxProperties: 1,
		required: ['id'],
		properties: {
			id: { type: 'integer' }
		}
	};
	if (!validate(req.body, validParams).valid) {
		res.sendStatus(400);
	}
	try {
		await Group.delete(req.body.id);
	} catch (err) {
		console.error(`Error while deleting group ${err}`); // eslint-disable-line no-console
		res.sendStatus(500);
	}
});

module.exports = router;
<commit_msg>Fix more syntax issues<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

const express = require('express');
const _ = require('lodash');
const validate = require('jsonschema').validate;

const Group = require('../models/Group');

const router = express.Router();

/**
 * Given a meter or group, return only the name and ID of that meter or group.
 * This exists to control what data we send to the client.
 * @param item group or meter
 * @returns {{id, name}}
 */
function formatToIDandNameOnly(item) {
	return { id: item.id, name: item.name };
}

/**
 * GET IDs and names of all groups
 */
router.get('/', async (req, res) => {
	try {
		const rows = await Group.getAll();
		res.json(rows.map(formatToIDandNameOnly));
	} catch (err) {
		console.error(`Error while preforming GET all groups query: ${err}`); // eslint-disable-line no-console
	}
});

/**
 * GET meters and groups that are immediate children of a given group
 * This will only return IDs because it queries groups_immediate_children and groups_immediate_meters, which store
 * only the IDs of the children.
 * @param int group_id
 * @return {[int], [int]}  child meter IDs and child group IDs
 */
router.get('/children/:group_id', async (req, res) => {
	try {
		const [meters, groups] = await Promise.all([
			Group.getImmediateMetersByGroupID(req.params.group_id),
			Group.getImmediateGroupsByGroupID(req.params.group_id)
		]);
		res.json({ meters, groups });
	} catch (err) {
		console.error(`Error while preforming GET on all immediate children (meters and groups) of specific group: ${err}`); // eslint-disable-line no-console, max-len
	}
});

router.get('/deep/groups/:group_id', async (req, res) => {
	try {
		const [deepGroups] = await Group.getDeepGroupsByGroupID(req.params.group_id);
		res.json({ deepGroups });
	} catch (err) {
		console.error(`Error while preforming GET on all deep child groups of specific group: ${err}`); // eslint-disable-line no-console
	}
});

router.get('/deep/meters/:group_id', async (req, res) => {
	try {
		const [deepMeters] = await Group.getDeepMetersByGroupID(req.params.group_id);
		res.json({ deepMeters });
	} catch (err) {
		console.error(`Error while preforming GET on all deep child meters of specific group: ${err}`); // eslint-disable-line no-console
	}
});

router.post('/create', async (req, res) => {
	const validGroup = {
		type: 'object',
		maxProperties: 3,
		required: ['name', 'childGroups', 'childMeters'],
		properties: {
			name: {
				type: 'string',
				minLength: 1,
			},
			childGroups: {
				type: 'array',
				uniqueItems: true,
				items: {
					type: 'integer'
				},
			},
			childMeters: {
				type: 'array',
				uniqueItems: true,
				items: {
					type: 'integer'
				},
			}
		}
	};

	if (!validate(req.body, validGroup).valid) {
		res.sendStatus(400);
	}

	try {
		const newGroup = new Group(undefined, req.body.name);
		await newGroup.insert();
		await Promise.all(req.body.childGroups.map(gid => newGroup.adoptGroup(gid)));
		await Promise.all(req.body.childMeters.map(mid => newGroup.adoptMeter(mid)));

		res.sendStatus(200);
	} catch (err) {
		console.error(`Error while inserting new group ${err}`); // eslint-disable-line no-console
		res.sendStatus(500);
	}
});

router.put('/edit', async (req, res) => {
	const validGroup = {
		type: 'object',
		maxProperties: 4,
		required: ['id', 'name', 'childGroups', 'childMeters'],
		properties: {
			id: {
				type: 'integer'
			},
			name: {
				type: 'string',
				minLength: 1,
			},
			childGroups: {
				type: 'array',
				uniqueItems: true,
				items: {
					type: 'integer'
				},
			},
			childMeters: {
				type: 'array',
				uniqueItems: true,
				items: {
					type: 'integer'
				},
			}
		}
	};

	if (!validate(req.body, validGroup).valid) {
		res.sendStatus(400);
	}

	try {
		const currentGroup = await Group.getByID(req.body.id);
		if (req.body.name !== currentGroup.name) {
			await currentGroup.rename(req.body.name);
		}

		const currentChildGroups = await Group.getImmediateGroupsByGroupID(currentGroup.id);

		const adoptedGroups = _.difference(req.body.childGroups, currentChildGroups);
		if (adoptedGroups.length === 0) {
			await Promise.all(adoptedGroups.map(gid => currentGroup.adoptGroup(gid)));
		}

		const disownedGroups = _.difference(currentChildGroups, req.body.childGroups);
		if (disownedGroups.length === 0) {
			await Promise.all(disownedGroups.map(gid => currentGroup.disownGroup(gid)));
		}

		// Compute meters differences and adopt/disown to make changes
		const currentChildMeters = await Group.getImmediateMetersByGroupID(currentGroup.id);

		const adoptedMeters = _.difference(req.body.childMeters, currentChildMeters);
		if (adoptedMeters.length === 0) {
			await Promise.all(adoptedMeters.map(mid => currentGroup.adoptMeter(mid)));
		}

		const disownedMeters = _.difference(currentChildMeters, req.body.childMeters);
		if (disownedMeters.length === 0) {
			await Promise.all(disownedMeters.map(mid => currentGroup.disownMeter(mid)));
		}

		res.sendStatus(200);
	} catch (err) {
		console.error(`Error while editing existing group: ${err}`); // eslint-disable-line no-console
		res.sendStatus(500);
	}
});

router.delete('/delete', async (req, res) => {
	const validParams = {
		type: 'object',
		maxProperties: 1,
		required: ['id'],
		properties: {
			id: { type: 'integer' }
		}
	};
	if (!validate(req.body, validParams).valid) {
		res.sendStatus(400);
	}
	try {
		await Group.delete(req.body.id);
	} catch (err) {
		console.error(`Error while deleting group ${err}`); // eslint-disable-line no-console
		res.sendStatus(500);
	}
});

module.exports = router;
<|endoftext|>


<commit_before>var userDAO = require(__dirname + '/../dao/user'),
    mailer = require(__dirname + '/../../mail/mailer');

module.exports = function(app) {
    app.post('/signup', function(req, res) {
        console.log('fram');
        var user = req.body,
            options = {};

        if (!user || !user.email || !user.password)
            return res.send({
                err: 'user\'s informations were not received'
            });

        if (user.role)
            options.role = user.role;

        options.otherFields = {};
        if (user.firstName) {
            options.otherFields.firstName = user.firstName;
        }
        if (user.lastName) {
            options.otherFields.lastName = user.lastName;
        }

        userDAO.signUp(user.email, user.password, options, function(err, user, token) {
            if (err) {
                return res.send({
                    "err": err
                });
            }
            else {
                return res.send({
                    user: user,
                    token: token
                });
            }
        });
    });

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

        if (!user || !user.email || !user.password)
            return res.send({
                err: 'user\'s informations were not received'
            });

        userDAO.signIn(user.email, user.password, function(err, user, token) {
            if (err)
                return res.send({
                    err: err
                });
            else
                return res.send({
                    user: user,
                    token: token
                });
        });
    });

    app.get('/user', function(req, res) {
        var data = req.body;

        if (!data || !data.token) {
            return res.send({
                err: 'token not received'
            });
        }

        userDAO.tokenIsValid(data.token, function(err, user) {
            if (err) {
                return res.send({
                    err: err
                });
            }
            else {
                return res.send({
                    user: user
                })
            }
        });
    });
};<commit_msg>User fix<commit_after>var userDAO = require(__dirname + '/../dao/user'),
    mailer = require(__dirname + '/../../mail/mailer');

module.exports = function(app) {
    app.post('/signup', function(req, res) {
        console.log('fram');
        var user = req.body,
            options = {};

        if (!user || !user.email || !user.password)
            return res.send({
                err: 'user\'s informations were not received'
            });

        if (user.role)
            options.role = user.role;

        options.otherFields = {};
        if (user.firstName) {
            options.otherFields.firstName = user.firstName;
        }
        if (user.lastName) {
            options.otherFields.lastName = user.lastName;
        }

        userDAO.signUp(user.email, user.password, options, function(err, user, token) {
            if (err) {
                return res.send({
                    "err": err
                });
            }
            else {
                return res.send({
                    user: user,
                    token: token
                });
            }
        });
    });

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

        if (!user || !user.email || !user.password)
            return res.send({
                err: 'user\'s informations were not received'
            });

        userDAO.signIn(user.email, user.password, function(err, user, token) {
            if (err)
                return res.send({
                    err: err
                });
            else
                return res.send({
                    user: user,
                    token: token
                });
        });
    });

    app.get('/user', function(req, res) {
        var data = req.query;

        if (!data || !data.token) {
            return res.send({
                err: 'token not received'
            });
        }

        userDAO.tokenIsValid(data.token, function(err, user) {
            if (err) {
                return res.send({
                    err: err
                });
            }
            else {
                return res.send({
                    user: user
                })
            }
        });
    });
};<|endoftext|>


<commit_before>/*
 * The MIT License (MIT)
 *
 * Copyright (c) 2016-2017 Dan "Ducky" Little
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

function OSMGeocoder(Application, options) {
    /** Define the title of the service. */
    this.title = options.title ? options.title : 'Search address';

    /** Title to show at the top of the results. */
    this.resultsTitle = options.resultsTitle ? options.resultsTitle : 'Address search results';

    /** There are no tools for the geocoder, just the search field. */
    this.tools = {};

    /** Aaand the search field. */
    this.fields = [{
        name: 'address', type: 'text', label: 'Address'
    }];

    /** Define the highlight layer */
    this.highlightPath = options.highlightPath ? options.highlightPath : 'highlight/highlight';

    /** This template is specified in HTML instead of referring to a
     *  layer's set of named templates.  This also makes an assumption about the name
     *  of the application (app), and therefore would need overridden in something
     *  other than the demo application.
     */
    this.template = '
' +
                    '' +
                        '' +
                        '{{ display_name }}' +
                    '' +
                    '
';

    this.resultsAsHtml = function(queryid, query) {
        var html = '';

        // TODO: Handle errors from the GeoCoder service better.
        if(true) {
            // get the addresses from the results set.
            html += Application.renderFeaturesWithTemplate(query, 'geocoder', this.template);
        }

        return html;
    };

    this.runQuery = function(queryId, query) {
        var osm_url = 'http://nominatim.openstreetmap.org/search/';
        // boom kick this off.
        var highlight_path = this.highlightPath;
        Application.xhr({
            url: osm_url,
            type: 'json',
            data: {
                format: 'json',
                q: query.fields[0].value
            },
            success: function(results) {
                // populate the reuslts
                Application.dispatch({
                    id: queryId,
                    type: 'MAP_QUERY_RESULTS',
                    // this is a bit of a cheat.
                    layer: 'geocoder', features: results,
                });
                // mark this as finished.
                Application.dispatch({
                    id: queryId,
                    type: 'MAP_QUERY_FINISHED'
                });

                let features = [];
                for(var i = 0, ii = results.length; i < ii; i++) {
                    var r = results[i];
                    features.push({
                        type: 'Feature',
                        geometry: {
                            type: 'Point',
                            coordinates: [feature.lon, feature.lat]
                        },
                        properties: {
                            id: 'address' + i
                        }
                    });
                }

                if(features.length > 0) {
                    Application.clearFeatures(highlight_path);
                    Application.addFeatures(highlight_path, features);
                }
            }
        });
    };

    /** Query the OSM Geocoder Service/.
     */
    this.query = function(selection, fields) {
        Application.dispatchQuery(this.name, selection, fields, []);
    };
}
<commit_msg>Don't make changes at the last minute<commit_after>/*
 * The MIT License (MIT)
 *
 * Copyright (c) 2016-2017 Dan "Ducky" Little
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

function OSMGeocoder(Application, options) {
    /** Define the title of the service. */
    this.title = options.title ? options.title : 'Search address';

    /** Title to show at the top of the results. */
    this.resultsTitle = options.resultsTitle ? options.resultsTitle : 'Address search results';

    /** There are no tools for the geocoder, just the search field. */
    this.tools = {};

    /** Aaand the search field. */
    this.fields = [{
        name: 'address', type: 'text', label: 'Address'
    }];

    /** Define the highlight layer */
    this.highlightPath = options.highlightPath ? options.highlightPath : 'highlight/highlight';

    /** This template is specified in HTML instead of referring to a
     *  layer's set of named templates.  This also makes an assumption about the name
     *  of the application (app), and therefore would need overridden in something
     *  other than the demo application.
     */
    this.template = '
' +
                    '' +
                        '' +
                        '{{ display_name }}' +
                    '' +
                    '
';

    this.resultsAsHtml = function(queryid, query) {
        var html = '';

        // TODO: Handle errors from the GeoCoder service better.
        if(true) {
            // get the addresses from the results set.
            html += Application.renderFeaturesWithTemplate(query, 'geocoder', this.template);
        }

        return html;
    };

    this.runQuery = function(queryId, query) {
        var osm_url = 'http://nominatim.openstreetmap.org/search/';
        // boom kick this off.
        var highlight_path = this.highlightPath;
        Application.xhr({
            url: osm_url,
            type: 'json',
            data: {
                format: 'json',
                q: query.fields[0].value
            },
            success: function(results) {
                // populate the reuslts
                Application.dispatch({
                    id: queryId,
                    type: 'MAP_QUERY_RESULTS',
                    // this is a bit of a cheat.
                    layer: 'geocoder', features: results,
                });
                // mark this as finished.
                Application.dispatch({
                    id: queryId,
                    type: 'MAP_QUERY_FINISHED'
                });

                let features = [];
                for(var i = 0, ii = results.length; i < ii; i++) {
                    var r = results[i];
                    features.push({
                        type: 'Feature',
                        geometry: {
                            type: 'Point',
                            coordinates: [r.lon, r.lat]
                        },
                        properties: {
                            id: 'address' + i
                        }
                    });
                }

                if(features.length > 0) {
                    Application.clearFeatures(highlight_path);
                    Application.addFeatures(highlight_path, features);
                }
            }
        });
    };

    /** Query the OSM Geocoder Service/.
     */
    this.query = function(selection, fields) {
        Application.dispatchQuery(this.name, selection, fields, []);
    };
}
<|endoftext|>


<commit_before>import React, {Component} from 'react';
import { connect } from 'react-redux';
import { Form, TextArea, Button } from 'semantic-ui-react';
import { add } from '../../actions/blog';

class AddBlog extends Component {
	constructor(props) {
		super(props);
		this.state = {
			data: {},
			errors: []
		};
	};

	submit = () => this.props.add(this.state.data).then( () =>{
		this.props.history.push('/blogs');
	}).catch( error => {
		alert(error.message);
	});

	handleChange = e => { this.setState({ data: { ...this.state.data, [e.target.name]: e.target.value } }); };

	render() {
		return (

Add blog form goes here

				<Form onSubmit={this.submit}>
					<Form.Input
					fluid={true}
					name={'title'}
					placeholder={'Add a title here...'}
					label={'Blog title'}
					onChange={this.handleChange}/>
					<TextArea
						autoHeight
						fluid={'true'}
						name={'content'}
						placeholder={'Blog content'}
						style={{minHeight: 250}}
						onChange={this.handleChange}/>

					<Form.Input
						fluid={true}
						name={'imageUrl'}
						placeholder={'Link to an image...'}
						label={'Blog Cover'}
						onChange={this.handleChange}/>
					<Form.Input
						fluid={true}
						name={'imageCaption'}
						placeholder={'Image caption, credit'}
						label={'Image caption'}
						onChange={this.handleChange}/>
					<Form.Button social={'twitter'}>Submit</Form.Button>
				</Form>

		);
	}
}

export default connect(null, { add })(AddBlog);
<commit_msg>Blog add with category<commit_after>import React, {Component} from 'react';
import { connect } from 'react-redux';
import { Form, TextArea, Button } from 'semantic-ui-react';
import { add } from '../../actions/blog';

class AddBlog extends Component {
	constructor(props) {
		super(props);
		this.state = {
			data: {},
			errors: []
		};
	};

	submit = () => this.props.add(this.state.data).then( () =>{
		this.props.history.push('/blogs');
	}).catch( error => {
		alert(error.message);
	});

	handleChange = e => { this.setState({ data: { ...this.state.data, [e.target.name]: e.target.value } }); };

	handleSelect = (e, data) => { this.setState({ data: { ...this.state.data, [data.name]: data.value } }); };

	render() {
		const options = [ ...this.props.categories.map( category => ( { text: category.name, value: category._id }))];
		return (

Add blog form goes here

				<Form onSubmit={this.submit}>
					<Form.Input
					fluid={true}
					name={'title'}
					placeholder={'Add a title here...'}
					label={'Blog title'}
					onChange={this.handleChange}/>
					<TextArea
						autoHeight
						fluid={'true'}
						name={'content'}
						placeholder={'Blog content'}
						style={{minHeight: 250}}
						onChange={this.handleChange}/>

					<Form.Input
						fluid={true}
						name={'imageUrl'}
						placeholder={'Link to an image...'}
						label={'Blog Cover'}
						onChange={this.handleChange}/>
					<Form.Input
						fluid={true}
						name={'imageCaption'}
						placeholder={'Image caption, credit'}
						label={'Image caption'}
						onChange={this.handleChange}/>
					<Form.Select
						fluid={true}
						label='Category'
						options={options}
						placeholder='Category'
						name={'category'}
						onChange={this.handleSelect}/>
					<Form.Button social={'twitter'}>Submit</Form.Button>
				</Form>

		);
	}
}

const mapStateToProps = state => ({
	categories: state.categories,
	blogs: state.blogs
});

export default connect(mapStateToProps, { add })(AddBlog);
<|endoftext|>


<commit_before>(function () {
	/**
	 * Reference to determine what "options" member should be created using the type of parameter that is received through the $-plugin.
	 * @namespace
	 */
	// TODO: This should be in the init() method of each widget
	var map = {
		/**
		 * When a type String is received, an options.content should be created.
		 * @memberOf map
		 * @type {String}
		 */
		'string': 'content',
		/**
		 * When a type Object and instance of $ is received, an options.content should be created.
		 * @memberOf map
		 * @type {String}
		 */
		'object': 'content',
		/**
		 * When a type Number is received, an options.num should be created.
		 * @memberOf map
		 * @type {String}
		 */
		'number': 'num',
		/**
		 * When a type Function is received, an options.fn should be created.
		 * @memberOf map
		 * @type {String}
		 */
		'function': 'fn'
	};

	/**
	 * Method in change of expose a friendly interface of the Chico constructors.
	 * @methodOf ch
	 * @param {Object} Klass Direct reference to the constructor from where the $-plugin will be created.
	 * @see http://docs.jquery.com/Plugins/Authoring
	 */
	function factory(Klass) {
		/**
		 * Identification of the constructor, in lowercases.
		 * @type {String}
		 */
		var name = Klass.prototype.name,
			/**
			 * Reference to the class name. When it's a interface, take its constructor name via the "interface" property.
			 * @type {String}
			 */
			constructorName = Klass.prototype.preset || name;

		/**
		 * The class constructor exposed directly into the "ch" namespace.
		 * @exampleDescription Creating a widget instance by specifying a query selector and a configuration object.
		 * @example
		 * ch.Widget($('#example'), {
		 *     'key': 'value'
		 * });
		 */
		// Uses the function.name property (non-standard) on the newest browsers OR
		// uppercases the first letter from the identification name of the constructor
		ch[(name.charAt(0).toUpperCase() + name.substr(1))] = Klass;

		/**
		 * The class constructor exposed into the "$" namespace.
		 * @exampleDescription Creating a widget instance by specifying a query selector and a configuration object.
		 * @example
		 * $.widget($('#example'), {
		 *     'key': 'value'
		 * });
		 * @exampleDescription Creating a widget instance by specifying only a query selector. The default options of each widget will be used.
		 * @example
		 * $.widget($('#example')});
		 * @exampleDescription Creating a widget instance by specifying only a cofiguration object. It only works on compatible widgets, when those doesn't depends on a element to be created.
		 * @example
		 * $.widget({
		 *     'key': 'value'
		 * });
		 * @exampleDescription Creating a widget instance by no specifying parameters. It only works on compatible widgets, when those doesn't depends on a element to be created. The default options of each widget will be used.
		 * @example
		 * $.widget();
		 */
		$[name] = function ($el, options) {
			// Create a new instance of the constructor and return it
			return new Klass($el, options);
		};

		/**
		 * The class constructor exposed as a "$" plugin.
		 * @exampleDescription Creating a widget instance by specifying only a cofiguration object.
		 * @example
		 * $('#example').widget({
		 *     'key': 'value'
		 * });
		 * @exampleDescription Creating a widget instance by specifying only a query selector as parameter. It will be into the "options" object as "content".
		 * @example
		 * $('#example').widget($('#anotherElement'));
		 * @exampleDescription Creating a widget instance by specifying only a string parameter. It will be into the "options" object as "content".
		 * @example
		 * $('#example').widget('A string parameter');
		 * @exampleDescription Creating a widget instance by specifying only a numeric parameter. It will be into the "options" object as "num".
		 * @example
		 * $('#example').widget(123);
		 * @exampleDescription Creating a widget instance by specifying a numeric parameter followed by a string parameter. These will be into the "options" object as "num" and "content" respectively.
		 * @example
		 * $('#example').widget(123, 'A string parameter');
		 * @exampleDescription Creating a widget instance by specifying only a function as parameter. It will be into the "options" object as "fn".
		 * @example
		 * $('#example').widget(function () { ... });
		 * @exampleDescription Creating a widget instance by specifying a function followed by a string parameter. These will be into the "options" object as "fn" and "content" respectively.
		 * @example
		 * $('#example').widget(function () { ... }, 'A string parameter');
		 * @exampleDescription Creating a widget instance by no specifying parameters. The default options of each widget will be used.
		 * @example
		 * $('#example').widget();
		 */
		$.fn[name] = function (options) {
			// Collection with each instanced widget
			var widgets = [],
				// Each instance of the widget
				widget,
				// What kind of parameter is "options"
				type = typeof options;

			// Put the specified parameters into corresponding options object
			// when the "options" parameter is not the configuration object or
			// it's a query selector
			// TODO: This must be in the init() method of each widget
			if ((options !== undefined && type !== 'object') || ch.util.is$(options)) {
				// Grab temporally the received parameter
				var parameter = options,
					// Grab the second parameter
					content = arguments[1];
				// Empty "options" to use it as the real configuration object
				options = {};
				// Put the received parameter into options object with correspondig name getted from the map
				options[map[type]] = parameter;

				// Could have a content as a second argument when it receives a string or a query selector
				if (typeof content === 'string' || ch.util.is$(content)) {
					options.content = content;
				}
			}

			// Analize every match of the main query selector
			$.each(this, function (i, el) {
				// Get into the "$" scope
				var $el = $(el),
					// Try to get the "data" reference to this widget related to the element
					data = $el.data(constructorName);

				// When this widget isn't related to the element via data, create a new instance and save
				if (!data) {
					// Create a new instance of the widget
					widget = new Klass($el, options);
					// Save the reference to this instance into the element data
					$el.data(constructorName, widget);
				// When this widget is related to the element via data, return it
				} else {
					// Get the data as the widget itself
					widget = data;
					// By dispatching an event, widgets can know when it already exists
					if (ch.util.hasOwn(widget, 'emit')) {
						widget.emit('exists', {
							'type': name,
							'options': options
						});
					}
				}

				// Add the widget reference to the final collection
				widgets.push(widget);
			});

			// Return the instance/instances of widgets
			return ((widgets.length > 1) ? widgets : widgets[0]);
		};
	}

	// Export
	ch.factory = factory;
}());<commit_msg>GH-936 changed<commit_after>(function () {
	/**
	 * Reference to determine what "options" member should be created using the type of parameter that is received through the $-plugin.
	 * @namespace
	 */
	// TODO: This should be in the init() method of each widget
	var map = {
		/**
		 * When a type String is received, an options.content should be created.
		 * @memberOf map
		 * @type {String}
		 */
		'string': 'content',
		/**
		 * When a type Object and instance of $ is received, an options.content should be created.
		 * @memberOf map
		 * @type {String}
		 */
		'object': 'content',
		/**
		 * When a type Number is received, an options.num should be created.
		 * @memberOf map
		 * @type {String}
		 */
		'number': 'num',
		/**
		 * When a type Function is received, an options.fn should be created.
		 * @memberOf map
		 * @type {String}
		 */
		'function': 'fn'
	};

	/**
	 * Method in change of expose a friendly interface of the Chico constructors.
	 * @methodOf ch
	 * @param {Object} Klass Direct reference to the constructor from where the $-plugin will be created.
	 * @see http://docs.jquery.com/Plugins/Authoring
	 */
	function factory(Klass) {
		/**
		 * Identification of the constructor, in lowercases.
		 * @type {String}
		 */
		var name = Klass.prototype.name,
			/**
			 * Reference to the class name. When it's a interface, take its constructor name via the "preset" property.
			 * @type {String}
			 */
			constructorName = Klass.prototype.preset || name;

		/**
		 * The class constructor exposed directly into the "ch" namespace.
		 * @exampleDescription Creating a widget instance by specifying a query selector and a configuration object.
		 * @example
		 * ch.Widget($('#example'), {
		 *     'key': 'value'
		 * });
		 */
		// Uses the function.name property (non-standard) on the newest browsers OR
		// uppercases the first letter from the identification name of the constructor
		ch[(name.charAt(0).toUpperCase() + name.substr(1))] = Klass;

		/**
		 * The class constructor exposed into the "$" namespace.
		 * @exampleDescription Creating a widget instance by specifying a query selector and a configuration object.
		 * @example
		 * $.widget($('#example'), {
		 *     'key': 'value'
		 * });
		 * @exampleDescription Creating a widget instance by specifying only a query selector. The default options of each widget will be used.
		 * @example
		 * $.widget($('#example')});
		 * @exampleDescription Creating a widget instance by specifying only a cofiguration object. It only works on compatible widgets, when those doesn't depends on a element to be created.
		 * @example
		 * $.widget({
		 *     'key': 'value'
		 * });
		 * @exampleDescription Creating a widget instance by no specifying parameters. It only works on compatible widgets, when those doesn't depends on a element to be created. The default options of each widget will be used.
		 * @example
		 * $.widget();
		 */
		$[name] = function ($el, options) {
			// Create a new instance of the constructor and return it
			return new Klass($el, options);
		};

		/**
		 * The class constructor exposed as a "$" plugin.
		 * @exampleDescription Creating a widget instance by specifying only a cofiguration object.
		 * @example
		 * $('#example').widget({
		 *     'key': 'value'
		 * });
		 * @exampleDescription Creating a widget instance by specifying only a query selector as parameter. It will be into the "options" object as "content".
		 * @example
		 * $('#example').widget($('#anotherElement'));
		 * @exampleDescription Creating a widget instance by specifying only a string parameter. It will be into the "options" object as "content".
		 * @example
		 * $('#example').widget('A string parameter');
		 * @exampleDescription Creating a widget instance by specifying only a numeric parameter. It will be into the "options" object as "num".
		 * @example
		 * $('#example').widget(123);
		 * @exampleDescription Creating a widget instance by specifying a numeric parameter followed by a string parameter. These will be into the "options" object as "num" and "content" respectively.
		 * @example
		 * $('#example').widget(123, 'A string parameter');
		 * @exampleDescription Creating a widget instance by specifying only a function as parameter. It will be into the "options" object as "fn".
		 * @example
		 * $('#example').widget(function () { ... });
		 * @exampleDescription Creating a widget instance by specifying a function followed by a string parameter. These will be into the "options" object as "fn" and "content" respectively.
		 * @example
		 * $('#example').widget(function () { ... }, 'A string parameter');
		 * @exampleDescription Creating a widget instance by no specifying parameters. The default options of each widget will be used.
		 * @example
		 * $('#example').widget();
		 */
		$.fn[name] = function (options) {
			// Collection with each instanced widget
			var widgets = [],
				// Each instance of the widget
				widget,
				// What kind of parameter is "options"
				type = typeof options;

			// Put the specified parameters into corresponding options object
			// when the "options" parameter is not the configuration object or
			// it's a query selector
			// TODO: This must be in the init() method of each widget
			if ((options !== undefined && type !== 'object') || ch.util.is$(options)) {
				// Grab temporally the received parameter
				var parameter = options,
					// Grab the second parameter
					content = arguments[1];
				// Empty "options" to use it as the real configuration object
				options = {};
				// Put the received parameter into options object with correspondig name getted from the map
				options[map[type]] = parameter;

				// Could have a content as a second argument when it receives a string or a query selector
				if (typeof content === 'string' || ch.util.is$(content)) {
					options.content = content;
				}
			}

			// Analize every match of the main query selector
			$.each(this, function (i, el) {
				// Get into the "$" scope
				var $el = $(el),
					// Try to get the "data" reference to this widget related to the element
					data = $el.data(constructorName);

				// When this widget isn't related to the element via data, create a new instance and save
				if (!data) {
					// Create a new instance of the widget
					widget = new Klass($el, options);
					// Save the reference to this instance into the element data
					$el.data(constructorName, widget);
				// When this widget is related to the element via data, return it
				} else {
					// Get the data as the widget itself
					widget = data;
					// By dispatching an event, widgets can know when it already exists
					if (ch.util.hasOwn(widget, 'emit')) {
						widget.emit('exists', {
							'type': name,
							'options': options
						});
					}
				}

				// Add the widget reference to the final collection
				widgets.push(widget);
			});

			// Return the instance/instances of widgets
			return ((widgets.length > 1) ? widgets : widgets[0]);
		};
	}

	// Export
	ch.factory = factory;
}());<|endoftext|>


<commit_before>/*
 *
 * HtmlPrinter (html-printer.js)
 * Author: rtfpessoa
 *
 */

(function() {

  var diffParser = require('./diff-parser.js').DiffParser;
  var printerUtils = require('./printer-utils.js').PrinterUtils;
  var utils = require('./utils.js').Utils;
  var Rematch = require('./rematch.js').Rematch;

  function SideBySidePrinter(config) {
    this.config = config;
  }

  SideBySidePrinter.prototype.generateSideBySideJsonHtml = function(diffFiles) {
    var that = this;
    return '
\n' +
      diffFiles.map(function(file) {

        var diffs;
        if (file.blocks.length) {
          diffs = that.generateSideBySideFileHtml(file);
        } else {
          diffs = that.generateEmptyDiff();
        }

        return '
\n' +
          '
\n' +
          '
\n' +
          '         ' +
          '           +' + file.addedLines + '\n' +
          '         \n' +
          '         ' +
          '           -' + file.deletedLines + '\n' +
          '         \n' +
          '
\n' +
          '
' + printerUtils.getDiffName(file) + '
\n' +
          '
\n' +
          '
\n' +
          '
\n' +
          '
\n' +
          '
\n' +
          '
\n' +
          '           ' + diffs.left +
          '
\n' +
          '
\n' +
          '
\n' +
          '
\n' +
          '
\n' +
          '
\n' +
          '
\n' +
          '
\n' +
          '           ' + diffs.right +
          '
\n' +
          '
\n' +
          '
\n' +
          '
\n' +
          '
\n' +
          '
\n';
      }).join('\n') +
      '
\n';
  };

  var matcher = Rematch.rematch(function(a, b) {
    var amod = a.content.substr(1),
      bmod = b.content.substr(1);
    return Rematch.distance(amod, bmod);
  });

  SideBySidePrinter.prototype.generateSideBySideFileHtml = function(file) {
    var that = this;
    var fileHtml = {};
    fileHtml.left = '';
    fileHtml.right = '';

    file.blocks.forEach(function(block) {

      fileHtml.left += '
\n' +
        '  	\n' +
        '  ' +
        '
' +
        '      ' + utils.escape(block.header) +
        '
' +
        '  	\n' +
        '
\n';

      fileHtml.right += '
\n' +
        '  	\n' +
        '  ' +
        '

' +
        '  	\n' +
        '
\n';

      var oldLines = [];
      var newLines = [];

      function processChangeBlock() {
        var matches;
        var insertType;
        var deleteType;
        var doMatching = that.config.matching === 'lines' || that.config.matching === 'words';

        if (doMatching) {
          matches = matcher(oldLines, newLines);
          insertType = diffParser.LINE_TYPE.INSERT_CHANGES;
          deleteType = diffParser.LINE_TYPE.DELETE_CHANGES;
        } else {
          matches = [[oldLines, newLines]];
          insertType = diffParser.LINE_TYPE.INSERTS;
          deleteType = diffParser.LINE_TYPE.DELETES;
        }

        matches.forEach(function(match) {
          oldLines = match[0];
          newLines = match[1];

          var common = Math.min(oldLines.length, newLines.length);
          var max = Math.max(oldLines.length, newLines.length);

          for (var j = 0; j < common; j++) {
            var oldLine = oldLines[j];
            var newLine = newLines[j];

            that.config.isCombined = file.isCombined;

            var diff = printerUtils.diffHighlight(oldLine.content, newLine.content, that.config);

            fileHtml.left +=
              that.generateSingleLineHtml(deleteType, oldLine.oldNumber,
                diff.first.line, diff.first.prefix);
            fileHtml.right +=
              that.generateSingleLineHtml(insertType, newLine.newNumber,
                diff.second.line, diff.second.prefix);
          }

          if (max > common) {
            var oldSlice = oldLines.slice(common);
            var newSlice = newLines.slice(common);

            var tmpHtml = that.processLines(oldSlice, newSlice);
            fileHtml.left += tmpHtml.left;
            fileHtml.right += tmpHtml.right;
          }
        });

        oldLines = [];
        newLines = [];
      }

      for (var i = 0; i < block.lines.length; i++) {
        var line = block.lines[i];
        var prefix = line[0];
        var escapedLine = utils.escape(line.content.substr(1));

        if (line.type !== diffParser.LINE_TYPE.INSERTS &&
          (newLines.length > 0 || (line.type !== diffParser.LINE_TYPE.DELETES && oldLines.length > 0))) {
          processChangeBlock();
        }

        if (line.type === diffParser.LINE_TYPE.CONTEXT) {
          fileHtml.left += that.generateSingleLineHtml(line.type, line.oldNumber, escapedLine, prefix);
          fileHtml.right += that.generateSingleLineHtml(line.type, line.newNumber, escapedLine, prefix);
        } else if (line.type === diffParser.LINE_TYPE.INSERTS && !oldLines.length) {
          fileHtml.left += that.generateSingleLineHtml(diffParser.LINE_TYPE.CONTEXT, '', '', '');
          fileHtml.right += that.generateSingleLineHtml(line.type, line.newNumber, escapedLine, prefix);
        } else if (line.type === diffParser.LINE_TYPE.DELETES) {
          oldLines.push(line);
        } else if (line.type === diffParser.LINE_TYPE.INSERTS && Boolean(oldLines.length)) {
          newLines.push(line);
        } else {
          console.error('unknown state in html side-by-side generator');
          processChangeBlock();
        }
      }

      processChangeBlock();
    });

    return fileHtml;
  };

  SideBySidePrinter.prototype.processLines = function(oldLines, newLines) {
    var that = this;
    var fileHtml = {};
    fileHtml.left = '';
    fileHtml.right = '';

    var maxLinesNumber = Math.max(oldLines.length, newLines.length);
    for (var i = 0; i < maxLinesNumber; i++) {
      var oldLine = oldLines[i];
      var newLine = newLines[i];
      var oldContent;
      var newContent;
      var oldPrefix;
      var newPrefix;

      if (oldLine) {
        oldContent = utils.escape(oldLine.content.substr(1));
        oldPrefix = oldLine.content[0];
      }

      if (newLine) {
        newContent = utils.escape(newLine.content.substr(1));
        newPrefix = newLine.content[0];
      }

      if (oldLine && newLine) {
        fileHtml.left += that.generateSingleLineHtml(oldLine.type, oldLine.oldNumber, oldContent, oldPrefix);
        fileHtml.right += that.generateSingleLineHtml(newLine.type, newLine.newNumber, newContent, newPrefix);
      } else if (oldLine) {
        fileHtml.left += that.generateSingleLineHtml(oldLine.type, oldLine.oldNumber, oldContent, oldPrefix);
        fileHtml.right += that.generateSingleLineHtml(diffParser.LINE_TYPE.CONTEXT, '', '', '');
      } else if (newLine) {
        fileHtml.left += that.generateSingleLineHtml(diffParser.LINE_TYPE.CONTEXT, '', '', '');
        fileHtml.right += that.generateSingleLineHtml(newLine.type, newLine.newNumber, newContent, newPrefix);
      } else {
        console.error('How did it get here?');
      }
    }

    return fileHtml;
  };

  SideBySidePrinter.prototype.generateSingleLineHtml = function(type, number, content, prefix) {
    var htmlPrefix = '';
    if (prefix) {
      htmlPrefix = '' + prefix + '';
    }

    var htmlContent = '';
    if (content) {
      htmlContent = '' + content + '';
    }

    return '
\n' +
      '    ' + number + '	\n' +
      '    ' +
      '
' + htmlPrefix + htmlContent + '
' +
      '    	\n' +
      '
\n';
  };

  SideBySidePrinter.prototype.generateEmptyDiff = function() {
    var fileHtml = {};
    fileHtml.right = '';

    fileHtml.left = '
\n' +
      '  ' +
      '
' +
      'File without changes' +
      '
' +
      '  	\n' +
      '
\n';

    return fileHtml;
  };

  module.exports.SideBySidePrinter = SideBySidePrinter;

})();
<commit_msg>Fixes prefix bug on side by side diff<commit_after>/*
 *
 * HtmlPrinter (html-printer.js)
 * Author: rtfpessoa
 *
 */

(function() {

  var diffParser = require('./diff-parser.js').DiffParser;
  var printerUtils = require('./printer-utils.js').PrinterUtils;
  var utils = require('./utils.js').Utils;
  var Rematch = require('./rematch.js').Rematch;

  function SideBySidePrinter(config) {
    this.config = config;
  }

  SideBySidePrinter.prototype.generateSideBySideJsonHtml = function(diffFiles) {
    var that = this;
    return '
\n' +
      diffFiles.map(function(file) {

        var diffs;
        if (file.blocks.length) {
          diffs = that.generateSideBySideFileHtml(file);
        } else {
          diffs = that.generateEmptyDiff();
        }

        return '
\n' +
          '
\n' +
          '
\n' +
          '         ' +
          '           +' + file.addedLines + '\n' +
          '         \n' +
          '         ' +
          '           -' + file.deletedLines + '\n' +
          '         \n' +
          '
\n' +
          '
' + printerUtils.getDiffName(file) + '
\n' +
          '
\n' +
          '
\n' +
          '
\n' +
          '
\n' +
          '
\n' +
          '
\n' +
          '           ' + diffs.left +
          '
\n' +
          '
\n' +
          '
\n' +
          '
\n' +
          '
\n' +
          '
\n' +
          '
\n' +
          '
\n' +
          '           ' + diffs.right +
          '
\n' +
          '
\n' +
          '
\n' +
          '
\n' +
          '
\n' +
          '
\n';
      }).join('\n') +
      '
\n';
  };

  var matcher = Rematch.rematch(function(a, b) {
    var amod = a.content.substr(1),
      bmod = b.content.substr(1);
    return Rematch.distance(amod, bmod);
  });

  SideBySidePrinter.prototype.generateSideBySideFileHtml = function(file) {
    var that = this;
    var fileHtml = {};
    fileHtml.left = '';
    fileHtml.right = '';

    file.blocks.forEach(function(block) {

      fileHtml.left += '
\n' +
        '  	\n' +
        '  ' +
        '
' +
        '      ' + utils.escape(block.header) +
        '
' +
        '  	\n' +
        '
\n';

      fileHtml.right += '
\n' +
        '  	\n' +
        '  ' +
        '

' +
        '  	\n' +
        '
\n';

      var oldLines = [];
      var newLines = [];

      function processChangeBlock() {
        var matches;
        var insertType;
        var deleteType;
        var doMatching = that.config.matching === 'lines' || that.config.matching === 'words';

        if (doMatching) {
          matches = matcher(oldLines, newLines);
          insertType = diffParser.LINE_TYPE.INSERT_CHANGES;
          deleteType = diffParser.LINE_TYPE.DELETE_CHANGES;
        } else {
          matches = [[oldLines, newLines]];
          insertType = diffParser.LINE_TYPE.INSERTS;
          deleteType = diffParser.LINE_TYPE.DELETES;
        }

        matches.forEach(function(match) {
          oldLines = match[0];
          newLines = match[1];

          var common = Math.min(oldLines.length, newLines.length);
          var max = Math.max(oldLines.length, newLines.length);

          for (var j = 0; j < common; j++) {
            var oldLine = oldLines[j];
            var newLine = newLines[j];

            that.config.isCombined = file.isCombined;

            var diff = printerUtils.diffHighlight(oldLine.content, newLine.content, that.config);

            fileHtml.left +=
              that.generateSingleLineHtml(deleteType, oldLine.oldNumber,
                diff.first.line, diff.first.prefix);
            fileHtml.right +=
              that.generateSingleLineHtml(insertType, newLine.newNumber,
                diff.second.line, diff.second.prefix);
          }

          if (max > common) {
            var oldSlice = oldLines.slice(common);
            var newSlice = newLines.slice(common);

            var tmpHtml = that.processLines(oldSlice, newSlice);
            fileHtml.left += tmpHtml.left;
            fileHtml.right += tmpHtml.right;
          }
        });

        oldLines = [];
        newLines = [];
      }

      for (var i = 0; i < block.lines.length; i++) {
        var line = block.lines[i];
        var prefix = line.content[0];
        var escapedLine = utils.escape(line.content.substr(1));

        if (line.type !== diffParser.LINE_TYPE.INSERTS &&
          (newLines.length > 0 || (line.type !== diffParser.LINE_TYPE.DELETES && oldLines.length > 0))) {
          processChangeBlock();
        }

        if (line.type === diffParser.LINE_TYPE.CONTEXT) {
          fileHtml.left += that.generateSingleLineHtml(line.type, line.oldNumber, escapedLine, prefix);
          fileHtml.right += that.generateSingleLineHtml(line.type, line.newNumber, escapedLine, prefix);
        } else if (line.type === diffParser.LINE_TYPE.INSERTS && !oldLines.length) {
          fileHtml.left += that.generateSingleLineHtml(diffParser.LINE_TYPE.CONTEXT, '', '', '');
          fileHtml.right += that.generateSingleLineHtml(line.type, line.newNumber, escapedLine, prefix);
        } else if (line.type === diffParser.LINE_TYPE.DELETES) {
          oldLines.push(line);
        } else if (line.type === diffParser.LINE_TYPE.INSERTS && Boolean(oldLines.length)) {
          newLines.push(line);
        } else {
          console.error('unknown state in html side-by-side generator');
          processChangeBlock();
        }
      }

      processChangeBlock();
    });

    return fileHtml;
  };

  SideBySidePrinter.prototype.processLines = function(oldLines, newLines) {
    var that = this;
    var fileHtml = {};
    fileHtml.left = '';
    fileHtml.right = '';

    var maxLinesNumber = Math.max(oldLines.length, newLines.length);
    for (var i = 0; i < maxLinesNumber; i++) {
      var oldLine = oldLines[i];
      var newLine = newLines[i];
      var oldContent;
      var newContent;
      var oldPrefix;
      var newPrefix;

      if (oldLine) {
        oldContent = utils.escape(oldLine.content.substr(1));
        oldPrefix = oldLine.content[0];
      }

      if (newLine) {
        newContent = utils.escape(newLine.content.substr(1));
        newPrefix = newLine.content[0];
      }

      if (oldLine && newLine) {
        fileHtml.left += that.generateSingleLineHtml(oldLine.type, oldLine.oldNumber, oldContent, oldPrefix);
        fileHtml.right += that.generateSingleLineHtml(newLine.type, newLine.newNumber, newContent, newPrefix);
      } else if (oldLine) {
        fileHtml.left += that.generateSingleLineHtml(oldLine.type, oldLine.oldNumber, oldContent, oldPrefix);
        fileHtml.right += that.generateSingleLineHtml(diffParser.LINE_TYPE.CONTEXT, '', '', '');
      } else if (newLine) {
        fileHtml.left += that.generateSingleLineHtml(diffParser.LINE_TYPE.CONTEXT, '', '', '');
        fileHtml.right += that.generateSingleLineHtml(newLine.type, newLine.newNumber, newContent, newPrefix);
      } else {
        console.error('How did it get here?');
      }
    }

    return fileHtml;
  };

  SideBySidePrinter.prototype.generateSingleLineHtml = function(type, number, content, prefix) {
    var htmlPrefix = '';
    if (prefix) {
      htmlPrefix = '' + prefix + '';
    }

    var htmlContent = '';
    if (content) {
      htmlContent = '' + content + '';
    }

    return '
\n' +
      '    ' + number + '	\n' +
      '    ' +
      '
' + htmlPrefix + htmlContent + '
' +
      '    	\n' +
      '
\n';
  };

  SideBySidePrinter.prototype.generateEmptyDiff = function() {
    var fileHtml = {};
    fileHtml.right = '';

    fileHtml.left = '
\n' +
      '  ' +
      '
' +
      'File without changes' +
      '
' +
      '  	\n' +
      '
\n';

    return fileHtml;
  };

  module.exports.SideBySidePrinter = SideBySidePrinter;

})();
<|endoftext|>


<commit_before>RealityCheck = (function ($) {
    "use strict";

    var reality_check_url = page.url.url_for('user/reality_check');

    function RealityCheck(cookieName, persistentStore, logoutLocation) {
        var val;

        this.cookieName = cookieName;
        this.storage = persistentStore;

        val = ($.cookie(this.cookieName)||'').split(',');
        val[0] = parseInt(val[0]);
        if (!val[0]>0) return;  // no or invalid cookie

        if (val[1] && val[1] != persistentStore.get('reality_check.srvtime')) {
            persistentStore.set('reality_check.srvtime', val[1]);
            persistentStore.set('reality_check.basetime', (new Date).getTime());
            persistentStore.set('reality_check.ack', 1);
        }

        this.logoutLocation = logoutLocation;
        if (!this.logoutLocation) return; // not logged in?

        this.interval = parseInt(val) * 60 * 1000; // convert minutes to millisec

        this.basetime = persistentStore.get('reality_check.basetime');

        return this.setAlarm();
    };

    RealityCheck.prototype.setAlarm = function () {
        var that = this;
        var alrm = this.interval - ((new Date).getTime() - this.basetime) % this.interval;

        if (this.tmout) window.clearTimeout(this.tmout);

        this.tmout = window.setTimeout(function () {
            that.fire();
        }, alrm);
    };

    RealityCheck.prototype.fire = function () {
        var that = this;

        $.ajax({
            url: reality_check_url,
            dataType: 'text',
            success: function (data) {
                that.display(data);
            },
            error: function (xhr) {
                if (xhr.status === 404) return; // no MF loginid
                window.setTimeout(function () {
                    that.fire();
                }, 5000)
            },
        });
    };

    RealityCheck.prototype.display = function (data) {
        var that = this, outer, middle, storage_handler;

        if (outer = $('#reality-check')) outer.remove();

        outer = $("

").appendTo('body');
        middle = $('
').appendTo(outer);
        $('
' + data + '
').appendTo(middle);

        storage_handler = function (jq_event) {
            var ack;

            if (jq_event.originalEvent.key !== 'reality_check.ack') return;
            ack = parseInt(jq_event.originalEvent.newValue || 1);
            if (ack > that.lastAck) {
                $(window).off('storage', storage_handler);
                that.setAlarm();
                $('#reality-check').remove();
            }
        };

        // in case the client works with multiple windows, check if he has acknowleged
        // it in another window.
        $(window).on('storage', storage_handler);

        this.lastAck = parseInt(this.storage.get('reality_check.ack') || 1);
        $('#reality-check [bcont=1]').on('click', function () {
            that.storage.set('reality_check.ack', that.lastAck+1);
            $(window).off('storage', storage_handler);
            that.setAlarm();
            $('#reality-check').remove();
        });

        $('#reality-check .blogout').on('click', function () {
            window.location.href = that.logoutLocation;
        });
    };

    return RealityCheck;
}(jQuery));

onLoad.queue(function () {
    var logoutBtn = $('#btn_logout')[0];
    if (!logoutBtn) return;
    new RealityCheck('reality_check', LocalStore, logoutBtn.getAttribute('href'));
});
<commit_msg>satisfy jslint<commit_after>RealityCheck = (function ($) {
    "use strict";

    var reality_check_url = page.url.url_for('user/reality_check');

    function RealityCheck(cookieName, persistentStore, logoutLocation) {
        var val;

        this.cookieName = cookieName;
        this.storage = persistentStore;

        val = ($.cookie(this.cookieName)||'').split(',');
        val[0] = parseInt(val[0]);
        if (isNaN(val[0]) || val[0]<=0) return;  // no or invalid cookie

        if (val[1] && val[1] != persistentStore.get('reality_check.srvtime')) {
            persistentStore.set('reality_check.srvtime', val[1]);
            persistentStore.set('reality_check.basetime', (new Date()).getTime());
            persistentStore.set('reality_check.ack', 1);
        }

        this.logoutLocation = logoutLocation;
        if (!this.logoutLocation) return; // not logged in?

        this.interval = parseInt(val) * 60 * 1000; // convert minutes to millisec

        this.basetime = persistentStore.get('reality_check.basetime');

        return this.setAlarm();
    }

    RealityCheck.prototype.setAlarm = function () {
        var that = this;
        var alrm = this.interval - ((new Date()).getTime() - this.basetime) % this.interval;

        if (this.tmout) window.clearTimeout(this.tmout);

        this.tmout = window.setTimeout(function () {
            that.fire();
        }, alrm);
    };

    RealityCheck.prototype.fire = function () {
        var that = this;

        $.ajax({
            url: reality_check_url,
            dataType: 'text',
            success: function (data) {
                that.display(data);
            },
            error: function (xhr) {
                if (xhr.status === 404) return; // no MF loginid
                window.setTimeout(function () {
                    that.fire();
                }, 5000);
            },
        });
    };

    RealityCheck.prototype.display = function (data) {
        var that = this, outer, middle, storage_handler;

        outer = $('#reality-check');
        if (outer) outer.remove();

        outer = $("

").appendTo('body');
        middle = $('
').appendTo(outer);
        $('
' + data + '
').appendTo(middle);

        storage_handler = function (jq_event) {
            var ack;

            if (jq_event.originalEvent.key !== 'reality_check.ack') return;
            ack = parseInt(jq_event.originalEvent.newValue || 1);
            if (ack > that.lastAck) {
                $(window).off('storage', storage_handler);
                that.setAlarm();
                $('#reality-check').remove();
            }
        };

        // in case the client works with multiple windows, check if he has acknowleged
        // it in another window.
        $(window).on('storage', storage_handler);

        this.lastAck = parseInt(this.storage.get('reality_check.ack') || 1);
        $('#reality-check [bcont=1]').on('click', function () {
            that.storage.set('reality_check.ack', that.lastAck+1);
            $(window).off('storage', storage_handler);
            that.setAlarm();
            $('#reality-check').remove();
        });

        $('#reality-check .blogout').on('click', function () {
            window.location.href = that.logoutLocation;
        });
    };

    return RealityCheck;
}(jQuery));

onLoad.queue(function () {
    var logoutBtn = $('#btn_logout')[0];
    if (!logoutBtn) return;
    new RealityCheck('reality_check', LocalStore, logoutBtn.getAttribute('href'));
});
<|endoftext|>


<commit_before>module("calendar rendering", {
  setup: createCalendarContainer
});

test("shows year", function() {
  calendar().continuousCalendar({
    startDate: [30, 4, 2008],
    weeksBefore: 0,
    weeksAfter: 0
  });
  equals($.trim(calendar().find(".continuousCalendar thead th.year").text()), "2008");
});

test("shows week days", function() {
  calendar().continuousCalendar({
    startDate: [30, 4, 2008],
    weeksBefore: 0,
    weeksAfter: 0
  });
  equals($.trim(calendar().find(".continuousCalendar thead th.weekDay").text()), [
    "ma", "ti", "ke", "to", "pe", "la", "su"
  ].join(""));
});

test("lists week days for vappu 2009", function() {
  calendar().continuousCalendar({
    startDate: [30, 4, 2009],
    weeksBefore: 0,
    weeksAfter: 0
  });
  equals($.trim(calendar().find(".continuousCalendar tbody").html()), [
    '
',
    '	',
    '27	',
    '28	',
    '29	',
    '30	',
    '1	',
    '2	',
    '3	',
    '
'
  ].join(""));
});

test("lists given number of weeks before given date", function() {
  calendar().continuousCalendar({
    startDate: [18, 4, 2009],
    weeksBefore: 2,
    weeksAfter: 0
  });
  equals(calendar().find(".date").text(), [
    "30","31","1","2","3","4","5",
    "6","7","8","9","10","11","12",
    "13","14","15","16","17","18","19"
  ].join(""));
});

test("lists given number of weeks after given date", function() {
  calendar().continuousCalendar({
    startDate: [18, 4, 2009],
    weeksBefore: 0,
    weeksAfter: 2
  });
  equals($.trim(calendar().find(".date").text()), [
    "13","14","15","16","17","18","19",
    "20","21","22","23","24","25","26",
    "27","28","29","30","1","2","3"
  ].join(""));
});

test("shows month name on first row of full week", function() {
  calendar().continuousCalendar({
    startDate: [30, 4, 2009],
    weeksBefore: 0,
    weeksAfter: 5
  });
  var months = calendar().find(".month");
  equals(months.size(), 2);
  var firstMonth = months.eq(0);
  equals(firstMonth.text(), "toukokuu");
  equals(firstMonth.next().text(), "4");
  var secondMonth = months.eq(1);
  equals(secondMonth.text(), "kesäkuu");
  equals(secondMonth.next().text(), "1");
});

test("highlights current date", function() {
  var today = new Date();
  calendar().continuousCalendar({
    startDate: [today.getDate(), today.getMonth() + 1, today.getFullYear()],
    weeksBefore: 20,
    weeksAfter: 20
  });
  var cells = calendar().find(".today");
  equals(cells.size(), 1);
  equals(cells.text(), today.getDate());
});

test("highlights selected date", function() {
  calendar().continuousCalendar({
    startDate:  [30, 4, 2009],
    weeksBefore:2,
    weeksAfter:2
  });
  equals(calendar().find(".selected").text(), "30");
});

test("higlights selected date range", function() {
  calendar().continuousCalendar({
    startDate:  [29, 4, 2009],
    endDate: [5, 5, 2009],
    weeksBefore:2,
    weeksAfter:2
  });
  equals(calendar().find(".selected").size(), 7);
});

module("calendar events", {
  setup: createCalendarContainer
});

test("highlight clicked day", function() {
  calendar().continuousCalendar({
    startDate: [30, 4, 2008],
    weeksBefore: 0,
    weeksAfter: 0
  });
  calendar().find(".date:eq(1)").click();
  equals(calendar().find(".selected").text(), "29");
});

//TODO week number selects week
//TODO month nam selects month
//TODO kahva selectioniin
var testIndex = 0;

function createCalendarContainer() {
  testIndex++;
  var container = $("

");
  var index = $('

').append(testIndex).css({
    "float": "left",
    "font-weight": "bold",
    "color": "green"
  });
  container.attr("id", calendarId());
  $("#main").append(index).append(container);
}

function calendar() {
  return $("#" + calendarId());
}

function calendarId() {
  return "continuousCalendar" + testIndex;
}
<commit_msg>todo's<commit_after>module("calendar rendering", {
  setup: createCalendarContainer
});

test("shows year", function() {
  calendar().continuousCalendar({
    startDate: [30, 4, 2008],
    weeksBefore: 0,
    weeksAfter: 0
  });
  equals($.trim(calendar().find(".continuousCalendar thead th.year").text()), "2008");
});

test("shows week days", function() {
  calendar().continuousCalendar({
    startDate: [30, 4, 2008],
    weeksBefore: 0,
    weeksAfter: 0
  });
  equals($.trim(calendar().find(".continuousCalendar thead th.weekDay").text()), [
    "ma", "ti", "ke", "to", "pe", "la", "su"
  ].join(""));
});

test("lists week days for vappu 2009", function() {
  calendar().continuousCalendar({
    startDate: [30, 4, 2009],
    weeksBefore: 0,
    weeksAfter: 0
  });
  equals($.trim(calendar().find(".continuousCalendar tbody").html()), [
    '
',
    '	',
    '27	',
    '28	',
    '29	',
    '30	',
    '1	',
    '2	',
    '3	',
    '
'
  ].join(""));
});

test("lists given number of weeks before given date", function() {
  calendar().continuousCalendar({
    startDate: [18, 4, 2009],
    weeksBefore: 2,
    weeksAfter: 0
  });
  equals(calendar().find(".date").text(), [
    "30","31","1","2","3","4","5",
    "6","7","8","9","10","11","12",
    "13","14","15","16","17","18","19"
  ].join(""));
});

test("lists given number of weeks after given date", function() {
  calendar().continuousCalendar({
    startDate: [18, 4, 2009],
    weeksBefore: 0,
    weeksAfter: 2
  });
  equals($.trim(calendar().find(".date").text()), [
    "13","14","15","16","17","18","19",
    "20","21","22","23","24","25","26",
    "27","28","29","30","1","2","3"
  ].join(""));
});

test("shows month name on first row of full week", function() {
  calendar().continuousCalendar({
    startDate: [30, 4, 2009],
    weeksBefore: 0,
    weeksAfter: 5
  });
  var months = calendar().find(".month");
  equals(months.size(), 2);
  var firstMonth = months.eq(0);
  equals(firstMonth.text(), "toukokuu");
  equals(firstMonth.next().text(), "4");
  var secondMonth = months.eq(1);
  equals(secondMonth.text(), "kesäkuu");
  equals(secondMonth.next().text(), "1");
});

test("highlights current date", function() {
  var today = new Date();
  calendar().continuousCalendar({
    startDate: [today.getDate(), today.getMonth() + 1, today.getFullYear()],
    weeksBefore: 20,
    weeksAfter: 20
  });
  var cells = calendar().find(".today");
  equals(cells.size(), 1);
  equals(cells.text(), today.getDate());
});

test("highlights selected date", function() {
  calendar().continuousCalendar({
    startDate:  [30, 4, 2009],
    weeksBefore:2,
    weeksAfter:2
  });
  equals(calendar().find(".selected").text(), "30");
});

test("higlights selected date range", function() {
  calendar().continuousCalendar({
    startDate:  [29, 4, 2009],
    endDate: [5, 5, 2009],
    weeksBefore:2,
    weeksAfter:2
  });
  equals(calendar().find(".selected").size(), 7);
});

//TODO render week numbers
//TODO render year for first month

module("calendar events", {
  setup: createCalendarContainer
});

test("highlight clicked day", function() {
  calendar().continuousCalendar({
    startDate: [30, 4, 2008],
    weeksBefore: 0,
    weeksAfter: 0
  });
  calendar().find(".date:eq(1)").click();
  equals(calendar().find(".selected").text(), "29");
});

//TODO week number selects week
//TODO month nam selects month
//TODO kahva selectioniin
var testIndex = 0;

function createCalendarContainer() {
  testIndex++;
  var container = $("

");
  var index = $('

').append(testIndex).css({
    "float": "left",
    "font-weight": "bold",
    "color": "green"
  });
  container.attr("id", calendarId());
  $("#main").append(index).append(container);
}

function calendar() {
  return $("#" + calendarId());
}

function calendarId() {
  return "continuousCalendar" + testIndex;
}
<|endoftext|>


<commit_before>
import _ from 'lodash';

import module from '../module';
import GameState from '../../rogue/init/gamestate';
import { StartGameCycle } from '../../rogue/init/init';
import { NewState, SetState } from '../../rogue/init/gameupgrades';
import Upgrades from '../../rogue/constants/upgrades';

module.filter('visibleUpgrades', (CurrencyDataManager, UpgradeDataManager) => {
  return (upgrades, type) => {
    return _.filter(upgrades, upgrade => upgrade.cost / 2 < CurrencyDataManager.currency[type]
      && upgrade.currency === type
      && UpgradeDataManager.canSeeUpgrade(upgrade)
    );
  };
});

module.controller('Upgrades', ($scope, CurrencyDataManager, UpgradeDataManager, TemplateDataManager) => {

  $scope.upgrades = Upgrades;
  $scope.upgradeDataManager = UpgradeDataManager;
  $scope.currencyDataManager = CurrencyDataManager;

  let curState = {};

  $scope.buyUpgrade = (upgrade) => {
    if(!UpgradeDataManager.buyUpgrade(upgrade)) return;
    if(upgrade.operate) upgrade.operate(curState);
  };

  const rebuildUpgrades = () => {
    const newState = curState = NewState();

    _.each(UpgradeDataManager.upgrades, name => {
      const upgrade = _.findWhere(Upgrades, { name });
      if(upgrade && upgrade.operate) upgrade.operate(newState);
    });

    newState.templates = TemplateDataManager.templates;

    SetState(newState);
  };

  rebuildUpgrades();
  StartGameCycle();

  GameState.on('gameover', () => {
    _.each(['sp', 'kp', 'vp'], key => {
      const add = GameState[`${key}Earned`] || 0;
      CurrencyDataManager.addCurrency(key, add);
    });

    rebuildUpgrades();

    $scope.$apply();
  });

});<commit_msg>feat(game): sp/kp are cached while the game runs now and will be added upon restart if the game crashes<commit_after>
import _ from 'lodash';

import module from '../module';
import GameState from '../../rogue/init/gamestate';
import { StartGameCycle } from '../../rogue/init/init';
import { NewState, SetState } from '../../rogue/init/gameupgrades';
import Upgrades from '../../rogue/constants/upgrades';

module.filter('visibleUpgrades', (CurrencyDataManager, UpgradeDataManager) => {
  return (upgrades, type) => {
    return _.filter(upgrades, upgrade => upgrade.cost / 2 < CurrencyDataManager.currency[type]
      && upgrade.currency === type
      && UpgradeDataManager.canSeeUpgrade(upgrade)
    );
  };
});

module.controller('Upgrades', ($scope, $localStorage, CurrencyDataManager, UpgradeDataManager, TemplateDataManager) => {

  $scope.upgrades = Upgrades;
  $scope.upgradeDataManager = UpgradeDataManager;
  $scope.currencyDataManager = CurrencyDataManager;

  let curState = {};

  $scope.buyUpgrade = (upgrade) => {
    if(!UpgradeDataManager.buyUpgrade(upgrade)) return;
    if(upgrade.operate) upgrade.operate(curState);
  };

  const rebuildUpgrades = () => {
    const newState = curState = NewState();

    _.each(UpgradeDataManager.upgrades, name => {
      const upgrade = _.findWhere(Upgrades, { name });
      if(upgrade && upgrade.operate) upgrade.operate(newState);
    });

    newState.templates = TemplateDataManager.templates;

    SetState(newState);
  };

  const getCurrencyFrom = (store) => {
    _.each(['sp', 'kp', 'vp'], key => {
      const add = store[`${key}Earned`] || 0;
      CurrencyDataManager.addCurrency(key, add);
    });
  };

  if($localStorage.saveStateCache) {
    getCurrencyFrom($localStorage.saveStateCache);
    $localStorage.saveStateCache = null;
  }

  rebuildUpgrades();
  StartGameCycle();

  GameState.on('redraw', () => {
    $localStorage.saveStateCache = {
      spEarned: GameState.spEarned,
      kpEarned: GameState.kpEarned
    };
  });

  GameState.on('gameover', () => {
    getCurrencyFrom(GameState);

    $localStorage.saveStateCache = null;

    rebuildUpgrades();

    $scope.$apply();
  });

});<|endoftext|>