<commit_before>/*@pjs preload="data/images/logo1.png";*/

var sketchProc=function(processingInstance){ with (processingInstance){

//Setup
var wi = window.innerWidth ||
        document.documentElement.clientWidth ||
        document.body.clientWidth;
var he = window.innerHeight ||
         document.documentElement.clientHeight ||
         document.body.clientHeight;
size(wi*0.9,he*0.9);
frameRate(60);

setup = function() {
	mainfont = createFont("Times New Roman");
	logo = loadImage("data/images/logo1.png");
	ismobile = /iPhone|iPad|iPod|Android/i.test(navigator.userAgent);

	keys = [];
	pages = [];
	page = 0;

	chapter1 = [];
	chapter1pages = [];
	chapter1length = 6;

	chapter2 = [];
	chapter2pages = [];

	m = false;
	md = false;
	flevel = 255;
	bw = width/6;
	bh = width/12;
	prim = color(15,15,15);
	sec = color(100,30,30);
	tert = color(150,150,150);
	backg = color(55,55,55);

	// Standard fade in.
	fade = function() {
		flevel -= 10;
		fill(55,55,55,flevel);
		noStroke();
		rectMode(CORNER);
		rect(-1,-1,width+1,height+1);
	};

	// Loads all panels in the named folder into the target array.
	loadpanels = function(name,number,target) {
		for (i = 0; i < number;) {
			target[i] = loadImage("data/images/panels/"+name+i+".png");
			i ++;
		}
	};

	// The array of data for buttons used.
	buttons = {
		start:{x:width*(3/4),y:height/2,w:bw,h:bh,text:"Enter"},
		next:{x:width*(8/9),y:height*(1/2),w:bw,h:bh,text:"Next"},
		prev:{x:width*(1/9),y:height*(1/2),w:bw,h:bh,text:"Previous"},
	};

	// The hefty button function.  Works on all platforms tested.  Needs an exterior click call.
	button = function(con) {
		bux = con.x
		buy = con.y
		buw = con.w
		buh = con.h
		butext = con.text
		con.pressed = false;
		mouseover = false;

		rectMode(CENTER);

		if (mouseX>bux-buw/2&&mouseX<bux+buw/2 && mouseY>buy-buh/2&&mouseY<buy+buh/2) {
			mouseover = true;
		}

		if (mouseover) {
			fill(prim);
		} else {
			fill(sec);
		}
		stroke(prim);
		strokeWeight(buh/10);
		rect(bux,buy,buw,buh,buh/2.5);

		if (mouseover) {
			fill(sec);
		} else {
			fill(prim);
		}
		textFont(mainfont,buh/2);
		if (ismobile) {
			textSize(buh/3);
		}
		textAlign(CENTER,CENTER);
		text(butext,bux,buy);

		if (ismobile && mouseover) {
			con.pressed = true;
			return con.pressed;
		}

		if (mouseover && m) {
			con.pressed = true;
			return con.pressed;
		}
		return con.pressed;
	};

	// Standard button layout
	standardbuttons = function() {
		button(buttons.next);
		if (buttons.next.pressed) {
			if (ismobile) {
				if (flevel < 0) {
					page += 1;
					flevel = 255;
				}
			} else if (flevel < 80) {
				page += 1;
				flevel = 255;
			}
		};

		button(buttons.prev);
		if (buttons.prev.pressed) {
			if (ismobile) {
				if (flevel < 0) {
					page -= 1;
					flevel = 255;
				}
			} else if (flevel < 80) {
				page -= 1;
				flevel = 255;
			}

		};
	};

	// Displays an image properly.
	displaypanel = function(img,x,y) {
		imageMode(CENTER);
		pushMatrix();
		translate(x,y);
		scale(0.001*height,0.001*height);
			image(img,0,0);
		popMatrix();
	};

	// Creates a full page
	displaypage = function(chap,number) {
		background(backg);
		displaypanel(chap[number],width/2,height/2);
		standardbuttons();
		fade();
	};
	// Loads an array with full pages
	loadpages = function(cha,targ) {
		for (i = 0; i < cha.length; i ++) {
			targ.push(function() {fcha= cha; fi= i; displaypage(fcha,fi)});
		};
		targ[cha.length] = function() {
			background(backg);
			textSize(height/5);
			fill(sec);
			text("Under Construction",width/2,height/2);
		};
	};

	loadpanels("chap1/",chapter1length,chapter1);

	pages = [
		function() {
			background(backg)

			displaypanel(logo,width/4,height/2)

			button(buttons.start)
			if (buttons.start.pressed==true) {
				page += 1
			}
		},

	];
	loadpages(chapter1,pages);
};

draw = function() {
	pages[page]();
	println(pages);
};

keyPressed = function() {
	keys[keyCode] = true;
};
keyReleased = function() {
	keys[keyCode] = false;
};
mousePressed = function() {
	m = true;
};
mouseReleased = function() {
	m = false;
};
}};
<commit_msg>Update main.js<commit_after>/*@pjs preload="data/images/logo1.png";*/

var sketchProc=function(processingInstance){ with (processingInstance){

//Setup
var wi = window.innerWidth ||
        document.documentElement.clientWidth ||
        document.body.clientWidth;
var he = window.innerHeight ||
         document.documentElement.clientHeight ||
         document.body.clientHeight;
size(wi*0.9,he*0.9);
frameRate(60);

setup = function() {
	mainfont = createFont("Times New Roman");
	logo = loadImage("data/images/logo1.png");
	ismobile = /iPhone|iPad|iPod|Android/i.test(navigator.userAgent);

	keys = [];
	pages = [];
	page = 0;

	chapter1 = [];
	chapter1pages = [];
	chapter1length = 6;

	chapter2 = [];
	chapter2pages = [];

	m = false;
	md = false;
	flevel = 255;
	bw = width/6;
	bh = width/12;
	prim = color(15,15,15);
	sec = color(100,30,30);
	tert = color(150,150,150);
	backg = color(55,55,55);

	// Standard fade in.
	fade = function() {
		flevel -= 10;
		fill(55,55,55,flevel);
		noStroke();
		rectMode(CORNER);
		rect(-1,-1,width+1,height+1);
	};

	// Loads all panels in the named folder into the target array.
	loadpanels = function(name,number,target) {
		for (i = 0; i < number;) {
			target[i] = loadImage("data/images/panels/"+name+i+".png");
			i ++;
		}
	};

	// The array of data for buttons used.
	buttons = {
		start:{x:width*(3/4),y:height/2,w:bw,h:bh,text:"Enter"},
		next:{x:width*(8/9),y:height*(1/2),w:bw,h:bh,text:"Next"},
		prev:{x:width*(1/9),y:height*(1/2),w:bw,h:bh,text:"Previous"},
	};

	// The hefty button function.  Works on all platforms tested.  Needs an exterior click call.
	button = function(con) {
		bux = con.x
		buy = con.y
		buw = con.w
		buh = con.h
		butext = con.text
		con.pressed = false;
		mouseover = false;

		rectMode(CENTER);

		if (mouseX>bux-buw/2&&mouseX<bux+buw/2 && mouseY>buy-buh/2&&mouseY<buy+buh/2) {
			mouseover = true;
		}

		if (mouseover) {
			fill(prim);
		} else {
			fill(sec);
		}
		stroke(prim);
		strokeWeight(buh/10);
		rect(bux,buy,buw,buh,buh/2.5);

		if (mouseover) {
			fill(sec);
		} else {
			fill(prim);
		}
		textFont(mainfont,buh/2);
		if (ismobile) {
			textSize(buh/3);
		}
		textAlign(CENTER,CENTER);
		text(butext,bux,buy);

		if (ismobile && mouseover) {
			con.pressed = true;
			return con.pressed;
		}

		if (mouseover && m) {
			con.pressed = true;
			return con.pressed;
		}
		return con.pressed;
	};

	// Standard button layout
	standardbuttons = function() {
		button(buttons.next);
		if (buttons.next.pressed) {
			if (ismobile) {
				if (flevel < 0) {
					page += 1;
					flevel = 255;
				}
			} else if (flevel < 80) {
				page += 1;
				flevel = 255;
			}
		};

		button(buttons.prev);
		if (buttons.prev.pressed) {
			if (ismobile) {
				if (flevel < 0) {
					page -= 1;
					flevel = 255;
				}
			} else if (flevel < 80) {
				page -= 1;
				flevel = 255;
			}

		};
	};

	// Displays an image properly.
	displaypanel = function(img,x,y) {
		imageMode(CENTER);
		pushMatrix();
		translate(x,y);
		scale(0.001*height,0.001*height);
			image(img,0,0);
		popMatrix();
	};

	// Creates a full page
	displaypage = function(chap,number) {
		background(backg);
		displaypanel(chap[number],width/2,height/2);
		standardbuttons();
		fade();
	};
	// Loads an array with full pages
	loadpages = function(cha,targ) {
		for (i = 0; i < cha.length; i ++) {
			targ.push(function() {fcha= cha; fi= i; displaypage(fcha,fi)});
		};
		targ[cha.length] = function() {
			background(backg);
			textSize(height/5);
			fill(sec);
			text("Under Construction",width/2,height/2);
		};
	};

	loadpanels("chap1/",chapter1length,chapter1);

	pages = [
		function() {
			background(backg)

			displaypanel(logo,width/4,height/2)

			button(buttons.start)
			if (buttons.start.pressed==true) {
				page += 1
			}
		},

	];
	loadpages(chapter1,pages);
};

draw = function() {
	pages[page]();
	text(pages,width/2,height/2);
};

keyPressed = function() {
	keys[keyCode] = true;
};
keyReleased = function() {
	keys[keyCode] = false;
};
mousePressed = function() {
	m = true;
};
mouseReleased = function() {
	m = false;
};
}};
<|endoftext|>


<commit_before>const EVENT_SHORTCUTS = <%= Drab.Config.get(:events_shorthands) |> Drab.Core.encode_js %>;
const EVENTS_TO_DISABLE = <%= Drab.Config.get(:events_to_disable_while_processing) |> Drab.Core.encode_js %>;

Drab.disable_drab_objects = function (disable) {
  <%= if Drab.Config.get(:disable_controls_when_disconnected) do %>
    var found =  document.querySelectorAll("[drab-event]");
    for (var i = 0; i < found.length; i++) {
      var element = found[i];
      element['disabled'] = disable;
    };
  <% end %>
};

//http://davidwalsh.name/javascript-debounce-function
function debounce(func, wait, immediate) {
  var timeout;
  return function () {
    var context = this,
        args = arguments;
    var later = function () {
      timeout = null;
      if (!immediate) func.apply(context, args);
    };
    var callNow = immediate && !timeout;
    clearTimeout(timeout);
    timeout = setTimeout(later, wait);
    if (callNow) func.apply(context, args);
  };
};

function payload(sender, event) {
  var p;
  if (sender) {
    p = default_payload(sender, event);
  } else {
    p = {};
  }

  for (var i = 0; i < Drab.additional_payloads.length; i++) {
    var fx = Drab.additional_payloads[i];
    p = Object.assign(p, fx(sender, event));
  }

  return p;
}

// default payload contains sender information and some info about event
function default_payload(sender, event) {
  var params = {};
  var form = closest(sender, function (el) {
    return el.nodeName == "FORM";
  });
  if (form) {
    var inputs = form.querySelectorAll("input, textarea, select");
    for (var i = 0; i < inputs.length; i++) {
      var input = inputs[i];
      var key = input.name || input.id || false;
      if (key) {
        if (input.type == "radio" || input.type == 'checkbox') {
          if (input.checked) {
            params[key] = input.value;
          }
        } else {
          params[key] = input.value;
        }
      }
    };
  }
  return {
    // by default, we pass back some sender properties
    id: sender.id,
    name: sender.name,
    class: sender.className,
    classes: sender.classList,
    text: sender.innerText,
    html: sender.innerHTML,
    value: sender.value,
    dataset: sender.dataset,
    drab_id: sender.getAttribute("drab-id"),
    event: {
      altKey: event.altKey,
      data: event.data,
      key: event.key,
      keyCode: event.keyCode,
      metaKey: event.metaKey,
      shiftKey: event.shiftKey,
      ctrlKey: event.ctlrKey,
      type: event.type,
      which: event.which,
      clientX: event.clientX,
      clientY: event.clientY,
      offsetX: event.offsetX,
      offsetY: event.offsetY,
      pageX: event.pageX,
      pageY: event.pageY,
      screenX: event.screenX,
      screenY: event.screenY
    },
    form: params
  };
}

function do_setid(whom) {
  if (!whom.getAttribute("drab-id")) {
    whom.setAttribute("drab-id", uuid());
  }
}

Drab.setid = function (whom) {
  if (Array.isArray(whom)) {
    for (var i = 0; i < whom.length; i++) {
      var x = whom[i]
      do_setid(x);
    };
  } else {
    do_setid(whom);
  }
  return whom.getAttribute("drab-id");
};

function update_event_handler(node, event, func) {
  // TODO: learn more about event listeners
  // node.removeEventListener(event, func)
  // node.addEventListener(event, func)
  node["on" + event] = func;
}

Drab.enable_drab_on = function(selector) {
  for (var i = 0; i < Drab.change.length; i++) {
    var fx = Drab.change[i];
    fx(selector);
  }
  Drab.set_event_handlers(selector);
}

function add_drab_attribute(node, event, handler, options) {
  if ((!event) || (!handler)) {
    Drab.error("Event and handler name must be set for: " + node);
  } else {
    var events_and_handlers = node.getAttribute("drab");
    node.setAttribute("drab",
      (events_and_handlers ? events_and_handlers + " " : "") +
      event + (options ? "#" + options : "") +
      ":" + handler
      );
  }
}

// re-read event handlers
Drab.set_event_handlers = function (obj) {
  var drab_objects = [];
  var drab_objects_shortcutted = [];
  var where = obj ? document.querySelector(obj).parentNode : document;

  // first serve the shortcut controls by adding the internal attribute
  for (var ei = 0; ei < EVENT_SHORTCUTS.length; ei++) {
    var ev = EVENT_SHORTCUTS[ei];

    drab_objects_shortcutted = where.querySelectorAll("[drab-" + ev + "]");
    for (var i = 0; i < drab_objects_shortcutted.length; i++) {
      var node = drab_objects_shortcutted[i];
      add_drab_attribute(node, ev, node.getAttribute("drab-" + ev), node.getAttribute("drab-options"));
    };
  }

  // long way, to be depreciated
  // drab-event= drab-handler= drab-options=
  var drab_objects_long = where.querySelectorAll("[drab-event]");
  for (var i = 0; i < drab_objects_long.length; i++) {
    var node = drab_objects_long[i];
    add_drab_attribute(
      node,
      node.getAttribute("drab-event"),
      node.getAttribute("drab-handler"),
      node.getAttribute("drab-options")
      )
  }

  var events_to_disable = EVENTS_TO_DISABLE;
  drab_objects = where.querySelectorAll("[drab]")

  for (var i = 0; i < drab_objects.length; i++) {
    var node = drab_objects[i];
    var events_and_handlers = node.getAttribute("drab").match(/\S+/g);
    for (var j = 0; j < events_and_handlers.length; j++) {
      var event_and_handler = events_and_handlers[j];
      var l = event_and_handler.split(":");
      var event_with_options = l[0].split("#");
      var event_name = event_with_options[0];
      var options = event_with_options[1];
      let handler_name = l[1];

      if ((!handler_name) || (!event_name)) {
        Drab.error("Drab attribute value '" + event_and_handler + "' incorrect for " + node)
      } else {
        var event_handler_function = function (event) {
          // disable current control - will be re-enabled after finish
          var n = this;
          <%= if Drab.Config.get(:disable_controls_while_processing) do %>
            if (events_to_disable.indexOf(event.type) >= 0) {
              n['disabled'] = true;
            }
          <% end %>
          Drab.setid(n);
          // send the message back to the server
          Drab.exec_elixir(
            handler_name,
            payload(n, event)
            <%= if Drab.Config.get(:disable_controls_while_processing) do %>
              , function() {
                  n['disabled'] = false
                }
            <% end %>
          );
          return false; // prevent default
        };

        // options. Wraps around event_handler_function, eg. debounce(event_handler_function, 500)
        var matched = /(\w+)\s*\((.*)\)/.exec(options);
        if (matched) {
          var fname = matched[1];
          var fargs = matched[2].replace(/^\s+|\s+$/g, ''); // strip whitespace
          var f = fname + "(event_handler_function" + (fargs == "" ? "" : ", " + fargs) + ")";
          update_event_handler(node, event_name, eval(f));
        } else {
          update_event_handler(node, event_name, event_handler_function);
        }
      }
    }
  };
};

Drab.on_load(function (drab) {
  drab.disable_drab_objects(true);
});

Drab.on_disconnect(function (drab) {
  drab.disable_drab_objects(true);
});

Drab.on_connect(function (resp, drab) {
  drab.set_event_handlers();

  // re-enable drab controls
  drab.disable_drab_objects(false);
});

<commit_msg>no duplicates in drab attribute<commit_after>const EVENT_SHORTCUTS = <%= Drab.Config.get(:events_shorthands) |> Drab.Core.encode_js %>;
const EVENTS_TO_DISABLE = <%= Drab.Config.get(:events_to_disable_while_processing) |> Drab.Core.encode_js %>;

Drab.disable_drab_objects = function (disable) {
  <%= if Drab.Config.get(:disable_controls_when_disconnected) do %>
    var found =  document.querySelectorAll("[drab-event]");
    for (var i = 0; i < found.length; i++) {
      var element = found[i];
      element['disabled'] = disable;
    };
  <% end %>
};

//http://davidwalsh.name/javascript-debounce-function
function debounce(func, wait, immediate) {
  var timeout;
  return function () {
    var context = this,
        args = arguments;
    var later = function () {
      timeout = null;
      if (!immediate) func.apply(context, args);
    };
    var callNow = immediate && !timeout;
    clearTimeout(timeout);
    timeout = setTimeout(later, wait);
    if (callNow) func.apply(context, args);
  };
};

function payload(sender, event) {
  var p;
  if (sender) {
    p = default_payload(sender, event);
  } else {
    p = {};
  }

  for (var i = 0; i < Drab.additional_payloads.length; i++) {
    var fx = Drab.additional_payloads[i];
    p = Object.assign(p, fx(sender, event));
  }

  return p;
}

// default payload contains sender information and some info about event
function default_payload(sender, event) {
  var params = {};
  var form = closest(sender, function (el) {
    return el.nodeName == "FORM";
  });
  if (form) {
    var inputs = form.querySelectorAll("input, textarea, select");
    for (var i = 0; i < inputs.length; i++) {
      var input = inputs[i];
      var key = input.name || input.id || false;
      if (key) {
        if (input.type == "radio" || input.type == 'checkbox') {
          if (input.checked) {
            params[key] = input.value;
          }
        } else {
          params[key] = input.value;
        }
      }
    };
  }
  return {
    // by default, we pass back some sender properties
    id: sender.id,
    name: sender.name,
    class: sender.className,
    classes: sender.classList,
    text: sender.innerText,
    html: sender.innerHTML,
    value: sender.value,
    dataset: sender.dataset,
    drab_id: sender.getAttribute("drab-id"),
    event: {
      altKey: event.altKey,
      data: event.data,
      key: event.key,
      keyCode: event.keyCode,
      metaKey: event.metaKey,
      shiftKey: event.shiftKey,
      ctrlKey: event.ctlrKey,
      type: event.type,
      which: event.which,
      clientX: event.clientX,
      clientY: event.clientY,
      offsetX: event.offsetX,
      offsetY: event.offsetY,
      pageX: event.pageX,
      pageY: event.pageY,
      screenX: event.screenX,
      screenY: event.screenY
    },
    form: params
  };
}

function do_setid(whom) {
  if (!whom.getAttribute("drab-id")) {
    whom.setAttribute("drab-id", uuid());
  }
}

Drab.setid = function (whom) {
  if (Array.isArray(whom)) {
    for (var i = 0; i < whom.length; i++) {
      var x = whom[i]
      do_setid(x);
    };
  } else {
    do_setid(whom);
  }
  return whom.getAttribute("drab-id");
};

function update_event_handler(node, event, func) {
  // TODO: learn more about event listeners
  // node.removeEventListener(event, func)
  // node.addEventListener(event, func)
  node["on" + event] = func;
}

Drab.enable_drab_on = function(selector) {
  for (var i = 0; i < Drab.change.length; i++) {
    var fx = Drab.change[i];
    fx(selector);
  }
  Drab.set_event_handlers(selector);
}

function add_drab_attribute(node, event, handler, options) {
  if ((!event) || (!handler)) {
    Drab.error("Event and handler name must be set for: " + node);
  } else {
    var events_and_handlers = node.getAttribute("drab") || "";
    var new_event_handler = event + (options ? "#" + options : "") + ":" + handler;
    if (events_and_handlers.indexOf(new_event_handler) == -1) {
      node.setAttribute("drab",
        events_and_handlers + " " + new_event_handler
        );
    }
  }
}

// re-read event handlers
Drab.set_event_handlers = function (obj) {
  var drab_objects = [];
  var drab_objects_shortcutted = [];
  var where = obj ? document.querySelector(obj).parentNode : document;

  // first serve the shortcut controls by adding the internal attribute
  for (var ei = 0; ei < EVENT_SHORTCUTS.length; ei++) {
    var ev = EVENT_SHORTCUTS[ei];

    drab_objects_shortcutted = where.querySelectorAll("[drab-" + ev + "]");
    for (var i = 0; i < drab_objects_shortcutted.length; i++) {
      var node = drab_objects_shortcutted[i];
      add_drab_attribute(node, ev, node.getAttribute("drab-" + ev), node.getAttribute("drab-options"));
    };
  }

  // long way, to be depreciated
  // drab-event= drab-handler= drab-options=
  var drab_objects_long = where.querySelectorAll("[drab-event]");
  for (var i = 0; i < drab_objects_long.length; i++) {
    var node = drab_objects_long[i];
    add_drab_attribute(
      node,
      node.getAttribute("drab-event"),
      node.getAttribute("drab-handler"),
      node.getAttribute("drab-options")
      )
  }

  var events_to_disable = EVENTS_TO_DISABLE;
  drab_objects = where.querySelectorAll("[drab]")

  for (var i = 0; i < drab_objects.length; i++) {
    var node = drab_objects[i];
    var events_and_handlers = node.getAttribute("drab").match(/\S+/g);
    for (var j = 0; j < events_and_handlers.length; j++) {
      var event_and_handler = events_and_handlers[j];
      var l = event_and_handler.split(":");
      var event_with_options = l[0].split("#");
      var event_name = event_with_options[0];
      var options = event_with_options[1];
      let handler_name = l[1];

      if ((!handler_name) || (!event_name)) {
        Drab.error("Drab attribute value '" + event_and_handler + "' incorrect for " + node)
      } else {
        var event_handler_function = function (event) {
          // disable current control - will be re-enabled after finish
          var n = this;
          <%= if Drab.Config.get(:disable_controls_while_processing) do %>
            if (events_to_disable.indexOf(event.type) >= 0) {
              n['disabled'] = true;
            }
          <% end %>
          Drab.setid(n);
          // send the message back to the server
          Drab.exec_elixir(
            handler_name,
            payload(n, event)
            <%= if Drab.Config.get(:disable_controls_while_processing) do %>
              , function() {
                  n['disabled'] = false
                }
            <% end %>
          );
          return false; // prevent default
        };

        // options. Wraps around event_handler_function, eg. debounce(event_handler_function, 500)
        var matched = /(\w+)\s*\((.*)\)/.exec(options);
        if (matched) {
          var fname = matched[1];
          var fargs = matched[2].replace(/^\s+|\s+$/g, ''); // strip whitespace
          var f = fname + "(event_handler_function" + (fargs == "" ? "" : ", " + fargs) + ")";
          update_event_handler(node, event_name, eval(f));
        } else {
          update_event_handler(node, event_name, event_handler_function);
        }
      }
    }
  };
};

Drab.on_load(function (drab) {
  drab.disable_drab_objects(true);
});

Drab.on_disconnect(function (drab) {
  drab.disable_drab_objects(true);
});

Drab.on_connect(function (resp, drab) {
  drab.set_event_handlers();

  // re-enable drab controls
  drab.disable_drab_objects(false);
});

<|endoftext|>


<commit_before>var fs = require('fs'),
    os = require('os'),
    path = require('path'),
    spawn = require('child_process').spawn,
    rimraf = require('rimraf'),
    mkdirp = require('mkdirp'),
    async = require('async'),
    pkgcloud = require('pkgcloud'),
    semver = require('semver'),
    back = require('back'),
    uidNumber = require('uid-number'),
    killer = require('killer');

function noop() {}

//
// Starts an application.
//
exports.start = function (options, callback) {
  if (!options.app) {
    return callback(new Error('options.app is required.'));
  }

  options.packedSnapshotPath = path.join(options.runDir, 'snapshot.tgz');
  options.snapshotPath       = path.join(options.runDir, 'snapshot');
  options.packagePath        = path.join(options.snapshotPath, 'package');
  options.tmpPath            = path.join(options.snapshotPath, '.tmp');

  //
  // ### function runApp()
  // Performs the app startup sequence without
  // modifying the file system.
  //
  function runApp(err) {
    if (err) {
      return callback(err);
    }

    async.series([
      async.apply(readPackageJSON, options),
      async.apply(findEngines, options),
      async.apply(readEnv, options),
      async.apply(restrictFilesystem, options),
      async.apply(startApp, options)
    ], callback);
  }

  //
  // If this is a "soft" start then do not
  // modify the underlying file system: assume
  // the snapshot is already in-place.
  //
  if (options.soft) {
    return runApp();
  }

  async.series([
    async.apply(clean, options),
    async.apply(mkdirp, options.runDir),
    async.apply(fetch, options),
    async.apply(unpack, options),
  ], runApp);
};

exports.restart = function (options, callback) {
  var logger = options.logger;

  fs.exists(options.pidFile, function (exists) {
    if (!exists) {
      return callback(new Error(options.pidFile + ' does not exist.'));
    }

    fs.readFile(options.pidFile, 'utf8', function (err, content) {
      if (err) {
        return callback(err);
      }

      content = parseInt(content, 10);
      logger.info('Sending SIGUSR1 - ' + content);
      if (!Number.isNaN(content)) {
        try { process.kill(content, 'SIGUSR1') }
        catch (ex) { logger.info('SIGUSR1 Error - ' + ex.message) }
      }

      callback();
    });
  });
};

exports.stop = function (options, callback) {
  var logger = options.logger;

  fs.readFile(options.pidFile, 'utf8', function (err, content) {
    if (err) {
      return callback(err);
    }

    content = parseInt(content, 10);
    if (Number.isNaN(content)) {
      //
      // If the content in the pidFile is bad, then
      // **clean the server hard**.
      //
      return !options.soft
        ? clean(options, callback)
        : callback();
    }

    logger.info('Sending terminate signal - ' + content);
    killer({
      pid: content,
      timeout: 10000,
      interval: 100
    }, function () {
      async.parallel([
        function removeCode(next) {
          return !options.soft
            ? rimraf(options.runDir, next.bind(null, null))
            : next();
        },
        function removePid(next) {
          fs.unlink(options.pidFile, next.bind(null, null));
        }
      ], callback);
    });
  });
};

var clean = exports.clean = function clean(options, callback) {
  var commands = [
    ['pkill', '-9', 'aeternum'],
    ['pkill', 'forza'],
    ['rm', '-rf', options.pidFile || path.join(process.env.HOME, 'app.pid')],
    ['rm', '-rf', options.runDir  || ('/' + path.join('opt', 'run'))]
  ];

  var logger = options.logger;

  logger.info('Cleaning ' + options.runDir);
  async.forEachSeries(commands, function (args, next) {
    var command = args.shift(),
        child   = spawn(command, args);

    child.stdout.on('data', function (d) { logger.info('' + d) });
    child.stderr.on('data', function (d) { logger.info('' + d) });
    child.on('exit', next.bind(null, null));
  }, callback);
};

var fetch = exports.fetch = function fetch(options, callback) {
  var client = pkgcloud.storage.createClient(options.storage),
      packedSnapshotPath = options.packedSnapshotPath,
      logger = options.logger,
      maxTries = 5,
      tries = 0;

  function doFetch(callback) {
    ++tries;

    var stream = client.download({
      container: options.storage.container,
      remote: [ options.app.user, options.app.name, options.app.version ].join('-') + '.tgz'
    });

    stream.pipe(fs.createWriteStream(packedSnapshotPath));
    stream.on('error', function (err) {
      logger.error('Error while fetching snapshot: ' + err.message);
      return tries === maxTries
        ? callback(err)
        : doFetch(callback);
    });

    stream.on('end', function () {
      logger.info('Application snapshot fetched.');
      callback();
    });
  }

  logger.info('Fetching application snapshot...');
  doFetch(callback);
};

var unpack = exports.unpack = function unpack(options, callback) {
  var snapshotPath = options.snapshotPath,
      logger = options.logger,
      child;

  logger.info('Unpacking snapshot...');
  mkdirp(options.tmpPath, function (err) {
    if (err) {
      return callback(err);
    }

    child = spawn('tar', ['-xf', options.packedSnapshotPath], {
      cwd: snapshotPath
    });

    child.on('exit', function (code, signal) {
      fs.unlink(options.packedSnapshotPath, noop);

      if (code !== 0) {
        return callback(
          new Error('`tar` exited with ' + (code
            ? 'code ' + code.toString()
            : 'signal ' + signal.toString()
         ))
        );
      }

      return callback();
    });
  });
};

var readPackageJSON = exports.readPackageJSON = function readPackageJSON(options, callback) {
  var logger = options.logger;

  logger.info('Reading `package.json`...');

  fs.readFile(path.join(options.packagePath, 'package.json'), function (err, data) {
    if (err) {
      return callback(new Error('Error while reading `package.json`: ' + err.message));
    }

    try {
      data = JSON.parse(data);
    }
    catch (ex) {
      return callback(new Error('Error while parsing `package.json`: ' + ex.message));
    }

    options['package.json'] = data;
    if (!data.scripts || typeof data.scripts.start !== 'string' || !data.scripts.start) {
      return callback(new Error('`scripts.start` property is required'));
    }

    callback();
  });
};

var findEngines = exports.findEnginess = function findEngines(options, callback) {
  var pkgEngines = options['package.json'].engines,
      paths = [];

  if (!pkgEngines || !Object.keys(pkgEngines).length) {
    pkgEngines = { 'node': (options.engines.node && options.engines.node.default) || '0.6.x' };
  }

  async.map(
    Object.keys(pkgEngines),
    function resolveEngine(engine, callback) {
      //
      // Remark: Should throw an error if this particular
      // "engine" is required (e.g. node).
      //
      if (!options.engines[engine]) {
        return callback(null, null);
      }

      var versionRange = pkgEngines[engine],
          enginePath = options.engines[engine].path;

      if (!Object.hasOwnProperty.call(options.engines, engine) || !enginePath) {
        return callback(new Error('Unknown engine ' + engine));
      }

      if (!semver.validRange(versionRange)) {
        return callback(new Error(versionRange + ' is not a valid SemVer version range!'));
      }

      fs.readdir(enginePath, function(err, versions) {
        if (err) {
          err.message = 'Cannot read engines directory: ' + err.message;
          return callback(err);
        }

        var version = semver.maxSatisfying(versions, versionRange);
        if (!version) {
          return callback(new Error('No satisfying ' + engine + ' version found.\n'));
        }

        callback(null, path.join(enginePath, version, 'bin'));
      });
    },
    function saveEngines(err, engines) {
      options.enginePaths = engines.filter(Boolean);
      callback(err);
    }
  );
};

var readEnv = exports.readEnv = function readEnv(options, callback) {
  var env;

  if (options.app.env) {
    try { env = JSON.parse(options.app.env) }
    catch (ex) { }
  }

  options.app.env          = env || {};
  options.app.env.NODE_ENV = options.app.env.NODE_ENV || 'production';
  options.app.env.USER     = 'solenoid';
  options.app.env.HOME     = options.packagePath;
  options.app.env.TEMP     = options.tmpPath;
  options.app.env.TMPDIR   = options.tmpPath;
  callback();
};

var restrictFilesystem = exports.restrictFilesystem = function restrictFilesystem(options, callback) {
  //
  // Helper function to run the specified command
  // and assert a non-zero exit code.
  //
  function runFs(cmd, args, msg, next) {
    spawn(cmd, args).on('exit', function (code) {
      return code
        ? next(new Error(msg))
        : next();
    });
  }

  async.parallel([
    async.apply(
      runFs,
      'chown',
      ['-R', 'solenoid:nogroup', options.runDir],
      'Unable to grab ownership for ' + options.runDir
    ),
    async.apply(
      runFs,
      'chmod',
      ['-R', '777', options.runDir],
      'Unable to change permissions for ' + options.runDir
    )
  ], callback);
};

var startApp = exports.startApp = function startApp(options, callback) {
  var backoff = { retries: options.retries, minDelay: options.minDelay },
      startLog = path.join(options.runDir, 'start.log'),
      logger = options.logger,
      responded = false,
      retries = 0,
      args = [],
      pid = '',
      env = {},
      nodeModulesBin,
      backupPath,
      initPath,
      attempt,
      start,
      child,
      tail;

  logger.info('Starting application...');

  //
  // Set the node_modules .bin path to be set in various locations
  //
  nodeModulesBin = path.join(options.snapshotPath, 'package', 'node_modules', '.bin');

  //
  // Setup the `aeternum` arguments.
  //
  args = ['start', '--min-uptime', options.minUptime, '-o', path.join(options.runDir, 'forza.log'), '--', 'forza'];

  //
  // Setup `forza` arguments.
  //
  args.push('-h', options.instruments.host);
  args.push('-p', (options.instruments.port || 8556).toString());

  args.push('--start-log', startLog);
  args.push('--app-user', options.app.user);
  args.push('--app-name', options.app.name);
  args.push('--');

  //
  // Merge the env with `app.env` and `process.env`.
  //
  [process.env, options.app.env].forEach(function (obj) {
    if (!obj || typeof obj !== 'object') { return }
    Object.keys(obj).forEach(function (key) {
      env[key] = obj[key];
    });
  });

  env.PATH = [nodeModulesBin]
    .concat(options.enginePaths)
    .concat(env.PATH.split(':'))
    .join(':');

  //
  // Remark: we have to do custom detection of coffee-script as it needs to be
  // prepended with node (#!/usr/bin/env node is not intelligent enough)
  //
  start = options['package.json'].scripts.start.split(' ')

  //
  // Set possible path locations where a coffee binary would live and replace
  // 'coffee' with one of these paths
  //
  if (start[0] === 'coffee') {
    initPath = path.resolve(nodeModulesBin, start[0]);
    backupPath = path.resolve(options.enginePaths[0], start[0]);
    start[0] = fs.existsSync(initPath)
      ? initPath
      : backupPath;

    start.unshift('node');
  }
  else if (start[0] !== 'node') {
    start.unshift('node');
  }

  args.push.apply(args, start);
  uidNumber('solenoid', 'nogroup', function (err, uid, gid) {
    if (err) {
      //
      // Remark: should we allow for errors here?
      //
      return callback(new Error('Could not get uid/gid of child process.'));
    }

    //
    // ### function done (err)
    // Responds once to the callback
    //
    function done(err) {
      if (!responded) {
        responded = true;
        callback.apply(null, arguments);
      }
    }

    //
    // ### function awaitStart ()
    // Writes `options.pidFile` and tails the `forza`
    // start log for the start event.
    //
    function awaitStart() {
      async.series([
        function writePid(next) {
          logger.info('Writing pidfile: ' + options.pidFile);
          fs.writeFile(options.pidFile, pid, next.bind(null, null));
        },
        function tailLog(next) {
          var allLog = '',
              json;

          //
          // Helper function which attempts to respawn
          // the `tail` process in the (rare) case
          // that forza has not already written to it.
          //
          function retry(err) {
            //
            // Exponential backoff for retrying the tail process
            //
            return back(function (fail) {
              if(fail) {
                attempt = null;
                return next(err);
              }
              tailLog(next);
            }, backoff);
          }

          logger.info('Tailing forza log: ' + startLog);
          tail = spawn('tail', [ startLog ]);
          tail.on('error', retry);

          //
          // Remark: use close event so we ensure we got all possible data out
          // of the streams
          //
          tail.on('close', function (code, signal) {
            logger.info('Tail closing..');
            //
            // Remark: tail.kill() sends sigterm so ensure we have the json and
            // we killed it appropriately
            //
            if (typeof json !== 'undefined' || signal === 'SIGTERM') {
              return next();
            }

            retry(new Error('tailing start log exited poorly with code ' + code || signal))
          });

          tail.stdout.on('data', function (data) {
            allLog += data;

            try { json = JSON.parse(allLog) }
            catch (ex) { return }

            // Use console.log to ensure it is JSON parseable
            console.log(JSON.stringify(json));

            try { tail.kill() }
            catch (ex) { }
          });
        }
      ], done);
    }

    logger.info('Spawn: ' + args.join(' '));
    child = spawn('aeternum', args, {
      cwd: path.join(options.snapshotPath, 'package'),
      env: env,
      uid: uid,
      gid: gid
    });

    child.on('error', done);
    child.stdout.on('data', function (chunk) {
      pid += chunk.toString('utf8');
    });

    child.stdout.on('end', function () {
      if (!responded) {
        pid = parseInt(pid, 10).toString();
        logger.info('`aeternum` pid: ' + pid);
        awaitStart();
      }
    });
  });
};

<commit_msg>[fix] add some logging around retry<commit_after>var fs = require('fs'),
    os = require('os'),
    path = require('path'),
    spawn = require('child_process').spawn,
    rimraf = require('rimraf'),
    mkdirp = require('mkdirp'),
    async = require('async'),
    pkgcloud = require('pkgcloud'),
    semver = require('semver'),
    back = require('back'),
    uidNumber = require('uid-number'),
    killer = require('killer');

function noop() {}

//
// Starts an application.
//
exports.start = function (options, callback) {
  if (!options.app) {
    return callback(new Error('options.app is required.'));
  }

  options.packedSnapshotPath = path.join(options.runDir, 'snapshot.tgz');
  options.snapshotPath       = path.join(options.runDir, 'snapshot');
  options.packagePath        = path.join(options.snapshotPath, 'package');
  options.tmpPath            = path.join(options.snapshotPath, '.tmp');

  //
  // ### function runApp()
  // Performs the app startup sequence without
  // modifying the file system.
  //
  function runApp(err) {
    if (err) {
      return callback(err);
    }

    async.series([
      async.apply(readPackageJSON, options),
      async.apply(findEngines, options),
      async.apply(readEnv, options),
      async.apply(restrictFilesystem, options),
      async.apply(startApp, options)
    ], callback);
  }

  //
  // If this is a "soft" start then do not
  // modify the underlying file system: assume
  // the snapshot is already in-place.
  //
  if (options.soft) {
    return runApp();
  }

  async.series([
    async.apply(clean, options),
    async.apply(mkdirp, options.runDir),
    async.apply(fetch, options),
    async.apply(unpack, options),
  ], runApp);
};

exports.restart = function (options, callback) {
  var logger = options.logger;

  fs.exists(options.pidFile, function (exists) {
    if (!exists) {
      return callback(new Error(options.pidFile + ' does not exist.'));
    }

    fs.readFile(options.pidFile, 'utf8', function (err, content) {
      if (err) {
        return callback(err);
      }

      content = parseInt(content, 10);
      logger.info('Sending SIGUSR1 - ' + content);
      if (!Number.isNaN(content)) {
        try { process.kill(content, 'SIGUSR1') }
        catch (ex) { logger.info('SIGUSR1 Error - ' + ex.message) }
      }

      callback();
    });
  });
};

exports.stop = function (options, callback) {
  var logger = options.logger;

  fs.readFile(options.pidFile, 'utf8', function (err, content) {
    if (err) {
      return callback(err);
    }

    content = parseInt(content, 10);
    if (Number.isNaN(content)) {
      //
      // If the content in the pidFile is bad, then
      // **clean the server hard**.
      //
      return !options.soft
        ? clean(options, callback)
        : callback();
    }

    logger.info('Sending terminate signal - ' + content);
    killer({
      pid: content,
      timeout: 10000,
      interval: 100
    }, function () {
      async.parallel([
        function removeCode(next) {
          return !options.soft
            ? rimraf(options.runDir, next.bind(null, null))
            : next();
        },
        function removePid(next) {
          fs.unlink(options.pidFile, next.bind(null, null));
        }
      ], callback);
    });
  });
};

var clean = exports.clean = function clean(options, callback) {
  var commands = [
    ['pkill', '-9', 'aeternum'],
    ['pkill', 'forza'],
    ['rm', '-rf', options.pidFile || path.join(process.env.HOME, 'app.pid')],
    ['rm', '-rf', options.runDir  || ('/' + path.join('opt', 'run'))]
  ];

  var logger = options.logger;

  logger.info('Cleaning ' + options.runDir);
  async.forEachSeries(commands, function (args, next) {
    var command = args.shift(),
        child   = spawn(command, args);

    child.stdout.on('data', function (d) { logger.info('' + d) });
    child.stderr.on('data', function (d) { logger.info('' + d) });
    child.on('exit', next.bind(null, null));
  }, callback);
};

var fetch = exports.fetch = function fetch(options, callback) {
  var client = pkgcloud.storage.createClient(options.storage),
      packedSnapshotPath = options.packedSnapshotPath,
      logger = options.logger,
      maxTries = 5,
      tries = 0;

  function doFetch(callback) {
    ++tries;

    var stream = client.download({
      container: options.storage.container,
      remote: [ options.app.user, options.app.name, options.app.version ].join('-') + '.tgz'
    });

    stream.pipe(fs.createWriteStream(packedSnapshotPath));
    stream.on('error', function (err) {
      logger.error('Error while fetching snapshot: ' + err.message);
      return tries === maxTries
        ? callback(err)
        : doFetch(callback);
    });

    stream.on('end', function () {
      logger.info('Application snapshot fetched.');
      callback();
    });
  }

  logger.info('Fetching application snapshot...');
  doFetch(callback);
};

var unpack = exports.unpack = function unpack(options, callback) {
  var snapshotPath = options.snapshotPath,
      logger = options.logger,
      child;

  logger.info('Unpacking snapshot...');
  mkdirp(options.tmpPath, function (err) {
    if (err) {
      return callback(err);
    }

    child = spawn('tar', ['-xf', options.packedSnapshotPath], {
      cwd: snapshotPath
    });

    child.on('exit', function (code, signal) {
      fs.unlink(options.packedSnapshotPath, noop);

      if (code !== 0) {
        return callback(
          new Error('`tar` exited with ' + (code
            ? 'code ' + code.toString()
            : 'signal ' + signal.toString()
         ))
        );
      }

      return callback();
    });
  });
};

var readPackageJSON = exports.readPackageJSON = function readPackageJSON(options, callback) {
  var logger = options.logger;

  logger.info('Reading `package.json`...');

  fs.readFile(path.join(options.packagePath, 'package.json'), function (err, data) {
    if (err) {
      return callback(new Error('Error while reading `package.json`: ' + err.message));
    }

    try {
      data = JSON.parse(data);
    }
    catch (ex) {
      return callback(new Error('Error while parsing `package.json`: ' + ex.message));
    }

    options['package.json'] = data;
    if (!data.scripts || typeof data.scripts.start !== 'string' || !data.scripts.start) {
      return callback(new Error('`scripts.start` property is required'));
    }

    callback();
  });
};

var findEngines = exports.findEnginess = function findEngines(options, callback) {
  var pkgEngines = options['package.json'].engines,
      paths = [];

  if (!pkgEngines || !Object.keys(pkgEngines).length) {
    pkgEngines = { 'node': (options.engines.node && options.engines.node.default) || '0.6.x' };
  }

  async.map(
    Object.keys(pkgEngines),
    function resolveEngine(engine, callback) {
      //
      // Remark: Should throw an error if this particular
      // "engine" is required (e.g. node).
      //
      if (!options.engines[engine]) {
        return callback(null, null);
      }

      var versionRange = pkgEngines[engine],
          enginePath = options.engines[engine].path;

      if (!Object.hasOwnProperty.call(options.engines, engine) || !enginePath) {
        return callback(new Error('Unknown engine ' + engine));
      }

      if (!semver.validRange(versionRange)) {
        return callback(new Error(versionRange + ' is not a valid SemVer version range!'));
      }

      fs.readdir(enginePath, function(err, versions) {
        if (err) {
          err.message = 'Cannot read engines directory: ' + err.message;
          return callback(err);
        }

        var version = semver.maxSatisfying(versions, versionRange);
        if (!version) {
          return callback(new Error('No satisfying ' + engine + ' version found.\n'));
        }

        callback(null, path.join(enginePath, version, 'bin'));
      });
    },
    function saveEngines(err, engines) {
      options.enginePaths = engines.filter(Boolean);
      callback(err);
    }
  );
};

var readEnv = exports.readEnv = function readEnv(options, callback) {
  var env;

  if (options.app.env) {
    try { env = JSON.parse(options.app.env) }
    catch (ex) { }
  }

  options.app.env          = env || {};
  options.app.env.NODE_ENV = options.app.env.NODE_ENV || 'production';
  options.app.env.USER     = 'solenoid';
  options.app.env.HOME     = options.packagePath;
  options.app.env.TEMP     = options.tmpPath;
  options.app.env.TMPDIR   = options.tmpPath;
  callback();
};

var restrictFilesystem = exports.restrictFilesystem = function restrictFilesystem(options, callback) {
  //
  // Helper function to run the specified command
  // and assert a non-zero exit code.
  //
  function runFs(cmd, args, msg, next) {
    spawn(cmd, args).on('exit', function (code) {
      return code
        ? next(new Error(msg))
        : next();
    });
  }

  async.parallel([
    async.apply(
      runFs,
      'chown',
      ['-R', 'solenoid:nogroup', options.runDir],
      'Unable to grab ownership for ' + options.runDir
    ),
    async.apply(
      runFs,
      'chmod',
      ['-R', '777', options.runDir],
      'Unable to change permissions for ' + options.runDir
    )
  ], callback);
};

var startApp = exports.startApp = function startApp(options, callback) {
  var backoff = { retries: options.retries, minDelay: options.minDelay },
      startLog = path.join(options.runDir, 'start.log'),
      logger = options.logger,
      responded = false,
      retries = 0,
      args = [],
      pid = '',
      env = {},
      nodeModulesBin,
      backupPath,
      initPath,
      attempt,
      start,
      child,
      tail;

  logger.info('Starting application...');

  //
  // Set the node_modules .bin path to be set in various locations
  //
  nodeModulesBin = path.join(options.snapshotPath, 'package', 'node_modules', '.bin');

  //
  // Setup the `aeternum` arguments.
  //
  args = ['start', '--min-uptime', options.minUptime, '-o', path.join(options.runDir, 'forza.log'), '--', 'forza'];

  //
  // Setup `forza` arguments.
  //
  args.push('-h', options.instruments.host);
  args.push('-p', (options.instruments.port || 8556).toString());

  args.push('--start-log', startLog);
  args.push('--app-user', options.app.user);
  args.push('--app-name', options.app.name);
  args.push('--');

  //
  // Merge the env with `app.env` and `process.env`.
  //
  [process.env, options.app.env].forEach(function (obj) {
    if (!obj || typeof obj !== 'object') { return }
    Object.keys(obj).forEach(function (key) {
      env[key] = obj[key];
    });
  });

  env.PATH = [nodeModulesBin]
    .concat(options.enginePaths)
    .concat(env.PATH.split(':'))
    .join(':');

  //
  // Remark: we have to do custom detection of coffee-script as it needs to be
  // prepended with node (#!/usr/bin/env node is not intelligent enough)
  //
  start = options['package.json'].scripts.start.split(' ')

  //
  // Set possible path locations where a coffee binary would live and replace
  // 'coffee' with one of these paths
  //
  if (start[0] === 'coffee') {
    initPath = path.resolve(nodeModulesBin, start[0]);
    backupPath = path.resolve(options.enginePaths[0], start[0]);
    start[0] = fs.existsSync(initPath)
      ? initPath
      : backupPath;

    start.unshift('node');
  }
  else if (start[0] !== 'node') {
    start.unshift('node');
  }

  args.push.apply(args, start);
  uidNumber('solenoid', 'nogroup', function (err, uid, gid) {
    if (err) {
      //
      // Remark: should we allow for errors here?
      //
      return callback(new Error('Could not get uid/gid of child process.'));
    }

    //
    // ### function done (err)
    // Responds once to the callback
    //
    function done(err) {
      if (!responded) {
        responded = true;
        callback.apply(null, arguments);
      }
    }

    //
    // ### function awaitStart ()
    // Writes `options.pidFile` and tails the `forza`
    // start log for the start event.
    //
    function awaitStart() {
      async.series([
        function writePid(next) {
          logger.info('Writing pidfile: ' + options.pidFile);
          fs.writeFile(options.pidFile, pid, next.bind(null, null));
        },
        function tailLog(next) {
          var allLog = '',
              json;

          //
          // Helper function which attempts to respawn
          // the `tail` process in the (rare) case
          // that forza has not already written to it.
          //
          function retry(err) {
            //
            // Exponential backoff for retrying the tail process
            //
            return back(function (fail) {
              if(fail) {
                logger.info('Tail failed after ' + attempt.retries + ' retries');
                attempt = null;
                return next(err);
              }
              logger.info('Retry # ' + attempt.attempt + ' with ' + attempt.timeout + 'ms interval');
              tailLog(next);
            }, backoff);
          }

          logger.info('Tailing forza log: ' + startLog);
          tail = spawn('tail', [ startLog ]);
          tail.on('error', retry);

          //
          // Remark: use close event so we ensure we got all possible data out
          // of the streams
          //
          tail.on('close', function (code, signal) {
            logger.info('Tail closing..');
            //
            // Remark: tail.kill() sends sigterm so ensure we have the json and
            // we killed it appropriately
            //
            if (typeof json !== 'undefined' || signal === 'SIGTERM') {
              return next();
            }

            retry(new Error('tailing start log exited poorly with code ' + code || signal))
          });

          tail.stdout.on('data', function (data) {
            allLog += data;

            try { json = JSON.parse(allLog) }
            catch (ex) { return }

            // Use console.log to ensure it is JSON parseable
            console.log(JSON.stringify(json));

            try { tail.kill() }
            catch (ex) { }
          });
        }
      ], done);
    }

    logger.info('Spawn: ' + args.join(' '));
    child = spawn('aeternum', args, {
      cwd: path.join(options.snapshotPath, 'package'),
      env: env,
      uid: uid,
      gid: gid
    });

    child.on('error', done);
    child.stdout.on('data', function (chunk) {
      pid += chunk.toString('utf8');
    });

    child.stdout.on('end', function () {
      if (!responded) {
        pid = parseInt(pid, 10).toString();
        logger.info('`aeternum` pid: ' + pid);
        awaitStart();
      }
    });
  });
};

<|endoftext|>


<commit_before>var fs = require('fs'),
    os = require('os'),
    path = require('path'),
    spawn = require('child_process').spawn,
    async = require('async'),
    pkgcloud = require('pkgcloud'),
    useradd = require('useradd');

function noop() {}

//
// Starts an application.
//
module.exports = function (options, callback) {
  async.parallel([
    async.apply(fetch, options),
    async.apply(createUser, options)
  ], function (err) {
    if (err) {
      return callback(err);
    }

    async.series([
      async.apply(unpack, options),
      async.apply(readPackageJSON, options),
      async.apply(start, options)
    ], callback);
  });
};

module.exports.fetch = function fetch(options, callback) {
  var client = pkgcloud.storage.createClient(options.storage),
      tries = 0,
      maxTries = 5,
      packedSnapshotPath = path.join(os.tmpDir(), 'snapshot.tgz');

  options.packedSnapshotPath = packedSnapshotPath;

  function doFetch(callback) {
    ++tries;

    client.download({
      container: options.storage.container,
      remote: [ options.app.user, options.app.name, options.app.version ].join('-') + '.tgz'
    })
      .pipe(fs.createWriteStream(packedSnapshotPath))
      .on('error', function (err) {
        console.error('Error while fetching snapshot: ' + err.message);
        return tries === maxTries
          ? callback(err)
          : doFetch(callback);
      });
  }

  console.log('Fetching application snapshot...');

  doFetch(callback);
};

//
// Create a dedicated user account.
//
module.exports.createUser = function createUser(options, callback) {
  console.log('Creating user...');

  useradd({
    login: 'nodejitsu-' + options.app.user,
    shell: false,
    home: false
  }, callback);
};

module.exports.unpack = function unpack(options, callback) {
  var snapshotPath = path.join(os.tmpDir(), 'snapshot'),
      child;

  console.log('Unpacking snapshot...');

  options.snapshotPath = snapshotPath;
  options.packagePath = path.join(snapshotPath, 'package');

  child = spawn('tar', ['-xf', options.packedSnapshotPath], {
    cwd: snapshotPath
  });

  child.on('exit', function (code, signal) {
    fs.unlink(options.packedSnapshotPath, noop);

    if (code !== 0) {
      return callback(
        new Error('`tar` exited with ' + (code
          ? 'code ' + code.toString()
          : 'signal ' + signal.toString()
       ));
      );
    }

    return callback();
  });
};

module.exports.readPackageJSON = function readPackageJSON(options, callback) {
  console.log('Reading `package.json`...');

  fs.readFile(path.join(options.packagePath, 'package.json'), function (err, data) {
    if (err) {
      return callback(new Error('Error while reading `package.json`: ' + err.message));
    }

    try {
      data = JSON.parse(data);
    }
    catch (ex) {
      return callback(new Error('Error while parsing `package.json`: ' + err.message));
    }

    options['package.json'] = data;
    callback();
  });
};

module.exports.start = function start(options, callback) {
  var args = [],
      child;

  console.log('Starting application...');

  // XXX(mmalecki): LOLOLOL HACK
  args = ['start', '-o', 'estragon.log', '--'];
  options.instruments.forEach(function (instrument) {
    args.push('-h', instrument.host + ':' + (instruments.port || 8556).toString());
  });
  args.push('--', 'node', options['package.json'].scripts.start.split(' ')[1]);

  child = spawn('aeternum', args, {
    cwd: path.join(snapshotPath, 'package')
  });
};
<commit_msg>[fix] Fix syntax error<commit_after>var fs = require('fs'),
    os = require('os'),
    path = require('path'),
    spawn = require('child_process').spawn,
    async = require('async'),
    pkgcloud = require('pkgcloud'),
    useradd = require('useradd');

function noop() {}

//
// Starts an application.
//
module.exports = function (options, callback) {
  async.parallel([
    async.apply(fetch, options),
    async.apply(createUser, options)
  ], function (err) {
    if (err) {
      return callback(err);
    }

    async.series([
      async.apply(unpack, options),
      async.apply(readPackageJSON, options),
      async.apply(start, options)
    ], callback);
  });
};

module.exports.fetch = function fetch(options, callback) {
  var client = pkgcloud.storage.createClient(options.storage),
      tries = 0,
      maxTries = 5,
      packedSnapshotPath = path.join(os.tmpDir(), 'snapshot.tgz');

  options.packedSnapshotPath = packedSnapshotPath;

  function doFetch(callback) {
    ++tries;

    client.download({
      container: options.storage.container,
      remote: [ options.app.user, options.app.name, options.app.version ].join('-') + '.tgz'
    })
      .pipe(fs.createWriteStream(packedSnapshotPath))
      .on('error', function (err) {
        console.error('Error while fetching snapshot: ' + err.message);
        return tries === maxTries
          ? callback(err)
          : doFetch(callback);
      });
  }

  console.log('Fetching application snapshot...');

  doFetch(callback);
};

//
// Create a dedicated user account.
//
module.exports.createUser = function createUser(options, callback) {
  console.log('Creating user...');

  useradd({
    login: 'nodejitsu-' + options.app.user,
    shell: false,
    home: false
  }, callback);
};

module.exports.unpack = function unpack(options, callback) {
  var snapshotPath = path.join(os.tmpDir(), 'snapshot'),
      child;

  console.log('Unpacking snapshot...');

  options.snapshotPath = snapshotPath;
  options.packagePath = path.join(snapshotPath, 'package');

  child = spawn('tar', ['-xf', options.packedSnapshotPath], {
    cwd: snapshotPath
  });

  child.on('exit', function (code, signal) {
    fs.unlink(options.packedSnapshotPath, noop);

    if (code !== 0) {
      return callback(
        new Error('`tar` exited with ' + (code
          ? 'code ' + code.toString()
          : 'signal ' + signal.toString()
       ))
      );
    }

    return callback();
  });
};

module.exports.readPackageJSON = function readPackageJSON(options, callback) {
  console.log('Reading `package.json`...');

  fs.readFile(path.join(options.packagePath, 'package.json'), function (err, data) {
    if (err) {
      return callback(new Error('Error while reading `package.json`: ' + err.message));
    }

    try {
      data = JSON.parse(data);
    }
    catch (ex) {
      return callback(new Error('Error while parsing `package.json`: ' + err.message));
    }

    options['package.json'] = data;
    callback();
  });
};

module.exports.start = function start(options, callback) {
  var args = [],
      child;

  console.log('Starting application...');

  // XXX(mmalecki): LOLOLOL HACK
  args = ['start', '-o', 'estragon.log', '--'];
  options.instruments.forEach(function (instrument) {
    args.push('-h', instrument.host + ':' + (instruments.port || 8556).toString());
  });
  args.push('--', 'node', options['package.json'].scripts.start.split(' ')[1]);

  child = spawn('aeternum', args, {
    cwd: path.join(snapshotPath, 'package')
  });
};
<|endoftext|>


<commit_before>'use strict'

const axios = require('axios')
const pkg = require('../package.json')

const bindResources = require('./helpers/bindResources')

class SparkPay {

  constructor (conf) {
    if (!conf) {
      throw new Error('Missing SparkPay configuration!')
    } else if (!conf.domain || !conf.token) {
      throw new Error('SparkPay configuration requires both domain and token.')
    }

    let reqProtocol = conf.insecure ? 'http://' : 'https://'

    this.dispatch = axios.create({
      baseURL: reqProtocol + conf.domain + '/api/v1',
      headers: {
        'X-AC-Auth-Token': conf.token,
        'User-Agent': 'sparkpay-node/' + pkg.version
      }
    })

    this.dispatch.interceptors.response.use(null, (err) => {
      if (err.status === 429) {
        return this.retry(err)
      }

      throw err
    })

    this.get = {}

    bindResources(this.get, this.dispatch)
  }

  retry (req) {
    return new Promise((resolve, reject) => {
      let retryTimeout = 0

      if (req.headers['retry-after']) {
        retryTimeout = new Date(req.headers['retry-after']) - Date.now()
      }

      setTimeout(() => {
        this.dispatch.request(req.config).then(resolve).catch(reject)
      }, retryTimeout)
    })
  }
}

module.exports = {
  init: (conf) => new SparkPay(conf)
}
<commit_msg>sparkpay api is https only<commit_after>'use strict'

const axios = require('axios')
const pkg = require('../package.json')

const bindResources = require('./helpers/bindResources')

class SparkPay {

  constructor (conf) {
    if (!conf) {
      throw new Error('Missing SparkPay configuration!')
    } else if (!conf.domain || !conf.token) {
      throw new Error('SparkPay configuration requires both domain and token.')
    }

    this.dispatch = axios.create({
      baseURL: 'https://' + conf.domain + '/api/v1',
      headers: {
        'X-AC-Auth-Token': conf.token,
        'User-Agent': 'sparkpay-node/' + pkg.version
      }
    })

    this.dispatch.interceptors.response.use(null, (err) => {
      if (err.status === 429) {
        return this.retry(err)
      }

      throw err
    })

    this.get = {}

    bindResources(this.get, this.dispatch)
  }

  retry (req) {
    return new Promise((resolve, reject) => {
      let retryTimeout = 0

      if (req.headers['retry-after']) {
        retryTimeout = new Date(req.headers['retry-after']) - Date.now()
      }

      setTimeout(() => {
        this.dispatch.request(req.config).then(resolve).catch(reject)
      }, retryTimeout)
    })
  }
}

module.exports = {
  init: (conf) => new SparkPay(conf)
}
<|endoftext|>


<commit_before>(function (exports) {
    "use strict";

    var sort, round, min, max, range, sum, median, mean, deviation, variance, standardDeviation, standardize,
        rank, correlation, distance, pairwiseDistance, hierarchicalClustering;

 // @param {[number]} x Array of numbers.
    exports.sort = sort = function (x) {
        var copy;
     // Copy array.
        copy = x.slice();
        return copy.sort(function (a, b) {
            return a - b;
        });
    };

 // @param {number} x Number to round.
 // @param {number} [n] Number of decimal places.
    exports.round = round = function (x, n) {
        n = typeof n === "number" ? n : 0;
        return Math.round(x * Math.pow(10, n)) / Math.pow(10, n);
    };

 // @param {[number]} x Array of numbers.
    exports.min = min = function (x) {
        var min, i, n;
        min = Infinity;
        for (i = 0, n = x.length; i < n; i++) {
            if (x[i] < min) {
                min = x[i];
            }
        }
        return min;
    };

 // @param {[number]} x Array of numbers.
    exports.max = max = function (x) {
        var max, i, n;
        max = -Infinity;
        for (i = 0, n = x.length; i < n; i++) {
            if (x[i] > max) {
                max = x[i];
            }
        }
        return max;
    };

 // @param {number} start Start value.
 // @param {number} stop Stop value.
    exports.range = range = function (start, stop) {
        var len, range, idx;
        len = stop - start;
        range = new Array(len);
        for (idx = 0; idx < len; idx++) {
            range[idx] = start++;
        }
        return range;
    };

 // @param {[number]} x Array of numbers.
    exports.sum = sum = function (x) {
        return x.reduce(function (previousValue, currentValue) {
            return previousValue + currentValue;
        });
    };

 // @param {[number]} x Array of numbers.
    exports.median = median = function (x) {
        var sorted;
        if (x.length === 1) {
            return x[0];
        } else {
            sorted = sort(x);
            if (sorted.length % 2 === 0) {
                return mean([x[(sorted.length / 2) - 1], x[sorted.length / 2]]);
            } else {
                return x[Math.floor(sorted.length / 2)];
            }
        }
    };

 // @param {[number]} x Array of numbers.
    exports.mean = mean = function (x) {
        return sum(x) / x.length;
    };

 // @param {[number]} x Array of numbers.
    exports.deviation = deviation = function (x) {
        var xBar;
        xBar = mean(x);
        return x.map(function (xi) {
            return xi - xBar;
        });
    };

 // Calculates the variance.
 // @param {[number]} x Array of numbers.
 // @param {boolean} [bias] If true, the biased sample variance is used.
    exports.variance = variance = function (x, bias) {
        bias = typeof bias === "boolean" ? bias : false;
        return sum(deviation(x).map(function (xi) {
            return Math.pow(xi, 2);
        })) / (x.length - (bias === false ? 1 : 0));
    };

 // Calculates the sample standard deviation.
 // @param {[number]} x Array of numbers.
 // @param {boolean} [bias] If true, the biased sample variance is used.
    exports.standardDeviation = standardDeviation = function (x, bias) {
        bias = typeof bias === "boolean" ? bias : false;
        return Math.sqrt(variance(x, bias));
    };

 // @param {[number]} x Array of numbers.
    exports.standardize = standardize = function (x) {
        var sd;
        sd = standardDeviation(x);
        return deviation(x).map(function (xi) {
            return xi / sd;
        });
    };

 // @param {[number]} x Array of numbers.
    exports.rank = rank = function (x) {
        var ranks;
     // Copy array.
        ranks = x.slice();
     // Sort array.
        ranks = sort(ranks);
     // Calculate ranks.
        return x.map(function (xi) {
            var rank, first, last;
         // Handle tied ranks.
            first = ranks.indexOf(xi);
            last = ranks.lastIndexOf(xi);
            if (first === last) {
                rank = first;
            } else {
                rank = (first + last) / 2;
            }
         // Add 1 because ranks start with 1.
            return rank + 1;
        });
    };

 // Calculates the correlation coefficient for two variables.
 // @param {[number]} x Array of numbers.
 // @param {[number]} y Array of numbers.
    exports.correlation = correlation = {
     // @param {boolean} [standardize] If false, x and y will not be standardized.
        pearson: function (x, y, standardize) {
            standardize = typeof standardize === "boolean" ? standardize : true;
            if (standardize === true) {
                x = exports.standardize(x);
                y = exports.standardize(y);
            }
            return sum(x.map(function (xi, i) {
                return xi * y[i];
            })) / (x.length - 1);
        },
     // @param {boolean} [rank] If false, x and y will not be ranked.
        spearman: function (x, y, rank) {
            var xDeviation, yDeviation;
            rank = typeof rank === "boolean" ? rank : true;
            if (rank === true) {
                x = exports.rank(x);
                y = exports.rank(y);
            }
            xDeviation = deviation(x);
            yDeviation = deviation(y);
            return sum(xDeviation.map(function (xi, i) {
                return xi * yDeviation[i];
            })) / Math.sqrt(sum(xDeviation.map(function (xi) {
                return Math.pow(xi, 2);
            })) * sum(yDeviation.map(function (yi) {
                return Math.pow(yi, 2);
            })));
        }
    };

 // @param {[number]} x Array of numbers.
 // @param {[number]} y Array of numbers.
    exports.distance = distance = {
        euclidean: function (x, y) {
            return Math.sqrt(sum(x.map(function (xi, i) {
                return Math.pow(xi - y[i], 2);
            })));
        },
        manhattan: function (x, y) {
            return sum(x.map(function (xi, i) {
                return Math.abs(xi - y[i]);
            }));
        }
    };

 // @param {[[number]]} x Array of array of numbers.
 // @param {(x, y)} distanceMetric Distance metric.
 // TODO: Save memory by throwing away upper or lower triangle and diagonal.
    exports.pairwiseDistance = pairwiseDistance = function (x, distanceMetric) {
        var pairwiseDistances, distance, i, j;
        pairwiseDistances = [];
        for (i = 0; i < x.length; i++) {
            pairwiseDistances[i] = [];
            for (j = 0; j <= i; j++) {
                if (i === j) {
                    pairwiseDistances[i][j] = 0;
                } else {
                    distance = distanceMetric(x[i], x[j]);
                    pairwiseDistances[i][j] = distance;
                    pairwiseDistances[j][i] = distance;
                }
            }
        }
        return pairwiseDistances;
    };

 // @param {[[number]]} pairwiseDistances Pairwise distance matrix.
 // @param {string} linkage Linkage criterion.
 // Inspired by Heather Arthur's clusterfck: https://github.com/harthur/clusterfck
    exports.hierarchicalClustering = hierarchicalClustering = function (pairwiseDistances, linkage) {
        var clusters, minDistance, clusterA, clusterB, distance, distanceA,
            distanceB, candidates, mergedCluster, i, j;
        if (["single", "complete", "upgma"].indexOf(linkage) === -1) {
            throw new Error("The second argument (linkage) has to be either one of \"single\", \"complete\", or \"upgma\".");
        }
        clusters = [];
     // Initialize one cluster per observation.
        for (i = 0; i < pairwiseDistances.length; i++) {
            clusters.push({
                label: i,
                key: i,
                index: i,
                size: 1
            });
        }
        while (true) {
         // Stop if all clusters have been merged into a single cluster.
            if (clusters.length === 1) {
                delete clusters[0].index;
                delete clusters[0].key;
                break;
            }
         // Find closest clusters.
            minDistance = Infinity;
            for (i = 0; i < clusters.length; i++) {
                clusterA = clusters[i];
                for (j = 0; j < clusters.length; j++) {
                    if (i !== j) {
                        clusterB = clusters[j];
                        distance = pairwiseDistances[clusterA.key][clusterB.key];
                        if (distance < minDistance) {
                            minDistance = distance;
                            candidates = [clusterA, clusterB];
                        }
                    }
                }
            }
         // Merge clusters.
            mergedCluster = {
                children: candidates,
                key: candidates[0].key,
                distance: minDistance,
                size: candidates[0].size + candidates[1].size
            };
         // Replace first cluster with merged cluster in list of clusters.
            clusters[candidates[0].index] = mergedCluster;
         // Remove second cluster from list of clusters.
            clusters.splice(candidates[1].index, 1);
         // Recompute distances from merged cluster to all other clusters.
            for (i = 0; i < clusters.length; i++) {
                if (clusters[i].key === candidates[0].key) {
                    distance = 0;
                } else {
                    distanceA = pairwiseDistances[candidates[0].key][clusters[i].key];
                    distanceB = pairwiseDistances[candidates[1].key][clusters[i].key];
                    switch (linkage) {
                        case "single":
                            if (distanceA < distanceB) {
                                distance = distanceA;
                            } else {
                                distance = distanceB;
                            }
                            break;
                        case "complete":
                            if (distanceA > distanceB) {
                                distance = distanceA;
                            } else {
                                distance = distanceB;
                            }
                            break;
                        case "upgma":
                            distance = ((distanceA * candidates[0].size) + (distanceB * candidates[1].size)) / (candidates[0].size + candidates[1].size);
                            break;
                    }
                }
                pairwiseDistances[candidates[0].key][clusters[i].key] = distance;
                pairwiseDistances[clusters[i].key][candidates[0].key] = distance;
            }
         // Remove column of second cluster from pairwise distance matrix.
            for (i = 0; i < pairwiseDistances.length; i++) {
                pairwiseDistances[i].splice(candidates[1].key, 1);
            }
         // Remove row of second cluster from pairwise distance matrix.
            pairwiseDistances.splice(candidates[1].key, 1);
         // Update keys of clusters to reflect removal of the column.
            for (i = candidates[1].key; i < clusters.length; i++) {
                clusters[i].key--;
            }
         // Remove obsolete key and index of merged clusters.
            delete candidates[0].key;
            delete candidates[0].index;
            delete candidates[1].key;
            delete candidates[1].index;
         // Reindex clusters.
            for (i = 0; i < clusters.length; i++) {
                clusters[i].index = i;
            }
        }
        return clusters;
    };

}(typeof exports === "undefined" ? this.spearson = {} : exports));
<commit_msg>Speed up sum.<commit_after>(function (exports) {
    "use strict";

    var sort, round, min, max, range, sum, median, mean, deviation, variance, standardDeviation, standardize,
        rank, correlation, distance, pairwiseDistance, hierarchicalClustering;

 // @param {[number]} x Array of numbers.
    exports.sort = sort = function (x) {
        var copy;
     // Copy array.
        copy = x.slice();
        return copy.sort(function (a, b) {
            return a - b;
        });
    };

 // @param {number} x Number to round.
 // @param {number} [n] Number of decimal places.
    exports.round = round = function (x, n) {
        n = typeof n === "number" ? n : 0;
        return Math.round(x * Math.pow(10, n)) / Math.pow(10, n);
    };

 // @param {[number]} x Array of numbers.
    exports.min = min = function (x) {
        var min, i, n;
        min = Infinity;
        for (i = 0, n = x.length; i < n; i++) {
            if (x[i] < min) {
                min = x[i];
            }
        }
        return min;
    };

 // @param {[number]} x Array of numbers.
    exports.max = max = function (x) {
        var max, i, n;
        max = -Infinity;
        for (i = 0, n = x.length; i < n; i++) {
            if (x[i] > max) {
                max = x[i];
            }
        }
        return max;
    };

 // @param {number} start Start value.
 // @param {number} stop Stop value.
    exports.range = range = function (start, stop) {
        var len, range, idx;
        len = stop - start;
        range = new Array(len);
        for (idx = 0; idx < len; idx++) {
            range[idx] = start++;
        }
        return range;
    };

 // @param {[number]} x Array of numbers.
    exports.sum = sum = function (x) {
        var sum, i, n;
        sum = 0;
        for (i = 0, n = x.length; i < n; i++) {
            sum += x[i];
        }
        return sum;
    };

 // @param {[number]} x Array of numbers.
    exports.median = median = function (x) {
        var sorted;
        if (x.length === 1) {
            return x[0];
        } else {
            sorted = sort(x);
            if (sorted.length % 2 === 0) {
                return mean([x[(sorted.length / 2) - 1], x[sorted.length / 2]]);
            } else {
                return x[Math.floor(sorted.length / 2)];
            }
        }
    };

 // @param {[number]} x Array of numbers.
    exports.mean = mean = function (x) {
        return sum(x) / x.length;
    };

 // @param {[number]} x Array of numbers.
    exports.deviation = deviation = function (x) {
        var xBar;
        xBar = mean(x);
        return x.map(function (xi) {
            return xi - xBar;
        });
    };

 // Calculates the variance.
 // @param {[number]} x Array of numbers.
 // @param {boolean} [bias] If true, the biased sample variance is used.
    exports.variance = variance = function (x, bias) {
        bias = typeof bias === "boolean" ? bias : false;
        return sum(deviation(x).map(function (xi) {
            return Math.pow(xi, 2);
        })) / (x.length - (bias === false ? 1 : 0));
    };

 // Calculates the sample standard deviation.
 // @param {[number]} x Array of numbers.
 // @param {boolean} [bias] If true, the biased sample variance is used.
    exports.standardDeviation = standardDeviation = function (x, bias) {
        bias = typeof bias === "boolean" ? bias : false;
        return Math.sqrt(variance(x, bias));
    };

 // @param {[number]} x Array of numbers.
    exports.standardize = standardize = function (x) {
        var sd;
        sd = standardDeviation(x);
        return deviation(x).map(function (xi) {
            return xi / sd;
        });
    };

 // @param {[number]} x Array of numbers.
    exports.rank = rank = function (x) {
        var ranks;
     // Copy array.
        ranks = x.slice();
     // Sort array.
        ranks = sort(ranks);
     // Calculate ranks.
        return x.map(function (xi) {
            var rank, first, last;
         // Handle tied ranks.
            first = ranks.indexOf(xi);
            last = ranks.lastIndexOf(xi);
            if (first === last) {
                rank = first;
            } else {
                rank = (first + last) / 2;
            }
         // Add 1 because ranks start with 1.
            return rank + 1;
        });
    };

 // Calculates the correlation coefficient for two variables.
 // @param {[number]} x Array of numbers.
 // @param {[number]} y Array of numbers.
    exports.correlation = correlation = {
     // @param {boolean} [standardize] If false, x and y will not be standardized.
        pearson: function (x, y, standardize) {
            standardize = typeof standardize === "boolean" ? standardize : true;
            if (standardize === true) {
                x = exports.standardize(x);
                y = exports.standardize(y);
            }
            return sum(x.map(function (xi, i) {
                return xi * y[i];
            })) / (x.length - 1);
        },
     // @param {boolean} [rank] If false, x and y will not be ranked.
        spearman: function (x, y, rank) {
            var xDeviation, yDeviation;
            rank = typeof rank === "boolean" ? rank : true;
            if (rank === true) {
                x = exports.rank(x);
                y = exports.rank(y);
            }
            xDeviation = deviation(x);
            yDeviation = deviation(y);
            return sum(xDeviation.map(function (xi, i) {
                return xi * yDeviation[i];
            })) / Math.sqrt(sum(xDeviation.map(function (xi) {
                return Math.pow(xi, 2);
            })) * sum(yDeviation.map(function (yi) {
                return Math.pow(yi, 2);
            })));
        }
    };

 // @param {[number]} x Array of numbers.
 // @param {[number]} y Array of numbers.
    exports.distance = distance = {
        euclidean: function (x, y) {
            return Math.sqrt(sum(x.map(function (xi, i) {
                return Math.pow(xi - y[i], 2);
            })));
        },
        manhattan: function (x, y) {
            return sum(x.map(function (xi, i) {
                return Math.abs(xi - y[i]);
            }));
        }
    };

 // @param {[[number]]} x Array of array of numbers.
 // @param {(x, y)} distanceMetric Distance metric.
 // TODO: Save memory by throwing away upper or lower triangle and diagonal.
    exports.pairwiseDistance = pairwiseDistance = function (x, distanceMetric) {
        var pairwiseDistances, distance, i, j;
        pairwiseDistances = [];
        for (i = 0; i < x.length; i++) {
            pairwiseDistances[i] = [];
            for (j = 0; j <= i; j++) {
                if (i === j) {
                    pairwiseDistances[i][j] = 0;
                } else {
                    distance = distanceMetric(x[i], x[j]);
                    pairwiseDistances[i][j] = distance;
                    pairwiseDistances[j][i] = distance;
                }
            }
        }
        return pairwiseDistances;
    };

 // @param {[[number]]} pairwiseDistances Pairwise distance matrix.
 // @param {string} linkage Linkage criterion.
 // Inspired by Heather Arthur's clusterfck: https://github.com/harthur/clusterfck
    exports.hierarchicalClustering = hierarchicalClustering = function (pairwiseDistances, linkage) {
        var clusters, minDistance, clusterA, clusterB, distance, distanceA,
            distanceB, candidates, mergedCluster, i, j;
        if (["single", "complete", "upgma"].indexOf(linkage) === -1) {
            throw new Error("The second argument (linkage) has to be either one of \"single\", \"complete\", or \"upgma\".");
        }
        clusters = [];
     // Initialize one cluster per observation.
        for (i = 0; i < pairwiseDistances.length; i++) {
            clusters.push({
                label: i,
                key: i,
                index: i,
                size: 1
            });
        }
        while (true) {
         // Stop if all clusters have been merged into a single cluster.
            if (clusters.length === 1) {
                delete clusters[0].index;
                delete clusters[0].key;
                break;
            }
         // Find closest clusters.
            minDistance = Infinity;
            for (i = 0; i < clusters.length; i++) {
                clusterA = clusters[i];
                for (j = 0; j < clusters.length; j++) {
                    if (i !== j) {
                        clusterB = clusters[j];
                        distance = pairwiseDistances[clusterA.key][clusterB.key];
                        if (distance < minDistance) {
                            minDistance = distance;
                            candidates = [clusterA, clusterB];
                        }
                    }
                }
            }
         // Merge clusters.
            mergedCluster = {
                children: candidates,
                key: candidates[0].key,
                distance: minDistance,
                size: candidates[0].size + candidates[1].size
            };
         // Replace first cluster with merged cluster in list of clusters.
            clusters[candidates[0].index] = mergedCluster;
         // Remove second cluster from list of clusters.
            clusters.splice(candidates[1].index, 1);
         // Recompute distances from merged cluster to all other clusters.
            for (i = 0; i < clusters.length; i++) {
                if (clusters[i].key === candidates[0].key) {
                    distance = 0;
                } else {
                    distanceA = pairwiseDistances[candidates[0].key][clusters[i].key];
                    distanceB = pairwiseDistances[candidates[1].key][clusters[i].key];
                    switch (linkage) {
                        case "single":
                            if (distanceA < distanceB) {
                                distance = distanceA;
                            } else {
                                distance = distanceB;
                            }
                            break;
                        case "complete":
                            if (distanceA > distanceB) {
                                distance = distanceA;
                            } else {
                                distance = distanceB;
                            }
                            break;
                        case "upgma":
                            distance = ((distanceA * candidates[0].size) + (distanceB * candidates[1].size)) / (candidates[0].size + candidates[1].size);
                            break;
                    }
                }
                pairwiseDistances[candidates[0].key][clusters[i].key] = distance;
                pairwiseDistances[clusters[i].key][candidates[0].key] = distance;
            }
         // Remove column of second cluster from pairwise distance matrix.
            for (i = 0; i < pairwiseDistances.length; i++) {
                pairwiseDistances[i].splice(candidates[1].key, 1);
            }
         // Remove row of second cluster from pairwise distance matrix.
            pairwiseDistances.splice(candidates[1].key, 1);
         // Update keys of clusters to reflect removal of the column.
            for (i = candidates[1].key; i < clusters.length; i++) {
                clusters[i].key--;
            }
         // Remove obsolete key and index of merged clusters.
            delete candidates[0].key;
            delete candidates[0].index;
            delete candidates[1].key;
            delete candidates[1].index;
         // Reindex clusters.
            for (i = 0; i < clusters.length; i++) {
                clusters[i].index = i;
            }
        }
        return clusters;
    };

}(typeof exports === "undefined" ? this.spearson = {} : exports));
<|endoftext|>


<commit_before>const EventEmitter = require('events').EventEmitter
const clone = require('clone')
const utils = require('./utils')
const Server = require('./server')

function Splitter (configuration) {
  if (!utils.config.validate(configuration)) { throw new Error('Invalid configuration') }

  // Private
  const clonedConfig = clone(configuration)
  const log = utils.logger.init(configuration)
  const bootstrapMiddlewares = []
  const requestMiddlewares = []
  const rules = {}
  const started = () => log.error('This splitter instance already started')
  let server

  // Optimize criteria and handle performance
  const optimizedConfig = Object.assign(
    {},
    clonedConfig,
    clonedConfig.rulesets ? {rulesets: utils.config.getOptimizedRulesets(clonedConfig)} : {},
    {upstreams: utils.config.getOptimizedUpstreams(clonedConfig)},
    {api: Object.assign({}, clonedConfig.api, {performance: utils.config.getPerformance(clonedConfig.api)})}
  )

  // Public
  this.events = new EventEmitter()
  this.getConfiguration = () => clone(optimizedConfig)
  this.getLogger = () => log

  this.bootstrap = (fn) => {
    validateCallback(fn)
    bootstrapMiddlewares.push(fn)
  }

  this.use = (fn) => {
    validateCallback(fn)
    requestMiddlewares.push(fn)
  }

  this.addRule = (key, fn) => {
    validateString(key, 'Rule name')
    validateCallback(fn)
    rules[key] = fn
  }

  this.start = () => {
    this.events.emit('applicationStart')
    server = new Server(
      this.getConfiguration(),
      this.events,
      bootstrapMiddlewares,
      requestMiddlewares,
      rules,
      this.getLogger()
    )
    server.start()

    // redefine, fire once, and destroy
    this.start = started
  }
}

const isFunction = fn => fn && typeof fn === 'function'
const validateCallback = fn => { if (!isFunction(fn)) { throw new Error('Callback isn\'t a function') } }
const isString = str => str && typeof str === 'string'
const validateString = (str, msg) => { if (!isString(str)) { throw new Error(msg + ' isn\'t a string') } }

exports = module.exports = Splitter
exports.isConfigurationValid = (configuration) => utils.config.validate(configuration)
<commit_msg>Add method to receive custom executors from end user.<commit_after>const EventEmitter = require('events').EventEmitter
const clone = require('clone')
const utils = require('./utils')
const Server = require('./server')

function Splitter (configuration) {
  if (!utils.config.validate(configuration)) { throw new Error('Invalid configuration') }

  // Private
  const clonedConfig = clone(configuration)
  const log = utils.logger.init(configuration)
  const bootstrapMiddlewares = []
  const requestMiddlewares = []
  const rules = {}
  const executors = {}
  const started = () => log.error('This splitter instance already started')
  let server

  // Optimize criteria and handle performance
  const optimizedConfig = Object.assign(
    {},
    clonedConfig,
    clonedConfig.rulesets ? {rulesets: utils.config.getOptimizedRulesets(clonedConfig)} : {},
    {upstreams: utils.config.getOptimizedUpstreams(clonedConfig)},
    {api: Object.assign({}, clonedConfig.api, {performance: utils.config.getPerformance(clonedConfig.api)})}
  )

  // Public
  this.events = new EventEmitter()
  this.getConfiguration = () => clone(optimizedConfig)
  this.getLogger = () => log

  this.bootstrap = (fn) => {
    validateCallback(fn)
    bootstrapMiddlewares.push(fn)
  }

  this.use = (fn) => {
    validateCallback(fn)
    requestMiddlewares.push(fn)
  }

  this.addRule = (key, fn) => {
    validateString(key, 'Rule name')
    validateCallback(fn)
    rules[key] = fn
  }

  this.addExecutor = (key, fn) => {
    validateString(key, 'Executor name')
    validateCallback(fn)
    executors[key] = fn
  }

  this.start = () => {
    this.events.emit('applicationStart')
    server = new Server(
      this.getConfiguration(),
      this.events,
      bootstrapMiddlewares,
      requestMiddlewares,
      rules,
      executors,
      this.getLogger()
    )
    server.start()

    // redefine, fire once, and destroy
    this.start = started
  }
}

const isFunction = fn => fn && typeof fn === 'function'
const validateCallback = fn => { if (!isFunction(fn)) { throw new Error('Callback isn\'t a function') } }
const isString = str => str && typeof str === 'string'
const validateString = (str, msg) => { if (!isString(str)) { throw new Error(msg + ' isn\'t a string') } }

exports = module.exports = Splitter
exports.isConfigurationValid = (configuration) => utils.config.validate(configuration)
<|endoftext|>


<commit_before>/*
 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
 * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

'use strict';

var executor = require('./executor');

var accumulator = function() {
  var so = '';
  var se = '';

  var stdout = function(str) {
    so += str;
  };
  var stderr = function(str) {
    se += str;
  };
  var output = function() {
    return so + se;
  };
  return {
    stdout: stdout,
    stderr: stderr,
    output: output
  };
};

module.exports = function() {

  var check = function(host, user, keyPath, out, cb) {
    var cmd;
    var w = accumulator();

    if (keyPath) {
      cmd = 'ssh -i ' + keyPath + ' -o batchMode=yes -o StrictHostKeyChecking=no -T ' + user + '@' + host + ' true';
    }
    else {
      cmd = 'ssh -o batchMode=yes -o StrictHostKeyChecking=no -T ' + user + '@' + host + ' true';
    }
    var op = {cmd: cmd,
              host: host,
              user: user,
              keyPath: keyPath,
              loc: __dirname};
    executor.exec('live', op.cmd, op.loc, w, function() {
      if (w.output().indexOf('Permission denied') !== -1) {
        cb('permission denied - possible ssh key or key password problem');
      }
      else {
        cb(null, op);
      }
    });
  };

  return {
    check: check
  };
};

<commit_msg>Error if ssh check returns 'Authentication failure'<commit_after>/*
 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
 * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

'use strict';

var executor = require('./executor');

var accumulator = function() {
  var so = '';
  var se = '';

  var stdout = function(str) {
    so += str;
  };
  var stderr = function(str) {
    se += str;
  };
  var output = function() {
    return so + se;
  };
  return {
    stdout: stdout,
    stderr: stderr,
    output: output
  };
};

module.exports = function() {

  var check = function(host, user, keyPath, out, cb) {
    var cmd;
    var w = accumulator();

    if (keyPath) {
      cmd = 'ssh -i ' + keyPath + ' -o batchMode=yes -o StrictHostKeyChecking=no -T ' + user + '@' + host + ' true';
    }
    else {
      cmd = 'ssh -o batchMode=yes -o StrictHostKeyChecking=no -T ' + user + '@' + host + ' true';
    }
    var op = {cmd: cmd,
              host: host,
              user: user,
              keyPath: keyPath,
              loc: __dirname};
    executor.exec('live', op.cmd, op.loc, w, function() {
      if (w.output().indexOf('Permission denied') !== -1 || w.output().indexOf('Authentication failure') !== -1) {
        cb(new Error('permission denied - possible ssh key or key password problem'));
      }
      else {
        cb(null, op);
      }
    });
  };

  return {
    check: check
  };
};

<|endoftext|>