<commit_before>//Array of demand behavior
//[time slot, min production, max production, min deliveries, max deliveries]
//I might not need this if I put hte max and min in the For loop.
var demand = [
  //Hillsboro
  [['8am to 11am', 0, 4, 0, 4],
  ['11am to 2pm', 0, 7, 0, 4],
  ['2pm to 5pm', 2, 15, 1, 4],
  ['5pm to 8pm', 15, 35, 3, 8],
  ['8pm to 11pm', 12, 31, 5, 12],
  ['11pm to 2am', 5, 20, 5, 11]],
  //Pearl
  [['8am to 11am', 1, 7, 1, 3],
  ['11am to 2pm', 5, 9, 2, 8],
  ['2pm to 5pm', 2, 13, 1, 6],
  ['5pm to 8pm', 18, 32, 3, 9],
  ['8pm to 11pm', 5, 12, 1, 3],
  ['11pm to 2am', 8, 20, 6, 16]],
  //DowntownPDX
  [['8am to 11am', 0, 4, 0, 4],
  ['11am to 2pm', 0, 7, 0, 4],
  ['2pm to 5pm', 2, 15, 1, 4],
  ['5pm to 8pm', 10, 26, 4, 6],
  ['8pm to 11pm', 8, 22, 7, 15],
  ['11pm to 2am', 0, 8, 0, 2]],
  //Buckman
  [['8am to 11am', 0, 4, 0, 4],
  ['11am to 2pm', 0, 7, 0, 4],
  ['2pm to 5pm', 5, 15, 0, 4],
  ['5pm to 8pm', 25, 39, 13, 18],
  ['8pm to 11pm', 22, 36, 5, 22],
  ['11pm to 2am', 16, 31, 5, 21]],
  //PDXairport
  [['8am to 11am', 2, 7, 0, 0],
  ['11am to 2pm', 3, 9, 0, 0],
  ['2pm to 5pm', 1, 5, 0, 0],
  ['5pm to 8pm', 5, 13, 0, 0],
  ['8pm to 11pm', 22, 42, 0, 0],
  ['11pm to 2am', 15, 21, 0, 0]],
  //Clackamas
  [['8am to 11am', 0, 4, 0, 4],
  ['11am to 2pm', 0, 7, 0, 4],
  ['2pm to 5pm', 2, 15, 1, 4],
  ['5pm to 8pm', 6, 19, 5, 9],
  ['8pm to 11pm', 4, 8, 2, 5],
  ['11pm to 2am', 2, 5, 2, 4]],
];

var hours = [8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 0, 1];//pizzerias close at 2am.

//Array of pizza locations. in calculate2.js, these are the property of the Demand object.
var neighborhood = ['Hillsboro', 'Pearl', 'DowntownPDX', 'Buckman', 'PDXairport', 'Clackamas'];

//Function to return pizzas, deliveries, and drivers to be used for a specified range of hours
//Random generation of pizza's count, given min and max for that hour
function getValues(minPizza, maxPizza, minDelivery, maxDelivery){ //compare maxDelivery to calcPizza for ceiling
  var calcPizza = Math.floor(Math.random() * (maxPizza - minPizza + 1)) + minPizza;
  console.log('Randomly generated pizzas this hour: ' + calcPizza);

//Use pizza's calc as delivery's max for that specific hour. Delivery min is in the array.
  var calculateDelivery = function(){//start calculate Delivery function
    var calcDelivery;
    if(maxDelivery < calcPizza){
      calcDelivery = Math.floor(Math.random() * (1 + maxDelivery - minDelivery)) + minDelivery; //maxDelivery is the ceiling
    } else {
      calcDelivery = Math.floor(Math.random() * (1 + calcPizza - minDelivery)) + minDelivery;
    }//end else
    return calcDelivery;
  };//end anonymous function
  var calcDelivery = calculateDelivery();
  console.log('Randomly generated deliveries this hour: ' + calcDelivery);
//Use deliveries to determine the count of drivers. 1 driver has capacity of 3 deliveries.
  var calcDriver = Math.ceil(calcDelivery / 3);
  console.log('Count of drivers needed to make these deliveries: ' + calcDriver);
//Return an object with the count of pizzas, count of deliveries, and count of drivers.
//Later the next function will push countPizza, countDelivery, and countDriver to an array.
   var hourStats = {
    countPizza: calcPizza,
    countDelivery: calcDelivery,
    countDriver: calcDriver
   };
  return hourStats;
}

//function Pizzeria(pizzeria, pizzaTotal, deliveryTotal, driverTotal){
function storeValues(){
  //create empty array to store all the neighborhoods.
  var finalArray = [];

  //outer loop iterates through neighborhoods
  for(i = 0; i < neighborhood.length; i++){
    //create an object for each neighborhood pizzeria
    var pizzeria = {
      neighborhood: neighborhood[i],

    };//end define pizzeria object

    //create array to store the hourly production and driver needs within the neighborhood object
    var singleStats = [];
    //Daily totals: as you go through the loop, add the hourly pizza, deliveries, and drivers for each location.
    var pizzaTotal = 0;
    var deliveryTotal = 0;
    var driverTotal = 0;
    //inner loop generates the countPizza, countDelivery, and countDriver by iterating through 18 hours of operation.
    for(j = 0; j < hours.length; j++){
        //Look through six scenarios

        //8am to 10am
      if(j < 3){
        minPizza = demand[0][1];
        maxPizza = demand[0][2];
        minDelivery = demand[0][3];
        maxDelivery = demand[0][4];
        //11am to 1pm
      } else if (j < 6){
        minPizza = demand[1][1];
        maxPizza = demand[1][2];
        minDelivery = demand[1][3];
        maxDelivery = demand[1][4];
      //2pm to 4pm
      } else if (j < 9){
        minPizza = demand[2][1];
        maxPizza = demand[2][2];
        minDelivery = demand[2][3];
        maxDelivery = demand[2][4];
          //5pm to 7pm
      } else if (j < 12){
        minPizza = demand[3][1];
        maxPizza = demand[3][2];
        minDelivery = demand[3][3];
        maxDelivery = demand[3][4];
        //8pm to 10pm
      } else if (j < 15){
        minPizza = demand[4][1];
        maxPizza = demand[4][2];
        minDelivery = demand[4][3];
        maxDelivery = demand[4][4];
        //11pm to 2am
      } else {
        minPizza = demand[5][1];
        maxPizza = demand[5][2];
        minDelivery = demand[5][3];
        maxDelivery = demand[5][4];
      }//end else
      //create a dummy object to equal the output of getValues.
      var dummy = getValues(minPizza, maxPizza, minDelivery, maxDelivery);

      //each neighborhood will have an array of 18 sets (pizza, delivery, driver)
      //push to the array that stores the object for each hours value
      singleStats.push(dummy);
      //var pizzeriaValues = getValues(minPizza, maxPizza, minDelivery); //add property to pizzeria object.
        //the above line also runs getValues and returns an object

        //use dummy.key to add a value to pizzaTotal, deliveryTotal, and driverTotal.
      pizzaTotal += dummy.countPizza;
      deliveryTotal += dummy.countDelivery;
      driverTotal += dummy.countDriver;
      //hourStats[i].Values;////////////I don't think i need this?
        //update the value for the hourValues key (hourStats)

    }//end inner for loop
    //add properties to the object.
    //push all 18 values to the array
    pizzeria.Values = singleStats;
    //create another property for total pizzas
    pizzeria.totalPizza = pizzaTotal;
    //create another property for total deliveries
    pizzeria.totalDelivery = deliveryTotal;
    //create another property for total drivers
    pizzeria.totalDriver = driverTotal;
    //push the neighborhood object to an array
    finalArray.push(pizzeria);
  }//end outer for loop
  console.table(finalArray);
  return finalArray;
}; //end storeValues
//} //end object constructor function

//Function to assess weekly sales and operations.
//Function to prouduce a sum of six days' operation for all neighborhood locations.
function weeklyBusiness(){
  //Create empty arrays for each total you need. Put all the arrays into a parent array.
  var weeklyPizza = [];
  var weeklyDelivery = [];
  var weeklyDriver = [];

  var weeklyOperations = [];

  for (var ii = 0; ii < 6; ii++) {

//Function to push countPizza, countDelivery, and countDriver every hour, for each neighborhood
//run storeValues
  var finalArray = storeValues();

    weeklyPizza.push(finalArray[ii].totalPizza);
    weeklyDelivery.push(finalArray[ii].totalDelivery);
    weeklyDriver.push(finalArray[ii].totalDriver);
}//end for loop weeklyBusiness
    weeklyOperations.push(weeklyPizza, weeklyDelivery, weeklyDriver);
    console.log(weeklyOperations);
    return weeklyOperations;

}; //end weeklyBusiness
var a = weeklyBusiness(); //run the function ;D
console.log(a);
//Yet another function to loop through and add the six days together.
//Returns totalPizza, totalDelivery, and totalDriver for all locations.
function sumOperations(a){ //accepts a two-dimensional array.

  var operationSum = [];
  //loop through the three elements in weeklyOperations
  for (var ii = 0; ii < a.length; ii++) { //ii represents pizza, delivery, and driver
    var partialSum = 0;
    for (var k = 0; k < 6; k++) { //k represents one day of the operating week.
      partialSum += a[ii][k];
    }//end inner for
    operationSum.push(partialSum);

  }//end outer for
  return operationSum;
}//end sumOperations

var b = sumOperations(a);
console.log(b);
<commit_msg>Fixed table, took off multi neighborhood<commit_after>//Array of demand behavior
//[time slot, min production, max production, min deliveries, max deliveries]
//I might not need this if I put hte max and min in the For loop.
var demand = [
  //Hillsboro
  ['8am to 11am', 0, 4, 0, 4],
  ['11am to 2pm', 0, 7, 0, 4],
  ['2pm to 5pm', 2, 15, 1, 4],
  ['5pm to 8pm', 15, 35, 3, 8],
  ['8pm to 11pm', 12, 31, 5, 12],
  ['11pm to 2am', 5, 20, 5, 11]
];

var hours = [8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 0, 1];//pizzerias close at 2am.

//Array of pizza locations. in calculate2.js, these are the property of the Demand object.
var neighborhood = ['Hillsboro', 'Pearl', 'DowntownPDX', 'Buckman', 'PDXairport', 'Clackamas'];

//Function to return pizzas, deliveries, and drivers to be used for a specified range of hours
//Random generation of pizza's count, given min and max for that hour
function getValues(minPizza, maxPizza, minDelivery, maxDelivery){ //compare maxDelivery to calcPizza for ceiling
  var calcPizza = Math.floor(Math.random() * (maxPizza - minPizza + 1)) + minPizza;
  console.log('Randomly generated pizzas this hour: ' + calcPizza);

//Use pizza's calc as delivery's max for that specific hour. Delivery min is in the array.
  var calculateDelivery = function(){//start calculate Delivery function
    var calcDelivery;
    if(maxDelivery < calcPizza){
      calcDelivery = Math.floor(Math.random() * (1 + maxDelivery - minDelivery)) + minDelivery; //maxDelivery is the ceiling
    } else {
      calcDelivery = Math.floor(Math.random() * (1 + calcPizza - minDelivery)) + minDelivery;
    }//end else
    return calcDelivery;
  };//end anonymous function
  var calcDelivery = calculateDelivery();
  console.log('Randomly generated deliveries this hour: ' + calcDelivery);
//Use deliveries to determine the count of drivers. 1 driver has capacity of 3 deliveries.
  var calcDriver = Math.ceil(calcDelivery / 3);
  console.log('Count of drivers needed to make these deliveries: ' + calcDriver);
//Return an object with the count of pizzas, count of deliveries, and count of drivers.
//Later the next function will push countPizza, countDelivery, and countDriver to an array.
   var hourStats = {
    countPizza: calcPizza,
    countDelivery: calcDelivery,
    countDriver: calcDriver
   };
  return hourStats;
}

function Pizzeria(pizzeria, pizzaTotal, deliveryTotal, driverTotal){
function storeValues(){
  //create empty array to store all the neighborhoods.
  var finalArray = [];

  //outer loop iterates through neighborhoods
  for(i = 0; i < neighborhood.length; i++){
    //create an object for each neighborhood pizzeria
    var pizzeria = {
      neighborhood: neighborhood[i],

    };//end define pizzeria object

    //create array to store the hourly production and driver needs within the neighborhood object
    var singleStats = [];
    //Daily totals: as you go through the loop, add the hourly pizza, deliveries, and drivers for each location.
    var pizzaTotal = 0;
    var deliveryTotal = 0;
    var driverTotal = 0;
    //inner loop generates the countPizza, countDelivery, and countDriver by iterating through 18 hours of operation.
    for(j = 0; j < hours.length; j++){
        //Look through six scenarios

        //8am to 10am
      if(j < 3){
        minPizza = demand[0][1];
        maxPizza = demand[0][2];
        minDelivery = demand[0][3];
        maxDelivery = demand[0][4];
        //11am to 1pm
      } else if (j < 6){
        minPizza = demand[1][1];
        maxPizza = demand[1][2];
        minDelivery = demand[1][3];
        maxDelivery = demand[1][4];
      //2pm to 4pm
      } else if (j < 9){
        minPizza = demand[2][1];
        maxPizza = demand[2][2];
        minDelivery = demand[2][3];
        maxDelivery = demand[2][4];
          //5pm to 7pm
      } else if (j < 12){
        minPizza = demand[3][1];
        maxPizza = demand[3][2];
        minDelivery = demand[3][3];
        maxDelivery = demand[3][4];
        //8pm to 10pm
      } else if (j < 15){
        minPizza = demand[4][1];
        maxPizza = demand[4][2];
        minDelivery = demand[4][3];
        maxDelivery = demand[4][4];
        //11pm to 2am
      } else {
        minPizza = demand[5][1];
        maxPizza = demand[5][2];
        minDelivery = demand[5][3];
        maxDelivery = demand[5][4];
      }//end else
      //create a dummy object to equal the output of getValues.
      var dummy = getValues(minPizza, maxPizza, minDelivery, maxDelivery);

      //each neighborhood will have an array of 18 sets (pizza, delivery, driver)
      //push to the array that stores the object for each hours value
      singleStats.push(dummy);
      //var pizzeriaValues = getValues(minPizza, maxPizza, minDelivery); //add property to pizzeria object.
        //the above line also runs getValues and returns an object

        //use dummy.key to add a value to pizzaTotal, deliveryTotal, and driverTotal.
      pizzaTotal += dummy.countPizza;
      deliveryTotal += dummy.countDelivery;
      driverTotal += dummy.countDriver;
      //hourStats[i].Values;////////////I don't think i need this?
        //update the value for the hourValues key (hourStats)

    }//end inner for loop
    //add properties to the object.
    //push all 18 values to the array
    pizzeria.Values = singleStats;
    //create another property for total pizzas
    pizzeria.totalPizza = pizzaTotal;
    //create another property for total deliveries
    pizzeria.totalDelivery = deliveryTotal;
    //create another property for total drivers
    pizzeria.totalDriver = driverTotal;
    //push the neighborhood object to an array
    finalArray.push(pizzeria);
  }//end outer for loop
  console.table(finalArray);
  return finalArray;
}; //end storeValues
//} //end object constructor function

//Function to assess weekly sales and operations.
//Function to prouduce a sum of six days' operation for all neighborhood locations.
function weeklyBusiness(){
  //Create empty arrays for each total you need. Put all the arrays into a parent array.
  var weeklyPizza = [];
  var weeklyDelivery = [];
  var weeklyDriver = [];

  var weeklyOperations = [];

  for (var ii = 0; ii < 6; ii++) {

//Function to push countPizza, countDelivery, and countDriver every hour, for each neighborhood
//run storeValues
  var finalArray = storeValues();

    weeklyPizza.push(finalArray[ii].totalPizza);
    weeklyDelivery.push(finalArray[ii].totalDelivery);
    weeklyDriver.push(finalArray[ii].totalDriver);
}//end for loop weeklyBusiness
    weeklyOperations.push(weeklyPizza, weeklyDelivery, weeklyDriver);
    console.log(weeklyOperations);
    return weeklyOperations;

}; //end weeklyBusiness
var a = weeklyBusiness(); //run the function ;D
console.log(a);
//Yet another function to loop through and add the six days together.
//Returns totalPizza, totalDelivery, and totalDriver for all locations.
function sumOperations(a){ //accepts a two-dimensional array.

  var operationSum = [];
  //loop through the three elements in weeklyOperations
  for (var ii = 0; ii < a.length; ii++) { //ii represents pizza, delivery, and driver
    var partialSum = 0;
    for (var k = 0; k < 6; k++) { //k represents one day of the operating week.
      partialSum += a[ii][k];
    }//end inner for
    operationSum.push(partialSum);

  }//end outer for
  return operationSum;
}//end sumOperations

var b = sumOperations(a);
console.log(b);
<|endoftext|>


<commit_before>blippex.define('blippex.p2p.manager', function(oArgs){
	var _id = oArgs.id;
	var _peers = oArgs.peers;
	var _callback = oArgs.callback;

	var _this = this;

	var peer = null;

	/*
	 *	Create new peer
	 *	Remote peers should be notified automatically, also it works correctly even in case browser is closing, etc
	 *	@author blippex
	 *	@private
	 */
	var init = function(){
		blippex.browser.debug.log('p2p: creating new peer ' + _id);

		peer = new Peer(_id, blippex.config.p2p);

		/* create peer */
		peer.on('open', function(__id){
			/* try to connect to known peers, keep connections internally */
			_peers.forEach(function(peer_id){
				//
				var conn = peer.connect(peer_id, {
					'reliable': true
				});

				conn.on('data', function(data){
					blippex.browser.debug.log('p2p: got data');
					_this.forward(data);
				});

				conn.on('close', function(data){
					blippex.browser.debug.log('p2p: remote peer closed connection, active connections: ' + getPeersCount());
					//TODO check how many active connections we have, recreate the manager?
				});

			});
		});

		peer.on('connection' ,function(conn, meta){
			blippex.browser.debug.log('p2p: new peer connected, active connections: ' + getPeersCount());

			conn.on('data', function(data){
				blippex.browser.debug.log('p2p: got data');
				_this.forward(data);
			});

			//TODO conn.on

		});

		peer.on('error' ,function(err){
			//TODO smth
		});
	}

	/*
	 *	Forward the data
	 *	Forward data or upload
	 *	@public
	 *	@author blippex
	 */
	this.forward = function(data){
		var conn = _this.getConnection();
		if (conn && shouldForward()){
			blippex.browser.debug.log('p2p: forwarding data to peer');
			conn.send(data);
		} else {
			blippex.browser.debug.log('p2p: uploading the data to api server');
			blippex.api.upload.upload(data);
		}
	}

	/*
	 *	Get random connection
	 *	Forward possible to active connection only
	 *	@type Peer
	 *	@returns {Peer} returns remote connection object, some of known peers
	 *	@author blippex
	 *	@public
	 */
	this.getConnection = function(){
		var cons = [];
		for (var _peer_id in peer.connections){
			if (peer.connections[_peer_id] && peer.connections[_peer_id][0].open == true && _peer_id !== peer.id) {//skip itself
				cons.push(_peer_id);
			}
		}
		return cons.length ? peer.connections[cons[Math.floor(Math.random() * cons.length)]][0] : null;
	},

	/*
	 *	Get current peer
	 *	@type Peer
	 *	@returns {Peer} return active peer
	 *	@author blippex
	 *	@public
	 */
	this.getPeer = function(){
		return peer;
	}

	/*
	 *	Destroy current peer, close all active connections
	 *	Remote peers should be notified automatically, also it works correctly even in case browser is closing, etc
	 *	@author blippex
	 *	@public
	 */
	this.destroy = function(){
		blippex.browser.debug.log('p2p: destroying active peer ' + peer.id);
		for (var peerId in peer.connections){
			peer.connections[peerId][0].close();
		}
		//peer.disconnect();
		peer.destroy();
	}

	/*
	 *	Get forward flag
	 *	Forward the data if flag is true
	 *	@author blippex
	 *	@type Boolean
	 *	@private
	 *	@returns {Boolean} returns whether data should be forwarded or uploaded instead
	 */
	var shouldForward = function(){
		return Math.round(Math.random()) == 1;
	}

	var getPeersCount = function(){
		return Object.keys(peer.connections || {}).length;
	}

	/*
	 *	Make it work()
	 *	Start the manager, call the costructor
	 *	@public
	 */
	init();
});<commit_msg>Active connections detection fix<commit_after>blippex.define('blippex.p2p.manager', function(oArgs){
	var _id = oArgs.id;
	var _peers = oArgs.peers;
	var _callback = oArgs.callback;

	var _this = this;

	var peer = null;

	/*
	 *	Create new peer
	 *	Remote peers should be notified automatically, also it works correctly even in case browser is closing, etc
	 *	@author blippex
	 *	@private
	 */
	var init = function(){
		blippex.browser.debug.log('p2p: creating new peer ' + _id);

		peer = new Peer(_id, blippex.config.p2p);

		/* create peer */
		peer.on('open', function(__id){
			/* try to connect to known peers, keep connections internally */
			_peers.forEach(function(peer_id){
				//
				var conn = peer.connect(peer_id, {
					'reliable': true
				});

				conn.on('data', function(data){
					blippex.browser.debug.log('p2p: got data');
					_this.forward(data);
				});

				conn.on('close', function(data){
					blippex.browser.debug.log('p2p: remote peer closed connection, active connections: ' + getPeersCount());
					//TODO check how many active connections we have, recreate the manager?
				});

			});
		});

		peer.on('connection' ,function(conn, meta){
			blippex.browser.debug.log('p2p: new peer connected, active connections: ' + getPeersCount());

			conn.on('data', function(data){
				blippex.browser.debug.log('p2p: got data');
				_this.forward(data);
			});

			//TODO conn.on

		});

		peer.on('error' ,function(err){
			//TODO smth
		});
	}

	/*
	 *	Forward the data
	 *	Forward data or upload
	 *	@public
	 *	@author blippex
	 */
	this.forward = function(data){
		var conn = _this.getConnection();
		if (conn && shouldForward()){
			blippex.browser.debug.log('p2p: forwarding data to peer');
			conn.send(data);
		} else {
			blippex.browser.debug.log('p2p: uploading the data to api server');
			blippex.api.upload.upload(data);
		}
	}

	/*
	 *	Get random connection
	 *	Forward possible to active connection only
	 *	@type Peer
	 *	@returns {Peer} returns remote connection object, some of known peers
	 *	@author blippex
	 *	@public
	 */
	this.getConnection = function(){
		var cons = [];
		for (var _peer_id in peer.connections){
			if ((peer.connections[_peer_id] || []).length && peer.connections[_peer_id][0].open == true && _peer_id !== peer.id) {//skip itself
				cons.push(_peer_id);
			}
		}
		return cons.length ? peer.connections[cons[Math.floor(Math.random() * cons.length)]][0] : null;
	},

	/*
	 *	Get current peer
	 *	@type Peer
	 *	@returns {Peer} return active peer
	 *	@author blippex
	 *	@public
	 */
	this.getPeer = function(){
		return peer;
	}

	/*
	 *	Destroy current peer, close all active connections
	 *	Remote peers should be notified automatically, also it works correctly even in case browser is closing, etc
	 *	@author blippex
	 *	@public
	 */
	this.destroy = function(){
		blippex.browser.debug.log('p2p: destroying active peer ' + peer.id);
		for (var peerId in peer.connections){
			(peer.connections[_peer_id] || []).length && peer.connections[peerId][0].close();
		}
		//peer.disconnect();
		peer.destroy();
	}

	/*
	 *	Get forward flag
	 *	Forward the data if flag is true
	 *	@author blippex
	 *	@type Boolean
	 *	@private
	 *	@returns {Boolean} returns whether data should be forwarded or uploaded instead
	 */
	var shouldForward = function(){
		return Math.round(Math.random()) == 1;
	}

	var getPeersCount = function(){
		return Object.keys(peer.connections || {}).length;
	}

	/*
	 *	Make it work()
	 *	Start the manager, call the costructor
	 *	@public
	 */
	init();
});<|endoftext|>


<commit_before><commit_msg>Updated reports, updated spreadsheets and added and updated graphics<commit_after><|endoftext|>


<commit_before>/*
 @author Zakai Hamilton
 @component CoreService
 */

package.core.service = function CoreService(me) {
    me.init = function () {
        if (me.platform === "server") {
            me.core.util.config(config => {
                if (config.settings && config.settings.service && config.settings.service.port) {
                    me.io = require("socket.io");
                    me.core.console.log("listening for services on port: " + config.settings.service.port);
                    me.server = me.io.listen(config.settings.service.port);
                    me.clients = new Map();
                    me.server.on("connection", (socket) => {
                        me.core.console.log(`Service connected [id=${socket.id}]`);
                        var ref = me.core.ref.gen();
                        socket.on("disconnect", () => {
                            var info = me.clients.get(socket);
                            if (info) {
                                me.clients.delete(socket);
                                me.core.console.log(`Service disconnected [id=${socket.id} name=${info.name} ref=${info.ref}]`);
                            }
                        });
                        socket.on("method", (info) => {
                            me.core.message.handleLocal((response) => {
                                socket.emit("method", response);
                            }, info);
                        });
                        me.core.console.log("Service setup request for ref: " + ref);
                        me.core.message.send_service.call(socket, "core.service.setup", (name, ref) => {
                            me.core.console.log("Service setup complete for service: " + name + " ref: " + ref);
                            me.clients.set(socket, {ref: ref, name: name});
                        }, ref);
                    });
                }
            });
        } else if (me.platform === "service") {
            me.io = require("socket.io-client");
            if (process.argv.length <= 3) {
                console.log("params: service_name http://ip:port");
                process.exit(-1);
            }
            me.serverAddress = process.argv[2];
            me.serviceNames = process.argv.splice(3);
            me.client = me.io.connect(me.serverAddress);
            me.client.on("method", (info) => {
                me.core.message.handleLocal((response) => {
                    me.client.emit("method", response);
                }, info);
            });
        }
    };
    me.list = function (callback) {
        var items = [];
        if (me.clients) {
            me.clients.forEach((value, key) => {
                items.push(value);
            });
        }
        callback(items);
    };
    me.setup = function (callback, ref) {
        me.lock((task) => {
            me.serviceNames.map((serviceName) => {
                me.lock(task, (task) => {
                    me.core.console.log("loading service: " + serviceName + "...");
                    me.include("service." + serviceName, function (info) {
                        if (info.complete) {
                            me.core.console.log("service loaded: " + serviceName);
                            me.core.console.log("setup service: " + serviceName + "...");
                            me.core.message.send("service." + serviceName + ".setup", () => {
                                me.core.console.log("setup service: " + serviceName + " complete");
                                me.unlock(task);
                            }, ref);
                        }
                    });
                });
            });
            me.unlock(task, () => {
                callback(me.serviceNames, ref);
            });
        });
    };
    me.config = function (callback, name) {
        me.core.util.config(config => {
            var response = null;
            if (config && config.settings) {
                var services = config.settings.service;
                if (services) {
                    if (services) {
                        var service = services[name];
                        if (service) {
                            response = service;
                        }
                    }
                }
            }
            callback(response);
        });
    };
    me.sendAll = function (path, callback, params) {
        me.clients.forEach((info, socket) => {
            var args = Array.prototype.slice.call(arguments);
            me.core.message.send_service.apply(socket, args);
        });
    };
};
<commit_msg>bug fix<commit_after>/*
 @author Zakai Hamilton
 @component CoreService
 */

package.core.service = function CoreService(me) {
    me.init = function () {
        if (me.platform === "server") {
            me.core.util.config(config => {
                if (config.settings && config.settings.service && config.settings.service.port) {
                    me.io = require("socket.io");
                    me.core.console.log("listening for services on port: " + config.settings.service.port);
                    me.server = me.io.listen(config.settings.service.port);
                    me.clients = new Map();
                    me.server.on("connection", (socket) => {
                        me.core.console.log(`Service connected [id=${socket.id}]`);
                        var ref = me.core.ref.gen();
                        socket.on("disconnect", () => {
                            var info = me.clients.get(socket);
                            if (info) {
                                me.clients.delete(socket);
                                me.core.console.log(`Service disconnected [id=${socket.id} name=${info.name} ref=${info.ref}]`);
                            }
                        });
                        socket.on("method", (info) => {
                            me.core.message.handleLocal((response) => {
                                socket.emit("method", response);
                            }, info);
                        });
                        me.core.console.log("Service setup request for ref: " + ref);
                        me.core.message.send_service.call(socket, "core.service.setup", (name, ref) => {
                            me.core.console.log("Service setup complete for service: " + name + " ref: " + ref);
                            me.clients.set(socket, {ref: ref, name: name});
                        }, ref);
                    });
                }
            });
        } else if (me.platform === "service") {
            me.io = require("socket.io-client");
            if (process.argv.length <= 3) {
                console.log("params: service_name http://ip:port");
                process.exit(-1);
            }
            me.serverAddress = process.argv[2];
            me.serviceNames = process.argv.splice(3);
            me.client = me.io.connect(me.serverAddress);
            me.client.on("method", (info) => {
                me.core.message.handleLocal((response) => {
                    me.client.emit("method", response);
                }, info);
            });
        }
    };
    me.list = function (callback) {
        var items = [];
        if (me.clients) {
            me.clients.forEach((value, key) => {
                items.push(value);
            });
        }
        callback(items);
    };
    me.setup = function (callback, ref) {
        me.lock((task) => {
            me.serviceNames.map((serviceName) => {
                me.lock(task, (task) => {
                    me.core.console.log("loading service: " + serviceName + "...");
                    me.include("service." + serviceName, function (info) {
                        if (info.complete) {
                            me.core.console.log("service loaded: " + serviceName);
                            me.core.console.log("setup service: " + serviceName + "...");
                            me.core.message.send("service." + serviceName + ".setup", () => {
                                me.core.console.log("setup service: " + serviceName + " complete");
                                me.unlock(task);
                            }, ref);
                        }
                    });
                });
            });
            me.unlock(task, () => {
                callback(me.serviceNames, ref);
            });
        });
    };
    me.config = function (callback, name) {
        me.core.util.config(config => {
            var response = null;
            if (config && config.settings) {
                var services = config.settings.service;
                if (services) {
                    if (services) {
                        var service = services[name];
                        if (service) {
                            response = service;
                        }
                    }
                }
            }
            callback(response);
        });
    };
    me.sendAll = function (path, callback, params) {
        var args = Array.prototype.slice.call(arguments);
        me.clients.forEach((info, socket) => {
            me.core.message.send_service.apply(socket, args);
        });
    };
};
<|endoftext|>


<commit_before>/**
 * 在[[Special:Newpages]]中讓沒有patrolmarks權限的用戶也能標記巡查過的編輯
 */

(function() {

	mw.loader.using(['mediawiki.api']).done(function() {

		let api = new mw.Api();
		let is_autoreviewer = {};

		let users = [];
		$('#mw-content-text>ul>li').each((i, e) => {
			let creator = $(e).find('a.mw-userlink').text();
			if (!mw.util.isIPAddress(creator)) {
				users.push(creator);
			} else {
				is_autoreviewer[creator] = false;
			}
		});
		api.get({
			'action': 'query',
			'format': 'json',
			'list': 'users',
			'usprop': 'rights',
			'ususers': users.join('|')
		}).then(data => {
			data.query.users.forEach(user => {
				is_autoreviewer[user.name] = (user.rights.indexOf('autopatrol') !== -1);
			});
		}).then(() => {
			$('#mw-content-text>ul>li').each((i, e) => {
				let pagename = $(e).find('>a:nth-child(2)').text();
				let oldpagename = $(e).find('.mw-newpages-oldtitle').text();
				if (oldpagename) {
					pagename = oldpagename.replace(/最初建立的名稱為 '(.+)'/, '$1').replace(/最初创建为“(.+?)”/, '$1');
				}
				let creator = $(e).find('a.mw-userlink').text();
				let oldid = mw.util.getParamValue('oldid', $(e).find('>a:first()').attr('href'));
				api.get({
					'action': 'query',
					'format': 'json',
					'list': 'logevents',
					'letype': 'patrol',
					'letitle': pagename
				}).then(data => {
					if (data.query.logevents.length == 0) {
						if (!is_autoreviewer[creator]) {
							e.classList.add('not-patrolled');
						}
					} else {
						if (data.query.logevents[0].params.curid != oldid) {
							e.classList.add('not-patrolled');
						}
					}
				});
			});
		})

	});

})();
<commit_msg>patrolmarks: Fix oldpagename pattern<commit_after>/**
 * 在[[Special:Newpages]]中讓沒有patrolmarks權限的用戶也能標記巡查過的編輯
 */

(function() {

	mw.loader.using(['mediawiki.api']).done(function() {

		let api = new mw.Api();
		let is_autoreviewer = {};

		let users = [];
		$('#mw-content-text>ul>li').each((i, e) => {
			let creator = $(e).find('a.mw-userlink').text();
			if (!mw.util.isIPAddress(creator)) {
				users.push(creator);
			} else {
				is_autoreviewer[creator] = false;
			}
		});
		api.get({
			'action': 'query',
			'format': 'json',
			'list': 'users',
			'usprop': 'rights',
			'ususers': users.join('|')
		}).then(data => {
			data.query.users.forEach(user => {
				is_autoreviewer[user.name] = (user.rights.indexOf('autopatrol') !== -1);
			});
		}).then(() => {
			$('#mw-content-text>ul>li').each((i, e) => {
				let pagename = $(e).find('>a:nth-child(2)').text();
				let oldpagename = $(e).find('.mw-newpages-oldtitle').text();
				if (oldpagename) {
					pagename = oldpagename.replace(/最初建立的名稱為 "(.+?)"/, '$1').replace(/最初创建为“(.+?)”/, '$1');
				}
				let creator = $(e).find('a.mw-userlink').text();
				let oldid = mw.util.getParamValue('oldid', $(e).find('>a:first()').attr('href'));
				api.get({
					'action': 'query',
					'format': 'json',
					'list': 'logevents',
					'letype': 'patrol',
					'letitle': pagename
				}).then(data => {
					if (data.query.logevents.length == 0) {
						if (!is_autoreviewer[creator]) {
							e.classList.add('not-patrolled');
						}
					} else {
						if (data.query.logevents[0].params.curid != oldid) {
							e.classList.add('not-patrolled');
						}
					}
				});
			});
		})

	});

})();
<|endoftext|>


<commit_before>import Vuex from 'vuex'
import axios from 'axios'
import VueAxios from 'vue-axios'
import HalJsonVuex from 'hal-json-vuex'
import lang from './lang'

class StorePlugin {
  install (Vue, options) {
    Vue.use(Vuex)

    store = new Vuex.Store({
      modules: {
        lang
      },
      strict: process.env.NODE_ENV !== 'production'
    })

    axios.defaults.withCredentials = true
    axios.defaults.baseURL = window.environment.API_ROOT_URL
    Vue.use(VueAxios, axios)

    apiStore = HalJsonVuex(store, axios, { forceRequestedSelfLink: true })
    Vue.use(apiStore)
  }
}

export let apiStore
export let store

export default new StorePlugin()
<commit_msg>frontend: fix vite build<commit_after>import Vuex from 'vuex'
import axios from 'axios'
import VueAxios from 'vue-axios'
import HalJsonVuex from 'hal-json-vuex'
import lang from './lang'

class StorePlugin {
  install (Vue, options) {
    Vue.use(Vuex)

    store = new Vuex.Store({
      modules: {
        lang
      },
      strict: process.env.NODE_ENV !== 'production'
    })

    axios.defaults.withCredentials = true
    axios.defaults.baseURL = window.environment.API_ROOT_URL
    Vue.use(VueAxios, axios)

    let halJsonVuex = HalJsonVuex
    if (typeof halJsonVuex !== 'function') {
      halJsonVuex = HalJsonVuex.default
    }
    apiStore = halJsonVuex(store, axios, { forceRequestedSelfLink: true })
    Vue.use(apiStore)
  }
}

export let apiStore
export let store

export default new StorePlugin()
<|endoftext|>


<commit_before>angular.module('citizen-engagement.mapCtrl', [])

    .factory('GeolocServiceFla', function (geolocation) {
        var service = {

            locateUser: function () {

                return geolocation.getLocation().then(function (data) {

                    /*$scope.mapCenter.lat = data.coords.latitude;
                     $scope.mapCenter.lng = data.coords.longitude;
                     $scope.mapCenter.zoom = 8;*/

                    /*  $scope.mapCenter = {
                     lat: data.coords.latitude,
                     lng: data.coords.longitude,
                     zoom: 8

                     };*/
                    console.log(data.coords.longitude);
                    console.log(data.coords.latitude);
                    return data.coords;

                }, function (error) {
                    $log.error("Could not get location: " + error);
                    console.log("Could not get location: " + error);
                });

            }
        };

        return service;
    })
    .controller("IssuesMapController", function ($scope, mapboxMapId, mapboxAccessToken, $http, apiUrl, GeolocServiceFla) {
        var mapboxTileLayer = "http://api.tiles.mapbox.com/v4/" + mapboxMapId; //openlayers --> mapbox
        mapboxTileLayer = mapboxTileLayer + "/{z}/{x}/{y}.png?access_token=" + mapboxAccessToken;
        $scope.mapDefaults = {
            tileLayer: mapboxTileLayer
        };

        $scope.mapCenter = {};
// TODO loading while geoloc

        GeolocServiceFla.locateUser().then(function (coords) {
            $scope.mapCenter = {
                lat: coords.latitude,
                lng: coords.longitude,
                zoom: 15

            };
        });

        $scope.mapMarkers = [];

        $http.get(apiUrl + '/issues').success(function (issues) {

            $scope.issues = issues;

            console.log(issues);

            GeolocServiceFla.locateUser().then(function (coords) {
                $scope.mapMarkers.push({

                    lat: coords.latitude,
                    lng: coords.longitude,
                    message: '
{{ issue.description }}
',
                    getMessageScope: function () {
                        var scope = $scope.$new();
                        /*    scope.issue = issue;*/
                        return scope;
                    }
                });
            });

        });

    })

    .controller("IssueMapController", function ($scope, mapboxMapId, mapboxAccessToken, $http, apiUrl, GeolocServiceFla,  $stateParams) {
        var mapboxTileLayer = "http://api.tiles.mapbox.com/v4/" + mapboxMapId; //openlayers --> mapbox
        mapboxTileLayer = mapboxTileLayer + "/{z}/{x}/{y}.png?access_token=" + mapboxAccessToken;
        $scope.mapDefaults = {
            tileLayer: mapboxTileLayer
        };
        $scope.mapCenter = {};

        GeolocServiceFla.locateUser().then(function (coords) {

        });

        $scope.mapMarkers = [];
        /*        var issue = IssueService.getIssue();*/

        var id = $stateParams.issueId;
        $http({
            method: 'GET',
            url: apiUrl + '/issues/'+id
        }).success(function(issue) {

            $scope.mapCenter = {
                lat: issue.lat,
                lng: issue.lng,
                zoom: 15

            };
            $scope.issue = issue;
            console.log(issue);
            $scope.mapMarkers.push({
                lat: issue.lat,
                lng: issue.lng,

            });

        });

       /* $http.get(apiUrl + '/issues').success(function (issues) {

            $scope.issues = issues;

            //console.log(issues);

            $scope.mapMarkers.push({
                lat: 46.5,
                lng: 6.6,
                message: '
{{ issue.description }}
',
                getMessageScope: function () {
                    var scope = $scope.$new();
                    /!*    scope.issue = issue;*!/
                    return scope;
                }
            });
        });*/

    });

<commit_msg>map controller<commit_after>angular.module('citizen-engagement.mapCtrl', [])

    .factory('GeolocServiceFla', function (geolocation) {
        var service = {

            locateUser: function () {

                return geolocation.getLocation().then(function (data) {

                    /*$scope.mapCenter.lat = data.coords.latitude;
                     $scope.mapCenter.lng = data.coords.longitude;
                     $scope.mapCenter.zoom = 8;*/

                    /*  $scope.mapCenter = {
                     lat: data.coords.latitude,
                     lng: data.coords.longitude,
                     zoom: 8

                     };*/
                    console.log(data.coords.longitude);
                    console.log(data.coords.latitude);
                    return data.coords;

                }, function (error) {
                    $log.error("Could not get location: " + error);
                    console.log("Could not get location: " + error);
                });

            }
        };

        return service;
    })
    .controller("IssuesMapController", function ($scope, mapboxMapId, mapboxAccessToken, $http, apiUrl, GeolocServiceFla) {
        var mapboxTileLayer = "http://api.tiles.mapbox.com/v4/" + mapboxMapId; //openlayers --> mapbox
        mapboxTileLayer = mapboxTileLayer + "/{z}/{x}/{y}.png?access_token=" + mapboxAccessToken;
        $scope.mapDefaults = {
            tileLayer: mapboxTileLayer
        };

        $scope.mapCenter = {};
// TODO loading while geoloc

        GeolocServiceFla.locateUser().then(function (coords) {
            $scope.mapCenter = {
                lat: coords.latitude,
                lng: coords.longitude,
                zoom: 15

            };
        });

        $scope.mapMarkers = [];

        $http.get(apiUrl + '/issues').success(function (issues) {

            $scope.issues = issues;

            console.log(issues);

            GeolocServiceFla.locateUser().then(function (coords) {
                $scope.mapMarkers.push({

                    lat: coords.latitude,
                    lng: coords.longitude,
                    message: '
{{ issue.description }}
',
                    getMessageScope: function () {
                        var scope = $scope.$new();
                        /*    scope.issue = issue;*/
                        return scope;
                    }
                });
            });

        });

    })

    .controller("IssueMapController", function ($scope, mapboxMapId, mapboxAccessToken, $http, apiUrl, GeolocServiceFla,  $stateParams) {
        var mapboxTileLayer = "http://api.tiles.mapbox.com/v4/" + mapboxMapId; //openlayers --> mapbox
        mapboxTileLayer = mapboxTileLayer + "/{z}/{x}/{y}.png?access_token=" + mapboxAccessToken;
        $scope.mapDefaults = {
            tileLayer: mapboxTileLayer
        };
        $scope.mapCenter = {};

        GeolocServiceFla.locateUser().then(function (coords) {

        });

        $scope.mapMarkers = [];
        /*        var issue = IssueService.getIssue();*/

        var id = $stateParams.issueId;
        $http({
            method: 'GET',
            url: apiUrl + '/issues/'+id
        }).success(function(issue) {

            $scope.mapCenter = {
                lat: issue.lat,
                lng: issue.lng,
                zoom: 15

            };
            $scope.issue = issue;
            console.log(issue);
            $scope.mapMarkers.push({
                lat: issue.lat,
                lng: issue.lng,

            });

        });

       /* $http.get(apiUrl + '/issues').success(function (issues) {

            $scope.issues = issues;

            //console.log(issues);

            $scope.mapMarkers.push({
                lat: 46.5,
                lng: 6.6,
                message: '
{{ issue.description }}
',
                getMessageScope: function () {
                    var scope = $scope.$new();
                    /!*    scope.issue = issue;*!/
                    return scope;
                }
            });
        });*/

    });

<|endoftext|>


<commit_before><commit_msg>Speed up joinDigits by avoiding an extra copy<commit_after><|endoftext|>


<commit_before>//META{"name":"BetterReplyer"}*//

var BetterReplyer = function () {};

BetterReplyer.prototype.getName = function() {
    return "BetterReplyer";
};

BetterReplyer.prototype.getDescription = function() {
    return "Reply to people with a button; Original by Hammock & Natsulus";
};

BetterReplyer.prototype.getVersion = function() {
    return "1.0";
};

BetterReplyer.prototype.getAuthor = function() {
    return "Zerthox";
};

BetterReplyer.prototype.start = function() {
	$(document).on("mouseover.brpr", function(e) {
		var target = $(e.target);
		if (target.parents(".message").length > 0) {
			var isCompact = false;
			var allmessages = $('.messages .message-group');
			var nameDateBlock = $('.messages .message-group .comment .message .body h2');
			var replyBtn = 'Reply';
			allmessages.on('mouseover',function() {
				if (nameDateBlock.find('.replyer').length == 0) {
					$(this).find(nameDateBlock).append(replyBtn);
					$(this).find('.replyer').click(function() {
						$('.content .channel-textarea textarea').val('<@'+$(this).parents(".message-group").find(".avatar-large").attr("style").split("/")[4]+'> '+$('.content .channel-textarea textarea').val()).focus();
					});
				}
			});
			allmessages.on('mouseleave',function() {
				if (nameDateBlock.find('.replyer').length == 1) {
					$(this).find('.replyer').empty().remove();
				}
			});
		}
	});
	console.log('[BetterReplyer] Started');
};

BetterReplyer.prototype.load = function() {};

BetterReplyer.prototype.unload = function() {
	$(document).off("mouseover.brpr");
	$('.messages .message-group').off('mouseover');
	$('.messages .message-group').off('mouseleave');
};

BetterReplyer.prototype.stop = function() {
	$(document).off("mouseover.brpr");
	$('.messages .message-group').off('mouseover');
	$('.messages .message-group').off('mouseleave');
};

BetterReplyer.prototype.getSettingsPanel = function() {
	return null;
};

BetterReplyer.prototype.onMessage = function() {};

BetterReplyer.prototype.onSwitch = function() {};
<commit_msg>Update BetterReplyer.plugin.js<commit_after>//META{"name":"BetterReplyer"}*//

var BetterReplyer = function () {};

BetterReplyer.prototype.getName = function() {
    return "BetterReplyer";
};

BetterReplyer.prototype.getDescription = function() {
    return "Reply to people using their ID with a buttonBy @Zerthox";
};

BetterReplyer.prototype.getVersion = function() {
    return "1.0";
};

BetterReplyer.prototype.getAuthor = function() {
    return "Zerthox";
};

BetterReplyer.prototype.start = function() {
	$(document).on("mouseover.brpr", function(e) {
		var target = $(e.target);
		if (target.parents(".message").length > 0) {
			var isCompact = false;
			var allmessages = $('.messages .message-group');
			var nameDateBlock = $('.messages .message-group .comment .message .body h2');
			var replyBtn = 'Reply';
			allmessages.on('mouseover',function() {
				if (nameDateBlock.find('.replyer').length == 0) {
					$(this).find(nameDateBlock).append(replyBtn);
					$(this).find('.replyer').click(function() {
						$('.content .channel-textarea textarea').val('<@'+$(this).parents(".message-group").find(".avatar-large").attr("style").split("/")[4]+'> '+$('.content .channel-textarea textarea').val()).focus();
					});
				}
			});
			allmessages.on('mouseleave',function() {
				if (nameDateBlock.find('.replyer').length == 1) {
					$(this).find('.replyer').empty().remove();
				}
			});
		}
	});
	console.log('[BetterReplyer] Started');
};

BetterReplyer.prototype.load = function() {};

BetterReplyer.prototype.unload = function() {
	$(document).off("mouseover.brpr");
	$('.messages .message-group').off('mouseover');
	$('.messages .message-group').off('mouseleave');
};

BetterReplyer.prototype.stop = function() {
	$(document).off("mouseover.brpr");
	$('.messages .message-group').off('mouseover');
	$('.messages .message-group').off('mouseleave');
};

BetterReplyer.prototype.getSettingsPanel = function() {
	return null;
};

BetterReplyer.prototype.onMessage = function() {};

BetterReplyer.prototype.onSwitch = function() {};
<|endoftext|>


<commit_before>var app = angular.module('app', ['angularFileUpload', 'ngAnimate', 'ngSanitize']);

app.run(function ($rootScope) {
    if (baseURL) {
        $rootScope.baseURL = baseURL;
    }

    else {
        $rootScope.baseURL = 'http://sailr.co';
    }

    $rootScope.loggedInUser = loggedInUser;
});

app.directive('contenteditable', function () {
    return {
        restrict: 'A', // only activate on element attribute
        require: '?ngModel', // get a hold of NgModelController
        link: function (scope, element, attrs, ngModel) {
            if (!ngModel) return; // do nothing if no ng-model

            // Specify how UI should be updated
            ngModel.$render = function () {
                element.html(ngModel.$viewValue);
            };

            // Listen for change events to enable binding
            element.on('blur keyup change input', function () {
                scope.$apply(read);
            });
            read(); // initialize

            // Write data to the model
            function read() {
                var html = element.html();
                // When we clear the content editable the browser leaves a
 behind
                // If strip-br attribute is provided then we strip this out
                if (attrs.stripBr && html == '
') {
                    html = '';
                }
                ngModel.$setViewValue(html);
            }
        }
    };
});

app.directive('num-binding', function () {
    return {
        restrict: 'A',
        require: 'ngModel',
        scope: {
            model: '=ngModel',
        },
        link: function (scope, element, attrs, ngModelCtrl) {
            if (scope.model && typeof scope.model == 'string') {
                scope.model = parseInt(scope.model);
            }
        }
    };
});

app.directive('focusMe', function($timeout, $parse) {
    return {
        //scope: true,   // optionally create a child scope
        link: function(scope, element, attrs) {
            var model = $parse(attrs.focusMe);
            scope.$watch(model, function(value) {
                console.log('value=',value);
                if(value === true) {
                    $timeout(function() {
                        element[0].focus();
                    });
                }
            });
            /*
            // to address @blesh's comment, set attribute value to 'false'
            // on blur event:
            element.bind('blur', function() {
                console.log('blur');
                scope.$apply(model.assign(scope, false));
            });
            */
        }
    };
});

app.directive('sailrFooter', ['$document', function($document) {
    function link(scope, element, attrs) {
        var hasVScroll = document.body.scrollHeight > document.body.clientHeight;

        if (!hasVScroll) {
            element.css({
                position: 'relative'
            });
        }

        else {
            element.css({
                position: 'absolute'
            });
        }

    }

    return {
        link: link
    }
}]);

app.factory('StripeFactory', function($q, $rootScope) {
   var service = {};
app.directive('sailrOpactiy', function () {

    function link(scope, iElement, iAttrs) {

        scope.sailrOpacity = iAttrs.sailrOpacity;
        iElement.css({
            opacity: scope.sailrOpacity
        });
    }

    service.sayHello = function() {
    return {
        restrict: 'A',
        scope: {
            sailrOpacity: '@'
        },
        link: link
    }
});
        return 'HELLO FROM STRIPE FACTORY';
    };

    service.setPublishableKey = function(key) {
        Stripe.setPublishableKey(key);
    };

    service.createToken = function(cardData) {
        var defered = $q.defer();

        Stripe.card.createToken(cardData, function(status, response) {

            if (response.error) {
                service.errors = response.error;
                defered.reject(response);

            }

            else {
                service.token = response;
                defered.resolve(response);

            }
        });

        return defered.promise;
    };

    service.getToken = function() {
        return service.token;
    };

    service.getErrors = function() {
        return service.errors;
    };

    return service;
});

app.factory('HelperFactory', function($http) {
    var service = {};

    service.stripWhiteSpace = function (string) {
        string = string.replace(/\s/g, "");
        return string;
    };

    service.createStripeCardObjectFromFormattedInput = function(inputObject) {

        var returnCard = {};
        var expiryArray = service.stripWhiteSpace(inputObject.expiry).split('/');

        returnCard = {
            number: service.stripWhiteSpace(inputObject.number),
            cvc: service.stripWhiteSpace(inputObject.cvc),
            exp_month: expiryArray[0],
            exp_year: expiryArray[1]
        };

        /* If there is a cardholder name, add it to the card object */
        if (typeof inputObject.name !== 'undefined') {
            if (inputObject.name.length > 0) {
                returnCard.name = inputObject.name;
            }
        }

        return returnCard;

    };

    return service;
});

app.factory('SubscriptionFactory', function($q, $rootScope, $http) {

    var service = {};
    service.subscriptionURL = baseURL + '/settings/subscription';
    console.log('SUBSCRIPTION URL:: ' + service.subscriptionURL);

    service.sayHello = function () {
        return 'HELLO FROM STRIPE FACTORY';
    };

    service.createSubscription = function (planID, stripeToken) {

        var data = {
            _token: csrfToken,
            stripeToken: stripeToken,
            plan: 'awesome'
        };

        var defered = $q.defer();

        $http.post(service.subscriptionURL, data)
            .success(function (data, status) {
                defered.resolve(data);
            })

            .error(function (data, status, headers, config) {
                var rejectObject = {
                    data: data,
                    status: status,
                    headers: headers,
                    config: config
                };

                defered.reject(rejectObject);

            });

        return defered.promise;
    };

    service.cancelSubscription = function() {

        var configObject = {
              _token: csrfToken
              //_method: 'delete'
        };

        var defered = $q.defer();

        $http.post(service.subscriptionURL + '/delete', configObject)
            .success(function (data, status) {
                defered.resolve(data);
            })
            .error(function (data, status) {
                defered.reject(data);

            });

        return defered.promise;

    };

    return service;

});

app.factory('CommentsFactory', function ($q, $http) {

    var service = {};

    service.sayHello = function () {
        console.log('Hello from CommentsFactory');
        return 'Hello from CommentFactory';
    };

    service.postNewComment = function (commentText, productID) {
        console.log('Add new comment function called');
        var postObject = {
            _token: csrfToken,
            comment: commentText,
            item_id: productID
        };

        var defered = $q.defer();

        console.log('BASE URL:: ' + baseURL);
        console.log(postObject);

        $http.post(baseURL + '/comments', postObject)
            .success(function (data, status) {
                defered.resolve(data);
            })
            .error(function (data, status) {
                defered.reject(data);
            });

        return defered.promise;

    };

    service.getComments = function (productID) {
        var defered = $q.defer();

        $http.get(baseURL + '/username/product/' + productID + '/' + 'comments')
            .success(function (data, status) {
                defered.resolve(data);
            })
            .error(function (data, status) {
                defered.reject(data);
            });

        return defered.promise;

    };

    return service;

});

app.directive('sailrComments', function () {
    return {
        restrict: 'AE',
        require: '^sailrProductId',
        scope: {
            sailrProductId: '@'
        },
        controller: ['$scope', '$http', 'CommentsFactory', function ($scope, $http, CommentsFactory) {

            $scope.comments = [];
            $scope.commentOpacity = 1.00;
            $scope.webError = false;

            $scope.newComment = {};
            $scope.loggedInUser = loggedInUser;
            $scope.item_id = 0;

            $scope.getComments = function (product_id) {
                //console.log('PRODUCT ID:: ' + product_id);
                $scope.item_id = product_id;

                var getCommentsPromise = CommentsFactory.getComments($scope.item_id);

                getCommentsPromise.then(function (successResponse) {
                        $scope.comments = successResponse.data;
                    },
                    function (failResponse) {
                        console.log(failResponse);
                        $scope.webError = true;
                    });

                };

            $scope.postNewComment = function () {
                var newCommentIndex = $scope.comments.unshift({
                    item_id: $scope.item_id,
                    comment: $scope.newComment.comment,
                    created_at: new Date(),
                    user: loggedInUser
                });

                var newCommentPromise = CommentsFactory.postNewComment($scope.newComment.comment, $scope.item_id);
                console.log(CommentsFactory);
                console.log(newCommentPromise);

                newCommentPromise.then(function (successResponse) {
                        console.log(successResponse);
                        $scope.newComment.comment = '';
                        //Set opacity to 1.00
                    },
                    function (failResponse) {
                        console.log(failResponse);
                        $scope.comments = $scope.comments.splice(1, newCommentIndex);
                    });

            }
        }],

        templateUrl: baseURL + '/js/templates/comments/master.html',
        link: function (scope, iElement, iAttrs, ctrl) {
            scope.getComments(iAttrs.sailrProductId);
            scope.item_id = iAttrs.sailrProductId;
        }
    }
});

app.directive('sailrProductId', function () {
    return {
        controller: function ($scope) {}
    }
});

app.directive('sailrComment', function () {
    return {
        restrict: 'AE',
        scope: {
            profileImageUrl: '@',
            username: '@',
            name: '@',
            commentText: '@'
        },
        controller: ['$scope', function ($scope) {

        }],

        link: function (scope, iElement, iAttrs, ctrl) {
            scope.username = iAttrs.username;
            scope.name = iAttrs.name;
            scope.profileImageUrl = iAttrs.profileImageUrl;
            scope.commentText = iAttrs.commentText;
            scope.baseURL = baseURL;

        },

        templateUrl: baseURL + '/js/templates/comments/comment-item.html'

    }

});

<commit_msg>Auto code formatting<commit_after>var app = angular.module('app', ['angularFileUpload', 'ngAnimate', 'ngSanitize']);

app.run(function ($rootScope) {
    if (baseURL) {
        $rootScope.baseURL = baseURL;
    }

    else {
        $rootScope.baseURL = 'http://sailr.co';
    }

    $rootScope.loggedInUser = loggedInUser;
});

app.directive('contenteditable', function () {
    return {
        restrict: 'A', // only activate on element attribute
        require: '?ngModel', // get a hold of NgModelController
        link: function (scope, element, attrs, ngModel) {
            if (!ngModel) return; // do nothing if no ng-model

            // Specify how UI should be updated
            ngModel.$render = function () {
                element.html(ngModel.$viewValue);
            };

            // Listen for change events to enable binding
            element.on('blur keyup change input', function () {
                scope.$apply(read);
            });
            read(); // initialize

            // Write data to the model
            function read() {
                var html = element.html();
                // When we clear the content editable the browser leaves a
 behind
                // If strip-br attribute is provided then we strip this out
                if (attrs.stripBr && html == '
') {
                    html = '';
                }
                ngModel.$setViewValue(html);
            }
        }
    };
});

app.directive('num-binding', function () {
    return {
        restrict: 'A',
        require: 'ngModel',
        scope: {
            model: '=ngModel',
        },
        link: function (scope, element, attrs, ngModelCtrl) {
            if (scope.model && typeof scope.model == 'string') {
                scope.model = parseInt(scope.model);
            }
        }
    };
});

app.directive('focusMe', function ($timeout, $parse) {
    return {
        //scope: true,   // optionally create a child scope
        link: function (scope, element, attrs) {
            var model = $parse(attrs.focusMe);
            scope.$watch(model, function (value) {
                console.log('value=', value);
                if (value === true) {
                    $timeout(function () {
                        element[0].focus();
                    });
                }
            });
            /*
             // to address @blesh's comment, set attribute value to 'false'
             // on blur event:
             element.bind('blur', function() {
             console.log('blur');
             scope.$apply(model.assign(scope, false));
             });
             */
        }
    };
});

app.directive('sailrFooter', ['$document', function ($document) {
    function link(scope, element, attrs) {
        var hasVScroll = document.body.scrollHeight > document.body.clientHeight;

        if (!hasVScroll) {
            element.css({
                position: 'relative'
            });
        }

        else {
            element.css({
                position: 'absolute'
            });
        }

    }

    return {
        link: link
    }
}]);

app.directive('sailrOpactiy', function () {

    function link(scope, iElement, iAttrs) {

        scope.sailrOpacity = iAttrs.sailrOpacity;
        iElement.css({
            opacity: scope.sailrOpacity
        });
    }

    return {
        restrict: 'A',
        scope: {
            sailrOpacity: '@'
        },
        link: link
    }
});

app.factory('StripeFactory', function ($q, $rootScope) {
    var service = {};

    service.sayHello = function () {
        return 'HELLO FROM STRIPE FACTORY';
    };

    service.setPublishableKey = function (key) {
        Stripe.setPublishableKey(key);
    };

    service.createToken = function (cardData) {
        var defered = $q.defer();

        Stripe.card.createToken(cardData, function (status, response) {

            if (response.error) {
                service.errors = response.error;
                defered.reject(response);

            }

            else {
                service.token = response;
                defered.resolve(response);

            }
        });

        return defered.promise;
    };

    service.getToken = function () {
        return service.token;
    };

    service.getErrors = function () {
        return service.errors;
    };

    return service;
});

app.factory('HelperFactory', function ($http) {
    var service = {};

    service.stripWhiteSpace = function (string) {
        string = string.replace(/\s/g, "");
        return string;
    };

    service.createStripeCardObjectFromFormattedInput = function (inputObject) {

        var returnCard = {};
        var expiryArray = service.stripWhiteSpace(inputObject.expiry).split('/');

        returnCard = {
            number: service.stripWhiteSpace(inputObject.number),
            cvc: service.stripWhiteSpace(inputObject.cvc),
            exp_month: expiryArray[0],
            exp_year: expiryArray[1]
        };

        /* If there is a cardholder name, add it to the card object */
        if (typeof inputObject.name !== 'undefined') {
            if (inputObject.name.length > 0) {
                returnCard.name = inputObject.name;
            }
        }

        return returnCard;

    };

    return service;
});

app.factory('SubscriptionFactory', function ($q, $rootScope, $http) {

    var service = {};
    service.subscriptionURL = baseURL + '/settings/subscription';
    console.log('SUBSCRIPTION URL:: ' + service.subscriptionURL);

    service.sayHello = function () {
        return 'HELLO FROM STRIPE FACTORY';
    };

    service.createSubscription = function (planID, stripeToken) {

        var data = {
            _token: csrfToken,
            stripeToken: stripeToken,
            plan: 'awesome'
        };

        var defered = $q.defer();

        $http.post(service.subscriptionURL, data)
            .success(function (data, status) {
                defered.resolve(data);
            })

            .error(function (data, status, headers, config) {
                var rejectObject = {
                    data: data,
                    status: status,
                    headers: headers,
                    config: config
                };

                defered.reject(rejectObject);

            });

        return defered.promise;
    };

    service.cancelSubscription = function () {

        var configObject = {
            _token: csrfToken
            //_method: 'delete'
        };

        var defered = $q.defer();

        $http.post(service.subscriptionURL + '/delete', configObject)
            .success(function (data, status) {
                defered.resolve(data);
            })
            .error(function (data, status) {
                defered.reject(data);

            });

        return defered.promise;

    };

    return service;

});

app.factory('CommentsFactory', function ($q, $http) {

    var service = {};

    service.sayHello = function () {
        console.log('Hello from CommentsFactory');
        return 'Hello from CommentFactory';
    };

    service.postNewComment = function (commentText, productID) {
        console.log('Add new comment function called');
        var postObject = {
            _token: csrfToken,
            comment: commentText,
            item_id: productID
        };

        var defered = $q.defer();

        console.log('BASE URL:: ' + baseURL);
        console.log(postObject);

        $http.post(baseURL + '/comments', postObject)
            .success(function (data, status) {
                defered.resolve(data);
            })
            .error(function (data, status) {
                defered.reject(data);
            });

        return defered.promise;

    };

    service.getComments = function (productID) {
        var defered = $q.defer();

        $http.get(baseURL + '/username/product/' + productID + '/' + 'comments')
            .success(function (data, status) {
                defered.resolve(data);
            })
            .error(function (data, status) {
                defered.reject(data);
            });

        return defered.promise;

    };

    return service;

});

app.directive('sailrComments', function () {
    return {
        restrict: 'AE',
        require: '^sailrProductId',
        scope: {
            sailrProductId: '@'
        },
        controller: ['$scope', '$http', 'CommentsFactory', function ($scope, $http, CommentsFactory) {

            $scope.comments = [];
            $scope.commentOpacity = 1.00;
            $scope.webError = false;

            $scope.newComment = {};
            $scope.loggedInUser = loggedInUser;
            $scope.item_id = 0;

            $scope.getComments = function (product_id) {
                //console.log('PRODUCT ID:: ' + product_id);
                $scope.item_id = product_id;

                var getCommentsPromise = CommentsFactory.getComments($scope.item_id);

                getCommentsPromise.then(function (successResponse) {
                        $scope.comments = successResponse.data;
                    },
                    function (failResponse) {
                        console.log(failResponse);
                        $scope.webError = true;
                    });

                };

            $scope.postNewComment = function () {
                var newCommentIndex = $scope.comments.unshift({
                    item_id: $scope.item_id,
                    comment: $scope.newComment.comment,
                    created_at: new Date(),
                    user: loggedInUser
                });

                var newCommentPromise = CommentsFactory.postNewComment($scope.newComment.comment, $scope.item_id);
                console.log(CommentsFactory);
                console.log(newCommentPromise);

                newCommentPromise.then(function (successResponse) {
                        console.log(successResponse);
                        $scope.newComment.comment = '';
                        //Set opacity to 1.00
                    },
                    function (failResponse) {
                        console.log(failResponse);
                        $scope.comments = $scope.comments.splice(1, newCommentIndex);
                    });

            }
        }],

        templateUrl: baseURL + '/js/templates/comments/master.html',
        link: function (scope, iElement, iAttrs, ctrl) {
            scope.getComments(iAttrs.sailrProductId);
            scope.item_id = iAttrs.sailrProductId;
        }
    }
});

app.directive('sailrProductId', function () {
    return {
        controller: function ($scope) {}
    }
});

app.directive('sailrComment', function () {
    return {
        restrict: 'AE',
        scope: {
            profileImageUrl: '@',
            username: '@',
            name: '@',
            commentText: '@'
        },
        controller: ['$scope', function ($scope) {

        }],

        link: function (scope, iElement, iAttrs, ctrl) {
            scope.username = iAttrs.username;
            scope.name = iAttrs.name;
            scope.profileImageUrl = iAttrs.profileImageUrl;
            scope.commentText = iAttrs.commentText;
            scope.baseURL = baseURL;

        },

        templateUrl: baseURL + '/js/templates/comments/comment-item.html'

    }

});

<|endoftext|>


<commit_before>/**
 * Sets the headers for the TestRail API calls
 */
function setTestRailHeaders() {
  // Construct request parameters object
  var params = {};

  // Set the authentication header. Sorry, the credentials are hardcoded.
  params[gadgets.io.RequestParameters.HEADERS] =  {"Content-Type" : "application/json", "Authorization" : "Basic " + btoa("user:pass")};

  return(params);
}

/**
 * Pad numbers with leading zeros
 */
function pad(num, size) {
    var s = num+"";
    while (s.length < size) s = "0" + s;
    return s;
}

/**
 * Gets the label for a status
 */
function getStatusLabel(search_name) {
  var label = "";

  for (var i = 0; i < statusIndex; i++) {
    if (statusList[i][1] == search_name) {
      label = statusList[i][2];
      break;
    }
  }
  return(label);
}

/**
 * Gets the color for a status
 */
function getStatusColor(search_name) {
  var color = "";

  for (var i = 0; i < statusIndex; i++) {
    if (statusList[i][1] == search_name) {
      color = "#" + pad(statusList[i][3].toString(16),6);
      break;
    }
  }
  return(color);
}

/**
 * Fetches information about the TestRail statuses
 */
function fetchStatusList() {
  // Request URL for status
  var statusAPI = testRailURL + "/index.php?/api/v2/get_statuses";
  var params = setTestRailHeaders();

  // Proxy the request through the container server
  gadgets.io.makeRequest(statusAPI, handleStatusResponse, params);
}

/**
 * Processes the TestRail get_status response
 */
function handleStatusResponse(obj) {
  // obj.data contains a JSON element
  var statusData = gadgets.json.parse(obj.data);
  var responseLength = 0;

  if (statusData != null) {
    responseLength = statusData.length;
  }

  // Loop through all of the active statuses
  for (var i = 0; i < responseLength; i++) {
    statusList[statusIndex] = new Array(4);
    statusList[statusIndex][0] = statusData[i].id;
    if (statusData[i].is_system === false) {
      customStatusCount++;
      statusList[statusIndex][1] = "custom_status" + String(customStatusCount);
    } else {
      statusList[statusIndex][1] = statusData[i].name;
    }
    statusList[statusIndex][2] = statusData[i].label;
    statusList[statusIndex][3] = statusData[i].color_dark;

    statusIndex++;
  }

  // This should never happen if everything is configured correctly, but just in case
  if (statusIndex == 0) {
    document.getElementById('projectCaption').innerHTML = "Unable to retrieve the list of statuses";
    msg.dismissMessage(loadMessage);
    gadgets.window.adjustHeight();
  } else {
    fetchProjectInfo();
  }
}

/**
 * Fetches information about the TestRail project
 */
function fetchProjectInfo() {
  // Request URL for project
  var projectAPI = testRailURL + "/index.php?/api/v2/get_project/" + projectID;
  var params = setTestRailHeaders();

  // Don't make the API call if no test plans/runs were in the XML
  if (projectID == "0") {
    document.getElementById('projectCaption').innerHTML = "No active test runs found";
    msg.dismissMessage(loadMessage);
    gadgets.window.adjustHeight();
  } else {
    // Proxy the request through the container server
    gadgets.io.makeRequest(projectAPI, handleProjectResponse, params);
  }
}

/**
 * Processes the TestRail get_project response
 */
function handleProjectResponse(obj) {
  // obj.data contains a JSON element
  var projectData = gadgets.json.parse(obj.data);
  if (projectData != null) {
    projectName = projectData.name;
    projectURL = projectData.url;
  }

  fetchRunResults();
}

/**
 * Fetches all of the testrun results from TestRail
 */
function fetchRunResults() {
  // Request URL for test runs
  var runAPI = testRailURL + "/index.php?/api/v2/get_runs/" + projectID;
  var params = setTestRailHeaders();

  // Proxy the request through the container server
  gadgets.io.makeRequest(runAPI, handleRunResponse, params);
}

/**
 * Processes the TestRail get_runs response
 */
function handleRunResponse(obj) {
  // obj.data contains a JSON element
  var jsondata = gadgets.json.parse(obj.data);
  var responseLength = 0;

  if (jsondata != null) {
    responseLength = jsondata.length;
  }

  // Loop through all of the test run results
  for (var i = 0; i < responseLength; i++) {
    var testrun = jsondata[i];
    if ((showCompleted === true) || (testrun.is_completed === false)) {
      testResults[trIndex] = new Array(7+customStatusCount);
      testResults[trIndex][0] = testrun.name;
      testResults[trIndex][1] = testrun.url;
      testResults[trIndex][2] = testrun.passed_count;
      testResults[trIndex][3] = testrun.blocked_count;
      testResults[trIndex][4] = testrun.untested_count;
      testResults[trIndex][5] = testrun.retest_count;
      testResults[trIndex][6] = testrun.failed_count;

      // Add results for any custom statuses
      for (var j = 1; j <= customStatusCount; j++) {
        testResults[trIndex][6+j] = eval("testrun.custom_status" + j + "_count");
      }
      trIndex++;
    }
  }

  fetchPlanResults();
}

/**
 * Fetches all of the testplan results from TestRail
 */
function fetchPlanResults() {
  // Request URL for test plans
  var planAPI = testRailURL + "/index.php?/api/v2/get_plans/" + projectID;
  var params = setTestRailHeaders();

  // Proxy the request through the container server
  gadgets.io.makeRequest(planAPI, handlePlanResponse, params);
}

/**
 * Processes the TestRail get_plans response
 */
function handlePlanResponse(obj) {
  // obj.data contains a JSON element
  var jsondata = gadgets.json.parse(obj.data);
  var responseLength = 0;

  if (jsondata != null) {
    responseLength = jsondata.length;
  }

  // Loop through all of the test plan results
  for (var i = 0; i < responseLength; i++) {
    var testplan = jsondata[i];
    if ((showCompleted === true) || (testplan.is_completed === false)) {
      testResults[trIndex] = new Array(7+customStatusCount);
      testResults[trIndex][0] = testplan.name;
      testResults[trIndex][1] = testplan.url;
      testResults[trIndex][2] = testplan.passed_count;
      testResults[trIndex][3] = testplan.blocked_count;
      testResults[trIndex][4] = testplan.untested_count;
      testResults[trIndex][5] = testplan.retest_count;
      testResults[trIndex][6] = testplan.failed_count;

      // Add results for any custom statuses
      for (var j = 1; j <= customStatusCount; j++) {
        testResults[trIndex][6+j] = eval("testplan.custom_status" + j + "_count");
      }
      trIndex++;
    }
  }

  testResults.sort(sortByName);
  google.charts.setOnLoadCallback(renderTestResults);
  msg.dismissMessage(loadMessage);
  var title = "";
  if ((projectName !== "") && (projectName != undefined)) {
    title = projectName + ": ";
  }
  if (showCompleted === true) {
    title += "All Test Results";
  } else {
    title += "Active Test Results";
  }
  gadgets.window.setTitle(title);
  gadgets.window.adjustHeight();
}

/**
 * Sort the array by the first column (name)
 */
function sortByName(a, b) {
    return a[0] > b[0];
}

/**
 * Create the HTML output and display it
 */
function renderTestResults() {

  var trLength = testResults.length;
  if (trLength > 0 ) {

    // Get the labels for each of the system statuses
    var passed_label = getStatusLabel("passed");
    var blocked_label = getStatusLabel("blocked");
    var untested_label = getStatusLabel("untested");
    var retest_label = getStatusLabel("retest");
    var failed_label = getStatusLabel("failed");

    var data = new google.visualization.arrayToDataTable([
      [{label: 'Test Plan', id: 'TestPlan'},
       {label: passed_label, id: 'Passed', type: 'number'},
       {label: blocked_label, id: 'Blocked', type: 'number'},
       {label: untested_label, id: 'Untested', type: 'number'},
       {label: retest_label, id: 'Retest', type: 'number'},
       {label: failed_label, id: 'Failed', type: 'number'}]
    ]);

    // Add the label information for any custom statuses
    for (var i = 1; i <= customStatusCount; i++) {
      data.addColumn({label: getStatusLabel("custom_status" + String(i)), id: 'CustomStatus'+String(i), type: 'number'});
    }

    // Add a row for each set of test results
    for (var i = 0; i < trLength; i++) {
      var rowArray = [testResults[i][0], testResults[i][2], testResults[i][3], testResults[i][4], testResults[i][5], testResults[i][6]];

      // Push any custom statuses onto the row
      for (var j = 1; j <= customStatusCount; j++) {
        rowArray.push(testResults[i][j+6]);
      }
      data.addRow(rowArray);
    }

    var chartAreaHeight = data.getNumberOfRows() * 30;
    var chartHeight = chartAreaHeight + 80;
    var chartWidth = gadgets.window.getViewportDimensions().width - 50;

    // Get the display color for each of the system statuses
    var passed_color = getStatusColor("passed");
    var blocked_color = getStatusColor("blocked");
    var untested_color = getStatusColor("untested");
    var retest_color = getStatusColor("retest");
    var failed_color = getStatusColor("failed");

    var colorArray = [passed_color, blocked_color, untested_color, retest_color, failed_color];

    // Add the display color information for any custom statuses
    for (var i = 1; i <= customStatusCount; i++) {
       colorArray.push(getStatusColor("custom_status" + String(i)));
    }

    var options = {
      allowHtml: true,
      sliceVisibilityThreshold: 0,
      height: chartHeight,
      width: chartWidth,
      colors: colorArray,
      isStacked: 'percent',
      legend: { position: 'top', maxLines: 3 },
      bar: { groupWidth: 20 },
      chartArea: {
        left: '18%',
        height: chartAreaHeight,
        width: '75%'
      },
      vAxis: {
        textStyle: { fontSize: 11 }
      },
      hAxis: {
        minValue: 0,
        ticks: [0, .25, .5, .75, 1]
      }
    };

    // Instantiate and draw our chart, passing in the options.
    var myProjectChart = new google.visualization.BarChart(document.getElementById('projectChart'));
    myProjectChart.draw(data, options);
    document.getElementById('projectCaption').innerHTML = "Testing results for the " + projectName + " project";
    document.getElementById('disclaimer').innerHTML = "For any of the above links a TestRail login is required.";
  } else {
    document.getElementById('projectCaption').innerHTML = "No active test runs found";
  }
  gadgets.window.adjustHeight();
}

/**
 * Main
 */

// Fix a JIRA bug that causes the Edit button to not work
var edit = window.parent.document.getElementById(window.frameElement.id + '-edit');
if (edit) {  // Ensure element was found
    edit.classList.remove('hidden');
    edit.style.display = 'none';
}

var msg = new gadgets.MiniMessage();  // Create minimessage factory
var loadMessage = msg.createStaticMessage("loading...");  // Show a small loading message to the user

var testResults = new Array();
var statusList = new Array();
var trIndex = 0;
var statusIndex = 0;
var customStatusCount = 0;
var projectName = "";
var projectURL = "";

// Get configured user prefs
var prefs = new gadgets.Prefs();
var projectID = prefs.getInt("projectID");
var showCompleted = prefs.getBool("showCompleted");
var testRailURL = prefs.getString("testRailURL");

google.charts.load('current', {packages: ['corechart', 'bar']});

// Fetch status info when the gadget loads
gadgets.util.registerOnLoadHandler(fetchStatusList);
<commit_msg>Fixed sorting of test plans and test runs<commit_after>/**
 * Sets the headers for the TestRail API calls
 */
function setTestRailHeaders() {
  // Construct request parameters object
  var params = {};

  // Set the authentication header. Sorry, the credentials are hardcoded.
  params[gadgets.io.RequestParameters.HEADERS] =  {"Content-Type" : "application/json", "Authorization" : "Basic " + btoa("user:pass")};

  return(params);
}

/**
 * Pad numbers with leading zeros
 */
function pad(num, size) {
    var s = num+"";
    while (s.length < size) s = "0" + s;
    return s;
}

/**
 * Gets the label for a status
 */
function getStatusLabel(search_name) {
  var label = "";

  for (var i = 0; i < statusIndex; i++) {
    if (statusList[i][1] == search_name) {
      label = statusList[i][2];
      break;
    }
  }
  return(label);
}

/**
 * Gets the color for a status
 */
function getStatusColor(search_name) {
  var color = "";

  for (var i = 0; i < statusIndex; i++) {
    if (statusList[i][1] == search_name) {
      color = "#" + pad(statusList[i][3].toString(16),6);
      break;
    }
  }
  return(color);
}

/**
 * Fetches information about the TestRail statuses
 */
function fetchStatusList() {
  // Request URL for status
  var statusAPI = testRailURL + "/index.php?/api/v2/get_statuses";
  var params = setTestRailHeaders();

  // Proxy the request through the container server
  gadgets.io.makeRequest(statusAPI, handleStatusResponse, params);
}

/**
 * Processes the TestRail get_status response
 */
function handleStatusResponse(obj) {
  // obj.data contains a JSON element
  var statusData = gadgets.json.parse(obj.data);
  var responseLength = 0;

  if (statusData != null) {
    responseLength = statusData.length;
  }

  // Loop through all of the active statuses
  for (var i = 0; i < responseLength; i++) {
    statusList[statusIndex] = new Array(4);
    statusList[statusIndex][0] = statusData[i].id;
    if (statusData[i].is_system === false) {
      customStatusCount++;
      statusList[statusIndex][1] = "custom_status" + String(customStatusCount);
    } else {
      statusList[statusIndex][1] = statusData[i].name;
    }
    statusList[statusIndex][2] = statusData[i].label;
    statusList[statusIndex][3] = statusData[i].color_dark;

    statusIndex++;
  }

  // This should never happen if everything is configured correctly, but just in case
  if (statusIndex == 0) {
    document.getElementById('projectCaption').innerHTML = "Unable to retrieve the list of statuses";
    msg.dismissMessage(loadMessage);
    gadgets.window.adjustHeight();
  } else {
    fetchProjectInfo();
  }
}

/**
 * Fetches information about the TestRail project
 */
function fetchProjectInfo() {
  // Request URL for project
  var projectAPI = testRailURL + "/index.php?/api/v2/get_project/" + projectID;
  var params = setTestRailHeaders();

  // Don't make the API call if no test plans/runs were in the XML
  if (projectID == "0") {
    document.getElementById('projectCaption').innerHTML = "No active test runs found";
    msg.dismissMessage(loadMessage);
    gadgets.window.adjustHeight();
  } else {
    // Proxy the request through the container server
    gadgets.io.makeRequest(projectAPI, handleProjectResponse, params);
  }
}

/**
 * Processes the TestRail get_project response
 */
function handleProjectResponse(obj) {
  // obj.data contains a JSON element
  var projectData = gadgets.json.parse(obj.data);
  if (projectData != null) {
    projectName = projectData.name;
    projectURL = projectData.url;
  }

  fetchRunResults();
}

/**
 * Fetches all of the testrun results from TestRail
 */
function fetchRunResults() {
  // Request URL for test runs
  var runAPI = testRailURL + "/index.php?/api/v2/get_runs/" + projectID;
  var params = setTestRailHeaders();

  // Proxy the request through the container server
  gadgets.io.makeRequest(runAPI, handleRunResponse, params);
}

/**
 * Processes the TestRail get_runs response
 */
function handleRunResponse(obj) {
  // obj.data contains a JSON element
  var jsondata = gadgets.json.parse(obj.data);
  var responseLength = 0;

  if (jsondata != null) {
    responseLength = jsondata.length;
  }

  // Loop through all of the test run results
  for (var i = 0; i < responseLength; i++) {
    var testrun = jsondata[i];
    if ((showCompleted === true) || (testrun.is_completed === false)) {
      testResults[trIndex] = new Array(7+customStatusCount);
      testResults[trIndex][0] = testrun.name;
      testResults[trIndex][1] = testrun.url;
      testResults[trIndex][2] = testrun.passed_count;
      testResults[trIndex][3] = testrun.blocked_count;
      testResults[trIndex][4] = testrun.untested_count;
      testResults[trIndex][5] = testrun.retest_count;
      testResults[trIndex][6] = testrun.failed_count;

      // Add results for any custom statuses
      for (var j = 1; j <= customStatusCount; j++) {
        testResults[trIndex][6+j] = eval("testrun.custom_status" + j + "_count");
      }
      trIndex++;
    }
  }

  fetchPlanResults();
}

/**
 * Fetches all of the testplan results from TestRail
 */
function fetchPlanResults() {
  // Request URL for test plans
  var planAPI = testRailURL + "/index.php?/api/v2/get_plans/" + projectID;
  var params = setTestRailHeaders();

  // Proxy the request through the container server
  gadgets.io.makeRequest(planAPI, handlePlanResponse, params);
}

/**
 * Processes the TestRail get_plans response
 */
function handlePlanResponse(obj) {
  // obj.data contains a JSON element
  var jsondata = gadgets.json.parse(obj.data);
  var responseLength = 0;

  if (jsondata != null) {
    responseLength = jsondata.length;
  }

  // Loop through all of the test plan results
  for (var i = 0; i < responseLength; i++) {
    var testplan = jsondata[i];
    if ((showCompleted === true) || (testplan.is_completed === false)) {
      testResults[trIndex] = new Array(7+customStatusCount);
      testResults[trIndex][0] = testplan.name;
      testResults[trIndex][1] = testplan.url;
      testResults[trIndex][2] = testplan.passed_count;
      testResults[trIndex][3] = testplan.blocked_count;
      testResults[trIndex][4] = testplan.untested_count;
      testResults[trIndex][5] = testplan.retest_count;
      testResults[trIndex][6] = testplan.failed_count;

      // Add results for any custom statuses
      for (var j = 1; j <= customStatusCount; j++) {
        testResults[trIndex][6+j] = eval("testplan.custom_status" + j + "_count");
      }
      trIndex++;
    }
  }

  testResults.sort(sortByName);
  google.charts.setOnLoadCallback(renderTestResults);
  msg.dismissMessage(loadMessage);
  var title = "";
  if ((projectName !== "") && (projectName != undefined)) {
    title = projectName + ": ";
  }
  if (showCompleted === true) {
    title += "All Test Results";
  } else {
    title += "Active Test Results";
  }
  gadgets.window.setTitle(title);
  gadgets.window.adjustHeight();
}

/**
 * Sort the array by the first column (name)
 */
function sortByName(a, b) {
  var n1 = a[0].toLowerCase();
  var n2 = b[0].toLowerCase();

  if (n1 < n2) return -1;
  if (n1 > n2) return 1;
  return 0;
}

/**
 * Create the HTML output and display it
 */
function renderTestResults() {

  var trLength = testResults.length;
  if (trLength > 0 ) {

    // Get the labels for each of the system statuses
    var passed_label = getStatusLabel("passed");
    var blocked_label = getStatusLabel("blocked");
    var untested_label = getStatusLabel("untested");
    var retest_label = getStatusLabel("retest");
    var failed_label = getStatusLabel("failed");

    var data = new google.visualization.arrayToDataTable([
      [{label: 'Test Plan', id: 'TestPlan'},
       {label: passed_label, id: 'Passed', type: 'number'},
       {label: blocked_label, id: 'Blocked', type: 'number'},
       {label: untested_label, id: 'Untested', type: 'number'},
       {label: retest_label, id: 'Retest', type: 'number'},
       {label: failed_label, id: 'Failed', type: 'number'}]
    ]);

    // Add the label information for any custom statuses
    for (var i = 1; i <= customStatusCount; i++) {
      data.addColumn({label: getStatusLabel("custom_status" + String(i)), id: 'CustomStatus'+String(i), type: 'number'});
    }

    // Add a row for each set of test results
    for (var i = 0; i < trLength; i++) {
      var rowArray = [testResults[i][0], testResults[i][2], testResults[i][3], testResults[i][4], testResults[i][5], testResults[i][6]];

      // Push any custom statuses onto the row
      for (var j = 1; j <= customStatusCount; j++) {
        rowArray.push(testResults[i][j+6]);
      }
      data.addRow(rowArray);
    }

    var chartAreaHeight = data.getNumberOfRows() * 30;
    var chartHeight = chartAreaHeight + 80;
    var chartWidth = gadgets.window.getViewportDimensions().width - 50;

    // Get the display color for each of the system statuses
    var passed_color = getStatusColor("passed");
    var blocked_color = getStatusColor("blocked");
    var untested_color = getStatusColor("untested");
    var retest_color = getStatusColor("retest");
    var failed_color = getStatusColor("failed");

    var colorArray = [passed_color, blocked_color, untested_color, retest_color, failed_color];

    // Add the display color information for any custom statuses
    for (var i = 1; i <= customStatusCount; i++) {
       colorArray.push(getStatusColor("custom_status" + String(i)));
    }

    var options = {
      allowHtml: true,
      sliceVisibilityThreshold: 0,
      height: chartHeight,
      width: chartWidth,
      colors: colorArray,
      isStacked: 'percent',
      legend: { position: 'top', maxLines: 3 },
      bar: { groupWidth: 20 },
      chartArea: {
        left: '18%',
        height: chartAreaHeight,
        width: '75%'
      },
      vAxis: {
        textStyle: { fontSize: 11 }
      },
      hAxis: {
        minValue: 0,
        ticks: [0, .25, .5, .75, 1]
      }
    };

    // Instantiate and draw our chart, passing in the options.
    var myProjectChart = new google.visualization.BarChart(document.getElementById('projectChart'));
    myProjectChart.draw(data, options);
    document.getElementById('projectCaption').innerHTML = "Testing results for the " + projectName + " project";
    document.getElementById('disclaimer').innerHTML = "For any of the above links a TestRail login is required.";
  } else {
    document.getElementById('projectCaption').innerHTML = "No active test runs found";
  }
  gadgets.window.adjustHeight();
}

/**
 * Main
 */

// Fix a JIRA bug that causes the Edit button to not work
var edit = window.parent.document.getElementById(window.frameElement.id + '-edit');
if (edit) {  // Ensure element was found
    edit.classList.remove('hidden');
    edit.style.display = 'none';
}

var msg = new gadgets.MiniMessage();  // Create minimessage factory
var loadMessage = msg.createStaticMessage("loading...");  // Show a small loading message to the user

var testResults = new Array();
var statusList = new Array();
var trIndex = 0;
var statusIndex = 0;
var customStatusCount = 0;
var projectName = "";
var projectURL = "";

// Get configured user prefs
var prefs = new gadgets.Prefs();
var projectID = prefs.getInt("projectID");
var showCompleted = prefs.getBool("showCompleted");
var testRailURL = prefs.getString("testRailURL");

google.charts.load('current', {packages: ['corechart', 'bar']});

// Fetch status info when the gadget loads
gadgets.util.registerOnLoadHandler(fetchStatusList);
<|endoftext|>