<commit_before>var types = require("../lib/HAP-NodeJS/accessories/types.js");
var request = require("request");

function GarageDoor(veraIP, device) {
  this.device = device;
  this.veraIP = veraIP;
  this.name = device.name;
}

function CorrectUrn(founddevice) {
    switch (founddevice.split(',')[2]) {
      case "urn:schemas-micasaverde-com:device:DoorLock:1":
        return "urn:micasaverde-com:serviceId:DoorLock1";
      break;
      case "urn:schemas-upnp-org:device:BinaryLight:1":
        return "urn:upnp-org:serviceId:SwitchPower1";
      break;
    }
}

function CorrectState(newState, urn) {
    switch (urn) {
      case "urn:micasaverde-com:serviceId:DoorLock1":
        return newState ? 1 : 0;
      break;
      case "urn:upnp-org:serviceId:SwitchPower1":
        return newState ? 0 : 1;
      break;
    }
}

GarageDoor.prototype = {

  onSetUnlocked: function(newState) {

    if (!newState) {
      console.log("Opening the " + this.device.name);
    } else {
      console.log("Closing the " + this.device.name);
    }

    var self = this;

    request.get({ url: "http://" + self.veraIP + ":3480/data_request?id=finddevice&devnum=" + self.device.id },
                function (error, response, body) {
                  self.urn = CorrectUrn(body);
                  self.binaryState = CorrectState(newState, self.urn);
                  request.get({url: "http://" + self.veraIP + ":3480/data_request?id=lu_action&output_format=xml&DeviceNum=" + self.device.id + "&serviceId=" + self.urn + "&action=SetTarget&newTargetValue=" + self.binaryState},
                              function(err, response, body) {
                                if (!err && response.statusCode == 200) {
                                  if (!newState) {
                                    console.log("The " + self.device.name + " has been opened");
                                  } else {
                                    console.log("The " + self.device.name + " has been closed");
                                  }
                                } else {
                                  console.log("Error '" + err + "' opening/closing the " + self.device.name + ":  " + body);
                                }
                              }
                             );
                }
               );
  },

  onGetUnlocked: function(newState) {

    console.log("Reading status on " + this.device.name);
    var self = this;

    request.get({ url: "http://" + self.veraIP + ":3480/data_request?id=finddevice&devnum=" + self.device.id },
                function (error, response, body) {
                  self.urn = CorrectUrn(body);
                  self.binaryState = CorrectState(newState, self.urn);
                  request.get({url: "http://" + this.veraIP + ":3480/data_request?id=variableget&output_format=xml&DeviceNum=" + this.device.id + "&serviceId="+self.urn+"&Variable=Status"},
                    function(err, response, body) {
                      if (!err && response.statusCode == 200) {

                        var open = parseInt(body) == 0;

                        if (open) {
                          console.log("The " + self.device.name + " is open");
                        } else {
                          console.log("The " + self.device.name + " is closed");
                        }

                        callback(open);
                      } else {
                        console.log("Error '" + err + "' turning the " + self.device.name + " on/off:  " + body);
                      }
                    }
                  );
                }
               );
  },

  /**
   *  This method is called when the user tries to identify this accessory
   */
  onIdentify: function(identify) {
    if (identify) {
      console.log("User wants to identify this accessory");
    } else {
      console.log("User is finished identifying this accessory");
    }
  },

  getServices: function() {
    var that = this;
    return [{

      sType: types.ACCESSORY_INFORMATION_STYPE,
      characteristics: [{
        cType: types.NAME_CTYPE,
        onUpdate: null,
        perms: ["pr"],
        format: "string",
        initialValue: "Garage Door Opener",
        supportEvents: false,
        supportBonjour: false,
        manfDescription: "Name of the accessory",
        designedMaxLength: 255
      },{
        cType: types.MANUFACTURER_CTYPE,
        onUpdate: null,
        perms: ["pr"],
        format: "string",
        initialValue: "Oltica",
        supportEvents: false,
        supportBonjour: false,
        manfDescription: "Manufacturer",
        designedMaxLength: 255
      },{
        cType: types.MODEL_CTYPE,
        onUpdate: null,
        perms: ["pr"],
        format: "string",
        initialValue: "Rev-1",
        supportEvents: false,
        supportBonjour: false,
        manfDescription: "Model",
        designedMaxLength: 255
      },{
        cType: types.SERIAL_NUMBER_CTYPE,
        onUpdate: null,
        perms: ["pr"],
        format: "string",
        initialValue: "A1S2NASF88EW",
        supportEvents: false,
        supportBonjour: false,
        manfDescription: "SN",
        designedMaxLength: 255
      },{
        cType: types.IDENTIFY_CTYPE,
        onUpdate: null,
        perms: ["pw"],
        format: "bool",
        initialValue: false,
        supportEvents: false,
        supportBonjour: false,
        manfDescription: "Identify Accessory",
        designedMaxLength: 1
      }]
    },{
      sType: types.GARAGE_DOOR_OPENER_STYPE,
      characteristics: [{
        cType: types.NAME_CTYPE,
        onUpdate: null,
        perms: ["pr"],
        format: "string",
        initialValue: "Garage Door Opener Control",
        supportEvents: false,
        supportBonjour: false,
        manfDescription: "Name of service",
        designedMaxLength: 255
      },{
        cType: types.CURRENT_DOOR_STATE_CTYPE,
        onUpdate: function(value) { console.log("Change:",value); execute("Garage Door", "Current State", value); },
        onRead: function(callback) { console.log("Read:",value); onGetUnlocked(callback); },
        perms: ["pr","ev"],
        format: "int",
        initialValue: 0,
        supportEvents: false,
        supportBonjour: false,
        manfDescription: "BlaBla",
        designedMinValue: 0,
        designedMaxValue: 4,
        designedMinStep: 1,
        designedMaxLength: 1
      },{
        cType: types.TARGET_DOORSTATE_CTYPE,
        onUpdate: function(value) { that.onSetUnlocked(value); },
        //onUpdate: function(value) { console.log("Change:",value); execute("Garage Door", "Target State", value); },
        perms: ["pr","pw","ev"],
        format: "int",
        initialValue: 0,
        supportEvents: false,
        supportBonjour: false,
        manfDescription: "BlaBla",
        designedMinValue: 0,
        designedMaxValue: 1,
        designedMinStep: 1,
        designedMaxLength: 1
      },{
        cType: types.OBSTRUCTION_DETECTED_CTYPE,
        onUpdate: function(value) { console.log("Change:",value); execute("Garage Door", "Obstruction Detected", value); },
        perms: ["pr","ev"],
        format: "bool",
        initialValue: false,
        supportEvents: false,
        supportBonjour: false,
        manfDescription: "BlaBla",
      }]

    }]
  }
};

module.exports.initializeWithDevice = GarageDoor;
<commit_msg>Fix GarageDoor<commit_after>var types = require("../lib/HAP-NodeJS/accessories/types.js");
var request = require("request");

function GarageDoor(veraIP, device) {
  this.device = device;
  this.veraIP = veraIP;
  this.name = device.name;
}

function CorrectUrn(founddevice) {
    switch (founddevice.split(',')[2]) {
      case "urn:schemas-micasaverde-com:device:DoorLock:1":
        return "urn:micasaverde-com:serviceId:DoorLock1";
      break;
      case "urn:schemas-upnp-org:device:BinaryLight:1":
        return "urn:upnp-org:serviceId:SwitchPower1";
      break;
    }
}

function CorrectState(newState, urn) {
    switch (urn) {
      case "urn:micasaverde-com:serviceId:DoorLock1":
        return newState ? 1 : 0;
      break;
      case "urn:upnp-org:serviceId:SwitchPower1":
        return newState ? 0 : 1;
      break;
    }
}

GarageDoor.prototype = {

  onSetUnlocked: function(newState) {

    if (!newState) {
      console.log("Opening the " + this.device.name);
    } else {
      console.log("Closing the " + this.device.name);
    }

    var self = this;

    request.get({ url: "http://" + self.veraIP + ":3480/data_request?id=finddevice&devnum=" + self.device.id },
                function (error, response, body) {
                  self.urn = CorrectUrn(body);
                  self.binaryState = CorrectState(newState, self.urn);
                  request.get({url: "http://" + self.veraIP + ":3480/data_request?id=lu_action&output_format=xml&DeviceNum=" + self.device.id + "&serviceId=" + self.urn + "&action=SetTarget&newTargetValue=" + self.binaryState},
                              function(err, response, body) {
                                if (!err && response.statusCode == 200) {
                                  if (!newState) {
                                    console.log("The " + self.device.name + " has been opened");
                                  } else {
                                    console.log("The " + self.device.name + " has been closed");
                                  }
                                } else {
                                  console.log("Error '" + err + "' opening/closing the " + self.device.name + ":  " + body);
                                }
                              }
                             );
                }
               );
  },

  onGetUnlocked: function(callback) {

    console.log("Reading status on " + this.device.name);
    var self = this;

    request.get({ url: "http://" + self.veraIP + ":3480/data_request?id=finddevice&devnum=" + self.device.id },
                function (error, response, body) {
                  self.urn = CorrectUrn(body);
                  request.get({url: "http://" + self.veraIP + ":3480/data_request?id=variableget&output_format=xml&DeviceNum=" + self.device.id + "&serviceId=" + self.urn + "&Variable=Status"},
                    function(err, response, body) {
                      if (!err && response.statusCode == 200) {

                        var open = CorrectState(parseInt(body), self.urn);

                        if (open) {
                          console.log("The " + self.device.name + " is open");
                        } else {
                          console.log("The " + self.device.name + " is closed");
                        }

                        callback(open);
                      } else {
                        console.log("Error '" + err + "' turning the " + self.device.name + " on/off:  " + body);
                      }
                    }
                  );
                }
               );
  },

  /**
   *  This method is called when the user tries to identify this accessory
   */
  onIdentify: function(identify) {
    if (identify) {
      console.log("User wants to identify this accessory");
    } else {
      console.log("User is finished identifying this accessory");
    }
  },

  getServices: function() {
    var that = this;
    return [{

      sType: types.ACCESSORY_INFORMATION_STYPE,
      characteristics: [{
        cType: types.NAME_CTYPE,
        onUpdate: null,
        perms: ["pr"],
        format: "string",
        initialValue: this.name,
        supportEvents: false,
        supportBonjour: false,
        manfDescription: "Name of the accessory",
        designedMaxLength: 255
      },{
        cType: types.MANUFACTURER_CTYPE,
        onUpdate: null,
        perms: ["pr"],
        format: "string",
        initialValue: "Oltica",
        supportEvents: false,
        supportBonjour: false,
        manfDescription: "Manufacturer",
        designedMaxLength: 255
      },{
        cType: types.MODEL_CTYPE,
        onUpdate: null,
        perms: ["pr"],
        format: "string",
        initialValue: "Rev-1",
        supportEvents: false,
        supportBonjour: false,
        manfDescription: "Model",
        designedMaxLength: 255
      },{
        cType: types.SERIAL_NUMBER_CTYPE,
        onUpdate: null,
        perms: ["pr"],
        format: "string",
        initialValue: "A1S2NASF88EW",
        supportEvents: false,
        supportBonjour: false,
        manfDescription: "SN",
        designedMaxLength: 255
      },{
        cType: types.IDENTIFY_CTYPE,
        onUpdate: null,
        perms: ["pw"],
        format: "bool",
        initialValue: false,
        supportEvents: false,
        supportBonjour: false,
        manfDescription: "Identify Accessory",
        designedMaxLength: 1
      }]
    },{
      sType: types.GARAGE_DOOR_OPENER_STYPE,
      characteristics: [{
        cType: types.NAME_CTYPE,
        onUpdate: null,
        perms: ["pr"],
        format: "string",
        initialValue: "Garage Door Opener Control",
        supportEvents: false,
        supportBonjour: false,
        manfDescription: "Name of service",
        designedMaxLength: 255
      },{
        cType: types.CURRENT_DOOR_STATE_CTYPE,
        onUpdate: function(value) { execute("Garage Door", "Current State", value); },
        onRead: function(callback) { that.onGetUnlocked(callback); },
        perms: ["pr","ev"],
        format: "int",
        initialValue: 0,
        supportEvents: false,
        supportBonjour: false,
        manfDescription: "BlaBla",
        designedMinValue: 0,
        designedMaxValue: 4,
        designedMinStep: 1,
        designedMaxLength: 1
      },{
        cType: types.TARGET_DOORSTATE_CTYPE,
        onUpdate: function(value) { that.onSetUnlocked(value); },
        //onUpdate: function(value) { console.log("Change:",value); execute("Garage Door", "Target State", value); },
        perms: ["pr","pw","ev"],
        format: "int",
        initialValue: 0,
        supportEvents: false,
        supportBonjour: false,
        manfDescription: "BlaBla",
        designedMinValue: 0,
        designedMaxValue: 1,
        designedMinStep: 1,
        designedMaxLength: 1
      },{
        cType: types.OBSTRUCTION_DETECTED_CTYPE,
        onUpdate: function(value) { console.log("Change:",value); execute("Garage Door", "Obstruction Detected", value); },
        perms: ["pr","ev"],
        format: "bool",
        initialValue: false,
        supportEvents: false,
        supportBonjour: false,
        manfDescription: "BlaBla",
      }]

    }]
  }
};

module.exports.initializeWithDevice = GarageDoor;
<|endoftext|>


<commit_before>// casperjs test

function getHelps() {
  var children = $("#helps-display").children();

  var result = [];
  for (var i = 0; i < children.length; i ++) {
    var child = children[i];
    var kbds = $("div:first", child).children();

    var keys = "";
    for (var j = 0; j < kbds.length; j ++) {
      var kbd = kbds[j];
      keys += kbd.innerHTML;
    }
    result.push(keys);
  }
  return result;
}; // getHelps()

function getFocus() {
  casper.page.evaluate(function() {
    editor.focus();
  });
}; // getFocus()

function runSingleTest(test, testCase) {

  var given = testCase[0];
  var expected = testCase[1];
  var comment = (testCase.length >= 2) ? testCase[2] : "no comment.";

  // Send key inputs.
  casper.each(given, function(self, key) {
    self.wait(INTERVAL_KEY_EVENT, function() {
      casper.page.sendEvent('keypress', key);
    });
  });

  // After a while, get the displayed helps.
  casper.wait(INTERVAL_RESULT, function() {
    var result = casper.evaluate(getHelps);
    var message = given.toString() + " : " + comment;

    test.assertEquals(result, expected, message);
  });
}; // singleFSMTest()

function runTestSuite(title, suite) {

  casper.test.begin(title, suite.length, function (test) {
    casper.start("../../index.html", function() {
      getFocus();
    });

    casper.each(suite, function(self, testCase) {
      self.wait(INTERVAL_TEST_CASE, function() {
        runSingleTest(test, testCase);
      });
    });

    casper.run(function() {
      test.done();
    });
  });
};

var INTERVAL_TEST_CASE = 150;
var   INTERVAL_KEY_EVENT = 10;
var   INTERVAL_RESULT = 30;

casper.test.begin("Confirm test parameters", 1, function(test) {
  var LONGEST_KEYS = 10;
  var longest_test_case = INTERVAL_KEY_EVENT * LONGEST_KEYS + INTERVAL_RESULT;
  test.assertTruthy(INTERVAL_TEST_CASE > longest_test_case, "interval values are ok");
  test.done();
});

runTestSuite("motion keys", [
  [ ['w'],         ['w'],      "simple motion" ],
  [ ['g','g'],     ['gg'],     "two key motion" ],
  [ ['f','w'],     ['fchar'],  "two key motion with wildcard" ],
  [ ['0','w'],     ['w'],      "zero key should not treated as count." ],
  [ ['2','b'],     ['2','b'],  "motion with count" ],
  [ ['1','0','w'], ['10','w'], "zero key as a count" ],
]);

runTestSuite("operators", [
  [ ['d','d'],         ['d','d'],         "simple double operator" ],
  [ ['d','2','d'],     ['d','2','d'],     "double operator with repetition" ],
  [ ['2','d','d'],     ['2','d','d'],     "double operator with repetition" ],
  [ ['2','d','3','d'], ['2','d','3','d'], "double operator with repetition" ],
  [ ['d','w'],         ['d','w'],         "simple operator motion" ],
  [ ['1','2','d','2','0','3','w'], ['12','d','203','w'], "repeatition frenzy!" ],
  [ ['d','c'],         [], "different operators in a row is an error." ],
  [ ['d','2','c'],     [], "different operators in a row is an error." ],
  [ ['2','d','c'],     [], "different operators in a row is an error." ],
  [ ['4','d','2','c'], [], "different operators in a row is an error." ],
  [ ['x','w'],         ['w'], "x key is not an operator in normal mode" ],
]);

runTestSuite("visual mode", [
  [ ['v','v'],                 ['v'],               "abort visual mode" ],
  [ ['v','d'],                 ['v','d'],           "simple visual mode operation" ],
  [ ['v','w','w','w','d'],     ['v','w','d'],       "show only last motion" ],
  [ ['v','3','0','2','w','d'], ['v','302','w','d'], "show only last motion" ],
  [ ['v','i',')','d'],         ['v','i',')','d'],   "visual mode text object" ],
  [ ['v','w','x'],             ['v','w','x'],       "x key is an operator in visual mode" ],
]);

<commit_msg>[test] Add test for ex mode behaviour related to helps.<commit_after>// casperjs test

function getHelps() {
  var children = $("#helps-display").children();

  var result = [];
  for (var i = 0; i < children.length; i ++) {
    var child = children[i];
    var kbds = $("div:first", child).children();

    var keys = "";
    for (var j = 0; j < kbds.length; j ++) {
      var kbd = kbds[j];
      keys += kbd.innerHTML;
    }
    result.push(keys);
  }
  return result;
}; // getHelps()

function getFocus() {
  casper.page.evaluate(function() {
    editor.focus();
  });
}; // getFocus()

function runSingleTest(test, testCase) {

  var given = testCase[0];
  var expected = testCase[1];
  var comment = (testCase.length >= 2) ? testCase[2] : "no comment.";

  // Send key inputs.
  casper.each(given, function(self, key) {
    self.wait(INTERVAL_KEY_EVENT, function() {
      casper.page.sendEvent('keypress', key);
    });
  });

  // After a while, get the displayed helps.
  casper.wait(INTERVAL_RESULT, function() {
    var result = casper.evaluate(getHelps);
    var message = given.toString() + " : " + comment;

    test.assertEquals(result, expected, message);
  });
}; // singleFSMTest()

function runTestSuite(title, suite) {

  casper.test.begin(title, suite.length, function (test) {
    casper.start("../../index.html", function() {
      getFocus();
    });

    casper.each(suite, function(self, testCase) {
      self.wait(INTERVAL_TEST_CASE, function() {
        runSingleTest(test, testCase);
      });
    });

    casper.run(function() {
      test.done();
    });
  });
};

var INTERVAL_TEST_CASE = 150;
var   INTERVAL_KEY_EVENT = 10;
var   INTERVAL_RESULT = 30;

casper.test.begin("Confirm test parameters", 1, function(test) {
  var LONGEST_KEYS = 10;
  var longest_test_case = INTERVAL_KEY_EVENT * LONGEST_KEYS + INTERVAL_RESULT;
  test.assertTruthy(INTERVAL_TEST_CASE > longest_test_case, "interval values are ok");
  test.done();
});

runTestSuite("motion keys", [
  [ ['w'],         ['w'],      "simple motion" ],
  [ ['g','g'],     ['gg'],     "two key motion" ],
  [ ['f','w'],     ['fchar'],  "two key motion with wildcard" ],
  [ ['0','w'],     ['w'],      "zero key should not treated as count." ],
  [ ['2','b'],     ['2','b'],  "motion with count" ],
  [ ['1','0','w'], ['10','w'], "zero key as a count" ],
]);

runTestSuite("operators", [
  [ ['d','d'],         ['d','d'],         "simple double operator" ],
  [ ['d','2','d'],     ['d','2','d'],     "double operator with repetition" ],
  [ ['2','d','d'],     ['2','d','d'],     "double operator with repetition" ],
  [ ['2','d','3','d'], ['2','d','3','d'], "double operator with repetition" ],
  [ ['d','w'],         ['d','w'],         "simple operator motion" ],
  [ ['1','2','d','2','0','3','w'], ['12','d','203','w'], "repeatition frenzy!" ],
  [ ['d','c'],         [], "different operators in a row is an error." ],
  [ ['d','2','c'],     [], "different operators in a row is an error." ],
  [ ['2','d','c'],     [], "different operators in a row is an error." ],
  [ ['4','d','2','c'], [], "different operators in a row is an error." ],
  [ ['x','w'],         ['w'], "x key is not an operator in normal mode" ],
]);

runTestSuite("visual mode", [
  [ ['v','v'],                 ['v'],               "abort visual mode" ],
  [ ['v','d'],                 ['v','d'],           "simple visual mode operation" ],
  [ ['v','w','w','w','d'],     ['v','w','d'],       "show only last motion" ],
  [ ['v','3','0','2','w','d'], ['v','302','w','d'], "show only last motion" ],
  [ ['v','i',')','d'],         ['v','i',')','d'],   "visual mode text object" ],
  [ ['v','w','x'],             ['v','w','x'],       "x key is an operator in visual mode" ],
]);

var ENTER = casper.page.event.key.Enter;
runTestSuite("ex mode", [
  [ [':set nu',ENTER,'d','d'], ['d','d'],           "enter key to come back to normal mode" ],
]);

<|endoftext|>


<commit_before>#!/usr/bin/env node

var os = require('os')
var path = require('path')
var nugget = require('nugget')
var extract = require('extract-zip')
var fs = require('fs')

var platform = os.platform()
var arch = os.arch()
var version = '0.25.1'
var filename = 'electron-v' + version + '-' + platform + '-' + arch + '.zip'
var url = 'https://github.com/atom/electron/releases/download/v' + version + '/electron-v' + version + '-' + platform + '-' + arch + '.zip'

function onerror (err) {
  throw err
}

var paths = {
  darwin: path.join(__dirname, './dist/Electron.app/Contents/MacOS/Electron'),
  linux: path.join(__dirname, './dist/electron'),
  win32: path.join(__dirname, './dist/electron.exe')
}

if (!paths[platform]) throw new Error('Unknown platform: ' + platform)

nugget(url, {target: filename, dir: __dirname, resume: true, verbose: true}, function (err) {
  if (err) return onerror(err)
  fs.writeFileSync(path.join(__dirname, 'path.txt'), paths[platform])
  extract(path.join(__dirname, filename), {dir: path.join(__dirname, 'dist')}, function (err) {
    if (err) return onerror(err)
  })
})
<commit_msg>version 0.25.2<commit_after>#!/usr/bin/env node

var os = require('os')
var path = require('path')
var nugget = require('nugget')
var extract = require('extract-zip')
var fs = require('fs')

var platform = os.platform()
var arch = os.arch()
var version = '0.25.2'
var filename = 'electron-v' + version + '-' + platform + '-' + arch + '.zip'
var url = 'https://github.com/atom/electron/releases/download/v' + version + '/electron-v' + version + '-' + platform + '-' + arch + '.zip'

function onerror (err) {
  throw err
}

var paths = {
  darwin: path.join(__dirname, './dist/Electron.app/Contents/MacOS/Electron'),
  linux: path.join(__dirname, './dist/electron'),
  win32: path.join(__dirname, './dist/electron.exe')
}

if (!paths[platform]) throw new Error('Unknown platform: ' + platform)

nugget(url, {target: filename, dir: __dirname, resume: true, verbose: true}, function (err) {
  if (err) return onerror(err)
  fs.writeFileSync(path.join(__dirname, 'path.txt'), paths[platform])
  extract(path.join(__dirname, filename), {dir: path.join(__dirname, 'dist')}, function (err) {
    if (err) return onerror(err)
  })
})
<|endoftext|>


<commit_before>/*
 * The MIT License (MIT)
 *
 * Copyright (C) 2016 Quantum HPC
 *
 * 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 cproc = require('child_process');
var spawn = cproc.spawnSync;
var fs = require('fs');
var path = require('path');

// Regex for lmstat output
var featureRegEx=/^Users of ([^:]*):[^0-9:]*([0-9]+)[^0-9]*([0-9]+)[^0-9]*([\)]+)/;
var errorRegEx=/^Users of ([^:]*):[^0-9]*(Error)[^0-9]*([0-9]+)[^0-9]*,([^:]*)([\)]+)/;
var versionTokenRegEx=/\s*([^\s]*)\s+([^\s,]*),\s+([^\s]*)\s+([^\s,]*),\s+([^\s]*)\s+([^\s,]*)/;
var userTokenRegEx=/([^\s]*)\s+([^\s]*)\s+([^\s]*)\s+\(([^\s]*)\)\s+\(([^\)]*)\),\s+([^\s]*)\s(.*)/;
var result = {};
var tokenFeature;
var vendorInfo;

// Parse the output of lmutil lmstat and return a JSON array
// serverURL can be used to test with the output of lmstat stored in a file by sending an array ['test',filePath]
function lmstat(flexConfig, callback){
  var flexCmd = flexConfig.flexCmd.trim().split(/\s/g);

  // Create Stream
  var output = [];
  if (flexConfig.serverURL[0] === 'test'){
    var outputFile = fs.readFileSync(flexConfig.serverURL[1],'utf8');
    output.stdout = outputFile;
  }else{
    flexCmd.push(flexConfig.serverURL);
    output = spawn(path.resolve(flexConfig.flexBinary), flexCmd, { encoding : 'utf8' });
  }

  // Invalid lmutil binary
  if (output.error){
      return callback(new Error(output.error));
  }
  // Transmit the error if any
  if (output.stderr){
      return callback(new Error(output.stderr.replace(/\n/g,"")));
  }
  // Treat output
  output = output.stdout.split('\n');

  for (var i=0; i<output.length; i++){
    // Line by line
    var line = output[i];
    var m;
    // Feature line
    m = line.match(featureRegEx);
    if (m) {
      //Assuming token line is given right after feature line, we save it for the next lines
      tokenFeature = m[1];
      // Push the feature
      result[tokenFeature] = {
        "total":m[2],
        "used":m[3],
        "free":m[2]-m[3],
        "tokens":[]
      };
    }else{
      // Token line with vendor
      m = line.match(versionTokenRegEx);
      if (m) {
        // Save the vendor information temporarly
        vendorInfo = {
          "name" : m[4],
          "version" : m[2],
          "expiry" : m[6]
        };
      }else{
        // Token line with username and machine
        m = line.match(userTokenRegEx);
        if (m) {
          // Need the previous lines to know which licence
          result[tokenFeature].tokens.push({
            "username" : m[1],
            "machine" : m[2],
            "started" : m[7],
            "vendorname" : vendorInfo.name,
            "version" : vendorInfo.version,
            "expiry" : vendorInfo.expiry
          });
        }else{
          // Error lines
          m = line.match(errorRegEx);
          if (m) {
            result[m[1]] = {
              "total":m[3],
              "error":m[4]
            };
          }
        }
      }
    }
  }
  // Return result table
  return callback(null, result);
}

module.exports = {
    lmstat           : lmstat,
};
<commit_msg>Cross platform spawn<commit_after>/*
 * The MIT License (MIT)
 *
 * Copyright (C) 2016 Quantum HPC
 *
 * 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 cproc = require('child_process');
var spawn = cproc.spawnSync;
var fs = require('fs');
var path = require('path');

// Regex for lmstat output
var featureRegEx=/^Users of ([^:]*):[^0-9:]*([0-9]+)[^0-9]*([0-9]+)[^0-9]*([\)]+)/;
var errorRegEx=/^Users of ([^:]*):[^0-9]*(Error)[^0-9]*([0-9]+)[^0-9]*,([^:]*)([\)]+)/;
var versionTokenRegEx=/\s*([^\s]*)\s+([^\s,]*),\s+([^\s]*)\s+([^\s,]*),\s+([^\s]*)\s+([^\s,]*)/;
var userTokenRegEx=/([^\s]*)\s+([^\s]*)\s+([^\s]*)\s+\(([^\s]*)\)\s+\(([^\)]*)\),\s+([^\s]*)\s(.*)/;
var result = {};
var tokenFeature;
var vendorInfo;

// Parse the output of lmutil lmstat and return a JSON array
// serverURL can be used to test with the output of lmstat stored in a file by sending an array ['test',filePath]
function lmstat(flexConfig, callback){
  var flexCmd = flexConfig.flexCmd.trim().split(/\s/g);

  // Create Stream
  var output = [];
  if (flexConfig.serverURL[0] === 'test'){
    var outputFile = fs.readFileSync(flexConfig.serverURL[1],'utf8');
    output.stdout = outputFile;
  }else{
    flexCmd.push(flexConfig.serverURL);
    output = spawn(path.resolve(flexConfig.flexBinary + (/^win/.test(process.platform) ? (!flexConfig.flexBinary.endsWith(".exe") ? '.exe' : '') : '')), flexCmd, { encoding : 'utf8' });
  }

  // Invalid lmutil binary
  if (output.error){
      return callback(new Error(output.error));
  }
  // Transmit the error if any
  if (output.stderr){
      return callback(new Error(output.stderr.replace(/\n/g,"")));
  }
  // Treat output
  output = output.stdout.split('\n');

  for (var i=0; i<output.length; i++){
    // Line by line
    var line = output[i];
    var m;
    // Feature line
    m = line.match(featureRegEx);
    if (m) {
      //Assuming token line is given right after feature line, we save it for the next lines
      tokenFeature = m[1];
      // Push the feature
      result[tokenFeature] = {
        "total":m[2],
        "used":m[3],
        "free":m[2]-m[3],
        "tokens":[]
      };
    }else{
      // Token line with vendor
      m = line.match(versionTokenRegEx);
      if (m) {
        // Save the vendor information temporarly
        vendorInfo = {
          "name" : m[4],
          "version" : m[2],
          "expiry" : m[6]
        };
      }else{
        // Token line with username and machine
        m = line.match(userTokenRegEx);
        if (m) {
          // Need the previous lines to know which licence
          result[tokenFeature].tokens.push({
            "username" : m[1],
            "machine" : m[2],
            "started" : m[7],
            "vendorname" : vendorInfo.name,
            "version" : vendorInfo.version,
            "expiry" : vendorInfo.expiry
          });
        }else{
          // Error lines
          m = line.match(errorRegEx);
          if (m) {
            result[m[1]] = {
              "total":m[3],
              "error":m[4]
            };
          }
        }
      }
    }
  }
  // Return result table
  return callback(null, result);
}

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


<commit_before>/* vim: set expandtab tabstop=2 shiftwidth=2 foldmethod=marker: */

"use strict";

var rewire = require('rewire');
var should = require('should');
var common = require(__dirname + '/mock.js');

var Child = rewire(__dirname + '/../lib/child.js');

var PROCESS;
beforeEach(function () {

  common.resetAllStatic();

  PROCESS = common.mockProcess();
  PROCESS.makesureCleanAllMessage();
  PROCESS.__getOutMessage().should.eql([]);
  PROCESS.__getEvents().should.eql([]);

  var sub = common.mockFork();
  sub.makesureCleanAllMessage;
  Child.__set__({
    'PROCESS' : PROCESS,
    'fork'  : common.mockFork,
  });
});

describe('child manager', function () {

  /* {{{ should_public_method_works_fine() */
  it('should_public_method_works_fine', function (_done) {
    var _me = Child.create(['filename.js', 'b'], {
      'listen' : __dirname + '/a.socket,,33047', 'children' : 3,
    });

    var _messages = [];
    _me.on('fork', function (pid) {
      _messages.push(['fork', pid]);
    });
    _me.on('broadcast', function (who, msg, by) {
      _messages.push(['broadcast', who, JSON.stringify(msg), by]);
    });

    var done = function () {
      _messages.should.eql([
        ['fork', 1],
        ['fork', 2],
        ['fork', 3],
        ['fork', 4],
        ['fork', 5],
        ['fork', 6],
        ['broadcast', 'FBX', '"fuck"', 1],
        ]);
      _done();
    };

    common.resetAllStatic();

    var one = _me._fork();
    one.__getArguments().should.eql({
      '0' : 'filename.js',
      '1' : ['b'],
      '2' : {
        'cwd' : __dirname, 'env' : {'test-env' : 'lalal'}
      }
    });

    _me.start();
    _me.running.should.eql(1);
    Object.keys(_me.pstatus).should.eql(['1', '2', '3']);

    _me.reload();
    _me.start();
    Object.keys(_me.dielist).should.eql(['1', '2', '3']);
    Object.keys(_me.pstatus).should.eql(['1', '2', '3', '4', '5', '6']);

    _me.broadcast(['world'], 'Obama', -1);

    var expects = [];
    for (var i = 1; i < 7; i++) {
      expects.push(JSON.stringify([i, {
        'type' : 'hello',
        'data' : ['world'],
        'from' : 'Obama',
        '_pid' : -1,
      }, null]));
    }
    one.__getGlobalMessage().should.eql(expects);

    one.emit('message');
    one.emit('message', {'data' : 1});

    /**
     * @ 心跳信息
     */
    one.emit('message', {'type' : 'heartbeat', 'data' : {'aa' : 1}});
    _me.pstatus['1'].should.have.property('aa', 1);
    _me.pstatus['1'].should.have.property('_time');

    /**
     * @ 请求广播
     */
    one.emit('message', {'type' : 'broadcast', 'data' : {'who' : 'FBX'}});
    one.emit('message', {'type' : 'broadcast', 'data' : {'who' : 'FBX', 'msg' : 'fuck'}});

    one.__getOutMessage().pop();

    /**
     * @ 请求句柄
     */
    one.emit('message', {'type' : 'ready'});
    one.__getOutMessage().pop()[0].should.eql({'type' : 'listen', 'data' : 33047});

    setTimeout(function () {
      Object.keys(_me.dielist).should.eql(['2', '3']);
      Object.keys(_me.pstatus).should.eql(['2', '3', '4', '5', '6']);
      _me.stop();
      _me.running.should.eql(0);
      done();
    }, 10);
  });
  /* }}} */

  /**
   * XXX: 这个case逻辑上还有点问题
   */
  /* {{{ should_max_fatals_works_fine() */
  it('should_max_fatals_works_fine', function (done) {

    common.resetAllStatic();

    var _me = Child.create(['a'], {
      'listen' : null, 'max_fatal_restart' : 2, 'pause_after_fatal' : 10
    });

    var _messages = [];
    _me.on('giveup', function (n, p) {
      n.should.eql(2);
      p.should.eql(10);
      _messages.push(['giveup', n, p]);
    });
    _me.on('fork', function (pid) {
      _messages.push(['fork', pid]);
    });

    var one = _me._fork();
    _messages.should.eql([['fork', 1]]);

    _me.start();

    var cpu = require('os').cpus().length;
    Object.keys(_me.pstatus).should.have.property('length', cpu);

    /**
     * exit cases:
     * 0, sigterm : [-] fork 0
     * 0, sigkill : [!] fork 1
     * 1, sigterm : [!] fork 2
     * 1, sigkill : [!] pause
     */
    for (var i = 0; i < 2; i++) {
      ['SIGTERM', 'SIGKILL'].forEach(function (s) {
        one.emit('exit', i, s);
      });
    }

    var expects = [];
    for (var i = 0; i < cpu; i++) {
      expects.push(['fork', 1 + i]);
    }

    expects.push(['fork', i++]);

    setTimeout(function () {
      //_messages.should.eql(expects);
      done();
    }, 50);
  });
  /* }}} */

});

<commit_msg>found bug in reload, only the first process in dielist will be kill<commit_after>/* vim: set expandtab tabstop=2 shiftwidth=2 foldmethod=marker: */

"use strict";

var rewire = require('rewire');
var should = require('should');
var common = require(__dirname + '/mock.js');

var Child = rewire(__dirname + '/../lib/child.js');

var PROCESS;
beforeEach(function () {

  common.resetAllStatic();

  PROCESS = common.mockProcess();
  PROCESS.makesureCleanAllMessage();
  PROCESS.__getOutMessage().should.eql([]);
  PROCESS.__getEvents().should.eql([]);

  var sub = common.mockFork();
  sub.makesureCleanAllMessage;
  Child.__set__({
    'PROCESS' : PROCESS,
    'fork'  : common.mockFork,
  });
});

describe('child manager', function () {

  /* {{{ should_public_method_works_fine() */
  it('should_public_method_works_fine', function (_done) {
    var _me = Child.create(['filename.js', 'b'], {
      'listen' : __dirname + '/a.socket,,33047', 'children' : 3,
    });

    var _messages = [];
    _me.on('fork', function (pid) {
      _messages.push(['fork', pid]);
    });
    _me.on('broadcast', function (who, msg, by) {
      _messages.push(['broadcast', who, JSON.stringify(msg), by]);
    });

    var done = function () {
      _messages.should.eql([
        ['fork', 1],
        ['fork', 2],
        ['fork', 3],
        ['fork', 4],
        ['fork', 5],
        ['fork', 6],
        ['broadcast', 'FBX', '"fuck"', 1],
        ]);
      _done();
    };

    common.resetAllStatic();

    var one = _me._fork();
    one.__getArguments().should.eql({
      '0' : 'filename.js',
      '1' : ['b'],
      '2' : {
        'cwd' : __dirname, 'env' : {'test-env' : 'lalal'}
      }
    });

    _me.start();
    _me.running.should.eql(1);
    Object.keys(_me.pstatus).should.eql(['1', '2', '3']);

    _me.reload();
    _me.start();
    Object.keys(_me.dielist).should.eql(['1', '2', '3']);
    Object.keys(_me.pstatus).should.eql(['1', '2', '3', '4', '5', '6']);

    _me.broadcast(['world'], 'Obama', -1);

    var expects = [];
    for (var i = 1; i < 7; i++) {
      expects.push(JSON.stringify([i, {
        'type' : 'hello',
        'data' : ['world'],
        'from' : 'Obama',
        '_pid' : -1,
      }, null]));
    }
    one.__getGlobalMessage().should.eql(expects);

    one.emit('message');
    one.emit('message', {'data' : 1});

    /**
     * @ 心跳信息
     */
    one.emit('message', {'type' : 'heartbeat', 'data' : {'aa' : 1}});
    _me.pstatus['1'].should.have.property('aa', 1);
    _me.pstatus['1'].should.have.property('_time');

    /**
     * @ 请求广播
     */
    one.emit('message', {'type' : 'broadcast', 'data' : {'who' : 'FBX'}});
    one.emit('message', {'type' : 'broadcast', 'data' : {'who' : 'FBX', 'msg' : 'fuck'}});

    one.__getOutMessage().pop();

    /**
     * @ 请求句柄
     */
    one.emit('message', {'type' : 'ready'});
    one.__getOutMessage().pop()[0].should.eql({'type' : 'listen', 'data' : 33047});

    /**
     * @ 模拟两个进程ready，dielist应该至少杀掉2个
     */
    one.emit('message', {'type' : 'ready'});
    setTimeout(function () {
      Object.keys(_me.dielist).length.should.below(2);
      Object.keys(_me.pstatus).should.eql(['2', '3', '4', '5', '6']);
      _me.stop();
      _me.running.should.eql(0);
      done();
    }, 10);
  });
  /* }}} */

  /**
   * XXX: 这个case逻辑上还有点问题
   */
  /* {{{ should_max_fatals_works_fine() */
  it('should_max_fatals_works_fine', function (done) {

    common.resetAllStatic();

    var _me = Child.create(['a'], {
      'listen' : null, 'max_fatal_restart' : 2, 'pause_after_fatal' : 10
    });

    var _messages = [];
    _me.on('giveup', function (n, p) {
      n.should.eql(2);
      p.should.eql(10);
      _messages.push(['giveup', n, p]);
    });
    _me.on('fork', function (pid) {
      _messages.push(['fork', pid]);
    });

    var one = _me._fork();
    _messages.should.eql([['fork', 1]]);

    _me.start();

    var cpu = require('os').cpus().length;
    Object.keys(_me.pstatus).should.have.property('length', cpu);

    /**
     * exit cases:
     * 0, sigterm : [-] fork 0
     * 0, sigkill : [!] fork 1
     * 1, sigterm : [!] fork 2
     * 1, sigkill : [!] pause
     */
    for (var i = 0; i < 2; i++) {
      ['SIGTERM', 'SIGKILL'].forEach(function (s) {
        one.emit('exit', i, s);
      });
    }

    var expects = [];
    for (var i = 0; i < cpu; i++) {
      expects.push(['fork', 1 + i]);
    }

    expects.push(['fork', i++]);

    setTimeout(function () {
      //_messages.should.eql(expects);
      done();
    }, 50);
  });
  /* }}} */

});

<|endoftext|>


<commit_before>// Copyright 2012 The Obvious Corporation.

/*
 * This simply fetches the right version of phantom for the current platform.
 */

'use strict'

var requestProgress = require('request-progress')
var progress = require('progress')
var AdmZip = require('adm-zip')
var cp = require('child_process')
var fs = require('fs')
var helper = require('./lib/phantomjs')
var kew = require('kew')
var mkdirp = require('mkdirp')
var ncp = require('ncp')
var npmconf = require('npmconf')
var path = require('path')
var request = require('request')
var rimraf = require('rimraf')
var urlLib = require('url')
var util = require('util')
var which = require('which')

// allow to specify a completely custom download url
var customDownloadUrl = process.env.PHANTOMJS2_DOWNLOAD_URL || false
var url = process.platform === 'win32' ? 'https://github.com/gskachkov/phantomjs/releases/download/' : 'https://github.com/bprodoehl/phantomjs/releases/download/'
var systemPrefix = process.platform === 'win32' ? '-x86' : ''
var cdnUrl = process.env.PHANTOMJS_CDNURL || url
var downloadUrl = cdnUrl + helper.version + systemPrefix + '/phantomjs-' + helper.version + '-'

var originalPath = process.env.PATH

// If the process exits without going through exit(), then we did not complete.
var validExit = false

process.on('exit', function () {
  if (!validExit) {
    console.log('Install exited unexpectedly')
    exit(1)
  }
})

// NPM adds bin directories to the path, which will cause `which` to find the
// bin for this package not the actual phantomjs bin.  Also help out people who
// put ./bin on their path
process.env.PATH = helper.cleanPath(originalPath)

var libPath = path.join(__dirname, 'lib')
var pkgPath = path.join(libPath, 'phantom')
var phantomPath = null
var tmpPath = null

// If the user manually installed PhantomJS, we want
// to use the existing version.
//
// Do not re-use a manually-installed PhantomJS with
// a different version.
//
// Do not re-use an npm-installed PhantomJS, because
// that can lead to weird circular dependencies between
// local versions and global versions.
// https://github.com/Obvious/phantomjs/issues/85
// https://github.com/Medium/phantomjs/pull/184
var whichDeferred = kew.defer()
which('phantomjs', whichDeferred.makeNodeResolver())
whichDeferred.promise
  .then(function (result) {
    phantomPath = result

    // Horrible hack to avoid problems during global install. We check to see if
    // the file `which` found is our own bin script.
    if (phantomPath.indexOf(path.join('npm', 'phantomjs')) !== -1) {
      console.log('Looks like an `npm install -g` on windows; unable to check for already installed version.')
      throw new Error('Global install')
    }

    var contents = fs.readFileSync(phantomPath, 'utf8')
    if (/NPM_INSTALL_MARKER/.test(contents)) {
      console.log('Looks like an `npm install -g`; unable to check for already installed version.')
      throw new Error('Global install')
    } else {
      var checkVersionDeferred = kew.defer()
      cp.execFile(phantomPath, ['--version'], checkVersionDeferred.makeNodeResolver())
      return checkVersionDeferred.promise
    }
  })
  .then(function (stdout) {
    var version = stdout.trim()
    if (helper.version == version) {
      writeLocationFile(phantomPath);
      console.log('PhantomJS is already installed at', phantomPath + '.')
      exit(0)

    } else {
      console.log('PhantomJS detected, but wrong version', stdout.trim(), '@', phantomPath + '.')
      throw new Error('Wrong version')
    }
  })
  .fail(function (err) {
    // Trying to use a local file failed, so initiate download and install
    // steps instead.
    var npmconfDeferred = kew.defer()
    npmconf.load(npmconfDeferred.makeNodeResolver())
    return npmconfDeferred.promise
  })
  .then(function (conf) {
    tmpPath = findSuitableTempDirectory(conf)

    // Can't use a global version so start a download.

    if (customDownloadUrl) {
      downloadUrl = customDownloadUrl
    } else if (process.platform === 'linux' && process.arch === 'x64') {
      downloadUrl += 'u1404-x86_64.zip'
    } else if (process.platform === 'darwin' || process.platform === 'openbsd' || process.platform === 'freebsd') {
      downloadUrl += 'macosx.zip'
    } else if (process.platform === 'win32') {
      downloadUrl += 'win-x86.zip'
    } else {
      console.error('Unexpected platform or architecture:', process.platform, process.arch)
      exit(1)
    }

    var fileName = downloadUrl.split('/').pop()
    var downloadedFile = path.join(tmpPath, fileName)

    // Start the install.
    if (!fs.existsSync(downloadedFile)) {
      console.log('Downloading', downloadUrl)
      console.log('Saving to', downloadedFile)
      return requestBinary(getRequestOptions(conf), downloadedFile)
    } else {
      console.log('Download already available at', downloadedFile)
      return downloadedFile
    }
  })
  .then(function (downloadedFile) {
    return extractDownload(downloadedFile)
  })
  .then(function (extractedPath) {
    return copyIntoPlace(extractedPath, pkgPath)
  })
  .then(function () {
    var location = process.platform === 'win32' ?
        path.join(pkgPath, 'phantomjs.exe') :
        path.join(pkgPath, 'bin' ,'phantomjs')
    var relativeLocation = path.relative(libPath, location)
    writeLocationFile(relativeLocation)

    // Ensure executable is executable by all users
    fs.chmodSync(location, '755')

    console.log('Done. Phantomjs binary available at', location)
    exit(0)
  })
  .fail(function (err) {
    console.error('Phantom installation failed', err, err.stack)
    exit(1)
  })

function writeLocationFile(location) {
  console.log('Writing location.js file')
  if (process.platform === 'win32') {
    location = location.replace(/\\/g, '\\\\')
  }
  fs.writeFileSync(path.join(libPath, 'location.js'),
      'module.exports.location = "' + location + '"')
}

function exit(code) {
  validExit = true
  process.env.PATH = originalPath
  process.exit(code || 0)
}

function findSuitableTempDirectory(npmConf) {
  var now = Date.now()
  var candidateTmpDirs = [
    process.env.TMPDIR || process.env.TEMP || npmConf.get('tmp'),
    '/tmp',
    path.join(process.cwd(), 'tmp')
  ]

  for (var i = 0; i < candidateTmpDirs.length; i++) {
    var candidatePath = path.join(candidateTmpDirs[i], 'phantomjs')

    try {
      mkdirp.sync(candidatePath, '0777')
      // Make double sure we have 0777 permissions; some operating systems
      // default umask does not allow write by default.
      fs.chmodSync(candidatePath, '0777')
      var testFile = path.join(candidatePath, now + '.tmp')
      fs.writeFileSync(testFile, 'test')
      fs.unlinkSync(testFile)
      return candidatePath
    } catch (e) {
      console.log(candidatePath, 'is not writable:', e.message)
    }
  }

  console.error('Can not find a writable tmp directory, please report issue ' +
      'on https://github.com/Obvious/phantomjs/issues/59 with as much ' +
      'information as possible.')
  exit(1)
}

function getRequestOptions(conf) {
  var options = {
    uri: downloadUrl,
    encoding: null, // Get response as a buffer
    followRedirect: true, // The default download path redirects to a CDN URL.
    headers: {},
    strictSSL: conf.get('strict-ssl')
  }

  var proxyUrl = conf.get('https-proxy') || conf.get('http-proxy') || conf.get('proxy')
  if (proxyUrl) {

    // Print using proxy
    var proxy = urlLib.parse(proxyUrl)
    if (proxy.auth) {
      // Mask password
      proxy.auth = proxy.auth.replace(/:.*$/, ':******')
    }
    console.log('Using proxy ' + urlLib.format(proxy))

    // Enable proxy
    options.proxy = proxyUrl

    // If going through proxy, spoof the User-Agent, since may commerical proxies block blank or unknown agents in headers
    options.headers['User-Agent'] = 'curl/7.21.4 (universal-apple-darwin11.0) libcurl/7.21.4 OpenSSL/0.9.8r zlib/1.2.5'
  }

  return options
}

function requestBinary(requestOptions, filePath) {
  var deferred = kew.defer()

  var count = 0
  var notifiedCount = 0
  var writePath = filePath + '-download-' + Date.now()
  var outFile = fs.openSync(writePath, 'w')

  console.log('Receiving...')
  var bar = null
  requestProgress(request(requestOptions, function (error, response, body) {
    console.log('');
    if (!error && response.statusCode === 200) {
      fs.writeFileSync(writePath, body)
      console.log('Received ' + Math.floor(body.length / 1024) + 'K total.')
      fs.renameSync(writePath, filePath)
      deferred.resolve(filePath)

    } else if (response) {
      console.error('Error requesting archive.\n' +
          'Status: ' + response.statusCode + '\n' +
          'Request options: ' + JSON.stringify(requestOptions, null, 2) + '\n' +
          'Response headers: ' + JSON.stringify(response.headers, null, 2) + '\n' +
          'Make sure your network and proxy settings are correct.\n\n' +
          'If you continue to have issues, please report this full log at ' +
          'https://github.com/Medium/phantomjs')
      exit(1)
    } else if (error && error.stack && error.stack.indexOf('SELF_SIGNED_CERT_IN_CHAIN') != -1) {
      console.error('Error making request, SELF_SIGNED_CERT_IN_CHAIN. Please read https://github.com/Medium/phantomjs#i-am-behind-a-corporate-proxy-that-uses-self-signed-ssl-certificates-to-intercept-encrypted-traffic')
      exit(1)
    } else if (error) {
      console.error('Error making request.\n' + error.stack + '\n\n' +
          'Please report this full log at https://github.com/Medium/phantomjs')
      exit(1)
    } else {
      console.error('Something unexpected happened, please report this full ' +
          'log at https://github.com/Medium/phantomjs')
      exit(1)
    }
  })).on('progress', function (state) {
    if (!bar) {
      bar = new progress('  [:bar] :percent :etas', {total: state.total, width: 40})
    }
    bar.curr = state.received
    bar.tick(0)
  })

  return deferred.promise
}

function extractDownload(filePath) {
  var deferred = kew.defer()
  // extract to a unique directory in case multiple processes are
  // installing and extracting at once
  var extractedPath = filePath + '-extract-' + Date.now()
  var options = {cwd: extractedPath}

  mkdirp.sync(extractedPath, '0777')
  // Make double sure we have 0777 permissions; some operating systems
  // default umask does not allow write by default.
  fs.chmodSync(extractedPath, '0777')

  if (filePath.substr(-4) === '.zip') {
    console.log('Extracting zip contents')

    try {
      var zip = new AdmZip(filePath)
      zip.extractAllTo(extractedPath, true)
      deferred.resolve(extractedPath)
    } catch (err) {
      console.error('Error extracting zip')
      deferred.reject(err)
    }

  } else {
    console.log('Extracting tar contents (via spawned process)')
    cp.execFile('tar', ['jxf', filePath], options, function (err, stdout, stderr) {
      if (err) {
        console.error('Error extracting archive')
        deferred.reject(err)
      } else {
        deferred.resolve(extractedPath)
      }
    })
  }
  return deferred.promise
}

function copyIntoPlace(extractedPath, targetPath) {
  console.log('Removing', targetPath)
  return kew.nfcall(rimraf, targetPath).then(function () {
    // Look for the extracted directory, so we can rename it.
    var files = fs.readdirSync(extractedPath)
    for (var i = 0; i < files.length; i++) {
      var file = path.join(extractedPath, files[i])
      if (fs.statSync(file).isDirectory() && file.indexOf(helper.version) != -1) {
        console.log('Copying extracted folder', file, '->', targetPath)
        return kew.nfcall(ncp, file, targetPath)
      }
    }

    console.log('Could not find extracted file', files)
    throw new Error('Could not find extracted file')
  })
  .then(function () {
    // Cleanup extracted directory after it's been copied
    console.log('Removing', extractedPath)
    return kew.nfcall(rimraf, extractedPath).fail(function (e) {
      // Swallow the error quietly.
      console.warn(e)
      console.warn('Unable to remove temporary files at "' + extractedPath +
          '", see https://github.com/Obvious/phantomjs/issues/108 for details.')
    })
  })
}
<commit_msg>add ability to specify PHANTOMJS2_VERSION via env variable<commit_after>// Copyright 2012 The Obvious Corporation.

/*
 * This simply fetches the right version of phantom for the current platform.
 */

'use strict'

var requestProgress = require('request-progress')
var progress = require('progress')
var AdmZip = require('adm-zip')
var cp = require('child_process')
var fs = require('fs')
var helper = require('./lib/phantomjs')
var kew = require('kew')
var mkdirp = require('mkdirp')
var ncp = require('ncp')
var npmconf = require('npmconf')
var path = require('path')
var request = require('request')
var rimraf = require('rimraf')
var urlLib = require('url')
var util = require('util')
var which = require('which')

// allow to specify a completely custom download url
var customDownloadUrl = process.env.PHANTOMJS2_DOWNLOAD_URL || false
var url = process.platform === 'win32' ? 'https://github.com/gskachkov/phantomjs/releases/download/' : 'https://github.com/bprodoehl/phantomjs/releases/download/'
var systemPrefix = process.platform === 'win32' ? '-x86' : ''
var cdnUrl = process.env.PHANTOMJS_CDNURL || url
var phantomVersion = process.env.PHANTOMJS2_VERSION || helper.version
var downloadUrl = cdnUrl + phantomVersion + systemPrefix + '/phantomjs-' + phantomVersion + '-'

var originalPath = process.env.PATH

// If the process exits without going through exit(), then we did not complete.
var validExit = false

process.on('exit', function () {
  if (!validExit) {
    console.log('Install exited unexpectedly')
    exit(1)
  }
})

// NPM adds bin directories to the path, which will cause `which` to find the
// bin for this package not the actual phantomjs bin.  Also help out people who
// put ./bin on their path
process.env.PATH = helper.cleanPath(originalPath)

var libPath = path.join(__dirname, 'lib')
var pkgPath = path.join(libPath, 'phantom')
var phantomPath = null
var tmpPath = null

// If the user manually installed PhantomJS, we want
// to use the existing version.
//
// Do not re-use a manually-installed PhantomJS with
// a different version.
//
// Do not re-use an npm-installed PhantomJS, because
// that can lead to weird circular dependencies between
// local versions and global versions.
// https://github.com/Obvious/phantomjs/issues/85
// https://github.com/Medium/phantomjs/pull/184
var whichDeferred = kew.defer()
which('phantomjs', whichDeferred.makeNodeResolver())
whichDeferred.promise
  .then(function (result) {
    phantomPath = result

    // Horrible hack to avoid problems during global install. We check to see if
    // the file `which` found is our own bin script.
    if (phantomPath.indexOf(path.join('npm', 'phantomjs')) !== -1) {
      console.log('Looks like an `npm install -g` on windows; unable to check for already installed version.')
      throw new Error('Global install')
    }

    var contents = fs.readFileSync(phantomPath, 'utf8')
    if (/NPM_INSTALL_MARKER/.test(contents)) {
      console.log('Looks like an `npm install -g`; unable to check for already installed version.')
      throw new Error('Global install')
    } else {
      var checkVersionDeferred = kew.defer()
      cp.execFile(phantomPath, ['--version'], checkVersionDeferred.makeNodeResolver())
      return checkVersionDeferred.promise
    }
  })
  .then(function (stdout) {
    var version = stdout.trim()
    if (phantomVersion == version) {
      writeLocationFile(phantomPath);
      console.log('PhantomJS is already installed at', phantomPath + '.')
      exit(0)

    } else {
      console.log('PhantomJS detected, but wrong version', stdout.trim(), '@', phantomPath + '.')
      throw new Error('Wrong version')
    }
  })
  .fail(function (err) {
    // Trying to use a local file failed, so initiate download and install
    // steps instead.
    var npmconfDeferred = kew.defer()
    npmconf.load(npmconfDeferred.makeNodeResolver())
    return npmconfDeferred.promise
  })
  .then(function (conf) {
    tmpPath = findSuitableTempDirectory(conf)

    // Can't use a global version so start a download.

    if (customDownloadUrl) {
      downloadUrl = customDownloadUrl
    } else if (process.platform === 'linux' && process.arch === 'x64') {
      downloadUrl += 'u1404-x86_64.zip'
    } else if (process.platform === 'darwin' || process.platform === 'openbsd' || process.platform === 'freebsd') {
      downloadUrl += 'macosx.zip'
    } else if (process.platform === 'win32') {
      downloadUrl += 'win-x86.zip'
    } else {
      console.error('Unexpected platform or architecture:', process.platform, process.arch)
      exit(1)
    }

    var fileName = downloadUrl.split('/').pop()
    var downloadedFile = path.join(tmpPath, fileName)

    // Start the install.
    if (!fs.existsSync(downloadedFile)) {
      console.log('Downloading', downloadUrl)
      console.log('Saving to', downloadedFile)
      return requestBinary(getRequestOptions(conf), downloadedFile)
    } else {
      console.log('Download already available at', downloadedFile)
      return downloadedFile
    }
  })
  .then(function (downloadedFile) {
    return extractDownload(downloadedFile)
  })
  .then(function (extractedPath) {
    return copyIntoPlace(extractedPath, pkgPath)
  })
  .then(function () {
    var location = process.platform === 'win32' ?
        path.join(pkgPath, 'phantomjs.exe') :
        path.join(pkgPath, 'bin' ,'phantomjs')
    var relativeLocation = path.relative(libPath, location)
    writeLocationFile(relativeLocation)

    // Ensure executable is executable by all users
    fs.chmodSync(location, '755')

    console.log('Done. Phantomjs binary available at', location)
    exit(0)
  })
  .fail(function (err) {
    console.error('Phantom installation failed', err, err.stack)
    exit(1)
  })

function writeLocationFile(location) {
  console.log('Writing location.js file')
  if (process.platform === 'win32') {
    location = location.replace(/\\/g, '\\\\')
  }
  fs.writeFileSync(path.join(libPath, 'location.js'),
      'module.exports.location = "' + location + '"')
}

function exit(code) {
  validExit = true
  process.env.PATH = originalPath
  process.exit(code || 0)
}

function findSuitableTempDirectory(npmConf) {
  var now = Date.now()
  var candidateTmpDirs = [
    process.env.TMPDIR || process.env.TEMP || npmConf.get('tmp'),
    '/tmp',
    path.join(process.cwd(), 'tmp')
  ]

  for (var i = 0; i < candidateTmpDirs.length; i++) {
    var candidatePath = path.join(candidateTmpDirs[i], 'phantomjs')

    try {
      mkdirp.sync(candidatePath, '0777')
      // Make double sure we have 0777 permissions; some operating systems
      // default umask does not allow write by default.
      fs.chmodSync(candidatePath, '0777')
      var testFile = path.join(candidatePath, now + '.tmp')
      fs.writeFileSync(testFile, 'test')
      fs.unlinkSync(testFile)
      return candidatePath
    } catch (e) {
      console.log(candidatePath, 'is not writable:', e.message)
    }
  }

  console.error('Can not find a writable tmp directory, please report issue ' +
      'on https://github.com/Obvious/phantomjs/issues/59 with as much ' +
      'information as possible.')
  exit(1)
}

function getRequestOptions(conf) {
  var options = {
    uri: downloadUrl,
    encoding: null, // Get response as a buffer
    followRedirect: true, // The default download path redirects to a CDN URL.
    headers: {},
    strictSSL: conf.get('strict-ssl')
  }

  var proxyUrl = conf.get('https-proxy') || conf.get('http-proxy') || conf.get('proxy')
  if (proxyUrl) {

    // Print using proxy
    var proxy = urlLib.parse(proxyUrl)
    if (proxy.auth) {
      // Mask password
      proxy.auth = proxy.auth.replace(/:.*$/, ':******')
    }
    console.log('Using proxy ' + urlLib.format(proxy))

    // Enable proxy
    options.proxy = proxyUrl

    // If going through proxy, spoof the User-Agent, since may commerical proxies block blank or unknown agents in headers
    options.headers['User-Agent'] = 'curl/7.21.4 (universal-apple-darwin11.0) libcurl/7.21.4 OpenSSL/0.9.8r zlib/1.2.5'
  }

  return options
}

function requestBinary(requestOptions, filePath) {
  var deferred = kew.defer()

  var count = 0
  var notifiedCount = 0
  var writePath = filePath + '-download-' + Date.now()
  var outFile = fs.openSync(writePath, 'w')

  console.log('Receiving...')
  var bar = null
  requestProgress(request(requestOptions, function (error, response, body) {
    console.log('');
    if (!error && response.statusCode === 200) {
      fs.writeFileSync(writePath, body)
      console.log('Received ' + Math.floor(body.length / 1024) + 'K total.')
      fs.renameSync(writePath, filePath)
      deferred.resolve(filePath)

    } else if (response) {
      console.error('Error requesting archive.\n' +
          'Status: ' + response.statusCode + '\n' +
          'Request options: ' + JSON.stringify(requestOptions, null, 2) + '\n' +
          'Response headers: ' + JSON.stringify(response.headers, null, 2) + '\n' +
          'Make sure your network and proxy settings are correct.\n\n' +
          'If you continue to have issues, please report this full log at ' +
          'https://github.com/Medium/phantomjs')
      exit(1)
    } else if (error && error.stack && error.stack.indexOf('SELF_SIGNED_CERT_IN_CHAIN') != -1) {
      console.error('Error making request, SELF_SIGNED_CERT_IN_CHAIN. Please read https://github.com/Medium/phantomjs#i-am-behind-a-corporate-proxy-that-uses-self-signed-ssl-certificates-to-intercept-encrypted-traffic')
      exit(1)
    } else if (error) {
      console.error('Error making request.\n' + error.stack + '\n\n' +
          'Please report this full log at https://github.com/Medium/phantomjs')
      exit(1)
    } else {
      console.error('Something unexpected happened, please report this full ' +
          'log at https://github.com/Medium/phantomjs')
      exit(1)
    }
  })).on('progress', function (state) {
    if (!bar) {
      bar = new progress('  [:bar] :percent :etas', {total: state.total, width: 40})
    }
    bar.curr = state.received
    bar.tick(0)
  })

  return deferred.promise
}

function extractDownload(filePath) {
  var deferred = kew.defer()
  // extract to a unique directory in case multiple processes are
  // installing and extracting at once
  var extractedPath = filePath + '-extract-' + Date.now()
  var options = {cwd: extractedPath}

  mkdirp.sync(extractedPath, '0777')
  // Make double sure we have 0777 permissions; some operating systems
  // default umask does not allow write by default.
  fs.chmodSync(extractedPath, '0777')

  if (filePath.substr(-4) === '.zip') {
    console.log('Extracting zip contents')

    try {
      var zip = new AdmZip(filePath)
      zip.extractAllTo(extractedPath, true)
      deferred.resolve(extractedPath)
    } catch (err) {
      console.error('Error extracting zip')
      deferred.reject(err)
    }

  } else {
    console.log('Extracting tar contents (via spawned process)')
    cp.execFile('tar', ['jxf', filePath], options, function (err, stdout, stderr) {
      if (err) {
        console.error('Error extracting archive')
        deferred.reject(err)
      } else {
        deferred.resolve(extractedPath)
      }
    })
  }
  return deferred.promise
}

function copyIntoPlace(extractedPath, targetPath) {
  console.log('Removing', targetPath)
  return kew.nfcall(rimraf, targetPath).then(function () {
    // Look for the extracted directory, so we can rename it.
    var files = fs.readdirSync(extractedPath)
    for (var i = 0; i < files.length; i++) {
      var file = path.join(extractedPath, files[i])
      if (fs.statSync(file).isDirectory() && file.indexOf(phantomVersion) != -1) {
        console.log('Copying extracted folder', file, '->', targetPath)
        return kew.nfcall(ncp, file, targetPath)
      }
    }

    console.log('Could not find extracted file', files)
    throw new Error('Could not find extracted file')
  })
  .then(function () {
    // Cleanup extracted directory after it's been copied
    console.log('Removing', extractedPath)
    return kew.nfcall(rimraf, extractedPath).fail(function (e) {
      // Swallow the error quietly.
      console.warn(e)
      console.warn('Unable to remove temporary files at "' + extractedPath +
          '", see https://github.com/Obvious/phantomjs/issues/108 for details.')
    })
  })
}
<|endoftext|>


<commit_before>/*
 * name: main.js
 * Authors: Tanner Garrett, Brandon Thepvongsa
 * Description: JavaScript used to create the functionality of FolderDocs
 * TODO:
 * -Marked.js has been changed to not enclose given markup in
 tags, need
 * to create own renderer later
*/

$(document).ready(function() {
	$("#dboxButton").on("click", connectDropbox);
	//initialize context menu
	$.contextMenu({
            selector: '.context-menu-one',
            items: {
                "edit": {
                	name: "Edit DisplayText",
                	callback: function(e) {
                		var element = $(this);
                		selectAssociation(element);
                		editboxAssociation(element);
                	}
                },
                "open": {
                	name: "Open Subfolder",
                	callback: function(e) {
                		var elementGUID = $(this).attr('data-guid');
                		if(im.isAssociationAssociatedItemGrouping(elementGUID)) {
                			navigateMirror(elementGUID);
                		}
                	},
                	// Disabled if the element is a non-grouping item
                	disabled: function() {return !im.isAssociationAssociatedItemGrouping($(this).attr('data-guid')); }
                },
                "copy" : {
                	name: "Copy",
                	disabled: true
                },
                "moveUp": {
                	name: "Move Assocition Up",
                	callback: function(e) {
                		var element = $(this);
                		element.prev().before(element);
                		saveOrder();
                	},
                	// Disabled if there is no element before it in the list or if it is a non-grouping item
                	disabled: function() {return (!$(this).prev()[0] ||
                		!im.isAssociationAssociatedItemGrouping($(this).attr('data-guid'))); }
                },
                "moveDown": {
                	name: "Move Association Down",
                	callback: function(e) {
                		var element = $(this);
                		element.next().after(element);
                		saveOrder();
                	},
                	// Disabled if there is no element after it in the list or if it is a non-grouping item
                	disabled: function() {return (!$(this).next()[0] ||
                		!im.isAssociationAssociatedItemGrouping($(this).attr('data-guid'))); }
                },
                "delete": {
                	name: "Delete",
                	disabled: true
                },
                "sep1": "----------",
                "openInCloud": {
                	name: "Open in Cloud Store",
                	callback: function(e) {
                        var elementGUID = $(this).attr("data-guid");
                        var url = im.getPublicURL(elementGUID);
                        window.open(url);
                    }
                }
            }
        });
});

var
	im,
	previous,
	associations,
	dropboxClientCredentials,
	selectedAssociation,
	dropboxClient;

dropboxClientCredentials = {
	key: config.key,
	secret: config.secret
};

dropboxClient = new Dropbox.Client(dropboxClientCredentials);

dropboxClient.authDriver(new Dropbox.AuthDriver.Popup({
	receiverUrl: "http://localhost:9000/app/misc/oauth_reciever.html"
}));

var authenticatedClient = null;

function getClient() {
	return authenticatedClient;
}

// Constructs the root ItemMirror object from the root of the Dropbox.
function constructIMObject() {
	dropboxXooMLUtility = {
		fragmentURI: '/XooML2.xml',
		driverURI: 'DropboxXooMLUtility',
		dropboxClient: dropboxClient
	};
	dropboxItemUtility = {
		driverURI: 'DropboxItemUtility',
		dropboxClient: dropboxClient
	};
	mirrorSyncUtility = {
		utilityURI: 'MirrorSyncUtility'
	};
	var options = {
		groupingItemURI: "/",
		xooMLDriver: dropboxXooMLUtility,
		itemDriver: dropboxItemUtility,
		syncDriver: mirrorSyncUtility
	};
	im = new ItemMirror(options, function(error, newMirror) {
		if(error) {
			console.log(error);
		} else {
			im = newMirror
			console.log(im);
			refreshIMDisplay();
		}
	});
}

// Directs the client to Dropbox's authentication page to sign in.
function connectDropbox() {
	if(authenticatedClient) {
		console.log('Dropbox authenticated');
	} else {
		console.log('Dropbox authenticating...');
		dropboxClient.authenticate(function (error, client) {
			if(error) {
				console.log('Dropbox failed to authenticate');
			} else {
				authenticatedClient = client;
				console.log('Dropbox authenticated');
				constructIMObject();
			}
		});
	}
}

// Signs current client out of Dropbox
function disconnectDropbox() {
	dropboxClient.signOut();
}

// Deletes all elements in the display, then populates the list with paragraphs for each
// association (WiP).
function refreshIMDisplay() {

	// Hides the jumbotron if we are already connected to Dropbox
	if(getClient()) {
		$(".jumbotron").hide();
	}

	var entryDisplayName;
	$("#groupingItems").empty();
	$("#nonGroupingItems").empty();
	$("#toolbar").empty();

	// Creates the previous/back button
	$("#toolbar").append(printToolbar());

	associations = im.listAssociations();
	var length = associations.length;

	// Grab associations and organize them by type
	var groupingItems = [];
	var nonGroupingItems = [];
	for(var i = 0; i < length; i++) {
		if(im.isAssociationAssociatedItemGrouping(associations[i])) {
			groupingItems.push(associations[i]);
		} else {
			nonGroupingItems.push(associations[i]);
		}
	}

	// Prints out items in alphabetical order
	printAssociations(orderAssociations(groupingItems), $("#groupingItems"));
	printAssociations(nonGroupingItems.sort(), $("#nonGroupingItems"));

	createClickHandlers();
}

function orderAssociations(associationList) {
	var orderedItems = [];
	var nonOrderedItems = [];

	for(var i = 0; i < associationList.length; i++) {
		var guid = associationList[i];
		var placement = im.getAssociationNamespaceAttribute('order', guid, 'folder-docs');
		if(placement) {
			orderedItems[placement] = guid;
		} else {
			nonOrderedItems.push(guid);
		}
	}

	// Return an array of unorderedItems + orderedItems (in that order)
	return nonOrderedItems.concat(orderedItems);
}

function printAssociations(associationList, div) {
	for(var i = 0; i < associationList.length; i++) {
		var originalDisplayText = im.getAssociationDisplayText(associationList[i]);
		var appendingObject = associationMarkup(associationList[i]);
		div.append(appendingObject);
		$("#" + associationList[i]).data("originalDisplayText", originalDisplayText);
		console.log($("#" + associationList[i]).data("originalDisplayText"));
	}
}

// Creates the JS click handlers for the various associations and links
// Also creates the handlers for the textbox editing of associations
function createClickHandlers() {
	handleDisplaytextClicks();

	$('.assoc-textbox').on('blur', function() {
		var element = $(this);
		textboxHandler(element);
    });

	$('.assoc-textbox').keypress(function (e) {
		if(e.which == 13) {
			var element = $(this);
			textboxHandler(element);
		}
	});

	$('.assoc-textbox').live('keyup', function(e) {
		var guid = $(this).attr('id');
		var newText = $(this).val();
		$("div[data-guid='" + guid + "'] .assoc-displaytext").html(marked(newText));
	});

    $('.glyphicon-folder-open').on('click', function(e) {
        var guid = $(this).attr("data-guid");
        navigateMirror(guid);
    });

    $('.glyphicon-file').on('click', function(e) {
        var guid = $(this).parent().parent().attr("data-guid");
        var url = im.getPublicURL(guid);
        window.open(url);
    });

	$("#groupingItems").sortable({
		// placeholder: "drop-placeholder",
		stop: function() {
			saveOrder();
		}
	});

	$("#previous-link").on("click", navigatePrevious);
}

// Handles the logic and timing of the single vs double clicks on
// display text. Single clicks select the association in preperation
// to enter edit mode upon another single click, double clicks navigate
// to that itemmirror object
function handleDisplaytextClicks() {
	// $('.association-row').on('click', function() {
	// 	var element = $(this);
	// 	selectAssociation(element);
	// });
	//
	// $('.association-row').on('dblclick', function() {
	// 	var element = $(this);
	// 	var guid = element.attr('data-guid');
	// 	navigateMirror(guid);
	// });
	var DELAY = 350, clicks = 0, timer = null;
	$('.association-row').on("click", function(e) {
		clicks++;  // count clicks
		var element = $(this);
		// Check if the element has been selected already
		var alreadySelected = element.hasClass('selected-association');
		selectAssociation(element);
		if(clicks === 1) {
			timer = setTimeout(function() {
				// Single click case
				clicks = 0; // reset counter
				// If element has been selected already, open edit box
				if(alreadySelected) { editboxAssociation(element); }
			}, DELAY);
		// If element is a grouping item
		} else if(im.isAssociationAssociatedItemGrouping(element.attr('data-guid'))) {
			// Double click case
			clearTimeout(timer);    //prevent single-click action
			var element = $(this);
			var guid = element.attr('data-guid');
			navigateMirror(guid);
			clicks = 0; // reset counter
		 }})
		 .on("dblclick", function(e){
			 e.preventDefault();  // prevent default dblclick event
		 });
	}

function selectAssociation(element) {
	if(selectedAssociation) {
		selectedAssociation.removeClass('selected-association');
	}
	element.addClass('selected-association');
	selectedAssociation = element;
}

// Takes in the row element of the clicked association, selects it
// by saving the guid as the currently selected guid and highlights
// the association in view by placing a border around it.
function editboxAssociation(element) {
	if(element.hasClass('selected-association')) {
		// The clicked element is the currently selected element, let's
		// toggle into edit
		var guid = element.attr('data-guid');
		var textbox = $('#' + guid);
		$("h4[data-guid='" + guid + "']").show();
		textbox.show();
		textbox.putCursorAtEnd();
	}
}

// Handles the showing/hiding/saving functionality of the edit textareas
function textboxHandler(element) {
	var guid = element.attr('id');
	var newText = element.val();
	$("div[data-guid='" + guid + "'] .assoc-displaytext").html(marked(newText)).show();

	// Hides the "live preview" header
	$("h4[data-guid='" + guid + "']").hide();
	im.setAssociationDisplayText(guid, newText);
	saveMirror();
  element.hide();
}

// Saves the current itemMirror object
function saveMirror() {
	im.save(function(error) {
		if(error) {
			console.log('Save Error: ' + error);
		} else {
			console.log('Successfully saved.');
		}
	});
}

// Refreshes the itemMirror object
function refreshMirror() {
	im.refresh(function(error) {
		if(error) {
			console.log('Refresh error:' + error);
		}
	});
}

// Attempts to navigate and display a new itemMirror association
function navigateMirror(guid) {
	im.createItemMirrorForAssociatedGroupingItem(guid, function(error, newMirror) {
		console.log(error);

		if(!error) {
			im = newMirror;
			refreshIMDisplay();
		}
	});

}

// Prints the previous link to go back up to parent/creator
function printToolbar() {
	var result = "";
	var previous = im.getCreator();

	// Print the fragment name
	result += "
" + im.getDisplayName() + "
";

	// Print the previous link if we have one
	if(previous) {
		result += "<button type='button' class='btn btn-primary' id='previous-link'>"
		+ " Back</button>";
	}

	return result;
}

// Navigates and refreshes the display to the previous mirror
function navigatePrevious() {
	var previous = im.getCreator();

	if(previous) {
		im = previous;
		refreshIMDisplay();
	}
}

// Attempts to save the order of the associations by matching
// each associations guid with the array of guids returned on a reordering drop.
function saveOrder() {
	var displayedAssocs = $("#groupingItems").sortable("toArray", {attribute: 'data-guid'});
	// Loop through each association
	for(var i = 0; i < associations.length; i++) {
		// Loop through each association we grabbed from the drop event
		for(var k = 0; k < displayedAssocs.length; k++) {
			// Find where the guids match, k will equal the proper order placement
			// when we find a match
			if(associations[i] == displayedAssocs[k]) {
				im.setAssociationNamespaceAttribute('order', k, associations[i], 'folder-docs');
			}
		}
	}
	// After we've set all the proper namespace attributes, let's save the itemMirror
	saveMirror();
}

// Returns the markup for an association to be printed to the screen
// Differentiates between a groupingItem and nonGroupinItem via icon
function associationMarkup(guid) {
	var originalDisplayText = im.getAssociationDisplayText(guid);
	var displayTextWithMarkdown = marked(originalDisplayText);
	var functionCall = "navigateMirror(" + guid + ")";
	var markup = "
" +
	"
<textarea class='assoc-textbox form-control' rows='5' id='" + guid + "' style='display:none;'>" + originalDisplayText
	+ "</textarea>
Live preview:
" +

	// Display text area
	"
" + displayTextWithMarkdown + "

" +
	"
";

	if(im.isAssociationAssociatedItemGrouping(guid)) {
		markup += "
";
	} else {
		markup += "
";
	}

	return markup;

}

jQuery.fn.putCursorAtEnd = function() {

  return this.each(function() {

    $(this).focus()

    // If this function exists...
    if (this.setSelectionRange) {
      // ... then use it (Doesn't work in IE)

      // Double the length because Opera is inconsistent about whether a carriage return is one character or two. Sigh.
      var len = $(this).val().length * 2;

      this.setSelectionRange(len, len);

    } else {
    // ... otherwise replace the contents with itself
    // (Doesn't work in Google Chrome)

      $(this).val($(this).val());

    }

    // Scroll to the bottom, in case we're in a tall textarea
    // (Necessary for Firefox and Google Chrome)
    this.scrollTop = 999999;

  });

};
<commit_msg>added lastVisited functionality<commit_after>/*
 * name: main.js
 * Authors: Tanner Garrett, Brandon Thepvongsa
 * Description: JavaScript used to create the functionality of FolderDocs
 * TODO:
 * -Marked.js has been changed to not enclose given markup in
 tags, need
 * to create own renderer later
*/

$(document).ready(function() {
	$("#dboxButton").on("click", connectDropbox);
	//initialize context menu
	$.contextMenu({
            selector: '.context-menu-one',
            items: {
                "edit": {
                	name: "Edit DisplayText",
                	callback: function(e) {
                		var element = $(this);
                		selectAssociation(element);
                		editboxAssociation(element);
                	}
                },
                "open": {
                	name: "Open Subfolder",
                	callback: function(e) {
                		var elementGUID = $(this).attr('data-guid');
                		if(im.isAssociationAssociatedItemGrouping(elementGUID)) {
                			navigateMirror(elementGUID);
                		}
                	},
                	// Disabled if the element is a non-grouping item
                	disabled: function() {return !im.isAssociationAssociatedItemGrouping($(this).attr('data-guid')); }
                },
                "copy" : {
                	name: "Copy",
                	disabled: true
                },
                "moveUp": {
                	name: "Move Assocition Up",
                	callback: function(e) {
                		var element = $(this);
                		element.prev().before(element);
                		saveOrder();
                	},
                	// Disabled if there is no element before it in the list or if it is a non-grouping item
                	disabled: function() {return (!$(this).prev()[0] ||
                		!im.isAssociationAssociatedItemGrouping($(this).attr('data-guid'))); }
                },
                "moveDown": {
                	name: "Move Association Down",
                	callback: function(e) {
                		var element = $(this);
                		element.next().after(element);
                		saveOrder();
                	},
                	// Disabled if there is no element after it in the list or if it is a non-grouping item
                	disabled: function() {return (!$(this).next()[0] ||
                		!im.isAssociationAssociatedItemGrouping($(this).attr('data-guid'))); }
                },
                "delete": {
                	name: "Delete",
                	disabled: true
                },
                "sep1": "----------",
                "openInCloud": {
                	name: "Open in Cloud Store",
                	callback: function(e) {
                        var elementGUID = $(this).attr("data-guid");
                        var url = im.getPublicURL(elementGUID);
                        window.open(url);
                    }
                }
            }
        });
});

var
	im,
    rootMirror,
	previous,
	associations,
	dropboxClientCredentials,
	selectedAssociation,
	dropboxClient;

dropboxClientCredentials = {
	key: config.key,
	secret: config.secret
};

dropboxClient = new Dropbox.Client(dropboxClientCredentials);

dropboxClient.authDriver(new Dropbox.AuthDriver.Popup({
	receiverUrl: "http://localhost:9000/app/misc/oauth_reciever.html"
}));

var authenticatedClient = null;

function getClient() {
	return authenticatedClient;
}

// Constructs the root ItemMirror object from the root of the Dropbox.
function constructIMObject(pathURI) {
	dropboxXooMLUtility = {
		fragmentURI: '/XooML2.xml',
		driverURI: 'DropboxXooMLUtility',
		dropboxClient: dropboxClient
	};
	dropboxItemUtility = {
		driverURI: 'DropboxItemUtility',
		dropboxClient: dropboxClient
	};
	mirrorSyncUtility = {
		utilityURI: 'MirrorSyncUtility'
	};
	var options = {
		groupingItemURI: pathURI,
		xooMLDriver: dropboxXooMLUtility,
		itemDriver: dropboxItemUtility,
		syncDriver: mirrorSyncUtility
	};
	im = new ItemMirror(options, function(error, newMirror) {
		if(error) {
			console.log(error);
		} else {

            if(pathURI == "/") {
                handleLastNavigated(newMirror);
            }

            im = newMirror;
            refreshIMDisplay();
		}
	});
}

// Called upon the successful (re)authentication of a user.
function handleLastNavigated(newMirror) {
    rootMirror = newMirror;

    var lastVisited = im.getFragmentNamespaceAttribute('lastVisited', 'folder-docs');
    console.log("lastVisited: " + lastVisited);

    if(lastVisited && lastVisited != "/") {
        constructIMObject(lastVisited);
    }
}

// Directs the client to Dropbox's authentication page to sign in.
function connectDropbox() {
	if(authenticatedClient) {
		console.log('Dropbox authenticated');
	} else {
		console.log('Dropbox authenticating...');
		dropboxClient.authenticate(function (error, client) {
			if(error) {
				console.log('Dropbox failed to authenticate');
			} else {
				authenticatedClient = client;
				console.log('Dropbox authenticated');
				constructIMObject("/");
			}
		});
	}
}

// Signs current client out of Dropbox
function disconnectDropbox() {
	dropboxClient.signOut();
}

// Deletes all elements in the display, then populates the list with paragraphs for each
// association (WiP).
function refreshIMDisplay() {

	// Hides the jumbotron if we are already connected to Dropbox
	if(getClient()) {
		$(".jumbotron").hide();
	}

    // Save the rootMirror lastvisited fragment
    rootMirror.setFragmentNamespaceAttribute('lastVisited', im.getURIforItemDescribed(), 'folder-docs');
    console.log("after set: " + rootMirror.getFragmentNamespaceAttribute('lastVisited', 'folder-docs'));
    rootMirror.save(function(error) {
        if(error) {
            console.log('Save Error: ' + error);
        } else {
            console.log('Successfully saved.');
        }
    });

	var entryDisplayName;
	$("#groupingItems").empty();
	$("#nonGroupingItems").empty();
	$("#toolbar").empty();

	// Creates the previous/back button
	$("#toolbar").append(printToolbar());

	associations = im.listAssociations();
	var length = associations.length;

	// Grab associations and organize them by type
	var groupingItems = [];
	var nonGroupingItems = [];
	for(var i = 0; i < length; i++) {
		if(im.isAssociationAssociatedItemGrouping(associations[i])) {
			groupingItems.push(associations[i]);
		} else {
			nonGroupingItems.push(associations[i]);
		}
	}

	// Prints out items in alphabetical order
	printAssociations(orderAssociations(groupingItems), $("#groupingItems"));
	printAssociations(nonGroupingItems.sort(), $("#nonGroupingItems"));

	createClickHandlers();
}

function orderAssociations(associationList) {
	var orderedItems = [];
	var nonOrderedItems = [];

	for(var i = 0; i < associationList.length; i++) {
		var guid = associationList[i];
		var placement = im.getAssociationNamespaceAttribute('order', guid, 'folder-docs');
		if(placement) {
			orderedItems[placement] = guid;
		} else {
			nonOrderedItems.push(guid);
		}
	}

	// Return an array of unorderedItems + orderedItems (in that order)
	return nonOrderedItems.concat(orderedItems);
}

function printAssociations(associationList, div) {
	for(var i = 0; i < associationList.length; i++) {
		var originalDisplayText = im.getAssociationDisplayText(associationList[i]);
		var appendingObject = associationMarkup(associationList[i]);
		div.append(appendingObject);
		$("#" + associationList[i]).data("originalDisplayText", originalDisplayText);
	}
}

// Creates the JS click handlers for the various associations and links
// Also creates the handlers for the textbox editing of associations
function createClickHandlers() {
	handleDisplaytextClicks();

	$('.assoc-textbox').on('blur', function() {
		var element = $(this);
		textboxHandler(element);
    });

	$('.assoc-textbox').keypress(function (e) {
		if(e.which == 13) {
			var element = $(this);
			textboxHandler(element);
		}
	});

	$('.assoc-textbox').live('keyup', function(e) {
		var guid = $(this).attr('id');
		var newText = $(this).val();
		$("div[data-guid='" + guid + "'] .assoc-displaytext").html(marked(newText));
	});

    $('.glyphicon-folder-open').on('click', function(e) {
        var guid = $(this).attr("data-guid");
        navigateMirror(guid);
    });

    $('.glyphicon-file').on('click', function(e) {
        var guid = $(this).parent().parent().attr("data-guid");
        var url = im.getPublicURL(guid);
        window.open(url);
    });

	$("#groupingItems").sortable({
		// placeholder: "drop-placeholder",
		stop: function() {
			saveOrder();
		}
	});

	$("#previous-link").on("click", navigatePrevious);

    $("#root-link").on("click", navigateRoot);
}

// Handles the logic and timing of the single vs double clicks on
// display text. Single clicks select the association in preperation
// to enter edit mode upon another single click, double clicks navigate
// to that itemmirror object
function handleDisplaytextClicks() {
	var DELAY = 350, clicks = 0, timer = null;
	$('.association-row').on("click", function(e) {
		clicks++;  // count clicks
		var element = $(this);
		// Check if the element has been selected already
		var alreadySelected = element.hasClass('selected-association');
		selectAssociation(element);
		if(clicks === 1) {
			timer = setTimeout(function() {
				// Single click case
				clicks = 0; // reset counter
				// If element has been selected already, open edit box
				if(alreadySelected) { editboxAssociation(element); }
			}, DELAY);
		// If element is a grouping item
		} else if(im.isAssociationAssociatedItemGrouping(element.attr('data-guid'))) {
			// Double click case
			clearTimeout(timer);    //prevent single-click action
			var element = $(this);
			var guid = element.attr('data-guid');
			navigateMirror(guid);
			clicks = 0; // reset counter
		 }})
		 .on("dblclick", function(e){
			 e.preventDefault();  // prevent default dblclick event
		 });
	}

function selectAssociation(element) {
	if(selectedAssociation) {
		selectedAssociation.removeClass('selected-association');
	}
	element.addClass('selected-association');
	selectedAssociation = element;
}

// Takes in the row element of the clicked association, selects it
// by saving the guid as the currently selected guid and highlights
// the association in view by placing a border around it.
function editboxAssociation(element) {
	if(element.hasClass('selected-association')) {
		// The clicked element is the currently selected element, let's
		// toggle into edit
		var guid = element.attr('data-guid');
		var textbox = $('#' + guid);
		$("h4[data-guid='" + guid + "']").show();
		textbox.show();
		textbox.putCursorAtEnd();
	}
}

// Handles the showing/hiding/saving functionality of the edit textareas
function textboxHandler(element) {
	var guid = element.attr('id');
	var newText = element.val();
	$("div[data-guid='" + guid + "'] .assoc-displaytext").html(marked(newText)).show();

	// Hides the "live preview" header
	$("h4[data-guid='" + guid + "']").hide();
	im.setAssociationDisplayText(guid, newText);
	saveMirror();
  element.hide();
}

// Saves the current itemMirror object
function saveMirror() {
	im.save(function(error) {
		if(error) {
			console.log('Save Error: ' + error);
		} else {
			console.log('Successfully saved.');
		}
	});

}

// Refreshes the itemMirror object
function refreshMirror() {
	im.refresh(function(error) {
		if(error) {
			console.log('Refresh error:' + error);
		}
	});
}

// Attempts to navigate and display a new itemMirror association
function navigateMirror(guid) {
	im.createItemMirrorForAssociatedGroupingItem(guid, function(error, newMirror) {
		console.log(error);

		if(!error) {
			im = newMirror;
            refreshIMDisplay();
		}
	});

}

// Navigates and refreshes the display to the previous mirror
function navigatePrevious() {
    var previous = im.getCreator();

    if(previous) {
        im = previous;
        refreshIMDisplay();
    }
}

// Navigates to the root mirror
function navigateRoot() {
    if(rootMirror) {
        im = rootMirror;
        refreshIMDisplay();
    }

}

// Prints the previous link to go back up to parent/creator
function printToolbar() {
	var result = "";
	var previous = im.getCreator();

	// Print the fragment name
	result += "
" + im.getDisplayName() + "
";

    result += "<button type='button' class='btn btn-primary' id='root-link'>"
        + " Home</button>";
	// Print the previous link if we have one
	if(previous) {
		result += "<button type='button' class='btn btn-primary' id='previous-link'>"
		+ " Back</button>";
	}

	return result;
}

// Attempts to save the order of the associations by matching
// each associations guid with the array of guids returned on a reordering drop.
function saveOrder() {
	var displayedAssocs = $("#groupingItems").sortable("toArray", {attribute: 'data-guid'});
	// Loop through each association
	for(var i = 0; i < associations.length; i++) {
		// Loop through each association we grabbed from the drop event
		for(var k = 0; k < displayedAssocs.length; k++) {
			// Find where the guids match, k will equal the proper order placement
			// when we find a match
			if(associations[i] == displayedAssocs[k]) {
				im.setAssociationNamespaceAttribute('order', k, associations[i], 'folder-docs');
			}
		}
	}
	// After we've set all the proper namespace attributes, let's save the itemMirror
	saveMirror();
}

// Returns the markup for an association to be printed to the screen
// Differentiates between a groupingItem and nonGroupinItem via icon
function associationMarkup(guid) {
	var originalDisplayText = im.getAssociationDisplayText(guid);
	var displayTextWithMarkdown = marked(originalDisplayText);
	var functionCall = "navigateMirror(" + guid + ")";
	var markup = "
" +
	"
<textarea class='assoc-textbox form-control' rows='5' id='" + guid + "' style='display:none;'>" + originalDisplayText
	+ "</textarea>
Live preview:
" +

	// Display text area
	"
" + displayTextWithMarkdown + "

" +
	"
";

	if(im.isAssociationAssociatedItemGrouping(guid)) {
		markup += "
";
	} else {
		markup += "
";
	}

	return markup;

}

jQuery.fn.putCursorAtEnd = function() {

  return this.each(function() {

    $(this).focus()

    // If this function exists...
    if (this.setSelectionRange) {
      // ... then use it (Doesn't work in IE)

      // Double the length because Opera is inconsistent about whether a carriage return is one character or two. Sigh.
      var len = $(this).val().length * 2;

      this.setSelectionRange(len, len);

    } else {
    // ... otherwise replace the contents with itself
    // (Doesn't work in Google Chrome)

      $(this).val($(this).val());

    }

    // Scroll to the bottom, in case we're in a tall textarea
    // (Necessary for Firefox and Google Chrome)
    this.scrollTop = 999999;

  });

};
<|endoftext|>