<commit_before>"use strict";

var Promise = require("bluebird"),
    net     = require("net"),
    http    = require("http"),
    cluster = require("cluster"),
    debug   = require("debug"),
    shimmer = require("shimmer"),
    clusterphone = require("clusterphone").ns("hotpotato");

// TODO: use a separate agent for all proxy requests.
// TODO: clean up socket state on finished pass.
// TODO: figure out how we handle failed passes gracefully.

var worker = cluster.worker;
var workerDebug = debug("hotpotato:worker" + worker.id);

// This will run a http.Server listening on the separate socket the master
// directs us to.
var sideChannelServer;

// This is the actual server that is handling requests in caller. We pump
// sidechanneled requests into this. We also attach the transferred connection
// to this server.
var targetServer;

function setupSideChannelServer(socketPath) {
  workerDebug("Creating side-channel server");

  var deferred = Promise.defer();

  // TODO: This is some epic fucking hax. But I couldn't figure out how else
  // to create a Server that doesn't get shared with master.
  sideChannelServer = http.createServer();
  var handle = net._createServerHandle(socketPath, -1, -1, undefined);
  sideChannelServer._listen2(null, null, null, -1, handle.fd);

  sideChannelServer.on("connection", function(connection) {
    connection._hotpotato = {
      isRerouted: true
    };
  });

  sideChannelServer.on("request", function(req, res) {
    if (!targetServer) {
      workerDebug("Side channel request received, but no dest server to forward to.");
      // TODO: properly handle this.
      req.abort();
      res.writeHead(500);
      res.end();
    }

    // TODO: I think this is safe - keep-alive on the internal side-channel
    // connections should be fine. Without this, pipelined requests from the
    // remote client will break.
    var shouldKeepAlive = req.headers["x-hotpotato-keepalive"] === "true";
    res.shouldKeepAlive = shouldKeepAlive;
    targetServer.emit("request", req, res);
  });

  sideChannelServer.on("listening", deferred.callback);

  return deferred.promise;
}

clusterphone.handlers.createServer = function(msg) {
  return setupSideChannelServer(msg.path);
};

clusterphone.handlers.connection = function(msg, socket) {
  workerDebug("Got a new connection passed to me.");

  if (!targetServer) {
    // TODO: handle me.
    workerDebug("Ouch. No target server for a newly handed off connection.");
    return;
  }

  targetServer.emit("connection", socket);
};

clusterphone.handlers.upgrade = function(msg, socket) {
  workerDebug("Got a new upgrade passed to me.");

  if (!targetServer) {
    // TODO: handle me.
    workerDebug("No target server to handle upgrade.");
    socket.close();
    return;
  }

  // Reconstruct the request.
  var req = new http.IncomingMessage(socket);
  req.httpVersionMinor = msg.httpVersionMinor;
  req.httpVersionMajor = msg.httpVersionMajor;
  req.httpVersion = msg.httpVersionMajor + "." + msg.httpVersionMinor;
  req.headers = msg.headers;
  req.url = msg.url;

  var head;
  if (msg.head) {
    head = new Buffer(msg.head, "base64");
  }
  targetServer.emit("upgrade", req, socket, head);
};

function connectionHandler(connection) {
  connection._hotpotato = {
    pendingReqs: []
  };

  // The idea here is to shim the parser onHeaders / onHeadersComplete events.
  // When they're called we mark the request as "parsing". When we've fully
  // received the request, we check if we've already begun parsing again.
  // This can happen if we get pipelined requests.
  var shim = function(original) {
    return function() {
      connection._hotpotato.parsing = true;
      original.apply(this, arguments);
    };
  };

  shimmer.wrap(connection.parser, "onHeaders", shim);
  shimmer.wrap(connection.parser, "onHeadersComplete", shim);
}

function handlePassingRequest(req, res) {
  var deferred = Promise.defer(),
      socket = req.connection;

  req.headers["X-HOTPOTATO-WORKER"] = cluster.worker.id;
  req.headers["X-HOTPOTATO-KEEPALIVE"] = res.shouldKeepAlive;

  // Open a connection to the target and proxy the inbound request to it.
  var proxyReq = http.request({
    socketPath: req._hotpotato.proxyTo,
    path: req.url,
    method: req.method,
    headers: req.headers
  });

  req.pipe(proxyReq);

  proxyReq.on("error", function() {
    // TODO: cleanup here.
    workerDebug("Incoming request errored while we were proxying it.");
  });

  // Once we've finished sending the request over the wire, we might want to
  // pause the connection in preparation for a handover.
  req.on("end", function() {
    if (socket._hotpotato.passConnection && !socket._hotpotato.parsing && !socket._hotpotato.pendingReqs.length) {
      // Okay. We finished proxying this request, and we're not already parsing
      // another. Pause the connection so that we don't pull anything else
      // off it.
      req.pause();
      socket.parser.pause();
    }
  });

  // Once we get a response from the target worker, we proxy that over to the
  // client.
  proxyReq.on("response", function(proxyResp) {
    // TODO: write status reason phrase if exists.
    res.writeHead(proxyResp.statusCode, proxyResp.headers);
    // TODO: error handling on pipe.
    proxyResp.pipe(res);
    proxyResp.on("end", function() {
      if (socket._hotpotato.passConnection && !socket._hotpotato.parsing && !socket._hotpotato.pendingReqs.length) {
        workerDebug("Passing off a connection.");
        // The connection needs to be passed to the new worker, *and* this is
        // a good time to do it. So let's do it.
        clusterphone.sendToMaster("passConnection", { id: req._hotpotato.targetWorker }, socket);

        // Cleanup the socket from our end.
        socket.emit("close");
      }

      // We have now finished proxying this request.
      // If we're attempting to pass off this connection though, we're not done
      // until all pending requests have been proxied and finished up.
      if (socket._hotpotato.passConnection) {
        if (socket._hotpotato.pendingReqs.length) {
          var next = socket._hotpotato.pendingReqs.shift();
          var nextReq = next[0], nextRes = next[1];
          nextReq._hotpotato = {
            targetWorker: req._hotpotato.targetWorker,
            proxyTo: req._hotpotato.proxyTo
          };
          deferred.resolve(handlePassingRequest(nextReq, nextRes));
        }
      }
      deferred.resolve();
    });
  });

  return deferred.promise;
}

// TODO: what happens if a second request is passed from the same
// connection whilst one is still in progress?

// This handles the process of passing off the given request to another worker.
// It can also operate in a mode where it passes off the whole connection to
// the new worker.
function pass(passConnection, req, res) {
  workerDebug("Passing off a request.");

  var socket = req.connection;

  // Sanity checks.
  if (socket._hotpotato.isRerouted) {
    throw new Error("Attempting to pass a request that was already passed from another worker. This is not supported.");
  }
  if (req._hotpotato) {
    throw new Error("Looks like pass was called on same request more than once.");
  }

  // Set up state.
  req._hotpotato = {};

  if (passConnection) {
    socket._hotpotato.passConnection = true;
    // Reset the parsing flag.
    socket._hotpotato.parsing = false;

    socket.on("__hotpotato-request", function(req, res) {
      socket._hotpotato.parsing = false;
      socket._hotpotato.pendingReqs.push([req, res]);
    });
  }

  // Make sure we don't read anything off connection whilst we're asking master
  // where to route this request to.
  req.pause();

  clusterphone.sendToMaster("routeConnection", {
    method: req.method,
    url: req.url,
    headers: req.headers
  }).then(function(routeReply) {
    if (routeReply.error) {
      // TODO: handle this better.
      workerDebug("Failed to pass off connection: " + routeReply.error);
      res.writeHead(500);
      res.end();
      return;
    }

    req._hotpotato.targetWorker = routeReply.id;
    req._hotpotato.proxyTo = routeReply.path;

    // It's safe to resume the request now.
    req.resume();

    handlePassingRequest(req, res);
  });
}

function setServer(server) {
  if (targetServer) {
    targetServer.removeListener("connection", connectionHandler);
  }
  targetServer = server;
  targetServer.on("connection", connectionHandler);

  // This is a little hacky, but hear me out.
  // If the caller opts to passAll() on a request, it means that the current
  // request and all subsequent requests for that connection should be
  // bounced elsewhere. The problem is, we might get two requests in rapid
  // succession (pipelined), or a request that comes in whilst we're still
  // finishing up flushing out a response (keep-alive requests on a good
  // network). In this case we don't want to emit request events for that
  // connection anymore. This is how we filter those out.
  shimmer.wrap(server, "emit", function(original) {
    return function(event, req, res) {
      // If we don't own this server anymore, then skip this.
      if (targetServer !== this) {
        return original.apply(this, arguments);
      }

      if ((event === "request") && req && req.connection &&
          req.connection._hotpotato && req.connection._hotpotato.passConnection) {
        return req.connection.emit("__hotpotato-request", req, res);
      }

      return original.apply(this, arguments);
    };
  });
}

function passUpgrade(req, socket, head) {
  workerDebug("Passing off an upgrade.");

  if (req._hotpotato) {
    throw new Error("Looks like passUpgrade was called on same request more than once.");
  }

  req._hotpotato = {};

  clusterphone.sendToMaster ("passUpgrade", {
    method: req.method,
    url: req.url,
    headers: req.headers,
    httpVersionMinor: req.httpVersionMinor,
    httpVersionMajor: req.httpVersionMajor,
    head: head ? head.toString("base64") : null,
  }, socket);
}

exports.passRequest = pass.bind(null, false);
exports.passConnection = pass.bind(null, true);
exports.passUpgrade = passUpgrade;
exports.server = setServer;
<commit_msg>Compatibility with node 0.11. It didn't take much.<commit_after>"use strict";

var Promise = require("bluebird"),
    net     = require("net"),
    http    = require("http"),
    cluster = require("cluster"),
    debug   = require("debug"),
    shimmer = require("shimmer"),
    clusterphone = require("clusterphone").ns("hotpotato");

var HTTPParser = process.binding('http_parser').HTTPParser;

// TODO: use a separate agent for all proxy requests.
// TODO: clean up socket state on finished pass.
// TODO: figure out how we handle failed passes gracefully.

var worker = cluster.worker;
var workerDebug = debug("hotpotato:worker" + worker.id);

// This will run a http.Server listening on the separate socket the master
// directs us to.
var sideChannelServer;

// This is the actual server that is handling requests in caller. We pump
// sidechanneled requests into this. We also attach the transferred connection
// to this server.
var targetServer;

function setupSideChannelServer(socketPath) {
  workerDebug("Creating side-channel server");

  var deferred = Promise.defer();

  // TODO: This is some epic fucking hax. But I couldn't figure out how else
  // to create a Server that doesn't get shared with master.
  sideChannelServer = http.createServer();
  var handle = net._createServerHandle(socketPath, -1, -1, undefined);
  sideChannelServer._listen2(null, null, null, -1, handle.fd);

  sideChannelServer.on("connection", function(connection) {
    connection._hotpotato = {
      isRerouted: true
    };
  });

  sideChannelServer.on("request", function(req, res) {
    if (!targetServer) {
      workerDebug("Side channel request received, but no dest server to forward to.");
      // TODO: properly handle this.
      req.abort();
      res.writeHead(500);
      res.end();
    }

    // TODO: I think this is safe - keep-alive on the internal side-channel
    // connections should be fine. Without this, pipelined requests from the
    // remote client will break.
    var shouldKeepAlive = req.headers["x-hotpotato-keepalive"] === "true";
    res.shouldKeepAlive = shouldKeepAlive;
    targetServer.emit("request", req, res);
  });

  sideChannelServer.on("listening", deferred.callback);

  return deferred.promise;
}

clusterphone.handlers.createServer = function(msg) {
  return setupSideChannelServer(msg.path);
};

clusterphone.handlers.connection = function(msg, socket) {
  workerDebug("Got a new connection passed to me.");

  if (!targetServer) {
    // TODO: handle me.
    workerDebug("Ouch. No target server for a newly handed off connection.");
    return;
  }

  targetServer.emit("connection", socket);
};

clusterphone.handlers.upgrade = function(msg, socket) {
  workerDebug("Got a new upgrade passed to me.");

  if (!targetServer) {
    // TODO: handle me.
    workerDebug("No target server to handle upgrade.");
    socket.close();
    return;
  }

  // Reconstruct the request.
  var req = new http.IncomingMessage(socket);
  req.httpVersionMinor = msg.httpVersionMinor;
  req.httpVersionMajor = msg.httpVersionMajor;
  req.httpVersion = msg.httpVersionMajor + "." + msg.httpVersionMinor;
  req.headers = msg.headers;
  req.url = msg.url;

  var head;
  if (msg.head) {
    head = new Buffer(msg.head, "base64");
  }
  targetServer.emit("upgrade", req, socket, head);
};

function connectionHandler(connection) {
  connection._hotpotato = {
    pendingReqs: []
  };

  // The idea here is to shim the parser onHeaders / onHeadersComplete events.
  // When they're called we mark the request as "parsing". When we've fully
  // received the request, we check if we've already begun parsing again.
  // This can happen if we get pipelined requests.
  var shim = function(original) {
    return function() {
      connection._hotpotato.parsing = true;
      original.apply(this, arguments);
    };
  };

  if (HTTPParser.kOnHeadersComplete) {
    shimmer.wrap(connection.parser, HTTPParser.kOnHeaders, shim);
    shimmer.wrap(connection.parser, HTTPParser.kOnHeadersComplete, shim);
  } else {
    shimmer.wrap(connection.parser, "onHeaders", shim);
    shimmer.wrap(connection.parser, "onHeadersComplete", shim);
  }
}

function handlePassingRequest(req, res) {
  var deferred = Promise.defer(),
      socket = req.connection;

  req.headers["X-HOTPOTATO-WORKER"] = cluster.worker.id;
  req.headers["X-HOTPOTATO-KEEPALIVE"] = res.shouldKeepAlive;

  // Open a connection to the target and proxy the inbound request to it.
  var proxyReq = http.request({
    socketPath: req._hotpotato.proxyTo,
    path: req.url,
    method: req.method,
    headers: req.headers
  });

  req.pipe(proxyReq);

  proxyReq.on("error", function() {
    // TODO: cleanup here.
    workerDebug("Incoming request errored while we were proxying it.");
  });

  // Once we've finished sending the request over the wire, we might want to
  // pause the connection in preparation for a handover.
  req.on("end", function() {
    if (socket._hotpotato.passConnection && !socket._hotpotato.parsing && !socket._hotpotato.pendingReqs.length) {
      // Okay. We finished proxying this request, and we're not already parsing
      // another. Pause the connection so that we don't pull anything else
      // off it.
      req.pause();
      socket.parser.pause();
    }
  });

  // Once we get a response from the target worker, we proxy that over to the
  // client.
  proxyReq.on("response", function(proxyResp) {
    // TODO: write status reason phrase if exists.
    res.writeHead(proxyResp.statusCode, proxyResp.headers);
    // TODO: error handling on pipe.
    proxyResp.pipe(res);
    proxyResp.on("end", function() {
      if (socket._hotpotato.passConnection && !socket._hotpotato.parsing && !socket._hotpotato.pendingReqs.length) {
        workerDebug("Passing off a connection.");
        // The connection needs to be passed to the new worker, *and* this is
        // a good time to do it. So let's do it.
        clusterphone.sendToMaster("passConnection", { id: req._hotpotato.targetWorker }, socket);

        // Cleanup the socket from our end.
        socket.emit("close");
      }

      // We have now finished proxying this request.
      // If we're attempting to pass off this connection though, we're not done
      // until all pending requests have been proxied and finished up.
      if (socket._hotpotato.passConnection) {
        if (socket._hotpotato.pendingReqs.length) {
          var next = socket._hotpotato.pendingReqs.shift();
          var nextReq = next[0], nextRes = next[1];
          nextReq._hotpotato = {
            targetWorker: req._hotpotato.targetWorker,
            proxyTo: req._hotpotato.proxyTo
          };
          deferred.resolve(handlePassingRequest(nextReq, nextRes));
        }
      }
      deferred.resolve();
    });
  });

  return deferred.promise;
}

// TODO: what happens if a second request is passed from the same
// connection whilst one is still in progress?

// This handles the process of passing off the given request to another worker.
// It can also operate in a mode where it passes off the whole connection to
// the new worker.
function pass(passConnection, req, res) {
  workerDebug("Passing off a request.");

  var socket = req.connection;

  // Sanity checks.
  if (socket._hotpotato.isRerouted) {
    throw new Error("Attempting to pass a request that was already passed from another worker. This is not supported.");
  }
  if (req._hotpotato) {
    throw new Error("Looks like pass was called on same request more than once.");
  }

  // Set up state.
  req._hotpotato = {};

  if (passConnection) {
    socket._hotpotato.passConnection = true;
    // Reset the parsing flag.
    socket._hotpotato.parsing = false;

    socket.on("__hotpotato-request", function(req, res) {
      socket._hotpotato.parsing = false;
      socket._hotpotato.pendingReqs.push([req, res]);
    });
  }

  // Make sure we don't read anything off connection whilst we're asking master
  // where to route this request to.
  req.pause();

  clusterphone.sendToMaster("routeConnection", {
    method: req.method,
    url: req.url,
    headers: req.headers
  }).then(function(routeReply) {
    if (routeReply.error) {
      // TODO: handle this better.
      workerDebug("Failed to pass off connection: " + routeReply.error);
      res.writeHead(500);
      res.end();
      return;
    }

    req._hotpotato.targetWorker = routeReply.id;
    req._hotpotato.proxyTo = routeReply.path;

    // It's safe to resume the request now.
    req.resume();

    handlePassingRequest(req, res);
  });
}

function setServer(server) {
  if (targetServer) {
    targetServer.removeListener("connection", connectionHandler);
  }
  targetServer = server;
  targetServer.on("connection", connectionHandler);

  // This is a little hacky, but hear me out.
  // If the caller opts to passAll() on a request, it means that the current
  // request and all subsequent requests for that connection should be
  // bounced elsewhere. The problem is, we might get two requests in rapid
  // succession (pipelined), or a request that comes in whilst we're still
  // finishing up flushing out a response (keep-alive requests on a good
  // network). In this case we don't want to emit request events for that
  // connection anymore. This is how we filter those out.
  shimmer.wrap(server, "emit", function(original) {
    return function(event, req, res) {
      // If we don't own this server anymore, then skip this.
      if (targetServer !== this) {
        return original.apply(this, arguments);
      }

      if ((event === "request") && req && req.connection &&
          req.connection._hotpotato && req.connection._hotpotato.passConnection) {
        return req.connection.emit("__hotpotato-request", req, res);
      }

      return original.apply(this, arguments);
    };
  });
}

function passUpgrade(req, socket, head) {
  workerDebug("Passing off an upgrade.");

  if (req._hotpotato) {
    throw new Error("Looks like passUpgrade was called on same request more than once.");
  }

  req._hotpotato = {};

  clusterphone.sendToMaster ("passUpgrade", {
    method: req.method,
    url: req.url,
    headers: req.headers,
    httpVersionMinor: req.httpVersionMinor,
    httpVersionMajor: req.httpVersionMajor,
    head: head ? head.toString("base64") : null,
  }, socket);
}

exports.passRequest = pass.bind(null, false);
exports.passConnection = pass.bind(null, true);
exports.passUpgrade = passUpgrade;
exports.server = setServer;
<|endoftext|>


<commit_before>var wunode = require("./wunode");

// Utility functions for creating a standard HTML from from an array of Wufoo fields

var buildForm = function(fieldsArray){
	//Create "intro": <form> tag etc
	var formHTML = "<form>";
	//Loop through fields
	//Only need input fields, see following for example of this check in an entry:
	// for (var property in entry) {
	// 	if(property.indexOf("Field") > -1){
	// 		If the property has a "Field" property, it's an input field
	// 	}
	// }
	var fields = fieldsArray.Fields;
	for (var i = 1; i < fields.length; i++) {

		//Determine type of field, and extract necessary info
		var fieldType = fields[i].Type;
		var fieldLabel = fields[i].Title;
		var fieldID = fields[i].ID;

		if(fieldLabel === "Date Created" ||fieldLabel === "Created By" || fieldLabel === "Last Updated" || fieldLabel === "Updated By"){
			continue;
		}
		//Call appropriate helper method to build HTML for this field
		switch (fieldType) {
			case "text":
				formHTML += buildText(fieldLabel, fieldID);
				break;
			case "number":
				formHTML += buildNumber(fieldLabel, fieldID);
				break;
			case "textarea":
				formHTML += buildParagraph(fieldLabel, fieldID);
				break;
			case "checkbox":
				formHTML += buildCheckbox(fieldLabel, fieldID);
				break;
			case "radio":
				formHTML += buildMultipleChoice(fieldLabel, fieldID);
				break;
			case "select":
				formHTML += buildDropdown(fieldLabel, fieldID);
				break;
			case "shortname":
				formHTML += buildName(fieldLabel, fieldID);
				break;
			case "file":
				formHTML += buildFile(fieldLabel, fieldID);
				break;
			case "address":
				formHTML += buildAddress(fieldLabel, fieldID);
				break;
			case "date":
				formHTML += buildDate(fieldLabel, fieldID);
				break;
			case "email":
				formHTML += buildEmail(fieldLabel, fieldID);
				break;
			case "time":
				formHTML += buildTime(fieldLabel, fieldID);
				break;
			case "phone":
				formHTML += buildPhone(fieldLabel, fieldID);
				break;
			case "url":
				formHTML += buildWebsite(fieldLabel, fieldID);
				break;
			case "money":
				formHTML += buildPrice(fieldLabel, fieldID);
				break;
			case "likert":
				formHTML += buildLikert(fieldLabel, fieldID);
				break;
			case "rating":
				formHTML += buildRating(fieldLabel, fieldID);
				break;
			default:
				console.log("Found "+fieldType);
				break;
		}
		formHTML+= "
";
	};

	//Add "outro": </form>, etc
	formHTML += "</form>";
	//Return string containing HTML for the form
	return formHTML;
}

var buildText = function(fieldLabel, fieldID){
	var fieldHTML = ""+fieldLabel+"";
	fieldHTML += "<input type='text'  name="+fieldID+">";
	return fieldHTML;
}

var buildNumber = function(fieldLabel, fieldID){
	var fieldHTML = ""+fieldLabel+"";
	fieldHTML += "<input type='number'  name="+fieldID+">";
	return fieldHTML;
}

var buildParagraph = function(fieldLabel, fieldID){
	var fieldHTML = ""+fieldLabel+"";
	fieldHTML += "<textarea name="+fieldID+"></textarea>";
	return fieldHTML;
}

var buildCheckbox = function(fieldLabel, fieldID){
	var fieldHTML = ""+fieldLabel+"";
	fieldHTML += "<input type='text'  name="+fieldID+">";
	return fieldHTML;
}

var buildMultipleChoice = function(fieldLabel, fieldID){
	var fieldHTML = ""+fieldLabel+"";
	fieldHTML += "<input type='text'  name="+fieldID+">";
	return fieldHTML;
}

var buildDropdown = function(fieldLabel, fieldID){
	var fieldHTML = ""+fieldLabel+"";
	fieldHTML += "<input type='text'  name="+fieldID+">";
	return fieldHTML;
}

var buildName = function(fieldLabel, fieldID){
	var fieldHTML = ""+fieldLabel+"";
	fieldHTML += "<input type='text'  name="+fieldID+">";
	fieldHTML += "First"
	fieldHTML += "<input type='text'  name="+(fieldID+1)+">";
	fieldHTML += "Last"
	return fieldHTML;
}

var buildFile = function(fieldLabel, fieldID){
	var fieldHTML = ""+fieldLabel+"";
	fieldHTML += "<input type='file'  name="+fieldID+">";
	return fieldHTML;
}

var buildAddress = function(fieldLabel, fieldID){
	var fieldHTML = ""+fieldLabel+"";
	fieldHTML += "<input type='text'  name="+fieldID+">";
	return fieldHTML;
}

var buildDate = function(fieldLabel, fieldID){
	var fieldHTML = ""+fieldLabel+"";
	fieldHTML += "<input type='date'  name="+fieldID+">";
	return fieldHTML;
}

var buildEmail = function(fieldLabel, fieldID){
	var fieldHTML = ""+fieldLabel+"";
	fieldHTML += "<input type='email'  name="+fieldID+">";
	return fieldHTML;
}

var buildTime = function(fieldLabel, fieldID){
	var fieldHTML = ""+fieldLabel+"";
	fieldHTML += "<input type='time'  name="+fieldID+">";
	return fieldHTML;
}

var buildPhone = function(fieldLabel, fieldID){
	var fieldHTML = ""+fieldLabel+"";
	fieldHTML += "<input type='phone'  name="+fieldID+">";
	return fieldHTML;
}

var buildWebsite = function(fieldLabel, fieldID){
	var fieldHTML = ""+fieldLabel+"";
	fieldHTML += "<input type='url'  name="+fieldID+">";
	return fieldHTML;
}

var buildPrice = function(fieldLabel, fieldID){
	var fieldHTML = ""+fieldLabel+"";
	fieldHTML += "<input type='money'  name="+fieldID+">";
	return fieldHTML;
}

var buildLikert = function(fieldLabel, fieldID){
	var fieldHTML = ""+fieldLabel+"";
	fieldHTML += "<input type='text'  name="+fieldID+">";
	return fieldHTML;
}

var buildRating = function(fieldLabel, fieldID){
	var fieldHTML = ""+fieldLabel+"";
	fieldHTML += "<input type='text'  name="+fieldID+">";
	return fieldHTML;
}
wunode.setSubdomain("michaellimsm");
wunode.setApiKey("M8X9-0MP5-63FD-EUG8");
wunode.getFields("papqekz17234pt", false, false, function(fields){

	console.log(buildForm(fields));

});

<commit_msg>Added structure for building other field types<commit_after>var wunode = require("./wunode");

// Utility functions for creating a standard HTML from from an array of Wufoo fields

var buildForm = function(fieldsArray){
	//Create "intro": <form> tag etc
	var formHTML = "<form>";
	//Loop through fields
	//Only need input fields, see following for example of this check in an entry:
	// for (var property in entry) {
	// 	if(property.indexOf("Field") > -1){
	// 		If the property has a "Field" property, it's an input field
	// 	}
	// }
	var fields = fieldsArray.Fields;
	for (var i = 1; i < fields.length; i++) {

		//Determine type of field, and extract necessary info
		var fieldType = fields[i].Type;
		var fieldLabel = fields[i].Title;
		var fieldID = fields[i].ID;

		if(fieldLabel === "Date Created" ||fieldLabel === "Created By" || fieldLabel === "Last Updated" || fieldLabel === "Updated By"){
			continue;
		}
		//Call appropriate helper method to build HTML for this field
		//For Checkbox, MulipleChoice, Dropdown, pass choices/subfields
		switch (fieldType) {
			case "text":
				formHTML += buildText(fieldLabel, fieldID);
				break;
			case "number":
				formHTML += buildNumber(fieldLabel, fieldID);
				break;
			case "textarea":
				formHTML += buildParagraph(fieldLabel, fieldID);
				break;
			case "checkbox":
				formHTML += buildCheckbox(fieldLabel, fieldID, choices);
				break;
			case "radio":
				formHTML += buildMultipleChoice(fieldLabel, fieldID, choices);
				break;
			case "select":
				formHTML += buildDropdown(fieldLabel, fieldID, choices);
				break;
			case "shortname":
				formHTML += buildName(fieldLabel, fieldID);
				break;
			case "file":
				formHTML += buildFile(fieldLabel, fieldID);
				break;
			case "address":
				formHTML += buildAddress(fieldLabel, fieldID);
				break;
			case "date":
				formHTML += buildDate(fieldLabel, fieldID);
				break;
			case "email":
				formHTML += buildEmail(fieldLabel, fieldID);
				break;
			case "time":
				formHTML += buildTime(fieldLabel, fieldID);
				break;
			case "phone":
				formHTML += buildPhone(fieldLabel, fieldID);
				break;
			case "url":
				formHTML += buildWebsite(fieldLabel, fieldID);
				break;
			case "money":
				formHTML += buildPrice(fieldLabel, fieldID);
				break;
			case "likert":
				formHTML += buildLikert(fieldLabel, fieldID);
				break;
			case "rating":
				formHTML += buildRating(fieldLabel, fieldID);
				break;
			default:
				console.log("Found "+fieldType);
				break;
		}
		formHTML+= "
";
	};

	//Add "outro": </form>, etc
	formHTML += "</form>";
	//Return string containing HTML for the form
	return formHTML;
}

var buildText = function(fieldLabel, fieldID){
	var fieldHTML = ""+fieldLabel+"";
	fieldHTML += "<input type='text'  name="+fieldID+">";
	return fieldHTML;
}

var buildNumber = function(fieldLabel, fieldID){
	var fieldHTML = ""+fieldLabel+"";
	fieldHTML += "<input type='number'  name="+fieldID+">";
	return fieldHTML;
}

var buildParagraph = function(fieldLabel, fieldID){
	var fieldHTML = ""+fieldLabel+"";
	fieldHTML += "<textarea name="+fieldID+"></textarea>";
	return fieldHTML;
}

var buildCheckbox = function(fieldLabel, fieldID, choices){
	var fieldHTML = ""+fieldLabel+"";
	fieldHTML += "<input type='checkbox'  name="+fieldID+">";
	//create checkbox input for each choice
	return fieldHTML;
}

var buildMultipleChoice = function(fieldLabel, fieldID, choices){
	var fieldHTML = ""+fieldLabel+"";
	fieldHTML += "<input type='radio'  name="+fieldID+">";
	//create another radio for each choice, same name, different value (choice.label)
	return fieldHTML;
}

var buildDropdown = function(fieldLabel, fieldID, choices){
	var fieldHTML = ""+fieldLabel+"";
	fieldHTML += "<select name="+fieldID+">";
	//add option tag, one for each choice value=choice.label
	fieldHTML += "</select>";
	return fieldHTML;
}

var buildName = function(fieldLabel, fieldID){
	var fieldHTML = ""+fieldLabel+"";
	fieldHTML += "<input type='text'  name="+fieldID+">";
	fieldHTML += "First"
	fieldHTML += "<input type='text'  name="+(fieldID+1)+">";
	fieldHTML += "Last"
	return fieldHTML;
}

var buildFile = function(fieldLabel, fieldID){
	var fieldHTML = ""+fieldLabel+"";
	fieldHTML += "<input type='file'  name="+fieldID+">";
	return fieldHTML;
}

var buildAddress = function(fieldLabel, fieldID){
	var fieldHTML = ""+fieldLabel+"";
	fieldHTML += "<input type='text'  name="+fieldID+">";
	return fieldHTML;
}

var buildDate = function(fieldLabel, fieldID){
	var fieldHTML = ""+fieldLabel+"";
	fieldHTML += "<input type='date'  name="+fieldID+">";
	return fieldHTML;
}

var buildEmail = function(fieldLabel, fieldID){
	var fieldHTML = ""+fieldLabel+"";
	fieldHTML += "<input type='email'  name="+fieldID+">";
	return fieldHTML;
}

var buildTime = function(fieldLabel, fieldID){
	var fieldHTML = ""+fieldLabel+"";
	fieldHTML += "<input type='time'  name="+fieldID+">";
	return fieldHTML;
}

var buildPhone = function(fieldLabel, fieldID){
	var fieldHTML = ""+fieldLabel+"";
	fieldHTML += "<input type='phone'  name="+fieldID+">";
	return fieldHTML;
}

var buildWebsite = function(fieldLabel, fieldID){
	var fieldHTML = ""+fieldLabel+"";
	fieldHTML += "<input type='url'  name="+fieldID+">";
	return fieldHTML;
}

var buildPrice = function(fieldLabel, fieldID){
	var fieldHTML = ""+fieldLabel+"";
	fieldHTML += "<input type='money'  name="+fieldID+">";
	return fieldHTML;
}

var buildLikert = function(fieldLabel, fieldID){
	var fieldHTML = ""+fieldLabel+"";
	fieldHTML += "<input type='text'  name="+fieldID+">";
	return fieldHTML;
}

var buildRating = function(fieldLabel, fieldID){
	var fieldHTML = ""+fieldLabel+"";
	fieldHTML += "<input type='text'  name="+fieldID+">";
	return fieldHTML;
}
wunode.setSubdomain("michaellimsm");
wunode.setApiKey("M8X9-0MP5-63FD-EUG8");
wunode.getFields("papqekz17234pt", false, false, function(fields){

	console.log(buildForm(fields));

});

<|endoftext|>


<commit_before>/**
* Quizapp.js
*
* @description :: TODO: You might write a short summary of how this model works and what it represents here.
* @docs        :: http://sailsjs.org/#!documentation/models
*/

module.exports = {

  attributes: {

  }
};

<commit_msg>tablas<commit_after>/**
* Quizapp.js
*
* @description :: TODO: You might write a short summary of how this model works and what it represents here.
* @docs        :: http://sailsjs.org/#!documentation/models
*/

module.exports = {

  attributes: {

};

<|endoftext|>


<commit_before>/* global _ps */
import React from 'react';
import PropTypes from 'prop-types';
import isRequiredIf from 'react-proptype-conditional-require';
import uuid from 'uuid/v4';
import PSSnippet from './PSSnippet';

class PSClickWrap extends React.Component {
  _isMounted = false;

  constructor(props) {
    super(props);
    this.createClickWrap = this.createClickWrap.bind(this);
    this.state = {
      clickwrapGroupKey: null,
      dynamicGroup: false,
    };
    this.propsEventMap = {
      onAll: 'all',
      onSent: 'sent',
      onRetrieved: 'retrieved',
      onSet: 'set',
      onSetSignerId: 'set:signer_id',
      onValid: 'valid',
      onInvalid: 'invalid',
      onRendered: 'rendered',
      onDisplayed: 'displayed',
      onScrolledContract: 'scrolled:contract',
      onScrolled: 'scrolled',
      onError: 'error',
    };
    const {
      psScriptUrl,
      backupScriptURL,
      accessId,
      testMode,
      disableSending,
      dynamic,
      signerId,
      debug,
    } = this.props;
    if (!PSSnippet.isSnippetLoaded(psScriptUrl, backupScriptURL)) {
      PSSnippet.injectSnippet(psScriptUrl, backupScriptURL);
    }
    _ps('create', accessId, {
      test_mode: testMode,
      disable_sending: disableSending,
      dynamic,
      signer_id: signerId,
    });
    if (debug) {
      _ps.debug = true;
    }
  }

  componentDidMount() {
    this._isMounted = true;
    this.createClickWrap();
  }

  componentDidUpdate(prevProps) {
    const {
      clickWrapStyle,
      renderData,
      filter,
      groupKey,
      signerId,
    } = this.props;
    const { clickwrapGroupKey, dynamicGroup } = this.state;
    if (
      clickWrapStyle !== prevProps.clickWrapStyle
      && !dynamicGroup
      && _ps
      && _ps.getByKey
      && typeof _ps.getByKey === 'function'
    ) {
      _ps.getByKey(clickwrapGroupKey).site.set('style', clickWrapStyle);
      _ps.getByKey(clickwrapGroupKey).retrieveHTML();
    }
    if (renderData !== prevProps.renderData) {
      _ps(`${clickwrapGroupKey}:retrieveHTML`, renderData);
    }
    if (signerId !== prevProps.signerId) {
      // Prevent style from resetting on signerId update
      _ps.getByKey(clickwrapGroupKey).site.set('style', clickWrapStyle);
      _ps('set', 'signer_id', signerId);
    }
    if (clickWrapStyle !== prevProps.clickWrapStyle && dynamicGroup) {
      this.createClickWrap();
    }
    if (filter !== prevProps.filter && dynamicGroup) {
      this.createClickWrap();
    }
    if (groupKey !== prevProps.groupKey && !dynamicGroup) {
      this.createClickWrap();
      _ps.getByKey(clickwrapGroupKey).retrieveHTML();
    }
  }

  componentWillUnmount() {
    this._isMounted = false;
    const { groupKey } = this.props;
    if (
      _ps
      && _ps.getByKey
      && typeof _ps.getByKey === 'function'
      && _ps.getByKey(groupKey)
    ) {
      if (_ps.getByKey(groupKey).rendered) {
        _ps.getByKey(groupKey).rendered = false;
      }
      _ps.getByKey(groupKey).resetData();
    }
    this.unregisterEventListeners();
  }

  registerEventListener(eventProp, groupKey) {
    const eventCallbackFn = (...args) => {
      let shouldFireListener = false;
      args.forEach((arg) => {
        // We need to check the context variable and see if it matches the groupKey, if it does -> fire the event (context argument position varies)
        if (arg && arg.get && arg.get('key') && arg.get('key') === groupKey) {
          shouldFireListener = true;
          // Else we should check if the context is for the entire site, and as such the context variable is a Site object.
        } else if (arg && arg.toString() === '[object Site]') {
          shouldFireListener = true;
        }
      });
      if (shouldFireListener) {
        this.props[eventProp](...args);
      }
    };
    // In order to handle unregistration of event listeners, we override the toString function to identify the
    // function by a UUID rather than the default toString of a function.
    const newEventListenerID = uuid();
    eventCallbackFn.toString = () => newEventListenerID;
    _ps.on(this.propsEventMap[eventProp], eventCallbackFn);
    return eventCallbackFn.toString();
  }

  registerEventListeners(groupKey) {
    const eventListeners = {};
    Object.keys(this.propsEventMap).forEach((eventProp) => {
      if (this.props[eventProp]) {
        eventListeners[
          this.propsEventMap[eventProp]
        ] = this.registerEventListener(eventProp, groupKey);
      }
    });
    // Store event listeners in state so we can unregister them later on unmount
    if (this._isMounted) {
      this.setState({ eventListeners });
    }
  }

  unregisterEventListeners() {
    if (this.state.eventListeners) {
      Object.keys(this.state.eventListeners).forEach((event) => {
        const eventUUID = this.state.eventListeners[event];
        // In order to unregister the event, we must create a fake function (typeof passed to _ps.off must be a function),
        // that returns the UUID we want to unregister.
        const fakeEventListener = () => eventUUID;
        fakeEventListener.toString = () => eventUUID;
        _ps.off(event, fakeEventListener);
      });
    }
  }

  createClickWrap() {
    const {
      filter,
      containerId,
      signerIdSelector,
      clickWrapStyle,
      displayAll,
      renderData,
      displayImmediately,
      forceScroll,
      groupKey,
      confirmationEmail,
    } = this.props;
    const options = {
      filter,
      container_selector: containerId,
      confirmation_email: confirmationEmail,
      signer_id_selector: signerIdSelector,
      style: clickWrapStyle,
      display_all: displayAll,
      render_data: renderData,
      auto_run: displayImmediately,
      force_scroll: forceScroll,
    };

    if (groupKey && this._isMounted) { this.setState({ clickwrapGroupKey: groupKey, dynamicGroup: false }); }
    const isDynamic = !groupKey;

    const eventCallback = (err, group) => {
      if (group) {
        const key = groupKey || group.get('key');

        const state = { clickwrapGroupKey: key };
        if (isDynamic) state.dynamicGroup = true;
        if (this._isMounted) {
          this.setState(state);
        }

        if (!isDynamic) group.render();
        this.registerEventListeners(key);
      }
    };

    if (groupKey) {
      _ps('load', groupKey, { ...options, event_callback: eventCallback });
    } else _ps('load', { ...options, event_callback: eventCallback });
  }

  render() {
    const { containerId } = this.props;
    return
;
  }
}

PSClickWrap.FILTER_OR_GROUPKEY_REQUIRED_ERROR_MESSAGE = 'PSClickWrap Error: You must provide either a groupKey or filter prop in order to use the PactSafe ClickWrap component!';
PSClickWrap.MUST_PROVIDE_RENDER_DATA_ERROR_MESSAGE = 'PSClickWrap Error: You must provide a renderData prop when passing down the dynamic prop';
PSClickWrap.MUST_PROVIDE_SIGNER_ID_OR_SIGNER_ID_SELECTOR = 'PSClickWrap Error: You must provide either a signer ID or a signer ID selector';

PSClickWrap.propTypes = {
  accessId: PropTypes.string.isRequired,
  clickWrapStyle: PropTypes.oneOf([
    'full',
    'scroll',
    'checkbox',
    'combined',
    'embedded',
  ]),
  confirmationEmail: PropTypes.bool,
  disableSending: PropTypes.bool,
  displayAll: PropTypes.bool,
  displayImmediately: PropTypes.bool,
  dynamic: PropTypes.bool,
  containerId: PropTypes.string,
  filter: isRequiredIf(
    PropTypes.string,
    props => !props.hasOwnProperty('groupKey'),
    PSClickWrap.FILTER_OR_GROUPKEY_REQUIRED_ERROR_MESSAGE,
  ),
  forceScroll: PropTypes.bool,
  groupKey: isRequiredIf(
    PropTypes.string,
    props => !props.hasOwnProperty('filter'),
    PSClickWrap.FILTER_OR_GROUPKEY_REQUIRED_ERROR_MESSAGE,
  ),
  psScriptUrl: PropTypes.string,
  backupScriptURL: PropTypes.string,
  renderData: isRequiredIf(
    PropTypes.object,
    props => props.hasOwnProperty('dynamic') && props.dynamic === true,
    PSClickWrap.MUST_PROVIDE_RENDER_DATA_ERROR_MESSAGE,
  ),
  signerIdSelector: isRequiredIf(
    PropTypes.string,
    props => !props.hasOwnProperty('signerId'),
    PSClickWrap.MUST_PROVIDE_SIGNER_ID_OR_SIGNER_ID_SELECTOR,
  ),
  signerId: isRequiredIf(
    PropTypes.string,
    props => !props.hasOwnProperty('signerIdSelector'),
    PSClickWrap.MUST_PROVIDE_SIGNER_ID_OR_SIGNER_ID_SELECTOR,
  ),
  testMode: PropTypes.bool,
  debug: PropTypes.bool,
  onAll: PropTypes.func,
  onSent: PropTypes.func,
  onRetrieved: PropTypes.func,
  onSet: PropTypes.func,
  onSetSignerId: PropTypes.func,
  onValid: PropTypes.func,
  onInvalid: PropTypes.func,
  onRendered: PropTypes.func,
  onDisplayed: PropTypes.func,
  onScrolledContract: PropTypes.func,
  onScrolled: PropTypes.func,
  onError: PropTypes.func,
};

PSClickWrap.defaultProps = {
  psScriptUrl: '//vault.pactsafe.io/ps.min.js',
  backupScriptURL: '//d3l1mqnl5xpsuc.cloudfront.net/ps.min.js',
  containerId: 'ps-clickwrap',
  displayImmediately: true,
  disableSending: false,
  displayAll: true,
  dynamic: false,
  testMode: false,
};

export default PSClickWrap;
<commit_msg>fix style resetting on renderData updating<commit_after>/* global _ps */
import React from 'react';
import PropTypes from 'prop-types';
import isRequiredIf from 'react-proptype-conditional-require';
import uuid from 'uuid/v4';
import PSSnippet from './PSSnippet';

class PSClickWrap extends React.Component {
  _isMounted = false;

  constructor(props) {
    super(props);
    this.createClickWrap = this.createClickWrap.bind(this);
    this.state = {
      clickwrapGroupKey: null,
      dynamicGroup: false,
    };
    this.propsEventMap = {
      onAll: 'all',
      onSent: 'sent',
      onRetrieved: 'retrieved',
      onSet: 'set',
      onSetSignerId: 'set:signer_id',
      onValid: 'valid',
      onInvalid: 'invalid',
      onRendered: 'rendered',
      onDisplayed: 'displayed',
      onScrolledContract: 'scrolled:contract',
      onScrolled: 'scrolled',
      onError: 'error',
    };
    const {
      psScriptUrl,
      backupScriptURL,
      accessId,
      testMode,
      disableSending,
      dynamic,
      signerId,
      debug,
    } = this.props;
    if (!PSSnippet.isSnippetLoaded(psScriptUrl, backupScriptURL)) {
      PSSnippet.injectSnippet(psScriptUrl, backupScriptURL);
    }
    _ps('create', accessId, {
      test_mode: testMode,
      disable_sending: disableSending,
      dynamic,
      signer_id: signerId,
    });
    if (debug) {
      _ps.debug = true;
    }
  }

  componentDidMount() {
    this._isMounted = true;
    this.createClickWrap();
  }

  componentDidUpdate(prevProps) {
    const {
      clickWrapStyle,
      renderData,
      filter,
      groupKey,
      signerId,
    } = this.props;
    const { clickwrapGroupKey, dynamicGroup } = this.state;
    if (
      clickWrapStyle !== prevProps.clickWrapStyle
      && !dynamicGroup
      && _ps
      && _ps.getByKey
      && typeof _ps.getByKey === 'function'
    ) {
      _ps.getByKey(clickwrapGroupKey).site.set('style', clickWrapStyle);
      _ps.getByKey(clickwrapGroupKey).retrieveHTML();
    }
    if (renderData !== prevProps.renderData) {
      if (clickWrapStyle) { _ps.getByKey(clickwrapGroupKey).site.set('style', clickWrapStyle); }
      _ps(`${clickwrapGroupKey}:retrieveHTML`, renderData);
    }
    if (signerId !== prevProps.signerId) {
      if (clickWrapStyle) { _ps.getByKey(clickwrapGroupKey).site.set('style', clickWrapStyle); }
      _ps('set', 'signer_id', signerId);
    }
    if (clickWrapStyle !== prevProps.clickWrapStyle && dynamicGroup) {
      this.createClickWrap();
    }
    if (filter !== prevProps.filter && dynamicGroup) {
      this.createClickWrap();
    }
    if (groupKey !== prevProps.groupKey && !dynamicGroup) {
      this.createClickWrap();
      _ps.getByKey(clickwrapGroupKey).retrieveHTML();
    }
  }

  componentWillUnmount() {
    this._isMounted = false;
    const { groupKey } = this.props;
    if (
      _ps
      && _ps.getByKey
      && typeof _ps.getByKey === 'function'
      && _ps.getByKey(groupKey)
    ) {
      if (_ps.getByKey(groupKey).rendered) {
        _ps.getByKey(groupKey).rendered = false;
      }
      _ps.getByKey(groupKey).resetData();
    }
    this.unregisterEventListeners();
  }

  registerEventListener(eventProp, groupKey) {
    const eventCallbackFn = (...args) => {
      let shouldFireListener = false;
      args.forEach((arg) => {
        // We need to check the context variable and see if it matches the groupKey, if it does -> fire the event (context argument position varies)
        if (arg && arg.get && arg.get('key') && arg.get('key') === groupKey) {
          shouldFireListener = true;
          // Else we should check if the context is for the entire site, and as such the context variable is a Site object.
        } else if (arg && arg.toString() === '[object Site]') {
          shouldFireListener = true;
        }
      });
      if (shouldFireListener) {
        this.props[eventProp](...args);
      }
    };
    // In order to handle unregistration of event listeners, we override the toString function to identify the
    // function by a UUID rather than the default toString of a function.
    const newEventListenerID = uuid();
    eventCallbackFn.toString = () => newEventListenerID;
    _ps.on(this.propsEventMap[eventProp], eventCallbackFn);
    return eventCallbackFn.toString();
  }

  registerEventListeners(groupKey) {
    const eventListeners = {};
    Object.keys(this.propsEventMap).forEach((eventProp) => {
      if (this.props[eventProp]) {
        eventListeners[
          this.propsEventMap[eventProp]
        ] = this.registerEventListener(eventProp, groupKey);
      }
    });
    // Store event listeners in state so we can unregister them later on unmount
    if (this._isMounted) {
      this.setState({ eventListeners });
    }
  }

  unregisterEventListeners() {
    if (this.state.eventListeners) {
      Object.keys(this.state.eventListeners).forEach((event) => {
        const eventUUID = this.state.eventListeners[event];
        // In order to unregister the event, we must create a fake function (typeof passed to _ps.off must be a function),
        // that returns the UUID we want to unregister.
        const fakeEventListener = () => eventUUID;
        fakeEventListener.toString = () => eventUUID;
        _ps.off(event, fakeEventListener);
      });
    }
  }

  createClickWrap() {
    const {
      filter,
      containerId,
      signerIdSelector,
      clickWrapStyle,
      displayAll,
      renderData,
      displayImmediately,
      forceScroll,
      groupKey,
      confirmationEmail,
    } = this.props;
    const options = {
      filter,
      container_selector: containerId,
      confirmation_email: confirmationEmail,
      signer_id_selector: signerIdSelector,
      style: clickWrapStyle,
      display_all: displayAll,
      render_data: renderData,
      auto_run: displayImmediately,
      force_scroll: forceScroll,
    };

    if (groupKey && this._isMounted) {
      this.setState({ clickwrapGroupKey: groupKey, dynamicGroup: false });
    }
    const isDynamic = !groupKey;

    const eventCallback = (err, group) => {
      if (group) {
        const key = groupKey || group.get('key');

        const state = { clickwrapGroupKey: key };
        if (isDynamic) state.dynamicGroup = true;
        if (this._isMounted) {
          this.setState(state);
        }

        if (!isDynamic) group.render();
        this.registerEventListeners(key);
      }
    };

    if (groupKey) {
      _ps('load', groupKey, { ...options, event_callback: eventCallback });
    } else _ps('load', { ...options, event_callback: eventCallback });
  }

  render() {
    const { containerId } = this.props;
    return
;
  }
}

PSClickWrap.FILTER_OR_GROUPKEY_REQUIRED_ERROR_MESSAGE = 'PSClickWrap Error: You must provide either a groupKey or filter prop in order to use the PactSafe ClickWrap component!';
PSClickWrap.MUST_PROVIDE_RENDER_DATA_ERROR_MESSAGE = 'PSClickWrap Error: You must provide a renderData prop when passing down the dynamic prop';
PSClickWrap.MUST_PROVIDE_SIGNER_ID_OR_SIGNER_ID_SELECTOR = 'PSClickWrap Error: You must provide either a signer ID or a signer ID selector';

PSClickWrap.propTypes = {
  accessId: PropTypes.string.isRequired,
  clickWrapStyle: PropTypes.oneOf([
    'full',
    'scroll',
    'checkbox',
    'combined',
    'embedded',
  ]),
  confirmationEmail: PropTypes.bool,
  disableSending: PropTypes.bool,
  displayAll: PropTypes.bool,
  displayImmediately: PropTypes.bool,
  dynamic: PropTypes.bool,
  containerId: PropTypes.string,
  filter: isRequiredIf(
    PropTypes.string,
    props => !props.hasOwnProperty('groupKey'),
    PSClickWrap.FILTER_OR_GROUPKEY_REQUIRED_ERROR_MESSAGE,
  ),
  forceScroll: PropTypes.bool,
  groupKey: isRequiredIf(
    PropTypes.string,
    props => !props.hasOwnProperty('filter'),
    PSClickWrap.FILTER_OR_GROUPKEY_REQUIRED_ERROR_MESSAGE,
  ),
  psScriptUrl: PropTypes.string,
  backupScriptURL: PropTypes.string,
  renderData: isRequiredIf(
    PropTypes.object,
    props => props.hasOwnProperty('dynamic') && props.dynamic === true,
    PSClickWrap.MUST_PROVIDE_RENDER_DATA_ERROR_MESSAGE,
  ),
  signerIdSelector: isRequiredIf(
    PropTypes.string,
    props => !props.hasOwnProperty('signerId'),
    PSClickWrap.MUST_PROVIDE_SIGNER_ID_OR_SIGNER_ID_SELECTOR,
  ),
  signerId: isRequiredIf(
    PropTypes.string,
    props => !props.hasOwnProperty('signerIdSelector'),
    PSClickWrap.MUST_PROVIDE_SIGNER_ID_OR_SIGNER_ID_SELECTOR,
  ),
  testMode: PropTypes.bool,
  debug: PropTypes.bool,
  onAll: PropTypes.func,
  onSent: PropTypes.func,
  onRetrieved: PropTypes.func,
  onSet: PropTypes.func,
  onSetSignerId: PropTypes.func,
  onValid: PropTypes.func,
  onInvalid: PropTypes.func,
  onRendered: PropTypes.func,
  onDisplayed: PropTypes.func,
  onScrolledContract: PropTypes.func,
  onScrolled: PropTypes.func,
  onError: PropTypes.func,
};

PSClickWrap.defaultProps = {
  psScriptUrl: '//vault.pactsafe.io/ps.min.js',
  backupScriptURL: '//d3l1mqnl5xpsuc.cloudfront.net/ps.min.js',
  containerId: 'ps-clickwrap',
  displayImmediately: true,
  disableSending: false,
  displayAll: true,
  dynamic: false,
  testMode: false,
};

export default PSClickWrap;
<|endoftext|>


<commit_before>import lively from 'src/client/lively.js';
import Morph from './Morph.js';

import * as nodes from 'src/client/morphic/node-helpers.js';
import * as events from 'src/client/morphic/event-helpers.js';

import selecting from 'src/client/morphic/selecting.js';

import {pt, rect, Rectangle} from 'lively.graphics';

/*
 * Halo, the container for HaloItems
 */

export default class Halo extends Morph {

  get isMetaNode() { return true}

  initialize() {
    Halo.halo = $(this);
    Halo.halo.hide();
    window.HaloService = Halo;
    this.registerBodyDragAndDrop(document.body);
  }

  registerBodyDragAndDrop(targetContext) {
    // document.body.draggable=true;
    lively.removeEventListener("Halo", targetContext)
    lively.addEventListener("Halo", targetContext, "mousedown",
      evt => this.onBodyMouseDown(evt, targetContext));
    lively.addEventListener("Halo", targetContext, "dragstart",
      evt => this.onBodyDragStart(evt, targetContext));
    lively.addEventListener("Halo", targetContext, "drag",
      evt => this.onBodyDrag(evt, targetContext));
    lively.addEventListener("Halo", targetContext, "dragend",
      evt => this.onBodyDragEnd(evt, targetContext));
  }

  onBodyMouseDown(evt, targetContext) {
    var whitelistNodes = lively.html.findAllNodes() // #TODO only find nodes of subelement
        .filter (ea => ea.tagName == 'INPUT' ||
          ea.tagName == "LI" || ea.tagName == "TD" ||
          ea.tagName == "P" ||  ea.tagName == "PRE")
        .filter (ea => {
          var b = ea.getBoundingClientRect();
          var bounds = new Rectangle(b.left, b.top, b.width, b.height) ;
          var pos = events.globalPosition(evt);
          // lively.showPoint(bounds.topLeft())
          // lively.showPoint(pos)
          return bounds.containsPoint(pos);
      });
    // inputFields.forEach( ea => lively.showElement(ea))
    if (whitelistNodes.length > 0) {
      // evt.preventDefault();
      // evt.stopPropagation();
      document.body.draggable=false;
      return false;
    }
    document.body.draggable=true;
  }

  onBodyDragStart(evt) {
    lively.notify("drag start")
    if (this.selection) this.selection.remove(); // #TODO reuse eventually?
    this.selection = lively.components.createComponent("lively-selection");
    lively.components.openIn(document.body, this.selection).then(comp => {
      comp.onSelectionDragStart(evt);
    });

    // give it something to drag
    var div = document.createElement("div");
    evt.dataTransfer.setDragImage(div, 0, 0);
  }

  onBodyDrag(evt, targetContext) {
    lively.notify("drag")

    //evt.preventDefault();
    // return false
    if (!this.selection) return;
    this.selection.onSelectionDrag && this.selection.onSelectionDrag(evt)
  }

  onBodyDragEnd(evt, targetContext) {
    // evt.preventDefault();
    // return false
    if (!this.selection) return;
    this.selection.onSelectionDragEnd && this.selection.onSelectionDragEnd(evt)
  }

  showHalo(target, path) {

    document.body.appendChild(this);

    if (!target.getBoundingClientRect) {
      $(this).show();
      return;
    }
    var bounds = target.getBoundingClientRect();
    var offset = {
      top: bounds.top +  $(document).scrollTop(),
      left: bounds.left +  $(document).scrollLeft()};

    // viewport coordinates
    var scrollTop = Math.abs($(document).scrollTop());
    var scrollLeft = Math.abs($(document).scrollLeft());

    // make sure halo respects left and top viewport boundary
    var offsetTop = Math.max(offset.top - 30, scrollTop);
    var offsetLeft = Math.max(offset.left - 30, scrollLeft);
    var offsetTopDiff = offsetTop - offset.top;
    var offsetLeftDiff = offsetLeft - offset.left;
    offset.top = offsetTop;
    offset.left = offsetLeft;

    // make sure halo respects right and bottom viewport boundary
    var width = $(target).outerWidth() - offsetLeftDiff + 30;
    var height = $(target).outerHeight() - offsetTopDiff + 30;
    var offsetBottom = Math.min(offset.top + height, scrollTop + $(window).height());
    var offsetRight = Math.min(offset.left + width, scrollLeft + $(window).width());
    width = offsetRight - offsetLeft;
    height = offsetBottom - offsetTop;

    // set position and dimensions of halo
    $(this).show();
    $(this).offset(offset);
    $(this).outerWidth(width);
    $(this).outerHeight(height);
  }

  static showHalos(target, path) {
    this.target = $(target);
    this.halo[0].showHalo(target, path);
  }

  static hideHalos() {
    if (this.areHalosActive())
      this.halosHidden = Date.now();
    this.halo.offset({left:0, top: 0});
    this.halo.hide();
  }

  static areHalosActive() {
    return this.halo && this.halo.is(":visible");
  }
}
<commit_msg>AUTO-COMMIT templates/lively-halo.js<commit_after>import lively from 'src/client/lively.js';
import Morph from './Morph.js';

import * as nodes from 'src/client/morphic/node-helpers.js';
import * as events from 'src/client/morphic/event-helpers.js';

import selecting from 'src/client/morphic/selecting.js';

import {pt, rect, Rectangle} from 'lively.graphics';

/*
 * Halo, the container for HaloItems
 */

export default class Halo extends Morph {

  get isMetaNode() { return true}

  initialize() {
    Halo.halo = $(this);
    Halo.halo.hide();
    window.HaloService = Halo;
    this.registerBodyDragAndDrop(document.body);
  }

  registerBodyDragAndDrop(targetContext) {
    // document.body.draggable=true;
    lively.removeEventListener("Halo", targetContext)
    lively.addEventListener("Halo", targetContext, "mousedown",
      evt => this.onBodyMouseDown(evt, targetContext));
    lively.addEventListener("Halo", targetContext, "dragstart",
      evt => this.onBodyDragStart(evt, targetContext));
    lively.addEventListener("Halo", targetContext, "drag",
      evt => this.onBodyDrag(evt, targetContext));
    lively.addEventListener("Halo", targetContext, "dragend",
      evt => this.onBodyDragEnd(evt, targetContext));
  }

  onBodyMouseDown(evt, targetContext) {
    var whitelistNodes = lively.html.findAllNodes() // #TODO only find nodes of subelement
        .filter (ea => ea.tagName == 'INPUT' ||
          ea.tagName == "LI" || ea.tagName == "TD" ||
          ea.tagName == "P" ||  ea.tagName == "PRE")
        .filter (ea => {
          var b = ea.getBoundingClientRect();
          var bounds = new Rectangle(b.left, b.top, b.width, b.height) ;
          var pos = events.globalPosition(evt);
          // lively.showPoint(bounds.topLeft())
          // lively.showPoint(pos)
          return bounds.containsPoint(pos);
      });
    // inputFields.forEach( ea => lively.showElement(ea))
    if (whitelistNodes.length > 0) {
      // evt.preventDefault();
      // evt.stopPropagation();
      document.body.draggable=false;
      return false;
    }
    this.registerBodyDragAndDrop(targetContext);
    document.body.draggable=true;
  }

  onBodyDragStart(evt) {
    lively.notify("drag start")
    if (this.selection) this.selection.remove(); // #TODO reuse eventually?
    this.selection = lively.components.createComponent("lively-selection");
    lively.components.openIn(document.body, this.selection).then(comp => {
      comp.onSelectionDragStart(evt);
    });

    // give it something to drag
    var div = document.createElement("div");
    evt.dataTransfer.setDragImage(div, 0, 0);
  }

  onBodyDrag(evt, targetContext) {
    lively.notify("drag")

    //evt.preventDefault();
    // return false
    if (!this.selection) return;
    this.selection.onSelectionDrag && this.selection.onSelectionDrag(evt)
  }

  onBodyDragEnd(evt, targetContext) {
    // evt.preventDefault();
    // return false
    if (!this.selection) return;
    this.selection.onSelectionDragEnd && this.selection.onSelectionDragEnd(evt)
  }

  showHalo(target, path) {

    document.body.appendChild(this);

    if (!target.getBoundingClientRect) {
      $(this).show();
      return;
    }
    var bounds = target.getBoundingClientRect();
    var offset = {
      top: bounds.top +  $(document).scrollTop(),
      left: bounds.left +  $(document).scrollLeft()};

    // viewport coordinates
    var scrollTop = Math.abs($(document).scrollTop());
    var scrollLeft = Math.abs($(document).scrollLeft());

    // make sure halo respects left and top viewport boundary
    var offsetTop = Math.max(offset.top - 30, scrollTop);
    var offsetLeft = Math.max(offset.left - 30, scrollLeft);
    var offsetTopDiff = offsetTop - offset.top;
    var offsetLeftDiff = offsetLeft - offset.left;
    offset.top = offsetTop;
    offset.left = offsetLeft;

    // make sure halo respects right and bottom viewport boundary
    var width = $(target).outerWidth() - offsetLeftDiff + 30;
    var height = $(target).outerHeight() - offsetTopDiff + 30;
    var offsetBottom = Math.min(offset.top + height, scrollTop + $(window).height());
    var offsetRight = Math.min(offset.left + width, scrollLeft + $(window).width());
    width = offsetRight - offsetLeft;
    height = offsetBottom - offsetTop;

    // set position and dimensions of halo
    $(this).show();
    $(this).offset(offset);
    $(this).outerWidth(width);
    $(this).outerHeight(height);
  }

  static showHalos(target, path) {
    this.target = $(target);
    this.halo[0].showHalo(target, path);
  }

  static hideHalos() {
    if (this.areHalosActive())
      this.halosHidden = Date.now();
    this.halo.offset({left:0, top: 0});
    this.halo.hide();
  }

  static areHalosActive() {
    return this.halo && this.halo.is(":visible");
  }
}
<|endoftext|>


<commit_before>'use strict';

/**
 * Module dependencies.
 */
var mongoose = require('mongoose'),
        Schema = mongoose.Schema,
        autoIncrement = require('mongoose-auto-increment');

//autoIncrement.initialize(mongoose);

var ProjectSchema = new Schema({
    project_ref: {
        type: String,
        required: true
    },
    title: {
        type: String,
        required: true,
        trim: true
    },
    coordinator: {
        type: String,
        required: true
    },
    organisation: {
        type: Schema.ObjectId,
        ref: 'Organisation',
        required: true
    },
    state: {
        type: String,
        default: 'rekisteröity',
        required: true
    },
    reg_date: {
        type: Date,
        default: Date.now
    },
    funding: {
        applied_curr_local: {
            type: Number,
            required: true
        },
        applied_curr_eur: {
            type: Number,
            required: true
        },
        paid_eur: {
            type: Number,
            default: 0
        },
        left_eur: {
            type: Number,
            default: 0
        }
    },
    duration_months: {
        type: Number,
        required: true,
        trim: true
    },
    description: {
        type: String,
        required: true,
        trim: true
    },
    description_en: {
        type: String,
        required: true,
        trim: true
    },
    background: {
        type: String,
        required: true,
        trim: true
    },
    beneficiaries: {
        type: String,
        required: true,
        trim: true
    },
    gender_aspect: {
        type: String,
        required: true,
        trim: true
    },
    project_goal: {
        type: String,
        required: true,
        trim: true
    },
    sustainability_risks: {
        type: String,
        required: true,
        trim: true
    },
    reporting_evaluation: {
        type: String,
        required: true,
        trim: true
    },
    other_donors_proposed: {
        type: String,
        required: false,
        trim: true
    },
    dac: {
        type: String,
        required: true,
        trim: true
    },
    region: {
        type: String,
        required: true,
        trim: true
    },
    in_review: {
        date: {
          type: Date
        },
        user: {
          type: String
        },
        comments: {
          type: String
        }
    },
    approved: {
        date: {
          type: Date
        },
        user: {
          type: String
        },
        approved_date: {
          type: Date
        },
        approved_by: {
          type: String
        },
        board_notified: {
          type: Date
        },
        granted_sum_eur: {
          type: Number
        },
        themes: {
          type: Array
        },
        methods: {
          type: Array
        }
    },
    rejected: {
        date: {
          type: Date
        },
        user: {
          type: String
        },
        rejection_categories: {
          type: Array
        },
        rejection_comments: {
          type: String
        }
    },
    signed: {
        date: {
          type: Date
        },
        user: {
          type: String
        },
        signed_by: {
          type: String
        },
        signed_date: {
          type: Date
        },
        planned_payments: {
          type: Array
        },
        intreport_deadlines: {
          type: Array
      }
    },
    payments: {
      type: Array
    },
    intermediary_reports:
    {
      type: Array
    },
    end_report: {
        date: {
          type: Date
        },
        user: {
          type: String
        },
        audit: {
            date: {
              type: Date
            },
            review: {
              type: String
            }
        },
        approved_by: {
          type: String
        },
        approved_date: {
          type: Date
        },
        general_review: {
          type: String
        },
        methods: {
          type: Array
        },
        objective: {
          type: String
        },
        comments: {
          type: String
        },
        processed: {
          type: Boolean,
          default: false
        }
    },
    ended: {
        date: {
          type: Date
        },
        user: {
          type: String
        },
        end_date: {
          type: Date
        },
        board_notified: {
          type: String
        },
        approved_by: {
          type: String
        },
        other_comments: {
          type: String
      }
    }

});

/*ProjectSchema.plugin(autoIncrement.plugin, {
    model: 'Project',
    field: 'project_ref',
    startAt: 1,
    prefix: prefix
});*/

ProjectSchema.path('title').validate(function (title) {
    return !!title;
}, 'Nimi ei voi olla tyhjä');

ProjectSchema.path('coordinator').validate(function (coordinator) {
    return !!coordinator;
}, 'Koordinaattorin nimi ei voi olla tyhjä');

ProjectSchema.statics.load = function (id, cb) {
    this.findOne({
        _id: id
    }).populate([
        {path: 'organisation', model: 'Organisation'}
      ]).exec(cb);
};

mongoose.model('Project', ProjectSchema);
<commit_msg>added working project ref creation<commit_after>'use strict';

/**
 * Module dependencies.
 */
var mongoose = require('mongoose'),
        Schema = mongoose.Schema;

var ProjectSchema = new Schema({
    project_ref: {
        type: String,
        required: true
    },
    title: {
        type: String,
        required: true,
        trim: true
    },
    coordinator: {
        type: String,
        required: true
    },
    organisation: {
        type: Schema.ObjectId,
        ref: 'Organisation',
        required: true
    },
    state: {
        type: String,
        default: 'rekisteröity',
        required: true
    },
    reg_date: {
        type: Date,
        default: Date.now
    },
    funding: {
        applied_curr_local: {
            type: Number,
            required: true
        },
        applied_curr_eur: {
            type: Number,
            required: true
        },
        paid_eur: {
            type: Number,
            default: 0
        },
        left_eur: {
            type: Number,
            default: 0
        }
    },
    duration_months: {
        type: Number,
        required: true,
        trim: true
    },
    description: {
        type: String,
        required: true,
        trim: true
    },
    description_en: {
        type: String,
        required: true,
        trim: true
    },
    background: {
        type: String,
        required: true,
        trim: true
    },
    beneficiaries: {
        type: String,
        required: true,
        trim: true
    },
    gender_aspect: {
        type: String,
        required: true,
        trim: true
    },
    project_goal: {
        type: String,
        required: true,
        trim: true
    },
    sustainability_risks: {
        type: String,
        required: true,
        trim: true
    },
    reporting_evaluation: {
        type: String,
        required: true,
        trim: true
    },
    other_donors_proposed: {
        type: String,
        required: false,
        trim: true
    },
    dac: {
        type: String,
        required: true,
        trim: true
    },
    region: {
        type: String,
        required: true,
        trim: true
    },
    in_review: {
        date: {
          type: Date
        },
        user: {
          type: String
        },
        comments: {
          type: String
        }
    },
    approved: {
        date: {
          type: Date
        },
        user: {
          type: String
        },
        approved_date: {
          type: Date
        },
        approved_by: {
          type: String
        },
        board_notified: {
          type: Date
        },
        granted_sum_eur: {
          type: Number
        },
        themes: {
          type: Array
        },
        methods: {
          type: Array
        }
    },
    rejected: {
        date: {
          type: Date
        },
        user: {
          type: String
        },
        rejection_categories: {
          type: Array
        },
        rejection_comments: {
          type: String
        }
    },
    signed: {
        date: {
          type: Date
        },
        user: {
          type: String
        },
        signed_by: {
          type: String
        },
        signed_date: {
          type: Date
        },
        planned_payments: {
          type: Array
        },
        intreport_deadlines: {
          type: Array
      }
    },
    payments: {
      type: Array
    },
    intermediary_reports:
    {
      type: Array
    },
    end_report: {
        date: {
          type: Date
        },
        user: {
          type: String
        },
        audit: {
            date: {
              type: Date
            },
            review: {
              type: String
            }
        },
        approved_by: {
          type: String
        },
        approved_date: {
          type: Date
        },
        general_review: {
          type: String
        },
        methods: {
          type: Array
        },
        objective: {
          type: String
        },
        comments: {
          type: String
        },
        processed: {
          type: Boolean,
          default: false
        }
    },
    ended: {
        date: {
          type: Date
        },
        user: {
          type: String
        },
        end_date: {
          type: Date
        },
        board_notified: {
          type: String
        },
        approved_by: {
          type: String
        },
        other_comments: {
          type: String
      }
    }

});

ProjectSchema.path('title').validate(function (title) {
    return !!title;
}, 'Nimi ei voi olla tyhjä');

ProjectSchema.path('coordinator').validate(function (coordinator) {
    return !!coordinator;
}, 'Koordinaattorin nimi ei voi olla tyhjä');

ProjectSchema.statics.load = function (id, cb) {
    this.findOne({
        _id: id
    }).populate([
        {path: 'organisation', model: 'Organisation'}
      ]).exec(cb);
};

mongoose.model('Project', ProjectSchema);
<|endoftext|>


<commit_before>(function (module) {

	module.filter("translate", function (Translate) {
		return function (fallback, key) {
			if (!key) console.error("No key provided for translation text ", fallback);

			return Translate(key, fallback);
		}
	});

	module.directive("translate", function (Translate) {
		return {
			restrict: "A",
			link: function compile(scope, element, attrs) {
				if (!attrs.translate) console.error("No key provided for translation element", element[0]);

				var translation = Translate(attrs.translate);
				if (typeof translation == "string") {
					element.html(translation);
					return;
				}
				for (var key in translation) {
					if (key == "html")
						element.html(translation[key]);
					else if (key == "text") {
						for (var i in element[0].childNodes) {
							var node = element[0].childNodes[i]
							if (node.nodeType == Node.TEXT_NODE && node.nodeValue && node.nodeValue.replace(/\s/g, "")) {
								node.nodeValue = translation[key];
								return;
							}
						}
					}
					else
						element.attr(key, translation[key]);
				}
			}
		}
	});

	module.directive("contextMenuTrigger", function () {
		return {
			restrict: "A",
			link: function compile(scope, element, attrs) {
				element.bind("contextmenu", function (e) {
					var clickedElements = $(e.target).closest(".item").find(".dropdown-toggle").trigger("click").length;
					if (clickedElements)
						e.preventDefault();
				});
			}
		}
	});

	var key = { enter: 13, esc: 27, left: 37, up: 38, right: 39, down: 40, del: 46, a: 65, c: 67, n: 78, v: 86, x: 88, z: 90 };

	angular.forEach(["Enter", "Esc", "Down", "Up"], function (k) {
		var name = "n2Key" + k;
		module.directive(name, function () {
			return {
				restrict: "A",
				link: function compile(scope, element, attrs) {
					var code = key[k.toLowerCase()];
					element.bind("keyup", function(e) {
						if (e.keyCode == code) {
							e.preventDefault();
							e.stopPropagation();
							scope.$apply(attrs[name]);
						}
					});
				}
			};
		});
	});

	module.directive("n2Focus", function () {
		return {
			restrict: "A",
			link: function compile(scope, element, attrs) {
				scope.$watch(function() {
					return scope.$eval(attrs.n2Focus);
				}, function(focus) {
					if (focus) element.focus();
				});
			}
		};
	});

	module.directive("evaluateHref", function ($interpolate) {
		return {
			restrict: "A",
			link: function compile(scope, element, attrs) {
				scope.$watch(attrs.evaluateHref, function(expr) {
					element.attr("href", expr && $interpolate(expr)(scope));
				});
			}
		};
	});

	module.directive("evaluateTitle", function ($interpolate) {
		return {
			restrict: "A",
			link: function compile(scope, element, attrs) {
				scope.$watch(attrs.evaluateTitle, function(expr) {
					element.attr("title", expr && $interpolate(expr)(scope));
				});
			}
		};
	});

	module.directive("evaluateInnerHtml", function ($interpolate) {
		return {
			restrict: "A",
			link: function compile(scope, element, attrs) {
				scope.$watch(attrs.evaluateInnerHtml, function(expr) {
					element.html(expr && $interpolate(expr)(scope));
				});
			}
		};
	});

	module.directive("pageActionIconClass", function () {
		return {
			restrict: "A",
			link: function (scope, element, attrs) {
				if (attrs.pageActionIconClass)
					scope.$watch(function () {
						var i = scope.$eval(attrs.pageActionIconClass);
						return i && i.Current && (i.Current.IconClass || i.Current.IconUrl)
					}, function (icon) {
						element.addClass(icon ? "page-action-iconified" : "page-action-plain");
					});
				else
					element.addClass("page-action-iconified");
			}
		}
	});

	module.directive("pageActionLink", function ($interpolate) {
		return {
			restrict: "A",
			scope: true,
			link: function compile(scope, element, attrs) {

				function watch(expr, scope, applicator) {
					var factory = expr && $interpolate(expr);
					if (factory) {
						return scope.$watch(function() {
							return factory(scope);
						}, applicator);
					} else
						applicator(null);
				}

				scope.evaluateExpression = function(expr) {
					return expr && $interpolate(expr)(scope);
				};
				scope.evalExpression = function(expr) {
					expr && scope.$eval(expr);
				};

				var unwatchHref, unwatchTitle, unwatchInnerHtml, unwatchCurrent;
				scope.$watch(attrs.pageActionLink, function(node) {
					scope.node = node;

					unwatchCurrent && unwatchCurrent();
					unwatchCurrent = scope.$watch(function() { return node.Current }, function(current) {
						if (!current || current.Divider) {
							element.hide();
							return;
						} else
							element.show();

						if (!current.Target)
							current.Target = "preview";
						if (!current.Url && current.PreviewUrl)
							current.Url = current.PreviewUrl;

						unwatchHref && unwatchHref();
						unwatchHref = watch(current.Url, scope, function(value) { element.attr("href", value || "#"); });

						unwatchTitle && unwatchTitle();
						unwatchTitle = watch(current.ToolTip, scope, function(value) { element.attr("title", value); });

						unwatchInnerHtml && unwatchInnerHtml();

						unwatchInnerHtml = watch(
							(current.IconClass ? (" ") : current.IconUrl ? (" ") : "")
								+ "{{evaluateExpression(node.Current.Title)}}"
								+ (current.Description ? "
{{evaluateExpression(node.Current.Description)}}" : ""), scope, function(value) { element.html(value); });

						element.attr("target", current.Target);

						element.attr("class", (current.Description ? "page-action page-action-description" : "page-action") + (current.IconClass || current.IconUrl ? " page-action-iconified" : " page-action-plain"));

						element.click(function(e) {
							if (current.ClientAction) {
								e.preventDefault();
								scope.$apply(node.Current.ClientAction);
							}
							scope.$emit("nodeclicked", node);
						});
					});
				});
			}
		};
	});

	module.directive("backgroundImage", function () {
		return {
			restrict: "A",
			link: function compile(scope, element, attrs) {
				scope.$watch(attrs.backgroundImage, function (backgroundImage) {
					if (backgroundImage) {
						var style = element.attr("style");
						if (style)
							style += ";";
						else
							style = "";
						style += "background-image:url(" + backgroundImage + ")";
						element.attr("style", style);
					}
				});
			}
		}
	});

	module.directive("load", function($parse){
		return function (scope, element, attr) {
			var fn = $parse(attr.load);
			element.bind("load", function (e) {
				scope.$apply(function () {
					fn(scope, { $event: e });
				});
			});
		};
	});

	module.directive("keyup", function ($parse) {
		return function (scope, element, attr) {
			var fn = $parse(attr.keyup);
			element.bind("keyup", function (e) {
				scope.$apply(function () {
					fn(scope, { $event: e });
				});
			});
		};
	});

	module.directive("esc", function ($parse) {
		return function (scope, element, attr) {
			var fn = $parse(attr.keyup);
			element.bind("keyup", function (e) {
				if (e.keyCode == 27) {
					scope.$apply(function () {
						fn(scope, { $event: e });
					});
				}
			});
		};
	});

	module.directive("sortable", function () {

		var ctx = {
		};

		return {
			restrict: "A",
			link: function compile(scope, element, attrs) {
				var sort = {
					start: function (e, args) {
						var $from = $(args.item[0]).parent().closest("li");
						ctx = {
							operation: "sort",
							indexes: {
								from: args.item.index()
							},
							scopes: {
								from: $from.length && angular.element($from).scope()
							},
							paths: {
								from: $from.attr("sortable-path") || null
							}
						};
					},
					remove: function (e, args) {
					},
					update: function (e, args) {
					},
					receive: function (e, args) {
						ctx.operation = "move";
					},
					stop: function (e, args) {
						var $selected = $(args.item[0]);
						var $to = $selected.parent().closest("li");

						ctx.paths.selected = $selected.attr("sortable-path") || null;
						ctx.paths.to = $to.attr("sortable-path") || null;
						ctx.paths.before = $selected.next().attr("sortable-path") || null;

						ctx.scopes.selected = $selected.length && angular.element($selected).scope();
						ctx.scopes.to = $to.length && angular.element($to).scope();

						ctx.indexes.to = args.item.index();

						ctx.scopes.from.node.Children.splice(ctx.indexes.from, 1);

						ctx.callback = function () {
							$selected.remove();
						}

						var options = scope.$eval(attrs.sortable)

						if (ctx.operation == "move") {
							options.move && options.move(ctx);
						} else {
							options.sort && options.sort(ctx);
						}
						ctx.scopes.from.$digest();
						ctx.scopes.to.$digest();
						ctx = {};
					}
				};

				setTimeout(function () {
					element.sortable({
						connectWith: '.targettable',
						placeholder: 'sortable-placeholder',
						handle: '.handle',
						receive: sort.receive,
						remove: sort.remove,
						update: sort.update,
						start: sort.start,
						stop: sort.stop
					});
				}, 100);
			}
		}
	});

	module.directive('compile', function ($compile) {
		return function (scope, element, attrs) {
			scope.$watch(
				function (scope) {
					// watch the 'compile' expression for changes
					return scope.$eval(attrs.compile);
				},
				function (value) {
					if (value === null)
						return;

					// when the 'compile' expression changes assign it into the current DOM
					element.html(value);

					// compile the new DOM and link it to the current scope.
					// NOTE: we only compile .childNodes so that we don't get into infinite loop compiling ourselves
					$compile(element.contents())(scope);
				}
			);
		};
	});

	angular.forEach(['X', 'Y'], function (dir) {
		module.directive('n2Resize' + dir, function ($parse) {
			return function (scope, element, attrs) {
				var modelGet = $parse(attrs["n2Resize" + dir]);
				var modelSet = modelGet.assign;

				var initialClientValue, initialModelValue;

				element.bind("mousedown", function (e) {
					initialClientValue = e["client" + dir];
					initialModelValue = modelGet(scope);

					$(document).bind("mousemove.n2Resize", function (e) {
						modelSet(scope, initialModelValue + e["client" + dir] - initialClientValue);
						scope.$digest();
					});
					$(document.body).addClass("resizing");
				});
				element.bind("mouseup", function (e) {
					scope.$emit("resized", { direction: dir, from: initialModelValue, to: modelGet(scope)});
					$(document).unbind("mousemove.n2Resize");
					$(document.body).removeClass("resizing");
					initialClientValue = undefined;
					initialModelValue = undefined;
				});
			};
		});
	});

	module.filter('pretty', function () {
		function syntaxHighlight(json) {
			if (typeof json != 'string') {
				json = angular.toJson(json, true);
			}
			json = json.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>');
			return json.replace(/("(\\u[a-zA-Z0-9]{4}|\\[^u]|[^\\"])*"(\s*:)?|\b(true|false|null)\b|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?)/g, function (match) {
				var cls = 'number';
				if (/^"/.test(match)) {
					if (/:$/.test(match)) {
						cls = 'key';
					} else {
						cls = 'string';
					}
				} else if (/true|false/.test(match)) {
					cls = 'boolean';
				} else if (/null/.test(match)) {
					cls = 'null';
				}
				return '' + match + '';
			});
		}
		return function (obj) {
			return "
" + syntaxHighlight(obj) + "
";
		};
	});

	module.directive('n2DelayClass', function ($timeout, $compile) {
	    return {
	        link: function (scope, element, attrs) {
	            scope.$watch(attrs.n2DelayClass, function (options) {
	                if (!options)
	                    return;

	                $timeout(function () {
	                    for (var k in options) {
	                        if (options[k]) {
	                            element.addClass(k);
	                        } else {
	                            element.removeClass(k);
	                        }
	                    }
	                }, 10);
	            }, true);
	        }
	    };
	});

})(angular.module('n2.directives', ['n2.localization']));<commit_msg>Added some missing semicolons<commit_after>(function (module) {

	module.filter("translate", function (Translate) {
		return function (fallback, key) {
			if (!key) console.error("No key provided for translation text ", fallback);

			return Translate(key, fallback);
		}
	});

	module.directive("translate", function (Translate) {
		return {
			restrict: "A",
			link: function compile(scope, element, attrs) {
				if (!attrs.translate) console.error("No key provided for translation element", element[0]);

				var translation = Translate(attrs.translate);
				if (typeof translation == "string") {
					element.html(translation);
					return;
				}
				for (var key in translation) {
					if (key == "html")
						element.html(translation[key]);
					else if (key == "text") {
						for (var i in element[0].childNodes) {
							var node = element[0].childNodes[i]
							if (node.nodeType == Node.TEXT_NODE && node.nodeValue && node.nodeValue.replace(/\s/g, "")) {
								node.nodeValue = translation[key];
								return;
							}
						}
					}
					else
						element.attr(key, translation[key]);
				}
			}
		}
	});

	module.directive("contextMenuTrigger", function () {
		return {
			restrict: "A",
			link: function compile(scope, element, attrs) {
				element.bind("contextmenu", function (e) {
					var clickedElements = $(e.target).closest(".item").find(".dropdown-toggle").trigger("click").length;
					if (clickedElements)
						e.preventDefault();
				});
			}
		}
	});

	var key = { enter: 13, esc: 27, left: 37, up: 38, right: 39, down: 40, del: 46, a: 65, c: 67, n: 78, v: 86, x: 88, z: 90 };

	angular.forEach(["Enter", "Esc", "Down", "Up"], function (k) {
		var name = "n2Key" + k;
		module.directive(name, function () {
			return {
				restrict: "A",
				link: function compile(scope, element, attrs) {
					var code = key[k.toLowerCase()];
					element.bind("keyup", function(e) {
						if (e.keyCode == code) {
							e.preventDefault();
							e.stopPropagation();
							scope.$apply(attrs[name]);
						}
					});
				}
			};
		});
	});

	module.directive("n2Focus", function () {
		return {
			restrict: "A",
			link: function compile(scope, element, attrs) {
				scope.$watch(function() {
					return scope.$eval(attrs.n2Focus);
				}, function(focus) {
					if (focus) element.focus();
				});
			}
		};
	});

	module.directive("evaluateHref", function ($interpolate) {
		return {
			restrict: "A",
			link: function compile(scope, element, attrs) {
				scope.$watch(attrs.evaluateHref, function(expr) {
					element.attr("href", expr && $interpolate(expr)(scope));
				});
			}
		};
	});

	module.directive("evaluateTitle", function ($interpolate) {
		return {
			restrict: "A",
			link: function compile(scope, element, attrs) {
				scope.$watch(attrs.evaluateTitle, function(expr) {
					element.attr("title", expr && $interpolate(expr)(scope));
				});
			}
		};
	});

	module.directive("evaluateInnerHtml", function ($interpolate) {
		return {
			restrict: "A",
			link: function compile(scope, element, attrs) {
				scope.$watch(attrs.evaluateInnerHtml, function(expr) {
					element.html(expr && $interpolate(expr)(scope));
				});
			}
		};
	});

	module.directive("pageActionIconClass", function () {
		return {
			restrict: "A",
			link: function (scope, element, attrs) {
				if (attrs.pageActionIconClass)
					scope.$watch(function () {
						var i = scope.$eval(attrs.pageActionIconClass);
						return i && i.Current && (i.Current.IconClass || i.Current.IconUrl)
					}, function (icon) {
						element.addClass(icon ? "page-action-iconified" : "page-action-plain");
					});
				else
					element.addClass("page-action-iconified");
			}
		}
	});

	module.directive("pageActionLink", function ($interpolate) {
		return {
			restrict: "A",
			scope: true,
			link: function compile(scope, element, attrs) {

				function watch(expr, scope, applicator) {
					var factory = expr && $interpolate(expr);
					if (factory) {
						return scope.$watch(function() {
							return factory(scope);
						}, applicator);
					} else
						return applicator(null);
				}

				scope.evaluateExpression = function(expr) {
					return expr && $interpolate(expr)(scope);
				};
				scope.evalExpression = function(expr) {
					expr && scope.$eval(expr);
				};

				var unwatchHref, unwatchTitle, unwatchInnerHtml, unwatchCurrent;
				scope.$watch(attrs.pageActionLink, function(node) {
					scope.node = node;

					unwatchCurrent && unwatchCurrent();
					unwatchCurrent = scope.$watch(function() { return node.Current }, function(current) {
						if (!current || current.Divider) {
							element.hide();
							return;
						} else
							element.show();

						if (!current.Target)
							current.Target = "preview";
						if (!current.Url && current.PreviewUrl)
							current.Url = current.PreviewUrl;

						unwatchHref && unwatchHref();
						unwatchHref = watch(current.Url, scope, function(value) { element.attr("href", value || "#"); });

						unwatchTitle && unwatchTitle();
						unwatchTitle = watch(current.ToolTip, scope, function(value) { element.attr("title", value); });

						unwatchInnerHtml && unwatchInnerHtml();

						unwatchInnerHtml = watch(
							(current.IconClass ? (" ") : current.IconUrl ? (" ") : "")
								+ "{{evaluateExpression(node.Current.Title)}}"
								+ (current.Description ? "
{{evaluateExpression(node.Current.Description)}}" : ""), scope, function(value) { element.html(value); });

						element.attr("target", current.Target);

						element.attr("class", (current.Description ? "page-action page-action-description" : "page-action") + (current.IconClass || current.IconUrl ? " page-action-iconified" : " page-action-plain"));

						element.click(function(e) {
							if (current.ClientAction) {
								e.preventDefault();
								scope.$apply(node.Current.ClientAction);
							}
							scope.$emit("nodeclicked", node);
						});
					});
				});
			}
		};
	});

	module.directive("backgroundImage", function () {
		return {
			restrict: "A",
			link: function compile(scope, element, attrs) {
				scope.$watch(attrs.backgroundImage, function (backgroundImage) {
					if (backgroundImage) {
						var style = element.attr("style");
						if (style)
							style += ";";
						else
							style = "";
						style += "background-image:url(" + backgroundImage + ")";
						element.attr("style", style);
					}
				});
			}
		}
	});

	module.directive("load", function($parse){
		return function (scope, element, attr) {
			var fn = $parse(attr.load);
			element.bind("load", function (e) {
				scope.$apply(function () {
					fn(scope, { $event: e });
				});
			});
		};
	});

	module.directive("keyup", function ($parse) {
		return function (scope, element, attr) {
			var fn = $parse(attr.keyup);
			element.bind("keyup", function (e) {
				scope.$apply(function () {
					fn(scope, { $event: e });
				});
			});
		};
	});

	module.directive("esc", function ($parse) {
		return function (scope, element, attr) {
			var fn = $parse(attr.keyup);
			element.bind("keyup", function (e) {
				if (e.keyCode == 27) {
					scope.$apply(function () {
						fn(scope, { $event: e });
					});
				}
			});
		};
	});

	module.directive("sortable", function () {

		var ctx = {
		};

		return {
			restrict: "A",
			link: function compile(scope, element, attrs) {
				var sort = {
					start: function (e, args) {
						var $from = $(args.item[0]).parent().closest("li");
						ctx = {
							operation: "sort",
							indexes: {
								from: args.item.index()
							},
							scopes: {
								from: $from.length && angular.element($from).scope()
							},
							paths: {
								from: $from.attr("sortable-path") || null
							}
						};
					},
					remove: function (e, args) {
					},
					update: function (e, args) {
					},
					receive: function (e, args) {
						ctx.operation = "move";
					},
					stop: function (e, args) {
						var $selected = $(args.item[0]);
						var $to = $selected.parent().closest("li");

						ctx.paths.selected = $selected.attr("sortable-path") || null;
						ctx.paths.to = $to.attr("sortable-path") || null;
						ctx.paths.before = $selected.next().attr("sortable-path") || null;

						ctx.scopes.selected = $selected.length && angular.element($selected).scope();
						ctx.scopes.to = $to.length && angular.element($to).scope();

						ctx.indexes.to = args.item.index();

						ctx.scopes.from.node.Children.splice(ctx.indexes.from, 1);

						ctx.callback = function () {
							$selected.remove();
						}

						var options = scope.$eval(attrs.sortable);

						if (ctx.operation == "move") {
							options.move && options.move(ctx);
						} else {
							options.sort && options.sort(ctx);
						}
						ctx.scopes.from.$digest();
						ctx.scopes.to.$digest();
						ctx = {};
					}
				};

				setTimeout(function () {
					element.sortable({
						connectWith: '.targettable',
						placeholder: 'sortable-placeholder',
						handle: '.handle',
						receive: sort.receive,
						remove: sort.remove,
						update: sort.update,
						start: sort.start,
						stop: sort.stop
					});
				}, 100);
			}
		}
	});

	module.directive('compile', function ($compile) {
		return function (scope, element, attrs) {
			scope.$watch(
				function (scope) {
					// watch the 'compile' expression for changes
					return scope.$eval(attrs.compile);
				},
				function (value) {
					if (value === null)
						return;

					// when the 'compile' expression changes assign it into the current DOM
					element.html(value);

					// compile the new DOM and link it to the current scope.
					// NOTE: we only compile .childNodes so that we don't get into infinite loop compiling ourselves
					$compile(element.contents())(scope);
				}
			);
		};
	});

	angular.forEach(['X', 'Y'], function (dir) {
		module.directive('n2Resize' + dir, function ($parse) {
			return function (scope, element, attrs) {
				var modelGet = $parse(attrs["n2Resize" + dir]);
				var modelSet = modelGet.assign;

				var initialClientValue, initialModelValue;

				element.bind("mousedown", function (e) {
					initialClientValue = e["client" + dir];
					initialModelValue = modelGet(scope);

					$(document).bind("mousemove.n2Resize", function (evt) {
						modelSet(scope, initialModelValue + evt["client" + dir] - initialClientValue);
						scope.$digest();
					});
					$(document.body).addClass("resizing");
				});
				element.bind("mouseup", function (e) {
					scope.$emit("resized", { direction: dir, from: initialModelValue, to: modelGet(scope)});
					$(document).unbind("mousemove.n2Resize");
					$(document.body).removeClass("resizing");
					initialClientValue = undefined;
					initialModelValue = undefined;
				});
			};
		});
	});

	module.filter('pretty', function () {
		function syntaxHighlight(json) {
			if (typeof json != 'string') {
				json = angular.toJson(json, true);
			}
			json = json.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>');
			return json.replace(/("(\\u[a-zA-Z0-9]{4}|\\[^u]|[^\\"])*"(\s*:)?|\b(true|false|null)\b|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?)/g, function (match) {
				var cls = 'number';
				if (/^"/.test(match)) {
					if (/:$/.test(match)) {
						cls = 'key';
					} else {
						cls = 'string';
					}
				} else if (/true|false/.test(match)) {
					cls = 'boolean';
				} else if (/null/.test(match)) {
					cls = 'null';
				}
				return '' + match + '';
			});
		}
		return function (obj) {
			return "
" + syntaxHighlight(obj) + "
";
		};
	});

	module.directive('n2DelayClass', function ($timeout, $compile) {
	    return {
	        link: function (scope, element, attrs) {
	            scope.$watch(attrs.n2DelayClass, function (options) {
	                if (!options)
	                    return;

	                $timeout(function () {
	                    for (var k in options) {
	                        if (options[k]) {
	                            element.addClass(k);
	                        } else {
	                            element.removeClass(k);
	                        }
	                    }
	                }, 10);
	            }, true);
	        }
	    };
	});

})(angular.module('n2.directives', ['n2.localization']));
<|endoftext|>