<commit_before>/*jslint indent: 2, nomen: true, maxlen: 120, sloppy: true, vars: true, white: true, plusplus: true, stupid: true */
/*global module, require, exports, ArangoAgency, SYS_TEST_PORT */

////////////////////////////////////////////////////////////////////////////////
/// @brief Cluster kickstarting functionality using dispatchers
///
/// @file js/server/modules/org/arangodb/cluster/kickstarter.js
///
/// DISCLAIMER
///
/// Copyright 2014 triagens GmbH, Cologne, Germany
///
/// Licensed under the Apache License, Version 2.0 (the "License");
/// you may not use this file except in compliance with the License.
/// You may obtain a copy of the License at
///
///     http://www.apache.org/licenses/LICENSE-2.0
///
/// Unless required by applicable law or agreed to in writing, software
/// distributed under the License is distributed on an "AS IS" BASIS,
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
/// See the License for the specific language governing permissions and
/// limitations under the License.
///
/// Copyright holder is triAGENS GmbH, Cologne, Germany
///
/// @author Max Neunhoeffer
/// @author Copyright 2014, triAGENS GmbH, Cologne, Germany
////////////////////////////////////////////////////////////////////////////////

// -----------------------------------------------------------------------------
// --SECTION--                                         Kickstarter functionality
// -----------------------------------------------------------------------------

var download = require("internal").download;
var executeExternal = require("internal").executeExternal;
var killExternal = require("internal").killExternal;

var fs = require("fs");
var wait = require("internal").wait;

var exchangePort = require("org/arangodb/cluster/planner").exchangePort;

var print = require("internal").print;

var launchActions = {};
var shutdownActions = {};

function getAddrPort (endpoint) {
  var pos = endpoint.indexOf("://");
  if (pos !== -1) {
    return endpoint.substr(pos+3);
  }
  return endpoint;
}

function getAddr (endpoint) {
  var addrPort = getAddrPort(endpoint);
  var pos = addrPort.indexOf(":");
  if (pos !== -1) {
    return addrPort.substr(0,pos);
  }
  return addrPort;
}

function getPort (endpoint) {
  var pos = endpoint.lastIndexOf(":");
  if (pos !== -1) {
    return parseInt(endpoint.substr(pos+1),10);
  }
  return 8529;
}

function encode (st) {
  var st2 = "";
  var i;
  for (i = 0; i < st.length; i++) {
    if (st[i] === "_") {
      st2 += "@U";
    }
    else if (st[i] === "@") {
      st2 += "@@";
    }
    else {
      st2 += st[i];
    }
  }
  return encodeURIComponent(st2);
}

function sendToAgency (agencyURL, path, obj) {
  var res;
  var body;
  print("Sending ",path," to agency...");
  if (typeof obj === "string") {
    var count = 0;
    while (count++ <= 2) {
      body = "value="+encodeURIComponent(obj);
      res = download(agencyURL+path,body,
          {"method":"PUT", "followRedirects": true,
           "headers": { "Content-Type": "application/x-www-form-urlencoded"}});
      if (res.code === 201) {
        return true;
      }
    }
    return res;
  }
  if (typeof obj !== "object") {
    return "Strange object found: not a string or object";
  }
  var keys = Object.keys(obj);
  var i;
  if (keys.length !== 0) {
    for (i = 0; i < keys.length; i++) {
      res = sendToAgency (agencyURL, path+"/"+encode(keys[i]), obj[keys[i]]);
      if (res !== true) {
        return res;
      }
    }
    return true;
  }
  // Create a directory
  var count2 = 0;
  while (count2++ <= 2) {
    body = "dir=true";
    res = download(agencyURL+path,body,
          {"method": "PUT", "followRedirects": true,
           "headers": { "Content-Type": "application/x-www-form-urlencoded"}});
    if (res.code === 201) {
      return true;
    }
  }
  return res;
}

launchActions.startAgent = function (dispatchers, cmd, isRelaunch) {
  var agentDataDir = fs.join(cmd.dataPath,
                             "agent"+cmd.agencyPrefix+cmd.extPort);
  if (!isRelaunch) {
    if (fs.exists(agentDataDir)) {
      fs.removeDirectoryRecursive(agentDataDir,true);
    }
  }
  var args = ["-data-dir", agentDataDir,
              "-name", "agent"+cmd.agencyPrefix+cmd.extPort,
              "-bind-addr", (cmd.onlyLocalhost ? "127.0.0.1:"
                                               : "0.0.0.0:")+cmd.extPort,
              "-addr", getAddrPort(
                          exchangePort(dispatchers[cmd.dispatcher].endpoint,
                                       cmd.extPort)),
              "-peer-bind-addr", (cmd.onlyLocalhost ? "127.0.0.1:"
                                                    : "0.0.0.0:")+cmd.intPort,
              "-peer-addr", getAddrPort(
                          exchangePort(dispatchers[cmd.dispatcher].endpoint,
                                       cmd.intPort)) ];
  var i;
  if (cmd.peers.length > 0) {
    args.push("-peers");
    var st = getAddrPort(cmd.peers[0]);
    for (i = 1; i < cmd.peers.length; i++) {
      st = st + "," + getAddrPort(cmd.peers[i]);
    }
    args.push(getAddrPort(cmd.peers[0]));
  }
  var pid = executeExternal(cmd.agentPath, args);
  var res;
  while (true) {
    wait(0.5);   // Wait a bit to give it time to startup
    res = download("http://localhost:"+cmd.extPort+"/v2/keys/");
    if (res.code === 200) {
      return {"error":false, "isAgent": true, "pid": pid};
    }
  }
};

launchActions.sendConfiguration = function (dispatchers, cmd, isRelaunch) {
  if (isRelaunch) {
    // nothing to do here
    print("Waiting 10 seconds for agency to come alive...");
    wait(10);
    return {"error":false, "isSendConfiguration": true};
  }
  var url = "http://"+getAddrPort(cmd.agency.endpoints[0])+"/v2/keys";
  var res = sendToAgency(url, "", cmd.data);
  if (res === true) {
    return {"error":false, "isSendConfiguration": true};
  }
  return {"error":true, "isSendConfiguration": true, "suberror": res};
};

launchActions.startServers = function (dispatchers, cmd, isRelaunch) {
  var url = "http://"+getAddrPort(cmd.agency.endpoints[0])+"/v2/keys/"+
            cmd.agency.agencyPrefix+"/";
  print("Downloading ",url+"Launchers/"+cmd.name);
  var res = download(url+"Launchers/"+cmd.name,"",{method:"GET",
                                                   followRedirects:true});
  if (res.code !== 200) {
    return {"error": true, "isStartServers": true, "suberror": res};
  }
  var body = JSON.parse( res.body );
  var info = JSON.parse(body.node.value);
  var id,ep,args,pids,port;

  print("Starting servers...");
  var i;
  var servers = info.DBservers.concat(info.Coordinators);
  pids = [];
  for (i = 0; i < servers.length; i++) {
    id = servers[i];
    print("Downloading ",url+"Target/MapIDToEndpoint/"+id);
    res = download(url+"Target/MapIDToEndpoint/"+id);
    if (res.code !== 200) {
      return {"error": true, "pids": pids,
              "isStartServers": true, "suberror": res};
    }
    print("Starting server ",id);
    body = JSON.parse(res.body);
    ep = JSON.parse(body.node.value);
    port = getPort(ep);
    args = ["--cluster.my-id", id,
            "--cluster.agency-prefix", cmd.agency.agencyPrefix,
            "--cluster.agency-endpoint", cmd.agency.endpoints[0],
            "--server.endpoint"];
    if (cmd.onlyLocalhost) {
      args.push("tcp://127.0.0.1:"+port);
    }
    else {
      args.push("tcp://0.0.0.0:"+port);
    }
    args.push("--log.file");
    var logfile = fs.join(cmd.dataPath,"log-"+cmd.agency.agencyPrefix+"-"+id);
    args.push(logfile);
    if (!isRelaunch) {
      if (fs.exists(logfile)) {
        fs.remove(logfile);
      }
    }
    var datadir = fs.join(cmd.dataPath,"data-"+cmd.agency.agencyPrefix+"-"+id);
    args.push(datadir);
    if (!isRelaunch) {
      if (fs.exists(datadir)) {
        fs.removeDirectoryRecursive(datadir,true);
      }
      fs.makeDirectory(datadir);
    }
    pids.push(executeExternal(cmd.arangodPath, args));
  }
  return {"error": false, "pids": pids};
};

shutdownActions.startAgent = function (dispatchers, cmd, run) {
  print("Killing agent ", run.pid);
  killExternal(run.pid);
  return {"error": false};
};

shutdownActions.sendConfiguration = function (dispatchers, cmd, run) {
  print("Waiting for 10 seconds for servers before killing agency.");
  wait(10);
  return {"error": false};
};

shutdownActions.startServers = function (dispatchers, cmd, run) {
  var i;
  for (i = 0;i < run.pids.length;i++) {
    print("Killing ", run.pids[i]);
    killExternal(run.pids[i]);
  }
  return {"error": false};
};

function Kickstarter (clusterPlan, myname) {
  this.clusterPlan = clusterPlan;
  if (myname === undefined) {
    this.myname = "me";
  }
  else {
    this.myname = myname;
  }
}

Kickstarter.prototype.launch = function () {
  var clusterPlan = this.clusterPlan;
  var myname = this.myname;
  var dispatchers = clusterPlan.dispatchers;
  var cmds = clusterPlan.commands;
  var results = [];
  var cmd;

  var error = false;
  var i;
  for (i = 0; i < cmds.length; i++) {
    cmd = cmds[i];
    if (cmd.dispatcher === undefined || cmd.dispatcher === myname) {
      var res = launchActions[cmd.action](dispatchers, cmd, false);
      results.push(res);
      if (res.error === true) {
        error = true;
        break;
      }
    }
    else {
      var ep = dispatchers[cmd.dispatcher].endpoint;
      var body = JSON.stringify({ "action": "launch",
                                  "clusterPlan": {
                                        "dispatchers": dispatchers,
                                        "commands": [cmd] },
                                  "myname": cmd.dispatcher });
      var url = "http" + ep.substr(3) + "/_admin/clusterDispatch";
      var response = download(url, body, {"method": "POST"});
      if (response.code !== 200) {
        error = true;
        results.push({"error":true, "errorMessage": "bad HTTP response code",
                      "response": response});
      }
      else {
        try {
          var res = JSON.parse(response.body);
          results.push(res.runInfo[0]);
        }
        catch (err) {
          error = true;
          results.push({"error":true, "errorMessage": "exception in JSON.parse"});
        }
      }
      if (error) {
        break;
      }
    }
  }
  this.runInfo = results;
  if (error) {
    return {"error": true, "errorMessage": "some error during launch",
            "runInfo": results};
  }
  return {"error": false, "errorMessage": "none",
          "runInfo": results};
};

Kickstarter.prototype.relaunch = function () {
  var clusterPlan = this.clusterPlan;
  var myname = this.myname;
  var dispatchers = clusterPlan.dispatchers;
  var cmds = clusterPlan.commands;
  var results = [];
  var cmd;

  var error = false;
  var i;
  for (i = 0; i < cmds.length; i++) {
    cmd = cmds[i];
    if (cmd.dispatcher === undefined || cmd.dispatcher === myname) {
      var res = launchActions[cmd.action](dispatchers, cmd, true);
      results.push(res);
      if (res.error === true) {
        error = true;
        break;
      }
    }
    else {
      var ep = dispatchers[cmd.dispatcher].endpoint;
      var body = JSON.stringify({ "action": "relaunch",
                                  "clusterPlan": {
                                        "dispatchers": dispatchers,
                                        "commands": [cmd] },
                                  "myname": cmd.dispatcher });
      var url = "http" + ep.substr(3) + "/_admin/clusterDispatch";
      var response = download(url, body, {"method": "POST"});
      if (response.code !== 200) {
        error = true;
        results.push({"error":true, "errorMessage": "bad HTTP response code",
                      "response": response});
      }
      try {
        var res = JSON.parse(response.body);
        results.push(res.runInfo[0]);
      }
      catch (err) {
        results.push({"error":true, "errorMessage": "exception in JSON.parse"});
        error = true;
        break;
      }
    }
  }
  this.runInfo = results;
  if (error) {
    return {"error": true, "errorMessage": "some error during launch",
            "runInfo": results};
  }
  return {"error": false, "errorMessage": "none",
          "runInfo": results};
};

Kickstarter.prototype.shutdown = function() {
  var clusterPlan = this.clusterPlan;
  var myname = this.myname;
  var dispatchers = clusterPlan.dispatchers;
  var cmds = clusterPlan.commands;
  var runInfo = this.runInfo;
  var results = [];
  var cmd;

  var error = false;
  var i;
  for (i = cmds.length-1; i >= 0; i--) {
    cmd = cmds[i];
    var run = runInfo[i];
    if (cmd.dispatcher === undefined || cmd.dispatcher === myname) {
      var res = shutdownActions[cmd.action](dispatchers, cmd, run);
      if (res.error === true) {
        error = true;
      }
      results.push(res);
    }
    else {
      var ep = dispatchers[cmd.dispatcher].endpoint;
      var body = JSON.stringify({ "action": "shutdown",
                                  "clusterPlan": {
                                        "dispatchers": dispatchers,
                                        "commands": [cmd] },
                                  "runInfo": [run],
                                  "myname": cmd.dispatcher });
      var url = "http" + ep.substr(3) + "/_admin/clusterDispatch";
      var response = download(url, body, {"method": "POST"});
      if (response.code !== 200) {
        error = true;
        results.push({"error":true, "errorMessage": "bad HTTP response code",
                      "response": response});
      }
      else {
        try {
          var res = JSON.parse(response.body);
          results.push(res.results[0]);
        }
        catch (err) {
          results.push({"error":true,
                        "errorMessage": "exception in JSON.parse"});
          error = true;
        }
      }
    }
  }
  results = results.reverse();
  if (error) {
    return {"error": true, "errorMessage": "some error during shutdown",
            "results": results};
  }
  return {"error": false, "errorMessage": "none", "results": results};
};

Kickstarter.prototype.isHealthy = function() {
  throw "not yet implemented";
};

Kickstarter.prototype.checkDispatchers = function() {
  // Checks that all dispatchers are active, if none is configured,
  // a local one is started.
  throw "not yet implemented";
};

exports.Kickstarter = Kickstarter;

// -----------------------------------------------------------------------------
// --SECTION--                                                       END-OF-FILE
// -----------------------------------------------------------------------------

/// Local Variables:
/// mode: outline-minor
/// outline-regexp: "/// @brief\\|/// @addtogroup\\|/// @page\\|// --SECTION--\\|/// @\\}\\|/\\*jslint"
/// End:
<commit_msg>jslint<commit_after>/*jslint indent: 2, nomen: true, maxlen: 120, sloppy: true, vars: true, white: true, plusplus: true, stupid: true */
/*global module, require, exports, ArangoAgency, SYS_TEST_PORT */

////////////////////////////////////////////////////////////////////////////////
/// @brief Cluster kickstarting functionality using dispatchers
///
/// @file js/server/modules/org/arangodb/cluster/kickstarter.js
///
/// DISCLAIMER
///
/// Copyright 2014 triagens GmbH, Cologne, Germany
///
/// Licensed under the Apache License, Version 2.0 (the "License");
/// you may not use this file except in compliance with the License.
/// You may obtain a copy of the License at
///
///     http://www.apache.org/licenses/LICENSE-2.0
///
/// Unless required by applicable law or agreed to in writing, software
/// distributed under the License is distributed on an "AS IS" BASIS,
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
/// See the License for the specific language governing permissions and
/// limitations under the License.
///
/// Copyright holder is triAGENS GmbH, Cologne, Germany
///
/// @author Max Neunhoeffer
/// @author Copyright 2014, triAGENS GmbH, Cologne, Germany
////////////////////////////////////////////////////////////////////////////////

// -----------------------------------------------------------------------------
// --SECTION--                                         Kickstarter functionality
// -----------------------------------------------------------------------------

var download = require("internal").download;
var executeExternal = require("internal").executeExternal;
var killExternal = require("internal").killExternal;

var fs = require("fs");
var wait = require("internal").wait;

var exchangePort = require("org/arangodb/cluster/planner").exchangePort;

var print = require("internal").print;

var launchActions = {};
var shutdownActions = {};

function getAddrPort (endpoint) {
  var pos = endpoint.indexOf("://");
  if (pos !== -1) {
    return endpoint.substr(pos+3);
  }
  return endpoint;
}

function getAddr (endpoint) {
  var addrPort = getAddrPort(endpoint);
  var pos = addrPort.indexOf(":");
  if (pos !== -1) {
    return addrPort.substr(0,pos);
  }
  return addrPort;
}

function getPort (endpoint) {
  var pos = endpoint.lastIndexOf(":");
  if (pos !== -1) {
    return parseInt(endpoint.substr(pos+1),10);
  }
  return 8529;
}

function encode (st) {
  var st2 = "";
  var i;
  for (i = 0; i < st.length; i++) {
    if (st[i] === "_") {
      st2 += "@U";
    }
    else if (st[i] === "@") {
      st2 += "@@";
    }
    else {
      st2 += st[i];
    }
  }
  return encodeURIComponent(st2);
}

function sendToAgency (agencyURL, path, obj) {
  var res;
  var body;
  print("Sending ",path," to agency...");
  if (typeof obj === "string") {
    var count = 0;
    while (count++ <= 2) {
      body = "value="+encodeURIComponent(obj);
      res = download(agencyURL+path,body,
          {"method":"PUT", "followRedirects": true,
           "headers": { "Content-Type": "application/x-www-form-urlencoded"}});
      if (res.code === 201) {
        return true;
      }
    }
    return res;
  }
  if (typeof obj !== "object") {
    return "Strange object found: not a string or object";
  }
  var keys = Object.keys(obj);
  var i;
  if (keys.length !== 0) {
    for (i = 0; i < keys.length; i++) {
      res = sendToAgency (agencyURL, path+"/"+encode(keys[i]), obj[keys[i]]);
      if (res !== true) {
        return res;
      }
    }
    return true;
  }
  // Create a directory
  var count2 = 0;
  while (count2++ <= 2) {
    body = "dir=true";
    res = download(agencyURL+path,body,
          {"method": "PUT", "followRedirects": true,
           "headers": { "Content-Type": "application/x-www-form-urlencoded"}});
    if (res.code === 201) {
      return true;
    }
  }
  return res;
}

launchActions.startAgent = function (dispatchers, cmd, isRelaunch) {
  var agentDataDir = fs.join(cmd.dataPath,
                             "agent"+cmd.agencyPrefix+cmd.extPort);
  if (!isRelaunch) {
    if (fs.exists(agentDataDir)) {
      fs.removeDirectoryRecursive(agentDataDir,true);
    }
  }
  var args = ["-data-dir", agentDataDir,
              "-name", "agent"+cmd.agencyPrefix+cmd.extPort,
              "-bind-addr", (cmd.onlyLocalhost ? "127.0.0.1:"
                                               : "0.0.0.0:")+cmd.extPort,
              "-addr", getAddrPort(
                          exchangePort(dispatchers[cmd.dispatcher].endpoint,
                                       cmd.extPort)),
              "-peer-bind-addr", (cmd.onlyLocalhost ? "127.0.0.1:"
                                                    : "0.0.0.0:")+cmd.intPort,
              "-peer-addr", getAddrPort(
                          exchangePort(dispatchers[cmd.dispatcher].endpoint,
                                       cmd.intPort)) ];
  var i;
  if (cmd.peers.length > 0) {
    args.push("-peers");
    var st = getAddrPort(cmd.peers[0]);
    for (i = 1; i < cmd.peers.length; i++) {
      st = st + "," + getAddrPort(cmd.peers[i]);
    }
    args.push(getAddrPort(cmd.peers[0]));
  }
  var pid = executeExternal(cmd.agentPath, args);
  var res;
  while (true) {
    wait(0.5);   // Wait a bit to give it time to startup
    res = download("http://localhost:"+cmd.extPort+"/v2/keys/");
    if (res.code === 200) {
      return {"error":false, "isAgent": true, "pid": pid};
    }
  }
};

launchActions.sendConfiguration = function (dispatchers, cmd, isRelaunch) {
  if (isRelaunch) {
    // nothing to do here
    print("Waiting 10 seconds for agency to come alive...");
    wait(10);
    return {"error":false, "isSendConfiguration": true};
  }
  var url = "http://"+getAddrPort(cmd.agency.endpoints[0])+"/v2/keys";
  var res = sendToAgency(url, "", cmd.data);
  if (res === true) {
    return {"error":false, "isSendConfiguration": true};
  }
  return {"error":true, "isSendConfiguration": true, "suberror": res};
};

launchActions.startServers = function (dispatchers, cmd, isRelaunch) {
  var url = "http://"+getAddrPort(cmd.agency.endpoints[0])+"/v2/keys/"+
            cmd.agency.agencyPrefix+"/";
  print("Downloading ",url+"Launchers/"+cmd.name);
  var res = download(url+"Launchers/"+cmd.name,"",{method:"GET",
                                                   followRedirects:true});
  if (res.code !== 200) {
    return {"error": true, "isStartServers": true, "suberror": res};
  }
  var body = JSON.parse( res.body );
  var info = JSON.parse(body.node.value);
  var id,ep,args,pids,port;

  print("Starting servers...");
  var i;
  var servers = info.DBservers.concat(info.Coordinators);
  pids = [];
  for (i = 0; i < servers.length; i++) {
    id = servers[i];
    print("Downloading ",url+"Target/MapIDToEndpoint/"+id);
    res = download(url+"Target/MapIDToEndpoint/"+id);
    if (res.code !== 200) {
      return {"error": true, "pids": pids,
              "isStartServers": true, "suberror": res};
    }
    print("Starting server ",id);
    body = JSON.parse(res.body);
    ep = JSON.parse(body.node.value);
    port = getPort(ep);
    args = ["--cluster.my-id", id,
            "--cluster.agency-prefix", cmd.agency.agencyPrefix,
            "--cluster.agency-endpoint", cmd.agency.endpoints[0],
            "--server.endpoint"];
    if (cmd.onlyLocalhost) {
      args.push("tcp://127.0.0.1:"+port);
    }
    else {
      args.push("tcp://0.0.0.0:"+port);
    }
    args.push("--log.file");
    var logfile = fs.join(cmd.dataPath,"log-"+cmd.agency.agencyPrefix+"-"+id);
    args.push(logfile);
    if (!isRelaunch) {
      if (fs.exists(logfile)) {
        fs.remove(logfile);
      }
    }
    var datadir = fs.join(cmd.dataPath,"data-"+cmd.agency.agencyPrefix+"-"+id);
    args.push(datadir);
    if (!isRelaunch) {
      if (fs.exists(datadir)) {
        fs.removeDirectoryRecursive(datadir,true);
      }
      fs.makeDirectory(datadir);
    }
    pids.push(executeExternal(cmd.arangodPath, args));
  }
  return {"error": false, "pids": pids};
};

shutdownActions.startAgent = function (dispatchers, cmd, run) {
  print("Killing agent ", run.pid);
  killExternal(run.pid);
  return {"error": false};
};

shutdownActions.sendConfiguration = function (dispatchers, cmd, run) {
  print("Waiting for 10 seconds for servers before killing agency.");
  wait(10);
  return {"error": false};
};

shutdownActions.startServers = function (dispatchers, cmd, run) {
  var i;
  for (i = 0;i < run.pids.length;i++) {
    print("Killing ", run.pids[i]);
    killExternal(run.pids[i]);
  }
  return {"error": false};
};

function Kickstarter (clusterPlan, myname) {
  this.clusterPlan = clusterPlan;
  if (myname === undefined) {
    this.myname = "me";
  }
  else {
    this.myname = myname;
  }
}

Kickstarter.prototype.launch = function () {
  var clusterPlan = this.clusterPlan;
  var myname = this.myname;
  var dispatchers = clusterPlan.dispatchers;
  var cmds = clusterPlan.commands;
  var results = [];
  var cmd;

  var error = false;
  var i;
  var res;
  for (i = 0; i < cmds.length; i++) {
    cmd = cmds[i];
    if (cmd.dispatcher === undefined || cmd.dispatcher === myname) {
      res = launchActions[cmd.action](dispatchers, cmd, false);
      results.push(res);
      if (res.error === true) {
        error = true;
        break;
      }
    }
    else {
      var ep = dispatchers[cmd.dispatcher].endpoint;
      var body = JSON.stringify({ "action": "launch",
                                  "clusterPlan": {
                                        "dispatchers": dispatchers,
                                        "commands": [cmd] },
                                  "myname": cmd.dispatcher });
      var url = "http" + ep.substr(3) + "/_admin/clusterDispatch";
      var response = download(url, body, {"method": "POST"});
      if (response.code !== 200) {
        error = true;
        results.push({"error":true, "errorMessage": "bad HTTP response code",
                      "response": response});
      }
      else {
        try {
          res = JSON.parse(response.body);
          results.push(res.runInfo[0]);
        }
        catch (err) {
          error = true;
          results.push({"error":true, "errorMessage": "exception in JSON.parse"});
        }
      }
      if (error) {
        break;
      }
    }
  }
  this.runInfo = results;
  if (error) {
    return {"error": true, "errorMessage": "some error during launch",
            "runInfo": results};
  }
  return {"error": false, "errorMessage": "none",
          "runInfo": results};
};

Kickstarter.prototype.relaunch = function () {
  var clusterPlan = this.clusterPlan;
  var myname = this.myname;
  var dispatchers = clusterPlan.dispatchers;
  var cmds = clusterPlan.commands;
  var results = [];
  var cmd;

  var error = false;
  var i;
  var res;
  for (i = 0; i < cmds.length; i++) {
    cmd = cmds[i];
    if (cmd.dispatcher === undefined || cmd.dispatcher === myname) {
      res = launchActions[cmd.action](dispatchers, cmd, true);
      results.push(res);
      if (res.error === true) {
        error = true;
        break;
      }
    }
    else {
      var ep = dispatchers[cmd.dispatcher].endpoint;
      var body = JSON.stringify({ "action": "relaunch",
                                  "clusterPlan": {
                                        "dispatchers": dispatchers,
                                        "commands": [cmd] },
                                  "myname": cmd.dispatcher });
      var url = "http" + ep.substr(3) + "/_admin/clusterDispatch";
      var response = download(url, body, {"method": "POST"});
      if (response.code !== 200) {
        error = true;
        results.push({"error":true, "errorMessage": "bad HTTP response code",
                      "response": response});
      }
      try {
        res = JSON.parse(response.body);
        results.push(res.runInfo[0]);
      }
      catch (err) {
        results.push({"error":true, "errorMessage": "exception in JSON.parse"});
        error = true;
        break;
      }
    }
  }
  this.runInfo = results;
  if (error) {
    return {"error": true, "errorMessage": "some error during launch",
            "runInfo": results};
  }
  return {"error": false, "errorMessage": "none",
          "runInfo": results};
};

Kickstarter.prototype.shutdown = function() {
  var clusterPlan = this.clusterPlan;
  var myname = this.myname;
  var dispatchers = clusterPlan.dispatchers;
  var cmds = clusterPlan.commands;
  var runInfo = this.runInfo;
  var results = [];
  var cmd;

  var error = false;
  var i;
  var res;
  for (i = cmds.length-1; i >= 0; i--) {
    cmd = cmds[i];
    var run = runInfo[i];
    if (cmd.dispatcher === undefined || cmd.dispatcher === myname) {
      res = shutdownActions[cmd.action](dispatchers, cmd, run);
      if (res.error === true) {
        error = true;
      }
      results.push(res);
    }
    else {
      var ep = dispatchers[cmd.dispatcher].endpoint;
      var body = JSON.stringify({ "action": "shutdown",
                                  "clusterPlan": {
                                        "dispatchers": dispatchers,
                                        "commands": [cmd] },
                                  "runInfo": [run],
                                  "myname": cmd.dispatcher });
      var url = "http" + ep.substr(3) + "/_admin/clusterDispatch";
      var response = download(url, body, {"method": "POST"});
      if (response.code !== 200) {
        error = true;
        results.push({"error":true, "errorMessage": "bad HTTP response code",
                      "response": response});
      }
      else {
        try {
          res = JSON.parse(response.body);
          results.push(res.results[0]);
        }
        catch (err) {
          results.push({"error":true,
                        "errorMessage": "exception in JSON.parse"});
          error = true;
        }
      }
    }
  }
  results = results.reverse();
  if (error) {
    return {"error": true, "errorMessage": "some error during shutdown",
            "results": results};
  }
  return {"error": false, "errorMessage": "none", "results": results};
};

Kickstarter.prototype.isHealthy = function() {
  throw "not yet implemented";
};

Kickstarter.prototype.checkDispatchers = function() {
  // Checks that all dispatchers are active, if none is configured,
  // a local one is started.
  throw "not yet implemented";
};

exports.Kickstarter = Kickstarter;

// -----------------------------------------------------------------------------
// --SECTION--                                                       END-OF-FILE
// -----------------------------------------------------------------------------

/// Local Variables:
/// mode: outline-minor
/// outline-regexp: "/// @brief\\|/// @addtogroup\\|/// @page\\|// --SECTION--\\|/// @\\}\\|/\\*jslint"
/// End:
<|endoftext|>


<commit_before>import autoBind from 'react-autobind';
import React from 'react';

import {bem} from 'js/bem';
import {
  QUESTION_TYPES,
  META_QUESTION_TYPES,
} from 'js/constants';

class TableMediaPreview extends React.Component {
  constructor(props) {
    super(props);

    this.state = {
      isLoading: false,
    };

    autoBind(this);
  }

  renderPreviewByType() {
    switch (this.props.questionType) {
      case QUESTION_TYPES.image.id:
        return (
          <bem.TableMediaPreview__image
            src={this.props?.mediaURL}
          />
        );
      case QUESTION_TYPES.audio.id:
      case META_QUESTION_TYPES['background-audio']:
        return (
          // TODO: make our own audio player with options for here and in place
          // of bem.BackgroundAudioPlayer
          <audio
            src={this.props?.mediaURL}
            controls
            autoplay
          />
        );
      case QUESTION_TYPES.video.id:
        return (
          <bem.TableMediaPreview__video
            src={this.props?.mediaURL}
            controls
            autoplay
          />
        );
      default:
        return (
          <label>
            {t('Unsupported media type: ##QUESTION_TYPE##').replace(
              '##QUESTION_TYPE##',
              this.props.questionType
            )}
          </label>
        );
    }
  }

  render() {
    return (
      <bem.TableMediaPreview>
        {this.props.questionType && this.renderPreviewByType()}
      </bem.TableMediaPreview>
    );
  }
}

export default TableMediaPreview;
<commit_msg>autoplay -> autoPlay<commit_after>import autoBind from 'react-autobind';
import React from 'react';

import {bem} from 'js/bem';
import {
  QUESTION_TYPES,
  META_QUESTION_TYPES,
} from 'js/constants';

class TableMediaPreview extends React.Component {
  constructor(props) {
    super(props);

    this.state = {
      isLoading: false,
    };

    autoBind(this);
  }

  renderPreviewByType() {
    switch (this.props.questionType) {
      case QUESTION_TYPES.image.id:
        return (
          <bem.TableMediaPreview__image
            src={this.props?.mediaURL}
          />
        );
      case QUESTION_TYPES.audio.id:
      case META_QUESTION_TYPES['background-audio']:
        return (
          // TODO: make our own audio player with options for here and in place
          // of bem.BackgroundAudioPlayer
          <audio
            src={this.props?.mediaURL}
            controls
            autoPlay
          />
        );
      case QUESTION_TYPES.video.id:
        return (
          <bem.TableMediaPreview__video
            src={this.props?.mediaURL}
            controls
            autoPlay
          />
        );
      default:
        return (
          <label>
            {t('Unsupported media type: ##QUESTION_TYPE##').replace(
              '##QUESTION_TYPE##',
              this.props.questionType
            )}
          </label>
        );
    }
  }

  render() {
    return (
      <bem.TableMediaPreview>
        {this.props.questionType && this.renderPreviewByType()}
      </bem.TableMediaPreview>
    );
  }
}

export default TableMediaPreview;
<|endoftext|>


<commit_before>'use strict'

const EventEmitter = require('events')
const fs = require('fs')
const join = require('path').join

class Lego extends EventEmitter {

  constructor(argv) {
    super(argv)
    this.root = process.cwd()
    // message from child process
    process.on('message', (msg) => {
      this.emit('message', msg)
      if (msg.cmd) {
        this.emit(msg.cmd, msg)
      }
    })
  }

  mount(type) {
    const wares = this.config[type] || {}
    return Object.keys(wares)
      .map(key => Object.assign({ key: key }, wares[key]))
      .filter(ware => (ware.enable || ware.enable === undefined))
      .map(ware => {
        const entryName = ware.path || ware.package || ware.key
        const entryPath = ware.package ? 'node_modules' : '/app/' + type
        const entry = require(join(this.root, entryPath, entryName))
        return {
          name: ware.key,
          entry: entry,
          options: ware
        }
      })
  }

  list(type) {
    const root = join(this.root, '/app/', type)
    if (!this.access(root)) {
      console.warn(`[lego] No app/${type} directory.`)
      return []
    }
    return fs.readdirSync(root)
      .map(item => item.replace(/\.js$/, ''))
      .map(item => ({
        name: item,
        entry: require(join(root, item))
      }))
  }

  get config() {
    const configPath = join(this.root, '/config/config.js')
    const mountPath = join(this.root, '/config/mount.js')
    if (!this.access(configPath) || !this.access(mountPath)) {
      throw new Error('[lego] Missing config/config or config/mount')
      return
    }
    return Object.assign({
        env: process.env.ENV || configPath.env || 'develop'
      },
      require(configPath),
      require(mountPath)
    )
  }

  access(path) {
    try {
      fs.accessSync(path, fs.F_OK)
      return true
    } catch (e) {
      return false
    }
  }

  // send to parent
  send(msg) {
    process.send && process.send(msg)
  }

}

module.exports = Lego
<commit_msg>配置文件可选<commit_after>'use strict'

const EventEmitter = require('events')
const fs = require('fs')
const join = require('path').join

class Lego extends EventEmitter {

  constructor(argv) {
    super(argv)
    this.root = process.cwd()
    // message from child process
    process.on('message', (msg) => {
      this.emit('message', msg)
      if (msg.cmd) {
        this.emit(msg.cmd, msg)
      }
    })
  }

  mount(type) {
    const wares = this.config[type] || {}
    return Object.keys(wares)
      .map(key => Object.assign({ key: key }, wares[key]))
      .filter(ware => (ware.enable || ware.enable === undefined))
      .map(ware => {
        const entryName = ware.path || ware.package || ware.key
        const entryPath = ware.package ? 'node_modules' : '/app/' + type
        const entry = require(join(this.root, entryPath, entryName))
        return {
          name: ware.key,
          entry: entry,
          options: ware
        }
      })
  }

  list(type) {
    const root = join(this.root, '/app/', type)
    if (!access(root)) {
      console.warn(`[lego] No app/${type} directory.`)
      return []
    }
    return fs.readdirSync(root)
      .map(item => item.replace(/\.js$/, ''))
      .map(item => ({
        name: item,
        entry: require(join(root, item))
      }))
  }

  get config() {
    const configPath = join(this.root, '/config/config.js')
    const mountPath = join(this.root, '/config/mount.js')

    const configInfo = access(configPath) ? require(configPath) : {}
    const mountInfo = access(mountPath) ? require(mountPath) : {}

    return Object.assign({
        env: process.env.ENV || configInfo.env || 'develop'
      },
      configInfo,
      mountInfo
    )
  }

  // send to parent
  send(msg) {
    process.send && process.send(msg)
  }

}

function access(path) {
  try {
    fs.accessSync(path, fs.F_OK)
    return true
  } catch (e) {
    return false
  }
}

module.exports = Lego
<|endoftext|>


<commit_before>import React from "react";
import { Link } from "react-router";
import { prefixLink } from "gatsby-helpers";

function href(p, txt) {
  const path = `${prefixLink("/")}${p}/`;
  return path;
}

const items = [
  { href: "intro", text: "Start" },
  { href: "creating-a-react-project", text: "Setup" },
  { href: "jsx", text: "JSX" },
  { href: "the-virtual-dom", text: "Virtual DOM" },
  { href: "react-elements", text: "React Elements" },
  { href: "components", text: "Components" },
  { href: "patterns", text: "Patterns" },
  { href: "additional-topics", text: "Additional Topics" },
  { href: "more-resources", text: "Resources" }
];

function MenuItems(props) {
  const { showNavClass, handleClick } = props;
  return (

        {items.map(item => {
          return (

              <Link
                onClick={handleClick}
                activeClassName="active"
                to={href(item.href)}
              >
                {item.text}
              </Link>

          );
        })}

  );
}

export default MenuItems;
<commit_msg>Update menu-items.js<commit_after>import React from "react";
import { Link } from "react-router";
import { prefixLink } from "gatsby-helpers";

function href(p, txt) {
  const path = `${prefixLink("/")}${p}/`;
  return path;
}

const items = [
  { href: "intro", text: "Start" },
  { href: "creating-a-react-project", text: "Setup" },
  { href: "jsx", text: "JSX" },
  { href: "the-virtual-dom", text: "VDOM" },
  { href: "react-elements", text: "Elements" },
  { href: "components", text: "Components" },
  { href: "patterns", text: "Patterns" },
  { href: "additional-topics", text: "Additional Topics" },
  { href: "more-resources", text: "Resources"},
  { href: "react16", text: "React 16" }
];

function MenuItems(props) {
  const { showNavClass, handleClick } = props;
  return (

        {items.map(item => {
          return (

              <Link
                onClick={handleClick}
                activeClassName="active"
                to={href(item.href)}
              >
                {item.text}
              </Link>

          );
        })}

  );
}

export default MenuItems;
<|endoftext|>


<commit_before>'use strict';

import { core } from 'metal';

/**
 * Provides access to various type validators that will return an
 * instance of Error when validation fails.
 */
const validators = {
	any: () => true,
	array: validateType('array'),
	bool: validateType('boolean'),
	func: validateType('function'),
	number: validateType('number'),
	object: validateType('object'),
	string: validateType('string'),

	/**
	 * Creates a validator that checks the values of an array against a type.
	 * @param {function()} validator Type validator to check each index against.
	 * @return {function()} Validator.
	 */
	arrayOf: function(validator) {
		return (value, name, context) => {
			if (!Array.isArray(value)) {
				return composeError('Expected an array.', name, context);
			} else {
				const testArray = value.every(
					item => {
						return !(validator(item, name) instanceof Error);
					}
				);

				if (!testArray) {
					return composeError('Expected an array of single type', name, context);
				}
			}

			return true;
		};
	},

	/**
	 * Creates a validator that compares a value to a specific class.
	 * @param {function()} expectedClass Class to check value against.
	 * @return {function()} Validator.
	 */
	instanceOf: function(expectedClass) {
		return (value, name, context) => {
			if (!(value instanceof expectedClass)) {
				return composeError(`Expected instance of ${expectedClass}`, name, context);
			}

			return true;
		};
	},

	/**
	 * Creates a validator that checks a value against a single type, null, or undefined.
	 * @param {function()} typeValidator Validator to check value against.
	 * @return {function()} Validator.
	 */
	maybe: function(typeValidator) {
		return (value, name, context) => {
			const validation = typeValidator(value, name);

			if (!core.isDef(value) || core.isNull(value) || !(validation instanceof Error)) {
				return true;
			}

			return composeError(`${validation.toString} or null`, name, context);
		};
	},

	/**
	 * Creates a validator that checks the values of an object against a type.
	 * @param {function()} typeValidator Validator to check value against.
	 * @return {function()} Validator.
	 */
	objectOf: function(typeValidator) {
		return (value, name, context) => {
			let success = true;

			for (let key in value) {
				success = !(typeValidator(value[key], null) instanceof Error);
			}

			if (!success) {
				return composeError('Expected object of one type', name, context);
			}

			return true;
		};
	},

	/**
	 * Creates a validator that checks equality against specific values.
	 * @param {!Array} arrayOfValues Array of values to check equality against.
	 * @return {function()} Validator.
	 */
	oneOf: function(arrayOfValues) {
		if (!Array.isArray(arrayOfValues)) {
			return (value, name, context) => composeError(`Expected an array, but received type '${getStateType(arrayOfValues)}'.`, name, context);
		}

		return (value, name, context) => {
			for (let i = 0; i < arrayOfValues.length; i++) {
				const oneOfValue = arrayOfValues[i];

				if (value === oneOfValue) {
					return true;
				}
			}

			return composeError('Expected one of given values.', name, context);
		};
	},

	/**
	 * Creates a validator that checks a value against multiple types and only has to pass one.
	 * @param {!Array} arrayOfTypeValidators Array of validators to check value against.
	 * @return {function()} Validator.
	 */
	oneOfType: function(arrayOfTypeValidators) {
		if (!Array.isArray(arrayOfTypeValidators)) {
			return (value, name, context) => composeError(`Expected an array, but received type '${getStateType(arrayOfTypeValidators)}'.`, name, context);
		}

		return (value, name, context) => {
			for (let i = 0; i < arrayOfTypeValidators.length; i++) {
				const validator = arrayOfTypeValidators[i];

				if (!(validator(value, name) instanceof Error)) {
					return true;
				}
			}

			return composeError('Expected one of given types.', name, context);
		};
	},

	/**
	 * Creates a validator that checks the shape of an object.
	 * @param {!Object} shape An object containing type validators for each key.
	 * @return {function()} Validator.
	 */
	shapeOf: function(shape) {
		const type = getStateType(shape);
		if (type !== 'object') {
			return (value, name, context) => composeError(`Expected an object, but received type '${type}'.`, name, context);
		}

		return (value, name, context) => {
			for (let key in shape) {
				const validator = shape[key];
				const valueForKey = value[key];

				if (validator(valueForKey, null) instanceof Error) {
					return composeError('Expected object with a specific shape', name, context);
				}
			}

			return true;
		};
	}
};

/**
 * Composes a warning a warning message.
 * @param {string} error Error message to display to console.
 * @param {?string} name Name of state property that is giving the error.
 * @param {Object} context.
 * @return {!Error} Instance of Error class.
 */
function composeError(error, name, context) {
	const componentName = context ? core.getFunctionName(context.constructor) : null;
	const parentComponent = context && context.getRenderer ? context.getRenderer().lastParentComponent_ : null;
	const parentComponentName = parentComponent ? core.getFunctionName(parentComponent.constructor) : null;

	const location = parentComponentName ? `Check render method of '${parentComponentName}'.` : '';

	return new Error(`Warning: Invalid state passed to '${name}'. ${error} Passed to '${componentName}'. ${location}`);
}

/**
 * Checks type of given value.
 * @param {*} value Any value.
 * @return {string} Type of value.
 */
function getStateType(value) {
	const stateType = typeof value;
	if (Array.isArray(value)) {
		return 'array';
	}

	return stateType;
}

/**
 * Creates a validator that checks against a specific primitive type.
 * @param {string} expectedType Type to check against.
 * @return {function()} Validator.
 */
function validateType(expectedType) {
	return (value, name, context) => {
		const type = getStateType(value);

		if (type !== expectedType) {
			return composeError(`Expected type '${expectedType}', but received type '${type}'.`, name, context);
		}

		return true;
	};
}

export default validators;
<commit_msg>Fix error message for Types.maybe, should call function<commit_after>'use strict';

import { core } from 'metal';

/**
 * Provides access to various type validators that will return an
 * instance of Error when validation fails.
 */
const validators = {
	any: () => true,
	array: validateType('array'),
	bool: validateType('boolean'),
	func: validateType('function'),
	number: validateType('number'),
	object: validateType('object'),
	string: validateType('string'),

	/**
	 * Creates a validator that checks the values of an array against a type.
	 * @param {function()} validator Type validator to check each index against.
	 * @return {function()} Validator.
	 */
	arrayOf: function(validator) {
		return (value, name, context) => {
			if (!Array.isArray(value)) {
				return composeError('Expected an array.', name, context);
			} else {
				const testArray = value.every(
					item => {
						return !(validator(item, name) instanceof Error);
					}
				);

				if (!testArray) {
					return composeError('Expected an array of single type', name, context);
				}
			}

			return true;
		};
	},

	/**
	 * Creates a validator that compares a value to a specific class.
	 * @param {function()} expectedClass Class to check value against.
	 * @return {function()} Validator.
	 */
	instanceOf: function(expectedClass) {
		return (value, name, context) => {
			if (!(value instanceof expectedClass)) {
				return composeError(`Expected instance of ${expectedClass}`, name, context);
			}

			return true;
		};
	},

	/**
	 * Creates a validator that checks a value against a single type, null, or undefined.
	 * @param {function()} typeValidator Validator to check value against.
	 * @return {function()} Validator.
	 */
	maybe: function(typeValidator) {
		return (value, name, context) => {
			const validation = typeValidator(value, name);

			if (!core.isDef(value) || core.isNull(value) || !(validation instanceof Error)) {
				return true;
			}

			return composeError(`${validation.toString()} or null`, name, context);
		};
	},

	/**
	 * Creates a validator that checks the values of an object against a type.
	 * @param {function()} typeValidator Validator to check value against.
	 * @return {function()} Validator.
	 */
	objectOf: function(typeValidator) {
		return (value, name, context) => {
			let success = true;

			for (let key in value) {
				success = !(typeValidator(value[key], null) instanceof Error);
			}

			if (!success) {
				return composeError('Expected object of one type', name, context);
			}

			return true;
		};
	},

	/**
	 * Creates a validator that checks equality against specific values.
	 * @param {!Array} arrayOfValues Array of values to check equality against.
	 * @return {function()} Validator.
	 */
	oneOf: function(arrayOfValues) {
		if (!Array.isArray(arrayOfValues)) {
			return (value, name, context) => composeError(`Expected an array, but received type '${getStateType(arrayOfValues)}'.`, name, context);
		}

		return (value, name, context) => {
			for (let i = 0; i < arrayOfValues.length; i++) {
				const oneOfValue = arrayOfValues[i];

				if (value === oneOfValue) {
					return true;
				}
			}

			return composeError('Expected one of given values.', name, context);
		};
	},

	/**
	 * Creates a validator that checks a value against multiple types and only has to pass one.
	 * @param {!Array} arrayOfTypeValidators Array of validators to check value against.
	 * @return {function()} Validator.
	 */
	oneOfType: function(arrayOfTypeValidators) {
		if (!Array.isArray(arrayOfTypeValidators)) {
			return (value, name, context) => composeError(`Expected an array, but received type '${getStateType(arrayOfTypeValidators)}'.`, name, context);
		}

		return (value, name, context) => {
			for (let i = 0; i < arrayOfTypeValidators.length; i++) {
				const validator = arrayOfTypeValidators[i];

				if (!(validator(value, name) instanceof Error)) {
					return true;
				}
			}

			return composeError('Expected one of given types.', name, context);
		};
	},

	/**
	 * Creates a validator that checks the shape of an object.
	 * @param {!Object} shape An object containing type validators for each key.
	 * @return {function()} Validator.
	 */
	shapeOf: function(shape) {
		const type = getStateType(shape);
		if (type !== 'object') {
			return (value, name, context) => composeError(`Expected an object, but received type '${type}'.`, name, context);
		}

		return (value, name, context) => {
			for (let key in shape) {
				const validator = shape[key];
				const valueForKey = value[key];

				if (validator(valueForKey, null) instanceof Error) {
					return composeError('Expected object with a specific shape', name, context);
				}
			}

			return true;
		};
	}
};

/**
 * Composes a warning a warning message.
 * @param {string} error Error message to display to console.
 * @param {?string} name Name of state property that is giving the error.
 * @param {Object} context.
 * @return {!Error} Instance of Error class.
 */
function composeError(error, name, context) {
	const componentName = context ? core.getFunctionName(context.constructor) : null;
	const parentComponent = context && context.getRenderer ? context.getRenderer().lastParentComponent_ : null;
	const parentComponentName = parentComponent ? core.getFunctionName(parentComponent.constructor) : null;

	const location = parentComponentName ? `Check render method of '${parentComponentName}'.` : '';

	return new Error(`Warning: Invalid state passed to '${name}'. ${error} Passed to '${componentName}'. ${location}`);
}

/**
 * Checks type of given value.
 * @param {*} value Any value.
 * @return {string} Type of value.
 */
function getStateType(value) {
	const stateType = typeof value;
	if (Array.isArray(value)) {
		return 'array';
	}

	return stateType;
}

/**
 * Creates a validator that checks against a specific primitive type.
 * @param {string} expectedType Type to check against.
 * @return {function()} Validator.
 */
function validateType(expectedType) {
	return (value, name, context) => {
		const type = getStateType(value);

		if (type !== expectedType) {
			return composeError(`Expected type '${expectedType}', but received type '${type}'.`, name, context);
		}

		return true;
	};
}

export default validators;
<|endoftext|>


<commit_before>/**
	Linkify a HTML DOM node
*/

import * as linkify from './linkify';
var tokenize = linkify.tokenize;
var options = linkify.options;

const HTML_NODE = 1, TXT_NODE = 3;

/**
	Given a parent element and child node that the parent contains, replaces
	that child with the given array of new children
*/
function replaceChildWithChildren(parent, oldChild, newChildren) {
	let lastNewChild = newChildren[newChildren.length - 1];
	parent.replaceChild(lastNewChild, oldChild);
	for (let i = newChildren.length - 2; i >= 0; i--) {
		parent.insertBefore(newChildren[i], lastNewChild);
		lastNewChild = newChildren[i];
	}
}

/**
	Given an array of MultiTokens, return an array of Nodes that are either
	(a) Plain Text nodes (node type 3)
	(b) Anchor tag nodes (usually, unless tag name is overridden in the options)

	Takes the same options as linkifyElement and an optional doc element
	(this should be passed in by linkifyElement)
*/
function tokensToNodes(tokens, opts, doc) {
	let result = [];

	for (let i = 0; i < tokens.length; i++) {
		let token = tokens[i];
		let validated = token.isLink && options.resolve(opts.validate, token.toString(), token.type);

		if (token.isLink && validated) {

			let
			href			= token.toHref(opts.defaultProtocol),
			formatted		= options.resolve(opts.format, token.toString(), token.type),
			formattedHref	= options.resolve(opts.formatHref, href, token.type),
			attributesHash	= options.resolve(opts.attributes, href, token.type),
			tagName			= options.resolve(opts.tagName, href, token.type),
			linkClass		= options.resolve(opts.linkClass, href, token.type),
			target			= options.resolve(opts.target, href, token.type),
			events			= options.resolve(opts.events, href, token.type);

			// Build the link
			let link = doc.createElement(tagName);
			link.setAttribute('href', formattedHref);
			link.setAttribute('class', linkClass);
			if (target) {
				link.setAttribute('target', target);
			}

			// Build up additional attributes
			if (attributesHash) {
				for (var attr in attributesHash) {
					link.setAttribute(attr, attributesHash[attr]);
				}
			}

			if (events) {
				for (var event in events) {
					if (link.addEventListener) {
						link.addEventListener(event, events[event]);
					} else if (link.attachEvent)  {
						link.attachEvent('on' + event, events[event]);
					}
				}
			}

			link.appendChild(doc.createTextNode(formatted));
			result.push(link);

		} else if (token.type === 'nl' && opts.nl2br) {
			result.push(doc.createElement('br'));
		} else {
			result.push(doc.createTextNode(token.toString()));
		}
	}

	return result;
}

// Requires document.createElement
function linkifyElementHelper(element, opts, doc) {

	// Can the element be linkified?
	if (!element || typeof element !== 'object' || element.nodeType !== HTML_NODE) {
		throw new Error(`Cannot linkify ${element} - Invalid DOM Node type`);
	}

	let ignoreTags = opts.ignoreTags;

	// Is this element already a link?
	if (element.tagName === 'A' || options.contains(ignoreTags, element.tagName)) {
		// No need to linkify
		return element;
	}

	let childElement = element.firstChild;

	while (childElement) {

		switch (childElement.nodeType) {
		case HTML_NODE:
			linkifyElementHelper(childElement, opts, doc);
			break;
		case TXT_NODE:

			let
			str = childElement.nodeValue,
			tokens = tokenize(str),
			nodes = tokensToNodes(tokens, opts, doc);

			// Swap out the current child for the set of nodes
			replaceChildWithChildren(element, childElement, nodes);

			// so that the correct sibling is selected
			childElement = nodes[nodes.length - 1];

			break;
		}

		childElement = childElement.nextSibling;
	}

	return element;
}

function linkifyElement(element, opts, doc=null) {

	try {
		doc = doc || window && window.document || global && global.document;
	} catch (e) { /* do nothing for now */ }

	if (!doc) {
		throw new Error(
			'Cannot find document implementation. ' +
			'If you are in a non-browser environment like Node.js, ' +
			'pass the document implementation as the third argument to linkifyElement.'
		);
	}

	opts = options.normalize(opts);
	return linkifyElementHelper(element, opts, doc);
}

// Maintain reference to the recursive helper to cache option-normalization
linkifyElement.helper = linkifyElementHelper;
linkifyElement.normalize = options.normalize;

export default linkifyElement;
<commit_msg>Fixing element interface invalid DOM node error (#141)<commit_after>/**
	Linkify a HTML DOM node
*/

import * as linkify from './linkify';
var tokenize = linkify.tokenize;
var options = linkify.options;

const HTML_NODE = 1, TXT_NODE = 3;

/**
	Given a parent element and child node that the parent contains, replaces
	that child with the given array of new children
*/
function replaceChildWithChildren(parent, oldChild, newChildren) {
	let lastNewChild = newChildren[newChildren.length - 1];
	parent.replaceChild(lastNewChild, oldChild);
	for (let i = newChildren.length - 2; i >= 0; i--) {
		parent.insertBefore(newChildren[i], lastNewChild);
		lastNewChild = newChildren[i];
	}
}

/**
	Given an array of MultiTokens, return an array of Nodes that are either
	(a) Plain Text nodes (node type 3)
	(b) Anchor tag nodes (usually, unless tag name is overridden in the options)

	Takes the same options as linkifyElement and an optional doc element
	(this should be passed in by linkifyElement)
*/
function tokensToNodes(tokens, opts, doc) {
	let result = [];

	for (let i = 0; i < tokens.length; i++) {
		let token = tokens[i];
		let validated = token.isLink && options.resolve(opts.validate, token.toString(), token.type);

		if (token.isLink && validated) {

			let
			href			= token.toHref(opts.defaultProtocol),
			formatted		= options.resolve(opts.format, token.toString(), token.type),
			formattedHref	= options.resolve(opts.formatHref, href, token.type),
			attributesHash	= options.resolve(opts.attributes, href, token.type),
			tagName			= options.resolve(opts.tagName, href, token.type),
			linkClass		= options.resolve(opts.linkClass, href, token.type),
			target			= options.resolve(opts.target, href, token.type),
			events			= options.resolve(opts.events, href, token.type);

			// Build the link
			let link = doc.createElement(tagName);
			link.setAttribute('href', formattedHref);
			link.setAttribute('class', linkClass);
			if (target) {
				link.setAttribute('target', target);
			}

			// Build up additional attributes
			if (attributesHash) {
				for (var attr in attributesHash) {
					link.setAttribute(attr, attributesHash[attr]);
				}
			}

			if (events) {
				for (var event in events) {
					if (link.addEventListener) {
						link.addEventListener(event, events[event]);
					} else if (link.attachEvent)  {
						link.attachEvent('on' + event, events[event]);
					}
				}
			}

			link.appendChild(doc.createTextNode(formatted));
			result.push(link);

		} else if (token.type === 'nl' && opts.nl2br) {
			result.push(doc.createElement('br'));
		} else {
			result.push(doc.createTextNode(token.toString()));
		}
	}

	return result;
}

// Requires document.createElement
function linkifyElementHelper(element, opts, doc) {

	// Can the element be linkified?
	if (!element || element.nodeType !== HTML_NODE) {
		throw new Error(`Cannot linkify ${element} - Invalid DOM Node type`);
	}

	let ignoreTags = opts.ignoreTags;

	// Is this element already a link?
	if (element.tagName === 'A' || options.contains(ignoreTags, element.tagName)) {
		// No need to linkify
		return element;
	}

	let childElement = element.firstChild;

	while (childElement) {

		switch (childElement.nodeType) {
		case HTML_NODE:
			linkifyElementHelper(childElement, opts, doc);
			break;
		case TXT_NODE:

			let
			str = childElement.nodeValue,
			tokens = tokenize(str),
			nodes = tokensToNodes(tokens, opts, doc);

			// Swap out the current child for the set of nodes
			replaceChildWithChildren(element, childElement, nodes);

			// so that the correct sibling is selected
			childElement = nodes[nodes.length - 1];

			break;
		}

		childElement = childElement.nextSibling;
	}

	return element;
}

function linkifyElement(element, opts, doc=null) {

	try {
		doc = doc || window && window.document || global && global.document;
	} catch (e) { /* do nothing for now */ }

	if (!doc) {
		throw new Error(
			'Cannot find document implementation. ' +
			'If you are in a non-browser environment like Node.js, ' +
			'pass the document implementation as the third argument to linkifyElement.'
		);
	}

	opts = options.normalize(opts);
	return linkifyElementHelper(element, opts, doc);
}

// Maintain reference to the recursive helper to cache option-normalization
linkifyElement.helper = linkifyElementHelper;
linkifyElement.normalize = options.normalize;

export default linkifyElement;
<|endoftext|>


<commit_before>var Util = {};
var JsonParser = function() {
  this.parse = function(text, callback) {
    var tryParse = function(text) {
      try {
        return JSON.parse(text);
      } catch (e) {
      }
    };
    var parsedJson = tryParse(text);
    if (callback && parsedJson) {
      return callback(parsedJson);
    } else {
      return parsedJson;
    }
  };
};

Util.jsonParser = function() {
  return new JsonParser();
};

module.exports = Util;
<commit_msg>Refactor json parser<commit_after>var Util = {};

Util.jsonParser = function() {
  var JsonParser = function() {
    this.parse = function(text, callback) {
      var tryParse = function(text) {
        try {
          return JSON.parse(text);
        } catch (e) {
        }
      };
      var parsedJson = tryParse(text);
      if (callback && parsedJson) {
        return callback(parsedJson);
      } else {
        return parsedJson;
      }
    };
  };

  return new JsonParser();
};

module.exports = Util;
<|endoftext|>


<commit_before>'use strict';

var cc = require('./core');

var top = function(deferred) {
  deferred.then(null, function(ex) { console.log(ex.stack); });
};

var join = function(items) {
  return cc.go(function*() {
    var result = [];
    for (var i in items)
      result.push(yield items[i]);
    return result;
  });
};

var lift = function(fn, context) {
  return function() {
    var args = Array.prototype.slice.call(arguments);
    return cc.go(function*() {
      var i;
      for (i = 0; i < args.length; ++i)
        args[i] = yield args[i];
      return fn.apply(context, args);
    });
  };
};

var chain = function(initial) {
  var args = Array.prototype.slice.call(arguments, 1);

  return cc.go(function*() {
    var val = initial;
    var form;

    for (var i = 0; i < args.length; ++i) {
      form = args[i];
      val = yield val;

      if (typeof form == 'function')
        val = form(val);
      else if (Array.isArray(form) && typeof form[0] == 'function')
        val = form[0].apply(null, [].concat(form[1], [val], form.slice(2)));
      else if (Array.isArray(form) && typeof form[0] == 'string')
        val = val[form[0]].apply(val, form.slice(1));
      else
        val = form;
    }

    return yield val;
  });
};

var sleep = function(ms) {
  var result = cc.defer();
  var t = setTimeout(function() {
    clearTimeout(t);
    result.resolve();
  }, ms);
  return result;
};

var ncallback = function(deferred) {
  return function(err, val) {
    if (err)
      deferred.reject(new Error(err));
    else
      deferred.resolve(val);
  };
};

var nbind = function(fn, context) {
  var boundArgs = Array.prototype.slice.call(arguments, 2);

  return function() {
    var args = Array.prototype.slice.call(arguments);
    var result = cc.defer();

    fn.apply(context, boundArgs.concat(args, ncallback(result)));

    return result;
  };
};

var nodeify = function(deferred, callback) {
  if (callback === undefined)
    return deferred;
  else {
    deferred.then(function(val) {
      callback(null, val)
    }, function(err) {
      callback(err);
    });
  }
};

module.exports = {
  top      : top,
  join     : join,
  lift     : lift,
  chain    : chain,
  sleep    : sleep,
  ncallback: ncallback,
  nbind    : nbind,
  nodeify  : nodeify
};
<commit_msg>rewrite lift() in terms of join()<commit_after>'use strict';

var cc = require('./core');

var top = function(deferred) {
  deferred.then(null, function(ex) { console.log(ex.stack); });
};

var join = function(items) {
  return cc.go(function*() {
    var result = [];
    for (var i in items)
      result.push(yield items[i]);
    return result;
  });
};

var lift = function(fn, context) {
  return function() {
    var args = Array.prototype.slice.call(arguments);
    return cc.go(function*() {
      return fn.apply(context, yield join(args));
    });
  };
};

var chain = function(initial) {
  var args = Array.prototype.slice.call(arguments, 1);

  return cc.go(function*() {
    var val = initial;
    var form;

    for (var i = 0; i < args.length; ++i) {
      form = args[i];
      val = yield val;

      if (typeof form == 'function')
        val = form(val);
      else if (Array.isArray(form) && typeof form[0] == 'function')
        val = form[0].apply(null, [].concat(form[1], [val], form.slice(2)));
      else if (Array.isArray(form) && typeof form[0] == 'string')
        val = val[form[0]].apply(val, form.slice(1));
      else
        val = form;
    }

    return yield val;
  });
};

var sleep = function(ms) {
  var result = cc.defer();
  var t = setTimeout(function() {
    clearTimeout(t);
    result.resolve();
  }, ms);
  return result;
};

var ncallback = function(deferred) {
  return function(err, val) {
    if (err)
      deferred.reject(new Error(err));
    else
      deferred.resolve(val);
  };
};

var nbind = function(fn, context) {
  var boundArgs = Array.prototype.slice.call(arguments, 2);

  return function() {
    var args = Array.prototype.slice.call(arguments);
    var result = cc.defer();

    fn.apply(context, boundArgs.concat(args, ncallback(result)));

    return result;
  };
};

var nodeify = function(deferred, callback) {
  if (callback === undefined)
    return deferred;
  else {
    deferred.then(function(val) {
      callback(null, val)
    }, function(err) {
      callback(err);
    });
  }
};

module.exports = {
  top      : top,
  join     : join,
  lift     : lift,
  chain    : chain,
  sleep    : sleep,
  ncallback: ncallback,
  nbind    : nbind,
  nodeify  : nodeify
};
<|endoftext|>


<commit_before>'use strict';

// Borrowed from Angular 2.0 di module

var id = 0;

function isClass(fn) {
    if (!isFunction(fn)) {
        return false;
    }

    if (fn.name) {
        return isUpperCase(fn.name.charAt(0));
    }

    return Object.keys(fn.prototype).length > 0;
}

function isFunction(value) {
    return typeof value === 'function';
}

function getUID() {
    return id += 1;
}

module.exports = {
    isClass: isClass,
    isFunction: isFunction,
    getUID: getUID
};
<commit_msg>fix(util): Add missing isUpper case util function<commit_after>'use strict';

// Borrowed from Angular 2.0 di module

var id = 0;

function isClass(fn) {
    if (!isFunction(fn)) {
        return false;
    }

    if (fn.name) {
        return isUpperCase(fn.name.charAt(0));
    }

    return Object.keys(fn.prototype).length > 0;
}

function isFunction(value) {
    return typeof value === 'function';
}

function isUpperCase (value) {
    return value === value.toUpperCase();
}

function getUID() {
    return id += 1;
}

module.exports = {
    isClass: isClass,
    isFunction: isFunction,
    getUID: getUID
};
<|endoftext|>


<commit_before>import moment from 'moment';
import isPlainObject from 'lodash/isPlainObject';
import camelCase from 'lodash/camelCase';
import {LedEffects, LedColors} from './AuroraConstants';

exports.sleep = (ms = 0) => new Promise(resolve => setTimeout(resolve, ms));

exports.promisify = (fn, context = null) => {

    return (...args) => {

        return new Promise((resolve, reject) => {

            fn.apply(context, args.concat((error, result) => {

                if (error) return reject(error);

                resolve(result);

            }));
        });
    }
};

exports.promisifyStream = (stream) => {

    return new Promise((resolve, reject) =>  {

        stream.once('close', resolve);
        stream.once('error', reject);
        stream.once('end', resolve);
    });

};

exports.parseValueString = (value) => {

    value = value.trim();

    const valWithoutNumericSymbols = value.replace(/[$%,]+|(ms$)/g,'');

    if (!isNaN(valWithoutNumericSymbols)) {

        return +valWithoutNumericSymbols;
    }

    //support the funky date format GCC uses for __TIME__ constant
    let date = moment(value, [
        moment.ISO_8601,
        "YYYY-MM-DD HH:mm:ss:SSS",
        "MMM D YYYY - HH:mm:ss",
        "MMM  D YYYY - HH:mm:ss",
        "MMM DD YYYY - HH:mm:ss"
    ], true);

    if (date.isValid()){

        return +date;
    }

    //TODO: eventually remove this, as in the future, uppercase will be
    //required to trigger conversion
    const valueUC = value.toUpperCase();

    if (valueUC === 'TRUE' || valueUC === 'ON' || valueUC === 'ACTIVE' || valueUC === 'YES') {

        return true;
    }
    else if (valueUC === 'FALSE' || valueUC === 'OFF' || valueUC === 'INACTIVE' || valueUC === 'NO' || valueUC === 'NONE') {

        return false;
    }
    else if (valueUC === 'UNKNOWN'){

        return 0;
    }

    return value;
};

exports.camelCaseObjectKeys = (object) => {

    const camelCaseObject = {};

    for (let key of Object.keys(object)) {

        let value = object[key];

        if (isPlainObject(value)) {
            value = exports.camelCaseObjectKeys(value);
        }

        camelCaseObject[camelCase(key)] = value;
    }

    return camelCaseObject;
};

exports.maskFromIds = (...ids) => {

    return ids.reduce((mask, id) => mask + (1 << id), 0);

};

exports.versionToString = (version) => {

    if (!version) return "UNKNOWN";

    const major = Math.floor(version / 10000);
    const minor = Math.floor((version - major*10000) / 100);
    const build = version - major*10000 - minor*100;

    return `v${major}.${minor}.${build}`;

};

exports.stringToVersion = (versionString) => {

    const version = versionString.split('.');

    if (version.length != 3) return 0;

    return parseInt(version[0])*10000 + parseInt(version[1])*100 + parseInt(version[2]);
};

exports.buzzSongObjToCmd = (songObj) => {

    let {song, repeats, volume, tempoAdjust, pitchAdjust} = songObj;

    if (!song) throw new Error("Song field is required.");

    repeats = parseInt(repeats);

    if (isNaN(repeats)){

        repeats = 0;
    }

    volume = parseInt(volume);
    volume = isNaN(volume) ? 100 : Math.min(100, Math.max(0, volume));

    tempoAdjust = parseFloat(tempoAdjust);
    tempoAdjust = isNaN(tempoAdjust) ? 1 : Math.min(2.5, Math.max(.25, tempoAdjust));

    pitchAdjust = parseInt(pitchAdjust);
    pitchADjust = isNaN(pitchAdjust) ? 0 : Math.min(12, Math.max(-12, pitchAdjust));

    return `buzz-song ${song} ${repeat} ${volume} ${tempoAdjust} ${pitchAdjust}`;
};

exports.ledEffectObjToCmd = (effectObj) => {

    let effect = LedEffects.find((e) => e.name == effectObj.effect);

    if (!effect){

        throw new Error("Invalid effect specified.");
    }

    const [eyes, state1Eyes, state2Eyes] = ['eyes','state1Eyes','state2Eyes'].map((option) => {

        const eyesOption = effectObj[option];

        if (typeof eyesOption == 'string'){

            switch (eyesOptions.toLowerCase()){

                case 'right':
                    return 2;

                case 'left':
                    return 1;

                case 'none':
                    return 0;

                case 'both':
                default:
                    return 3;
            }
        }
        else {

            const eye= parseInt(eyesOption);

            return isNaN(eyes) || eyes < 0 || eyes > 3 ? 3 : eyes;
        }

        return 3;

    });

    const [
        brightness,
        state1Brightness,
        state2Brightness
    ] = ['brightness','state1Brightness','state2Brightness'].map((option) => {

        const brightnessOption = effectObj[option];
        const brightness = parseInt(brightnessOption);

        if (isNaN(brightness) || brightness > 255 || brightness < 0){
            return 255;
        }

        return brightness * (255 / 100);
    });

    const [
        color,
        state1Color,
        state2Color
    ] = ['color','state1Color','state2Color'].map((option) => {

        const colorOption = effectObj[option];

        if (!colorOption || colorOption == 'off' || colorOption == 'black'){

            return '0x000000';
        }

        let color = LedColors.find((c) => c.name == colorOption);
        color = !color ? colorOption : color.value;
        color = color.replace('#','0x');

        return isNaN(parseInt(color)) ? '0x000000' : color;

    });

    const [
        state1Duration,
        state2Duration,
        transitionDuration
    ] = ['state1Duration','state2Duration','transitionDuration'].map((option) => {

        const durationOption = effectObj[option];
        const duration = parseFloat(durationOption);

        if (isNaN(duration)){

            return 1000;
        }

        return Math.floor(duration*1000);
    });

    let shutdownDelay = parseInt(effectObj.shutdownDelay);
    shutdownDelay = isNaN(shutdownDelay) ? 0 : shutdownDelay * 1000;

    switch (effect.name){

        case 'set':
            return `${effect.cmd} ${eyes} ${color} ${brightness} ${shutdownDelay}`;

        case 'blink':
            let blinkCount = parseInt(effectObj.blinkCount);
            blinkCount = isNaN(blinkCount) ? 1 : blinkCount;
            let blinkRate = parseInt(effectObj.blinkRate);
            blinkRate = isNaN(blinkRate) ? 500 : blinkRate*1000;
            return `${effect.cmd} ${eyes} ${color} ${brightness} ${blinkCount} ${blinkRate} ${shutdownDelay}`;

        case 'alternate':
            let alternateCount = parseInt(effectObj.alternateCount);
            alternateCount = isNaN(alternateCount) ? 1 : alternateCount;
            return `${effect.cmd} ${state1Eyes} ${state1Color} ${state1Brightness}
                    ${state1Duration} ${state2Eyes} ${state2Color} ${state2Brighntess}
                    ${state2Duration} ${alternateCount} ${shutdownDelay}`;

        case 'transition':
            let transitionRewind = effectObj.transitionRewind ? 1 : 0;
            return `${effect.cmd} ${state1Eyes} ${state1Color} ${state1Brightness}
                    ${state2Eyes} ${state2Color} ${state2Brighntess}
                    ${transitionDuration} ${transitionRewind} ${shutdownDelay}`;

    }

};
<commit_msg>Misc bug fixes.<commit_after>import moment from 'moment';
import isPlainObject from 'lodash/isPlainObject';
import camelCase from 'lodash/camelCase';
import {LedEffects, LedColors} from './AuroraConstants';

exports.sleep = (ms = 0) => new Promise(resolve => setTimeout(resolve, ms));

exports.promisify = (fn, context = null) => {

    return (...args) => {

        return new Promise((resolve, reject) => {

            fn.apply(context, args.concat((error, result) => {

                if (error) return reject(error);

                resolve(result);

            }));
        });
    }
};

exports.promisifyStream = (stream) => {

    return new Promise((resolve, reject) =>  {

        stream.once('close', resolve);
        stream.once('error', reject);
        stream.once('end', resolve);
    });

};

exports.parseValueString = (value) => {

    value = value.trim();

    const valWithoutNumericSymbols = value.replace(/[$%,]+|(ms$)/g,'');

    if (!isNaN(valWithoutNumericSymbols)) {

        return +valWithoutNumericSymbols;
    }

    //support the funky date format GCC uses for __TIME__ constant
    let date = moment(value, [
        moment.ISO_8601,
        "YYYY-MM-DD HH:mm:ss:SSS",
        "MMM D YYYY - HH:mm:ss",
        "MMM  D YYYY - HH:mm:ss",
        "MMM DD YYYY - HH:mm:ss"
    ], true);

    if (date.isValid()){

        return +date;
    }

    //TODO: eventually remove this, as in the future, uppercase will be
    //required to trigger conversion
    const valueUC = value.toUpperCase();

    if (valueUC === 'TRUE' || valueUC === 'ON' || valueUC === 'ACTIVE' || valueUC === 'YES') {

        return true;
    }
    else if (valueUC === 'FALSE' || valueUC === 'OFF' || valueUC === 'INACTIVE' || valueUC === 'NO' || valueUC === 'NONE') {

        return false;
    }
    else if (valueUC === 'UNKNOWN'){

        return 0;
    }

    return value;
};

exports.camelCaseObjectKeys = (object) => {

    const camelCaseObject = {};

    for (let key of Object.keys(object)) {

        let value = object[key];

        if (isPlainObject(value)) {
            value = exports.camelCaseObjectKeys(value);
        }

        camelCaseObject[camelCase(key)] = value;
    }

    return camelCaseObject;
};

exports.maskFromIds = (...ids) => {

    return ids.reduce((mask, id) => mask + (1 << id), 0);

};

exports.versionToString = (version) => {

    if (!version) return "UNKNOWN";

    const major = Math.floor(version / 10000);
    const minor = Math.floor((version - major*10000) / 100);
    const build = version - major*10000 - minor*100;

    return `v${major}.${minor}.${build}`;

};

exports.stringToVersion = (versionString) => {

    const version = versionString.split('.');

    if (version.length != 3) return 0;

    return parseInt(version[0])*10000 + parseInt(version[1])*100 + parseInt(version[2]);
};

exports.buzzSongObjToCmd = (songObj) => {

    let {song, repeat, volume, tempoAdjust, pitchAdjust} = songObj;

    if (!song) throw new Error("Song field is required.");

    repeat = parseInt(repeat);

    if (isNaN(repeat)){

        repeat = 0;
    }

    volume = parseInt(volume);
    volume = isNaN(volume) ? 100 : Math.min(100, Math.max(0, volume));

    tempoAdjust = parseFloat(tempoAdjust);
    tempoAdjust = isNaN(tempoAdjust) ? 1 : Math.min(2.5, Math.max(.25, tempoAdjust));

    pitchAdjust = parseInt(pitchAdjust);
    pitchAdjust = isNaN(pitchAdjust) ? 0 : Math.min(12, Math.max(-12, pitchAdjust));

    return `buzz-song ${song} ${repeat} ${volume} ${tempoAdjust} ${pitchAdjust}`;
};

exports.ledEffectObjToCmd = (effectObj) => {

    let effect = LedEffects.find((e) => e.name == effectObj.effect);

    if (!effect){

        throw new Error("Invalid effect specified.");
    }

    const [eyes, state1Eyes, state2Eyes] = ['eyes','state1Eyes','state2Eyes'].map((option) => {

        const eyesOption = effectObj[option];

        if (typeof eyesOption == 'string'){

            switch (eyesOption.toLowerCase()){

                case 'right':
                    return '0x02';

                case 'left':
                    return '0x01';

                case 'none':
                    return '0x00';

                case 'both':
                default:
                    return '0x03';
            }
        }
        else {

            const eye= parseInt(eyesOption);

            return isNaN(eyes) || eyes < 0 || eyes > 3 ? 3 : eyes;
        }

        return 3;

    });

    const [
        brightness,
        state1Brightness,
        state2Brightness
    ] = ['brightness','state1Brightness','state2Brightness'].map((option) => {

        const brightnessOption = effectObj[option];
        const brightness = parseInt(brightnessOption);

        if (isNaN(brightness) || brightness > 255 || brightness < 0){
            return '0xFF';
        }

        return '0x' + ("00" + (Math.round(brightness * (255 / 100))).toString(16)).slice(-2).toUpperCase();
    });

    const [
        color,
        state1Color,
        state2Color
    ] = ['color','state1Color','state2Color'].map((option) => {

        const colorOption = effectObj[option];

        if (!colorOption || colorOption == 'off' || colorOption == 'black'){

            return '0x000000';
        }

        let color = LedColors.find((c) => c.name == colorOption);
        color = !color ? colorOption : color.value;
        color = color.replace('#','0x');

        return isNaN(parseInt(color)) ? '0x000000' : color.toUpperCase();

    });

    const [
        state1Duration,
        state2Duration,
        transitionDuration
    ] = ['state1Duration','state2Duration','transitionDuration'].map((option) => {

        const durationOption = effectObj[option];
        const duration = parseFloat(durationOption);

        if (isNaN(duration)){

            return 1000;
        }

        return Math.round(duration*1000);
    });

    let shutoffDelay = parseInt(effectObj.shutoffDelay);
    shutoffDelay = isNaN(shutoffDelay) ? 0 : Math.round(shutoffDelay * 1000);

    switch (effect.name){

        case 'set':
            return `${effect.cmd} ${eyes} ${color} ${brightness} ${shutoffDelay}`;

        case 'blink':
            let blinkCount = parseInt(effectObj.blinkCount);
            blinkCount = isNaN(blinkCount) ? 1 : blinkCount;
            let blinkRate = parseFloat(effectObj.blinkRate);
            blinkRate = isNaN(blinkRate) ? 500 : Math.round(blinkRate*1000);
            return `${effect.cmd} ${eyes} ${color} ${brightness} ${blinkCount} ${blinkRate} ${shutoffDelay}`;

        case 'alternate':
            let alternateCount = parseInt(effectObj.alternateCount);
            alternateCount = isNaN(alternateCount) ? 1 : alternateCount;
            return `${effect.cmd} ${state1Eyes} ${state1Color} ${state1Brightness} ${state1Duration} ${state2Eyes} ${state2Color} ${state2Brightness} ${state2Duration} ${alternateCount} ${shutoffDelay}`;

        case 'transition':
            let transitionRewind = effectObj.transitionRewind ? 1 : 0;
            return `${effect.cmd} ${state1Eyes} ${state1Color} ${state1Brightness} ${state2Eyes} ${state2Color} ${state2Brightness} ${transitionDuration} ${transitionRewind} ${shutoffDelay}`;
    }

};
<|endoftext|>


<commit_before>  // Default parameters for all charts
  var default_vars = {

    this_chart: null,

    new_data: null,

    dev : false,
    id : "id",
    id_var : "id",
    var_group: null,
    data: [],
    links: [],
    var_node_id: 'id', // ID to join the nodes/link data

    title: "",

    // Default dimensions
    margin: {top: 0, right: 0, bottom: 0, left: 0},
    rotate: 0,

    // Default Variables mapping
    var_color: null,
    var_sort_asc: false,

    // Interaction
    highlight: [],
    selection: [],
    filter: [],
    zoom: [],

    time: {
      var_time: null,
      current_time: null,
      parse: function(d) { return d; }
    },

    // TABLE
    columns: [],
    sort_by: {'column': 'name', 'asc': true},

    // DOTPLOT
    x_type: "linear",
    x_scale: [],
    x_ticks: 5,
    x_axis: null,
    x_format: function(d) { return d; },
    x_tickSize: 10,
    x_tickPadding: 0,
    x_tickValues: null,
    x_axis_show: false,
    x_axis_orient: "bottom",
    x_grid_show: false,
    x_text: true,
    x_text_custom: "",
    x_axis_translate: [0, 0],
    x_invert: false,
    x_domain: null,
    x_range: null,

    // SCATTERPLOT (extends DOTPLOT)
    y_type: "linear",
    y_scale: [],
    y_ticks: 5,
    y_axis: null,
    y_format: function(d) { return d; },
    y_tickSize: 10,
    y_tickPadding: 0,
    y_tickValues: null,
    y_axis_show: false,
    y_axis_orient: "left",
    y_grid_show: false,
    y_text: true,
    y_text_custom: "",
    y_axis_translate: [0, 0],
    y_invert: false,
    y_domain: null,
    y_range: null,

    r_scale: null,
    r_cutoff: function(d) { return d > 30; },

    // Automatically generate UI elements
    ui: true,

    lang: 'en_US', // 'es_ES, fr_FR'
    locales: {}, // Translations for various lang

    // Graphical properties for graphical marks
    color: d3.scale.category20c(),
    size: d3.scale.linear(),

    // Events management
    dispatch: [],
    evt: {register: function() {}, call: function() {} },

    // SVG Container
    container: "#viz",
    svg: null,      // Contains the svg element
    root_svg: null, // Contains the group children to the svg element
    ratio: 0.5, // Visualization aspect ratio

    width: 800,
    height: 400,
    // Animation and interpolation
    duration: 1000,
    interpolate: "monotone",

    layout: {},

    padding: 1,

    // TREEMAP
    treemap_mode: 'squarify',

    treemap: {
      padding: 1,
      d_x: 30,
      d_y: 30,
      depth_text: 1,
      depth_rect: 2
    },

    radius: 5,

    radius_min: 2,
    radius_max: 10,

    mark: {
      height: 10,
      width: 10,
      rotate: 0,
      radius: 5,
      fill: function(d) { return vars.color(vars.accessor_values(d)[vars.var_color]); }
    },

    accessor_values: function(d) { return d; },
    accessor_items: function(d) { return d; },

    accessor_static: function(d) { return d; },
    accessor_data: function(d) {
      return d.values[vars.time.current_time];
    },

    refresh: true,
    init: true,
    redraw_all: false,

    _user_vars: {},

    list: {type: ["sparkline", "dotplot", "barchart", "linechart", "scatterplot", "grid",
                  "stacked", "piechart", "slopegraph", "slopegraph_ordinal", "productspace", "treemap", "geomap",
                  "stackedbar", "ordinal_vertical", "ordinal_horizontal", "matrix", "radial",
                  "rectmap", "caterplot", "tickplot", "barchart_vertical"],
      mark: ['rect', 'circle', 'star', 'shape']
    },

    default_params: {},
    default_marks: {},

    // Order we use to draw the various marks
    z_index: [
 //     {selector: '.mark__group', attribute: '__aggregated', type: 'productspace', weight: 1},
      {selector: '.connect__group', type: 'productspace', weight: 1, event: 'highlightOut'},
      {selector: '.mark__group', type: 'productspace', weight: 1, event: 'highlightOut'},
      {selector: '.connect__group', attribute: '__highlighted', type: 'productspace', weight: 1, event: 'highlightOn'},
      {selector: '.mark__group', attribute: '__highlighted', type: 'productspace', weight: 1, event: 'highlightOn'},
      {selector: '.mark__group', attribute: '__highlighted__adjacent', type: 'productspace', weight: 1, event: 'highlightOn'},
    ],

    set: [],

    scale: 1,
    translate_x: 0,
    translate_y: 0,
    translate: [0, 0],

    flat_scene: false

  };

  vars = vistk.utils.merge(vars, default_vars);
<commit_msg>Highlighted elements are always on top<commit_after>  // Default parameters for all charts
  var default_vars = {

    this_chart: null,

    new_data: null,

    dev : false,
    id : "id",
    id_var : "id",
    var_group: null,
    data: [],
    links: [],
    var_node_id: 'id', // ID to join the nodes/link data

    title: "",

    // Default dimensions
    margin: {top: 0, right: 0, bottom: 0, left: 0},
    rotate: 0,

    // Default Variables mapping
    var_color: null,
    var_sort_asc: false,

    // Interaction
    highlight: [],
    selection: [],
    filter: [],
    zoom: [],

    time: {
      var_time: null,
      current_time: null,
      parse: function(d) { return d; }
    },

    // TABLE
    columns: [],
    sort_by: {'column': 'name', 'asc': true},

    // DOTPLOT
    x_type: "linear",
    x_scale: [],
    x_ticks: 5,
    x_axis: null,
    x_format: function(d) { return d; },
    x_tickSize: 10,
    x_tickPadding: 0,
    x_tickValues: null,
    x_axis_show: false,
    x_axis_orient: "bottom",
    x_grid_show: false,
    x_text: true,
    x_text_custom: "",
    x_axis_translate: [0, 0],
    x_invert: false,
    x_domain: null,
    x_range: null,

    // SCATTERPLOT (extends DOTPLOT)
    y_type: "linear",
    y_scale: [],
    y_ticks: 5,
    y_axis: null,
    y_format: function(d) { return d; },
    y_tickSize: 10,
    y_tickPadding: 0,
    y_tickValues: null,
    y_axis_show: false,
    y_axis_orient: "left",
    y_grid_show: false,
    y_text: true,
    y_text_custom: "",
    y_axis_translate: [0, 0],
    y_invert: false,
    y_domain: null,
    y_range: null,

    r_scale: null,
    r_cutoff: function(d) { return d > 30; },

    // Automatically generate UI elements
    ui: true,

    lang: 'en_US', // 'es_ES, fr_FR'
    locales: {}, // Translations for various lang

    // Graphical properties for graphical marks
    color: d3.scale.category20c(),
    size: d3.scale.linear(),

    // Events management
    dispatch: [],
    evt: {register: function() {}, call: function() {} },

    // SVG Container
    container: "#viz",
    svg: null,      // Contains the svg element
    root_svg: null, // Contains the group children to the svg element
    ratio: 0.5, // Visualization aspect ratio

    width: 800,
    height: 400,
    // Animation and interpolation
    duration: 1000,
    interpolate: "monotone",

    layout: {},

    padding: 1,

    // TREEMAP
    treemap_mode: 'squarify',

    treemap: {
      padding: 1,
      d_x: 30,
      d_y: 30,
      depth_text: 1,
      depth_rect: 2
    },

    radius: 5,
    radius_min: 2,
    radius_max: 10,

    mark: {
      height: 10,
      width: 10,
      rotate: 0,
      radius: 5,
      fill: function(d) { return vars.color(vars.accessor_values(d)[vars.var_color]); }
    },

    accessor_values: function(d) { return d; },
    accessor_items: function(d) { return d; },

    accessor_static: function(d) { return d; },
    accessor_data: function(d) {
      return d.values[vars.time.current_time];
    },

    refresh: true,
    init: true,
    redraw_all: false,

    _user_vars: {},

    list: {type: ["sparkline", "dotplot", "barchart", "linechart", "scatterplot", "grid",
                  "stacked", "piechart", "slopegraph", "slopegraph_ordinal", "productspace", "treemap", "geomap",
                  "stackedbar", "ordinal_vertical", "ordinal_horizontal", "matrix", "radial",
                  "rectmap", "caterplot", "tickplot", "barchart_vertical"],
      mark: ['rect', 'circle', 'star', 'shape']
    },

    default_params: {},
    default_marks: {},

    // Order we use to draw the various marks
    z_index: [
 //     {selector: '.mark__group', attribute: '__aggregated', type: 'productspace', weight: 1},
      {selector: '.connect__group', type: 'productspace', weight: 1, event: 'highlightOut'},
      {selector: '.mark__group', type: 'productspace', weight: 1, event: 'highlightOut'},
      {selector: '.connect__group', attribute: '__highlighted', type: 'productspace', weight: 1, event: 'highlightOn'},
      {selector: '.mark__group', attribute: '__highlighted', type: 'productspace', weight: 1, event: 'highlightOn'},
      {selector: '.mark__group', attribute: '__highlighted__adjacent', type: 'productspace', weight: 1, event: 'highlightOn'},
      {selector: '.mark__group', attribute: '__highlighted', type: 'scatterplot', weight: 1, event: 'highlightOn'},
    ],

    set: [],

    scale: 1,
    translate_x: 0,
    translate_y: 0,
    translate: [0, 0],

    flat_scene: false

  };

  vars = vistk.utils.merge(vars, default_vars);
<|endoftext|>