<commit_before>var forge = require('node-forge');
var fs = require('fs');
var net = require('net');
var path = require('path');
var crypto = require('crypto');
var LRU = require('lru-cache');
var tls = require('tls');
var hagent = require('hagent');
var extend = require('extend');
var getServer = require('./h2').getServer;
var pki = forge.pki;
var createSecureContext = require('tls').createSecureContext || crypto.createCredentials;
var config = require('../config');

var CUR_VERSION = process.version;
var requiredVersion = parseInt(CUR_VERSION.slice(1), 10) >= 6;
var HTTPS_DIR = mkdir(path.join(config.getDataDir(), 'certs'));
var ROOT_NEW_KEY_FILE = path.join(HTTPS_DIR, 'root_new.key');
var ROOT_NEW_CRT_FILE = path.join(HTTPS_DIR, 'root_new.crt');
var CUSTOM_CERTS_DIR = config.CUSTOM_CERTS_DIR;
var useNewKey = fs.existsSync(ROOT_NEW_KEY_FILE) && fs.existsSync(ROOT_NEW_CRT_FILE);
var ROOT_KEY_FILE = useNewKey ? ROOT_NEW_KEY_FILE : path.join(HTTPS_DIR, 'root.key');
var ROOT_CRT_FILE = useNewKey ? ROOT_NEW_CRT_FILE : path.join(HTTPS_DIR, 'root.crt');
var customCertDir = config.certDir;
var customPairs = {};
var customCertsInfo = {};
var customCertCount = 0;
var cachePairs = new LRU({max: 5120});
var serverCache = new LRU({max: 1024});
var ILEGAL_CHAR_RE = /[^a-z\d-]/i;
var RANDOM_SERIAL = '.' + Date.now() + '.' + Math.floor(Math.random() * 10000);
var PORT_RE = /:\d*$/;
var hasCustomRoot;
var ROOT_KEY, ROOT_CRT;

if (!useNewKey && requiredVersion && !checkCertificate()) {
  try {
    fs.unlinkSync(ROOT_KEY_FILE);
    fs.unlinkSync(ROOT_CRT_FILE);
  } catch(e) {}
}

function mkdir(path) {
  !fs.existsSync(path) && fs.mkdirSync(path);
  return path;
}

function checkCertificate() {
  try {
    var crt = pki.certificateFromPem(fs.readFileSync(ROOT_CRT_FILE));
    if (crt.publicKey.n.toString(2).length < 2048) {
      return false;
    }
    return /^whistle\.\d+$/.test(getCommonName(crt));
  } catch(e) {}
  return true;
}

function getCommonName(crt) {
  var attrs = crt.issuer && crt.issuer.attributes;
  if (Array.isArray(attrs)) {
    for (var i = 0, len = attrs.length; i < len; i++) {
      var attr = attrs[i];
      if (attr && attr.name === 'commonName') {
        return attr.value;
      }
    }
  }
  return '';
}

function getDomain(hostname) {
  if (getCertificate(hostname) || net.isIP(hostname)) {
    return hostname;
  }
  var list = hostname.split('.');
  var prefix = list[0];
  list[0] = '*';
  var wildDomain = list.join('.');
  if (getCertificate(wildDomain)) {
    return wildDomain;
  }
  var len = list.length;
  if (len < 3) {
    return hostname;
  }
  if (len > 3 || ILEGAL_CHAR_RE.test(prefix) || list[1].length > 3
    || list[2] === 'com' || list[1] === 'url') { // For tencent cdn
    return wildDomain;
  }

  return hostname;
}

exports.getDomain = getDomain;
exports.existsCustomCert = function(hostname) {
  if (!customCertCount) {
    return false;
  }
  hostname = hostname.replace(PORT_RE, '');
  var cert = customPairs[hostname];
  if (cert) {
    return true;
  }
  hostname = hostname.split('.');
  hostname[0] = '*';
  return customPairs[hostname.join('.')];
};

function getCertificate(hostname) {
  return customPairs[hostname] || cachePairs.get(hostname);
}

function createCertificate(hostname) {
  var cert = getCertificate(hostname);
  if (cert) {
    return cert;
  }

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

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

  cert.setIssuer(ROOT_CRT.subject.attributes);
  cert.setExtensions([ {
    name: 'subjectAltName',
    altNames: [net.isIP(hostname) ?
      {
        type: 7,
        ip: hostname
      } : {
        type: 2,
        value: hostname
      }]
  } ]);
  cert.sign(ROOT_KEY, forge.md.sha256.create());
  cert = {
    key: pki.privateKeyToPem(ROOT_KEY),
    cert: pki.certificateToPem(cert)
  };
  cachePairs.set(hostname, cert);
  return cert;
}

function loadCustomCerts(certDir) {
  if (!certDir) {
    return;
  }
  var certs = {};
  try {
    fs.readdirSync(certDir).forEach(function(name) {
      if (!/^(.+)\.(crt|key)$/.test(name)) {
        return;
      }
      var filename =RegExp.$1;
      var suffix = RegExp.$2;
      var cert = certs[filename] = certs[filename] || {};
      if (suffix === 'crt') {
        suffix = 'cert';
      }
      try {
        var filePath = path.join(certDir, name);
        cert[suffix] = fs.readFileSync(filePath, {encoding: 'utf8'});
        var mtime = fs.statSync(filePath).mtime.getTime();
        if (cert.mtime == null || cert.mtime < mtime) {
          cert.mtime = mtime;
        }
      } catch(e) {}
    });
  } catch(e) {}
  var rootCA = certs.root;
  delete certs.root;
  if (rootCA && rootCA.key && rootCA.cert && !hasCustomRoot) {
    hasCustomRoot = true;
    ROOT_KEY_FILE = path.join(certDir, 'root.key');
    ROOT_CRT_FILE = path.join(certDir, 'root.crt');
  }
  var keys = Object.keys(certs).filter(function(key) {
    var cert = certs[key];
    return cert && cert.mtime != null;
  }).sort(function(key1, key2) {
    return certs[key1].mtime > certs[key2].mtime ? -1 : 1;
  });
  keys.forEach(function(filename) {
    var cert = certs[filename];
    if (!cert || !cert.key || !cert.cert) {
      return;
    }
    try {
      var pem = pki.certificateFromPem(cert.cert);
      var altNames = getAltNames(pem.extensions);
      var mtime = cert.mtime;
      var validity = pem.validity;
      altNames.forEach(function(item) {
        if ((item.type === 2 || item.type === 7) && !customPairs[item.value]) {
          customPairs[item.value] = cert;
          customCertsInfo[item.value] = extend({ filename: filename, mtime: mtime, domain: item.value }, validity);
        }
      });
    } catch (e) {}
  });
  customCertCount = Object.keys(customPairs).length;
}

function getAltNames(exts) {
  for (var i = 0, len = exts.length; i < len; i++) {
    var item = exts[i];
    if (item.name === 'subjectAltName') {
      return item.altNames;
    }
  }
}

function createRootCA() {
  loadCustomCerts(customCertDir);
  loadCustomCerts(CUSTOM_CERTS_DIR);
  if (ROOT_KEY && ROOT_CRT) {
    return;
  }
  var rootKey, rootCrt;
  try {
    ROOT_KEY = fs.readFileSync(ROOT_KEY_FILE);
    ROOT_CRT = fs.readFileSync(ROOT_CRT_FILE);
    rootKey = ROOT_KEY;
    rootCrt = ROOT_CRT;
  } 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;
    rootKey = pki.privateKeyToPem(ROOT_KEY);
    rootCrt = pki.certificateToPem(ROOT_CRT);
    fs.writeFileSync(ROOT_KEY_FILE, rootKey.toString());
    fs.writeFileSync(ROOT_CRT_FILE, rootCrt.toString());
  }
}

function getRandom() {
  var random = Math.floor(Math.random() * 1000);
  if (random < 10) {
    return '00' + random;
  }
  if (random < 100) {
    return '0' + random;
  }
  return '' + random;
}

function createCACert() {
  var keys = pki.rsa.generateKeyPair(requiredVersion ? 2048 : 1024);
  var cert = createCert(keys.publicKey);
  var now = Date.now() + getRandom();
  var attrs = [ {
    name : 'commonName',
    value : 'whistle.' + now
  }, {
    name : 'countryName',
    value : 'CN'
  }, {
    shortName : 'ST',
    value : 'ZJ'
  }, {
    name : 'localityName',
    value : 'HZ'
  }, {
    name : 'organizationName',
    value : now + '.wproxy.org'
  }, {
    shortName : 'OU',
    value : 'wproxy.org'
  } ];

  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, isShortPeriod) {
  var cert = pki.createCertificate();
  cert.publicKey = publicKey;
  cert.serialNumber = serialNumber || '01';
  var curYear = new Date().getFullYear();
  cert.validity.notBefore = new Date();
  cert.validity.notAfter = new Date();
  cert.validity.notBefore.setFullYear(curYear - 1);
  // https://chromium.googlesource.com/chromium/src/+/refs/heads/master/net/cert/cert_verify_proc.cc#900
  cert.validity.notAfter.setFullYear(curYear + (isShortPeriod ? 1 : 10));
  return cert;
}

function getRootCAFile() {
  return ROOT_CRT_FILE;
}

createRootCA();// 启动生成ca

hagent.serverAgent.createCertificate = createCertificate;
var getHttpsServer = hagent.create(getServer, 43900);
var callbacks = [];
var h2Callbacks = [];
var TIMEOUT = 6000;
var sniPort, h2SniPort;

var SNICallback = function(serverName, cb) {
  serverName = getDomain(serverName);
  var options = createCertificate(serverName);
  if (!options._ctx) {
    options._ctx = createSecureContext(options);
  }
  cb(null, options._ctx);
};

exports.createServer =  function(serverName, listener) {
  serverName = getDomain(serverName);
  var server = serverCache.get(serverName);
  if (!server) {
    var options = createCertificate(serverName);
    server = tls.createServer(createSecureContext(options), listener);
    serverCache.set(serverName, server);
  }
  return server;
};
exports.getCustomCertsInfo = function() {
  return customCertsInfo;
};
exports.getRootCAFile = getRootCAFile;
exports.createCertificate = createCertificate;
exports.serverAgent = hagent.serverAgent;

var SNI_OPTIONS = { SNICallback: SNICallback };
exports.SNI_OPTIONS = SNI_OPTIONS;
exports.getSNIServer = function(listener, callback, disableH2) {
  var enableH2 = config.enableH2 && !disableH2;
  var curPort = enableH2 ? h2SniPort : sniPort;
  if (curPort) {
    return callback(curPort);
  }
  var cbList = enableH2 ? h2Callbacks : callbacks;
  if (curPort === false) {
    return cbList.push(callback);
  }
  var setPort = function(port) {
    curPort = port;
    if (enableH2) {
      h2SniPort = port;
    } else {
      sniPort = port;
    }
  };
  var removeServer = function() {
    setPort(null);
    try {
      this.close();
    } catch(e) {} //重复关闭会导致异常
  };
  setPort(false); // pending
  SNI_OPTIONS.allowHTTP1 = enableH2; // 是否启用http2
  getHttpsServer(SNI_OPTIONS, listener, function(server, port) {
    server.timeout = config.timeout;
    server.on('error', removeServer);
    var timeout = setTimeout(removeServer, TIMEOUT);
    var clearup = function() {
      clearTimeout(timeout);
    };
    server.once('tlsClientError', clearup);
    server.once('secureConnection', clearup);
    setPort(port);
    cbList.forEach(function(cb) {
      cb(port);
    });
    if (enableH2) {
      h2Callbacks = [];
    } else {
      callbacks = [];
    }
  });
};
<commit_msg>refactor: cancel custom timeout<commit_after>var forge = require('node-forge');
var fs = require('fs');
var net = require('net');
var path = require('path');
var crypto = require('crypto');
var LRU = require('lru-cache');
var tls = require('tls');
var hagent = require('hagent');
var extend = require('extend');
var getServer = require('./h2').getServer;
var pki = forge.pki;
var createSecureContext = require('tls').createSecureContext || crypto.createCredentials;
var config = require('../config');

var CUR_VERSION = process.version;
var requiredVersion = parseInt(CUR_VERSION.slice(1), 10) >= 6;
var HTTPS_DIR = mkdir(path.join(config.getDataDir(), 'certs'));
var ROOT_NEW_KEY_FILE = path.join(HTTPS_DIR, 'root_new.key');
var ROOT_NEW_CRT_FILE = path.join(HTTPS_DIR, 'root_new.crt');
var CUSTOM_CERTS_DIR = config.CUSTOM_CERTS_DIR;
var useNewKey = fs.existsSync(ROOT_NEW_KEY_FILE) && fs.existsSync(ROOT_NEW_CRT_FILE);
var ROOT_KEY_FILE = useNewKey ? ROOT_NEW_KEY_FILE : path.join(HTTPS_DIR, 'root.key');
var ROOT_CRT_FILE = useNewKey ? ROOT_NEW_CRT_FILE : path.join(HTTPS_DIR, 'root.crt');
var customCertDir = config.certDir;
var customPairs = {};
var customCertsInfo = {};
var customCertCount = 0;
var cachePairs = new LRU({max: 5120});
var serverCache = new LRU({max: 1024});
var ILEGAL_CHAR_RE = /[^a-z\d-]/i;
var RANDOM_SERIAL = '.' + Date.now() + '.' + Math.floor(Math.random() * 10000);
var PORT_RE = /:\d*$/;
var hasCustomRoot;
var ROOT_KEY, ROOT_CRT;

if (!useNewKey && requiredVersion && !checkCertificate()) {
  try {
    fs.unlinkSync(ROOT_KEY_FILE);
    fs.unlinkSync(ROOT_CRT_FILE);
  } catch(e) {}
}

function mkdir(path) {
  !fs.existsSync(path) && fs.mkdirSync(path);
  return path;
}

function checkCertificate() {
  try {
    var crt = pki.certificateFromPem(fs.readFileSync(ROOT_CRT_FILE));
    if (crt.publicKey.n.toString(2).length < 2048) {
      return false;
    }
    return /^whistle\.\d+$/.test(getCommonName(crt));
  } catch(e) {}
  return true;
}

function getCommonName(crt) {
  var attrs = crt.issuer && crt.issuer.attributes;
  if (Array.isArray(attrs)) {
    for (var i = 0, len = attrs.length; i < len; i++) {
      var attr = attrs[i];
      if (attr && attr.name === 'commonName') {
        return attr.value;
      }
    }
  }
  return '';
}

function getDomain(hostname) {
  if (getCertificate(hostname) || net.isIP(hostname)) {
    return hostname;
  }
  var list = hostname.split('.');
  var prefix = list[0];
  list[0] = '*';
  var wildDomain = list.join('.');
  if (getCertificate(wildDomain)) {
    return wildDomain;
  }
  var len = list.length;
  if (len < 3) {
    return hostname;
  }
  if (len > 3 || ILEGAL_CHAR_RE.test(prefix) || list[1].length > 3
    || list[2] === 'com' || list[1] === 'url') { // For tencent cdn
    return wildDomain;
  }

  return hostname;
}

exports.getDomain = getDomain;
exports.existsCustomCert = function(hostname) {
  if (!customCertCount) {
    return false;
  }
  hostname = hostname.replace(PORT_RE, '');
  var cert = customPairs[hostname];
  if (cert) {
    return true;
  }
  hostname = hostname.split('.');
  hostname[0] = '*';
  return customPairs[hostname.join('.')];
};

function getCertificate(hostname) {
  return customPairs[hostname] || cachePairs.get(hostname);
}

function createCertificate(hostname) {
  var cert = getCertificate(hostname);
  if (cert) {
    return cert;
  }

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

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

  cert.setIssuer(ROOT_CRT.subject.attributes);
  cert.setExtensions([ {
    name: 'subjectAltName',
    altNames: [net.isIP(hostname) ?
      {
        type: 7,
        ip: hostname
      } : {
        type: 2,
        value: hostname
      }]
  } ]);
  cert.sign(ROOT_KEY, forge.md.sha256.create());
  cert = {
    key: pki.privateKeyToPem(ROOT_KEY),
    cert: pki.certificateToPem(cert)
  };
  cachePairs.set(hostname, cert);
  return cert;
}

function loadCustomCerts(certDir) {
  if (!certDir) {
    return;
  }
  var certs = {};
  try {
    fs.readdirSync(certDir).forEach(function(name) {
      if (!/^(.+)\.(crt|key)$/.test(name)) {
        return;
      }
      var filename =RegExp.$1;
      var suffix = RegExp.$2;
      var cert = certs[filename] = certs[filename] || {};
      if (suffix === 'crt') {
        suffix = 'cert';
      }
      try {
        var filePath = path.join(certDir, name);
        cert[suffix] = fs.readFileSync(filePath, {encoding: 'utf8'});
        var mtime = fs.statSync(filePath).mtime.getTime();
        if (cert.mtime == null || cert.mtime < mtime) {
          cert.mtime = mtime;
        }
      } catch(e) {}
    });
  } catch(e) {}
  var rootCA = certs.root;
  delete certs.root;
  if (rootCA && rootCA.key && rootCA.cert && !hasCustomRoot) {
    hasCustomRoot = true;
    ROOT_KEY_FILE = path.join(certDir, 'root.key');
    ROOT_CRT_FILE = path.join(certDir, 'root.crt');
  }
  var keys = Object.keys(certs).filter(function(key) {
    var cert = certs[key];
    return cert && cert.mtime != null;
  }).sort(function(key1, key2) {
    return certs[key1].mtime > certs[key2].mtime ? -1 : 1;
  });
  keys.forEach(function(filename) {
    var cert = certs[filename];
    if (!cert || !cert.key || !cert.cert) {
      return;
    }
    try {
      var pem = pki.certificateFromPem(cert.cert);
      var altNames = getAltNames(pem.extensions);
      var mtime = cert.mtime;
      var validity = pem.validity;
      altNames.forEach(function(item) {
        if ((item.type === 2 || item.type === 7) && !customPairs[item.value]) {
          customPairs[item.value] = cert;
          customCertsInfo[item.value] = extend({ filename: filename, mtime: mtime, domain: item.value }, validity);
        }
      });
    } catch (e) {}
  });
  customCertCount = Object.keys(customPairs).length;
}

function getAltNames(exts) {
  for (var i = 0, len = exts.length; i < len; i++) {
    var item = exts[i];
    if (item.name === 'subjectAltName') {
      return item.altNames;
    }
  }
}

function createRootCA() {
  loadCustomCerts(customCertDir);
  loadCustomCerts(CUSTOM_CERTS_DIR);
  if (ROOT_KEY && ROOT_CRT) {
    return;
  }
  var rootKey, rootCrt;
  try {
    ROOT_KEY = fs.readFileSync(ROOT_KEY_FILE);
    ROOT_CRT = fs.readFileSync(ROOT_CRT_FILE);
    rootKey = ROOT_KEY;
    rootCrt = ROOT_CRT;
  } 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;
    rootKey = pki.privateKeyToPem(ROOT_KEY);
    rootCrt = pki.certificateToPem(ROOT_CRT);
    fs.writeFileSync(ROOT_KEY_FILE, rootKey.toString());
    fs.writeFileSync(ROOT_CRT_FILE, rootCrt.toString());
  }
}

function getRandom() {
  var random = Math.floor(Math.random() * 1000);
  if (random < 10) {
    return '00' + random;
  }
  if (random < 100) {
    return '0' + random;
  }
  return '' + random;
}

function createCACert() {
  var keys = pki.rsa.generateKeyPair(requiredVersion ? 2048 : 1024);
  var cert = createCert(keys.publicKey);
  var now = Date.now() + getRandom();
  var attrs = [ {
    name : 'commonName',
    value : 'whistle.' + now
  }, {
    name : 'countryName',
    value : 'CN'
  }, {
    shortName : 'ST',
    value : 'ZJ'
  }, {
    name : 'localityName',
    value : 'HZ'
  }, {
    name : 'organizationName',
    value : now + '.wproxy.org'
  }, {
    shortName : 'OU',
    value : 'wproxy.org'
  } ];

  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, isShortPeriod) {
  var cert = pki.createCertificate();
  cert.publicKey = publicKey;
  cert.serialNumber = serialNumber || '01';
  var curYear = new Date().getFullYear();
  cert.validity.notBefore = new Date();
  cert.validity.notAfter = new Date();
  cert.validity.notBefore.setFullYear(curYear - 1);
  // https://chromium.googlesource.com/chromium/src/+/refs/heads/master/net/cert/cert_verify_proc.cc#900
  cert.validity.notAfter.setFullYear(curYear + (isShortPeriod ? 1 : 10));
  return cert;
}

function getRootCAFile() {
  return ROOT_CRT_FILE;
}

createRootCA();// 启动生成ca

hagent.serverAgent.createCertificate = createCertificate;
var getHttpsServer = hagent.create(getServer, 43900);
var callbacks = [];
var h2Callbacks = [];
var TIMEOUT = 6000;
var sniPort, h2SniPort;

var SNICallback = function(serverName, cb) {
  serverName = getDomain(serverName);
  var options = createCertificate(serverName);
  if (!options._ctx) {
    options._ctx = createSecureContext(options);
  }
  cb(null, options._ctx);
};

exports.createServer =  function(serverName, listener) {
  serverName = getDomain(serverName);
  var server = serverCache.get(serverName);
  if (!server) {
    var options = createCertificate(serverName);
    server = tls.createServer(createSecureContext(options), listener);
    serverCache.set(serverName, server);
  }
  return server;
};
exports.getCustomCertsInfo = function() {
  return customCertsInfo;
};
exports.getRootCAFile = getRootCAFile;
exports.createCertificate = createCertificate;
exports.serverAgent = hagent.serverAgent;

var SNI_OPTIONS = { SNICallback: SNICallback };
exports.SNI_OPTIONS = SNI_OPTIONS;
exports.getSNIServer = function(listener, callback, disableH2) {
  var enableH2 = config.enableH2 && !disableH2;
  var curPort = enableH2 ? h2SniPort : sniPort;
  if (curPort) {
    return callback(curPort);
  }
  var cbList = enableH2 ? h2Callbacks : callbacks;
  if (curPort === false) {
    return cbList.push(callback);
  }
  var setPort = function(port) {
    curPort = port;
    if (enableH2) {
      h2SniPort = port;
    } else {
      sniPort = port;
    }
  };
  var removeServer = function() {
    setPort(null);
    try {
      this.close();
    } catch(e) {} //重复关闭会导致异常
  };
  setPort(false); // pending
  SNI_OPTIONS.allowHTTP1 = enableH2; // 是否启用http2
  getHttpsServer(SNI_OPTIONS, listener, function(server, port) {
    server.on('error', removeServer);
    var timeout = setTimeout(removeServer, TIMEOUT);
    var clearup = function() {
      clearTimeout(timeout);
    };
    server.once('tlsClientError', clearup);
    server.once('secureConnection', clearup);
    setPort(port);
    cbList.forEach(function(cb) {
      cb(port);
    });
    if (enableH2) {
      h2Callbacks = [];
    } else {
      callbacks = [];
    }
  });
};
<|endoftext|>


<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 SUPPORTS_2048_RSA = parseInt(process.version.slice(1), 10) >= 6;
var enableATS = SUPPORTS_2048_RSA && config.ATS;
var HTTPS_DIR = util.mkdir(path.join(config.getDataDir(), 'certs'));
var ROOT_2048_KEY_FILE = path.join(HTTPS_DIR, 'root_2048.key');
var ROOT_2048_CRT_FILE = path.join(HTTPS_DIR, 'root_2048.crt');
var use2048Key = enableATS || (fs.existsSync(ROOT_2048_KEY_FILE) && fs.existsSync(ROOT_2048_CRT_FILE));
var ROOT_KEY_FILE = use2048Key ? ROOT_2048_KEY_FILE : path.join(HTTPS_DIR, 'root.key');
var ROOT_CRT_FILE = use2048Key ? ROOT_2048_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(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';
  var curYear = new Date().getFullYear();
  cert.validity.notBefore = new Date();
  cert.validity.notAfter = new Date();
  cert.validity.notBefore.setFullYear(curYear - 10);
  cert.validity.notAfter.setFullYear(curYear + 10);
  return cert;
}

function getRootCAFile() {
  return ROOT_CRT_FILE;
}

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

<commit_msg>refactor: Refine generate certificate<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 SUPPORTS_2048_RSA = parseInt(process.version.slice(1), 10) >= 6;
var enableATS = SUPPORTS_2048_RSA && config.ATS;
var HTTPS_DIR = util.mkdir(path.join(config.getDataDir(), 'certs'));
var ROOT_2048_KEY_FILE = path.join(HTTPS_DIR, 'root_2048.key');
var ROOT_2048_CRT_FILE = path.join(HTTPS_DIR, 'root_2048.crt');
var use2048Key = enableATS || (fs.existsSync(ROOT_2048_KEY_FILE) && fs.existsSync(ROOT_2048_CRT_FILE));
var ROOT_KEY_FILE = use2048Key ? ROOT_2048_KEY_FILE : path.join(HTTPS_DIR, 'root.key');
var ROOT_CRT_FILE = use2048Key ? ROOT_2048_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.setExtensions([ {
    name: 'subjectAltName',
    altNames: [{
      type: 2,
      value: hostname
    }]
  } ]);
  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';
  var curYear = new Date().getFullYear();
  cert.validity.notBefore = new Date();
  cert.validity.notAfter = new Date();
  cert.validity.notBefore.setFullYear(curYear - 10);
  cert.validity.notAfter.setFullYear(curYear + 10);
  return cert;
}

function getRootCAFile() {
  return ROOT_CRT_FILE;
}

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

<|endoftext|>


<commit_before>// Generate `id` attributes for HTML block elements.
// Copyright 2012 Tom Vincent <http://tlvince.com/contact>
// <https://github.com/tlvince/identify.js>

// DOM selectors and general heavy lifting
var cheerio = require('cheerio');

// Big list of HTML(5) block elements.
//
// From: [Mozilla Developer Network][1].
// [1]: https://developer.mozilla.org/en-US/docs/HTML/Block-level_elements
var block_elements = [
  'address',
  'article',
  'aside',
  'audio',
  'blockquote',
  'canvas',
  'dd',
  'div',
  'dl',
  'fieldset',
  'figcaption',
  'figure',
  'footer',
  'form',
  'h1',
  'h2',
  'h3',
  'h4',
  'h5',
  'h6',
  'header',
  'hgroup',
  'hr',
  'noscript',
  'ol',
  'output',
  'p',
  'pre',
  'section',
  'table',
  'tfoot',
  'ul',
  'video'
]

// Sanitise a given string
//
// Performs the following transformations (in order):
//
//   * Replace ampersand with 'and'
//   * Replace one or more spaces or commars with a dash
//   * Filter non alphanumerics and dashes
//   * Flatten a series of two or more dashes
//   * Strip leading/trailing dash
//
// `words` - The number of words to return.
//
//  Returns the sanitised string
var sanitise = function(str, words) {
  if (words == null) {
    words = 0;
  }
  str = str.toLowerCase();
  if (words > 0) {
    str = str.split(' ').slice(0, +(words - 1) + 1 || 9e9).toString();
  }
  str = str.replace(/&/g, 'and');
  str = str.replace(/(\s+|,+)/g, '-');
  str = str.replace(/[^a-z0-9-]/g, '');
  str = str.replace(/-{2,}/g, '-');
  return str = str.replace(/(^-|-$)/, '');
};

// Add id attributes to block elements in the given HTML.
//
// Does not clobber existing attributes.
//
// Returns the processed HTML.
var identify = function(html) {
  var $ = cheerio.load(html, {
    lowerCaseTags: true
  });

  var i, len;
  for (i = 0, len = block_elements.length; i < len; i++) {
    var tag = block_elements[i];
    $(tag).each(function(index, elem) {
      if (!$(this).attr('id')) {
        switch (tag) {
          case 'h1':
          case 'h2':
          case 'h3':
          case 'h4':
          case 'h5':
          case 'h6':
            return $(this).attr('id', sanitise($(this).text()));
          case 'p':
            return $(this).attr('id', sanitise($(this).text(), 3));
          default:
            return $(this).attr('id', tag + index);
        }
      }
    });
  }
  return $.html();
};

module.exports = identify;
<commit_msg>Make counts more human readable (0 -> 1)<commit_after>// Generate `id` attributes for HTML block elements.
// Copyright 2012 Tom Vincent <http://tlvince.com/contact>
// <https://github.com/tlvince/identify.js>

// DOM selectors and general heavy lifting
var cheerio = require('cheerio');

// Big list of HTML(5) block elements.
//
// From: [Mozilla Developer Network][1].
// [1]: https://developer.mozilla.org/en-US/docs/HTML/Block-level_elements
var block_elements = [
  'address',
  'article',
  'aside',
  'audio',
  'blockquote',
  'canvas',
  'dd',
  'div',
  'dl',
  'fieldset',
  'figcaption',
  'figure',
  'footer',
  'form',
  'h1',
  'h2',
  'h3',
  'h4',
  'h5',
  'h6',
  'header',
  'hgroup',
  'hr',
  'noscript',
  'ol',
  'output',
  'p',
  'pre',
  'section',
  'table',
  'tfoot',
  'ul',
  'video'
]

// Sanitise a given string
//
// Performs the following transformations (in order):
//
//   * Replace ampersand with 'and'
//   * Replace one or more spaces or commars with a dash
//   * Filter non alphanumerics and dashes
//   * Flatten a series of two or more dashes
//   * Strip leading/trailing dash
//
// `words` - The number of words to return.
//
//  Returns the sanitised string
var sanitise = function(str, words) {
  if (words == null) {
    words = 0;
  }
  str = str.toLowerCase();
  if (words > 0) {
    str = str.split(' ').slice(0, +(words - 1) + 1 || 9e9).toString();
  }
  str = str.replace(/&/g, 'and');
  str = str.replace(/(\s+|,+)/g, '-');
  str = str.replace(/[^a-z0-9-]/g, '');
  str = str.replace(/-{2,}/g, '-');
  return str = str.replace(/(^-|-$)/, '');
};

// Add id attributes to block elements in the given HTML.
//
// Does not clobber existing attributes.
//
// Returns the processed HTML.
var identify = function(html) {
  var $ = cheerio.load(html, {
    lowerCaseTags: true
  });

  var i, len;
  for (i = 0, len = block_elements.length; i < len; i++) {
    var tag = block_elements[i];
    $(tag).each(function(index, elem) {
      if (!$(this).attr('id')) {
        switch (tag) {
          case 'h1':
          case 'h2':
          case 'h3':
          case 'h4':
          case 'h5':
          case 'h6':
            return $(this).attr('id', sanitise($(this).text()));
          case 'p':
            return $(this).attr('id', sanitise($(this).text(), 3));
          default:
            return $(this).attr('id', tag + (index + 1));
        }
      }
    });
  }
  return $.html();
};

module.exports = identify;
<|endoftext|>


<commit_before>'use strict'
/**
 * Provides Solid helper functions involved with parsing a user's WebId profile.
 * @module identity
 */
module.exports.discoverWebID = discoverWebID
module.exports.getProfile = getProfile
module.exports.loadExtendedProfile = loadExtendedProfile

var graphUtil = require('./util/graph-util')
var webClient = require('./web')
var SolidProfile = require('./solid/profile')
var vocab = require('./vocab')

/**
 * Discovers a user's WebId (URL) starting from the account/domain URL
 * @method discoverWebID
 * @param url {String} Location of a user's account or domain.
 * @throw {Error} Reason why the WebID could not be discovered
 * @return {Promise<uri>}
 */

function discoverWebID (url) {
  return new Promise(function (resolve) {
    webClient.options(url)
      .then(function (meta) {
        var metaUrl = meta.meta
        if (metaUrl) {
          webClient.getParsedGraph(metaUrl)
            .then(function (graph) {
              var webid = graph.any(undefined, vocab.foaf('account'))
              if (webid && webid.uri) {
                resolve(webid.uri)
              } else {
                throw new Error('Could not find a WebID matching the domain ' +
                url)
              }
            })
        } else {
          throw new Error('Could not find a meta URL in the Link header')
        }
      })
      .catch(function (err) {
        throw new Error('Could not complete request.', err)
      })
  })
}

/**
 * Fetches a user's WebId profile, optionally follows `sameAs` etc links,
 *   and return a promise with a parsed SolidProfile instance.
 * @method getProfile
 * @param profileUrl {String} WebId or Location of a user's profile.
 * @param [options] Options hashmap (see solid.web.solidRequest() function docs)
 * @param [options.ignoreExtended=false] Do not load extended profile if true.
 * @return {Promise<SolidProfile>}
 */
function getProfile (profileUrl, options) {
  options = options || {}
  // Politely ask for Turtle formatted profiles
  options.headers = options.headers || {
    'Accept': 'text/turtle'
  }
  options.noCredentials = true  // profiles are always public
  // Load main profile
  return webClient.get(profileUrl, options)
    .then(function (response) {
      var contentType = response.contentType()
      if (!contentType) {
        throw new Error('Cannot parse profile without a Content-Type: header')
      }
      var parsedProfile = graphUtil.parseGraph(profileUrl, response.raw(),
        contentType)
      var profile = new SolidProfile(profileUrl, parsedProfile, response)
      profile.isLoaded = true
      if (options.ignoreExtended) {
        return profile
      } else {
        return loadExtendedProfile(profile, options)
      }
    })
}

/**
 * Loads the related external profile resources (all the `sameAs` and `seeAlso`
 * links, as well as Preferences), and appends them to the profile's
 * `parsedGraph`. Returns the profile instance.
 * @method loadExtendedProfile
 * @private
 * @param profile {SolidProfile}
 * @param [options] Options hashmap (see solid.web.solidRequest() function docs)
 * @return {Promise<SolidProfile>}
 */
function loadExtendedProfile (profile, options) {
  var links = profile.relatedProfilesLinks()
  return webClient.loadParsedGraphs(links, options)
    .then(function (loadedGraphs) {
      loadedGraphs.forEach(function (graph) {
        if (graph && graph.value) {
          profile.appendFromGraph(graph.value, graph.uri)
        }
      })
      return profile
    })
}
<commit_msg>Fix relative to abs URLs; added reject to discovery<commit_after>'use strict'
/**
 * Provides Solid helper functions involved with parsing a user's WebId profile.
 * @module identity
 */
module.exports.discoverWebID = discoverWebID
module.exports.getProfile = getProfile
module.exports.loadExtendedProfile = loadExtendedProfile

var graphUtil = require('./util/graph-util')
var webUtil = require('./util/web-util')
var webClient = require('./web')
var SolidProfile = require('./solid/profile')
var vocab = require('./vocab')

/**
 * Discovers a user's WebId (URL) starting from the account/domain URL
 * @method discoverWebID
 * @param url {String} Location of a user's account or domain.
 * @throw {Error} Reason why the WebID could not be discovered
 * @return {Promise<uri>}
 */

function discoverWebID (url) {
  return new Promise(function (resolve, reject) {
    webClient.options(url)
      .then(function (meta) {
        var metaUrl = meta.meta
        if (metaUrl) {
          metaUrl = webUtil.absoluteUrl(metaUrl, url)
          webClient.getParsedGraph(metaUrl)
            .then(function (graph) {
              var webid = graph.any(undefined, vocab.foaf('account'))
              if (webid && webid.uri) {
                resolve(webid.uri)
              } else {
                throw new Error('Could not find a WebID matching the domain ' +
                url)
              }
            })
            .catch(function (err) {
              reject(err)
            })
        } else {
          throw new Error('Could not find a meta URL in the Link header')
        }
      })
      .catch(function (err) {
        reject(err)
      })
  })
}

/**
 * Fetches a user's WebId profile, optionally follows `sameAs` etc links,
 *   and return a promise with a parsed SolidProfile instance.
 * @method getProfile
 * @param profileUrl {String} WebId or Location of a user's profile.
 * @param [options] Options hashmap (see solid.web.solidRequest() function docs)
 * @param [options.ignoreExtended=false] Do not load extended profile if true.
 * @return {Promise<SolidProfile>}
 */
function getProfile (profileUrl, options) {
  options = options || {}
  // Politely ask for Turtle formatted profiles
  options.headers = options.headers || {
    'Accept': 'text/turtle'
  }
  options.noCredentials = true  // profiles are always public
  // Load main profile
  return webClient.get(profileUrl, options)
    .then(function (response) {
      var contentType = response.contentType()
      if (!contentType) {
        throw new Error('Cannot parse profile without a Content-Type: header')
      }
      var parsedProfile = graphUtil.parseGraph(profileUrl, response.raw(),
        contentType)
      var profile = new SolidProfile(profileUrl, parsedProfile, response)
      profile.isLoaded = true
      if (options.ignoreExtended) {
        return profile
      } else {
        return loadExtendedProfile(profile, options)
      }
    })
}

/**
 * Loads the related external profile resources (all the `sameAs` and `seeAlso`
 * links, as well as Preferences), and appends them to the profile's
 * `parsedGraph`. Returns the profile instance.
 * @method loadExtendedProfile
 * @private
 * @param profile {SolidProfile}
 * @param [options] Options hashmap (see solid.web.solidRequest() function docs)
 * @return {Promise<SolidProfile>}
 */
function loadExtendedProfile (profile, options) {
  var links = profile.relatedProfilesLinks()
  return webClient.loadParsedGraphs(links, options)
    .then(function (loadedGraphs) {
      loadedGraphs.forEach(function (graph) {
        if (graph && graph.value) {
          profile.appendFromGraph(graph.value, graph.uri)
        }
      })
      return profile
    })
}
<|endoftext|>


<commit_before>// The socket.io server and polyglot clients. Called by scripts/_init.js

// dependencies
const Promise = require('bluebird')
const { spawn } = require('child_process')
const fs = require('fs')
const _ = require('lomath')
const path = require('path')
const { setEnv, log } = require(path.join(__dirname, '..', 'index'))

/* istanbul ignore next */
if (process.env.IOPORT == undefined) { setEnv() }

// import other languages via child_process
var polyglotCommands = _.pickBy({
    ruby: [path.join(__dirname, 'client.rb')],
    python3: [path.join(__dirname, 'client.py')]
  },
  (args, cmd) => {
    return global.config.get("ACTIVATE_IO_CLIENTS").get(cmd)
  })

const CLIENT_COUNT = 1 + _.size(polyglotCommands)

/**
 * Start a Socket IO server connecting to a robot.server (an Expressjs server), or a brand new Express server for use in dev. Sets global.io too.
 * Has a failsafe for not starting multiple times even when called repeatedly by accident in the same thread.
 * @param  {*} robot The hubot object
 * @return {server} server
 */
/* istanbul ignore next */
function io_server(robot) {
  if (global.io) {
    // if already started elsewhere, skip below including io_client, return promise with the existing server
    return Promise.resolve(global.io.server)
  }

  global.log.info(`Starting socket.io server on IOPORT: ${process.env.IOPORT}`)
  var server
  var app = require('express')()
  process.on('uncaughtException', global.log.error)
  server = app.listen(process.env.IOPORT)

  global.io = require('socket.io').listen(server)
  var count = CLIENT_COUNT
  global.ioPromise = new Promise((resolve, reject) => {
    global.io.sockets.on('connection', (socket) => {
      // serialize for direct communication by using join room
      socket.on('join', (id) => {
        socket.join(id)
        global.log.debug(id, socket.id, 'joined')
        if (--count == 0) {
          global.log.info(`All ${CLIENT_COUNT} clients have joined`)
          resolve(server) // resolve with the server
        }
      })
      socket.on('disconnect', () => { global.log.info(socket.id, 'left') })
    })
  }).catch((err) => {
    global.log.error(JSON.stringify(err, null, 2))
    global.log.error("Clients initialization error.")
    process.exit(0)
  })

  global.io.on('connection', (socket) => {
    // generic pass to other script
    socket.on('pass', (msg, fn) => {
      // console.log(msg)
      try {
        // e.g. split 'hello.py' into ['hello', 'py']
        // lang = 'py', module = 'hello'
        var tokens = msg.to.split('.'),
          lang = tokens.pop(),
          module = _.join(tokens, '.')
          // reset of <to> for easy calling. May be empty if just passing to client.<lang>
        msg.to = module
        global.io.sockets.in(lang).emit('take', msg)
      } catch (e) { global.log.error(e) }
    })
  })

  // call clients after setup, or skipped together above
  io_client(robot)
  return global.ioPromise
}

/**
 * Helper: called from within io_server after its setup.
 * Start all polyglot io_client processes using spawn. Kill them on error to prevent runaway processes, i.e. run all the io_import.<language> processes. The io_import.<language> im turn runs all the io clients of that language.
 * @param  {*} robot The hubot object
 */
/* istanbul ignore next */
function io_client(robot) {
  global.log.info("Starting polyglot io_clients")
    // the child processes,kill all on death
  var children = []
    /* istanbul ignore next */
  process.on('exit', () => {
    children.forEach((child) => {
      child.kill()
    })
    global.log.info('Exit: killed io_client.js children')
  })

  // import js locally
  require(path.join(__dirname, 'client.js')).importAll()

  _.each(polyglotCommands, (args, cmd) => {
    // spawn then add listeners, add to the list of child processes
    var cp = spawn(cmd, args, { stdio: [process.stdin, process.stdout, 'pipe'] })
    children.push(cp)

    /* istanbul ignore next */
    cp.stderr.on('data', (data) => {
      global.log.error('err:', data.toString('utf8'))
      cp.kill('SIGTERM') // kill if err to prevent runover process
    })
  })
}

/**
 * The main method to start the io server then clients.
 * Calls server and client methods above.
 */
/* istanbul ignore next */
function io_start(robot) {
  io_server(robot)
  return global.ioPromise
}

/* istanbul ignore next */
var cleanExit = () => { process.exit() }
process.on('SIGINT', cleanExit) // catch ctrl-c
process.on('SIGTERM', cleanExit) // catch kill

// export for use by hubot
module.exports = io_start

// if this file is run directly by `node server.js`
/* istanbul ignore next */
if (require.main === module) {
  io_start()
}
<commit_msg>unify ioClientCmds and their installation cmds, with proper debug logging<commit_after>// The socket.io server and polyglot clients. Called by scripts/_init.js

// dependencies
const Promise = require('bluebird')
const { spawn, execSync } = require('child_process')
const fs = require('fs')
const _ = require('lomath')
const path = require('path')
const { setEnv, log } = require(path.join(__dirname, '..', 'index'))

/* istanbul ignore next */
if (process.env.IOPORT == undefined) { setEnv() }

// import other languages via child_process
var ioClientCmds = _.pickBy({
    ruby: {
      install_dependency: "gem install socket.io-client-simple",
      client: path.join(__dirname, 'client.rb')
    },
    python3: {
      install_dependency: "python3 -m pip install socketIO-client",
      client: path.join(__dirname, 'client.py')
    }
  },
  (args, cmd) => {
    return global.config.get("ACTIVATE_IO_CLIENTS").get(cmd)
  })

const CLIENT_COUNT = 1 + _.size(ioClientCmds)

/**
 * Start a Socket IO server connecting to a robot.server (an Expressjs server), or a brand new Express server for use in dev. Sets global.io too.
 * Has a failsafe for not starting multiple times even when called repeatedly by accident in the same thread.
 * @param  {*} robot The hubot object
 * @return {server} server
 */
/* istanbul ignore next */
function io_server(robot) {
  if (global.io) {
    // if already started elsewhere, skip below including io_client, return promise with the existing server
    return Promise.resolve(global.io.server)
  }

  global.log.info(`Starting socket.io server on IOPORT: ${process.env.IOPORT}`)
  var server
  var app = require('express')()
  process.on('uncaughtException', global.log.error)
  server = app.listen(process.env.IOPORT)

  global.io = require('socket.io').listen(server)
  var count = CLIENT_COUNT
  global.ioPromise = new Promise((resolve, reject) => {
    global.io.sockets.on('connection', (socket) => {
      // serialize for direct communication by using join room
      socket.on('join', (id) => {
        socket.join(id)
        global.log.debug(id, socket.id, 'joined')
        if (--count == 0) {
          global.log.info(`All ${CLIENT_COUNT} clients have joined`)
          resolve(server) // resolve with the server
        }
      })
      socket.on('disconnect', () => { global.log.info(socket.id, 'left') })
    })
  }).catch((err) => {
    global.log.error(JSON.stringify(err, null, 2))
    global.log.error("Clients initialization error.")
    process.exit(0)
  })

  global.io.on('connection', (socket) => {
    // generic pass to other script
    socket.on('pass', (msg, fn) => {
      // console.log(msg)
      try {
        // e.g. split 'hello.py' into ['hello', 'py']
        // lang = 'py', module = 'hello'
        var tokens = msg.to.split('.'),
          lang = tokens.pop(),
          module = _.join(tokens, '.')
          // reset of <to> for easy calling. May be empty if just passing to client.<lang>
        msg.to = module
        global.io.sockets.in(lang).emit('take', msg)
      } catch (e) { global.log.error(e) }
    })
  })

  // call clients after setup, or skipped together above
  io_client(robot)
  return global.ioPromise
}

/**
 * Helper: called from within io_server after its setup.
 * Start all polyglot io_client processes using spawn. Kill them on error to prevent runaway processes, i.e. run all the io_import.<language> processes. The io_import.<language> im turn runs all the io clients of that language.
 * @param  {*} robot The hubot object
 */
/* istanbul ignore next */
function io_client(robot) {
  // the child processes,kill all on death
  var children = []

  /* istanbul ignore next */
  process.on('exit', () => {
    children.forEach((child) => {
      child.kill()
    })
    global.log.info('Exit: killed io_client.js children')
  })

  // import js locally
  require(path.join(__dirname, 'client.js')).importAll()

  _.each(ioClientCmds, (cmds, lang) => {
    // spawn then add listeners, add to the list of child processes
    global.log.info(`Starting socketIO client for ${lang}`)
    global.log.debug(execSync(cmds['install_dependency']).toString())
    var cp = spawn(lang, [cmds['client']], { stdio: [process.stdin, process.stdout, 'pipe'] })
    children.push(cp)

    /* istanbul ignore next */
    cp.stderr.on('data', (data) => {
      global.log.error(`${data.toString('utf8')}`)
      cp.kill('SIGTERM') // kill if err to prevent runover process
    })
  })
}

/**
 * The main method to start the io server then clients.
 * Calls server and client methods above.
 */
/* istanbul ignore next */
function io_start(robot) {
  io_server(robot)
  return global.ioPromise
}

/* istanbul ignore next */
var cleanExit = () => { process.exit() }
process.on('SIGINT', cleanExit) // catch ctrl-c
process.on('SIGTERM', cleanExit) // catch kill

// export for use by hubot
module.exports = io_start

// if this file is run directly by `node server.js`
/* istanbul ignore next */
if (require.main === module) {
  io_start()
}
<|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, ...args) {
	var timeout = setTimeout(delayed, time);

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

// TODO: more browser-specific checks?
var nextloop = (typeof setImmediate === 'function')
	? setImmediate
	: function (callback, ...args) {
		return setTimeout(callback, 0, ...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>Removed rest/spread args from iterwait (so I can possibly maybe spin it off later)<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 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;
<|endoftext|>


<commit_before>/*
 * An implementation of RQL for JavaScript arrays. For example:
 * require("./js-array").query("a=3", {}, [{a:1},{a:3}]) -> [{a:3}]
 *
 */

({define:typeof define!="undefined"?define:function(deps, factory){module.exports = factory(exports, require("./parser"));}}).
define(["exports", "./parser"], function(exports, parser){

var parseQuery = parser.parseQuery;

exports.jsOperatorMap = {
	"eq" : "===",
	"ne" : "!==",
	"le" : "<=",
	"ge" : ">=",
	"lt" : "<",
	"gt" : ">"
};
exports.operators = {
	sort: function(){
		var terms = [];
		for(var i = 0; i < arguments.length; i++){
			var sortAttribute = arguments[i];
			var firstChar = sortAttribute.charAt(0);
			var term = {attribute: sortAttribute, ascending: true};
			if (firstChar == "-" || firstChar == "+") {
				if(firstChar == "-"){
					term.ascending = false;
				}
				term.attribute = term.attribute.substring(1);
			}
			terms.push(term);
		}
		this.sort(function(a, b){
			for (var term, i = 0; term = terms[i]; i++) {
				if (a[term.attribute] != b[term.attribute]) {
					return term.ascending == a[term.attribute] > b[term.attribute] ? 1 : -1;
				}
			}
			return 0;
		});
		return this;
	},
	"in": filter(function(value, values){
		return values.indexOf(value) > -1;
	}),
	any: filter(function(array, value){
		if(typeof value == "function"){
			return array.some(function(i){
				return value([i]).length;
			});
		}
		else{
			return array.indexOf(value) > -1;
		}
	}),
	all: filter(function(array, value){
		if(typeof value == "function"){
			return array.every(function(i){
				return value([i]).length;
			});
		}
		else{
			return array.every(function(i){
				return i == value;
			});
		}
	}),
	or: function(){
		var items = [];
		//TODO: remove duplicates and use condition property
		for(var i = 0; i < arguments.length; i++){
			items = items.concat(arguments[i].call(this));
		}
		return items;
	},
	and: function(){
		var items = this;
		// TODO: use condition property
		for(var i = 0; i < arguments.length; i++){
			items = arguments[i].call(items);
		}
		return items;
	},
	select: function(){
		var args = arguments;
		var argc = arguments.length;
		return this.map(function(object){
			var selected = {};
			for(var i = 0; i < argc; i++){
				var propertyName = args[i];
				if(object.hasOwnProperty(propertyName)){
					selected[propertyName] = object[propertyName];
				}
			}
			return selected;
		});
	},
	values: function(first){
		if(arguments.length == 1){
			return this.map(function(object){
				return object[first];
			});
		}
		var args = arguments;
		var argc = arguments.length;
		return this.map(function(object){
			var selected = [];
			for(var i = 0; i < argc; i++){
				var propertyName = args[i];
				selected.push(object[propertyName]);
			}
			return selected;
		});
	},
	limit: function(limit, start, maxCount){
		var totalCount = this.length;
		start = start || 0;
		var sliced = this.slice(start, start + limit);
		if(maxCount){
			sliced.totalCount = Math.min(totalCount, typeof maxCount === "number" ? maxCount : Infinity);
		}
		return sliced;
	},
	distinct: function(){
		var primitives = {};
		var needCleaning = [];
		var newResults = this.filter(function(value){
			if(value && typeof value == "object"){
				if(!value.__found__){
					value.__found__ = function(){};// get ignored by JSON serialization
					needCleaning.push(value);
					return true;
				}
			}else{
				if(!primitives[value]){
					primitives[value] = true;
					return true;
				}
			}
		});
		needCleaning.forEach(function(object){
			delete object.__found__;
		});
		return newResults;
	},
	recurse: function(property){
		// TODO: this needs to use lazy-array
		var newResults = [];
		function recurse(value){
			if(value instanceof Array){
				value.forEach(recurse);
			}else{
				newResults.push(value);
				if(property){
					value = value[property];
					if(value && typeof value == "object"){
						recurse(value);
					}
				}else{
					for(var i in value){
						if(value[i] && typeof value[i] == "object"){
							recurse(value[i]);
						}
					}
				}
			}
		}
		recurse(this);
		return newResults;
	},
	aggregate: function(){
		var distinctives = [];
		var aggregates = [];
		for(var i = 0; i < arguments.length; i++){
			var arg = arguments[i];
			if(typeof arg === "function"){
				 aggregates.push(arg);
			}else{
				distinctives.push(arg);
			}
		}
		var distinctObjects = {};
		var dl = distinctives.length;
		this.forEach(function(object){
			var key = "";
			for(var i = 0; i < dl;i++){
				key += '/' + object[distinctives[i]];
			}
			var arrayForKey = distinctObjects[key];
			if(!arrayForKey){
				arrayForKey = distinctObjects[key] = [];
			}
			arrayForKey.push(object);
		});
		var al = aggregates.length;
		var newResults = [];
		for(var key in distinctObjects){
			var arrayForKey = distinctObjects[key];
			var newObject = {};
			for(var i = 0; i < dl;i++){
				var property = distinctives[i];
				newObject[property] = arrayForKey[0][property];
			}
			for(var i = 0; i < al;i++){
				var aggregate = aggregates[i];
				newObject[i] = aggregate.call(arrayForKey);
			}
			newResults.push(newObject);
		}
		return newResults;
	},
	sum: reducer(function(a, b){
		return a + b;
	}),
	mean: function(property){
		return exports.operators.sum.call(this, property)/this.length;
	},
	max: reducer(function(a, b){
		return Math.max(a, b);
	}),
	min: reducer(function(a, b){
		return Math.min(a, b);
	}),
	count: function(){
		return this.length;
	},
	first: function(){
		return this[0];
	},
	one: function(){
		if(this.length > 1){
			throw new TypeError("More than one object found");
		}
		return this[0];
	}
};
exports.filter = filter;
function filter(condition){
	var filter = function(property, second){
		var args = arguments;
		var filtered = [];
		for(var i = 0, length = this.length; i < length; i++){
			var item = this[i];
			if(condition(evaluateProperty(item, property), second)){
				filtered.push(item);
			}
		}
		return filtered;
	};
	filter.condition = condition;
	return filter;
};
function reducer(func){
	return function(property){
		if(property){
			return this.map(function(object){
				return object[property];
			}).reduce(func);
		}else{
			return this.reduce(func);
		}
	}
}
exports.evaluateProperty = evaluateProperty;
function evaluateProperty(object, property){
	if(property.indexOf(".") === -1){
		return object[decodeURIComponent(property)];
	}
	else{
		property.split(".").forEach(function(part){
			object = object[decodeURIComponent(part)];
		});
		return object;
	}
};
var conditionEvaluator = exports.conditionEvaluator = function(condition){
	var jsOperator = exports.jsOperatorMap[term.name];
	if(jsOperator){
		js += "(function(item){return item." + term[0] + jsOperator + "parameters[" + (index -1) + "][1];});";
	}
	else{
		js += "operators['" + term.name + "']";
	}
	return eval(js);
};
exports.executeQuery = function(query, options, target){
	return exports.query(query, options, target);
}
exports.query = query;
function query(query, options, target){
	options = options || {};
	query = parseQuery(query, options.parameters);
	function t(){}
	t.prototype = exports.operators;
	var operators = new t;
	// inherit from exports.operators
	for(var i in options.operators){
		operators[i] = options.operators[i];
	}
	var parameters = options.parameters || [];
	var js = "";
	function queryToJS(value){
		if(value && typeof value === "object" && !(value instanceof Array)){
			var jsOperator = exports.jsOperatorMap[value.name];
			if(jsOperator){
				if (jsOperator === '===' && typeof value.args[1] === 'string' && value.args[1].indexOf('re:') === 0){
					jsOperator = '.match(/'+value.args[1].substring(3)+'/i)&&""===';
					value.args[1] = '';
					//console.log('OP', jsOperator, queryToJS(value.args[1]), value.args[1]);
				}
				//return "(function(){var filtered = []; for(var i = 0, length = this.length; i < length; i++){var item = this[i];if(item." + value.args[0] + jsOperator + queryToJS(value.args[1]) + "){filtered.push(item);}} return filtered;})";
				var s = "(function(){var filtered = []; for(var i = 0, length = this.length; i < length; i++){var item = this[i];if(item." + value.args[0] + jsOperator + queryToJS(value.args[1]) + "){filtered.push(item);}} return filtered;})";
				//console.log('FUNC', s);
				return s;
			}else{
				return "(function(){return operators['" + value.name + "'].call(this" +
					(value && value.args && value.args.length > 0 ? (", " + value.args.map(queryToJS).join(",")) : "") +
					")})";
			}
		}else{
			return typeof value === "number" ? value : JSON.stringify(value);
		}
	}
	var evaluator = eval("(function(target){return " + queryToJS(query) + ".call(target);})");
	return target ? evaluator(target) : evaluator;
}
function throwMaxIterations(){
	throw new Error("Query has taken too much computation, and the user is not allowed to execute resource-intense queries. Increase maxIterations in your config file to allow longer running non-indexed queries to be processed.");
}
exports.maxIterations = 10000;
return exports;
});
<commit_msg>undefined.call() fix<commit_after>/*
 * An implementation of RQL for JavaScript arrays. For example:
 * require("./js-array").query("a=3", {}, [{a:1},{a:3}]) -> [{a:3}]
 *
 */

({define:typeof define!="undefined"?define:function(deps, factory){module.exports = factory(exports, require("./parser"));}}).
define(["exports", "./parser"], function(exports, parser){

var parseQuery = parser.parseQuery;

exports.jsOperatorMap = {
	"eq" : "===",
	"ne" : "!==",
	"le" : "<=",
	"ge" : ">=",
	"lt" : "<",
	"gt" : ">"
};
exports.operators = {
	sort: function(){
		var terms = [];
		for(var i = 0; i < arguments.length; i++){
			var sortAttribute = arguments[i];
			var firstChar = sortAttribute.charAt(0);
			var term = {attribute: sortAttribute, ascending: true};
			if (firstChar == "-" || firstChar == "+") {
				if(firstChar == "-"){
					term.ascending = false;
				}
				term.attribute = term.attribute.substring(1);
			}
			terms.push(term);
		}
		this.sort(function(a, b){
			for (var term, i = 0; term = terms[i]; i++) {
				if (a[term.attribute] != b[term.attribute]) {
					return term.ascending == a[term.attribute] > b[term.attribute] ? 1 : -1;
				}
			}
			return 0;
		});
		return this;
	},
	"in": filter(function(value, values){
		return values.indexOf(value) > -1;
	}),
	any: filter(function(array, value){
		if(typeof value == "function"){
			return array.some(function(i){
				return value([i]).length;
			});
		}
		else{
			return array.indexOf(value) > -1;
		}
	}),
	all: filter(function(array, value){
		if(typeof value == "function"){
			return array.every(function(i){
				return value([i]).length;
			});
		}
		else{
			return array.every(function(i){
				return i == value;
			});
		}
	}),
	or: function(){
		var items = [];
		//TODO: remove duplicates and use condition property
		for(var i = 0; i < arguments.length; i++){
			items = items.concat(arguments[i].call(this));
		}
		return items;
	},
	and: function(){
		var items = this;
		// TODO: use condition property
		for(var i = 0; i < arguments.length; i++){
			items = arguments[i].call(items);
		}
		return items;
	},
	select: function(){
		var args = arguments;
		var argc = arguments.length;
		return this.map(function(object){
			var selected = {};
			for(var i = 0; i < argc; i++){
				var propertyName = args[i];
				if(object.hasOwnProperty(propertyName)){
					selected[propertyName] = object[propertyName];
				}
			}
			return selected;
		});
	},
	values: function(first){
		if(arguments.length == 1){
			return this.map(function(object){
				return object[first];
			});
		}
		var args = arguments;
		var argc = arguments.length;
		return this.map(function(object){
			var selected = [];
			for(var i = 0; i < argc; i++){
				var propertyName = args[i];
				selected.push(object[propertyName]);
			}
			return selected;
		});
	},
	limit: function(limit, start, maxCount){
		var totalCount = this.length;
		start = start || 0;
		var sliced = this.slice(start, start + limit);
		if(maxCount){
			sliced.totalCount = Math.min(totalCount, typeof maxCount === "number" ? maxCount : Infinity);
		}
		return sliced;
	},
	distinct: function(){
		var primitives = {};
		var needCleaning = [];
		var newResults = this.filter(function(value){
			if(value && typeof value == "object"){
				if(!value.__found__){
					value.__found__ = function(){};// get ignored by JSON serialization
					needCleaning.push(value);
					return true;
				}
			}else{
				if(!primitives[value]){
					primitives[value] = true;
					return true;
				}
			}
		});
		needCleaning.forEach(function(object){
			delete object.__found__;
		});
		return newResults;
	},
	recurse: function(property){
		// TODO: this needs to use lazy-array
		var newResults = [];
		function recurse(value){
			if(value instanceof Array){
				value.forEach(recurse);
			}else{
				newResults.push(value);
				if(property){
					value = value[property];
					if(value && typeof value == "object"){
						recurse(value);
					}
				}else{
					for(var i in value){
						if(value[i] && typeof value[i] == "object"){
							recurse(value[i]);
						}
					}
				}
			}
		}
		recurse(this);
		return newResults;
	},
	aggregate: function(){
		var distinctives = [];
		var aggregates = [];
		for(var i = 0; i < arguments.length; i++){
			var arg = arguments[i];
			if(typeof arg === "function"){
				 aggregates.push(arg);
			}else{
				distinctives.push(arg);
			}
		}
		var distinctObjects = {};
		var dl = distinctives.length;
		this.forEach(function(object){
			var key = "";
			for(var i = 0; i < dl;i++){
				key += '/' + object[distinctives[i]];
			}
			var arrayForKey = distinctObjects[key];
			if(!arrayForKey){
				arrayForKey = distinctObjects[key] = [];
			}
			arrayForKey.push(object);
		});
		var al = aggregates.length;
		var newResults = [];
		for(var key in distinctObjects){
			var arrayForKey = distinctObjects[key];
			var newObject = {};
			for(var i = 0; i < dl;i++){
				var property = distinctives[i];
				newObject[property] = arrayForKey[0][property];
			}
			for(var i = 0; i < al;i++){
				var aggregate = aggregates[i];
				newObject[i] = aggregate.call(arrayForKey);
			}
			newResults.push(newObject);
		}
		return newResults;
	},
	sum: reducer(function(a, b){
		return a + b;
	}),
	mean: function(property){
		return exports.operators.sum.call(this, property)/this.length;
	},
	max: reducer(function(a, b){
		return Math.max(a, b);
	}),
	min: reducer(function(a, b){
		return Math.min(a, b);
	}),
	count: function(){
		return this.length;
	},
	first: function(){
		return this[0];
	},
	one: function(){
		if(this.length > 1){
			throw new TypeError("More than one object found");
		}
		return this[0];
	}
};
exports.filter = filter;
function filter(condition){
	var filter = function(property, second){
		var args = arguments;
		var filtered = [];
		for(var i = 0, length = this.length; i < length; i++){
			var item = this[i];
			if(condition(evaluateProperty(item, property), second)){
				filtered.push(item);
			}
		}
		return filtered;
	};
	filter.condition = condition;
	return filter;
};
function reducer(func){
	return function(property){
		if(property){
			return this.map(function(object){
				return object[property];
			}).reduce(func);
		}else{
			return this.reduce(func);
		}
	}
}
exports.evaluateProperty = evaluateProperty;
function evaluateProperty(object, property){
	if(property.indexOf(".") === -1){
		return object[decodeURIComponent(property)];
	}
	else{
		property.split(".").forEach(function(part){
			object = object[decodeURIComponent(part)];
		});
		return object;
	}
};
var conditionEvaluator = exports.conditionEvaluator = function(condition){
	var jsOperator = exports.jsOperatorMap[term.name];
	if(jsOperator){
		js += "(function(item){return item." + term[0] + jsOperator + "parameters[" + (index -1) + "][1];});";
	}
	else{
		js += "operators['" + term.name + "']";
	}
	return eval(js);
};
exports.executeQuery = function(query, options, target){
	return exports.query(query, options, target);
}
exports.query = query;
function relay(){return this;}
function query(query, options, target){
	options = options || {};
	query = parseQuery(query, options.parameters);
	function t(){}
	t.prototype = exports.operators;
	var operators = new t;
	// inherit from exports.operators
	for(var i in options.operators){
		operators[i] = options.operators[i];
	}
	var parameters = options.parameters || [];
	var js = "";
	function queryToJS(value){
		if(value && typeof value === "object" && !(value instanceof Array)){
			var jsOperator = exports.jsOperatorMap[value.name];
			if(jsOperator){
				if (jsOperator === '===' && typeof value.args[1] === 'string' && value.args[1].indexOf('re:') === 0){
					jsOperator = '.match(/'+value.args[1].substring(3)+'/i)&&""===';
					value.args[1] = '';
					//console.log('OP', jsOperator, queryToJS(value.args[1]), value.args[1]);
				}
				//return "(function(){var filtered = []; for(var i = 0, length = this.length; i < length; i++){var item = this[i];if(item." + value.args[0] + jsOperator + queryToJS(value.args[1]) + "){filtered.push(item);}} return filtered;})";
				var s = "(function(){var filtered = []; for(var i = 0, length = this.length; i < length; i++){var item = this[i];if(item." + value.args[0] + jsOperator + queryToJS(value.args[1]) + "){filtered.push(item);}} return filtered;})";
				//console.log('FUNC', s);
				return s;
			}else{
				return "(function(){return (operators['" + value.name + "']||relay).call(this" +
					(value && value.args && value.args.length > 0 ? (", " + value.args.map(queryToJS).join(",")) : "") +
					")})";
			}
		}else{
			return typeof value === "number" ? value : JSON.stringify(value);
		}
	}
	var evaluator = eval("(function(target){return " + queryToJS(query) + ".call(target);})");
	return target ? evaluator(target) : evaluator;
}
function throwMaxIterations(){
	throw new Error("Query has taken too much computation, and the user is not allowed to execute resource-intense queries. Increase maxIterations in your config file to allow longer running non-indexed queries to be processed.");
}
exports.maxIterations = 10000;
return exports;
});
<|endoftext|>


<commit_before>(function(ext) {

    var firstTime = true;
    var boolean = true;
    var jsonObject = null;
    var xScale = 240;
    var yScale = 180;
    var status = 1;
    var joints = [];

    alert("BEFORE CLICKING OK: Make sure you have have followed the instructions in Kinect2Scratch");
    console.log("Right after the alert");

    var wsImpl = window.WebSocket || window.MozWebSocket;

    console.log("connecting to server ..");

    // create a new websocket and connect
    window.ws = new wsImpl('ws://localhost:8181/');

    // when data is comming from the server, this metod is called
    ws.onmessage = function (evt) {
        //console.log(evt.data + '
');
        if(evt != "0")
        {
        jsonObject = JSON.parse(evt.data);
            if(jsonObject.bodies == '')
            {
                status = 1;
            } else
            {
                status = 2;
            }
        }
    };

    // when the connection is established, this method is called
    ws.onopen = function () {
        console.log('.. connection open');
        //status = 1;
    };

    // when the connection is closed, this method is called
    ws.onclose = function () {
        console.log('.. connection closed');
        status = 0;
    };

    // Cleanup function when the extension is unloaded
    ext._shutdown = function() {};

    // Status reporting code
    // Use this to report missing hardware, plugin or unsupported browser
    ext._getStatus = function() {
        if(status == 0)
        {
            return {status: 0, msg: 'Kinect is not connected to Scratch'};
            //polling function for auto-reconnect should go here
        }
        if(status == 1)
        {
            return {status: 1, msg: 'Kinect is connected, but is not detecting any bodies'};
        }
        if(status == 2)
        {
            return {status: 2, msg: 'Kinect is sending body data'};
        }

    };

        // Block and block menu descriptions
    var descriptor = {
        blocks: [
            ['', 'My First Block', 'my_first_block'],
            ['r', '%n ^ %n', 'power', 2, 3],
            ['r', '%m.k sensor value', 'k', 'Head X'],
            ['', 'restart connection', 'restart'],
            ['', 'test block', 'test_block'],
            ['b', 'connected', 'connected'],
            ['b', 'body tracked', 'tracked']
        ],

        menus: {
	    k: ['Left Ankle X', 'Left Ankle Y', 'Right Ankle X', 'Right Ankle Y', 'Left Elbow X', 'Left Elbow Y', 'Right Elbow X', 'Right Elbow Y', 'Left Foot X', 'Left Foot Y', 'Right Foot X', 'Right Foot Y', 'Left Hand X', 'Left Hand Y', 'Right Hand X', 'Right Hand Y', 'Left Hand Tip X', 'Left Hand Tip Y', 'Right Hand Tip X', 'Right Hand Tip Y', 'Head X', 'Head Y', 'Left Hip X', 'Left Hip Y', 'Right Hip X', 'Right Hip Y', 'Left Knee X', 'Left Knee Y', 'Right Knee X', 'Right Knee Y', 'Neck X', 'Neck Y', 'Left Shoulder X', 'Left Shoulder Y', 'Right Shoulder X', 'Right Shoulder Y', 'Spine Base X', 'Spine Base Y', 'Spine Middle X', 'Spine Middle Y', 'Spine Shoulder X', 'Spine Shoulder Y', 'Left Thumb X', 'Left Thumb Y', 'Right Thumb X', 'Right Thumb Y', 'Left Wrist X', 'Left Wrist Y', 'Right Wrist X', 'Right Wrist Y']
    }
    };

	ext.my_first_block = function() {
        console.log("My first block");
    };

    ext.restart = function() {
        console.log("connecting to server ..");
        window.ws = new wsImpl('ws://localhost:8181/');
    };

    ext.power = function(base, exponent) {
        return Math.pow(base, exponent);
    };

    ext.test_block = function() {
        console.log(jsonObject.bodies[0].joints[3].x);
    };

        ext.connected = function()
    {
        if(status == 0){
            return false;
        }

        if(status == 1 || 2){
            return true;
        }
    };

    ext.tracked = function()
    {
        if(status == (0 || 1)){
            return false;
        }

        if(status == 2){
            return true;
        }
    };

    ext.k = function(m) {
        var temp = 0;
        switch(m){
            case 'Left Ankle X': return jsonObject.bodies[0].joints[14].x*xScale;
            case 'Left Ankle Y': return jsonObject.bodies[0].joints[14].y*yScale;
            case 'Right Ankle X': return jsonObject.bodies[0].joints[18].x*xScale;
            case 'Right Ankle Y': return jsonObject.bodies[0].joints[18].y*yScale;
            case 'Left Elbow X': return jsonObject.bodies[0].joints[5].x*xScale;
            case 'Left Elbow Y': return jsonObject.bodies[0].joints[5].y*yScale;
            case 'Right Elbow X': return jsonObject.bodies[0].joints[9].x*xScale;
            case 'Right Elbow Y': return jsonObject.bodies[0].joints[9].y*yScale;
            case 'Left Foot X': return jsonObject.bodies[0].joints[15].x*xScale;
            case 'Left Foot Y': return jsonObject.bodies[0].joints[15].y*yScale;
            case 'Right Foot X': return jsonObject.bodies[0].joints[19].x*xScale;
            case 'Right Foot Y': return jsonObject.bodies[0].joints[19].y*yScale;
            case 'Left Hand X': return jsonObject.bodies[0].joints[7].x*xScale;
            case 'Left Hand Y': return jsonObject.bodies[0].joints[7].y*yScale;
            case 'Right Hand X': return jsonObject.bodies[0].joints[11].x*xScale;
            case 'Right Hand Y': return jsonObject.bodies[0].joints[11].y*yScale;
            case 'Left Hand Tip X': return jsonObject.bodies[0].joints[21].x*xScale;
            case 'Left Hand Tip Y': return jsonObject.bodies[0].joints[21].y*yScale;
            case 'Right Hand Tip X': return jsonObject.bodies[0].joints[23].x*xScale;
            case 'Right Hand Tip Y': return jsonObject.bodies[0].joints[23].y*yScale;
            case 'Head X': return jsonObject.bodies[0].joints[3].x*xScale;
            case 'Head Y': return jsonObject.bodies[0].joints[3].y*yScale;
            case 'Left Hip X': return jsonObject.bodies[0].joints[12].x*xScale;
            case 'Left Hip Y': return jsonObject.bodies[0].joints[12].y*yScale;
            case 'Right Hip X': return jsonObject.bodies[0].joints[16].x*xScale;
            case 'Right Hip Y': return jsonObject.bodies[0].joints[16].y*yScale;
            case 'Left Knee X': return jsonObject.bodies[0].joints[13].x*xScale;
            case 'Left Knee Y': return jsonObject.bodies[0].joints[13].y*yScale;
            case 'Right Knee X': return jsonObject.bodies[0].joints[17].x*xScale;
            case 'Right Knee Y': return jsonObject.bodies[0].joints[17].y*yScale;
            case 'Neck X': return jsonObject.bodies[0].joints[2].x*xScale;
            case 'Neck Y': return jsonObject.bodies[0].joints[2].y*yScale;
            case 'Left Shoulder X': return jsonObject.bodies[0].joints[4].x*xScale;
            case 'Left Shoulder Y': return jsonObject.bodies[0].joints[4].y*yScale;
            case 'Right Shoulder X': return jsonObject.bodies[0].joints[8].x*xScale;
            case 'Right Shoulder Y': return jsonObject.bodies[0].joints[8].y*yScale;
            case 'Spine Base X': return jsonObject.bodies[0].joints[0].x*xScale;
            case 'Spine Base Y': return jsonObject.bodies[0].joints[0].y*yScale;
            case 'Spine Middle X': return jsonObject.bodies[0].joints[1].x*xScale;
            case 'Spine Middle Y': return jsonObject.bodies[0].joints[1].y*yScale;
            case 'Spine Shoulder X': return jsonObject.bodies[0].joints[20].x*xScale;
            case 'Spine Shoulder Y': return jsonObject.bodies[0].joints[20].y*yScale;
            case 'Left Thumb X': return jsonObject.bodies[0].joints[22].x*xScale;
            case 'Left Thumb Y': return jsonObject.bodies[0].joints[22].y*yScale;
            case 'Right Thumb X': return jsonObject.bodies[0].joints[24].x*xScale;
            case 'Right Thumb Y': return jsonObject.bodies[0].joints[24].y*yScale;
            case 'Left Wrist X': return jsonObject.bodies[0].joints[6].x*xScale;
            case 'Left Wrist Y': return jsonObject.bodies[0].joints[6].y*yScale;
            case 'Right Wrist X': return jsonObject.bodies[0].joints[10].x*xScale;
            case 'Right Wrist Y': return jsonObject.bodies[0].joints[10].y*yScale;
        }
    };

    // Register the extension
    ScratchExtensions.register('KinectinScratch', descriptor, ext);
})({});<commit_msg>Try one more thing to completely eliminate my code from the issue.<commit_after>(function(ext) {

    var firstTime = true;
    var boolean = true;
    var jsonObject = null;
    var xScale = 240;
    var yScale = 180;
    var status = 1;
    var joints = [];

    alert("BEFORE CLICKING OK: Make sure you have have followed the instructions in Kinect2Scratch");
    console.log("Right after the alert");

    var wsImpl = window.WebSocket || window.MozWebSocket;

    console.log("connecting to server ..");

    // create a new websocket and connect
    window.ws = new wsImpl('ws://localhost:8181/');

    // when data is comming from the server, this metod is called
    ws.onmessage = function (evt) {
        //console.log(evt.data + '
');
        if(evt != "0")
        {
        jsonObject = JSON.parse(evt.data);
            if(jsonObject.bodies == '')
            {
                status = 1;
            } else
            {
                status = 2;
            }
        }
    };

    // when the connection is established, this method is called
    ws.onopen = function () {
        console.log('.. connection open');
        //status = 1;
    };

    // when the connection is closed, this method is called
    ws.onclose = function () {
        console.log('.. connection closed');
        status = 0;
    };

    // Cleanup function when the extension is unloaded
    ext._shutdown = function() {};

    // Status reporting code
    // Use this to report missing hardware, plugin or unsupported browser
    ext._getStatus = function() {
        if(status == 0)
        {
            return {status: 0, msg: 'Kinect is not connected to Scratch'};
            //polling function for auto-reconnect should go here
        }
        if(status == 1)
        {
            return {status: 1, msg: 'Kinect is connected, but is not detecting any bodies'};
        }
        if(status == 2)
        {
            return {status: 2, msg: 'Kinect is sending body data'};
        }

    };

        // Block and block menu descriptions
    var descriptor = {
        blocks: [
            ['', 'My First Block', 'my_first_block'],
            ['r', '%n ^ %n', 'power', 2, 3],
            ['r', '%m.k sensor value', 'k', 'Head X'],
            ['', 'restart connection', 'restart'],
            ['', 'test block', 'test_block'],
            ['b', 'connected', 'connected'],
            ['b', 'body tracked', 'tracked']
        ],

        menus: {
	    k: ['Left Ankle X', 'Left Ankle Y', 'Right Ankle X', 'Right Ankle Y', 'Left Elbow X', 'Left Elbow Y', 'Right Elbow X', 'Right Elbow Y', 'Left Foot X', 'Left Foot Y', 'Right Foot X', 'Right Foot Y', 'Left Hand X', 'Left Hand Y', 'Right Hand X', 'Right Hand Y', 'Left Hand Tip X', 'Left Hand Tip Y', 'Right Hand Tip X', 'Right Hand Tip Y', 'Head X', 'Head Y', 'Left Hip X', 'Left Hip Y', 'Right Hip X', 'Right Hip Y', 'Left Knee X', 'Left Knee Y', 'Right Knee X', 'Right Knee Y', 'Neck X', 'Neck Y', 'Left Shoulder X', 'Left Shoulder Y', 'Right Shoulder X', 'Right Shoulder Y', 'Spine Base X', 'Spine Base Y', 'Spine Middle X', 'Spine Middle Y', 'Spine Shoulder X', 'Spine Shoulder Y', 'Left Thumb X', 'Left Thumb Y', 'Right Thumb X', 'Right Thumb Y', 'Left Wrist X', 'Left Wrist Y', 'Right Wrist X', 'Right Wrist Y']
    }
    };

	ext.my_first_block = function() {
        console.log("My first block");
    };

    ext.restart = function() {
        console.log("connecting to server ..");
        window.ws = new wsImpl('ws://localhost:8181/');
    };

    ext.power = function(base, exponent) {
        return Math.pow(base, exponent);
    };

    ext.test_block = function() {
        console.log(jsonObject.bodies[0].joints[3].x*xScale);
    };

        ext.connected = function()
    {
        if(status == 0){
            return false;
        }

        if(status == 1 || 2){
            return true;
        }
    };

    ext.tracked = function()
    {
        if(status == (0 || 1)){
            return false;
        }

        if(status == 2){
            return true;
        }
    };

    ext.k = function(m) {
        var temp = 0;
        switch(m){
            case 'Left Ankle X': return jsonObject.bodies[0].joints[14].x*xScale;
            case 'Left Ankle Y': return jsonObject.bodies[0].joints[14].y*yScale;
            case 'Right Ankle X': return jsonObject.bodies[0].joints[18].x*xScale;
            case 'Right Ankle Y': return jsonObject.bodies[0].joints[18].y*yScale;
            case 'Left Elbow X': return jsonObject.bodies[0].joints[5].x*xScale;
            case 'Left Elbow Y': return jsonObject.bodies[0].joints[5].y*yScale;
            case 'Right Elbow X': return jsonObject.bodies[0].joints[9].x*xScale;
            case 'Right Elbow Y': return jsonObject.bodies[0].joints[9].y*yScale;
            case 'Left Foot X': return jsonObject.bodies[0].joints[15].x*xScale;
            case 'Left Foot Y': return jsonObject.bodies[0].joints[15].y*yScale;
            case 'Right Foot X': return jsonObject.bodies[0].joints[19].x*xScale;
            case 'Right Foot Y': return jsonObject.bodies[0].joints[19].y*yScale;
            case 'Left Hand X': return jsonObject.bodies[0].joints[7].x*xScale;
            case 'Left Hand Y': return jsonObject.bodies[0].joints[7].y*yScale;
            case 'Right Hand X': return jsonObject.bodies[0].joints[11].x*xScale;
            case 'Right Hand Y': return jsonObject.bodies[0].joints[11].y*yScale;
            case 'Left Hand Tip X': return jsonObject.bodies[0].joints[21].x*xScale;
            case 'Left Hand Tip Y': return jsonObject.bodies[0].joints[21].y*yScale;
            case 'Right Hand Tip X': return jsonObject.bodies[0].joints[23].x*xScale;
            case 'Right Hand Tip Y': return jsonObject.bodies[0].joints[23].y*yScale;
            case 'Head X': return jsonObject.bodies[0].joints[3].x*xScale;
            case 'Head Y': return jsonObject.bodies[0].joints[3].y*yScale;
            case 'Left Hip X': return jsonObject.bodies[0].joints[12].x*xScale;
            case 'Left Hip Y': return jsonObject.bodies[0].joints[12].y*yScale;
            case 'Right Hip X': return jsonObject.bodies[0].joints[16].x*xScale;
            case 'Right Hip Y': return jsonObject.bodies[0].joints[16].y*yScale;
            case 'Left Knee X': return jsonObject.bodies[0].joints[13].x*xScale;
            case 'Left Knee Y': return jsonObject.bodies[0].joints[13].y*yScale;
            case 'Right Knee X': return jsonObject.bodies[0].joints[17].x*xScale;
            case 'Right Knee Y': return jsonObject.bodies[0].joints[17].y*yScale;
            case 'Neck X': return jsonObject.bodies[0].joints[2].x*xScale;
            case 'Neck Y': return jsonObject.bodies[0].joints[2].y*yScale;
            case 'Left Shoulder X': return jsonObject.bodies[0].joints[4].x*xScale;
            case 'Left Shoulder Y': return jsonObject.bodies[0].joints[4].y*yScale;
            case 'Right Shoulder X': return jsonObject.bodies[0].joints[8].x*xScale;
            case 'Right Shoulder Y': return jsonObject.bodies[0].joints[8].y*yScale;
            case 'Spine Base X': return jsonObject.bodies[0].joints[0].x*xScale;
            case 'Spine Base Y': return jsonObject.bodies[0].joints[0].y*yScale;
            case 'Spine Middle X': return jsonObject.bodies[0].joints[1].x*xScale;
            case 'Spine Middle Y': return jsonObject.bodies[0].joints[1].y*yScale;
            case 'Spine Shoulder X': return jsonObject.bodies[0].joints[20].x*xScale;
            case 'Spine Shoulder Y': return jsonObject.bodies[0].joints[20].y*yScale;
            case 'Left Thumb X': return jsonObject.bodies[0].joints[22].x*xScale;
            case 'Left Thumb Y': return jsonObject.bodies[0].joints[22].y*yScale;
            case 'Right Thumb X': return jsonObject.bodies[0].joints[24].x*xScale;
            case 'Right Thumb Y': return jsonObject.bodies[0].joints[24].y*yScale;
            case 'Left Wrist X': return jsonObject.bodies[0].joints[6].x*xScale;
            case 'Left Wrist Y': return jsonObject.bodies[0].joints[6].y*yScale;
            case 'Right Wrist X': return jsonObject.bodies[0].joints[10].x*xScale;
            case 'Right Wrist Y': return jsonObject.bodies[0].joints[10].y*yScale;
        }
    };

    // Register the extension
    ScratchExtensions.register('KinectinScratch', descriptor, ext);
})({});<|endoftext|>