<commit_before>/* See license.txt for terms of usage */

"use strict";

module.metadata = {
  "stability": "stable"
};

const self = require("sdk/self");

const { Cu, Ci, Cc, CC } = require("chrome");
const { Trace, TraceError } = require("firebug.sdk/lib/core/trace.js").get(module.id);
const { getMostRecentBrowserWindow } = require("sdk/window/utils");
const { Content } = require("firebug.sdk/lib/core/content");
const { Class } = require("sdk/core/heritage");
const { defer, resolve } = require("sdk/core/promise");
const { emit } = require("sdk/event/core");
const { setTimeout, clearTimeout } = require("sdk/timers");
const base64 = require("sdk/base64");

// DevTools
const { devtools } = Cu.import("resource://gre/modules/devtools/Loader.jsm", {});
const { makeInfallible } = devtools["require"]("devtools/toolkit/DevToolsUtils.js");

// File Services
const { FileUtils } = Cu.import("resource://gre/modules/FileUtils.jsm", {});
const nsIFilePicker = Ci.nsIFilePicker;
const ioService = Cc["@mozilla.org/network/io-service;1"].getService(Ci.nsIIOService);

// Platform
Cu.importGlobalProperties(["btoa"]);

// Constants
const XHTML_NS = "http://www.w3.org/1999/xhtml";
const dataFileName = "data.json";
const asyncSaveDelay = 250;

/**
 * This object represents persistent storage for registered layers. Layers
 * and all their properties are stored into a JSON file that is located
 * within the current browser profile.
 *
 * Existing platform API could be utilized (introduced in Fx 39):
 * https://dxr.mozilla.org/mozilla-central/source/toolkit/devtools/shared/async-storage.js
 */
var PixelPerfectStore = Class(
/** @lends PixelPerfectStore */
{
  // Initialization

  initialize: function() {
    this.layers = [];
    this.version = 1;
    this.layers = this.loadLayers();

    // Generate imageDataURLs. These are sent to the backend
    // as well as to the popup window that runs with type 'content'.
    // Both these scopes don't have access to the local file system.
    this.layers.forEach(layer => {
      this.updateImageDataURL(layer);
    });

    if (!this.layers) {
      this.layers = [];
      this.save();
    }
  },

  destroy: function() {
  },

  // Public Layer API

  add: function(files) {
    files = files ? files : this.getImageFiles();
    if (!files) {
      return;
    }

    // Make sure we deal with an array.
    files = Array.isArray(files) ? files : [files];

    // Iterate over all selected files
    let result = [];
    files.forEach(file => {
      result.push(this.createLayer(file));
    });

    return result;
  },

  remove: function(id) {
    for (var i=0; i<this.layers.length; i++) {
      let layer = this.layers[i];
      if (layer.id == id) {
        this.layers.splice(i, 1);
        break;
      }
    }

    this.save();

    emit(this, "remove", id);
  },

  modify: function(id, props) {
    let layer = this.getLayer(id);
    for (let p in props) {
      layer[p] = props[p];
    }

    this.save();

    emit(this, "modify", id, props);
  },

  move: function(from, to) {
    var removed = this.layers.splice(from, 1)[0];
    this.layers.splice(to, 0, removed);

    emit(this, "move", from, to);
  },

  // Layers

  createLayer: function(imageFile) {
    let id = "layer-" + (new Date()).getTime();
    let layer = {
      id: id,
      opacity: 50,
      x: 0,
      y: 0,
      scale: 1,
      visible: true,
      lock: false,
      invert: false,
      url: this.getURLFromLocalFile(imageFile)
    };

    this.layers.push(layer);

    this.updateImageDataURL(layer);

    emit(this, "add", layer);

    this.save();

    return layer;
  },

  getLayer: function(id) {
    for (let i=0; i<this.layers.length; i++) {
      let layer = this.layers[i];
      if (layer.id == id) {
        return layer;
      }
    }
  },

  cloneLayer: function(layer) {
    let result = {};
    for (let p in layer) {
      result[p] = layer[p];
    }
    return result;
  },

  forEachLayer: function(callback) {
    this.layers.forEach(layer => {
      callback(layer);
    });
  },

  // Persistence

  getStorageFile: function(fileName) {
    return FileUtils.getFile("ProfD", ["pixel-perfect", fileName]);
  },

  loadLayers: function() {
    try {
      let json = this.load();
      let data = JSON.parse(json);

      // Check existence of image files and remove layers
      // with invalid image URL.
      return this.checkImageFiles(data.layers);
    } catch (err) {
    }
  },

  checkImageFiles: function(layers) {
    // Remove all layers that are not associated with
    // an existing image file.
    return layers.filter(layer => this.imageFileExists(layer.url));
  },

  load: makeInfallible(function() {
    let file = this.getStorageFile(dataFileName);
    if (!file.exists()) {
      return;
    }

    let inputStream = Cc["@mozilla.org/network/file-input-stream;1"]
      .createInstance(Ci.nsIFileInputStream);
    let cstream = Cc["@mozilla.org/intl/converter-input-stream;1"]
      .createInstance(Ci.nsIConverterInputStream);

    // Initialize input stream (read, create)
    let permFlags = parseInt("0666", 8);
    inputStream.init(file, 0x01 | 0x08, permFlags, 0);
    cstream.init(inputStream, "UTF-8", 0, 0);

    // Load JSON data
    let json = "";
    let data = {};
    while (cstream.readString(-1, data) != 0) {
      json += data.value;
    }

    inputStream.close();

    return json;
  }),

  save: makeInfallible(function() {
    if (this.saveTimeout) {
      return;
    }

    // Save on timeout to avoid slow disk performance penalties when
    // performing several save operations in a row.
    this.saveTimeout = setTimeout(() => {
      this.saveAsync();
      this.saveTimeout = null;
    }, asyncSaveDelay);
  }),

  saveAsync: makeInfallible(function() {
    let data = {
      version: this.version,
      layers: this.layers
    }

    // Generated data URLs aren't stored into a file.
    data.layers = data.layers.map(layer => {
      let clone = this.cloneLayer(layer);
      delete clone.dataUrl;
      return clone;
    });

    let json = JSON.stringify(data, 2, 2);
    let file = this.getStorageFile(dataFileName);

    // Initialize output stream.
    var outputStream = Cc["@mozilla.org/network/file-output-stream;1"]
      .createInstance(Ci.nsIFileOutputStream);

    // write, create, truncate
    let permFlags = parseInt("0666", 8);
    outputStream.init(file, 0x02 | 0x08 | 0x20, permFlags, 0);

    // Store JSON
    outputStream.write(json, json.length);
    outputStream.close();
  }),

  // Image Files Picker

  getImageFiles: function() {
    let browser = getMostRecentBrowserWindow();

    let fp = Cc["@mozilla.org/filepicker;1"].createInstance(nsIFilePicker);
    fp.init(browser, null, nsIFilePicker.modeOpen | nsIFilePicker.modeOpenMultiple);
    fp.appendFilters(nsIFilePicker.filterAll | nsIFilePicker.filterImages);
    fp.filterIndex = 1;

    let rv = fp.show();
    if (rv == nsIFilePicker.returnOK || rv == nsIFilePicker.returnReplace) {
      // Convert simple enumerator to an array.
      let result = [];
      let files = fp.files;
      while (files.hasMoreElements()) {
        result.push(files.getNext());
      }
      return result;
    }

    return null;
  },

  getURLFromLocalFile: function(file) {
    let fileHandler = ioService.getProtocolHandler("file")
      .QueryInterface(Ci.nsIFileProtocolHandler);
    let url = fileHandler.getURLSpecFromFile(file);
    return url;
  },

  imageFileExists: function(layerUrl) {
    if (layerUrl.startsWith("data:")) {
      return true;
    }

    let url = ioService.newURI(layerUrl, null, null);
    if (!url || !url.schemeIs("file")) {
      return false;
    }

    let imageFile = url.QueryInterface(Ci.nsIFileURL).file;
    return imageFile.exists();
  },

  // Image Data URL

  updateImageDataURL: function(layer) {
    layer.dataUrl = this.getImageDataURL(layer.id);
  },

  getImageDataURL: makeInfallible(function(id) {
    let layer = this.getLayer(id);
    if (layer.url.startsWith("data:")) {
      return layer.url;
    }

    if (layer.dataUrl) {
      return layer.dataUrl;
    }

    let url = ioService.newURI(layer.url, null, null);
    if (!url || !url.schemeIs("file")) {
      return null;
    }

    let imageFile = url.QueryInterface(Ci.nsIFileURL).file;
    if (!imageFile.exists()) {
      return null;
    }

    let inputStream = Cc["@mozilla.org/network/file-input-stream;1"].
      createInstance(Ci.nsIFileInputStream);

    inputStream.init(imageFile, -1, -1, false);
    let binaryStream = Cc["@mozilla.org/binaryinputstream;1"].
      createInstance(Ci.nsIBinaryInputStream);
    binaryStream.setInputStream(inputStream);

    let bytes = binaryStream.readBytes(binaryStream.available());
    let encodedData = btoa(bytes);

    let lastDot = layer.url.lastIndexOf(".");
    let fileExt = layer.url.substr(lastDot + 1);
    let mimeType = (fileExt == "svg") ? "image/svg+xml" : "image/png";

    return "data:" + mimeType + ";base64," + encodedData;
  })
});

// Exports from this module
exports.PixelPerfectStore = PixelPerfectStore;
<commit_msg>Avoid NPE<commit_after>/* See license.txt for terms of usage */

"use strict";

module.metadata = {
  "stability": "stable"
};

const self = require("sdk/self");

const { Cu, Ci, Cc, CC } = require("chrome");
const { Trace, TraceError } = require("firebug.sdk/lib/core/trace.js").get(module.id);
const { getMostRecentBrowserWindow } = require("sdk/window/utils");
const { Content } = require("firebug.sdk/lib/core/content");
const { Class } = require("sdk/core/heritage");
const { defer, resolve } = require("sdk/core/promise");
const { emit } = require("sdk/event/core");
const { setTimeout, clearTimeout } = require("sdk/timers");
const base64 = require("sdk/base64");

// DevTools
const { devtools } = Cu.import("resource://gre/modules/devtools/Loader.jsm", {});
const { makeInfallible } = devtools["require"]("devtools/toolkit/DevToolsUtils.js");

// File Services
const { FileUtils } = Cu.import("resource://gre/modules/FileUtils.jsm", {});
const nsIFilePicker = Ci.nsIFilePicker;
const ioService = Cc["@mozilla.org/network/io-service;1"].getService(Ci.nsIIOService);

// Platform
Cu.importGlobalProperties(["btoa"]);

// Constants
const XHTML_NS = "http://www.w3.org/1999/xhtml";
const dataFileName = "data.json";
const asyncSaveDelay = 250;

/**
 * This object represents persistent storage for registered layers. Layers
 * and all their properties are stored into a JSON file that is located
 * within the current browser profile.
 *
 * Existing platform API could be utilized (introduced in Fx 39):
 * https://dxr.mozilla.org/mozilla-central/source/toolkit/devtools/shared/async-storage.js
 */
var PixelPerfectStore = Class(
/** @lends PixelPerfectStore */
{
  // Initialization

  initialize: function() {
    this.layers = [];
    this.version = 1;
    this.layers = this.loadLayers();

    // Generate imageDataURLs. These are sent to the backend
    // as well as to the popup window that runs with type 'content'.
    // Both these scopes don't have access to the local file system.
    if (this.layers) {
      this.layers.forEach(layer => {
        this.updateImageDataURL(layer);
      });
    } else {
      this.layers = [];
      this.save();
    }
  },

  destroy: function() {
  },

  // Public Layer API

  add: function(files) {
    files = files ? files : this.getImageFiles();
    if (!files) {
      return;
    }

    // Make sure we deal with an array.
    files = Array.isArray(files) ? files : [files];

    // Iterate over all selected files
    let result = [];
    files.forEach(file => {
      result.push(this.createLayer(file));
    });

    return result;
  },

  remove: function(id) {
    for (var i=0; i<this.layers.length; i++) {
      let layer = this.layers[i];
      if (layer.id == id) {
        this.layers.splice(i, 1);
        break;
      }
    }

    this.save();

    emit(this, "remove", id);
  },

  modify: function(id, props) {
    let layer = this.getLayer(id);
    for (let p in props) {
      layer[p] = props[p];
    }

    this.save();

    emit(this, "modify", id, props);
  },

  move: function(from, to) {
    var removed = this.layers.splice(from, 1)[0];
    this.layers.splice(to, 0, removed);

    emit(this, "move", from, to);
  },

  // Layers

  createLayer: function(imageFile) {
    let id = "layer-" + (new Date()).getTime();
    let layer = {
      id: id,
      opacity: 50,
      x: 0,
      y: 0,
      scale: 1,
      visible: true,
      lock: false,
      invert: false,
      url: this.getURLFromLocalFile(imageFile)
    };

    this.layers.push(layer);

    this.updateImageDataURL(layer);

    emit(this, "add", layer);

    this.save();

    return layer;
  },

  getLayer: function(id) {
    for (let i=0; i<this.layers.length; i++) {
      let layer = this.layers[i];
      if (layer.id == id) {
        return layer;
      }
    }
  },

  cloneLayer: function(layer) {
    let result = {};
    for (let p in layer) {
      result[p] = layer[p];
    }
    return result;
  },

  forEachLayer: function(callback) {
    this.layers.forEach(layer => {
      callback(layer);
    });
  },

  // Persistence

  getStorageFile: function(fileName) {
    return FileUtils.getFile("ProfD", ["pixel-perfect", fileName]);
  },

  loadLayers: function() {
    try {
      let json = this.load();
      let data = JSON.parse(json);

      // Check existence of image files and remove layers
      // with invalid image URL.
      return this.checkImageFiles(data.layers);
    } catch (err) {
    }
  },

  checkImageFiles: function(layers) {
    // Remove all layers that are not associated with
    // an existing image file.
    return layers.filter(layer => this.imageFileExists(layer.url));
  },

  load: makeInfallible(function() {
    let file = this.getStorageFile(dataFileName);
    if (!file.exists()) {
      return;
    }

    let inputStream = Cc["@mozilla.org/network/file-input-stream;1"]
      .createInstance(Ci.nsIFileInputStream);
    let cstream = Cc["@mozilla.org/intl/converter-input-stream;1"]
      .createInstance(Ci.nsIConverterInputStream);

    // Initialize input stream (read, create)
    let permFlags = parseInt("0666", 8);
    inputStream.init(file, 0x01 | 0x08, permFlags, 0);
    cstream.init(inputStream, "UTF-8", 0, 0);

    // Load JSON data
    let json = "";
    let data = {};
    while (cstream.readString(-1, data) != 0) {
      json += data.value;
    }

    inputStream.close();

    return json;
  }),

  save: makeInfallible(function() {
    if (this.saveTimeout) {
      return;
    }

    // Save on timeout to avoid slow disk performance penalties when
    // performing several save operations in a row.
    this.saveTimeout = setTimeout(() => {
      this.saveAsync();
      this.saveTimeout = null;
    }, asyncSaveDelay);
  }),

  saveAsync: makeInfallible(function() {
    let data = {
      version: this.version,
      layers: this.layers
    }

    // Generated data URLs aren't stored into a file.
    data.layers = data.layers.map(layer => {
      let clone = this.cloneLayer(layer);
      delete clone.dataUrl;
      return clone;
    });

    let json = JSON.stringify(data, 2, 2);
    let file = this.getStorageFile(dataFileName);

    // Initialize output stream.
    var outputStream = Cc["@mozilla.org/network/file-output-stream;1"]
      .createInstance(Ci.nsIFileOutputStream);

    // write, create, truncate
    let permFlags = parseInt("0666", 8);
    outputStream.init(file, 0x02 | 0x08 | 0x20, permFlags, 0);

    // Store JSON
    outputStream.write(json, json.length);
    outputStream.close();
  }),

  // Image Files Picker

  getImageFiles: function() {
    let browser = getMostRecentBrowserWindow();

    let fp = Cc["@mozilla.org/filepicker;1"].createInstance(nsIFilePicker);
    fp.init(browser, null, nsIFilePicker.modeOpen | nsIFilePicker.modeOpenMultiple);
    fp.appendFilters(nsIFilePicker.filterAll | nsIFilePicker.filterImages);
    fp.filterIndex = 1;

    let rv = fp.show();
    if (rv == nsIFilePicker.returnOK || rv == nsIFilePicker.returnReplace) {
      // Convert simple enumerator to an array.
      let result = [];
      let files = fp.files;
      while (files.hasMoreElements()) {
        result.push(files.getNext());
      }
      return result;
    }

    return null;
  },

  getURLFromLocalFile: function(file) {
    let fileHandler = ioService.getProtocolHandler("file")
      .QueryInterface(Ci.nsIFileProtocolHandler);
    let url = fileHandler.getURLSpecFromFile(file);
    return url;
  },

  imageFileExists: function(layerUrl) {
    if (layerUrl.startsWith("data:")) {
      return true;
    }

    let url = ioService.newURI(layerUrl, null, null);
    if (!url || !url.schemeIs("file")) {
      return false;
    }

    let imageFile = url.QueryInterface(Ci.nsIFileURL).file;
    return imageFile.exists();
  },

  // Image Data URL

  updateImageDataURL: function(layer) {
    layer.dataUrl = this.getImageDataURL(layer.id);
  },

  getImageDataURL: makeInfallible(function(id) {
    let layer = this.getLayer(id);
    if (layer.url.startsWith("data:")) {
      return layer.url;
    }

    if (layer.dataUrl) {
      return layer.dataUrl;
    }

    let url = ioService.newURI(layer.url, null, null);
    if (!url || !url.schemeIs("file")) {
      return null;
    }

    let imageFile = url.QueryInterface(Ci.nsIFileURL).file;
    if (!imageFile.exists()) {
      return null;
    }

    let inputStream = Cc["@mozilla.org/network/file-input-stream;1"].
      createInstance(Ci.nsIFileInputStream);

    inputStream.init(imageFile, -1, -1, false);
    let binaryStream = Cc["@mozilla.org/binaryinputstream;1"].
      createInstance(Ci.nsIBinaryInputStream);
    binaryStream.setInputStream(inputStream);

    let bytes = binaryStream.readBytes(binaryStream.available());
    let encodedData = btoa(bytes);

    let lastDot = layer.url.lastIndexOf(".");
    let fileExt = layer.url.substr(lastDot + 1);
    let mimeType = (fileExt == "svg") ? "image/svg+xml" : "image/png";

    return "data:" + mimeType + ";base64," + encodedData;
  })
});

// Exports from this module
exports.PixelPerfectStore = PixelPerfectStore;
<|endoftext|>


<commit_before>/* See license.txt for terms of usage */

"use strict";

const self = require("sdk/self");

const { Cu, Ci, Cc, CC } = require("chrome");
const { Trace, TraceError } = require("./sdk/core/trace.js").get(module.id);
const { getMostRecentBrowserWindow } = require("sdk/window/utils");
const { Content } = require("./sdk/core/content");
const { Class } = require("sdk/core/heritage");
const { defer, resolve } = require("sdk/core/promise");
const { emit } = require("sdk/event/core");

// DevTools
const { devtools } = Cu.import("resource://gre/modules/devtools/Loader.jsm", {});
const { makeInfallible } = devtools["require"]("devtools/toolkit/DevToolsUtils.js");

// File Services
const { FileUtils } = Cu.import("resource://gre/modules/FileUtils.jsm", {});
const nsIFilePicker = Ci.nsIFilePicker;
const ioService = Cc["@mozilla.org/network/io-service;1"].getService(Ci.nsIIOService);

// Storage file name
const dataFileName = "data.json";

// Constants
const XHTML_NS = "http://www.w3.org/1999/xhtml";

// Default data with one example overlay.
const defaultOverlay = {
  id: "default-overlay",
  opacity: 80,
  x: 0,
  y: 0,
  scale: 1,
  visible: true,
  lock: false,
  url: "chrome://pixelperfect/skin/grid.png"
};

var overlayId = 5;

/**
 * TODO docs:
 */
var PixelPerfectStore = Class(
/** @lends PixelPerfectStore */
{
  // Initialization

  initialize: function() {
    let json = this.load();

    try {
      this.overlays = JSON.parse(json);
    } catch (err) {
    }

    if (!this.overlays) {
      this.overlays = [];
      this.save();
    }
  },

  destroy: function() {
  },

  // Accessors

  getJSON: function() {
    return JSON.stringify(this.overlays, 2, 2);
  },

  forEachOverlay: function(callback) {
    this.overlays.forEach(overlay => {
      callback(overlay);
    });
  },

  add: function() {
    let imageFile = this.getOverlayImage();
    if (!imageFile) {
      return;
    }

    let id = "overlay-" + (new Date()).getTime();
    let overlay = {
      id: id,
      opacity: 50,
      x: 0,
      y: 0,
      scale: 1,
      visible: true,
      lock: false,
      url: this.getURLFromLocalFile(imageFile)
    };

    this.overlays.push(overlay);

    this.save();

    emit(this, "add", overlay);
  },

  remove: function(id) {
    for (var i=0; i<this.overlays.length; i++) {
      let overlay = this.overlays[i];
      if (overlay.id == id) {
        this.overlays.splice(i, 1);
        break;
      }
    }

    this.save();

    emit(this, "remove", id);
  },

  modify: function(id, props) {
    let overlay = this.getOverlay(id);
    for (let p in props) {
      // xxxHonza: use property types.
      overlay[p] = props[p];
    }

    this.save();

    emit(this, "modify", id, props);
  },

  // Persistence

  getStoreFile: function(fileName) {
    return FileUtils.getFile("ProfD", ["pixel-perfect", fileName]);
  },

  load: makeInfallible(function() {
    let file = this.getStoreFile(dataFileName);
    if (!file.exists()) {
      return;
    }

    let inputStream = Cc["@mozilla.org/network/file-input-stream;1"]
      .createInstance(Ci.nsIFileInputStream);
    let cstream = Cc["@mozilla.org/intl/converter-input-stream;1"]
      .createInstance(Ci.nsIConverterInputStream);

    // Initialize input stream (read, create)
    let permFlags = parseInt("0666", 8);
    inputStream.init(file, 0x01 | 0x08, permFlags, 0);
    cstream.init(inputStream, "UTF-8", 0, 0);

    // Load JSON data
    let json = "";
    let data = {};
    while (cstream.readString(-1, data) != 0) {
      json += data.value;
    }

    inputStream.close();

    return json;
  }),

  // xxxHonza: implement async save
  save: makeInfallible(function() {
    let json = this.getJSON();
    let file = this.getStoreFile(dataFileName);

    // Initialize output stream.
    var outputStream = Cc["@mozilla.org/network/file-output-stream;1"]
      .createInstance(Ci.nsIFileOutputStream);

    // write, create, truncate
    let permFlags = parseInt("0666", 8);
    outputStream.init(file, 0x02 | 0x08 | 0x20, permFlags, 0);

    // Store JSON
    outputStream.write(json, json.length);
    outputStream.close();
  }),

  // Helpers

  getOverlay: function(id) {
    for (let i=0; i<this.overlays.length; i++) {
      let overlay = this.overlays[i];
      if (overlay.id == id) {
        return overlay;
      }
    }
  },

  getOverlayImage: function() {
    let browser = getMostRecentBrowserWindow();

    let fp = Cc["@mozilla.org/filepicker;1"].createInstance(nsIFilePicker);
    fp.init(browser, null, nsIFilePicker.modeOpen);
    fp.appendFilters(nsIFilePicker.filterAll | nsIFilePicker.filterImages);
    fp.filterIndex = 1;

    let rv = fp.show();
    if (rv == nsIFilePicker.returnOK || rv == nsIFilePicker.returnReplace) {
      return fp.file;
    }

    return null;
  },

  getURLFromLocalFile: function(file) {
    let fileHandler = ioService.getProtocolHandler("file")
      .QueryInterface(Ci.nsIFileProtocolHandler);
    let url = fileHandler.getURLSpecFromFile(file);
    return url;
  },

  // xxxHonza: cache the image data, but do not persist.
  getOverlayImageData: makeInfallible(function(id) {
    let overlay = this.getOverlay(id);

    if (overlay.url.startsWith("data:")) {
      return resolve(overlay.url);
    }

    let deferred = defer();
    let win = getMostRecentBrowserWindow();

    // xxxHonza: remove the Image object?
    let img = new win.Image();

    img.onload = () => {
      try {
        let imageData = imageToImageData(img);
        deferred.resolve(imageData);
      } catch (e) {
        deferred.reject(null);
      }
    }

    // If the URL doesn't point to a resource, reject
    img.onerror = () => {
      deferred.reject(null);
    }

    img.src = overlay.url;

    return deferred.promise;
  }),

  cloneOverlay: function(overlay) {
    let result = {};
    for (let p in overlay) {
      result[p] = overlay[p];
    }
    return result;
  }
});

/**
 * xxxHonza: TODO docs
 */
var imageToImageData = makeInfallible(node => {
  // Get the image resize ratio if a maxDim was provided
  let resizeRatio = 1;
  let imgWidth = node.naturalWidth || node.width;
  let imgHeight = node.naturalHeight || node.height;
  let imgMax = Math.max(imgWidth, imgHeight);

  // Extract the image data
  let imageData;

  // The image may already be a data-uri, in which case, save ourselves the
  // trouble of converting via the canvas.drawImage.toDataURL method
  if (node.src.startsWith("data:")) {
    imageData = node.src;
  } else {
    // Create a canvas to copy the rawNode into and get the imageData from
    let canvas = node.ownerDocument.createElementNS(XHTML_NS, "canvas");
    canvas.width = imgWidth * resizeRatio;
    canvas.height = imgHeight * resizeRatio;
    let ctx = canvas.getContext("2d");

    // Copy the rawNode image or canvas in the new canvas and extract data
    ctx.drawImage(node, 0, 0, canvas.width, canvas.height);
    imageData = canvas.toDataURL("image/png");
  }

  return {
    data: imageData,
    size: {
      naturalWidth: imgWidth,
      naturalHeight: imgHeight,
      resized: resizeRatio !== 1
    }
  }
})

// Exports from this module
exports.PixelPerfectStore = PixelPerfectStore;
<commit_msg>Support for appending multiple layers in one step<commit_after>/* See license.txt for terms of usage */

"use strict";

const self = require("sdk/self");

const { Cu, Ci, Cc, CC } = require("chrome");
const { Trace, TraceError } = require("./sdk/core/trace.js").get(module.id);
const { getMostRecentBrowserWindow } = require("sdk/window/utils");
const { Content } = require("./sdk/core/content");
const { Class } = require("sdk/core/heritage");
const { defer, resolve } = require("sdk/core/promise");
const { emit } = require("sdk/event/core");

// DevTools
const { devtools } = Cu.import("resource://gre/modules/devtools/Loader.jsm", {});
const { makeInfallible } = devtools["require"]("devtools/toolkit/DevToolsUtils.js");

// File Services
const { FileUtils } = Cu.import("resource://gre/modules/FileUtils.jsm", {});
const nsIFilePicker = Ci.nsIFilePicker;
const ioService = Cc["@mozilla.org/network/io-service;1"].getService(Ci.nsIIOService);

// Storage file name
const dataFileName = "data.json";

// Constants
const XHTML_NS = "http://www.w3.org/1999/xhtml";

// Default data with one example overlay.
const defaultOverlay = {
  id: "default-overlay",
  opacity: 80,
  x: 0,
  y: 0,
  scale: 1,
  visible: true,
  lock: false,
  url: "chrome://pixelperfect/skin/grid.png"
};

var overlayId = 5;

/**
 * TODO docs:
 */
var PixelPerfectStore = Class(
/** @lends PixelPerfectStore */
{
  // Initialization

  initialize: function() {
    let json = this.load();

    try {
      this.overlays = JSON.parse(json);
    } catch (err) {
    }

    if (!this.overlays) {
      this.overlays = [];
      this.save();
    }
  },

  destroy: function() {
  },

  // Accessors

  getJSON: function() {
    return JSON.stringify(this.overlays, 2, 2);
  },

  forEachOverlay: function(callback) {
    this.overlays.forEach(overlay => {
      callback(overlay);
    });
  },

  add: function() {
    let files = this.getImageFiles();
    if (!files) {
      return;
    }

    // Iterate over all selected files
    while (files.hasMoreElements()) {
      let file = files.getNext()
      this.createOverlay(file);
    }
  },

  createOverlay: function(imageFile) {
    let id = "overlay-" + (new Date()).getTime();
    let overlay = {
      id: id,
      opacity: 50,
      x: 0,
      y: 0,
      scale: 1,
      visible: true,
      lock: false,
      url: this.getURLFromLocalFile(imageFile)
    };

    this.overlays.push(overlay);

    emit(this, "add", overlay);

    this.save();
  },

  remove: function(id) {
    for (var i=0; i<this.overlays.length; i++) {
      let overlay = this.overlays[i];
      if (overlay.id == id) {
        this.overlays.splice(i, 1);
        break;
      }
    }

    this.save();

    emit(this, "remove", id);
  },

  modify: function(id, props) {
    let overlay = this.getOverlay(id);
    for (let p in props) {
      // xxxHonza: use property types.
      overlay[p] = props[p];
    }

    this.save();

    emit(this, "modify", id, props);
  },

  // Persistence

  getStorageFile: function(fileName) {
    return FileUtils.getFile("ProfD", ["pixel-perfect", fileName]);
  },

  load: makeInfallible(function() {
    let file = this.getStorageFile(dataFileName);
    if (!file.exists()) {
      return;
    }

    let inputStream = Cc["@mozilla.org/network/file-input-stream;1"]
      .createInstance(Ci.nsIFileInputStream);
    let cstream = Cc["@mozilla.org/intl/converter-input-stream;1"]
      .createInstance(Ci.nsIConverterInputStream);

    // Initialize input stream (read, create)
    let permFlags = parseInt("0666", 8);
    inputStream.init(file, 0x01 | 0x08, permFlags, 0);
    cstream.init(inputStream, "UTF-8", 0, 0);

    // Load JSON data
    let json = "";
    let data = {};
    while (cstream.readString(-1, data) != 0) {
      json += data.value;
    }

    inputStream.close();

    return json;
  }),

  // xxxHonza: implement async save
  save: makeInfallible(function() {
    let json = this.getJSON();
    let file = this.getStorageFile(dataFileName);

    // Initialize output stream.
    var outputStream = Cc["@mozilla.org/network/file-output-stream;1"]
      .createInstance(Ci.nsIFileOutputStream);

    // write, create, truncate
    let permFlags = parseInt("0666", 8);
    outputStream.init(file, 0x02 | 0x08 | 0x20, permFlags, 0);

    // Store JSON
    outputStream.write(json, json.length);
    outputStream.close();
  }),

  // Helpers

  getOverlay: function(id) {
    for (let i=0; i<this.overlays.length; i++) {
      let overlay = this.overlays[i];
      if (overlay.id == id) {
        return overlay;
      }
    }
  },

  getImageFiles: function() {
    let browser = getMostRecentBrowserWindow();

    let fp = Cc["@mozilla.org/filepicker;1"].createInstance(nsIFilePicker);
    fp.init(browser, null, nsIFilePicker.modeOpen | nsIFilePicker.modeOpenMultiple);
    fp.appendFilters(nsIFilePicker.filterAll | nsIFilePicker.filterImages);
    fp.filterIndex = 1;

    let rv = fp.show();
    if (rv == nsIFilePicker.returnOK || rv == nsIFilePicker.returnReplace) {
      return fp.files;
    }

    return null;
  },

  getURLFromLocalFile: function(file) {
    let fileHandler = ioService.getProtocolHandler("file")
      .QueryInterface(Ci.nsIFileProtocolHandler);
    let url = fileHandler.getURLSpecFromFile(file);
    return url;
  },

  // xxxHonza: cache the image data, but do not persist.
  getOverlayImageData: makeInfallible(function(id) {
    let overlay = this.getOverlay(id);

    if (overlay.url.startsWith("data:")) {
      return resolve(overlay.url);
    }

    let deferred = defer();
    let win = getMostRecentBrowserWindow();

    // xxxHonza: remove the Image object?
    let img = new win.Image();

    img.onload = () => {
      try {
        let imageData = imageToImageData(img);
        deferred.resolve(imageData);
      } catch (e) {
        deferred.reject(null);
      }
    }

    // If the URL doesn't point to a resource, reject
    img.onerror = () => {
      deferred.reject(null);
    }

    img.src = overlay.url;

    return deferred.promise;
  }),

  cloneOverlay: function(overlay) {
    let result = {};
    for (let p in overlay) {
      result[p] = overlay[p];
    }
    return result;
  }
});

/**
 * xxxHonza: TODO docs
 */
var imageToImageData = makeInfallible(node => {
  // Get the image resize ratio if a maxDim was provided
  let resizeRatio = 1;
  let imgWidth = node.naturalWidth || node.width;
  let imgHeight = node.naturalHeight || node.height;
  let imgMax = Math.max(imgWidth, imgHeight);

  // Extract the image data
  let imageData;

  // The image may already be a data-uri, in which case, save ourselves the
  // trouble of converting via the canvas.drawImage.toDataURL method
  if (node.src.startsWith("data:")) {
    imageData = node.src;
  } else {
    // Create a canvas to copy the rawNode into and get the imageData from
    let canvas = node.ownerDocument.createElementNS(XHTML_NS, "canvas");
    canvas.width = imgWidth * resizeRatio;
    canvas.height = imgHeight * resizeRatio;
    let ctx = canvas.getContext("2d");

    // Copy the rawNode image or canvas in the new canvas and extract data
    ctx.drawImage(node, 0, 0, canvas.width, canvas.height);
    imageData = canvas.toDataURL("image/png");
  }

  return {
    data: imageData,
    size: {
      naturalWidth: imgWidth,
      naturalHeight: imgHeight,
      resized: resizeRatio !== 1
    }
  }
})

// Exports from this module
exports.PixelPerfectStore = PixelPerfectStore;
<|endoftext|>


<commit_before>var util = require('util');
var path = require('path');
var http = require('http');
var url = require('url');
var iconv = require('iconv-lite');
var isUtf8 = require('../util/is-utf8');
var ca = require('../https/ca');
var Storage = require('../rules/storage');
var getServer = require('hagent').create(null, 40500);

var QUERY_RE = /\?.*$/;
var REQ_ID_RE = /^\d{13,15}-\d{1,5}$/;
var reqOpts, pending;
var reqCallbacks = {};
var resCallbacks = {};
var MAX_LENGTH = 100;
var TIMEOUT = 300;
var timer, pluginOpts, storage;
var noop = function() {};
var ctx;

var getValue = function(req, name) {
  const value = req.headers[name] || '';
  try {
    return value ? decodeURIComponent(value) : '';
  } catch(e) {}
  return String(value);
};
var setContenxt = function(req) {
  if (ctx) {
    ctx.request = req;
    req.ctx = ctx;
  }
  req.localStorage = storage;
  req.Storage = Storage;
  req.clientIp = getValue(req, pluginOpts.CLIENT_IP_HEADER) || '127.0.0.1';
};
var initReq = function(req, res) {
  req.on('error', noop);
  res.on('error', noop);
  var oReq = req.originalReq = {};
  var oRes = req.originalRes = {};
  setContenxt(req);
  oReq.clientIp = req.clientIp;
  oReq.id = getValue(req, pluginOpts.REQ_ID_HEADER);
  oReq.ruleValue = getValue(req, pluginOpts.RULE_VALUE_HEADER);
  oReq.url = oReq.fullUrl = getValue(req, pluginOpts.FULL_URL_HEADER);
  oReq.realUrl = getValue(req, pluginOpts.REAL_URL_HEADER);
  oReq.method = getValue(req, pluginOpts.METHOD_HEADER) || 'GET';
  oReq.clientPort = getValue(req, pluginOpts.CLIENT_PORT_HEAD);
  oReq.globalValue = getValue(req, pluginOpts.GLOBAL_VALUE_HEAD);
  oReq.proxyValue = getValue(req, pluginOpts.PROXY_VALUE_HEADER);
  oReq.pacValue = getValue(req, pluginOpts.PAC_VALUE_HEADER);
  oRes.serverIp = getValue(req, pluginOpts.HOST_IP_HEADER);
  oRes.statusCode = getValue(req, pluginOpts.STATUS_CODE_HEADER);
};
var toBuffer = function(base64) {
  if (base64) {
    try {
      return new Buffer(base64, 'base64');
    } catch(e) {}
  }
};
var getBuffer = function(item) {
  return toBuffer(item.base64);
};
var getText = function(item) {
  var body = toBuffer(item.base64) || '';
  if (body && !isUtf8(body)) {
    try {
      body = iconv.encode(body, 'GB18030');
    } catch(e) {}
  }
  return body + '';
};

var defineProps = function(obj) {
  if (!obj) {
    return;
  }
  if (Object.defineProperties) {
    Object.defineProperties(obj, {
      body: {
        get: function() {
          return getText(obj);
        }
      },
      buffer: {
        get: function() {
          return getBuffer(obj);
        }
      }
    });
  } else {
    obj.body = getText(obj);
    obj.buffer = getBuffer(obj);
  }
};

var execCallback = function(id, cbs, item) {
  var cb = cbs[id];
  if (cb && (cbs === reqCallbacks || !item || item.endTime)) {
    item = item || '';
    defineProps(item.req);
    defineProps(item.res);
    cb(item);
    delete cbs[id];
  }
};

var retryRequest = function(time) {
  if (!timer) {
    timer = setTimeout(request, time || TIMEOUT);
  }
};

var request = function() {
  clearTimeout(timer);
  timer = null;
  if (pending) {
    return;
  }
  var reqList = Object.keys(reqCallbacks);
  var resList = Object.keys(resCallbacks);
  if (!reqList.length && !resList.length) {
    return;
  }
  pending = true;
  var execCb = function(err, result) {
    pending = false;
    if (err || !result) {
      return retryRequest();
    }
    Object.keys(result).forEach(function(id) {
      var item = result[id];
      execCallback(id, reqCallbacks, item);
      execCallback(id, resCallbacks, item);
    });
    retryRequest(20);
  };
  var _reqList = reqList.slice(0, MAX_LENGTH);
  var _resList = resList.slice(0, MAX_LENGTH);
  var query = '?reqList=' + JSON.stringify(_reqList) + '&resList=' + JSON.stringify(_resList);

  reqOpts.path = reqOpts.path.replace(QUERY_RE, query);
  var client = http.get(reqOpts, function(res) {
    if (res.statusCode !== 200) {
      return execCb(true);
    }
    var body;
    res.on('data', function(data) {
      body = body ? Buffer.concat([body, data]) : data;
    });
    res.on('end', function() {
      if (body) {
        try {
          return execCb(null, JSON.parse(String(body)));
        } catch(e) {}
      }
      execCb(true);
    });
  });
  client.on('error', execCb);
  client.setTimeout(6000, function() {
    client.abort();
  });
};

var getSession = function(reqId, cb, isReq) {
  if (!REQ_ID_RE.test(reqId) || typeof cb !== 'function') {
    return;
  }
  if (isReq) {
    reqCallbacks[reqId] = cb;
  } else {
    resCallbacks[reqId] = cb;
  }
  retryRequest(20);
};

var handleStatsResponse = function(req, res) {
  var reqId = req.headers[pluginOpts.REQ_ID_HEADER];
  initReq(req, res);
  req.getReqSession = function(cb) {
    return getSession(reqId, cb, true);
  };
  req.getSession = function(cb) {
    return getSession(reqId, cb);
  };
  res.end();
};
var addSessionApi = function(req, res) {
  var reqId = req.headers[pluginOpts.REQ_ID_HEADER];
  initReq(req, res);
  req.unsafe_getReqSession = function(cb) {
    return getSession(reqId, cb, true);
  };
  req.unsafe_getSession = function(cb) {
    return getSession(reqId, cb);
  };
};

var loadModule = function(filepath) {
  try {
    return require(filepath);
  } catch (e) {}
};

module.exports = function(options, callback) {
  options.getRootCAFile = ca.getRootCAFile;
  options.createCertificate = ca.createCertificate;
  options.Storage = Storage;
  storage = new Storage(path.join(options.config.baseDir, '.plugins', options.name));
  options.storage = options.localStorage = storage;
  var config = options.config;
  pluginOpts = options;
  reqOpts = url.parse('http://127.0.0.1:' + config.uiport + '/cgi-bin/get-item?');
  reqOpts.headers = { 'x-whistle-auth-key': config.authKey };
  delete config.authKey;
  if (options.debugMode) {
    var cacheLogs = [];
    /*eslint no-console: "off"*/
    console.log = function() {
      var msg = util.format.apply(this, arguments);
      if (cacheLogs) {
        cacheLogs.push(msg);
      } else {
        process.sendData({
          type: 'console.log',
          message: msg
        });
      }
    };
    process.on('data', function(data) {
      if (cacheLogs && data && data.type == 'console.log' && data.status == 'ready') {
        var _cacheLogs = cacheLogs;
        cacheLogs = null;
        _cacheLogs.forEach(function(msg) {
          process.sendData({
            type: 'console.log',
            message: msg
          });
        });
      }
    });
  }

  var port, statsPort, resStatsPort, uiPort, rulesPort, resRulesPort, tunnelRulesPort, tunnelPort;
  var count = 0;
  var callbackHandler = function() {
    if (--count <= 0) {
      callback(null, {
        port: port,
        statsPort: statsPort,
        resStatsPort: resStatsPort,
        uiPort: uiPort,
        rulesPort: rulesPort,
        resRulesPort: resRulesPort,
        tunnelRulesPort: tunnelRulesPort,
        tunnelPort: tunnelPort
      });
    }
  };

  try {
    require.resolve(options.value);
  } catch(e) {
    return callbackHandler();
  }
  var initial = loadModule(path.join(options.value, 'initial.js'));
  if (typeof initial === 'function') {
    ctx = initial(options);
  }
  var execPlugin = require(options.value);
  var startServer = execPlugin.pluginServer || execPlugin.server || execPlugin;
  if (typeof startServer == 'function') {
    ++count;
    getServer(function(server, _port) {
      server.on('request', addSessionApi);
      startServer(server, options);
      port = _port;
      callbackHandler();
    });
  }

  var startStatsServer = execPlugin.statsServer || execPlugin.reqStatsServer;
  if (typeof startStatsServer == 'function') {
    ++count;
    getServer(function(server, _port) {
      server.on('request', handleStatsResponse);
      startStatsServer(server, options);
      statsPort = _port;
      callbackHandler();
    });
  }

  var startResStatsServer = execPlugin.resStatsServer;
  if (typeof startResStatsServer == 'function') {
    ++count;
    getServer(function(server, _port) {
      server.on('request', handleStatsResponse);
      startResStatsServer(server, options);
      resStatsPort = _port;
      callbackHandler();
    });
  }

  var startUIServer = execPlugin.uiServer || execPlugin.innerServer || execPlugin.internalServer;
  if (typeof startUIServer == 'function') {
    ++count;
    getServer(function(server, _port) {
      server.on('request', setContenxt);
      startUIServer(server, options);
      uiPort = _port;
      callbackHandler();
    });
  }

  var startRulesServer = execPlugin.pluginRulesServer || execPlugin.rulesServer || execPlugin.reqRulesServer;
  if (typeof startRulesServer == 'function') {
    ++count;
    getServer(function(server, _port) {
      server.on('request', addSessionApi);
      startRulesServer(server, options);
      rulesPort = _port;
      callbackHandler();
    });
  }

  var startResRulesServer = execPlugin.resRulesServer;
  if (typeof startResRulesServer == 'function') {
    ++count;
    getServer(function(server, _port) {
      server.on('request', function(req, res) {
        var reqId = req.headers[pluginOpts.REQ_ID_HEADER];
        initReq(req, res);
        req.getReqSession = function(cb) {
          return getSession(reqId, cb, true);
        };
        req.unsafe_getSession = function(cb) {
          return getSession(reqId, cb);
        };
      });
      startResRulesServer(server, options);
      resRulesPort = _port;
      callbackHandler();
    });
  }

  var startTunnelRulesServer = execPlugin.pluginRulesServer || execPlugin.tunnelRulesServer;
  if (typeof startTunnelRulesServer == 'function') {
    ++count;
    getServer(function(server, _port) {
      server.on('request', addSessionApi);
      startTunnelRulesServer(server, options);
      tunnelRulesPort = _port;
      callbackHandler();
    });
  }

  var startTunnelServer = execPlugin.pluginServer || execPlugin.tunnelServer || execPlugin.connectServer;
  if (typeof startTunnelServer == 'function') {
    ++count;
    getServer(function(server, _port) {
      server.on('request', addSessionApi);
      startTunnelServer(server, options);
      tunnelPort = _port;
      callbackHandler();
    });
  }

  if (!count) {
    callbackHandler();
  }
};

<commit_msg>refactor: getSession<commit_after>var util = require('util');
var path = require('path');
var http = require('http');
var url = require('url');
var iconv = require('iconv-lite');
var isUtf8 = require('../util/is-utf8');
var ca = require('../https/ca');
var Storage = require('../rules/storage');
var getServer = require('hagent').create(null, 40500);

var QUERY_RE = /\?.*$/;
var REQ_ID_RE = /^\d{13,15}-\d{1,5}$/;
var reqOpts, pending;
var reqCallbacks = {};
var resCallbacks = {};
var MAX_LENGTH = 100;
var TIMEOUT = 300;
var timer, pluginOpts, storage;
var noop = function() {};
/* eslint-disable no-undef */
var SESSION_KEY = typeof Symbol === 'undefined' ? '$symbol_' + Date.now() : Symbol();
/* eslint-enable no-undef */
var ctx;

var getValue = function(req, name) {
  const value = req.headers[name] || '';
  try {
    return value ? decodeURIComponent(value) : '';
  } catch(e) {}
  return String(value);
};
var setContenxt = function(req) {
  if (ctx) {
    ctx.request = req;
    req.ctx = ctx;
  }
  req.localStorage = storage;
  req.Storage = Storage;
  req.clientIp = getValue(req, pluginOpts.CLIENT_IP_HEADER) || '127.0.0.1';
};
var initReq = function(req, res) {
  req.on('error', noop);
  res.on('error', noop);
  var oReq = req.originalReq = {};
  var oRes = req.originalRes = {};
  setContenxt(req);
  oReq.clientIp = req.clientIp;
  oReq.id = getValue(req, pluginOpts.REQ_ID_HEADER);
  oReq.ruleValue = getValue(req, pluginOpts.RULE_VALUE_HEADER);
  oReq.url = oReq.fullUrl = getValue(req, pluginOpts.FULL_URL_HEADER);
  oReq.realUrl = getValue(req, pluginOpts.REAL_URL_HEADER);
  oReq.method = getValue(req, pluginOpts.METHOD_HEADER) || 'GET';
  oReq.clientPort = getValue(req, pluginOpts.CLIENT_PORT_HEAD);
  oReq.globalValue = getValue(req, pluginOpts.GLOBAL_VALUE_HEAD);
  oReq.proxyValue = getValue(req, pluginOpts.PROXY_VALUE_HEADER);
  oReq.pacValue = getValue(req, pluginOpts.PAC_VALUE_HEADER);
  oRes.serverIp = getValue(req, pluginOpts.HOST_IP_HEADER);
  oRes.statusCode = getValue(req, pluginOpts.STATUS_CODE_HEADER);
};
var toBuffer = function(base64) {
  if (base64) {
    try {
      return new Buffer(base64, 'base64');
    } catch(e) {}
  }
};
var getBuffer = function(item) {
  return toBuffer(item.base64);
};
var getText = function(item) {
  var body = toBuffer(item.base64) || '';
  if (body && !isUtf8(body)) {
    try {
      body = iconv.encode(body, 'GB18030');
    } catch(e) {}
  }
  return body + '';
};

var defineProps = function(obj) {
  if (!obj) {
    return;
  }
  if (Object.defineProperties) {
    Object.defineProperties(obj, {
      body: {
        get: function() {
          return getText(obj);
        }
      },
      buffer: {
        get: function() {
          return getBuffer(obj);
        }
      }
    });
  } else {
    obj.body = getText(obj);
    obj.buffer = getBuffer(obj);
  }
};

var execCallback = function(id, cbs, item) {
  var cbList = cbs[id];
  if (cbList && (cbs === reqCallbacks || !item || item.endTime)) {
    item = item || '';
    defineProps(item.req);
    defineProps(item.res);
    cbList.forEach(function(cb) {
      cb(item);
    });
    delete cbs[id];
  }
};

var retryRequest = function(time) {
  if (!timer) {
    timer = setTimeout(request, time || TIMEOUT);
  }
};

var request = function() {
  clearTimeout(timer);
  timer = null;
  if (pending) {
    return;
  }
  var reqList = Object.keys(reqCallbacks);
  var resList = Object.keys(resCallbacks);
  if (!reqList.length && !resList.length) {
    return;
  }
  pending = true;
  var execCb = function(err, result) {
    pending = false;
    if (err || !result) {
      return retryRequest();
    }
    Object.keys(result).forEach(function(id) {
      var item = result[id];
      execCallback(id, reqCallbacks, item);
      execCallback(id, resCallbacks, item);
    });
    retryRequest(20);
  };
  var _reqList = reqList.slice(0, MAX_LENGTH);
  var _resList = resList.slice(0, MAX_LENGTH);
  var query = '?reqList=' + JSON.stringify(_reqList) + '&resList=' + JSON.stringify(_resList);

  reqOpts.path = reqOpts.path.replace(QUERY_RE, query);
  var client = http.get(reqOpts, function(res) {
    if (res.statusCode !== 200) {
      return execCb(true);
    }
    var body;
    res.on('data', function(data) {
      body = body ? Buffer.concat([body, data]) : data;
    });
    res.on('end', function() {
      if (body) {
        try {
          return execCb(null, JSON.parse(String(body)));
        } catch(e) {}
      }
      execCb(true);
    });
  });
  client.on('error', execCb);
  client.setTimeout(6000, function() {
    client.abort();
  });
};

var getSession = function(reqId, req, cb, isReq) {
  if (!REQ_ID_RE.test(reqId) || typeof cb !== 'function') {
    return;
  }
  var session = req[SESSION_KEY];
  if (session != null) {
    if (isReq) {
      return cb(session);
    }
    if (!session || session.endTime) {
      return cb(session);
    }
  }
  var cbList = (isReq ? reqCallbacks[reqId] : resCallbacks[reqId]);
  if (cbList) {
    cbList.push(cb);
  } else {
    cbList = [function(s) {
      req[SESSION_KEY] = s;
      cb(s);
    }];
  }
  if (isReq) {
    reqCallbacks[reqId] = cbList;
  } else {
    resCallbacks[reqId] = cbList;
  }
  retryRequest(20);
};

var handleStatsResponse = function(req, res) {
  var reqId = req.headers[pluginOpts.REQ_ID_HEADER];
  initReq(req, res);
  req.getReqSession = function(cb) {
    return getSession(reqId, req, cb, true);
  };
  req.getSession = function(cb) {
    return getSession(reqId, req, cb);
  };
  res.end();
};
var addSessionApi = function(req, res) {
  var reqId = req.headers[pluginOpts.REQ_ID_HEADER];
  initReq(req, res);
  req.unsafe_getReqSession = function(cb) {
    return getSession(reqId, req, cb, true);
  };
  req.unsafe_getSession = function(cb) {
    return getSession(reqId, req, cb);
  };
};

var loadModule = function(filepath) {
  try {
    return require(filepath);
  } catch (e) {}
};

module.exports = function(options, callback) {
  options.getRootCAFile = ca.getRootCAFile;
  options.createCertificate = ca.createCertificate;
  options.Storage = Storage;
  storage = new Storage(path.join(options.config.baseDir, '.plugins', options.name));
  options.storage = options.localStorage = storage;
  var config = options.config;
  pluginOpts = options;
  reqOpts = url.parse('http://127.0.0.1:' + config.uiport + '/cgi-bin/get-item?');
  reqOpts.headers = { 'x-whistle-auth-key': config.authKey };
  delete config.authKey;
  if (options.debugMode) {
    var cacheLogs = [];
    /*eslint no-console: "off"*/
    console.log = function() {
      var msg = util.format.apply(this, arguments);
      if (cacheLogs) {
        cacheLogs.push(msg);
      } else {
        process.sendData({
          type: 'console.log',
          message: msg
        });
      }
    };
    process.on('data', function(data) {
      if (cacheLogs && data && data.type == 'console.log' && data.status == 'ready') {
        var _cacheLogs = cacheLogs;
        cacheLogs = null;
        _cacheLogs.forEach(function(msg) {
          process.sendData({
            type: 'console.log',
            message: msg
          });
        });
      }
    });
  }

  var port, statsPort, resStatsPort, uiPort, rulesPort, resRulesPort, tunnelRulesPort, tunnelPort;
  var count = 0;
  var callbackHandler = function() {
    if (--count <= 0) {
      callback(null, {
        port: port,
        statsPort: statsPort,
        resStatsPort: resStatsPort,
        uiPort: uiPort,
        rulesPort: rulesPort,
        resRulesPort: resRulesPort,
        tunnelRulesPort: tunnelRulesPort,
        tunnelPort: tunnelPort
      });
    }
  };

  try {
    require.resolve(options.value);
  } catch(e) {
    return callbackHandler();
  }
  var initial = loadModule(path.join(options.value, 'initial.js'));
  if (typeof initial === 'function') {
    ctx = initial(options);
  }
  var execPlugin = require(options.value);
  var startServer = execPlugin.pluginServer || execPlugin.server || execPlugin;
  if (typeof startServer == 'function') {
    ++count;
    getServer(function(server, _port) {
      server.on('request', addSessionApi);
      startServer(server, options);
      port = _port;
      callbackHandler();
    });
  }

  var startStatsServer = execPlugin.statsServer || execPlugin.reqStatsServer;
  if (typeof startStatsServer == 'function') {
    ++count;
    getServer(function(server, _port) {
      server.on('request', handleStatsResponse);
      startStatsServer(server, options);
      statsPort = _port;
      callbackHandler();
    });
  }

  var startResStatsServer = execPlugin.resStatsServer;
  if (typeof startResStatsServer == 'function') {
    ++count;
    getServer(function(server, _port) {
      server.on('request', handleStatsResponse);
      startResStatsServer(server, options);
      resStatsPort = _port;
      callbackHandler();
    });
  }

  var startUIServer = execPlugin.uiServer || execPlugin.innerServer || execPlugin.internalServer;
  if (typeof startUIServer == 'function') {
    ++count;
    getServer(function(server, _port) {
      server.on('request', setContenxt);
      startUIServer(server, options);
      uiPort = _port;
      callbackHandler();
    });
  }

  var startRulesServer = execPlugin.pluginRulesServer || execPlugin.rulesServer || execPlugin.reqRulesServer;
  if (typeof startRulesServer == 'function') {
    ++count;
    getServer(function(server, _port) {
      server.on('request', addSessionApi);
      startRulesServer(server, options);
      rulesPort = _port;
      callbackHandler();
    });
  }

  var startResRulesServer = execPlugin.resRulesServer;
  if (typeof startResRulesServer == 'function') {
    ++count;
    getServer(function(server, _port) {
      server.on('request', function(req, res) {
        var reqId = req.headers[pluginOpts.REQ_ID_HEADER];
        initReq(req, res);
        req.getReqSession = function(cb) {
          return getSession(reqId, req, cb, true);
        };
        req.unsafe_getSession = function(cb) {
          return getSession(reqId, req, cb);
        };
      });
      startResRulesServer(server, options);
      resRulesPort = _port;
      callbackHandler();
    });
  }

  var startTunnelRulesServer = execPlugin.pluginRulesServer || execPlugin.tunnelRulesServer;
  if (typeof startTunnelRulesServer == 'function') {
    ++count;
    getServer(function(server, _port) {
      server.on('request', addSessionApi);
      startTunnelRulesServer(server, options);
      tunnelRulesPort = _port;
      callbackHandler();
    });
  }

  var startTunnelServer = execPlugin.pluginServer || execPlugin.tunnelServer || execPlugin.connectServer;
  if (typeof startTunnelServer == 'function') {
    ++count;
    getServer(function(server, _port) {
      server.on('request', addSessionApi);
      startTunnelServer(server, options);
      tunnelPort = _port;
      callbackHandler();
    });
  }

  if (!count) {
    callbackHandler();
  }
};

<|endoftext|>


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

var assign = require('../assign');
var set = require('../set');
var getOwnPropertyNames = require('../get-own-property-names');

function toError(query, params, reason) {
  var type = query + 'Failed';

  var defaults = {
    type: type,
    reason: reason,
    at: Date.now()
  };

  return assign(defaults, params);
}

function resolve(value) {
  return value;
}

function reject(query, params, reason) {
  throw toError(query, params, reason);
}

module.exports = function (projection) {
  function Projection(state, version) {
    this._state = state;
    this._version = version;
  }

  Projection.getDefault = function () {
    return new Projection(projection.defaultState, { });
  };

  Projection.prototype.toSnapshot = function () {
    return {
      revision: projection.revision,
      version: this._version,
      state: this._state
    };
  };

  Projection.prototype.fromSnapshot = function (snapshot) {
    return this
      .withVersion(snapshot.version)
      .withState(snapshot.state);
  };

  Projection.prototype.withState = function (state) {
    return new Projection(
      state,
      this._version
    );
  };

  Projection.prototype.withVersion = function (version) {
    return new Projection(
      this._state,
      version
    );
  };

  Projection.prototype.withVersionFor = function (name, version) {
    var versions = set(this._version, name, version);
    return this.withVersion(versions);
  };

  Projection.prototype.accept = function (name, event) {
    var accept = projection.events[event.type];
    var version = this._version[name] || 0;
    var state = this._state;

    if (typeof accept === 'function') {
      state = accept(state, event);
    }

    return this.withState(state).withVersionFor(name, version + 1);
  };

  Projection.prototype.process = function (type, params) {
    var process = projection.queries[type];

    if (typeof process === 'function') {
      return process(
        this._state,
        params,
        resolve,
        reject.bind(this, type, params)
      );
    }

    return this;
  };

  getOwnPropertyNames(projection.queries)
    .forEach(function (query) {
      Projection.prototype[query] = function (params) {
        return this.process(query, params);
      };
    });

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

var assign = require('../assign');
var set = require('../set');
var getOwnPropertyNames = require('../get-own-property-names');

function toError(query, params, reason) {
  var type = query + 'Failed';

  var defaults = {
    type: type,
    reason: reason,
    at: Date.now()
  };

  return assign(defaults, params);
}

function resolve(value) {
  return value;
}

function reject(query, params, reason) {
  throw toError(query, params, reason);
}

module.exports = function (projection) {
  function Projection(state, version) {
    this._state = state;
    this._version = version;
  }

  Projection.getDefault = function () {
    return new Projection(projection.defaultState, { });
  };

  Projection.prototype.toSnapshot = function () {
    return {
      revision: projection.revision,
      version: this._version,
      state: this._state
    };
  };

  Projection.prototype.fromSnapshot = function (snapshot) {
    return this
      .withVersion(snapshot.version)
      .withState(snapshot.state);
  };

  Projection.prototype.withState = function (state) {
    return new Projection(
      state,
      this._version
    );
  };

  Projection.prototype.withVersion = function (version) {
    return new Projection(
      this._state,
      version
    );
  };

  Projection.prototype.withVersionFor = function (name, version) {
    var versions = set(this._version, name, version);
    return this.withVersion(versions);
  };

  Projection.prototype.accept = function (name, event) {
    var accept = projection.events[event.type];
    var version = event.at
    var state = this._state;

    if (typeof accept === 'function') {
      state = accept(state, event);
    }

    return this.withState(state).withVersionFor(name, version);
  };

  Projection.prototype.process = function (type, params) {
    var process = projection.queries[type];

    if (typeof process === 'function') {
      return process(
        this._state,
        params,
        resolve,
        reject.bind(this, type, params)
      );
    }

    return this;
  };

  getOwnPropertyNames(projection.queries)
    .forEach(function (query) {
      Projection.prototype[query] = function (params) {
        return this.process(query, params);
      };
    });

  return Projection;
};
<|endoftext|>


<commit_before>import update from 'immutability-helper'
import moment from 'moment'

import {constructNewCall} from '../util/call-taker'
import {FETCH_STATUS} from '../util/constants'

function createCallTakerReducer (config) {
  const calltakerConfig = config.modules.find(m => m.id === 'call')
  const initialState = {
    activeCall: null,
    callHistory: {
      calls: {
        status: FETCH_STATUS.UNFETCHED,
        data: []
      },
      visible: calltakerConfig &&
        calltakerConfig.options &&
        calltakerConfig.options.showCallHistoryOnLoad
    },
    fieldTrip: {
      activeId: null,
      filter: {
        tab: 'new'
      },
      groupSize: null,
      requests: {
        status: FETCH_STATUS.UNFETCHED,
        data: []
      },
      visible: false
    },
    session: null
  }
  return (state = initialState, action) => {
    switch (action.type) {
      case 'BEGIN_CALL': {
        const newCall = constructNewCall()
        // Initialize new call and show call history window.
        return update(state, {
          activeCall: { $set: newCall },
          callHistory: { visible: { $set: true } }
        })
      }
      case 'REQUESTING_CALLS': {
        return update(state, {
          callHistory: { calls: { status: { $set: FETCH_STATUS.FETCHING } } }
        })
      }
      case 'RECEIVED_CALLS': {
        const data = action.payload.calls
        const calls = {
          status: FETCH_STATUS.FETCHED,
          data: data.sort((a, b) => moment(b.endTime) - moment(a.endTime))
        }
        return update(state, {
          callHistory: { calls: { $set: calls } }
        })
      }
      case 'REQUESTING_FIELD_TRIPS': {
        return update(state, {
          fieldTrip: { requests: { status: { $set: FETCH_STATUS.FETCHING } } }
        })
      }
      case 'RECEIVED_FIELD_TRIPS': {
        const data = action.payload.fieldTrips
        const requests = {
          status: FETCH_STATUS.FETCHED,
          data: data.sort((a, b) => moment(b.endTime) - moment(a.endTime))
        }
        return update(state, {
          fieldTrip: { requests: { $set: requests } }
        })
      }
      case 'SET_FIELD_TRIP_FILTER': {
        return update(state, {
          fieldTrip: { filter: { $merge: action.payload } }
        })
      }
      case 'SET_GROUP_SIZE': {
        return update(state, {
          fieldTrip: { groupSize: { $set: action.payload } }
        })
      }
      case 'SET_ACTIVE_FIELD_TRIP': {
        return update(state, {
          fieldTrip: {
            activeId: { $set: action.payload },
            groupSize: { $set: null }
          }
        })
      }
      case 'RECEIVED_FIELD_TRIP_DETAILS': {
        const {fieldTrip} = action.payload
        const index = state.fieldTrip.requests.data.findIndex(req => req.id === fieldTrip.id)
        return update(state, {
          fieldTrip: { requests: { data: { [index]: { $set: fieldTrip } } } }
        })
      }
      case 'RECEIVED_QUERIES': {
        const {callId, queries} = action.payload
        const {data} = state.callHistory.calls
        const index = data.findIndex(call => call.id === callId)
        const call = {...data[index], queries}
        return update(state, {
          callHistory: { calls: { data: { [index]: { $set: call } } } }
        })
      }
      case 'ROUTING_RESPONSE': {
        const {searchId} = action.payload
        if (state.activeCall) {
          // If call is in progress, record search ID when a routing response is
          // fulfilled.
          // TODO: How should we handle routing errors.
          return update(state, {
            activeCall: { searches: { $push: [searchId] } }
          })
        } else if (calltakerConfig && !state.fieldTrip.visible && searchId.indexOf('_CALL') === -1) {
          // If
          // - the call module is enabled, and
          // - a call is not in progress, and
          // - the field trip window is not visible, and
          // - regardless of whether the call history is visible or not,
          // construct new call and add search. Excepting the case where the
          // searchId contains _CALL, which indicates that a user is viewing a
          // past call record (and not intending to start a new call session).
          const newCall = constructNewCall()
          newCall.searches.push(searchId)
          // Initialize new call and show call history window.
          return update(state, { activeCall: { $set: newCall } })
        }
        // Otherwise, ignore.
        return state
      }
      case 'STORE_SESSION': {
        const {session} = action.payload
        if (!session || !session.username) {
          const sessionId = session ? session.sessionId : 'N/A'
          // Session is invalid if username is missing.
          window.alert(`Session ID ${sessionId} is invalid!`)
          // TODO: Should we return to URL_ROOT at this point?
          return update(state, { session: { $set: null } })
        }
        return update(state, { session: { $set: session } })
      }
      case 'TOGGLE_CALL_HISTORY': {
        return update(state, {
          callHistory: { visible: { $set: !state.callHistory.visible } }
        })
      }
      case 'TOGGLE_FIELD_TRIPS': {
        return update(state, {
          fieldTrip: {
            activeId: { $set: null },
            visible: { $set: !state.fieldTrip.visible }
          }
        })
      }
      case 'END_CALL': {
        return update(state, {
          activeCall: { $set: null }
        })
      }
      default:
        return state
    }
  }
}

export default createCallTakerReducer
<commit_msg>Update lib/reducers/call-taker.js<commit_after>import update from 'immutability-helper'
import moment from 'moment'

import {constructNewCall} from '../util/call-taker'
import {FETCH_STATUS} from '../util/constants'

function createCallTakerReducer (config) {
  const calltakerConfig = config.modules.find(m => m.id === 'call')
  const initialState = {
    activeCall: null,
    callHistory: {
      calls: {
        status: FETCH_STATUS.UNFETCHED,
        data: []
      },
      visible: calltakerConfig?.options?.showCallHistoryOnLoad
    },
    fieldTrip: {
      activeId: null,
      filter: {
        tab: 'new'
      },
      groupSize: null,
      requests: {
        status: FETCH_STATUS.UNFETCHED,
        data: []
      },
      visible: false
    },
    session: null
  }
  return (state = initialState, action) => {
    switch (action.type) {
      case 'BEGIN_CALL': {
        const newCall = constructNewCall()
        // Initialize new call and show call history window.
        return update(state, {
          activeCall: { $set: newCall },
          callHistory: { visible: { $set: true } }
        })
      }
      case 'REQUESTING_CALLS': {
        return update(state, {
          callHistory: { calls: { status: { $set: FETCH_STATUS.FETCHING } } }
        })
      }
      case 'RECEIVED_CALLS': {
        const data = action.payload.calls
        const calls = {
          status: FETCH_STATUS.FETCHED,
          data: data.sort((a, b) => moment(b.endTime) - moment(a.endTime))
        }
        return update(state, {
          callHistory: { calls: { $set: calls } }
        })
      }
      case 'REQUESTING_FIELD_TRIPS': {
        return update(state, {
          fieldTrip: { requests: { status: { $set: FETCH_STATUS.FETCHING } } }
        })
      }
      case 'RECEIVED_FIELD_TRIPS': {
        const data = action.payload.fieldTrips
        const requests = {
          status: FETCH_STATUS.FETCHED,
          data: data.sort((a, b) => moment(b.endTime) - moment(a.endTime))
        }
        return update(state, {
          fieldTrip: { requests: { $set: requests } }
        })
      }
      case 'SET_FIELD_TRIP_FILTER': {
        return update(state, {
          fieldTrip: { filter: { $merge: action.payload } }
        })
      }
      case 'SET_GROUP_SIZE': {
        return update(state, {
          fieldTrip: { groupSize: { $set: action.payload } }
        })
      }
      case 'SET_ACTIVE_FIELD_TRIP': {
        return update(state, {
          fieldTrip: {
            activeId: { $set: action.payload },
            groupSize: { $set: null }
          }
        })
      }
      case 'RECEIVED_FIELD_TRIP_DETAILS': {
        const {fieldTrip} = action.payload
        const index = state.fieldTrip.requests.data.findIndex(req => req.id === fieldTrip.id)
        return update(state, {
          fieldTrip: { requests: { data: { [index]: { $set: fieldTrip } } } }
        })
      }
      case 'RECEIVED_QUERIES': {
        const {callId, queries} = action.payload
        const {data} = state.callHistory.calls
        const index = data.findIndex(call => call.id === callId)
        const call = {...data[index], queries}
        return update(state, {
          callHistory: { calls: { data: { [index]: { $set: call } } } }
        })
      }
      case 'ROUTING_RESPONSE': {
        const {searchId} = action.payload
        if (state.activeCall) {
          // If call is in progress, record search ID when a routing response is
          // fulfilled.
          // TODO: How should we handle routing errors.
          return update(state, {
            activeCall: { searches: { $push: [searchId] } }
          })
        } else if (calltakerConfig && !state.fieldTrip.visible && searchId.indexOf('_CALL') === -1) {
          // If
          // - the call module is enabled, and
          // - a call is not in progress, and
          // - the field trip window is not visible, and
          // - regardless of whether the call history is visible or not,
          // construct new call and add search. Excepting the case where the
          // searchId contains _CALL, which indicates that a user is viewing a
          // past call record (and not intending to start a new call session).
          const newCall = constructNewCall()
          newCall.searches.push(searchId)
          // Initialize new call and show call history window.
          return update(state, { activeCall: { $set: newCall } })
        }
        // Otherwise, ignore.
        return state
      }
      case 'STORE_SESSION': {
        const {session} = action.payload
        if (!session || !session.username) {
          const sessionId = session ? session.sessionId : 'N/A'
          // Session is invalid if username is missing.
          window.alert(`Session ID ${sessionId} is invalid!`)
          // TODO: Should we return to URL_ROOT at this point?
          return update(state, { session: { $set: null } })
        }
        return update(state, { session: { $set: session } })
      }
      case 'TOGGLE_CALL_HISTORY': {
        return update(state, {
          callHistory: { visible: { $set: !state.callHistory.visible } }
        })
      }
      case 'TOGGLE_FIELD_TRIPS': {
        return update(state, {
          fieldTrip: {
            activeId: { $set: null },
            visible: { $set: !state.fieldTrip.visible }
          }
        })
      }
      case 'END_CALL': {
        return update(state, {
          activeCall: { $set: null }
        })
      }
      default:
        return state
    }
  }
}

export default createCallTakerReducer
<|endoftext|>


<commit_before>'use strict';
const _ = require('lodash');
const createError = require('../error/create-error');

const _formatValidationError = (err) => {
  return err.errors.map(error => {
    return _.pick(error, ['type', 'path', 'value']);
  });
};

module.exports = (ERRORS) => (next, err) => {
  if (ERRORS.ValidationError(err))
    return next(createError(400, _formatValidationError(err)));
  else if (err.isCreatedError)
    return next(err);
  else
    return next(createError(500, err));
};
<commit_msg>replaced _.pick in error-handler with object deconstruction<commit_after>'use strict';
const createError = require('../error/create-error');

const _formatValidationError = (err) => {
  return err.errors.map(error => {
    const {type, path, value} = error;
    return {type, path, value};
  });
};

module.exports = (ERRORS) => (next, err) => {
  if (ERRORS.ValidationError(err))
    return next(createError(400, _formatValidationError(err)));
  else if (err.isCreatedError)
    return next(err);
  else
    return next(createError(500, err));
};
<|endoftext|>


<commit_before>'use strict';

const StripeResource = require('../StripeResource');
const stripeMethod = StripeResource.method;

module.exports = StripeResource.extend({
  path: 'customers',

  includeBasic: ['create', 'del', 'list', 'retrieve', 'update'],

  deleteDiscount: stripeMethod({
    method: 'DELETE',
    path: '/{customer}/discount',
  }),

  updateSource: stripeMethod({
    method: 'POST',
    path: '/{customer}/sources/{id}',
  }),

  deleteSource: stripeMethod({
    method: 'DELETE',
    path: '/{customer}/sources/{id}',
  }),

  verifySource: stripeMethod({
    method: 'POST',
    path: '/{customer}/sources/{sourceId}/verify',
  }),

  createSource: stripeMethod({
    method: 'POST',
    path: '/{customer}/sources',
  }),

  listSources: stripeMethod({
    method: 'GET',
    path: '/{customer}/sources',
    methodType: 'list',
  }),

  retrieveSource: stripeMethod({
    method: 'GET',
    path: '/{customer}/sources/{id}',
  }),

  createTaxId: stripeMethod({
    method: 'POST',
    path: '/{customer}/tax_ids',
  }),

  deleteTaxId: stripeMethod({
    method: 'DELETE',
    path: '/{customer}/tax_ids/{id}',
  }),

  listTaxIds: stripeMethod({
    method: 'GET',
    path: '/{customer}/tax_ids',
    methodType: 'list',
  }),

  retrieveTaxId: stripeMethod({
    method: 'GET',
    path: '/{customer}/tax_ids/{id}',
  }),

  createBalanceTransaction: stripeMethod({
    method: 'POST',
    path: '/{customer}/balance_transactions',
  }),

  listBalanceTransactions: stripeMethod({
    method: 'GET',
    path: '/{customer}/balance_transactions',
    methodType: 'list',
  }),

  retrieveBalanceTransaction: stripeMethod({
    method: 'GET',
    path: '/{customer}/balance_transactions/{id}',
  }),

  updateBalanceTransaction: stripeMethod({
    method: 'POST',
    path: '/{customer}/balance_transactions/{id}',
  }),
});
<commit_msg>Reorder customer methods for codegen (#653)<commit_after>'use strict';

const StripeResource = require('../StripeResource');
const stripeMethod = StripeResource.method;

module.exports = StripeResource.extend({
  path: 'customers',

  includeBasic: ['create', 'del', 'list', 'retrieve', 'update'],

  deleteDiscount: stripeMethod({
    method: 'DELETE',
    path: '/{customer}/discount',
  }),

  updateSource: stripeMethod({
    method: 'POST',
    path: '/{customer}/sources/{id}',
  }),

  deleteSource: stripeMethod({
    method: 'DELETE',
    path: '/{customer}/sources/{id}',
  }),

  verifySource: stripeMethod({
    method: 'POST',
    path: '/{customer}/sources/{sourceId}/verify',
  }),

  createBalanceTransaction: stripeMethod({
    method: 'POST',
    path: '/{customer}/balance_transactions',
  }),

  listBalanceTransactions: stripeMethod({
    method: 'GET',
    path: '/{customer}/balance_transactions',
    methodType: 'list',
  }),

  retrieveBalanceTransaction: stripeMethod({
    method: 'GET',
    path: '/{customer}/balance_transactions/{transaction}',
  }),

  updateBalanceTransaction: stripeMethod({
    method: 'POST',
    path: '/{customer}/balance_transactions/{transaction}',
  }),

  createSource: stripeMethod({
    method: 'POST',
    path: '/{customer}/sources',
  }),

  listSources: stripeMethod({
    method: 'GET',
    path: '/{customer}/sources',
    methodType: 'list',
  }),

  retrieveSource: stripeMethod({
    method: 'GET',
    path: '/{customer}/sources/{id}',
  }),

  createTaxId: stripeMethod({
    method: 'POST',
    path: '/{customer}/tax_ids',
  }),

  deleteTaxId: stripeMethod({
    method: 'DELETE',
    path: '/{customer}/tax_ids/{id}',
  }),

  listTaxIds: stripeMethod({
    method: 'GET',
    path: '/{customer}/tax_ids',
    methodType: 'list',
  }),

  retrieveTaxId: stripeMethod({
    method: 'GET',
    path: '/{customer}/tax_ids/{id}',
  }),
});
<|endoftext|>


<commit_before>'use strict';

var /*register = require('./_register'),*/
	login    = require('./_login');

exports.body = function () {
	header({ 'class': 'header-top' },
		div(a({ href: '/' },
				img({ 'src': '/img/logo-2.png' })
				)
			),
		nav(menu({ 'class': 'menu-top' },
				menuitem(a('en')),
				menuitem(a('sw')),
				menuitem(a('link one')),
				menuitem(a('link two')),
				menuitem(a('link tree')),
				menuitem(a({ 'class': 'login', 'onclick': login.show  },
					'Log in'
					))
				)
			)
		/*p(a({ onclick: login.show }, "LOGIN")),
		p(a({ onclick: register.show }, "REGISTER")),
		p(a({ href: '/public-inner/' }, "PUBLIC INNER"))*/
		);
	div({ 'class': 'modal-courtain' });
	insert(login);
	main({ id: 'main' });
};
<commit_msg>Removed comented code<commit_after>'use strict';

var login = require('./_login');

exports.body = function () {
	header({ 'class': 'header-top' },
		div(a({ href: '/' },
				img({ 'src': '/img/logo-2.png' })
				)
			),
		nav(menu({ 'class': 'menu-top' },
				menuitem(a('en')),
				menuitem(a('sw')),
				menuitem(a('link one')),
				menuitem(a('link two')),
				menuitem(a('link tree')),
				menuitem(a({ 'class': 'login', 'onclick': login.show  },
					'Log in'
					))
				)
			)
		/*p(a({ onclick: login.show }, "LOGIN")),
		p(a({ onclick: register.show }, "REGISTER")),
		p(a({ href: '/public-inner/' }, "PUBLIC INNER"))*/
		);
	div({ 'class': 'modal-courtain' });
	insert(login);
	main({ id: 'main' });
};
<|endoftext|>


<commit_before>'use strict';

var db = require('mano').db

  , user = db.User.prototype
  , reqRadio;

exports['step-guide'] = { class: { 'step-active': true } };

require('./_inventory');

exports.step = function () {
	h1("1. Individual registration guide for companies");

	form(
		{ class: 'user-guide' },
		div({ class: 'section-primary' }, h2("Questions"),
			hr(),
			ul({ class: 'form-elements' },
				['businessActivity',
					'isOwner',
					'inventory',
					'surfaceArea', 'members',
					'companyType',
					'isShoppingGallery'],
				function (name) {
					if (name === 'inventory') {
						div({ class: 'dbjs-input-component' },
							label({ for: 'input-' + name }, user.getDescriptor(name).label, ":"),
							div({ class: 'input' },
								input({ control: { id: 'input-' + name }, dbjs: user.getObservable(name) })));
						div({ class: 'user-guide-inventory-button' },
							a({ href: '#inventory' },
								span({ class: 'fa fa-calculator user-guide-inventory-icon' }, "Calculator"),
								span({ class: 'user-guide-inventory-label' }, "Calculate the amount")));
					} else if (name === 'isShoppingGallery') {
						li(div({ class: 'dbjs-input-component' },
							label(
								{ for: 'input-' + name },
								"Is shopping gallery?"
							),
							div({ class: 'input' },
								input({ control: { id: 'input-' + name }, dbjs: user.getObservable(name),
									type: 'checkbox' }))));
					} else {
						li(div({ class: 'dbjs-input-component' },
							label(
								{ for: 'input-' + name },
								user.getDescriptor(name).label,
								":"
							),
							div({ class: 'input' },
								input({ control: { id: 'input-' + name }, dbjs: user.getObservable(name) }))));
					}
				})),
		div(
			{ class: 'section-primary' },
			h2("Registrations"),
			hr(),
			p("Lorem ipsum dolor sit amet, consectetur adipiscing elit." +
				" Etiam vestibulum dui mi, nec ultrices diam ultricies id. " +
				" Etiam vestibulum dui mi, nec ultrices diam ultricies id. "),
			ul(li(label({ class: 'input-aside' },
				input({ dbjs: user._isARequested, type: 'checkbox', control: { class: 'readonly' } }), " ",
				span(user.getDescriptor('isARequested').label))),
				li(label({ class: 'input-aside' },
					input({ dbjs: user._isARequested, type: 'checkbox' }), " ",
					span(user.getDescriptor('isBRequested').label))),
				li(label({ class: 'input-aside' },
					input({ dbjs: user._isARequested, type: 'checkbox' }), " ",
					span(user.getDescriptor('isARequested').label)))),
			div(
				{ class: 'section-primary-wrapper' },
				h2("Optional Registrations"),
				hr(),
				ul(li(label({ class: 'input-aside' },
					input({ dbjs: user._isARequested, type: 'checkbox', control: { class: 'readonly' } }),
					" ",
					span(user.getDescriptor('isARequested').label))),
					li(label({ class: 'input-aside' },
						input({ dbjs: user._isARequested, type: 'checkbox' }), " ",
						span(user.getDescriptor('isBRequested').label))))
			)
		),

		div({ class: 'section-primary' }, h2("Requirements"),
			hr(),
			p("Lorem ipsum dolor sit amet, consectetur adipiscing elit." +
				" Etiam vestibulum dui mi, nec ultrices diam ultricies id. "),
			ul({ class: 'user-guide-requirements-list' },
				li("Lorem ipsum dolor sit amet, consectetur adipiscing elit. " +
					" Lorem ipsum dolor sit amet, consectetur adipiscing elit."),
				li("Lorem ipsum dolor sit amet, consectetur adipiscing elit. Lorem ipsum " +
					"dolor sit amet, consectetur adipiscing elit. Lorem ipsum dolor sit amet," +
					" consectetur adipiscing elit."),
				li("Lorem ipsum dolor sit amet, consectetur adipiscing elit."),
				li("Lorem ipsum dolor sit amet, consectetur adipiscing elit."),
				li("Lorem ipsum dolor sit amet, consectetur adipiscing elit."),
				li("Lorem ipsum dolor sit amet: ",
					ul(li(reqRadio = input({ dbjs: user._isType, type: 'radio',
						renderOption: function (labelTxt) {
							var data = {};
							data.dom = li(label({ class: 'input-aside' },
								span(data.input = input()),
								span(labelTxt)));
							return data;
						}  }), " "))),
				li(
					"Please choose x docs in the list: ",
					ul(
						li(label({ class: 'input-aside' },
							input({ dbjs: user._isARequested, type: 'checkbox' }), " ",
							span(user.getDescriptor('isBRequested').label))),
						li(label({ class: 'input-aside' },
							input({ dbjs: user._isARequested, type: 'checkbox' }), " ",
							span(user.getDescriptor('isBRequested').label))),
						li(
							label({ class: 'input-aside' }, input({ dbjs: user._isBRequested,
								type: 'checkbox',
								control: { class: 'readonly' },
								value: 'checked' }),
								" ",
								span(user.getDescriptor('isBRequested').label))
						),
						li(label({ class: 'input-aside' },
							input({ dbjs: user._isARequested, type: 'checkbox' }), " ",
							span(user.getDescriptor('isARequested').label))),
						li(label({ class: 'input-aside' },
							input({ dbjs: user._isARequested, type: 'checkbox' }), " ",
							span(user.getDescriptor('isBRequested').label)))
					)
				))),
		div({ class: 'section-primary' }, h2("Costs"),
			hr(),
			div(
				{ class: 'free-form' },
				p("LoLorem ipsum dolor sit amet, consectetur adipiscing elit. Sed feugiat aliquet massa," +
					" quis vulputate diam. Morbi non dolor ac tellus finibus commodo. Donec convallis" +
					" tortor felis, et sodales quam vulputate ac."),
				ul(
					li(
						p("Lorem ipsum dolor sit amet, consectetur adipiscing elit"),
						ul(
							li("Lorem ipsum dolor sit amet, consectetur adipiscing elit"),
							li("Lorem ipsum dolor sit amet, consectetur adipiscing elit")
						)
					),
					li("Lorem ipsum dolor sit amet, consectetur adipiscing elit"),
					li("Lorem ipsum dolor sit amet, consectetur adipiscing elit")
				),
				ol(
					li(
						p("Lorem ipsum dolor sit amet, consectetur adipiscing elit"),
						ol(
							li("Lorem ipsum dolor sit amet, consectetur adipiscing elit"),
							li("Lorem ipsum dolor sit amet, consectetur adipiscing elit")
						)
					),
					li("Lorem ipsum dolor sit amet, consectetur adipiscing elit")
				),
				p("LoLorem ipsum dolor sit amet, consectetur adipiscing elit. Sed feugiat aliquet massa," +
					" quis vulputate diam. Morbi non dolor ac tellus finibus commodo. Donec convallis" +
					" tortor felis, et sodales quam vulputate ac.")
			),
			p("Lorem ipsum dolor sit amet, consectetur adipiscing elit." +
				" Etiam vestibulum dui mi, nec ultrices diam ultricies id. "),
			ul({ class: 'user-guide-costs-list' },
				li(span("Registration fee"), " ",
					span("$50'000")),
				li(span("Stamp duty for registration"), " ",
					span("$10'000")),
				li(span("Filing fees for memorandum"), " ",
					span("$45'000")),
				li({ class: 'user-guide-total-costs' },
					span("Total Costs:"), " ",
					span("$105'000")),
				li(span("Filing fees for memorandum"), " ",
					span("$45'000")),
					small("Lorem ipsum dolor sit amet, consectetur adipiscing elit.")),
			p(
				a({ class: 'button-resource-link', href: 'costs-print/' },
					span({ class: 'fa fa-print' }), " ",
					"Print costs list"
					)
			)
			),
		p({ class: 'user-next-step-button' },
			button({ type: 'submit' },
				"Save and continue"))
	);
	reqRadio._dbjsInput.listItems[0].appendChild(
		div({ class: 'disabler-range' }, // add 'disabler-active' class to disable ul
			ul(
				li(label({ class: 'input-aside' },
					input({ dbjs: user._isARequested, type: 'checkbox' }), " ",
					span(user.getDescriptor('isBRequested').label))),
				li(label({ class: 'input-aside' },
					input({ dbjs: user._isARequested,
						type: 'checkbox', value: 'checked' }), " ",
					span(user.getDescriptor('isBRequested').label)))
			),
			div({ class: 'disabler' }))
	);
};
<commit_msg>Tootltip basic markup in user guide. #460<commit_after>'use strict';

var db = require('mano').db

  , user = db.User.prototype
  , reqRadio;

exports['step-guide'] = { class: { 'step-active': true } };

require('./_inventory');

exports.step = function () {
	h1("1. Individual registration guide for companies");

	form(
		{ class: 'user-guide' },
		div({ class: 'section-primary' }, h2("Questions"),
			hr(),
			ul({ class: 'form-elements' },
				['businessActivity',
					'isOwner',
					'inventory',
					'surfaceArea', 'members',
					'companyType',
					'isShoppingGallery'],
				function (name) {
					if (name === 'inventory') {
						div({ class: 'dbjs-input-component' },
							label({ for: 'input-' + name }, user.getDescriptor(name).label, ":"),
							div({ class: 'input' },
								input({ control: { id: 'input-' + name }, dbjs: user.getObservable(name) })));
						div({ class: 'user-guide-inventory-button' },
							a({ href: '#inventory' },
								span({ class: 'fa fa-calculator user-guide-inventory-icon' }, "Calculator"),
								span({ class: 'user-guide-inventory-label' }, "Calculate the amount")));
					} else if (name === 'isShoppingGallery') {
						li(div({ class: 'dbjs-input-component' },
							label(
								{ for: 'input-' + name },
								"Is shopping gallery?"
							),
							div({ class: 'input' },
								input({ control: { id: 'input-' + name }, dbjs: user.getObservable(name),
									type: 'checkbox' }))));
					} else {
						li(div({ class: 'dbjs-input-component' },
							label(
								{ for: 'input-' + name },
								user.getDescriptor(name).label,
								":"
							),
							div({ class: 'input' },
								input({ control: { id: 'input-' + name }, dbjs: user.getObservable(name) }))));
					}
				})),
		div(
			{ class: 'section-primary' },
			h2("Registrations"),
			hr(),
			p("Lorem ipsum dolor sit amet, consectetur adipiscing elit." +
				" Etiam vestibulum dui mi, nec ultrices diam ultricies id. " +
				" Etiam vestibulum dui mi, nec ultrices diam ultricies id. "),
			ul(li(label({ class: 'input-aside' },
				input({ dbjs: user._isARequested, type: 'checkbox', control: { class: 'readonly' } }), " ",
				span(user.getDescriptor('isARequested').label))),
				li(label({ class: 'input-aside' },
					input({ dbjs: user._isARequested, type: 'checkbox' }), " ",
					span(user.getDescriptor('isBRequested').label))),
				li(label({ class: 'input-aside tooltip-target' },
					input({ dbjs: user._isARequested, type: 'checkbox' }), " ",
					span(user.getDescriptor('isARequested').label)),
					div({ class: 'tooltip-container' },
						p(
							"Lorem ipsum dolor sit amet, consectetur adipiscing elit.",
							ul(
								li("Curabitur non"),
								li("lectus ut orci "),
								li("auctor scelerisque"),
								li(" ut id turpis")
							)
						))
					)),
			div(
				{ class: 'section-primary-wrapper' },
				h2("Optional Registrations"),
				hr(),
				ul(li(label({ class: 'input-aside' },
					input({ dbjs: user._isARequested, type: 'checkbox', control: { class: 'readonly' } }),
					" ",
					span(user.getDescriptor('isARequested').label))),
					li(label({ class: 'input-aside' },
						input({ dbjs: user._isARequested, type: 'checkbox' }), " ",
						span(user.getDescriptor('isBRequested').label))))
			)
		),

		div({ class: 'section-primary' }, h2("Requirements"),
			hr(),
			p("Lorem ipsum dolor sit amet, consectetur adipiscing elit." +
				" Etiam vestibulum dui mi, nec ultrices diam ultricies id. "),
			ul({ class: 'user-guide-requirements-list' },
				li("Lorem ipsum dolor sit amet, consectetur adipiscing elit. " +
					" Lorem ipsum dolor sit amet, consectetur adipiscing elit."),
				li("Lorem ipsum dolor sit amet, consectetur adipiscing elit. Lorem ipsum " +
					"dolor sit amet, consectetur adipiscing elit. Lorem ipsum dolor sit amet," +
					" consectetur adipiscing elit."),
				li("Lorem ipsum dolor sit amet, consectetur adipiscing elit."),
				li("Lorem ipsum dolor sit amet, consectetur adipiscing elit."),
				li("Lorem ipsum dolor sit amet, consectetur adipiscing elit."),
				li("Lorem ipsum dolor sit amet: ",
					ul(li(reqRadio = input({ dbjs: user._isType, type: 'radio',
						renderOption: function (labelTxt) {
							var data = {};
							data.dom = li(label({ class: 'input-aside' },
								span(data.input = input()),
								span(labelTxt)));
							return data;
						}  }), " "))),
				li(
					"Please choose x docs in the list: ",
					ul(
						li(label({ class: 'input-aside' },
							input({ dbjs: user._isARequested, type: 'checkbox' }), " ",
							span(user.getDescriptor('isBRequested').label))),
						li(label({ class: 'input-aside' },
							input({ dbjs: user._isARequested, type: 'checkbox' }), " ",
							span(user.getDescriptor('isBRequested').label))),
						li(
							label({ class: 'input-aside' }, input({ dbjs: user._isBRequested,
								type: 'checkbox',
								control: { class: 'readonly' },
								value: 'checked' }),
								" ",
								span(user.getDescriptor('isBRequested').label))
						),
						li(label({ class: 'input-aside' },
							input({ dbjs: user._isARequested, type: 'checkbox' }), " ",
							span(user.getDescriptor('isARequested').label))),
						li(label({ class: 'input-aside' },
							input({ dbjs: user._isARequested, type: 'checkbox' }), " ",
							span(user.getDescriptor('isBRequested').label)))
					)
				))),
		div({ class: 'section-primary' }, h2("Costs"),
			hr(),
			div(
				{ class: 'free-form' },
				p("LoLorem ipsum dolor sit amet, consectetur adipiscing elit. Sed feugiat aliquet massa," +
					" quis vulputate diam. Morbi non dolor ac tellus finibus commodo. Donec convallis" +
					" tortor felis, et sodales quam vulputate ac."),
				ul(
					li(
						p("Lorem ipsum dolor sit amet, consectetur adipiscing elit"),
						ul(
							li("Lorem ipsum dolor sit amet, consectetur adipiscing elit"),
							li("Lorem ipsum dolor sit amet, consectetur adipiscing elit")
						)
					),
					li("Lorem ipsum dolor sit amet, consectetur adipiscing elit"),
					li("Lorem ipsum dolor sit amet, consectetur adipiscing elit")
				),
				ol(
					li(
						p("Lorem ipsum dolor sit amet, consectetur adipiscing elit"),
						ol(
							li("Lorem ipsum dolor sit amet, consectetur adipiscing elit"),
							li("Lorem ipsum dolor sit amet, consectetur adipiscing elit")
						)
					),
					li("Lorem ipsum dolor sit amet, consectetur adipiscing elit")
				),
				p("LoLorem ipsum dolor sit amet, consectetur adipiscing elit. Sed feugiat aliquet massa," +
					" quis vulputate diam. Morbi non dolor ac tellus finibus commodo. Donec convallis" +
					" tortor felis, et sodales quam vulputate ac.")
			),
			p("Lorem ipsum dolor sit amet, consectetur adipiscing elit." +
				" Etiam vestibulum dui mi, nec ultrices diam ultricies id. "),
			ul({ class: 'user-guide-costs-list' },
				li(span("Registration fee"), " ",
					span("$50'000")),
				li(span("Stamp duty for registration"), " ",
					span("$10'000")),
				li(span("Filing fees for memorandum"), " ",
					span("$45'000")),
				li({ class: 'user-guide-total-costs' },
					span("Total Costs:"), " ",
					span("$105'000")),
				li(span("Filing fees for memorandum"), " ",
					span("$45'000")),
					small("Lorem ipsum dolor sit amet, consectetur adipiscing elit.")),
			p(
				a({ class: 'button-resource-link', href: 'costs-print/' },
					span({ class: 'fa fa-print' }), " ",
					"Print costs list"
					)
			)
			),
		p({ class: 'user-next-step-button' },
			button({ type: 'submit' },
				"Save and continue"))
	);
	reqRadio._dbjsInput.listItems[0].appendChild(
		div({ class: 'disabler-range' }, // add 'disabler-active' class to disable ul
			ul(
				li(label({ class: 'input-aside' },
					input({ dbjs: user._isARequested, type: 'checkbox' }), " ",
					span(user.getDescriptor('isBRequested').label))),
				li(label({ class: 'input-aside' },
					input({ dbjs: user._isARequested,
						type: 'checkbox', value: 'checked' }), " ",
					span(user.getDescriptor('isBRequested').label)))
			),
			div({ class: 'disabler' }))
	);
};
<|endoftext|>


<commit_before>/**
 * # WaitingRoom
 *
 * Starts a game room based on a list of criteria.
 */

"use strict";

// ## Global scope
module.exports = Wait;

var ngc = require('nodegame-client');
var J = require('JSUS').JSUS;

exports.WaitingRoom = WaitingRoom;

function WaitingRoom(options) {

    this.triggerManager = new ngc.TriggerManager(options);

    var that = this;

    var pullTriggers = function() {

        var groups  = that.pullTriggers();

        if (!groups) return;
        if (!J.isArray(groups)) groups = [groups];

        var i, name, count = 0;
        for (i = 0; i< groups.length; i++) {
            name = groups[i].name || count++;

            // Start Game.
            // node.game.room[name] = new Game(groups[i]);

        }
    };

    var onConnectFunc = function() {
        //console.log('added')
        node.onPLIST(function(){
            pullTriggers();
        });
    };

    var onConnect;
    Object.defineProperty(this, 'onConnect', {
        set: function(value) {
            if (value === false) {
                node.removeListener('in.say.PLIST', pullTriggers);
                node.removeListener('in.set.PLIST', pullTriggers);
            }
            else if (value === true) {
                node.onPLIST(pullTriggers);
            }
            onConnect = value;

        },
        get: function() {
            return onConnect;
        },
        configurable: true
    });

    var onTimeout, onTimeoutTime;

    Object.defineProperty(this, 'onTimeout', {
        set: function(value) {
            if (!value) {
                clearTimeout(onTimeout);
                onTimeoutTime = value;
                onTimeout = false;
            }
            else if ('numeric' === typeof value) {

                if (onTimeout) {
                    clearTimeout(onTimeout);
                }
                onTimeoutTime = value;
                onTimeout = setTimeout(pullTriggers);
            }
        },
        get: function() {
            return onTimeoutTime;
        },
        configurable: true
    });

    var onInterval, onIntervalTime;
    Object.defineProperty(this, 'onInterval', {
        set: function(value) {
            if (!value) {
                clearInterval(onInterval);
                onIntervalTime = value;
                onInterval = false;
            }
            else if ('numeric' === typeof value) {

                if (onInterval) {
                    clearInterval(onInterval);
                }
                onInterval = setInterval(pullTriggers);
                onIntervalTime = value;
            }
        },
        get: function() {
            return onIntervalTime;
        },
        configurable: true
    });

    this.init(options);
}

WaitingRoom.prototype.init = function (options) {
    options = options || {};

    this.onConnect = options.onConnect || true;
    this.onTimeout = options.onTimeout || false;
    this.onInterval = options.onInterval || false;

    this.addTrigger(function(){
        return new Group({
            players: node.pool,
            game: options.loops
        });
    });

    if (options.minPlayers && options.maxPlayers) {
        this.addTrigger(function(){
            if (node.pool.length < options.minPlayers) {
                return false;
            }
            if (node.pool.length > options.maxPlayers) {
                // Take N = maxPlayers random player
                var players = node.pool.shuffle().limit(options.maxPlayers);
                return new Group({
                    players: players,
                    game: options.loops
                });

            }

            return new Group({
                players: node.pool,
                game: options.loops
            });
        });
    }

    if (options.minPlayers) {
        this.addTrigger(function(){
            if (node.pool.length < options.minPlayers) {
                return false;
            }

            return new Group({
                players: node.pool,
                game: options.loops
            });
        });
    }

    if (options.maxPlayers) {
        this.addTrigger(function(){
            if (node.pool.length > options.maxPlayers) {
                // Take N = maxPlayers random player
                var players = node.pool.shuffle().limit(options.maxPlayers);
                return new Group({
                    players: players,
                    game: options.loops
                });

            }
        });
    }

    if (options.nPlayers) {
        this.addTrigger(function(){
            if (node.pool.length === options.nPlayers) {
                // Take N = maxPlayers random player
                return new Group({
                    players: node.pool,
                    game: options.loops
                });

            }
        });
    }

};

WaitingRoom.prototype.criteria = function (func, pos) {
    this.addTrigger(func, pos);
};

/**
 * ## WaitingRoom.setInterval
 *
 * Set the update interval
 *
 */
WaitingRoom.prototype.setInterval = function(interval) {
    if (!interval) clearInterval(this.interval);
    if (this.interval) clearInterval(this.interval);
    this.interval = setInterval(this.pullTriggers, interval);
};
<commit_msg>cleanup<commit_after><|endoftext|>