<commit_before>var forge = require('node-forge');
var fs = require('fs');
var path = require('path');
var os = require('os');
var crypto = require('crypto');
var pki = forge.pki;
var createSecureContext = require('tls').createSecureContext || crypto.createCredentials;
var util = require('../util');
var config = require('../config');
var HTTPS_DIR = util.mkdir(path.join(config.getDataDir(), 'certs'));
var ROOT_KEY_FILE = path.join(HTTPS_DIR, 'root.key');
var ROOT_CRT_FILE = path.join(HTTPS_DIR, 'root.crt');
var customCertDir = config.certDir;
var customPairs = {};
var wildcardPairs = {};
var cachePairs = {};
var MAC_ADDR = getMacAddress();
var ROOT_KEY, ROOT_CRT;

function getMacAddress() {
  var ifaces = os.networkInterfaces();
  for (var i in ifaces) {
    var addrList = ifaces[i];
    if (addrList) {
      for (var j = 0, len = addrList.length; j< len; j++) {
        var addr = addrList[j];
        if (addr && !addr.internal && addr.family == 'IPv4') {
          return addr.mac || addr.address;
        }
      }
    }
  }
}

function createCertificate(hostname) {
  var cert = customPairs[hostname]
    || wildcardPairs[hostname.slice(hostname.indexOf('.'))]
    || cachePairs[hostname];
  if (cert) {
    return cert;
  }

  cert = createCert(pki.setRsaPublicKey(ROOT_KEY.n, ROOT_KEY.e),
crypto.createHash('sha1')
.update(hostname, 'binary')
.digest('hex'));

  cert.setSubject([{
    name: 'commonName',
    value: hostname
  }]);

  cert.setIssuer(ROOT_CRT.subject.attributes);
  cert.sign(ROOT_KEY, forge.md.sha256.create());
  cert = cachePairs[hostname] = {
    key: pki.privateKeyToPem(ROOT_KEY),
    cert: pki.certificateToPem(cert)
  };
  return cert;
}

function loadCustomCerts() {
  if (!customCertDir) {
    return;
  }
  var certs = {};
  try {
    fs.readdirSync(customCertDir).forEach(function(name) {
      if (!/^([*_]\.)?(.+)\.(crt|key)$/.test(name)) {
        return;
      }
      var wildcard = RegExp.$1;
      var hostname = (wildcard ? '.' : '') + RegExp.$2;
      var suffix = RegExp.$3;
      var cert = certs[hostname] = certs[hostname] || {};
      if (suffix === 'crt') {
        suffix = 'cert';
      }
      if (wildcard) {
        cert.wildcard = true;
      }
      try {
        cert[suffix] = fs.readFileSync(path.join(customCertDir, name), {encoding: 'utf8'});
      } catch(e) {}
    });
  } catch(e) {}
  var rootCA = certs.root;
  delete certs.root;
  if (rootCA && rootCA.key && rootCA.cert) {
    ROOT_KEY_FILE = path.join(customCertDir, 'root.key');
    ROOT_CRT_FILE = path.join(customCertDir, 'root.crt');
  }
  Object.keys(certs).forEach(function(hostname) {
    var cert = certs[hostname];
    if (!cert || !cert.key || !cert.cert) {
      return;
    }
    if (cert.wildcard) {
      wildcardPairs[hostname] = cert;
    } else {
      customPairs[hostname] = cert;
    }
  });
}

function createRootCA() {
  loadCustomCerts();
  if (ROOT_KEY && ROOT_CRT) {
    return;
  }

  try {
    ROOT_KEY = fs.readFileSync(ROOT_KEY_FILE);
    ROOT_CRT = fs.readFileSync(ROOT_CRT_FILE);
  } catch (e) {
    ROOT_KEY = ROOT_CRT = null;
  }

  if (ROOT_KEY && ROOT_CRT && ROOT_KEY.length && ROOT_CRT.length) {
    ROOT_KEY = pki.privateKeyFromPem(ROOT_KEY);
    ROOT_CRT = pki.certificateFromPem(ROOT_CRT);
  } else {
    var cert = createCACert();
    ROOT_CRT = cert.cert;
    ROOT_KEY = cert.key;
    fs.writeFileSync(ROOT_KEY_FILE, pki.privateKeyToPem(ROOT_KEY).toString());
    fs.writeFileSync(ROOT_CRT_FILE, pki.certificateToPem(ROOT_CRT).toString());
  }

  exports.DEFAULT_CERT_CTX = createSecureContext({
    key: ROOT_KEY,
    cert: ROOT_CRT
  });
}

function createCACert() {
  var keys = pki.rsa.generateKeyPair(1024);
  var cert = createCert(keys.publicKey);
  var name = [];
  if (config.homeDirname) {
    try {
      //避免特殊字符导致根证书无法安装
      name.push(encodeURIComponent(config.homeDirname.slice(0, 20)));
    } catch(e) {}
  }
  if (MAC_ADDR) {
    name.push(MAC_ADDR);
  }
  name = name.join('@');
  name = 'WHISTLE' + (name ? '(' + name + ')' : '');
  var attrs = [ {
    name : 'commonName',
    value : name
  }, {
    name : 'countryName',
    value : 'CN'
  }, {
    shortName : 'ST',
    value : 'ZJ'
  }, {
    name : 'localityName',
    value : 'HZ'
  }, {
    name : 'organizationName',
    value : name
  }, {
    shortName : 'OU',
    value : 'WPROXY'
  } ];

  cert.setSubject(attrs);
  cert.setIssuer(attrs);
  cert.setExtensions([ {
    name : 'basicConstraints',
    cA : true
  }, {
    name : 'keyUsage',
    keyCertSign : true,
    digitalSignature : true,
    nonRepudiation : true,
    keyEncipherment : true,
    dataEncipherment : true
  }, {
    name : 'extKeyUsage',
    serverAuth : true,
    clientAuth : true,
    codeSigning : true,
    emailProtection : true,
    timeStamping : true
  }, {
    name : 'nsCertType',
    client : true,
    server : true,
    email : true,
    objsign : true,
    sslCA : true,
    emailCA : true,
    objCA : true
  } ]);

  cert.sign(keys.privateKey, forge.md.sha256.create());

  return {
    key: keys.privateKey,
    cert: cert
  };
}

function createCert(publicKey, serialNumber) {
  var cert = pki.createCertificate();
  cert.publicKey = publicKey;
  cert.serialNumber = serialNumber || '01';
  cert.validity.notBefore = new Date();
  cert.validity.notAfter = new Date();
  cert.validity.notAfter
.setFullYear(cert.validity.notBefore.getFullYear() + 10);
  return cert;
}

function getRootCAFile() {
  return ROOT_CRT_FILE;
}

createRootCA();// 启动生成ca
exports.getRootCAFile = getRootCAFile;
exports.createCertificate = createCertificate;

<commit_msg>feat: Use RSA 2048 bit instead of RSA 1024bit<commit_after>var forge = require('node-forge');
var fs = require('fs');
var path = require('path');
var os = require('os');
var crypto = require('crypto');
var pki = forge.pki;
var createSecureContext = require('tls').createSecureContext || crypto.createCredentials;
var util = require('../util');
var config = require('../config');
var HTTPS_DIR = util.mkdir(path.join(config.getDataDir(), 'certs'));
var ROOT_KEY_FILE = path.join(HTTPS_DIR, 'root.key');
var ROOT_CRT_FILE = path.join(HTTPS_DIR, 'root.crt');
var customCertDir = config.certDir;
var customPairs = {};
var wildcardPairs = {};
var cachePairs = {};
var MAC_ADDR = getMacAddress();
var SUPPORTS_2048_RSA = process.version >= 'v0.12.0';
var ROOT_KEY, ROOT_CRT;

function getMacAddress() {
  var ifaces = os.networkInterfaces();
  for (var i in ifaces) {
    var addrList = ifaces[i];
    if (addrList) {
      for (var j = 0, len = addrList.length; j< len; j++) {
        var addr = addrList[j];
        if (addr && !addr.internal && addr.family == 'IPv4') {
          return addr.mac || addr.address;
        }
      }
    }
  }
}

function createCertificate(hostname) {
  var cert = customPairs[hostname]
    || wildcardPairs[hostname.slice(hostname.indexOf('.'))]
    || cachePairs[hostname];
  if (cert) {
    return cert;
  }

  cert = createCert(pki.setRsaPublicKey(ROOT_KEY.n, ROOT_KEY.e),
crypto.createHash('sha1')
.update(hostname, 'binary')
.digest('hex'));

  cert.setSubject([{
    name: 'commonName',
    value: hostname
  }]);

  cert.setIssuer(ROOT_CRT.subject.attributes);
  cert.sign(ROOT_KEY, forge.md.sha256.create());
  cert = cachePairs[hostname] = {
    key: pki.privateKeyToPem(ROOT_KEY),
    cert: pki.certificateToPem(cert)
  };
  return cert;
}

function loadCustomCerts() {
  if (!customCertDir) {
    return;
  }
  var certs = {};
  try {
    fs.readdirSync(customCertDir).forEach(function(name) {
      if (!/^([*_]\.)?(.+)\.(crt|key)$/.test(name)) {
        return;
      }
      var wildcard = RegExp.$1;
      var hostname = (wildcard ? '.' : '') + RegExp.$2;
      var suffix = RegExp.$3;
      var cert = certs[hostname] = certs[hostname] || {};
      if (suffix === 'crt') {
        suffix = 'cert';
      }
      if (wildcard) {
        cert.wildcard = true;
      }
      try {
        cert[suffix] = fs.readFileSync(path.join(customCertDir, name), {encoding: 'utf8'});
      } catch(e) {}
    });
  } catch(e) {}
  var rootCA = certs.root;
  delete certs.root;
  if (rootCA && rootCA.key && rootCA.cert) {
    ROOT_KEY_FILE = path.join(customCertDir, 'root.key');
    ROOT_CRT_FILE = path.join(customCertDir, 'root.crt');
  }
  Object.keys(certs).forEach(function(hostname) {
    var cert = certs[hostname];
    if (!cert || !cert.key || !cert.cert) {
      return;
    }
    if (cert.wildcard) {
      wildcardPairs[hostname] = cert;
    } else {
      customPairs[hostname] = cert;
    }
  });
}

function createRootCA() {
  loadCustomCerts();
  if (ROOT_KEY && ROOT_CRT) {
    return;
  }

  try {
    ROOT_KEY = fs.readFileSync(ROOT_KEY_FILE);
    ROOT_CRT = fs.readFileSync(ROOT_CRT_FILE);
  } catch (e) {
    ROOT_KEY = ROOT_CRT = null;
  }

  if (ROOT_KEY && ROOT_CRT && ROOT_KEY.length && ROOT_CRT.length) {
    ROOT_KEY = pki.privateKeyFromPem(ROOT_KEY);
    ROOT_CRT = pki.certificateFromPem(ROOT_CRT);
  } else {
    var cert = createCACert();
    ROOT_CRT = cert.cert;
    ROOT_KEY = cert.key;
    fs.writeFileSync(ROOT_KEY_FILE, pki.privateKeyToPem(ROOT_KEY).toString());
    fs.writeFileSync(ROOT_CRT_FILE, pki.certificateToPem(ROOT_CRT).toString());
  }

  exports.DEFAULT_CERT_CTX = createSecureContext({
    key: ROOT_KEY,
    cert: ROOT_CRT
  });
}

function createCACert() {
  var keys = pki.rsa.generateKeyPair(SUPPORTS_2048_RSA ? 2048 : 1024);
  var cert = createCert(keys.publicKey);
  var name = [];
  if (config.homeDirname) {
    try {
      //避免特殊字符导致根证书无法安装
      name.push(encodeURIComponent(config.homeDirname.slice(0, 20)));
    } catch(e) {}
  }
  if (MAC_ADDR) {
    name.push(MAC_ADDR);
  }
  name = name.join('@');
  name = 'WHISTLE' + (name ? '(' + name + ')' : '');
  var attrs = [ {
    name : 'commonName',
    value : name
  }, {
    name : 'countryName',
    value : 'CN'
  }, {
    shortName : 'ST',
    value : 'ZJ'
  }, {
    name : 'localityName',
    value : 'HZ'
  }, {
    name : 'organizationName',
    value : name
  }, {
    shortName : 'OU',
    value : 'WPROXY'
  } ];

  cert.setSubject(attrs);
  cert.setIssuer(attrs);
  cert.setExtensions([ {
    name : 'basicConstraints',
    cA : true
  }, {
    name : 'keyUsage',
    keyCertSign : true,
    digitalSignature : true,
    nonRepudiation : true,
    keyEncipherment : true,
    dataEncipherment : true
  }, {
    name : 'extKeyUsage',
    serverAuth : true,
    clientAuth : true,
    codeSigning : true,
    emailProtection : true,
    timeStamping : true
  }, {
    name : 'nsCertType',
    client : true,
    server : true,
    email : true,
    objsign : true,
    sslCA : true,
    emailCA : true,
    objCA : true
  } ]);

  cert.sign(keys.privateKey, forge.md.sha256.create());

  return {
    key: keys.privateKey,
    cert: cert
  };
}

function createCert(publicKey, serialNumber) {
  var cert = pki.createCertificate();
  cert.publicKey = publicKey;
  cert.serialNumber = serialNumber || '01';
  cert.validity.notBefore = new Date();
  cert.validity.notAfter = new Date();
  cert.validity.notAfter
.setFullYear(cert.validity.notBefore.getFullYear() + 10);
  return cert;
}

function getRootCAFile() {
  return ROOT_CRT_FILE;
}

createRootCA();// 启动生成ca
exports.getRootCAFile = getRootCAFile;
exports.createCertificate = createCertificate;

<|endoftext|>


<commit_before>var accessKey = process.env.AWS_ACCESS_KEY;
var secretAccessKey = process.env.AWS_SECRET_ACCESS_KEY;

var _ = require('underscore'),
	moment = require('moment'),
	aws = require('aws2js');

var ec2 = aws.load('ec2', accessKey, secretAccessKey);

var ignition = {};

ignition.ec2 = ec2;

ignition.getInstanceDetails = function(instanceId, callback) {
	ignition.describeInstances(instanceId, function(e, instance) {
		if(e) {
			return callback(e, { state: "unknown", text: "unknown", name: "unknown" });
		}

		var state = instance.reservationSet.item.instancesSet.item.instanceState.name;
		var stateText = ignition.getDisplayTextForState(state);

		var tags = instance.reservationSet.item.instancesSet.item.tagSet.item;
		var name = _.find(tags, function(tag){ return tag.key == 'Name'; }).value;

		var launchtime = instance.reservationSet.item.instancesSet.item.launchTime;

		return callback(null, { currentState: state, action: stateText, name: name, launchtime: launchtime ? moment(new Date(launchtime)).format("ddd Mo, hA") : 'unknown' });
	});
};

ignition.describeInstances = function(instanceId, callback) {
	ignition.ec2.request('DescribeInstances', { "InstanceId.0" : instanceId }, callback);
};

// ignition.getStateOfInstance = function(instanceId, callback) {
// 	ignition.ec2.request('DescribeInstances', { "InstanceId.0" : instanceId }, function(error, response) {
// 		if (error) {
// 			console.error(error);
// 			return callback(error, "unknown");
// 		} else {
// 			//var tags = response.reservationSet.item.instancesSet.item.tagSet.item;
// 			//console.log(JSON.stringify(tags));
// 			var state = response.reservationSet.item.instancesSet.item.instanceState.name;
// 			return callback(null, state);
// 		}
// 	});
// };

ignition.getDisplayTextForState = function(state) {
  switch(state) {
		case "stopped":
		case "stopping":
			return "start";
		case "pending":
		case "running":
			return "stop";
		default:
			return "?";
	}
};

ignition.start = function(instanceId, callback) {
    ignition.ec2.request('StartInstances', { "InstanceId.0" : instanceId }, function(error, response) {
        console.log('start for instance %s', instanceId);
        return callback(error, response);
    });
};

ignition.stop = function(instanceId, callback) {
	ignition.ec2.request('StopInstances', { "InstanceId.0" : instanceId }, function(error, response) {
        console.log('stop for instance %s', instanceId);
        return callback(error, response);
    });
};

ignition.associateElasticIp = function(instanceId, elasticIp, callback) {
    ignition.ec2.request('AssociateAddress', { "InstanceId" : instanceId, "PublicIp" : elasticIp }, function(error, response) {
        console.log('AssociateAddress for instance %s, elasticIp %s', instanceId, elasticIp);

        if (error) {
            console.error(error);
        } else {
            console.log(response);
        }

        return callback(error, response);
    });
};

module.exports = ignition;<commit_msg>changed launchtime format<commit_after>var accessKey = process.env.AWS_ACCESS_KEY;
var secretAccessKey = process.env.AWS_SECRET_ACCESS_KEY;

var _ = require('underscore'),
	moment = require('moment'),
	aws = require('aws2js');

var ec2 = aws.load('ec2', accessKey, secretAccessKey);

var ignition = {};

ignition.ec2 = ec2;

ignition.getInstanceDetails = function(instanceId, callback) {
	ignition.describeInstances(instanceId, function(e, instance) {
		if(e) {
			return callback(e, { state: "unknown", text: "unknown", name: "unknown" });
		}

		var state = instance.reservationSet.item.instancesSet.item.instanceState.name;
		var stateText = ignition.getDisplayTextForState(state);

		var tags = instance.reservationSet.item.instancesSet.item.tagSet.item;
		var name = _.find(tags, function(tag){ return tag.key == 'Name'; }).value;

		var launchtime = instance.reservationSet.item.instancesSet.item.launchTime;

		return callback(null, { currentState: state, action: stateText, name: name, launchtime: launchtime ? moment(new Date(launchtime)).format("ddd , hA") : 'unknown' });
	});
};

ignition.describeInstances = function(instanceId, callback) {
	ignition.ec2.request('DescribeInstances', { "InstanceId.0" : instanceId }, callback);
};

// ignition.getStateOfInstance = function(instanceId, callback) {
// 	ignition.ec2.request('DescribeInstances', { "InstanceId.0" : instanceId }, function(error, response) {
// 		if (error) {
// 			console.error(error);
// 			return callback(error, "unknown");
// 		} else {
// 			//var tags = response.reservationSet.item.instancesSet.item.tagSet.item;
// 			//console.log(JSON.stringify(tags));
// 			var state = response.reservationSet.item.instancesSet.item.instanceState.name;
// 			return callback(null, state);
// 		}
// 	});
// };

ignition.getDisplayTextForState = function(state) {
  switch(state) {
		case "stopped":
		case "stopping":
			return "start";
		case "pending":
		case "running":
			return "stop";
		default:
			return "?";
	}
};

ignition.start = function(instanceId, callback) {
    ignition.ec2.request('StartInstances', { "InstanceId.0" : instanceId }, function(error, response) {
        console.log('start for instance %s', instanceId);
        return callback(error, response);
    });
};

ignition.stop = function(instanceId, callback) {
	ignition.ec2.request('StopInstances', { "InstanceId.0" : instanceId }, function(error, response) {
        console.log('stop for instance %s', instanceId);
        return callback(error, response);
    });
};

ignition.associateElasticIp = function(instanceId, elasticIp, callback) {
    ignition.ec2.request('AssociateAddress', { "InstanceId" : instanceId, "PublicIp" : elasticIp }, function(error, response) {
        console.log('AssociateAddress for instance %s, elasticIp %s', instanceId, elasticIp);

        if (error) {
            console.error(error);
        } else {
            console.log(response);
        }

        return callback(error, response);
    });
};

module.exports = ignition;<|endoftext|>


<commit_before>'use strict';

const _ = require('lodash');
const moment = require('moment');

const Helper = require('./helper');

class Industry {

  constructor(XmlClient) {

    this.XmlClient = XmlClient;

  }

  initJobs() {
    this._jobs = {
      1: {
        name: 'Production',
        color: '#ffcc00',
        statistic: [],
        data: []
      },
      3: {
        name: 'TE',
        color: '#0000ff',
        statistic: [],
        data: []
      },
      4: {
        name: 'ME',
        color: '#009900',
        statistic: [],
        data: []
      },
      5: {
        name: 'Copy',
        color: '#000000',
        statistic: [],
        data: []
      },
      8: {
        name: 'Invention',
        color: '#595959',
        statistic: [],
        data: []
      }
    };
  }

  history(keyId, vCode, period) {
    this.initJobs();
    return new Promise((resolve) => {
      try {

        const timePeriod = Helper.getTimePeriod(period);

        this.XmlClient.fetch('corp:IndustryJobsHistory', {
          keyID: keyId,
          vCode: vCode
        })
          .then((jobHistory) => {
            const cachedUntil = jobHistory.cachedUntil;
            const filteredJobs = _.filter(jobHistory.jobs, (job) => {
              let jobDate = moment(job.endDate).format('X');
              return jobDate >= timePeriod.start && jobDate <= timePeriod.end;
            });

            const jobs = _.orderBy(filteredJobs, ['endDate']);

            jobs.reduce((previousValue, currentValue) => {

              return previousValue
                .then((memo) => {

                  currentValue.cost = parseFloat(currentValue.cost);

                  let search = _.findIndex(memo[currentValue.activityID].statistic, {
                    characterID: currentValue.installerID
                  });

                  if (search === -1) {
                    memo[currentValue.activityID].statistic.push({
                      characterID: currentValue.installerID,
                      characterName: currentValue.installerName,
                      count: 1,
                      cost: currentValue.cost
                    });
                  } else {
                    memo[currentValue.activityID].statistic[search].count++;
                    memo[currentValue.activityID].statistic[search].cost += currentValue.cost;
                  }

                  return memo;
                });

            }, Promise.resolve(this._jobs))
              .then((complete) => {
                let attachments = [];

                _.forEach(complete, (jobs) => {
                  const sortJobs = _.orderBy(jobs.statistic, ['count'], ['desc']);
                  let attachmentText = '';

                  _.forEach(sortJobs, (job) => {
                    attachmentText += job.characterName+'\t'+job.count+'\t'+job.cost.formatISK(2, 3, '.', ',') + ' ISK\n';
                  });

                  attachments.push({
                    color: jobs.color,
                    title: 'Activity Type: ' + jobs.name,
                    text: attachmentText || 'no Jobs'
                  });
                });

                resolve({
                  text: 'Industry History\nFrom: ' + timePeriod.startHuman + '\nUntil: ' + timePeriod.endHuman + '\nchachedUntil: ' + cachedUntil + ' eveTime',
                  attachments: attachments,
                  mrkdwn_in: [
                    'text'
                  ]
                });
              });
          });
      } catch (e) {
        resolve({
          title: 'Error while fetching Industry Jobs',
          text: e.message
        });
      }
    });
  }

  now(keyId, vCode) {
    this.initJobs();
    return new Promise((resolve, reject) => {
      const self = this;
      let cachedUntil;

      try {
        this.XmlClient.fetch('corp:IndustryJobs', {
          keyID: keyId,
          vCode: vCode
        })
          .then((result) => {
            cachedUntil = result.cachedUntil;

            _.forEach(result.jobs, (value) => {
              self._jobs[value.activityID].data.push(value);
            });

            let attachments = [];

            _.forEach(self._jobs, (jobs) => {
              const sortJobs = _.orderBy(jobs.data, ['endDate'], ['asc']);
              let attachmentText = '';

              _.forEach(sortJobs, (job) => {
                attachmentText += moment(job.endDate).format('YYYY-MM-DD HH:mm')+'\t'+job.licensedRuns+'\t'+job.installerName + '\t' + job.productTypeName + '\n';
              });

              if (attachmentText.length === 0) {
                attachmentText = 'no Jobs';
              }

              attachments.push({
                color: jobs.color,
                title: jobs.name,
                text: attachmentText
              });
            });

            const resolveData = {
              text: 'Current Industry Jobs, chachedUntil: ' + cachedUntil + ' eveTime',
              attachments: attachments
            };

            if (JSON.stringify(resolveData).length >= 4000) {
              console.error('characters', JSON.stringify(resolveData).length);
              return reject('more then 4000 characters will be shown. Please inform the programmer about this.')
            }

            resolve(resolveData);
          });

      } catch (e) {
        reject(e.message);
      }
    });
  }

  static globalHelp() {
    return '• [!industry] -- command that ca return all actual industry jobs\n' +
      '• [!industry-history] [lastMonth|lastWeek|last2Weeks] -- command tha can return all complete industry jobs';
  }
}

module.exports = Industry;
<commit_msg>Finish issue-19<commit_after>'use strict';

const _ = require('lodash');
const moment = require('moment');

const Helper = require('./helper');

class Industry {

  constructor(XmlClient) {

    this.XmlClient = XmlClient;

  }

  initJobs() {
    this._jobs = {
      1: {
        name: 'Production',
        color: '#ffcc00',
        statistic: [],
        data: [],
        refund: 175000,
      },
      3: {
        name: 'TE',
        color: '#0000ff',
        statistic: [],
        data: [],
        refund: 75000,
      },
      4: {
        name: 'ME',
        color: '#009900',
        statistic: [],
        data: [],
        refund: 75000,
      },
      5: {
        name: 'Copy',
        color: '#000000',
        statistic: [],
        data: [],
        refund: 125000,
      },
      8: {
        name: 'Invention',
        color: '#595959',
        statistic: [],
        data: [],
        refund: 150000,
      }
    };
  }

  history(keyId, vCode, period) {
    this.initJobs();
    return new Promise((resolve) => {
      try {

        let timePeriod = Helper.getTimePeriod(period);

        this.XmlClient.fetch('corp:IndustryJobsHistory', {
          keyID: keyId,
          vCode: vCode
        })
          .then((jobHistory) => {
            let cachedUntil = jobHistory.cachedUntil;
            let filteredJobs = _.filter(jobHistory.jobs, (job) => {
              let jobDate = moment(job.endDate).format('X');
              return jobDate >= timePeriod.start && jobDate <= timePeriod.end;
            });

            let jobs = _.orderBy(filteredJobs, ['endDate']);

            jobs.reduce((previousValue, currentValue) => {

              return previousValue
                .then((memo) => {

                  currentValue.cost = parseFloat(currentValue.cost);

                  let search = _.findIndex(memo[currentValue.activityID].statistic, {
                    characterID: currentValue.installerID
                  });

                  if (search === -1) {
                    memo[currentValue.activityID].statistic.push({
                      characterID: currentValue.installerID,
                      characterName: currentValue.installerName,
                      count: 1,
                      cost: currentValue.cost
                    });
                  } else {
                    memo[currentValue.activityID].statistic[search].count++;
                    memo[currentValue.activityID].statistic[search].cost += currentValue.cost;
                  }

                  return memo;
                });

            }, Promise.resolve(this._jobs))
              .then((complete) => {
                let attachments = [];
                let refunds = [];

                _.forEach(complete, (jobs, activityID) => {

                  let sortJobs = _.orderBy(jobs.statistic, ['count'], ['desc']);
                  let attachmentText = '';

                  _.forEach(sortJobs, (job) => {
                    if (period === 'lastMonth') {
                      let search = _.findIndex(refunds, {characterID: job.characterID});
                      if (search === -1) {
                        refunds.push({
                          characterID: job.characterID,
                          characterName: job.characterName,
                          refund: job.count * this._jobs[activityID].refund
                        })
                      } else {
                        refunds[search].refund += job.count * this._jobs[activityID].refund;
                      }
                    }

                    attachmentText += job.characterName + '\t' + job.count + '\t' + job.cost.formatISK(2, 3, '.', ',') + ' ISK\n';
                  });

                  attachments.push({
                    color: jobs.color,
                    title: 'Activity Type: ' + jobs.name,
                    text: attachmentText || 'no Jobs'
                  });
                });

                let refundText = '';
                _.forEach(_.orderBy(refunds, ['refund'], ['desc']), refund => {
                  refundText += refund.characterName+'\t'+refund.refund.formatISK(2, 3, '.', ',') + ' ISK\n';
                });

                if (refundText.length) {
                  attachments.push({
                    color: '#FF00CC',
                    title: 'Refund:',
                    text: refundText || 'no Refund'
                  });
                }

                resolve({
                  text: 'Industry History\nFrom: ' + timePeriod.startHuman + '\nUntil: ' + timePeriod.endHuman + '\nchachedUntil: ' + cachedUntil + ' eveTime',
                  attachments: attachments,
                  mrkdwn_in: [
                    'text'
                  ]
                });
              });
          });
      } catch (e) {
        resolve({
          title: 'Error while fetching Industry Jobs',
          text: e.message
        });
      }
    });
  }

  now(keyId, vCode) {
    this.initJobs();
    return new Promise((resolve, reject) => {
      let self = this;
      let cachedUntil;

      try {
        this.XmlClient.fetch('corp:IndustryJobs', {
          keyID: keyId,
          vCode: vCode
        })
          .then((result) => {
            cachedUntil = result.cachedUntil;

            _.forEach(result.jobs, (value) => {
              self._jobs[value.activityID].data.push(value);
            });

            let attachments = [];

            _.forEach(self._jobs, (jobs) => {
              let sortJobs = _.orderBy(jobs.data, ['endDate'], ['asc']);
              let attachmentText = '';

              _.forEach(sortJobs, (job) => {
                attachmentText += moment(job.endDate).format('YYYY-MM-DD HH:mm')+'\t'+job.licensedRuns+'\t'+job.installerName + '\t' + job.productTypeName + '\n';
              });

              if (attachmentText.length === 0) {
                attachmentText = 'no Jobs';
              }

              attachments.push({
                color: jobs.color,
                title: jobs.name,
                text: attachmentText
              });
            });

            let resolveData = {
              text: 'Current Industry Jobs, chachedUntil: ' + cachedUntil + ' eveTime',
              attachments: attachments
            };

            if (JSON.stringify(resolveData).length >= 4000) {
              console.error('characters', JSON.stringify(resolveData).length);
              return reject('more then 4000 characters will be shown. Please inform the programmer about this.')
            }

            resolve(resolveData);
          });

      } catch (e) {
        reject(e.message);
      }
    });
  }

  static globalHelp() {
    return '• [!industry] -- command that ca return all actual industry jobs\n' +
      '• [!industry-history] [lastMonth|lastWeek|last2Weeks] -- command tha can return all complete industry jobs';
  }
}

module.exports = Industry;
<|endoftext|>


<commit_before>'use strict';

var timeouts = new Set();

function isthenable (obj) {
	return obj !== undefined && obj !== null && typeof obj === 'object' && 'then' in obj;
}

function delayloop (callback, time) {
	var args = Array.prototype.slice.call(arguments, 2);
	var timeout = setTimeout(delayed, time);

	function delayed () {
		// Cleanup now-expired timeout
		if (timeout) {
			timeouts.delete(timeout);
		}
		// Call function
		callback.apply(null, args);
	}
}

// TODO: more browser-specific checks?
var nextloop = (typeof setImmediate === 'function')
	? setImmediate
	: function (callback) {
		var args = [callback, 0];
		if (arguments.length > 1) {
			args.push.apply(args, Array.prototype.slice.call(arguments, 1));
		}
		return setTimeout.apply(null, args);
	};

function iterstep (iter, func, last, res, rej, time, skip) {
	var val;

	while (!last.done) {
		try {
			val = last.value;

			// Advance loop if not in progress
			if (!skip) {
				last = iter.next(val);
				if (last.done) {
					break;
				}
				val = last.value;

				// Check for promise
				if (isthenable(val)) {
					// Set loop to resume (skipping next()) once promise resolved and break; tack on rejector as catch handler
					return val.then(function (result) {
						return iterstep(iter, func, {value: result, done: last.done}, res, rej, time, true);
					}, rej);
				}
			}
			// Officially in progress
			skip = false;

			// Call function if given
			if (func !== undefined) {
				last.value = val = func(val);

				// Check for promise (again)
				if (isthenable(val)) {
					// Set loop to resume once promise resolved and break; tack on rejector as catch handler
					return val.then(function (result) {
						return iterstep(iter, func, {value: result, done: last.done}, res, rej, time);
					}, rej);
				}
			}

			if (time > 0) {
				// Schedule next iterator step after specified delay
				return delayloop(iterstep, time, iter, func, last, res, rej, time);
			}
			else if (time == 0) {
				// Schedule next iterator step for next event loop
				return nextloop(iterstep, iter, func, last, res, rej, time);
			}
			// else continue loop and get next value
		}
		catch (e) {
			// Reject original promise
			return rej(e);
		}
	};

	if (last.done) {
		return res(last.value);
	}
}

function iterlong (iterable, iterfunc, itertime) {
	// Callable sanity check
	if (iterfunc !== undefined && typeof iterfunc !== 'function') {
		throw new Error(`Paramter 'iterfunc' must be a function or undefined`);
	}

	if (isthenable(iterable)) {
		return iterable.then(function (iter) {
			return iterwait(iter, iterfunc);
		});
	}

	var iterator = iterable[Symbol.iterator]();
	var fakeval = {value: undefined, done: false};

	return new Promise(function (resolve, reject) {
		// Schedule stepper function
		if (itertime >= 0) {
			// Iteration job requested to be delayed per-iteration, so start on next event loop
			nextloop(iterstep, iterator, iterfunc, fakeval, resolve, reject, itertime);
		}
		else {
			// Begin actually-immediately
			iterstep(iterator, iterfunc, fakeval, resolve, reject, itertime);
		}
	});
}

iterlong.timeouts = timeouts;

module.exports = iterlong;
<commit_msg>Whoops, still hadn't renamed the main function in iterwait<commit_after>'use strict';

var timeouts = new Set();

function isthenable (obj) {
	return obj !== undefined && obj !== null && typeof obj === 'object' && 'then' in obj;
}

function delayloop (callback, time) {
	var args = Array.prototype.slice.call(arguments, 2);
	var timeout = setTimeout(delayed, time);

	function delayed () {
		// Cleanup now-expired timeout
		if (timeout) {
			timeouts.delete(timeout);
		}
		// Call function
		callback.apply(null, args);
	}
}

// TODO: more browser-specific checks?
var nextloop = (typeof setImmediate === 'function')
	? setImmediate
	: function (callback) {
		var args = [callback, 0];
		if (arguments.length > 1) {
			args.push.apply(args, Array.prototype.slice.call(arguments, 1));
		}
		return setTimeout.apply(null, args);
	};

function iterstep (iter, func, last, res, rej, time, skip) {
	var val;

	while (!last.done) {
		try {
			val = last.value;

			// Advance loop if not in progress
			if (!skip) {
				last = iter.next(val);
				if (last.done) {
					break;
				}
				val = last.value;

				// Check for promise
				if (isthenable(val)) {
					// Set loop to resume (skipping next()) once promise resolved and break; tack on rejector as catch handler
					return val.then(function (result) {
						return iterstep(iter, func, {value: result, done: last.done}, res, rej, time, true);
					}, rej);
				}
			}
			// Officially in progress
			skip = false;

			// Call function if given
			if (func !== undefined) {
				last.value = val = func(val);

				// Check for promise (again)
				if (isthenable(val)) {
					// Set loop to resume once promise resolved and break; tack on rejector as catch handler
					return val.then(function (result) {
						return iterstep(iter, func, {value: result, done: last.done}, res, rej, time);
					}, rej);
				}
			}

			if (time > 0) {
				// Schedule next iterator step after specified delay
				return delayloop(iterstep, time, iter, func, last, res, rej, time);
			}
			else if (time == 0) {
				// Schedule next iterator step for next event loop
				return nextloop(iterstep, iter, func, last, res, rej, time);
			}
			// else continue loop and get next value
		}
		catch (e) {
			// Reject original promise
			return rej(e);
		}
	};

	if (last.done) {
		return res(last.value);
	}
}

function iterwait (iterable, iterfunc, itertime) {
	// Callable sanity check
	if (iterfunc !== undefined && typeof iterfunc !== 'function') {
		throw new Error(`Paramter 'iterfunc' must be a function or undefined`);
	}

	if (isthenable(iterable)) {
		return iterable.then(function (iter) {
			return iterwait(iter, iterfunc);
		});
	}

	var iterator = iterable[Symbol.iterator]();
	var fakeval = {value: undefined, done: false};

	return new Promise(function (resolve, reject) {
		// Schedule stepper function
		if (itertime >= 0) {
			// Iteration job requested to be delayed per-iteration, so start on next event loop
			nextloop(iterstep, iterator, iterfunc, fakeval, resolve, reject, itertime);
		}
		else {
			// Begin actually-immediately
			iterstep(iterator, iterfunc, fakeval, resolve, reject, itertime);
		}
	});
}

iterwait.timeouts = timeouts;

module.exports = iterwait;
<|endoftext|>


<commit_before>var
    assert  = require('assert'),
    bole    = require('bole'),
    hooks   = require('github-webhook-handler'),
    slacker  = require('./slacker'),
    restify = require('restify')
    ;

var Jthoober = module.exports = function Jthoober(options)
{
    assert(options, 'you must pass an options object to the constructor');
    assert(options.path, 'you must pass a `path` to mount the route on in the options');
    assert(options.secret, 'you must pass a `secret` option');
    assert(options.rules && Array.isArray(options.rules), 'you must pass an array in `rules` in the options');

    this.options = options;
    this.rules = options.rules;
    this.logger = bole('server');

    this.hookHandler = hooks(options);
    this.hookHandler.on('push', this.handleHookPush.bind(this));
    this.hookHandler.on('ping', this.handleHookPing.bind(this));
    this.hookHandler.on('error', this.handleHookError.bind(this));

    this.server = restify.createServer(options);

    this.server.post(options.path, this.handleIncoming.bind(this));
    this.server.get('/ping', this.handlePing.bind(this));
};

Jthoober.prototype.server      = null;
Jthoober.prototype.options     = null;
Jthoober.prototype.rules       = null;
Jthoober.prototype.hookHandler = null;
Jthoober.prototype.logger      = null;

Jthoober.prototype.listen = function(port, host, callback)
{
    this.server.listen(port, host, callback);
};

Jthoober.prototype.handlePing = function(request, response, next)
{
    response.send(200, 'OK');
    next();
};

Jthoober.prototype.handleIncoming = function(request, response, next)
{
    var self = this;
    self.logger.info('webhook event incoming');

    self.hookHandler(request, response, function(err)
    {
        self.logger.warn(err, 'while handling incoming webhook');
    });
    next();
};

Jthoober.prototype.handleHookPush = function(event)
{
    var self = this;
    self.logger.info('handling ' + event.event + ' in repo ' + event.payload.repository.name);

    this.rules.forEach(function(rule)
    {
        if (rule.event === event.event || rule.event === '*')
        {
            if (rule.test(event))
            {
                self.logger.debug('match found; spawning the exec');
                rule.exec(event);

                rule.once('complete', function onRuleComplete(exitCode, errOutput, repo, branch){
                  // if we have a non-0 exit code, there was an error, notify slack with the contents of logger errors
                  if (exitCode){
                      slacker.report(errOutput, rule.slack)
                  }
                  else {
                      slacker.report('Execution complete. ' + repo + ': ' + (branch ? branch + ': ' : '') + rule.event, rule.slack)
                  }
                })
            }
            else {
              self.logger.debug('no match found; ignoring')
            }
        }
    });
};

Jthoober.prototype.handleHookError = function(err)
{
    this.logger.error(err, 'from hook handler');
};

Jthoober.prototype.handleHookPing = function(event)
{
    this.logger.info('got ping from webhook server');
};
<commit_msg>Better ignore message<commit_after>var
    assert  = require('assert'),
    bole    = require('bole'),
    hooks   = require('github-webhook-handler'),
    slacker  = require('./slacker'),
    restify = require('restify')
    ;

var Jthoober = module.exports = function Jthoober(options)
{
    assert(options, 'you must pass an options object to the constructor');
    assert(options.path, 'you must pass a `path` to mount the route on in the options');
    assert(options.secret, 'you must pass a `secret` option');
    assert(options.rules && Array.isArray(options.rules), 'you must pass an array in `rules` in the options');

    this.options = options;
    this.rules = options.rules;
    this.logger = bole('server');

    this.hookHandler = hooks(options);
    this.hookHandler.on('push', this.handleHookPush.bind(this));
    this.hookHandler.on('ping', this.handleHookPing.bind(this));
    this.hookHandler.on('error', this.handleHookError.bind(this));

    this.server = restify.createServer(options);

    this.server.post(options.path, this.handleIncoming.bind(this));
    this.server.get('/ping', this.handlePing.bind(this));
};

Jthoober.prototype.server      = null;
Jthoober.prototype.options     = null;
Jthoober.prototype.rules       = null;
Jthoober.prototype.hookHandler = null;
Jthoober.prototype.logger      = null;

Jthoober.prototype.listen = function(port, host, callback)
{
    this.server.listen(port, host, callback);
};

Jthoober.prototype.handlePing = function(request, response, next)
{
    response.send(200, 'OK');
    next();
};

Jthoober.prototype.handleIncoming = function(request, response, next)
{
    var self = this;
    self.logger.info('webhook event incoming');

    self.hookHandler(request, response, function(err)
    {
        self.logger.warn(err, 'while handling incoming webhook');
    });
    next();
};

Jthoober.prototype.handleHookPush = function(event)
{
    var self = this;
    self.logger.info('handling ' + event.event + ' in repo ' + event.payload.repository.name);

    this.rules.forEach(function(rule)
    {
        if (rule.event === event.event || rule.event === '*')
        {
            if (rule.test(event))
            {
                self.logger.debug('match found; spawning the exec');
                rule.exec(event);

                rule.once('complete', function onRuleComplete(exitCode, errOutput, repo, branch){
                  // if we have a non-0 exit code, there was an error, notify slack with the contents of logger errors
                  if (exitCode){
                      slacker.report(errOutput, rule.slack)
                  }
                  else {
                      slacker.report('Execution complete. ' + repo + ': ' + (branch ? branch + ': ' : '') + rule.event, rule.slack)
                  }
                })
            }
            else {
              self.logger.info(rule.getBranchName(event) + ' does not match; ignoring')
            }
        }
    });
};

Jthoober.prototype.handleHookError = function(err)
{
    this.logger.error(err, 'from hook handler');
};

Jthoober.prototype.handleHookPing = function(event)
{
    this.logger.info('got ping from webhook server');
};
<|endoftext|>


<commit_before>import Ember from 'ember';
const {
  $,
  run,
  computed,
  observer,
  on,
  computed: { alias, sort }
} = Ember;

export default Ember.Component.extend({
  remodal: Ember.inject.service(),
  isFullyEditable: alias('surveyTemplate.fullyEditable'),
  showAnswerChoices: alias('question.answerType.hasAnswerChoices'),
  sortTypesBy: ['displayName'],
  sortAnswerChoicesBy: ['sortOrder'],
  sortedAnswerTypes: sort('filteredAnswerTypes', 'sortTypesBy'),
  sortedAnswerChoices: sort('question.answerChoices', 'sortAnswerChoicesBy'),
  filteredAnswerTypes: computed('answerTypes', function() {
    let answerTypes = this.get('answerTypes');
    if(this.get('isSuperUser')){
      return answerTypes;
    }else{
      return answerTypes.filter((answerType) => {
        return !answerType.get('name').includes('taxon');
      });
    }
  }),
  ancestryQuestions: computed('questions', function() {
    return this.get('questions').filter((question) => {
      let allowedTypes = ['section','repeater'];
      return allowedTypes.includes(question.get('answerType.name'));
    });
  }),
  ancestryQuestion: computed('question.parentId', function() {
    return this.get('questions').findBy('id',this.get('question.parentId'));
  }),

  isRequiredDisabled: computed('question.{rule.isNew,answerType.name}','conditionsPendingSave.[]', function() {
    let question = this.get('question'),
        newRule = question.get('rule.isNew'),
        notTypes = ['section','repeater','helperabove','helperbelow'],
        pendingConditions = this.get('conditionsPendingSave.length') > 0;
    if(newRule === undefined){
      newRule = true;
    }
    let isDisabled = !newRule || pendingConditions || notTypes.includes(question.get('answerType.name'));
    if(isDisabled){
      run.later(this, ()=> { this.set('question.required', false); }, 0);
    }
    return isDisabled;
  }),

  showQuestionTypePlaceholder: computed('question.{isNew,answerType.id}', function() {
    let question = this.get('question');
    return question.get('isNew') && question.get('answerType.id') === undefined;
  }),

  // If a question has a rule associated with it, it should automatically be set to Hidden
  hideQuestion: on('init', observer('question.{rule.isNew}','conditionsPendingSave.[]', function() {
    let question = this.get('question'),
        newRule = question.get('rule.isNew'),
        pendingConditions = this.get('conditionsPendingSave.length') > 0;
    if(newRule === undefined){
      newRule = true;
    }
    let toHide = !newRule || pendingConditions;
    if(toHide && !question.isDeleted){
      run.later(this, ()=> { this.set('question.hidden', true); }, 0);
    }
  })),

  init() {
    this._super(...arguments);
    this.setProperties({
      answerChoicesPendingSave: [],
      conditionsPendingSave: []
    });
  },

  didInsertElement() {
    this._super(...arguments);
    Ember.run.scheduleOnce('afterRender', this, function () {
      this.get('remodal').open('question-modal');
      $('[data-toggle="popover"]').popover({});

    });
    // Tabs
    $('a[data-toggle="tab"]').on('click', function(e) {
      e.preventDefault();
      $(this).tab('show');
    });
  },

  _removeAnswerChoices() {
    this.get('question.answerChoices').then((answerChoices)=>{
      answerChoices.forEach(function(answerChoice) {
        answerChoice.deleteRecord();
        answerChoice.save();
      });
    });
  },

  _pendingObjectsPromises(pendingObjects, toSet, objTo){
    for (var pendingObject of pendingObjects) {
      pendingObject.set(toSet, objTo);
    }
    return pendingObjects.invoke('save');
  },

  _saveSuccess(question, promises, keepOpen){
    question.reload().then((question)=>{
      let answerChoicesPendingSave = this.get('answerChoicesPendingSave'),
          conditionsPendingSave = this.get('conditionsPendingSave');

      if(!question.get('answerType.hasAnswerChoices')){
        this._removeAnswerChoices();
      }
      promises = $.makeArray(promises);
      Ember.RSVP.all(promises).then(()=>{
        while (answerChoicesPendingSave.length > 0) {
          answerChoicesPendingSave.popObject();
        }
        while (conditionsPendingSave.length > 0) {
          conditionsPendingSave.popObject();
        }
        if(keepOpen){
          if(!question.get('rule')){
            let store = question.get('store');
            question.set('rule', store.createRecord('rule'));
          }
        }else{
          this.send('closeModal');
        }
      });
    });
  },

  _sortOrder(question){
    let surveyTemplate = this.get('surveyTemplate'),
        lastQuestion = surveyTemplate.get('questions').sortBy('sortOrder').get('lastObject');
    question.set('sortOrder',1);
    if(lastQuestion && lastQuestion !== question){
      question.set('sortOrder',lastQuestion.get('sortOrder') + 1);
    }
  },

  actions: {
    sortAnswerChoices(){
      let answerChoices = this.get('question.answerChoices');
      answerChoices.forEach((answerChoice, index) => {
        let oldSortOrder = answerChoice.get('sortOrder'),
            newSortOrder = index + 1;
        if(oldSortOrder !== newSortOrder){
          answerChoice.set('sortOrder', newSortOrder);
          if(!answerChoice.get('isNew')){
            answerChoice.save();
          }
        }
      });
    },

    ancestryChange(newAncestryId){
      let question = this.get('question');
      if(Ember.isBlank(newAncestryId)){
        newAncestryId = null;
      }
      question.set('parentId',newAncestryId);
    },

    setAnswerType(answerTypeId) {
      const answerType = this.get('answerTypes').findBy('id', answerTypeId);
      this.set('question.answerType', answerType);
    },

    setRuleMatchType(matchType) {
      this.set('question.rule.matchType', matchType);
    },

    save(_e, keepOpen = false) {
      let question = this.get('question'),
          surveyTemplate = this.get('surveyTemplate');
      question.set('surveyTemplate', surveyTemplate);
      if(question.validate()){
        if(question.get('isNew')){
          question.set('wasNew', true);
          this._sortOrder(question);
        }
        question.save().then(
          (question)=>{
            let promises = [],
                answerChoicesPendingSave = this.get('answerChoicesPendingSave'),
                answerChoicesPromises = this._pendingObjectsPromises(answerChoicesPendingSave, 'question', question);

            promises = promises.concat(answerChoicesPromises);

            // We can't save the rule until there is at least one condition associated with the rule
            if(question.get('rule') && (!question.get('rule.isNew') || this.get('conditionsPendingSave.length') > 0)){
              let conditionsPendingSave = this.get('conditionsPendingSave'),
                  rule = question.get('rule');
              rule.set('question',question);
              rule.save().then(
                (rule) =>{
                  let conditionsPromises = this._pendingObjectsPromises(conditionsPendingSave, 'rule', rule);
                  promises = promises.concat(conditionsPromises);
                  this._saveSuccess(question, promises, keepOpen);
                },
                // Rule was deleted on the server
                () =>{
                  question.get('store').unloadRecord(rule);
                  question.reload().then((question)=>{
                    this._saveSuccess(question, [], keepOpen);
                  });
                }
              );
            }else{
              this._saveSuccess(question, promises, keepOpen);
            }
          },
          (error)=>{
            console.error(`An error has occured: ${error}.`);
            surveyTemplate.get('questions').removeObject(question);
          }
        );
      }
    },

    saveCondition(condition){
      if(this.get('question.isNew') || this.get('question.rule.isNew')){
        if(this.get('conditionsPendingSave').indexOf(condition) === -1){
          this.get('conditionsPendingSave').pushObject(condition);
        }
      }else{
        condition.save();
      }
    },

    removeCondition(condition){
      if(this.get('question.rule.isNew') || condition.get('isNew')){
        this.get('conditionsPendingSave').removeObject(condition);
        condition.deleteRecord();
      }else{
        condition.deleteRecord();
        condition.save();
      }
    },

    saveAnswerChoice(answerChoice){
      if(this.get('question.isNew')){
        if(this.get('answerChoicesPendingSave').indexOf(answerChoice) === -1){
          this.get('answerChoicesPendingSave').pushObject(answerChoice);
        }
      }else{
        answerChoice.save();
      }
    },

    closeModal() {
      if(this.get('question.wasNew')){
        run.later(this, ()=> { $('html, body').animate({ scrollTop: $(document).height() }, 500); }, 500);
      }
      this.get('remodal').close('question-modal');
      this.sendAction('transitionToSurveyStep');
    }
  }
});
<commit_msg>Fix computed call.<commit_after>import Ember from 'ember';
const {
  $,
  run,
  computed,
  observer,
  on,
  computed: { alias, sort }
} = Ember;

export default Ember.Component.extend({
  remodal: Ember.inject.service(),
  isFullyEditable: alias('surveyTemplate.fullyEditable'),
  showAnswerChoices: alias('question.answerType.hasAnswerChoices'),
  sortTypesBy: ['displayName'],
  sortAnswerChoicesBy: ['sortOrder'],
  sortedAnswerTypes: sort('filteredAnswerTypes', 'sortTypesBy'),
  sortedAnswerChoices: sort('question.answerChoices', 'sortAnswerChoicesBy'),
  filteredAnswerTypes: computed('answerTypes', function() {
    let answerTypes = this.get('answerTypes');
    if(this.get('isSuperUser')){
      return answerTypes;
    }else{
      return answerTypes.filter((answerType) => {
        return !answerType.get('name').includes('taxon');
      });
    }
  }),
  ancestryQuestions: computed('questions', function() {
    return this.get('questions').filter((question) => {
      let allowedTypes = ['section','repeater'];
      return allowedTypes.includes(question.get('answerType.name'));
    });
  }),
  ancestryQuestion: computed('question.parentId', function() {
    return this.get('questions').findBy('id',this.get('question.parentId'));
  }),

  isRequiredDisabled: computed('question.{rule.isNew,answerType.name}','conditionsPendingSave.[]', function() {
    let question = this.get('question'),
        newRule = question.get('rule.isNew'),
        notTypes = ['section','repeater','helperabove','helperbelow'],
        pendingConditions = this.get('conditionsPendingSave.length') > 0;
    if(newRule === undefined){
      newRule = true;
    }
    let isDisabled = !newRule || pendingConditions || notTypes.includes(question.get('answerType.name'));
    if(isDisabled){
      run.later(this, ()=> { this.set('question.required', false); }, 0);
    }
    return isDisabled;
  }),

  showQuestionTypePlaceholder: computed('question.{isNew,answerType.id}', function() {
    let question = this.get('question');
    return question.get('isNew') && question.get('answerType.id') === undefined;
  }),

  // If a question has a rule associated with it, it should automatically be set to Hidden
  hideQuestion: on('afterRender', observer('question.{rule.isNew}','conditionsPendingSave.[]', function() {
    let question = this.get('question'),
        newRule = question.get('rule.isNew'),
        pendingConditions = this.get('conditionsPendingSave.length') > 0;
    if(newRule === undefined){
      newRule = true;
    }

    let toHide = !newRule || pendingConditions;
    if(toHide){
      this.set('question.hidden', true);
    }
  })),

  init() {
    this._super(...arguments);
    this.setProperties({
      answerChoicesPendingSave: [],
      conditionsPendingSave: []
    });
  },

  didInsertElement() {
    this._super(...arguments);
    Ember.run.scheduleOnce('afterRender', this, function () {
      this.get('remodal').open('question-modal');
      $('[data-toggle="popover"]').popover({});

    });
    // Tabs
    $('a[data-toggle="tab"]').on('click', function(e) {
      e.preventDefault();
      $(this).tab('show');
    });
  },

  _removeAnswerChoices() {
    this.get('question.answerChoices').then((answerChoices)=>{
      answerChoices.forEach(function(answerChoice) {
        answerChoice.deleteRecord();
        answerChoice.save();
      });
    });
  },

  _pendingObjectsPromises(pendingObjects, toSet, objTo){
    for (var pendingObject of pendingObjects) {
      pendingObject.set(toSet, objTo);
    }
    return pendingObjects.invoke('save');
  },

  _saveSuccess(question, promises, keepOpen){
    question.reload().then((question)=>{
      let answerChoicesPendingSave = this.get('answerChoicesPendingSave'),
          conditionsPendingSave = this.get('conditionsPendingSave');

      if(!question.get('answerType.hasAnswerChoices')){
        this._removeAnswerChoices();
      }
      promises = $.makeArray(promises);
      Ember.RSVP.all(promises).then(()=>{
        while (answerChoicesPendingSave.length > 0) {
          answerChoicesPendingSave.popObject();
        }
        while (conditionsPendingSave.length > 0) {
          conditionsPendingSave.popObject();
        }
        if(keepOpen){
          if(!question.get('rule')){
            let store = question.get('store');
            question.set('rule', store.createRecord('rule'));
          }
        }else{
          this.send('closeModal');
        }
      });
    });
  },

  _sortOrder(question){
    let surveyTemplate = this.get('surveyTemplate'),
        lastQuestion = surveyTemplate.get('questions').sortBy('sortOrder').get('lastObject');
    question.set('sortOrder',1);
    if(lastQuestion && lastQuestion !== question){
      question.set('sortOrder',lastQuestion.get('sortOrder') + 1);
    }
  },

  actions: {
    sortAnswerChoices(){
      let answerChoices = this.get('question.answerChoices');
      answerChoices.forEach((answerChoice, index) => {
        let oldSortOrder = answerChoice.get('sortOrder'),
            newSortOrder = index + 1;
        if(oldSortOrder !== newSortOrder){
          answerChoice.set('sortOrder', newSortOrder);
          if(!answerChoice.get('isNew')){
            answerChoice.save();
          }
        }
      });
    },

    ancestryChange(newAncestryId){
      let question = this.get('question');
      if(Ember.isBlank(newAncestryId)){
        newAncestryId = null;
      }
      question.set('parentId',newAncestryId);
    },

    setAnswerType(answerTypeId) {
      const answerType = this.get('answerTypes').findBy('id', answerTypeId);
      this.set('question.answerType', answerType);
    },

    setRuleMatchType(matchType) {
      this.set('question.rule.matchType', matchType);
    },

    save(_e, keepOpen = false) {
      let question = this.get('question'),
          surveyTemplate = this.get('surveyTemplate');
      question.set('surveyTemplate', surveyTemplate);
      if(question.validate()){
        if(question.get('isNew')){
          question.set('wasNew', true);
          this._sortOrder(question);
        }
        question.save().then(
          (question)=>{
            let promises = [],
                answerChoicesPendingSave = this.get('answerChoicesPendingSave'),
                answerChoicesPromises = this._pendingObjectsPromises(answerChoicesPendingSave, 'question', question);

            promises = promises.concat(answerChoicesPromises);

            // We can't save the rule until there is at least one condition associated with the rule
            if(question.get('rule') && (!question.get('rule.isNew') || this.get('conditionsPendingSave.length') > 0)){
              let conditionsPendingSave = this.get('conditionsPendingSave'),
                  rule = question.get('rule');
              rule.set('question',question);
              rule.save().then(
                (rule) =>{
                  let conditionsPromises = this._pendingObjectsPromises(conditionsPendingSave, 'rule', rule);
                  promises = promises.concat(conditionsPromises);
                  this._saveSuccess(question, promises, keepOpen);
                },
                // Rule was deleted on the server
                () =>{
                  question.get('store').unloadRecord(rule);
                  question.reload().then((question)=>{
                    this._saveSuccess(question, [], keepOpen);
                  });
                }
              );
            }else{
              this._saveSuccess(question, promises, keepOpen);
            }
          },
          (error)=>{
            console.error(`An error has occured: ${error}.`);
            surveyTemplate.get('questions').removeObject(question);
          }
        );
      }
    },

    saveCondition(condition){
      if(this.get('question.isNew') || this.get('question.rule.isNew')){
        if(this.get('conditionsPendingSave').indexOf(condition) === -1){
          this.get('conditionsPendingSave').pushObject(condition);
        }
      }else{
        condition.save();
      }
    },

    removeCondition(condition){
      if(this.get('question.rule.isNew') || condition.get('isNew')){
        this.get('conditionsPendingSave').removeObject(condition);
        condition.deleteRecord();
      }else{
        condition.deleteRecord();
        condition.save();
      }
    },

    saveAnswerChoice(answerChoice){
      if(this.get('question.isNew')){
        if(this.get('answerChoicesPendingSave').indexOf(answerChoice) === -1){
          this.get('answerChoicesPendingSave').pushObject(answerChoice);
        }
      }else{
        answerChoice.save();
      }
    },

    closeModal() {
      if(this.get('question.wasNew')){
        run.later(this, ()=> { $('html, body').animate({ scrollTop: $(document).height() }, 500); }, 500);
      }
      this.get('remodal').close('question-modal');
      this.sendAction('transitionToSurveyStep');
    }
  }
});
<|endoftext|>


<commit_before>import React from 'react'
import i18n from 'app/utils/i18n'
import Selector from 'app/components/selector'
import Flash from 'app/flash'
import GenericForm from 'app/components/genericform'
import Loading from 'app/components/loading'
import cache from 'app/utils/cache'
import plugin from 'app/utils/plugin'
import {MarkdownPreview} from 'react-marked-markdown'
import ServiceSelect from 'app/components/service/select'
import {goto} from 'app/utils/store'
import Tip from 'app/components/tip'
import utils from 'app/utils'

export class AddServiceDetailsForm extends React.Component{
  constructor(props){
    super(props)
    this.state={
      data: {},
      name: "",
      descritpion: ""
    }

    this.handleAddService = () => {
      const props = this.props
      const state = this.state
      const service = {
        name: state.name,
        type: props.service.type,
        description: state.description,
        config: state.data
      }
      return props
        .onAddService(props.project, service)
    }
  }
  render(){
    const {service, gotoStep, saveButtons} = this.props
    return (

        <MarkdownPreview value={service.description}/>

        <form className="ui form">
          {i18n("Service name")}
          <input
            onChange={(ev) => this.setState({name: ev.target.value})}
            defaultValue={this.state.name}
            />
          {i18n("Description")}
          <textarea
            onChange={(ev) => this.setState({description: ev.target.value})}
            defaultValue={this.state.description}
            />
        </form>

        <GenericForm fields={service.fields} updateForm={(data) => this.setState({data})}/>

            <button
              className="ui button basic"
              onClick={() => gotoStep(1)}>
                {i18n("Back")}
            </button>
            { saveButtons ? (
                saveButtons.map( sb => (
                  <button key={sb.label} type="button" className={`ui button ${sb.className}`}
                      onClick={() => this.handleAddService().then( (data) => sb.onClick && sb.onClick(data) )}
                      >
                    {sb.label}
                  </button>
                ))
            ) : (
              <button
                className="ui teal button"
                onClick={this.handleAddService}>
                  {i18n("Save and Continue")}
              </button>
            ) }

    )
  }
}

function AddServiceButton({service}){
  return (
    {i18n("Attach")}
  )
}

class AddServiceRouter extends React.Component{
  constructor(props){
    super(props)
    this.state={
      tab: 1
    }

    this.handleAttachService = (uuid) => {
      props
        .onAttachService(props.project, uuid)
        .then( () => goto(`/project/${props.project}/services/${uuid}`))
    }
  }
  componentDidMount(){
    let self = this
    $(this.refs.checkboxes).find('.checkbox').checkbox({
      onChange(ev){
        console.log("Enable %o", this)
        self.setState({tab: this.value})
      }
    })
  }
  render(){
    const props = this.props
    const tab = this.state.tab
    const {service} = props
    const my_type = service.type
    const my_project = props.project
    return (

{i18n("Add {service_type} service to project", {service_type: service.name})}

                  <input name="new_or_create" value="1" type="radio" checked={tab==1 && "checked"}/>
                  {i18n("Create new")}

              {!props.hide_old && (

                    <input name="new_or_create" value="2" type="radio" checked={tab==2 && "checked"}/>
                    {i18n("Select existing")}

              )}

        {tab == 1 ? (
          <AddServiceDetailsForm {...props}/>
        ) : (

              {i18n("This services are used on other projects but may be attached to the current one, so they will be shared between the projects.")}

            <ServiceSelect
              filter={(s) => s.type == my_type && s.projects.indexOf(my_project)<0 }
              onBack={() => gotoStep(1)}
              onSelect={(s) => this.handleAttachService(s.uuid)}
              bottomElement={AddServiceButton}
              />

        )}

    )
  }
}

function get_service_market_catalog(){
  return Promise.all([
      plugin.start_call_stop(
            "serverboards.optional.update/catalog",
            "component_filter",
            {type: "service"}
          )
      , cache.plugins()
    ]).then( (cp) => {
      const catalog = cp[0]
      const plugin_list = cp[1]

      console.log("Got catalog %o // %o", catalog, plugin_list)
      return catalog.filter( c => !plugin_list[c.plugin] )
    })
}

class ServiceAddRouter extends React.Component{
  constructor(props){
    super(props)
    this.state={
      tab: 1,
      filter: []
    }
  }
  filter(s){
    let type_filter = this.props.filter || {}
    if (type_filter.traits){
      const check = {has: s.traits, all: type_filter.traits}
      if (!utils.match_traits(check))
        return false
    }
    let desc = `${s.name || ""} ${s.description}`.toLocaleLowerCase()
    for (let f of this.state.filter){
      if (desc.indexOf(f)<0)
        return false
    }
    return true
  }
  render(){
    const props = this.props
    const state = this.state
    const tab= state.tab
    return (

            <input
              type="text"
              onChange={(ev) => {
                this.setState({filter:ev.target.value.toLocaleLowerCase().split(' ')})
              }}
              placeholder={i18n("Filter...")}
              />

            <i className={`icon cloud`}/>
            {i18n("Add a service to this project")}

            {i18n("First select the service type. If not available at the already installed services, there are many more available to install at the marketplace.")}

          <a className={`item ${tab==1 ? "active" : ""}`} onClick={() => this.setState({tab:1})}>
            {i18n("Available services")}

          <a className={`item ${tab==2 ? "active" : ""}`} onClick={() => this.setState({tab:2})}>
            {i18n("Marketplace")}

        { tab==1 ? (
          <Selector
            key="installed"
            show_filter={false}
            filter={this.filter.bind(this)}
            get_items={cache.service_catalog}
            onSelect={(what) => props.onSelectServiceType(what)}
            current={(props.service || {}).type}
            onSkip={props.onSkip}
            skip_label={props.skip_label}
            prevStep={props.prevStep}
            prev_label={props.prev_label}
            />
        ) : (tab == 2) ? (
          <Selector
            key="marketplace"
            show_filter={false}
            filter={this.filter.bind(this)}
            get_items={get_service_market_catalog}
            current={(props.service || {}).type}
            onSelect={(s) => {
              this.setState({tab:3})
              plugin.install(s.giturl).then(() => {
                s = {...s, type: s.id} // I need the component id in the type field.
                props.onSelectServiceType(s)
              }).catch((e) => {
                console.error(e)
                this.setState({tab:2})
                Flash.error(i18n("Error installing *{plugin}*. Please try again or check logs.", {plugin:s.name}))
              })
            }}
            onSkip={props.onSkip}
            skip_label={props.skip_label}
            prevStep={props.prevStep}
            prev_label={props.prev_label}
            />
        ) : (
          <Loading>{i18n("Installing the required add-on")}</Loading>
        ) }

    )
  }
}

class AddService extends React.Component{
  constructor(props){
    super(props)
    this.state={
      step: 1,
      service: undefined,
    }
  }
  handleSelectServiceType(service){
    this.setState({step:2, service})
  }
  render(){
    var section = null
    switch (this.state.step){
      case 1:
        section = (
          <ServiceAddRouter
            onSelectServiceType={(s) => this.handleSelectServiceType(s)}
            service={this.state.service}
            {...this.props}
            />
        )
        break;
      case 2:
        section = (
          <AddServiceRouter
            service={this.state.service}
            gotoStep={(step) => this.setState({step})}
            project={this.props.project.shortname}
            onAddService={this.props.onAddService}
            onAttachService={this.props.onAttachService}
            hide_old={this.props.hide_old}
            saveButtons={this.props.saveButtons}
            />)
        break;
    }

    return (

          <Tip
            className="ui round pane white background with padding"
            top_img={require("imgs/024-illustration-addaddons.svg")}
            title={i18n("Add Services to your project.")}
            middle_img={require("imgs/019-illustration-tips.svg")}
            subtitle={i18n("Add services you are already subscribed to manage, monitor or automate tasks with them.")}
            description={i18n(`
Serverboards core elements are services. Services are definitions of how to connect
to services and servers. This may mean required credentials, url addresses and so
on.

Here you can select the type of service you want to add, or check out at the store
to find more service types to use in Serverboards.
`)}
              />

            {section}

    )
  }
}

export default AddService
<commit_msg>Select existing service has proper scrollbars<commit_after>import React from 'react'
import i18n from 'app/utils/i18n'
import Selector from 'app/components/selector'
import Flash from 'app/flash'
import GenericForm from 'app/components/genericform'
import Loading from 'app/components/loading'
import cache from 'app/utils/cache'
import plugin from 'app/utils/plugin'
import {MarkdownPreview} from 'react-marked-markdown'
import ServiceSelect from 'app/components/service/select'
import {goto} from 'app/utils/store'
import Tip from 'app/components/tip'
import utils from 'app/utils'

export class AddServiceDetailsForm extends React.Component{
  constructor(props){
    super(props)
    this.state={
      data: {},
      name: "",
      descritpion: ""
    }

    this.handleAddService = () => {
      const props = this.props
      const state = this.state
      const service = {
        name: state.name,
        type: props.service.type,
        description: state.description,
        config: state.data
      }
      return props
        .onAddService(props.project, service)
    }
  }
  render(){
    const {service, gotoStep, saveButtons} = this.props
    return (

        <MarkdownPreview value={service.description}/>

        <form className="ui form">
          {i18n("Service name")}
          <input
            onChange={(ev) => this.setState({name: ev.target.value})}
            defaultValue={this.state.name}
            />
          {i18n("Description")}
          <textarea
            onChange={(ev) => this.setState({description: ev.target.value})}
            defaultValue={this.state.description}
            />
        </form>

        <GenericForm fields={service.fields} updateForm={(data) => this.setState({data})}/>

            <button
              className="ui button basic"
              onClick={() => gotoStep(1)}>
                {i18n("Back")}
            </button>
            { saveButtons ? (
                saveButtons.map( sb => (
                  <button key={sb.label} type="button" className={`ui button ${sb.className}`}
                      onClick={() => this.handleAddService().then( (data) => sb.onClick && sb.onClick(data) )}
                      >
                    {sb.label}
                  </button>
                ))
            ) : (
              <button
                className="ui teal button"
                onClick={this.handleAddService}>
                  {i18n("Save and Continue")}
              </button>
            ) }

    )
  }
}

function AddServiceButton({service}){
  return (
    {i18n("Attach")}
  )
}

class AddServiceRouter extends React.Component{
  constructor(props){
    super(props)
    this.state={
      tab: 1
    }

    this.handleAttachService = (uuid) => {
      props
        .onAttachService(props.project, uuid)
        .then( () => goto(`/project/${props.project}/services/${uuid}`))
    }
  }
  componentDidMount(){
    let self = this
    $(this.refs.checkboxes).find('.checkbox').checkbox({
      onChange(ev){
        console.log("Enable %o", this)
        self.setState({tab: this.value})
      }
    })
  }
  render(){
    const props = this.props
    const tab = this.state.tab
    const {service} = props
    const my_type = service.type
    const my_project = props.project
    return (

{i18n("Add {service_type} service to project", {service_type: service.name})}

                  <input name="new_or_create" value="1" type="radio" checked={tab==1 && "checked"}/>
                  {i18n("Create new")}

              {!props.hide_old && (

                    <input name="new_or_create" value="2" type="radio" checked={tab==2 && "checked"}/>
                    {i18n("Select existing")}

              )}

        {tab == 1 ? (
          <AddServiceDetailsForm {...props}/>
        ) : (

              {i18n("This services are used on other projects but may be attached to the current one, so they will be shared between the projects.")}

              <ServiceSelect
                filter={(s) => s.type == my_type && s.projects.indexOf(my_project)<0 }
                onBack={() => gotoStep(1)}
                onSelect={(s) => this.handleAttachService(s.uuid)}
                bottomElement={AddServiceButton}
                />

        )}

    )
  }
}

function get_service_market_catalog(){
  return Promise.all([
      plugin.start_call_stop(
            "serverboards.optional.update/catalog",
            "component_filter",
            {type: "service"}
          )
      , cache.plugins()
    ]).then( (cp) => {
      const catalog = cp[0]
      const plugin_list = cp[1]

      console.log("Got catalog %o // %o", catalog, plugin_list)
      return catalog.filter( c => !plugin_list[c.plugin] )
    })
}

class ServiceAddRouter extends React.Component{
  constructor(props){
    super(props)
    this.state={
      tab: 1,
      filter: []
    }
  }
  filter(s){
    let type_filter = this.props.filter || {}
    if (type_filter.traits){
      const check = {has: s.traits, all: type_filter.traits}
      if (!utils.match_traits(check))
        return false
    }
    let desc = `${s.name || ""} ${s.description}`.toLocaleLowerCase()
    for (let f of this.state.filter){
      if (desc.indexOf(f)<0)
        return false
    }
    return true
  }
  render(){
    const props = this.props
    const state = this.state
    const tab= state.tab
    return (

            <input
              type="text"
              onChange={(ev) => {
                this.setState({filter:ev.target.value.toLocaleLowerCase().split(' ')})
              }}
              placeholder={i18n("Filter...")}
              />

            <i className={`icon cloud`}/>
            {i18n("Add a service to this project")}

            {i18n("First select the service type. If not available at the already installed services, there are many more available to install at the marketplace.")}

          <a className={`item ${tab==1 ? "active" : ""}`} onClick={() => this.setState({tab:1})}>
            {i18n("Available services")}

          <a className={`item ${tab==2 ? "active" : ""}`} onClick={() => this.setState({tab:2})}>
            {i18n("Marketplace")}

        { tab==1 ? (
          <Selector
            key="installed"
            show_filter={false}
            filter={this.filter.bind(this)}
            get_items={cache.service_catalog}
            onSelect={(what) => props.onSelectServiceType(what)}
            current={(props.service || {}).type}
            onSkip={props.onSkip}
            skip_label={props.skip_label}
            prevStep={props.prevStep}
            prev_label={props.prev_label}
            />
        ) : (tab == 2) ? (
          <Selector
            key="marketplace"
            show_filter={false}
            filter={this.filter.bind(this)}
            get_items={get_service_market_catalog}
            current={(props.service || {}).type}
            onSelect={(s) => {
              this.setState({tab:3})
              plugin.install(s.giturl).then(() => {
                s = {...s, type: s.id} // I need the component id in the type field.
                props.onSelectServiceType(s)
              }).catch((e) => {
                console.error(e)
                this.setState({tab:2})
                Flash.error(i18n("Error installing *{plugin}*. Please try again or check logs.", {plugin:s.name}))
              })
            }}
            onSkip={props.onSkip}
            skip_label={props.skip_label}
            prevStep={props.prevStep}
            prev_label={props.prev_label}
            />
        ) : (
          <Loading>{i18n("Installing the required add-on")}</Loading>
        ) }

    )
  }
}

class AddService extends React.Component{
  constructor(props){
    super(props)
    this.state={
      step: 1,
      service: undefined,
    }
  }
  handleSelectServiceType(service){
    this.setState({step:2, service})
  }
  render(){
    var section = null
    switch (this.state.step){
      case 1:
        section = (
          <ServiceAddRouter
            onSelectServiceType={(s) => this.handleSelectServiceType(s)}
            service={this.state.service}
            {...this.props}
            />
        )
        break;
      case 2:
        section = (
          <AddServiceRouter
            service={this.state.service}
            gotoStep={(step) => this.setState({step})}
            project={this.props.project.shortname}
            onAddService={this.props.onAddService}
            onAttachService={this.props.onAttachService}
            hide_old={this.props.hide_old}
            saveButtons={this.props.saveButtons}
            />)
        break;
    }

    return (

          <Tip
            className="ui round pane white background with padding"
            top_img={require("imgs/024-illustration-addaddons.svg")}
            title={i18n("Add Services to your project.")}
            middle_img={require("imgs/019-illustration-tips.svg")}
            subtitle={i18n("Add services you are already subscribed to manage, monitor or automate tasks with them.")}
            description={i18n(`
Serverboards core elements are services. Services are definitions of how to connect
to services and servers. This may mean required credentials, url addresses and so
on.

Here you can select the type of service you want to add, or check out at the store
to find more service types to use in Serverboards.
`)}
              />

            {section}

    )
  }
}

export default AddService
<|endoftext|>


<commit_before>var exec = require('child_process').exec;
var path = require('path');
var log = require('./logger').create('launcher');
var env = process.env;
var fs = require('fs');
var util = require('./util');

var counter = 1;

// inspired by https://github.com/admc/jellyfish/blob/master/lib/browsers.js
var Browser = function(id) {

  var exitCallback = function() {};

  this._getCommand = function() {
    var cmd = env[this.ENV_CMD] || this.DEFAULT_CMD[process.platform];
    return '"' + path.normalize(cmd) + '"';
  };

  this._execCommand = function(cmd) {
    // TODO(vojta): show some errors if cmd does not exists...
    log.debug(cmd);
    this._process = exec(cmd, function(e) {
      if (e && !e.killed) {
        log.error(e);
      }
    });

    var tempDir = this._tempDir;
    this._process.on('exit', function() {
      log.debug('Cleaning the profile at %s', tempDir);
      exec('rm -rf "' + tempDir + '"', exitCallback);
    });
  };

  this._getOptions = function(url) {
    return [url];
  };

  this._tempDir = path.normalize((env.TMPDIR || env.TMP || env.TEMP || '/tmp') + '/testacular-' + id.toString());

  try {
    log.debug('Creating temp dir at ' + this._tempDir);
    fs.mkdirSync(this._tempDir);
  } catch (e) {}

  this.start = function(url) {
    this._execCommand(this._getCommand() + ' ' + this._getOptions(url).join(' '));
  };

  this.kill = function(callback) {
    exitCallback = callback || function() {};

    if (this._process.exitCode === null) {
      this._process.kill();
    } else {
      process.nextTick(exitCallback);
    }
  };
};

var ChromeBrowser = function() {
  Browser.apply(this, arguments);

  this._getOptions = function(url) {
    // Chrome CLI options
    // http://peter.sh/experiments/chromium-command-line-switches/
    return [
      '--user-data-dir=' + this._tempDir,
      '--no-default-browser-check',
      '--no-first-run',
      '--disable-default-apps',
      url
    ];
  };
};

ChromeBrowser.prototype = {
  name: 'Chrome',

  DEFAULT_CMD: {
    linux: '/usr/bin/google-chrome',
    darwin: '/Applications/Google Chrome.app/Contents/MacOS/Google Chrome',
    win32: process.env.LOCALAPPDATA + '\\Google\\Chrome\\Application\\chrome.exe'
  },
  ENV_CMD: 'CHROME_BIN'
};

var ChromeCanaryBrowser = function() {
  ChromeBrowser.apply(this, arguments);

  var parentOptions = this._getOptions;
  this._getOptions = function(url) {
    // disable crankshaft optimizations, as it causes lot of memory leaks (as of Chrome 23.0)
    return parentOptions.call(this, url).concat(['--js-flags="--nocrankshaft --noopt"']);
  };
};

ChromeCanaryBrowser.prototype = {
  name: 'ChromeCanary',

  DEFAULT_CMD: {
    linux: '/usr/bin/google-chrome-canary',
    darwin: '/Applications/Google Chrome Canary.app/Contents/MacOS/Google Chrome Canary',
    win32: process.env.LOCALAPPDATA + '\\Google\\Chrome SxS\\Application\\chrome.exe'
  },
  ENV_CMD: 'CHROME_CANARY_BIN'
};

var FirefoxBrowser = function() {
  Browser.apply(this, arguments);

  this.start = function(url) {
    var self = this;
    var command = this._getCommand();

    // create profile first
    exec(command + ' -CreateProfile testacular', function(e, stdout, stderr) {
      if (e) {
        log.error(e);
      }

      var match = /at\s\'(.*)\/prefs\.js\'/.exec(stderr.toString());

      if (match) {
        var profile = self._tempDir = match[1];
        var prefs = 'user_pref("browser.shell.checkDefaultBrowser", false);\n' +
                    'user_pref("browser.bookmarks.restore_default_bookmarks", false);\n';

        fs.createWriteStream(profile + '/prefs.js', {flags: 'a'}).write(prefs);
        self._execCommand(command + ' -profile "' + profile + '" ' + url);
      } else {
        log.warn('Cannot create Firefox profile');
        self._execCommand(command + ' ' + url);
      }
    });
  };
};

FirefoxBrowser.prototype = {
  name: 'Firefox',

  DEFAULT_CMD: {
    linux: '/usr/bin/firefox',
    darwin: '/Applications/Firefox.app/Contents/MacOS/firefox-bin',
    win32: process.env.ProgramFiles + '\\Mozilla Firefox\\firefox.exe'
  },
  ENV_CMD: 'FIREFOX_BIN'
};

var OperaBrowser = function() {
  Browser.apply(this, arguments);

  this._getOptions = function(url) {
    // Opera CLI options
    // http://www.opera.com/docs/switches/
    return [
      '-pd ' + this._tempDir,
      '-nomail',
      url
    ];
  };

  this.start = function(url) {
    var self = this;
    var prefs = 'Opera Preferences version 2.1\n' +
      '[User Prefs]\n' +
      'Show Default Browser Dialog=0\n' +
      'Recovery Strategy=2\n' +
      'Home URL=about:blank\n' +
      'Show Close All But Active Dialog=0\n' +
      'Show Close All Dialog=0\n' +
      'Show Crash Log Upload Dialog=0\n' +
      'Show Delete Mail Dialog=0\n' +
      'Show Download Manager Selection Dialog=0\n' +
      'Show Geolocation License Dialog=0\n' +
      'Show Mail Error Dialog=0\n' +
      'Show New Opera Dialog=0\n' +
      'Show Problem Dialog=0\n' +
      'Show Progress Dialog=0\n' +
      'Show Validation Dialog=0\n' +
      'Show Widget Debug Info Dialog=0\n' +
      'Show Startup Dialog=0\n' +
      'Enable Usage Statistics=0\n' +
      '[Install]\n' +
      'Newest Used Version=1.00.0000\n';

    var prefsFile = this._tempDir + '/operaprefs.ini';
    fs.writeFile(prefsFile, prefs, function(err) {
      // TODO(vojta): handle error
      self._execCommand(self._getCommand() + ' ' + self._getOptions(url).join(' '));
    });
  };
};

OperaBrowser.prototype = {
  name: 'Opera',

  DEFAULT_CMD: {
    linux: '/usr/bin/opera',
    darwin: '/Applications/Opera.app/Contents/MacOS/Opera',
    win32: process.env.ProgramFiles + '\\Opera\\opera.exe'
  },
  ENV_CMD: 'OPERA_BIN'
};

var SafariBrowser = function() {
  Browser.apply(this, arguments);

  this.start = function(url) {
    var HTML_TPL = path.normalize(__dirname + '/../static/safari.html');
    var self = this;

    fs.readFile(HTML_TPL, function(err, data) {
      var content = data.toString().replace('%URL%', url);
      var staticHtmlPath = self._tempDir + '/redirect.html';

      fs.writeFile(staticHtmlPath, content, function(err) {
        self._execCommand(self._getCommand() + ' ' + staticHtmlPath);
      });
    });
  };
};

SafariBrowser.prototype = {
  name: 'Safari',

  DEFAULT_CMD: {
    darwin: '/Applications/Safari.app/Contents/MacOS/Safari'
  },
  ENV_CMD: 'SAFARI_BIN'
};

var PhantomJSBrowser = function() {
  Browser.apply(this, arguments);

  this.start = function(url) {
    // create the js file, that will open testacular
    var captureFile = this._tempDir + '/capture.js';
    var captureCode = '(new WebPage()).open("' + url + '");';
    fs.createWriteStream(captureFile).end(captureCode);

    // and start phantomjs
    this._execCommand(this._getCommand() + ' ' + captureFile);
  };
};

PhantomJSBrowser.prototype = {
  name: 'PhantomJS',

  DEFAULT_CMD: {
    linux: process.env.TRAVIS ? 'phantomjs' : '/usr/bin/phantomjs',
    darwin: '/usr/local/bin/phantomjs'
  },
  ENV_CMD: 'PHANTOMJS_BIN'
};

var Launcher = function() {
  var browsers = [];

  this.launch = function(names, port, urlRoot) {
    var url = 'http://localhost:' + port + urlRoot;
    var Cls, browser, id;

    names.forEach(function(nameOrCls) {
      id = counter++;

      if (util.isString(nameOrCls)) {
        Cls = exports[nameOrCls + 'Browser'];
        if (!Cls) {
          throw new Error('Browser "' + nameOrCls + '" does not exists');
        }
      } else if (util.isFunction(nameOrCls)) {
        Cls = nameOrCls;
      }

      browser = new Cls(id);
      browser.id = id;
      browser.isCaptured = false;

      log.info('Starting  browser "%s"', browser.name || 'Custom');
      browser.start(url + '?id=' + browser.id);
      browsers.push(browser);
    });
  };

  this.kill = function(callback) {
    var remaining = 0;
    var finish = function() {
      remaining--;
      if (!remaining && callback) {
        callback();
      }
    };

    if (!browsers.length) {
      return process.nextTick(callback);
    }

    browsers.forEach(function(browser) {
      remaining++;
      browser.kill(finish);
    });
  };

  this.areAllCaptured = function() {
    return !browsers.some(function(browser) {
      return !browser.isCaptured;
    });
  };

  this.markCaptured = function(id) {
    browsers.forEach(function(browser) {
      if (browser.id === id) {
        browser.isCaptured = true;
      }
    });
  };
};

exports.Launcher = Launcher;
exports.BaseBrowser = Browser;
exports.ChromeBrowser = ChromeBrowser;
exports.ChromeCanaryBrowser = ChromeCanaryBrowser;
exports.FirefoxBrowser = FirefoxBrowser;
exports.OperaBrowser = OperaBrowser;
exports.SafariBrowser = SafariBrowser;
exports.PhantomJSBrowser = PhantomJSBrowser;
<commit_msg>Disable some more stuff in Opera<commit_after>var exec = require('child_process').exec;
var path = require('path');
var log = require('./logger').create('launcher');
var env = process.env;
var fs = require('fs');
var util = require('./util');

var counter = 1;

// inspired by https://github.com/admc/jellyfish/blob/master/lib/browsers.js
var Browser = function(id) {

  var exitCallback = function() {};

  this._getCommand = function() {
    var cmd = env[this.ENV_CMD] || this.DEFAULT_CMD[process.platform];
    return '"' + path.normalize(cmd) + '"';
  };

  this._execCommand = function(cmd) {
    // TODO(vojta): show some errors if cmd does not exists...
    log.debug(cmd);
    this._process = exec(cmd, function(e) {
      if (e && !e.killed) {
        log.error(e);
      }
    });

    var tempDir = this._tempDir;
    this._process.on('exit', function() {
      log.debug('Cleaning the profile at %s', tempDir);
      exec('rm -rf "' + tempDir + '"', exitCallback);
    });
  };

  this._getOptions = function(url) {
    return [url];
  };

  this._tempDir = path.normalize((env.TMPDIR || env.TMP || env.TEMP || '/tmp') + '/testacular-' + id.toString());

  try {
    log.debug('Creating temp dir at ' + this._tempDir);
    fs.mkdirSync(this._tempDir);
  } catch (e) {}

  this.start = function(url) {
    this._execCommand(this._getCommand() + ' ' + this._getOptions(url).join(' '));
  };

  this.kill = function(callback) {
    exitCallback = callback || function() {};

    if (this._process.exitCode === null) {
      this._process.kill();
    } else {
      process.nextTick(exitCallback);
    }
  };
};

var ChromeBrowser = function() {
  Browser.apply(this, arguments);

  this._getOptions = function(url) {
    // Chrome CLI options
    // http://peter.sh/experiments/chromium-command-line-switches/
    return [
      '--user-data-dir=' + this._tempDir,
      '--no-default-browser-check',
      '--no-first-run',
      '--disable-default-apps',
      url
    ];
  };
};

ChromeBrowser.prototype = {
  name: 'Chrome',

  DEFAULT_CMD: {
    linux: '/usr/bin/google-chrome',
    darwin: '/Applications/Google Chrome.app/Contents/MacOS/Google Chrome',
    win32: process.env.LOCALAPPDATA + '\\Google\\Chrome\\Application\\chrome.exe'
  },
  ENV_CMD: 'CHROME_BIN'
};

var ChromeCanaryBrowser = function() {
  ChromeBrowser.apply(this, arguments);

  var parentOptions = this._getOptions;
  this._getOptions = function(url) {
    // disable crankshaft optimizations, as it causes lot of memory leaks (as of Chrome 23.0)
    return parentOptions.call(this, url).concat(['--js-flags="--nocrankshaft --noopt"']);
  };
};

ChromeCanaryBrowser.prototype = {
  name: 'ChromeCanary',

  DEFAULT_CMD: {
    linux: '/usr/bin/google-chrome-canary',
    darwin: '/Applications/Google Chrome Canary.app/Contents/MacOS/Google Chrome Canary',
    win32: process.env.LOCALAPPDATA + '\\Google\\Chrome SxS\\Application\\chrome.exe'
  },
  ENV_CMD: 'CHROME_CANARY_BIN'
};

var FirefoxBrowser = function() {
  Browser.apply(this, arguments);

  this.start = function(url) {
    var self = this;
    var command = this._getCommand();

    // create profile first
    exec(command + ' -CreateProfile testacular', function(e, stdout, stderr) {
      if (e) {
        log.error(e);
      }

      var match = /at\s\'(.*)\/prefs\.js\'/.exec(stderr.toString());

      if (match) {
        var profile = self._tempDir = match[1];
        var prefs = 'user_pref("browser.shell.checkDefaultBrowser", false);\n' +
                    'user_pref("browser.bookmarks.restore_default_bookmarks", false);\n';

        fs.createWriteStream(profile + '/prefs.js', {flags: 'a'}).write(prefs);
        self._execCommand(command + ' -profile "' + profile + '" ' + url);
      } else {
        log.warn('Cannot create Firefox profile');
        self._execCommand(command + ' ' + url);
      }
    });
  };
};

FirefoxBrowser.prototype = {
  name: 'Firefox',

  DEFAULT_CMD: {
    linux: '/usr/bin/firefox',
    darwin: '/Applications/Firefox.app/Contents/MacOS/firefox-bin',
    win32: process.env.ProgramFiles + '\\Mozilla Firefox\\firefox.exe'
  },
  ENV_CMD: 'FIREFOX_BIN'
};

var OperaBrowser = function() {
  Browser.apply(this, arguments);

  this._getOptions = function(url) {
    // Opera CLI options
    // http://www.opera.com/docs/switches/
    return [
      '-pd ' + this._tempDir,
      '-nomail',
      url
    ];
  };

  this.start = function(url) {
    var self = this;
    var prefs = 'Opera Preferences version 2.1\n' +
      '[User Prefs]\n' +
      'Show Default Browser Dialog=0\n' +
      'Startup Type=2\n' + // use homepage
      'Home URL=about:blank\n' +
      'Show Close All But Active Dialog=0\n' +
      'Show Close All Dialog=0\n' +
      'Show Crash Log Upload Dialog=0\n' +
      'Show Delete Mail Dialog=0\n' +
      'Show Download Manager Selection Dialog=0\n' +
      'Show Geolocation License Dialog=0\n' +
      'Show Mail Error Dialog=0\n' +
      'Show New Opera Dialog=0\n' +
      'Show Problem Dialog=0\n' +
      'Show Progress Dialog=0\n' +
      'Show Validation Dialog=0\n' +
      'Show Widget Debug Info Dialog=0\n' +
      'Show Startup Dialog=0\n' +
      'Show E-mail Client=0\n' +
      'Show Mail Header Toolbar=0\n' +
      'Show Setupdialog On Start=0\n' +
      'Enable Usage Statistics=0\n' +
      '[Install]\n' +
      'Newest Used Version=1.00.0000\n';

    var prefsFile = this._tempDir + '/operaprefs.ini';
    fs.writeFile(prefsFile, prefs, function(err) {
      // TODO(vojta): handle error
      self._execCommand(self._getCommand() + ' ' + self._getOptions(url).join(' '));
    });
  };
};

OperaBrowser.prototype = {
  name: 'Opera',

  DEFAULT_CMD: {
    linux: '/usr/bin/opera',
    darwin: '/Applications/Opera.app/Contents/MacOS/Opera',
    win32: process.env.ProgramFiles + '\\Opera\\opera.exe'
  },
  ENV_CMD: 'OPERA_BIN'
};

var SafariBrowser = function() {
  Browser.apply(this, arguments);

  this.start = function(url) {
    var HTML_TPL = path.normalize(__dirname + '/../static/safari.html');
    var self = this;

    fs.readFile(HTML_TPL, function(err, data) {
      var content = data.toString().replace('%URL%', url);
      var staticHtmlPath = self._tempDir + '/redirect.html';

      fs.writeFile(staticHtmlPath, content, function(err) {
        self._execCommand(self._getCommand() + ' ' + staticHtmlPath);
      });
    });
  };
};

SafariBrowser.prototype = {
  name: 'Safari',

  DEFAULT_CMD: {
    darwin: '/Applications/Safari.app/Contents/MacOS/Safari'
  },
  ENV_CMD: 'SAFARI_BIN'
};

var PhantomJSBrowser = function() {
  Browser.apply(this, arguments);

  this.start = function(url) {
    // create the js file, that will open testacular
    var captureFile = this._tempDir + '/capture.js';
    var captureCode = '(new WebPage()).open("' + url + '");';
    fs.createWriteStream(captureFile).end(captureCode);

    // and start phantomjs
    this._execCommand(this._getCommand() + ' ' + captureFile);
  };
};

PhantomJSBrowser.prototype = {
  name: 'PhantomJS',

  DEFAULT_CMD: {
    linux: process.env.TRAVIS ? 'phantomjs' : '/usr/bin/phantomjs',
    darwin: '/usr/local/bin/phantomjs'
  },
  ENV_CMD: 'PHANTOMJS_BIN'
};

var Launcher = function() {
  var browsers = [];

  this.launch = function(names, port, urlRoot) {
    var url = 'http://localhost:' + port + urlRoot;
    var Cls, browser, id;

    names.forEach(function(nameOrCls) {
      id = counter++;

      if (util.isString(nameOrCls)) {
        Cls = exports[nameOrCls + 'Browser'];
        if (!Cls) {
          throw new Error('Browser "' + nameOrCls + '" does not exists');
        }
      } else if (util.isFunction(nameOrCls)) {
        Cls = nameOrCls;
      }

      browser = new Cls(id);
      browser.id = id;
      browser.isCaptured = false;

      log.info('Starting  browser "%s"', browser.name || 'Custom');
      browser.start(url + '?id=' + browser.id);
      browsers.push(browser);
    });
  };

  this.kill = function(callback) {
    var remaining = 0;
    var finish = function() {
      remaining--;
      if (!remaining && callback) {
        callback();
      }
    };

    if (!browsers.length) {
      return process.nextTick(callback);
    }

    browsers.forEach(function(browser) {
      remaining++;
      browser.kill(finish);
    });
  };

  this.areAllCaptured = function() {
    return !browsers.some(function(browser) {
      return !browser.isCaptured;
    });
  };

  this.markCaptured = function(id) {
    browsers.forEach(function(browser) {
      if (browser.id === id) {
        browser.isCaptured = true;
      }
    });
  };
};

exports.Launcher = Launcher;
exports.BaseBrowser = Browser;
exports.ChromeBrowser = ChromeBrowser;
exports.ChromeCanaryBrowser = ChromeCanaryBrowser;
exports.FirefoxBrowser = FirefoxBrowser;
exports.OperaBrowser = OperaBrowser;
exports.SafariBrowser = SafariBrowser;
exports.PhantomJSBrowser = PhantomJSBrowser;
<|endoftext|>