<commit_before>const { LokaliseApi } = require('@lokalise/node-api');

async function uploadToLokalise() {
  try {
    const lokaliseToken = process.env.TOKEN;
    const branchName = process.env.GITHUB_REF.replace('refs/heads/', '');
    const projectId = process.env.PROJECT_ID;
    const files = process.env.FILES.split(' ');
    const lokalise = new LokaliseApi({ apiKey: lokaliseToken });

    if (!files.length > 0) {
      const promises = [];
      files.forEach(path => {
        promises.push(
          lokalise.files.upload(`${projectId}:${branchName}`, {
            filename: path,
            lang_iso: 'en'
          })
        );
      });

      Promise.all(promises).then(() => {
        console.log('New translations have been uploaded');
        process.exit(0);
      });
    }
    console.log('No new translations');
    process.exit(0);
  } catch (e) {
    console.log(e);
    process.exit(1);
  }
}

uploadToLokalise();
<commit_msg>devop: 🔧 cleanup upload script and make sure it's working properly<commit_after>const { LokaliseApi } = require('@lokalise/node-api');
const fs = require('fs');

async function uploadToLokalise() {
  try {
    const lokaliseToken = process.env.TOKEN;
    const branchName = process.env.GITHUB_REF.replace('refs/heads/', '');
    const projectId = process.env.PROJECT_ID;
    const files = process.env.FILES.split(' ');
    const lokalise = new LokaliseApi({ apiKey: lokaliseToken });

    if (!files.length > 0) {
      const promises = [];
      files.forEach(path => {
        const splitPath = path.split('/');
        const readFile = fs.readFileSync(path);
        if (splitPath.includes('en_US')) {
          promises.push(
            lokalise.files.upload(`${projectId}:${branchName}`, {
              data: readFile.toString('base64'),
              filename: path,
              lang_iso: 'en'
            })
          );
        }
      });

      Promise.all(promises).then(() => {
        console.log('New translations have been uploaded');
        process.exit(0);
      });
    } else {
      console.log('No new translations');
      process.exit(0);
    }
  } catch (e) {
    console.log(e);
    process.exit(1);
  }
}

uploadToLokalise();
<|endoftext|>


<commit_before>// Generate a pass file.

var applyImageMethods = require("./images");
var Crypto            = require("crypto");
var EventEmitter      = require("events").EventEmitter;
var execFile          = require("child_process").execFile;
var Stream            = require("stream");
var inherits          = require("util").inherits;
var File              = require("fs");
var HTTP              = require("follow-redirects").http;
var HTTPS             = require("follow-redirects").https;
var Path              = require("path");
var Zip               = require("./zip");
var zlib              = require("zlib");

// Top-level pass fields.
var TOP_LEVEL           = [ "appLaunchURL", "associatedStoreIdentifiers", "authenticationToken",
                            "backgroundColor", "barcode", "description",
                            "foregroundColor", "labelColor", "locations", "logoText",
                            "organizationName", "relevantDate", "serialNumber",
                            "suppressStripShine", "webServiceURL", "beacons"];
// These top level fields are required for a valid pass.
var REQUIRED_TOP_LEVEL  = [ "description", "organizationName", "passTypeIdentifier",
                            "serialNumber", "teamIdentifier" ];
// Pass structure keys.
var STRUCTURE_FIELDS    = [ "auxiliaryFields", "backFields", "headerFields",
                            "primaryFields", "secondaryFields" ];
// These images are required for a valid pass.
var REQUIRED_IMAGES     = [ "icon", "logo" ];

// Create a new pass.
//
// template  - The template
// fields    - Pass fields (description, serialNumber, logoText)
function Pass(template, fields, images) {
  this.template = template;
  this.fields = cloneObject(fields);
  // Structure is basically reference to all the fields under a given style
  // key, e.g. if style is coupon then structure.primaryFields maps to
  // fields.coupon.primaryFields.
  var style = template.style;
  this.structure = this.fields[style];
  if (!this.structure)
    this.structure = this.fields[style] = {};
  this.images = cloneObject(images);
}

inherits(Pass, EventEmitter);
applyImageMethods(Pass);

// Accessor methods for top-level fields (description, serialNumber, logoText,
// etc).
//
// Call with an argument to set field and return self, call with no argument to
// get field value.
//
//   pass.description("Unbelievable discount");
//   console.log(pass.description());
TOP_LEVEL.forEach(function(key) {
  Pass.prototype[key] = function(value) {
    if (arguments.length === 0) {
      return this.fields[key];
    } else {
      this.fields[key] = value;
      return this;
    }
  };
});

// Accessor methods for structure fields (primaryFields, backFields, etc).
//
// For example:
//
//   pass.headerFields.add("time", "The Time", "10:00AM");
//   pass.backFields.add("url", "Web site", "http://example.com");
STRUCTURE_FIELDS.forEach(function(key) {
  Pass.prototype.__defineGetter__(key, function() {
    return new Fields(this, key);
  });
});

// Field accessors.
function Fields(pass, key) {
  this.pass = pass;
  this.key = key;
}

// Adds a field to the end of the list.
//
// You can call this method with three/four arguments:
// key      - Field key
// label    - Field label (optional)
// value    - Field value
// options  - Other field options (e.g. dateStyle)
//
// You can call this method with a single object that contains all field
// properties (key, label, etc).
//
// You can also call with an array of either one.
//
// Returns self.
Fields.prototype.add = function(key, label, value, options) {
  var field, k;
  if (arguments.length > 1) {
    this.remove(key);
    field = { key: key, value: value };
    if (label)
      field.label = label;
    if (options) {
      for (k in options)
        field[k] = options[k];
    }
    this.all().push(field);
  } else if (Array.isArray(arguments[0])) {
    var array = arguments[0];
    for (var i in array)
      this.add.call(this, array[i]);
  } else {
    var properties = arguments[0];
    key = properties.key;
    this.remove(key);
    field = {};
      for (k in properties)
        field[k] = properties[k];
    this.all().push(field);
  }
  return this;
};

// Returns a field.
//
// If field exists, returns an object with:
// key      - Field key
// label    - Field label (optional)
// value    - Field value
// Other field options (e.g. dateStyle)
Fields.prototype.get = function(key) {
  var fields = this.pass.structure[this.key];
  if (fields) {
    for (var i in fields) {
      var field = fields[i];
      if (field.key == key)
        return field;
    }
  }
  return null;
};

// Returns an array of all fields.
Fields.prototype.all = function() {
  var fields = this.pass.structure[this.key];
  if (!fields)
    this.pass.structure[this.key] = fields = [];
  return fields;
};

// Removes a given field.
Fields.prototype.remove = function(key) {
  var fields = this.pass.structure[this.key];
  if (fields) {
    for (var i in fields) {
      if (fields[i].key == key) {
        fields.splice(i, 1);
        break;
      }
    }
  }
  return this;
};

// Removes all fields.
Fields.prototype.clear = function() {
  this.pass.structure[this.key] = [];
  return this;
};

// Validate pass, throws error if missing a mandatory top-level field or image.
Pass.prototype.validate = function() {
  for (var i in REQUIRED_TOP_LEVEL) {
    var k1 = REQUIRED_TOP_LEVEL[i];
    if (!this.fields[k1])
      throw new Error("Missing field " + k1);
  }
  for (var j in REQUIRED_IMAGES) {
    var k2 = REQUIRED_IMAGES[j];
    if (!this.images[k2])
      throw new Error("Missing image " + k2 + ".png");
  }
};

// Returns the pass.json object (not a string).
Pass.prototype.getPassJSON = function() {
  var fields = cloneObject(this.fields);
  fields.formatVersion = 1;
  return fields;
};

// Pipe pass to a write stream.
//
// output - Write stream
Pass.prototype.pipe = function(output) {
  var self = this;
  var zip = new Zip(output);
  var lastError;

  zip.on("error", function(error) {
    lastError = error;
  });

  // Validate before attempting to create
  try {
    this.validate();
  } catch (error) {
    process.nextTick(function() {
      self.emit("error", error);
    });
    return output;
  }

  // Construct manifest here
  var manifest = {};
  // Add file to zip and it's SHA to manifest
  function addFile(filename) {
    var file = zip.addFile(filename);
    var sha = new SHAWriteStream(manifest, filename, file);
    return sha;
  }

  // Create pass.json
  var passJson = Buffer.from(JSON.stringify(this.getPassJSON()), "utf-8");
  addFile("pass.json").end(passJson, "utf8");

  var expecting = 0;
  for (var key in this.images) {
    var filename = key.replace(/2x$/, "@2x") + ".png";
    addImage(addFile(filename), this.images[key], function(error) {
      --expecting;
      if (error)
        lastError = error;
      if (expecting === 0)
        doneWithImages();
    });
    ++expecting;
  }

  function doneWithImages() {
    if (lastError) {
      zip.close();
      self.emit("error", lastError);
    } else {
      process.nextTick(function() {
        self.signZip(zip, manifest, function(error) {
          if (error) {
            return self.emit("error", error);
          }
          zip.close();
          zip.on("end", function() {
            self.emit("end");
          });
          zip.on("error", function(error) {
            self.emit("error", error);
          });
        });
      });
    }
  }
  return output;
};

// Use this to send pass as HTTP response.
//
// response - HTTP response
// callback - Called when done sending/error occurred
//
// Adds appropriate headers and pipes pass to response.
Pass.prototype.render = function(response, callback) {
  response.setHeader("Content-Type", "application/vnd.apple.pkpass");
  if (callback) {
    this.on("error", callback);
    this.on("end", callback);
  }
  this.pipe(response);
};

function addImage(file, source, callback) {
  if (typeof(source) == "string" || source instanceof String) {
    if (/^https?:/i.test(source)) {
      // URL
      var protocol = /^https:/i.test(source) ? HTTPS : HTTP;
      protocol.get(source, function(response) {
        if (response.statusCode == 200) {
          file.on("close", callback);
          if (response.headers['content-encoding'] === "gzip") {
            response.pipe(zlib.createGunzip()).pipe(file);
          } else {
            response.pipe(file);
          }
          response.resume();
        } else
          callback(new Error("Server returned " + response.statusCode + " for " + source));
      }).on("error", callback);
    } else {
      // Assume filename
      var stream = File.createReadStream(source);
      stream.pipe(file);
      file.on("close", callback);
    }
  } else if (source instanceof Buffer) {
    file.on("close", callback);
    file.write(source);
    file.end();
  } else if (typeof(source) == "function") {
    try {
      source(file);
      callback();
    } catch (error) {
      callback(error);
    }
  } else {
    // image is not a supported type
    callback(new Error("Cannot load image " + file.filename + ", must be String (filename), Buffer or function"));
  }
}

// Add manifest.json and signature files.
Pass.prototype.signZip = function(zip, manifest, callback) {
  var json = JSON.stringify(manifest);
  // Add manifest.json
  zip.addFile("manifest.json").end(json, "utf-8");
  // Create signature
  signManifest(this.template, json, function(error, signature) {
    if (!error) {
      // Write signature file
      zip.addFile("signature").end(signature);
    }
    callback(error);
  });
};

// Signs a manifest and returns the signature.
function signManifest(template, manifest, callback) {
  var identifier = template.passTypeIdentifier().replace(/^pass./, "");

  var args = [
    "smime",
    "-sign", "-binary",
    "-signer",    Path.resolve(template.keysPath, identifier + ".pem"),
    "-certfile",  Path.resolve(template.keysPath, "wwdr.pem"),
    "-passin",    "pass:" + template.password
  ];
  var sign = execFile("openssl", args, { stdio: "pipe" }, function(error, stdout, stderr) {
    var trimmedStderr = stderr.trim();
    // Windows outputs some unhelpful error messages, but still produces a valid signature
    if (error || (trimmedStderr && trimmedStderr.indexOf('- done') < 0)) {
      callback(new Error(stderr));
    } else {
      var signature = stdout.split(/(\r\n|\n\n)/)[3];
      callback(null, Buffer.from(signature, "base64"));
    }
  });
  sign.stdin.write(manifest);
  sign.stdin.end();
}

// Clone an object by copying all its properties and returning new object.
// If the argument is missing or null, returns a new object.
function cloneObject(object) {
  var clone = {};
  if (object) {
    for (var key in object)
      clone[key] = object[key];
  }
  return clone;
}

// -- Manifest output stream --

// A write stream that calculates SHA from the output and updates the manifest
// accordingly.
//
// manifest - Manifest object
// filename - Filename (manifest property to set)
// output   - Pipe to this output stream
function SHAWriteStream(manifest, filename, output) {
  this.output = output;
  this.manifest = manifest;
  this.filename = filename;
  this.sha = Crypto.createHash("sha1");
  output.on("close", this.emit.bind(this, "close"));
  output.on("error", this.emit.bind(this, "error"));
}

inherits(SHAWriteStream, Stream);

SHAWriteStream.prototype.write = function(buffer, encoding) {
  this.output.write(buffer, encoding);
  this.sha.update(buffer, encoding);
  return true;
};

SHAWriteStream.prototype.end = function(buffer, encoding) {
  if (buffer)
    this.write(buffer, encoding);
  this.output.end();
  this.manifest[this.filename] = this.sha.digest("hex");
};

module.exports = Pass;
<commit_msg>Removed bracket in regex to fixed empty signature issue<commit_after>// Generate a pass file.

var applyImageMethods = require("./images");
var Crypto            = require("crypto");
var EventEmitter      = require("events").EventEmitter;
var execFile          = require("child_process").execFile;
var Stream            = require("stream");
var inherits          = require("util").inherits;
var File              = require("fs");
var HTTP              = require("follow-redirects").http;
var HTTPS             = require("follow-redirects").https;
var Path              = require("path");
var Zip               = require("./zip");
var zlib              = require("zlib");

// Top-level pass fields.
var TOP_LEVEL           = [ "appLaunchURL", "associatedStoreIdentifiers", "authenticationToken",
                            "backgroundColor", "barcode", "description",
                            "foregroundColor", "labelColor", "locations", "logoText",
                            "organizationName", "relevantDate", "serialNumber",
                            "suppressStripShine", "webServiceURL", "beacons"];
// These top level fields are required for a valid pass.
var REQUIRED_TOP_LEVEL  = [ "description", "organizationName", "passTypeIdentifier",
                            "serialNumber", "teamIdentifier" ];
// Pass structure keys.
var STRUCTURE_FIELDS    = [ "auxiliaryFields", "backFields", "headerFields",
                            "primaryFields", "secondaryFields" ];
// These images are required for a valid pass.
var REQUIRED_IMAGES     = [ "icon", "logo" ];

// Create a new pass.
//
// template  - The template
// fields    - Pass fields (description, serialNumber, logoText)
function Pass(template, fields, images) {
  this.template = template;
  this.fields = cloneObject(fields);
  // Structure is basically reference to all the fields under a given style
  // key, e.g. if style is coupon then structure.primaryFields maps to
  // fields.coupon.primaryFields.
  var style = template.style;
  this.structure = this.fields[style];
  if (!this.structure)
    this.structure = this.fields[style] = {};
  this.images = cloneObject(images);
}

inherits(Pass, EventEmitter);
applyImageMethods(Pass);

// Accessor methods for top-level fields (description, serialNumber, logoText,
// etc).
//
// Call with an argument to set field and return self, call with no argument to
// get field value.
//
//   pass.description("Unbelievable discount");
//   console.log(pass.description());
TOP_LEVEL.forEach(function(key) {
  Pass.prototype[key] = function(value) {
    if (arguments.length === 0) {
      return this.fields[key];
    } else {
      this.fields[key] = value;
      return this;
    }
  };
});

// Accessor methods for structure fields (primaryFields, backFields, etc).
//
// For example:
//
//   pass.headerFields.add("time", "The Time", "10:00AM");
//   pass.backFields.add("url", "Web site", "http://example.com");
STRUCTURE_FIELDS.forEach(function(key) {
  Pass.prototype.__defineGetter__(key, function() {
    return new Fields(this, key);
  });
});

// Field accessors.
function Fields(pass, key) {
  this.pass = pass;
  this.key = key;
}

// Adds a field to the end of the list.
//
// You can call this method with three/four arguments:
// key      - Field key
// label    - Field label (optional)
// value    - Field value
// options  - Other field options (e.g. dateStyle)
//
// You can call this method with a single object that contains all field
// properties (key, label, etc).
//
// You can also call with an array of either one.
//
// Returns self.
Fields.prototype.add = function(key, label, value, options) {
  var field, k;
  if (arguments.length > 1) {
    this.remove(key);
    field = { key: key, value: value };
    if (label)
      field.label = label;
    if (options) {
      for (k in options)
        field[k] = options[k];
    }
    this.all().push(field);
  } else if (Array.isArray(arguments[0])) {
    var array = arguments[0];
    for (var i in array)
      this.add.call(this, array[i]);
  } else {
    var properties = arguments[0];
    key = properties.key;
    this.remove(key);
    field = {};
      for (k in properties)
        field[k] = properties[k];
    this.all().push(field);
  }
  return this;
};

// Returns a field.
//
// If field exists, returns an object with:
// key      - Field key
// label    - Field label (optional)
// value    - Field value
// Other field options (e.g. dateStyle)
Fields.prototype.get = function(key) {
  var fields = this.pass.structure[this.key];
  if (fields) {
    for (var i in fields) {
      var field = fields[i];
      if (field.key == key)
        return field;
    }
  }
  return null;
};

// Returns an array of all fields.
Fields.prototype.all = function() {
  var fields = this.pass.structure[this.key];
  if (!fields)
    this.pass.structure[this.key] = fields = [];
  return fields;
};

// Removes a given field.
Fields.prototype.remove = function(key) {
  var fields = this.pass.structure[this.key];
  if (fields) {
    for (var i in fields) {
      if (fields[i].key == key) {
        fields.splice(i, 1);
        break;
      }
    }
  }
  return this;
};

// Removes all fields.
Fields.prototype.clear = function() {
  this.pass.structure[this.key] = [];
  return this;
};

// Validate pass, throws error if missing a mandatory top-level field or image.
Pass.prototype.validate = function() {
  for (var i in REQUIRED_TOP_LEVEL) {
    var k1 = REQUIRED_TOP_LEVEL[i];
    if (!this.fields[k1])
      throw new Error("Missing field " + k1);
  }
  for (var j in REQUIRED_IMAGES) {
    var k2 = REQUIRED_IMAGES[j];
    if (!this.images[k2])
      throw new Error("Missing image " + k2 + ".png");
  }
};

// Returns the pass.json object (not a string).
Pass.prototype.getPassJSON = function() {
  var fields = cloneObject(this.fields);
  fields.formatVersion = 1;
  return fields;
};

// Pipe pass to a write stream.
//
// output - Write stream
Pass.prototype.pipe = function(output) {
  var self = this;
  var zip = new Zip(output);
  var lastError;

  zip.on("error", function(error) {
    lastError = error;
  });

  // Validate before attempting to create
  try {
    this.validate();
  } catch (error) {
    process.nextTick(function() {
      self.emit("error", error);
    });
    return output;
  }

  // Construct manifest here
  var manifest = {};
  // Add file to zip and it's SHA to manifest
  function addFile(filename) {
    var file = zip.addFile(filename);
    var sha = new SHAWriteStream(manifest, filename, file);
    return sha;
  }

  // Create pass.json
  var passJson = Buffer.from(JSON.stringify(this.getPassJSON()), "utf-8");
  addFile("pass.json").end(passJson, "utf8");

  var expecting = 0;
  for (var key in this.images) {
    var filename = key.replace(/2x$/, "@2x") + ".png";
    addImage(addFile(filename), this.images[key], function(error) {
      --expecting;
      if (error)
        lastError = error;
      if (expecting === 0)
        doneWithImages();
    });
    ++expecting;
  }

  function doneWithImages() {
    if (lastError) {
      zip.close();
      self.emit("error", lastError);
    } else {
      process.nextTick(function() {
        self.signZip(zip, manifest, function(error) {
          if (error) {
            return self.emit("error", error);
          }
          zip.close();
          zip.on("end", function() {
            self.emit("end");
          });
          zip.on("error", function(error) {
            self.emit("error", error);
          });
        });
      });
    }
  }
  return output;
};

// Use this to send pass as HTTP response.
//
// response - HTTP response
// callback - Called when done sending/error occurred
//
// Adds appropriate headers and pipes pass to response.
Pass.prototype.render = function(response, callback) {
  response.setHeader("Content-Type", "application/vnd.apple.pkpass");
  if (callback) {
    this.on("error", callback);
    this.on("end", callback);
  }
  this.pipe(response);
};

function addImage(file, source, callback) {
  if (typeof(source) == "string" || source instanceof String) {
    if (/^https?:/i.test(source)) {
      // URL
      var protocol = /^https:/i.test(source) ? HTTPS : HTTP;
      protocol.get(source, function(response) {
        if (response.statusCode == 200) {
          file.on("close", callback);
          if (response.headers['content-encoding'] === "gzip") {
            response.pipe(zlib.createGunzip()).pipe(file);
          } else {
            response.pipe(file);
          }
          response.resume();
        } else
          callback(new Error("Server returned " + response.statusCode + " for " + source));
      }).on("error", callback);
    } else {
      // Assume filename
      var stream = File.createReadStream(source);
      stream.pipe(file);
      file.on("close", callback);
    }
  } else if (source instanceof Buffer) {
    file.on("close", callback);
    file.write(source);
    file.end();
  } else if (typeof(source) == "function") {
    try {
      source(file);
      callback();
    } catch (error) {
      callback(error);
    }
  } else {
    // image is not a supported type
    callback(new Error("Cannot load image " + file.filename + ", must be String (filename), Buffer or function"));
  }
}

// Add manifest.json and signature files.
Pass.prototype.signZip = function(zip, manifest, callback) {
  var json = JSON.stringify(manifest);
  // Add manifest.json
  zip.addFile("manifest.json").end(json, "utf-8");
  // Create signature
  signManifest(this.template, json, function(error, signature) {
    if (!error) {
      // Write signature file
      zip.addFile("signature").end(signature);
    }
    callback(error);
  });
};

// Signs a manifest and returns the signature.
function signManifest(template, manifest, callback) {
  var identifier = template.passTypeIdentifier().replace(/^pass./, "");

  var args = [
    "smime",
    "-sign", "-binary",
    "-signer",    Path.resolve(template.keysPath, identifier + ".pem"),
    "-certfile",  Path.resolve(template.keysPath, "wwdr.pem"),
    "-passin",    "pass:" + template.password
  ];
  var sign = execFile("openssl", args, { stdio: "pipe" }, function(error, stdout, stderr) {
    var trimmedStderr = stderr.trim();
    // Windows outputs some unhelpful error messages, but still produces a valid signature
    if (error || (trimmedStderr && trimmedStderr.indexOf('- done') < 0)) {
      callback(new Error(stderr));
    } else {
      var signature = stdout.split(/\r\n|\n\n/)[3];
      callback(null, Buffer.from(signature, "base64"));
    }
  });
  sign.stdin.write(manifest);
  sign.stdin.end();
}

// Clone an object by copying all its properties and returning new object.
// If the argument is missing or null, returns a new object.
function cloneObject(object) {
  var clone = {};
  if (object) {
    for (var key in object)
      clone[key] = object[key];
  }
  return clone;
}

// -- Manifest output stream --

// A write stream that calculates SHA from the output and updates the manifest
// accordingly.
//
// manifest - Manifest object
// filename - Filename (manifest property to set)
// output   - Pipe to this output stream
function SHAWriteStream(manifest, filename, output) {
  this.output = output;
  this.manifest = manifest;
  this.filename = filename;
  this.sha = Crypto.createHash("sha1");
  output.on("close", this.emit.bind(this, "close"));
  output.on("error", this.emit.bind(this, "error"));
}

inherits(SHAWriteStream, Stream);

SHAWriteStream.prototype.write = function(buffer, encoding) {
  this.output.write(buffer, encoding);
  this.sha.update(buffer, encoding);
  return true;
};

SHAWriteStream.prototype.end = function(buffer, encoding) {
  if (buffer)
    this.write(buffer, encoding);
  this.output.end();
  this.manifest[this.filename] = this.sha.digest("hex");
};

module.exports = Pass;
<|endoftext|>


<commit_before>'use strict';

/**
 * path.js
 *
 * This file contains all necessary path calculation methods to get "shortest" path from point A to point B. Basically
 * this uses two different 3rd party libraries for calculation:
 *
 *  - EasyStar.js (https://github.com/prettymuchbryce/easystarjs)
 *  - node-pathfinder (https://github.com/Ezelia/node-pathfinder)
 *
 * Haven't decide yet which one is "better", both uses A* algorithm for actual path calculation with small differences.
 */

/**#@+
 * Module dependencies
 *
 * @type {exports}
 */
var EasyStar = require('easystarjs');
// noinspection JSUnresolvedVariable
var pathfinder = require('node-pathfinder').AStarJPS;
var _ = require('lodash');
var bluebird = require('bluebird');

/** @type {LoggerInstance} */
var Logger = require('./logger');
/**#@-*/

module.exports = {
  /**
   * Method to find path from defined point (x, y) to another point (x, y) according to given grid. Note that this
   * method work with callback function (this means that it's async). Main callback function get 'route' argument which
   * can be an array or boolean false. False value means that there isn't route to desired point.
   *
   * This uses EasyStar.js library for path calculation.
   *
   * @param   {number}    startX  Start point X coordinate
   * @param   {number}    startY  Start point y coordinate
   * @param   {number}    endX    End point X coordinate
   * @param   {number}    endY    End point y coordinate
   * @param   {*[]}       grid    Grid data
   * @param   {function}  next    Callback function to call after calculation are done
   */
  findEasyStar: function findEasyStar(startX, startY, endX, endY, grid, next) {
    // noinspection JSUnresolvedFunction, JSPotentiallyInvalidConstructorUsage
    var easyPath = new EasyStar.js();

    Logger.silly('Path.findEasyStar called.', JSON.stringify(arguments));

    /**
     * Callback function that method uses to format route data plain array and after that call main next function with
     * calculated results.
     *
     * @param   {{x: number, y: number}[]|null} route
     * @returns {[number, number][]|boolean}
     */
    var callback = function callback(route) {
      if (route === null) {
        Logger.warn('Route not found to apple', JSON.stringify([[startX, startY], [endX, endY]]));

        return next(false);
      }

      // Normalize results
      return next(_.map(route, function iterator(coordinates) {
        return [coordinates.x, coordinates.y];
      }));
    };

    try {
      easyPath.setGrid(_.clone(grid));
      easyPath.setAcceptableTiles([0]);
      easyPath.findPath(startX, startY, endX, endY, callback);
      easyPath.calculate();
    } catch (error) {
      Logger.error('Path calculation error', JSON.stringify(error));

      callback(null);
    }
  },
  /**
   * Method that uses EasyStar.js library for path calculation. Main difference against 'findEasyStar' method is that
   * this will return a promise.
   *
   * @param   {number}    startX  Start point X coordinate
   * @param   {number}    startY  Start point y coordinate
   * @param   {number}    endX    End point X coordinate
   * @param   {number}    endY    End point y coordinate
   * @param   {*[]}       grid    Grid data
   *
   * @returns {Promise}
   */
  findEasyStarPromise: function findEasyStarPromise(startX, startY, endX, endY, grid) {
    var self = this;
    var resolver = Promise.defer();

    Logger.silly('Path.findEasyStarPromise called.', JSON.stringify(arguments));

    /**
     * Callback function that contains promise resolve.
     *
     * @param {[number, number][]|boolean}  route
     */
    var callback = function callback(route) {
      resolver.resolve(route);
    };

    // And calculate that path!
    self.findEasyStar(startX, startY, endX, endY, grid, callback);

    return resolver.promise;
  },
  /**
   * Method to find path to specified point using 'node-pathfinder' library. This library is really fast, I mean really
   * really fast, but it uses diagonal calculations so there is some extra work to get this work. Also note that this
   * method is blocking.
   *
   * @param   {number}    startX  Start point X coordinate
   * @param   {number}    startY  Start point y coordinate
   * @param   {number}    endX    End point X coordinate
   * @param   {number}    endY    End point y coordinate
   * @param   {*[]}       grid    Grid data
   *
   * @returns {*[]}
   */
  findAStar: function findAStar(startX, startY, endX, endY, grid) {
    Logger.silly('Path.findAStar called.', JSON.stringify(arguments));

    return pathfinder(grid, startX, startY, endX, endY);
  },
  /**
   * Method to promisify 'node-pathfinder' library usage.
   *
   * @param   {number}    startX  Start point X coordinate
   * @param   {number}    startY  Start point y coordinate
   * @param   {number}    endX    End point X coordinate
   * @param   {number}    endY    End point y coordinate
   * @param   {*[]}       grid    Grid data
   *
   * @returns {Promise}
   */
  findAStarPromise: function findAStarPromise(startX, startY, endX, endY, grid) {
    var self = this;

    Logger.silly('Path.findAStarPromise called.', JSON.stringify(arguments));

    return new bluebird(function callback(resolve) {
      resolve(self.findAStar(startX, startY, endX, endY, grid));
    });
  }
};
<commit_msg>Fixed wrong bluebird issue.<commit_after>'use strict';

/**
 * path.js
 *
 * This file contains all necessary path calculation methods to get "shortest" path from point A to point B. Basically
 * this uses two different 3rd party libraries for calculation:
 *
 *  - EasyStar.js (https://github.com/prettymuchbryce/easystarjs)
 *  - node-pathfinder (https://github.com/Ezelia/node-pathfinder)
 *
 * Haven't decide yet which one is "better", both uses A* algorithm for actual path calculation with small differences.
 */

/**#@+
 * Module dependencies
 *
 * @type {exports}
 */
var EasyStar = require('easystarjs');
// noinspection JSUnresolvedVariable
var pathfinder = require('node-pathfinder').AStarJPS;
var _ = require('lodash');
var bluebird = require('bluebird');

/** @type {LoggerInstance} */
var Logger = require('./logger');
/**#@-*/

module.exports = {
  /**
   * Method to find path from defined point (x, y) to another point (x, y) according to given grid. Note that this
   * method work with callback function (this means that it's async). Main callback function get 'route' argument which
   * can be an array or boolean false. False value means that there isn't route to desired point.
   *
   * This uses EasyStar.js library for path calculation.
   *
   * @param   {number}    startX  Start point X coordinate
   * @param   {number}    startY  Start point y coordinate
   * @param   {number}    endX    End point X coordinate
   * @param   {number}    endY    End point y coordinate
   * @param   {*[]}       grid    Grid data
   * @param   {function}  next    Callback function to call after calculation are done
   */
  findEasyStar: function findEasyStar(startX, startY, endX, endY, grid, next) {
    // noinspection JSUnresolvedFunction, JSPotentiallyInvalidConstructorUsage
    var easyPath = new EasyStar.js();

    Logger.silly('Path.findEasyStar called.', JSON.stringify(arguments));

    /**
     * Callback function that method uses to format route data plain array and after that call main next function with
     * calculated results.
     *
     * @param   {{x: number, y: number}[]|null} route
     * @returns {[number, number][]|boolean}
     */
    var callback = function callback(route) {
      if (route === null) {
        Logger.warn('Route not found to apple', JSON.stringify([[startX, startY], [endX, endY]]));

        return next(false);
      }

      // Normalize results
      return next(_.map(route, function iterator(coordinates) {
        return [coordinates.x, coordinates.y];
      }));
    };

    try {
      easyPath.setGrid(_.clone(grid));
      easyPath.setAcceptableTiles([0]);
      easyPath.findPath(startX, startY, endX, endY, callback);
      easyPath.calculate();
    } catch (error) {
      Logger.error('Path calculation error', JSON.stringify(error));

      callback(null);
    }
  },
  /**
   * Method that uses EasyStar.js library for path calculation. Main difference against 'findEasyStar' method is that
   * this will return a promise.
   *
   * @param   {number}    startX  Start point X coordinate
   * @param   {number}    startY  Start point y coordinate
   * @param   {number}    endX    End point X coordinate
   * @param   {number}    endY    End point y coordinate
   * @param   {*[]}       grid    Grid data
   *
   * @returns {Promise}
   */
  findEasyStarPromise: function findEasyStarPromise(startX, startY, endX, endY, grid) {
    var self = this;
    var resolver = bluebird.defer();

    Logger.silly('Path.findEasyStarPromise called.', JSON.stringify(arguments));

    /**
     * Callback function that contains promise resolve.
     *
     * @param {[number, number][]|boolean}  route
     */
    var callback = function callback(route) {
      resolver.resolve(route);
    };

    // And calculate that path!
    self.findEasyStar(startX, startY, endX, endY, grid, callback);

    return resolver.promise;
  },
  /**
   * Method to find path to specified point using 'node-pathfinder' library. This library is really fast, I mean really
   * really fast, but it uses diagonal calculations so there is some extra work to get this work. Also note that this
   * method is blocking.
   *
   * @param   {number}    startX  Start point X coordinate
   * @param   {number}    startY  Start point y coordinate
   * @param   {number}    endX    End point X coordinate
   * @param   {number}    endY    End point y coordinate
   * @param   {*[]}       grid    Grid data
   *
   * @returns {*[]}
   */
  findAStar: function findAStar(startX, startY, endX, endY, grid) {
    Logger.silly('Path.findAStar called.', JSON.stringify(arguments));

    return pathfinder(grid, startX, startY, endX, endY);
  },
  /**
   * Method to promisify 'node-pathfinder' library usage.
   *
   * @param   {number}    startX  Start point X coordinate
   * @param   {number}    startY  Start point y coordinate
   * @param   {number}    endX    End point X coordinate
   * @param   {number}    endY    End point y coordinate
   * @param   {*[]}       grid    Grid data
   *
   * @returns {Promise}
   */
  findAStarPromise: function findAStarPromise(startX, startY, endX, endY, grid) {
    var self = this;

    Logger.silly('Path.findAStarPromise called.', JSON.stringify(arguments));

    return new bluebird(function callback(resolve) {
      resolve(self.findAStar(startX, startY, endX, endY, grid));
    });
  }
};
<|endoftext|>


<commit_before>"use strict"

var _ = require('underscore'),
  fs = require('fs'),
  sys = require('sys'),
  exec = require('child_process').exec,
  program = require('commander');

var path = require('path'),
  fs = require('fs'),
  lib = path.join(path.dirname(fs.realpathSync(__filename)), '../lib');

// CLI
program.version('0.0.1')
  .parse(process.argv);

// Exec new Gruntfile
var execGrunt = function () {
  var gruntfileTemplate = fs.readFileSync(path.join(lib, 'templates/Gruntfile.tmpl'), 'utf8');
  var pintTasks = fs.readFileSync('Pint.js', 'utf8');

  fs.writeFileSync("./Gruntfile.js", _.template(gruntfileTemplate)({gruntTasks : pintTasks}))

  exec("grunt", function(err, stdout) {
    sys.puts(stdout.trim());

    exec("rm -rf Gruntfile.js", function(err, stdout) {
      sys.puts(stdout.trim());
    });
  });
};

exports.drink = execGrunt;<commit_msg>Load config from pint<commit_after>"use strict"

var _ = require('underscore'),
  fs = require('fs'),
  sys = require('sys'),
  exec = require('child_process').exec,
  program = require('commander');

var path = require('path'),
  fs = require('fs'),
  lib = path.join(path.dirname(fs.realpathSync(__filename)), '../lib');

// CLI
program.version('0.0.1')
  .parse(process.argv);

// Exec new Gruntfile
var execGrunt = function () {
  var pintConfig = require(path.join(process.cwd(), 'Pint.js'));
  var gruntfileTemplate = fs.readFileSync(path.join(lib, 'templates/Gruntfile.tmpl'), 'utf8');

  // Stringify config but remove open and closing braces
  var config=JSON.stringify(pintConfig.build.config);
  config = config.substring(1, config.length - 1)

  fs.writeFileSync("./Gruntfile.js", _.template(gruntfileTemplate)({
    gruntTasks : config
  }))

  exec("grunt", function(err, stdout) {
    sys.puts(stdout.trim());

    exec("rm -rf Gruntfile.js", function(err, stdout) {
      sys.puts(stdout.trim());
    });
  });
};

exports.drink = execGrunt;<|endoftext|>


<commit_before>// Copyright 2012 The Obvious Corporation.

/*
 * A minimal in-process data pipe implementation. Pipes have
 * a write end, which provides the writable stream protocol, and
 * a read end, which emits events.
 */

/*
 * Modules used
 */

"use strict";

var events = require("events");
var stream = require("stream");
var util   = require("util");

var consts = require("./consts");
var sealer = require("./sealer");

/*
 * Helper functions
 */

/**
 * Construct the pipe shared state.
 */
function State() {
    this.readerOpen = true; // whether the reader side is still open
    this.writerOpen = true; // whether the writer side is still open
    this.readerEncoding = undefined;
    this.readerPaused = false;
    this.pending = []; // pending buffers to emit (accumulated while paused)
}

/**
 * Emit a data buffer as a reader-side event.
 *
 * Note that the writer side always ensures that what's handed in is a
 * buffer, even if the writer writes a string in a specified encoding
 * which matches the encoding that was set on the reader side. This is
 * done to ensure that there's never an emitted string which couldn't
 * possibly have gone through the specified encoding (e.g. code points
 * `> 0x7f` when the encoding is `"ascii"`).
 */
State.prototype.emitData = function emitData(buf) {
    var encoding = this.readerEncoding;
    this.reader.emit(consts.DATA, encoding ? buf.toString(encoding) : buf);
}

/**
 * Emit the end-of-stream events on the reader side, if appropriate.
 */
State.prototype.emitReaderEnd = function emitReaderEnd() {
    if (!this.readerPaused) {
        this.reader.emit(consts.END);
        this.reader.emit(consts.CLOSE);
    }
}

/**
 * Construct the reader end of a pipe.
 */
function Reader(state) {
    stream.Stream.call(this);
    this.pipe = state;
}

util.inherits(Reader, stream.Stream);

Reader.prototype.setEncoding = function setEncoding(encoding) {
    var state = sealer.unseal(this.pipe);

    // The Stream docs (Node 0.6.*) specify these three as the
    // only valid encodings.
    switch (encoding) {
        case consts.UTF8:
        case consts.ASCII:
        case consts.BASE64: {
            state.readerEncoding = encoding;
            break;
        }
        default: {
            throw new Error("Invalid encoding: " + encoding);
        }
    }
}

Reader.prototype.destroy = function destroy() {
    var state = sealer.unseal(this.pipe);

    state.readerOpen = false;
    state.readerPaused = false;
    state.pending = undefined;
}

Reader.prototype.pause = function pause() {
    var state = sealer.unseal(this.pipe);

    if (!state.readerOpen) {
        throw new Error("Closed");
    }

    state.readerPaused = true;
}

Reader.prototype.resume = function resume() {
    var state = sealer.unseal(this.pipe);
    var pending = state.pending;

    if (!state.readerOpen) {
        throw new Error("Closed");
    }

    for (var i = 0; i < pending.length; i++) {
        state.emitData(pending[i]);
    }

    state.writer.emit(consts.DRAIN);

    if (!state.writerOpen) {
        state.emitReaderEnd();
    }

    state.pending = [];
    state.readerPaused = false;
}

Object.freeze(Reader.prototype);

/**
 * Construct the reader end of a pipe.
 */
function Writer(state) {
    events.EventEmitter.call(this);
    this.pipe = state;
}

util.inherits(Writer, events.EventEmitter);

Writer.prototype.write = function write(value, encoding, fd) {
    var state = sealer.unseal(this.pipe);

    if (!state.writerOpen) {
        throw new Error("Closed");
    }

    if (!state.readerOpen) {
        // Just ignore the write if the reader has been closed.
        return true;
    }

    if (encoding !== undefined) {
        value = new Buffer(value, encoding);
    } else {
        assert(value instanceof Buffer);
    }

    // We ignore empty buffers, but we still return the proper
    // "success" state based on whether the reader is paused.
    if (value.length === 0) {
        return !state.readerPaused;
    }

    if (state.readerPaused) {
        // Note: We push buffers, not encoded strings, since the
        // reader side might change its requested encoding after the
        // data is queued up.
        state.pending.push(value);
        return false;
    } else {
        state.emitData(value);
        return true;
    }
}

Writer.prototype.end = function end(value, encoding) {
    var state = sealer.unseal(this.pipe);

    if (value !== undefined) {
        this.write(value, encoding);
    }

    if (state.writerOpen) {
        state.emitReaderEnd();
        this.emit(consts.CLOSE);
    }

    state.writerOpen = false;
}

Writer.prototype.destroy = Writer.prototype.end;
Writer.prototype.destroySoon = Writer.prototype.end;

Object.freeze(Writer.prototype);

/*
 * Exported bindings
 */

/**
 * Construct a pipe. The result has bindings for `{reader, writer}` to
 * the two ends of the pipe. The reader is a regular `stream.Stream`
 * to which event listeners may be attached. The writer similarly
 * implements the writable stream protocol and emits the expected
 * events.
 *
 * The optional `paused` argument indicates whether the reader side should
 * start out paused (defaults to `false`).
 */
function Pipe(paused) {
    var state = new State();
    var sealedState = sealer.seal(state);
    this.reader = state.reader = new Reader(sealedState);
    this.writer = state.writer = new Writer(sealedState);

    if (paused) {
        this.reader.pause();
    }
}

Object.freeze(Pipe.prototype);

module.exports = {
    Pipe: Pipe
};
<commit_msg>Get ordering right.<commit_after>// Copyright 2012 The Obvious Corporation.

/*
 * A minimal in-process data pipe implementation. Pipes have
 * a write end, which provides the writable stream protocol, and
 * a read end, which emits events.
 */

/*
 * Modules used
 */

"use strict";

var events = require("events");
var stream = require("stream");
var util   = require("util");

var consts = require("./consts");
var sealer = require("./sealer");

/*
 * Helper functions
 */

/**
 * Construct the pipe shared state.
 */
function State() {
    this.readerOpen = true; // whether the reader side is still open
    this.writerOpen = true; // whether the writer side is still open
    this.readerEncoding = undefined;
    this.readerPaused = false;
    this.pending = []; // pending buffers to emit (accumulated while paused)
}

/**
 * Emit a data buffer as a reader-side event.
 *
 * Note that the writer side always ensures that what's handed in is a
 * buffer, even if the writer writes a string in a specified encoding
 * which matches the encoding that was set on the reader side. This is
 * done to ensure that there's never an emitted string which couldn't
 * possibly have gone through the specified encoding (e.g. code points
 * `> 0x7f` when the encoding is `"ascii"`).
 */
State.prototype.emitData = function emitData(buf) {
    var encoding = this.readerEncoding;
    this.reader.emit(consts.DATA, encoding ? buf.toString(encoding) : buf);
}

/**
 * Emit the end-of-stream events on the reader side, if appropriate.
 */
State.prototype.emitReaderEnd = function emitReaderEnd() {
    if (!this.readerPaused) {
        this.reader.emit(consts.END);
        this.reader.emit(consts.CLOSE);
    }
}

/**
 * Construct the reader end of a pipe.
 */
function Reader(state) {
    stream.Stream.call(this);
    this.pipe = state;
}

util.inherits(Reader, stream.Stream);

Reader.prototype.setEncoding = function setEncoding(encoding) {
    var state = sealer.unseal(this.pipe);

    // The Stream docs (Node 0.6.*) specify these three as the
    // only valid encodings.
    switch (encoding) {
        case consts.UTF8:
        case consts.ASCII:
        case consts.BASE64: {
            state.readerEncoding = encoding;
            break;
        }
        default: {
            throw new Error("Invalid encoding: " + encoding);
        }
    }
}

Reader.prototype.destroy = function destroy() {
    var state = sealer.unseal(this.pipe);

    state.readerOpen = false;
    state.readerPaused = false;
    state.pending = undefined;
}

Reader.prototype.pause = function pause() {
    var state = sealer.unseal(this.pipe);

    if (!state.readerOpen) {
        throw new Error("Closed");
    }

    state.readerPaused = true;
}

Reader.prototype.resume = function resume() {
    var state = sealer.unseal(this.pipe);
    var pending = state.pending;

    if (!state.readerOpen) {
        throw new Error("Closed");
    }

    for (var i = 0; i < pending.length; i++) {
        state.emitData(pending[i]);
    }

    state.pending = [];
    state.readerPaused = false;

    state.writer.emit(consts.DRAIN);

    if (!state.writerOpen) {
        state.emitReaderEnd();
    }
}

Object.freeze(Reader.prototype);

/**
 * Construct the reader end of a pipe.
 */
function Writer(state) {
    events.EventEmitter.call(this);
    this.pipe = state;
}

util.inherits(Writer, events.EventEmitter);

Writer.prototype.write = function write(value, encoding, fd) {
    var state = sealer.unseal(this.pipe);

    if (!state.writerOpen) {
        throw new Error("Closed");
    }

    if (!state.readerOpen) {
        // Just ignore the write if the reader has been closed.
        return true;
    }

    if (encoding !== undefined) {
        value = new Buffer(value, encoding);
    } else {
        assert(value instanceof Buffer);
    }

    // We ignore empty buffers, but we still return the proper
    // "success" state based on whether the reader is paused.
    if (value.length === 0) {
        return !state.readerPaused;
    }

    if (state.readerPaused) {
        // Note: We push buffers, not encoded strings, since the
        // reader side might change its requested encoding after the
        // data is queued up.
        state.pending.push(value);
        return false;
    } else {
        state.emitData(value);
        return true;
    }
}

Writer.prototype.end = function end(value, encoding) {
    var state = sealer.unseal(this.pipe);

    if (value !== undefined) {
        this.write(value, encoding);
    }

    if (state.writerOpen) {
        state.emitReaderEnd();
        this.emit(consts.CLOSE);
    }

    state.writerOpen = false;
}

Writer.prototype.destroy = Writer.prototype.end;
Writer.prototype.destroySoon = Writer.prototype.end;

Object.freeze(Writer.prototype);

/*
 * Exported bindings
 */

/**
 * Construct a pipe. The result has bindings for `{reader, writer}` to
 * the two ends of the pipe. The reader is a regular `stream.Stream`
 * to which event listeners may be attached. The writer similarly
 * implements the writable stream protocol and emits the expected
 * events.
 *
 * The optional `paused` argument indicates whether the reader side should
 * start out paused (defaults to `false`).
 */
function Pipe(paused) {
    var state = new State();
    var sealedState = sealer.seal(state);
    this.reader = state.reader = new Reader(sealedState);
    this.writer = state.writer = new Writer(sealedState);

    if (paused) {
        this.reader.pause();
    }
}

Object.freeze(Pipe.prototype);

module.exports = {
    Pipe: Pipe
};
<|endoftext|>


<commit_before>'use strict';

let PlatformAccessory;
let Accessory;
let Service;
let Characteristic;
let UUIDGen;
let CustomCharacteristic;

const lookup = function (value) {
  let keys = Object.keys(this);
  for (var i = 0; i < keys.length; i++) {
    if (this[keys[i]] === value) { return keys[i]; }
  }
};

const getOrAddCharacteristic = (service, characteristic) => {
  return service.getCharacteristic(characteristic) || service.addCharacteristic(characteristic);
};

const removeCharacteristicIfFound = function (service, characteristic) {
  if (service.testCharacteristic(characteristic)) {
    const c = service.getCharacteristic(characteristic);
    this.log.warn('Removing stale Characteristic: [%s] [%s]', c.displayName, c.UUID);
    service.removeCharacteristic(c);
  }
};

class PlugAccessory {
  constructor (platform, config, homebridgeAccessory, plug) {
    this.platform = platform;
    PlatformAccessory = platform.api.platformAccessory;
    Accessory = platform.api.hap.Accessory;
    Service = platform.api.hap.Service;
    Characteristic = platform.api.hap.Characteristic;
    UUIDGen = platform.api.hap.uuid;
    CustomCharacteristic = require('./homekit')(platform.api).CustomCharacteristic;

    Accessory.Categories.lookup = lookup.bind(Accessory.Categories);

    this.log = platform.log;
    this.config = config;
    this.homebridgeAccessory = homebridgeAccessory;
    this.plug = plug;

    if (config.switchModels && config.switchModels.findIndex((m) => { if (plug.model.includes(m)) return true; }) !== -1) {
      this.homekitCategory = 'SWITCH';
      this.homekitServiceType = Service.Switch;
    } else {
      this.homekitCategory = 'OUTLET';
      this.homekitServiceType = Service.Outlet;
    }

    if (!this.homebridgeAccessory) {
      this.log.debug('Creating new Accessory [%s] [%s] [%s] homekitCategory: %s', plug.alias, plug.deviceId, UUIDGen.generate(plug.deviceId), this.homekitCategory);
      this.homebridgeAccessory = new PlatformAccessory(plug.alias, UUIDGen.generate(plug.deviceId), Accessory.Categories[this.homekitCategory]);
      platform.registerPlatformAccessory(this.homebridgeAccessory);
    } else {
      this.log.debug('Existing Accessory found [%s] [%s] [%s] homekitCategory: %s', homebridgeAccessory.displayName, homebridgeAccessory.context.deviceId, homebridgeAccessory.UUID, Accessory.Categories.lookup(homebridgeAccessory.category));
      this.homebridgeAccessory.displayName = plug.alias;
      if (this.homebridgeAccessory.category !== Accessory.Categories[this.homekitCategory]) {
        this.log.warn('[%s] Correcting Accessory Category from: %s to: %s', this.homebridgeAccessory.displayName, Accessory.Categories.lookup(this.homebridgeAccessory.category), this.homekitCategory);
        this.homebridgeAccessory.category = Accessory.Categories[this.homekitCategory];
      }
    }

    this.outletService = this.homebridgeAccessory.getService(this.homekitServiceType);
    if (!this.outletService) {
      this.log.debug('Creating new Service [%s] type:%s', plug.alias, this.homekitCategory);
      this.outletService = this.homebridgeAccessory.addService(this.homekitServiceType, plug.alias);
    } else {
      this.outletService.setCharacteristic(Characteristic.Name, plug.alias);
    }

    this.outletService.getCharacteristic(Characteristic.On)
      .on('get', (callback) => {
        this.log.debug('[%s] On get', this.homebridgeAccessory.displayName);
        this.plug.getPowerState().then((value) => {
          callback(null, value);
        }).catch((reason) => {
          this.log.error('[%s] %s', this.homebridgeAccessory.displayName, 'getPowerState');
          this.log.error(reason);
          callback(new Error(reason));
        });
      })
      .on('set', (value, callback) => {
        this.log.debug('[%s] On set', this.homebridgeAccessory.displayName);
        this.plug.setPowerState(value).then(() => {
          callback();
        }, (reason) => {
          this.log.error('[%s] %s', this.homebridgeAccessory.displayName, 'setPowerState');
          this.log.error(reason);
          callback(new Error(reason));
        });
      });

    this._plug.on('power-on', () => { this.setOn(true); });
    this._plug.on('power-off', () => { this.setOn(false); });

    if (plug.supportsEmeter || this.homekitCategory === 'OUTLET') {
      this.outletService.getCharacteristic(Characteristic.OutletInUse)
        .on('get', (callback) => {
          this.log.debug('[%s] OutletInUse get', this.homebridgeAccessory.displayName);
          this.plug.getInUse().then((value) => {
            callback(null, value);
          }).catch((reason) => {
            this.log.error('[%s] %s', this.homebridgeAccessory.displayName, 'getInUse');
            this.log.error(reason);
            callback(new Error(reason));
          });
        });

      this._plug.on('in-use', () => { this.setOutletInUse(true); });
      this._plug.on('not-in-use', () => { this.setOutletInUse(false); });
    } else {
      if (this.homekitCategory === 'SWITCH') {
        removeCharacteristicIfFound.call(this, this.outletService, Characteristic.OutletInUse);
      }
    }

    if (plug.supportsEmeter && this.config.addCustomCharacteristics) {
      this.log.debug('Adding CustomCharacteristics (if needed) [%s]', this.homebridgeAccessory.displayName);

      getOrAddCharacteristic(this.outletService, CustomCharacteristic.Volts)
        .on('get', (callback) => {
          this.plug.emeter.getRealtime().then((emeterRealtime) => {
            if (emeterRealtime.voltage == null) {
              callback(null, Math.round(emeterRealtime.voltage_mv / 100) / 10);
            } else {
              callback(null, Math.round(emeterRealtime.voltage));
            }
          }).catch((reason) => {
            this.log.error('[%s] %s', this.homebridgeAccessory.displayName, 'Volts emeter.getRealtime');
            this.log.error(reason);
            callback(new Error(reason));
          });
        });

      getOrAddCharacteristic(this.outletService, CustomCharacteristic.Amperes)
        .on('get', (callback) => {
          this.plug.emeter.getRealtime().then((emeterRealtime) => {
            if (emeterRealtime.current == null) {
              callback(null, Math.round(emeterRealtime.current_ma / 10) / 100);
            } else {
              callback(null, Math.round(emeterRealtime.current));
            }
          }).catch((reason) => {
            this.log.error('[%s] %s', this.homebridgeAccessory.displayName, 'Amperes emeter.getRealtime');
            this.log.error(reason);
            callback(new Error(reason));
          });
        });

      getOrAddCharacteristic(this.outletService, CustomCharacteristic.Watts)
        .on('get', (callback) => {
          this.plug.emeter.getRealtime().then((emeterRealtime) => {
            if (emeterRealtime.power == null) {
              callback(null, Math.round(emeterRealtime.power_mw / 100) / 10);
            } else {
              callback(null, Math.round(emeterRealtime.power));
            }
          }).catch((reason) => {
            this.log.error('[%s] %s', this.homebridgeAccessory.displayName, 'Watts emeter.getRealtime');
            this.log.error(reason);
            callback(new Error(reason));
          });
        });

      getOrAddCharacteristic(this.outletService, CustomCharacteristic.VoltAmperes)
        .on('get', (callback) => {
          this.plug.emeter.getRealtime().then((emeterRealtime) => {
            if (emeterRealtime.voltage == null) {
              callback(null, Math.round(emeterRealtime.voltage_mv * emeterRealtime.current_ma / 1000000));
            } else {
              callback(null, Math.round(emeterRealtime.voltage * emeterRealtime.current));
            }
          }).catch((reason) => {
            this.log.error('[%s] %s', this.homebridgeAccessory.displayName, 'VoltAmperes emeter.getRealtime');
            this.log.error(reason);
            callback(new Error(reason));
          });
        });

      getOrAddCharacteristic(this.outletService, CustomCharacteristic.KilowattHours)
        .on('get', (callback) => {
          this.plug.emeter.getRealtime().then((emeterRealtime) => {
            if (emeterRealtime.total == null) {
              callback(null, Math.round(emeterRealtime.total_wh) / 1000);
            } else {
              callback(null, Math.round(emeterRealtime.total));
            }
          }).catch((reason) => {
            this.log.error('[%s] %s', this.homebridgeAccessory.displayName, 'KilowattHours emeter.getRealtime');
            this.log.error(reason);
            callback(new Error(reason));
          });
        });
    } else {
      [CustomCharacteristic.Volts, CustomCharacteristic.Amperes, CustomCharacteristic.Watts,
        CustomCharacteristic.VoltAmperes, CustomCharacteristic.KilowattHours]
        .forEach((c) => { removeCharacteristicIfFound.call(this, this.outletService, c); });
    }

    this.infoService = this.homebridgeAccessory.getService(Service.AccessoryInformation);
    if (!this.infoService.getCharacteristic(Characteristic.FirmwareRevision)) {
      this.infoService.addCharacteristic(Characteristic.FirmwareRevision);
    }
    if (!this.infoService.getCharacteristic(Characteristic.HardwareRevision)) {
      this.infoService.addCharacteristic(Characteristic.HardwareRevision);
    }
    this.infoService
      .setCharacteristic(Characteristic.Name, plug.alias)
      .setCharacteristic(Characteristic.Manufacturer, 'TP-Link')
      .setCharacteristic(Characteristic.Model, plug.model)
      .setCharacteristic(Characteristic.SerialNumber, plug.mac)
      .setCharacteristic(Characteristic.FirmwareRevision, plug.softwareVersion)
      .setCharacteristic(Characteristic.HardwareRevision, plug.hardwareVersion);

    this.homebridgeAccessory.context.mac = plug.macNormalized;
    this.homebridgeAccessory.context.deviceId = plug.deviceId;

    this.homebridgeAccessory.on('identify', (paired, callback) => {
      this.log.debug('[%s] identify', this.homebridgeAccessory.displayName);
      let cbCalled = false;
      this.plug.blink(1, 500)
        .then(() => {
          callback(); // Callback after first blink so don't block
          cbCalled = true;
          return this.plug.blink(2, 500);
        })
        .then(() => { this.log.debug('[%s] identify done', this.homebridgeAccessory.displayName); })
        .catch((reason) => {
          if (!cbCalled) {
            return callback(new Error(reason));
          } else {
            this.log.error('[%s] identify error', this.homebridgeAccessory.displayName);
          }
        });
    });

    // Remove Old Services
    this.homebridgeAccessory.services.forEach((service) => {
      if (service === this.outletService) { return; }
      if (service === this.infoService) { return; }
      this.log.warn('Removing stale Service: [%s] [%s] [%s]', service.displayName, service.UUID, service.subtype);
      this.homebridgeAccessory.removeService(service);
    });
  }

  get plug () { return this._plug; }

  set plug (plug) {
    this._plug = plug;
  }

  setOn (value) {
    this.log.debug('[%s] setOn(%s)', this.homebridgeAccessory.displayName, value);
    this.outletService
      .getCharacteristic(Characteristic.On)
      .updateValue(value);
  }

  setOutletInUse (value) {
    this.log.debug('[%s] setOutletInUse(%s)', this.homebridgeAccessory.displayName, value);
    this.outletService
      .getCharacteristic(Characteristic.OutletInUse)
      .updateValue(value);
  }
}

module.exports = PlugAccessory;
<commit_msg>fix: support new-style energy consumption<commit_after>'use strict';

let PlatformAccessory;
let Accessory;
let Service;
let Characteristic;
let UUIDGen;
let CustomCharacteristic;

const lookup = function (value) {
  let keys = Object.keys(this);
  for (var i = 0; i < keys.length; i++) {
    if (this[keys[i]] === value) { return keys[i]; }
  }
};

const getOrAddCharacteristic = (service, characteristic) => {
  return service.getCharacteristic(characteristic) || service.addCharacteristic(characteristic);
};

const removeCharacteristicIfFound = function (service, characteristic) {
  if (service.testCharacteristic(characteristic)) {
    const c = service.getCharacteristic(characteristic);
    this.log.warn('Removing stale Characteristic: [%s] [%s]', c.displayName, c.UUID);
    service.removeCharacteristic(c);
  }
};

class PlugAccessory {
  constructor (platform, config, homebridgeAccessory, plug) {
    this.platform = platform;
    PlatformAccessory = platform.api.platformAccessory;
    Accessory = platform.api.hap.Accessory;
    Service = platform.api.hap.Service;
    Characteristic = platform.api.hap.Characteristic;
    UUIDGen = platform.api.hap.uuid;
    CustomCharacteristic = require('./homekit')(platform.api).CustomCharacteristic;

    Accessory.Categories.lookup = lookup.bind(Accessory.Categories);

    this.log = platform.log;
    this.config = config;
    this.homebridgeAccessory = homebridgeAccessory;
    this.plug = plug;

    if (config.switchModels && config.switchModels.findIndex((m) => { if (plug.model.includes(m)) return true; }) !== -1) {
      this.homekitCategory = 'SWITCH';
      this.homekitServiceType = Service.Switch;
    } else {
      this.homekitCategory = 'OUTLET';
      this.homekitServiceType = Service.Outlet;
    }

    if (!this.homebridgeAccessory) {
      this.log.debug('Creating new Accessory [%s] [%s] [%s] homekitCategory: %s', plug.alias, plug.deviceId, UUIDGen.generate(plug.deviceId), this.homekitCategory);
      this.homebridgeAccessory = new PlatformAccessory(plug.alias, UUIDGen.generate(plug.deviceId), Accessory.Categories[this.homekitCategory]);
      platform.registerPlatformAccessory(this.homebridgeAccessory);
    } else {
      this.log.debug('Existing Accessory found [%s] [%s] [%s] homekitCategory: %s', homebridgeAccessory.displayName, homebridgeAccessory.context.deviceId, homebridgeAccessory.UUID, Accessory.Categories.lookup(homebridgeAccessory.category));
      this.homebridgeAccessory.displayName = plug.alias;
      if (this.homebridgeAccessory.category !== Accessory.Categories[this.homekitCategory]) {
        this.log.warn('[%s] Correcting Accessory Category from: %s to: %s', this.homebridgeAccessory.displayName, Accessory.Categories.lookup(this.homebridgeAccessory.category), this.homekitCategory);
        this.homebridgeAccessory.category = Accessory.Categories[this.homekitCategory];
      }
    }

    this.outletService = this.homebridgeAccessory.getService(this.homekitServiceType);
    if (!this.outletService) {
      this.log.debug('Creating new Service [%s] type:%s', plug.alias, this.homekitCategory);
      this.outletService = this.homebridgeAccessory.addService(this.homekitServiceType, plug.alias);
    } else {
      this.outletService.setCharacteristic(Characteristic.Name, plug.alias);
    }

    this.outletService.getCharacteristic(Characteristic.On)
      .on('get', (callback) => {
        this.log.debug('[%s] On get', this.homebridgeAccessory.displayName);
        this.plug.getPowerState().then((value) => {
          callback(null, value);
        }).catch((reason) => {
          this.log.error('[%s] %s', this.homebridgeAccessory.displayName, 'getPowerState');
          this.log.error(reason);
          callback(new Error(reason));
        });
      })
      .on('set', (value, callback) => {
        this.log.debug('[%s] On set', this.homebridgeAccessory.displayName);
        this.plug.setPowerState(value).then(() => {
          callback();
        }, (reason) => {
          this.log.error('[%s] %s', this.homebridgeAccessory.displayName, 'setPowerState');
          this.log.error(reason);
          callback(new Error(reason));
        });
      });

    this._plug.on('power-on', () => { this.setOn(true); });
    this._plug.on('power-off', () => { this.setOn(false); });

    if (plug.supportsEmeter || this.homekitCategory === 'OUTLET') {
      this.outletService.getCharacteristic(Characteristic.OutletInUse)
        .on('get', (callback) => {
          this.log.debug('[%s] OutletInUse get', this.homebridgeAccessory.displayName);
          this.plug.getInUse().then((value) => {
            callback(null, value);
          }).catch((reason) => {
            this.log.error('[%s] %s', this.homebridgeAccessory.displayName, 'getInUse');
            this.log.error(reason);
            callback(new Error(reason));
          });
        });

      this._plug.on('in-use', () => { this.setOutletInUse(true); });
      this._plug.on('not-in-use', () => { this.setOutletInUse(false); });
    } else {
      if (this.homekitCategory === 'SWITCH') {
        removeCharacteristicIfFound.call(this, this.outletService, Characteristic.OutletInUse);
      }
    }

    if (plug.supportsEmeter && this.config.addCustomCharacteristics) {
      this.log.debug('Adding CustomCharacteristics (if needed) [%s]', this.homebridgeAccessory.displayName);

      getOrAddCharacteristic(this.outletService, CustomCharacteristic.Volts)
        .on('get', (callback) => {
          this.plug.emeter.getRealtime().then((emeterRealtime) => {
            callback(null, emeterRealtime.voltage);
          }).catch((reason) => {
            this.log.error('[%s] %s', this.homebridgeAccessory.displayName, 'Volts emeter.getRealtime');
            this.log.error(reason);
            callback(new Error(reason));
          });
        });

      getOrAddCharacteristic(this.outletService, CustomCharacteristic.Amperes)
        .on('get', (callback) => {
          this.plug.emeter.getRealtime().then((emeterRealtime) => {
            callback(null, emeterRealtime.current);
          }).catch((reason) => {
            this.log.error('[%s] %s', this.homebridgeAccessory.displayName, 'Amperes emeter.getRealtime');
            this.log.error(reason);
            callback(new Error(reason));
          });
        });

      getOrAddCharacteristic(this.outletService, CustomCharacteristic.Watts)
        .on('get', (callback) => {
          this.plug.emeter.getRealtime().then((emeterRealtime) => {
            callback(null, emeterRealtime.power);
          }).catch((reason) => {
            this.log.error('[%s] %s', this.homebridgeAccessory.displayName, 'Watts emeter.getRealtime');
            this.log.error(reason);
            callback(new Error(reason));
          });
        });

      getOrAddCharacteristic(this.outletService, CustomCharacteristic.VoltAmperes)
        .on('get', (callback) => {
          this.plug.emeter.getRealtime().then((emeterRealtime) => {
            callback(null, emeterRealtime.voltage * emeterRealtime.current);
          }).catch((reason) => {
            this.log.error('[%s] %s', this.homebridgeAccessory.displayName, 'VoltAmperes emeter.getRealtime');
            this.log.error(reason);
            callback(new Error(reason));
          });
        });

      getOrAddCharacteristic(this.outletService, CustomCharacteristic.KilowattHours)
        .on('get', (callback) => {
          this.plug.emeter.getRealtime().then((emeterRealtime) => {
            callback(null, emeterRealtime.total);
          }).catch((reason) => {
            this.log.error('[%s] %s', this.homebridgeAccessory.displayName, 'KilowattHours emeter.getRealtime');
            this.log.error(reason);
            callback(new Error(reason));
          });
        });
    } else {
      [CustomCharacteristic.Volts, CustomCharacteristic.Amperes, CustomCharacteristic.Watts,
        CustomCharacteristic.VoltAmperes, CustomCharacteristic.KilowattHours]
        .forEach((c) => { removeCharacteristicIfFound.call(this, this.outletService, c); });
    }

    this.infoService = this.homebridgeAccessory.getService(Service.AccessoryInformation);
    if (!this.infoService.getCharacteristic(Characteristic.FirmwareRevision)) {
      this.infoService.addCharacteristic(Characteristic.FirmwareRevision);
    }
    if (!this.infoService.getCharacteristic(Characteristic.HardwareRevision)) {
      this.infoService.addCharacteristic(Characteristic.HardwareRevision);
    }
    this.infoService
      .setCharacteristic(Characteristic.Name, plug.alias)
      .setCharacteristic(Characteristic.Manufacturer, 'TP-Link')
      .setCharacteristic(Characteristic.Model, plug.model)
      .setCharacteristic(Characteristic.SerialNumber, plug.mac)
      .setCharacteristic(Characteristic.FirmwareRevision, plug.softwareVersion)
      .setCharacteristic(Characteristic.HardwareRevision, plug.hardwareVersion);

    this.homebridgeAccessory.context.mac = plug.macNormalized;
    this.homebridgeAccessory.context.deviceId = plug.deviceId;

    this.homebridgeAccessory.on('identify', (paired, callback) => {
      this.log.debug('[%s] identify', this.homebridgeAccessory.displayName);
      let cbCalled = false;
      this.plug.blink(1, 500)
        .then(() => {
          callback(); // Callback after first blink so don't block
          cbCalled = true;
          return this.plug.blink(2, 500);
        })
        .then(() => { this.log.debug('[%s] identify done', this.homebridgeAccessory.displayName); })
        .catch((reason) => {
          if (!cbCalled) {
            return callback(new Error(reason));
          } else {
            this.log.error('[%s] identify error', this.homebridgeAccessory.displayName);
          }
        });
    });

    // Remove Old Services
    this.homebridgeAccessory.services.forEach((service) => {
      if (service === this.outletService) { return; }
      if (service === this.infoService) { return; }
      this.log.warn('Removing stale Service: [%s] [%s] [%s]', service.displayName, service.UUID, service.subtype);
      this.homebridgeAccessory.removeService(service);
    });
  }

  get plug () { return this._plug; }

  set plug (plug) {
    this._plug = plug;
  }

  setOn (value) {
    this.log.debug('[%s] setOn(%s)', this.homebridgeAccessory.displayName, value);
    this.outletService
      .getCharacteristic(Characteristic.On)
      .updateValue(value);
  }

  setOutletInUse (value) {
    this.log.debug('[%s] setOutletInUse(%s)', this.homebridgeAccessory.displayName, value);
    this.outletService
      .getCharacteristic(Characteristic.OutletInUse)
      .updateValue(value);
  }
}

module.exports = PlugAccessory;
<|endoftext|>


<commit_before>'use strict';

var jasmine = require('gulp-jasmine'),
    istanbul = require('gulp-istanbul'),
    replace = require('gulp-replace'),
    remap = require('remap-istanbul/lib/gulpRemapIstanbul'),
    instanbulEnforcer = require('gulp-istanbul-enforcer');

module.exports = function (gulp, config) {
    return function () {
        return gulp.src(config.test.coverage.src)
            .pipe(istanbul({ includeUntested: true }))
            .pipe(istanbul.hookRequire())
            .on('finish', () => {

                // Run jasmine under istanbul
                gulp.src(config.test.src)
                    .pipe(jasmine({ verbose: true, errorOnFail: true, includeStackTrace: false }))
                    .pipe(istanbul.writeReports({
                        dir: config.test.coverage.dest,
                        reporters: ['json']
                    }))

                    // Take js coverage json and remap to typescript.  Output html and text
                    .on('end', () => {
                        return gulp.src(config.test.coverage.coverageFile)
                            .pipe(remap({
                                reports: {
                                    'json': config.test.coverage.coverageFile, // overwrite js based json with ts remapped version
                                    'html': config.test.coverage.dest,
                                    'lcovonly': config.test.coverage.lcovFile,
                                    'text': null
                                }
                            }))

                            // Remove staging build directory in code coverage for correct file mapping
                            .on('finish', function () {
                                return gulp.src(config.test.coverage.coverageFile)
                                    .pipe(replace(/\\\\build\\\\src/mg, '\\\\src')) // windows
                                    .pipe(replace(/\/build\/src/mg, '/src')) // unix)
                                    .pipe(gulp.dest(config.test.coverage.dest))

                                    // Enforce statement coverage threshold based on config or if not defined 80%
                                    .on('finish', function () {
                                        return gulp.src('.')
                                            .pipe(instanbulEnforcer({
                                                coverageDirectory: config.test.coverage.dest,
                                                rootDirectory: '',
                                                thresholds: {
                                                    statements: config.test.coverage.threshold || 80
                                                }
                                            }));
                                    });
                            })
                    })
            });
    };
}

<commit_msg>Exit test with explicit error code on jasmine error (#381)<commit_after>'use strict';

var jasmine = require('gulp-jasmine'),
    istanbul = require('gulp-istanbul'),
    replace = require('gulp-replace'),
    remap = require('remap-istanbul/lib/gulpRemapIstanbul'),
    instanbulEnforcer = require('gulp-istanbul-enforcer');

module.exports = function (gulp, config) {
    return function () {
        return gulp.src(config.test.coverage.src)
            .pipe(istanbul({ includeUntested: true }))
            .pipe(istanbul.hookRequire())
            .on('finish', () => {

                // Run jasmine under istanbul
                gulp.src(config.test.src)
                    .pipe(jasmine({ verbose: true, errorOnFail: true, includeStackTrace: false }))
                    .on('error', (err) => {
                        process.exit(1);
                    })
                    .pipe(istanbul.writeReports({
                        dir: config.test.coverage.dest,
                        reporters: ['json']
                    }))

                    // Take js coverage json and remap to typescript.  Output html and text
                    .on('end', () => {
                        return gulp.src(config.test.coverage.coverageFile)
                            .pipe(remap({
                                reports: {
                                    'json': config.test.coverage.coverageFile, // overwrite js based json with ts remapped version
                                    'html': config.test.coverage.dest,
                                    'lcovonly': config.test.coverage.lcovFile,
                                    'text': null
                                }
                            }))

                            // Remove staging build directory in code coverage for correct file mapping
                            .on('finish', function () {
                                return gulp.src(config.test.coverage.coverageFile)
                                    .pipe(replace(/\\\\build\\\\src/mg, '\\\\src')) // windows
                                    .pipe(replace(/\/build\/src/mg, '/src')) // unix)
                                    .pipe(gulp.dest(config.test.coverage.dest))

                                    // Enforce statement coverage threshold based on config or if not defined 80%
                                    .on('finish', function () {
                                        return gulp.src('.')
                                            .pipe(instanbulEnforcer({
                                                coverageDirectory: config.test.coverage.dest,
                                                rootDirectory: '',
                                                thresholds: {
                                                    statements: config.test.coverage.threshold || 80
                                                }
                                            }));
                                    });
                            })
                    })
            });
    };
}

<|endoftext|>


<commit_before>'use strict';

var arrayify = require('arrayify');

module.exports = [

    'start watchers',

    function() {
        var watchers = [
            {
                glob: this.paths.js + '/**/*.js',
                tasks: ['build:js', 'livereload']
            }, {
                glob: this.paths.css + '/**/*.styl',
                tasks: ['build:css', 'livereload']
            }, {
                glob: this.paths.src + '/**/*.jade',
                tasks: ['build:html', 'livereload']
            }
        ];
        watchers.forEach(function(watcher) {
            this.gulp.watch(watcher.glob, function() {
                this.runSequence.apply(this.gulp, arrayify(watcher.tasks));
            }.bind(this)).on('change', this.log.watch.bind(this));
        }.bind(this));
    }

];
<commit_msg>refactor watchtasks to clean before build<commit_after>'use strict';

var arrayify = require('arrayify');

module.exports = [

    'start watchers',

    function() {
        var watchers = [
            {
                glob: this.paths.js + '/**/*.js',
                tasks: [
                    'clean:js',
                    'build:js',
                    'livereload'
                ]
            }, {
                glob: this.paths.css + '/**/*.styl',
                tasks: [
                    'clean:css',
                    'build:css',
                    'livereload'
                ]
            }, {
                glob: this.paths.src + '/**/*.jade',
                tasks: [
                    'clean:html',
                    'build:html',
                    'livereload'
                ]
            }
        ];
        watchers.forEach(function(watcher) {
            this.gulp.watch(watcher.glob, function() {
                this.runSequence.apply(this.gulp, arrayify(watcher.tasks));
            }.bind(this)).on('change', this.log.watch.bind(this));
        }.bind(this));
    }

];
<|endoftext|>


<commit_before>var Promise = require('bluebird');
var Dequeue = require(__dirname+"/dequeue.js");
var helper = require(__dirname+"/helper.js");

function Pool(r, options) {
    this._r = r;

    if (!helper.isPlainObject(options)) options = {};
    this.options = {};
    this.options.min = options.min || 50;
    this.options.max = options.max || 1000; // 4000 is about the maximum the kernel can take
    this.options.bufferSize = options.bufferSize || options.min || 50;
    this.options.timeoutError = options.timeoutError || 1000; // How long should we wait before recreating a connection that failed?
    this.options.timeoutGb = options.timeoutGb || 60*60*1000; // Default timeout for TCP connection is 2 hours on Linux, we time out after one hour.

    this.options.connection = {
        host: options.host || "localhost",
        port: options.port || 28015,
        db: options.db || "test"
    }

    this._pool = new Dequeue(this.options.min+1);
    this._line = new Dequeue(this.options.min+1);
    this._draining = null;

    if (this.options.min > this.options.bufferSize) throw new Error.ReqlDriverError("You cannot set a buffer smaller than the number of connections")

    this._numConnections = 0;

    for(var i=0; i<this.options.min; i++) {
        this.createConnection();
    }
}

Pool.prototype.getConnection = function() {
    var self = this;
    var p = new Promise(function(resolve, reject) {
        var connection = self._pool.pop();

        if (connection) {
            clearTimeout(connection.timeout);
            resolve(connection);
        }
        else {
            self._line.push(resolve);
        }

        self._expandBuffer();

    });
    return p;
};

Pool.prototype.putConnection = function(connection) {
    var self = this;

    if (self._line.getLength() > 0) {
        clearTimeout(connection.timeout);

        var resolve = self._line.shift();
        resolve(connection);
    }
    else {
        self._pool.push(connection);

        var timeoutCb = function() {
            if (self._pool.get(0) === connection) {
                if (self._pool.getLength() > self.options.min) {
                    self._pool.shift();
                    self._numConnections--;
                }
                else {
                    connection.timeout = setTimeout(timeoutCb, self.options.timeoutGb);
                }
            }
            else {
                connection.timeout = setTimeout(timeoutCb, self.options.timeoutGb);
            }
        }
        connection.timeout = setTimeout(timeoutCb, self.options.timeoutGb);

    }
};

Pool.prototype.createConnection = function() {
    var self = this;
    self._numConnections++;
    self._r.connect(self.options.connection).then(function(connection) {
        connection.on('error', function(e) {
            // We are going to close connection, but we don't want another process to use it before
            // So we remove it from the pool now (if it's inside)
            for(var i=0; i<self.getAvailableLength(); i++) {
                if (self._pool.get(i) === this) {
                    self._pool.delete(i);
                    break;
                }
            }

            // Not sure what happened here, so let's be safe and close this connection.
            connection.close().then(function() {
                self._numConnections--;
                self._expandBuffer();
            }).error(function(e) {
                console.log(e.message);
                // We failed to close this connection, but we removed it from the pool... so err, let's just ignore that.
                self._numConnections--;
                self._expandBuffer();
            });
        });
        connection.on('end', function(e) {
            // The connection was closed by something, let's clean...
            for(var i=0; i<self.getAvailableLength(); i++) {
                if (self._pool.get(i) === this) {
                    self._pool.delete(i);
                    break;
                }
            }

            self._numConnections--;
            self._expandBuffer();
        });
        connection.on('timeout', function() {
            for(var i=0; i<self.getAvailableLength(); i++) {
                if (self._pool.get(i) === this) {
                    self._pool.delete(i);
                    break;
                }
            }

            self._numConnections--;
            self._expandBuffer();

        });
        connection.on('release', function() {
            if (this._draining) {
                self._numConnections--;
                this.close();
                if (self.getLength() === 0) {
                    resolve();
                }
            }
            else {
                if (this._isOpen()) self.putConnection(this);
            }
        });
        self.putConnection(connection);
    }).error(function(error) {
        setTimeout(function() {
            self._expandBuffer
        }, self.options.timeoutError);
    })
};

Pool.prototype._expandBuffer = function() {
    if ((this._draining === null) && (this._pool.getLength() < this.options.bufferSize) && (this._numConnections < this.options.max)) {
        this.createConnection();
    }
}

Pool.prototype.getLength = function() {
    return this._numConnections;
}
Pool.prototype.getAvailableLength = function() {
    return this._pool.getLength();
}

Pool.prototype.getLineSize = function() {
    return this._line.getLength();
}
Pool.prototype.setOptions = function(options) {
    if (helper.isPlainObject(options)) {
        for(key in options) {
            this.options[key] = options[key];
        }
    }
    return this.options;
}
Pool.prototype.drain = function() {
    var self = this;
    self._draining = new Promise(function(resolve, reject) {
        var connection = self._pool.pop();
        while(connection) {
            self._numConnections--;

            connection.close();
            connection = self._pool.pop();
        }
        if (self.getLength() === 0) {
            resolve();
        }
    });
    return this._draining;
}

module.exports = Pool;
<commit_msg>Log error for the pool + fix retry<commit_after>var Promise = require('bluebird');
var Dequeue = require(__dirname+"/dequeue.js");
var helper = require(__dirname+"/helper.js");

function Pool(r, options) {
    this._r = r;

    if (!helper.isPlainObject(options)) options = {};
    this.options = {};
    this.options.min = options.min || 50;
    this.options.max = options.max || 1000; // 4000 is about the maximum the kernel can take
    this.options.bufferSize = options.bufferSize || options.min || 50;
    this.options.timeoutError = options.timeoutError || 1000; // How long should we wait before recreating a connection that failed?
    this.options.timeoutGb = options.timeoutGb || 60*60*1000; // Default timeout for TCP connection is 2 hours on Linux, we time out after one hour.

    this.options.connection = {
        host: options.host || "localhost",
        port: options.port || 28015,
        db: options.db || "test"
    }

    this._pool = new Dequeue(this.options.min+1);
    this._line = new Dequeue(this.options.min+1);
    this._draining = null;

    if (this.options.min > this.options.bufferSize) throw new Error.ReqlDriverError("You cannot set a buffer smaller than the number of connections")

    this._numConnections = 0;

    for(var i=0; i<this.options.min; i++) {
        this.createConnection();
    }
}

Pool.prototype.getConnection = function() {
    var self = this;
    var p = new Promise(function(resolve, reject) {
        var connection = self._pool.pop();

        if (connection) {
            clearTimeout(connection.timeout);
            resolve(connection);
        }
        else {
            self._line.push(resolve);
        }

        self._expandBuffer();

    });
    return p;
};

Pool.prototype.putConnection = function(connection) {
    var self = this;

    if (self._line.getLength() > 0) {
        clearTimeout(connection.timeout);

        var resolve = self._line.shift();
        resolve(connection);
    }
    else {
        self._pool.push(connection);

        var timeoutCb = function() {
            if (self._pool.get(0) === connection) {
                if (self._pool.getLength() > self.options.min) {
                    self._pool.shift();
                    self._numConnections--;
                }
                else {
                    connection.timeout = setTimeout(timeoutCb, self.options.timeoutGb);
                }
            }
            else {
                connection.timeout = setTimeout(timeoutCb, self.options.timeoutGb);
            }
        }
        connection.timeout = setTimeout(timeoutCb, self.options.timeoutGb);

    }
};

Pool.prototype.createConnection = function() {
    var self = this;
    self._numConnections++;
    self._r.connect(self.options.connection).then(function(connection) {
        connection.on('error', function(e) {
            // We are going to close connection, but we don't want another process to use it before
            // So we remove it from the pool now (if it's inside)
            for(var i=0; i<self.getAvailableLength(); i++) {
                if (self._pool.get(i) === this) {
                    self._pool.delete(i);
                    break;
                }
            }

            // Not sure what happened here, so let's be safe and close this connection.
            connection.close().then(function() {
                self._numConnections--;
                self._expandBuffer();
            }).error(function(e) {
                console.log(e.message);
                // We failed to close this connection, but we removed it from the pool... so err, let's just ignore that.
                self._numConnections--;
                self._expandBuffer();
            });
        });
        connection.on('end', function(e) {
            // The connection was closed by something, let's clean...
            for(var i=0; i<self.getAvailableLength(); i++) {
                if (self._pool.get(i) === this) {
                    self._pool.delete(i);
                    break;
                }
            }

            self._numConnections--;
            self._expandBuffer();
        });
        connection.on('timeout', function() {
            for(var i=0; i<self.getAvailableLength(); i++) {
                if (self._pool.get(i) === this) {
                    self._pool.delete(i);
                    break;
                }
            }

            self._numConnections--;
            self._expandBuffer();

        });
        connection.on('release', function() {
            if (this._draining) {
                self._numConnections--;
                this.close();
                if (self.getLength() === 0) {
                    resolve();
                }
            }
            else {
                if (this._isOpen()) self.putConnection(this);
            }
        });
        self.putConnection(connection);
    }).error(function(error) {
        // Log an error
        console.log("Fail to create a new connection for the connection pool The error returned was:")
        console.log(error.message);
        console.log(error.stack);
        setTimeout(function() {
            self._expandBuffer();
        }, self.options.timeoutError);
    })
};

Pool.prototype._expandBuffer = function() {
    if ((this._draining === null) && (this._pool.getLength() < this.options.bufferSize) && (this._numConnections < this.options.max)) {
        this.createConnection();
    }
}

Pool.prototype.getLength = function() {
    return this._numConnections;
}
Pool.prototype.getAvailableLength = function() {
    return this._pool.getLength();
}

Pool.prototype.getLineSize = function() {
    return this._line.getLength();
}
Pool.prototype.setOptions = function(options) {
    if (helper.isPlainObject(options)) {
        for(key in options) {
            this.options[key] = options[key];
        }
    }
    return this.options;
}
Pool.prototype.drain = function() {
    var self = this;
    self._draining = new Promise(function(resolve, reject) {
        var connection = self._pool.pop();
        while(connection) {
            self._numConnections--;

            connection.close();
            connection = self._pool.pop();
        }
        if (self.getLength() === 0) {
            resolve();
        }
    });
    return this._draining;
}

module.exports = Pool;
<|endoftext|>