<commit_before>import {ascending, min, sum} from "d3-array";
import {map, nest} from "d3-collection";
import {justify} from "./align.js";
import constant from "./constant.js";

function ascendingSourceBreadth(a, b) {
  return ascendingBreadth(a.source, b.source) || a.index - b.index;
}

function ascendingTargetBreadth(a, b) {
  return ascendingBreadth(a.target, b.target) || a.index - b.index;
}

function ascendingBreadth(a, b) {
  return a.y0 - b.y0;
}

function value(d) {
  return d.value;
}

function defaultId(d) {
  return d.index;
}

function defaultNodes(graph) {
  return graph.nodes;
}

function defaultLinks(graph) {
  return graph.links;
}

function find(nodeById, id) {
  var node = nodeById.get(id);
  if (!node) throw new Error("missing: " + id);
  return node;
}

export default function Sankey() {
  var x0 = 0, y0 = 0, x1 = 1, y1 = 1, // extent
      dx = 24, // nodeWidth
      py = 8, // nodePadding
      id = defaultId,
      align = justify,
      sort,
      nodes = defaultNodes,
      links = defaultLinks,
      iterations = 6;

  function sankey() {
    var graph = {nodes: nodes.apply(null, arguments), links: links.apply(null, arguments)};
    computeNodeLinks(graph);
    computeNodeValues(graph);
    computeNodeDepths(graph);
    computeNodeBreadths(graph);
    computeLinkBreadths(graph);
    return graph;
  }

  sankey.update = function(graph) {
    computeLinkBreadths(graph);
    return graph;
  };

  sankey.nodeId = function(_) {
    return arguments.length ? (id = typeof _ === "function" ? _ : constant(_), sankey) : id;
  };

  sankey.nodeAlign = function(_) {
    return arguments.length ? (align = typeof _ === "function" ? _ : constant(_), sankey) : align;
  };

  sankey.nodeSort = function(_) {
    return arguments.length ? (sort = _, sankey) : sort;
  };

  sankey.nodeWidth = function(_) {
    return arguments.length ? (dx = +_, sankey) : dx;
  };

  sankey.nodePadding = function(_) {
    return arguments.length ? (py = +_, sankey) : py;
  };

  sankey.nodes = function(_) {
    return arguments.length ? (nodes = typeof _ === "function" ? _ : constant(_), sankey) : nodes;
  };

  sankey.links = function(_) {
    return arguments.length ? (links = typeof _ === "function" ? _ : constant(_), sankey) : links;
  };

  sankey.size = function(_) {
    return arguments.length ? (x0 = y0 = 0, x1 = +_[0], y1 = +_[1], sankey) : [x1 - x0, y1 - y0];
  };

  sankey.extent = function(_) {
    return arguments.length ? (x0 = +_[0][0], x1 = +_[1][0], y0 = +_[0][1], y1 = +_[1][1], sankey) : [[x0, y0], [x1, y1]];
  };

  sankey.iterations = function(_) {
    return arguments.length ? (iterations = +_, sankey) : iterations;
  };

  // Populate the sourceLinks and targetLinks for each node.
  // Also, if the source and target are not objects, assume they are indices.
  function computeNodeLinks(graph) {
    graph.nodes.forEach(function(node, i) {
      node.index = i;
      node.sourceLinks = [];
      node.targetLinks = [];
    });
    var nodeById = map(graph.nodes, id);
    graph.links.forEach(function(link, i) {
      link.index = i;
      var source = link.source, target = link.target;
      if (typeof source !== "object") source = link.source = find(nodeById, source);
      if (typeof target !== "object") target = link.target = find(nodeById, target);
      source.sourceLinks.push(link);
      target.targetLinks.push(link);
    });
  }

  // Compute the value (size) of each node by summing the associated links.
  function computeNodeValues(graph) {
    graph.nodes.forEach(function(node) {
      node.value = Math.max(
        sum(node.sourceLinks, value),
        sum(node.targetLinks, value)
      );
    });
  }

  // Iteratively assign the depth (x-position) for each node.
  // Nodes are assigned the maximum depth of incoming neighbors plus one;
  // nodes with no incoming links are assigned depth zero, while
  // nodes with no outgoing links are assigned the maximum depth.
  function computeNodeDepths(graph) {
    var nodes, next, x, n = graph.nodes.length;

    for (nodes = graph.nodes, next = [], x = 0; nodes.length; ++x, nodes = next, next = []) {
      if (x > n) throw new Error("circular link");
      nodes.forEach(function(node) {
        node.depth = x;
        node.sourceLinks.forEach(function(link) {
          if (next.indexOf(link.target) < 0) {
            next.push(link.target);
          }
        });
      });
    }

    for (nodes = graph.nodes, next = [], x = 0; nodes.length; ++x, nodes = next, next = []) {
      if (x > n) throw new Error("circular link");
      nodes.forEach(function(node) {
        node.height = x;
        node.targetLinks.forEach(function(link) {
          if (next.indexOf(link.source) < 0) {
            next.push(link.source);
          }
        });
      });
    }

    var kx = (x1 - x0 - dx) / (x - 1);
    graph.nodes.forEach(function(node) {
      node.layer = Math.max(0, Math.min(x - 1, Math.floor(align.call(null, node, x))));
      node.x1 = (node.x0 = x0 + node.layer * kx) + dx;
    });
  }

  function computeNodeBreadths(graph) {
    var columns = nest()
        .key(function(d) { return d.x0; })
        .sortKeys(ascending)
        .entries(graph.nodes)
        .map(function(d) { return d.values; });

    //
    initializeNodeBreadth();
    resolveCollisions();
    for (var alpha = 0.95, n = iterations; n > 0; --n, alpha *= 0.95) {
      relaxRightToLeft(alpha);
      resolveCollisions();
      relaxLeftToRight(alpha);
      resolveCollisions();
    }

    function initializeNodeBreadth() {
      var ky = min(columns, function(nodes) {
        return (y1 - y0 - (nodes.length - 1) * py) / sum(nodes, value);
      });

      columns.forEach(function(nodes) {
        if (sort != null) nodes.sort(sort);
        nodes.forEach(function(node, i) {
          node.y1 = (node.y0 = i) + node.value * ky;
        });
      });

      graph.links.forEach(function(link) {
        link.width = link.value * ky;
      });
    }

    function relaxLeftToRight(alpha) {
      columns.forEach(function(nodes) {
        nodes.forEach(function(node) {
          let y = node.y0;
          for (const {target, width, value} of node.sourceLinks.sort(ascendingTargetBreadth)) {
            if (value > 0) {
              let dy = target.y0;
              for (const {source, width} of target.targetLinks) {
                if (source === node) break;
                dy += width;
              }
              dy = (y - dy) * Math.pow(alpha, target.layer - node.layer - 0.5) * value / target.value;
              target.y0 += dy;
              target.y1 += dy;
            }
            y += width;
          }
        });
      });
    }

    function relaxRightToLeft(alpha) {
      columns.slice().reverse().forEach(function(nodes) {
        nodes.forEach(function(node) {
          let y = node.y0;
          for (const {source, width, value} of node.targetLinks.sort(ascendingSourceBreadth)) {
            if (value > 0) {
              let dy = source.y0;
              for (const {target, width} of source.sourceLinks) {
                if (target === node) break;
                dy += width;
              }
              dy = (y - dy) * Math.pow(alpha, node.layer - source.layer - 0.5) * value / source.value;
              source.y0 += dy;
              source.y1 += dy;
            }
            y += width;
          }
        });
      });
    }

    function resolveCollisions() {
      columns.forEach(function(nodes) {
        var node,
            dy,
            y = y0,
            n = nodes.length,
            i;

        // Push any overlapping nodes down.
        if (sort === undefined) nodes.sort(ascendingBreadth);
        for (i = 0; i < n; ++i) {
          node = nodes[i];
          dy = y - node.y0;
          if (dy > 0) node.y0 += dy, node.y1 += dy;
          y = node.y1 + py;
        }

        // If the bottommost node goes outside the bounds, push it back up.
        dy = y - py - y1;
        if (dy > 0) {
          y = (node.y0 -= dy), node.y1 -= dy;

          // Push any overlapping nodes back up.
          for (i = n - 2; i >= 0; --i) {
            node = nodes[i];
            dy = node.y1 + py - y;
            if (dy > 0) node.y0 -= dy, node.y1 -= dy;
            y = node.y0;
          }
        }
      });
    }
  }

  function computeLinkBreadths(graph) {
    graph.nodes.forEach(function(node) {
      node.sourceLinks.sort(ascendingTargetBreadth);
      node.targetLinks.sort(ascendingSourceBreadth);
    });
    graph.nodes.forEach(function(node) {
      var y0 = node.y0, y1 = y0;
      node.sourceLinks.forEach(function(link) {
        link.y0 = y0 + link.width / 2, y0 += link.width;
      });
      node.targetLinks.forEach(function(link) {
        link.y1 = y1 + link.width / 2, y1 += link.width;
      });
    });
  }

  return sankey;
}
<commit_msg>Symmetric collision resolution.<commit_after>import {ascending, min, sum} from "d3-array";
import {map, nest} from "d3-collection";
import {justify} from "./align.js";
import constant from "./constant.js";

function ascendingSourceBreadth(a, b) {
  return ascendingBreadth(a.source, b.source) || a.index - b.index;
}

function ascendingTargetBreadth(a, b) {
  return ascendingBreadth(a.target, b.target) || a.index - b.index;
}

function ascendingBreadth(a, b) {
  return a.y0 - b.y0;
}

function value(d) {
  return d.value;
}

function defaultId(d) {
  return d.index;
}

function defaultNodes(graph) {
  return graph.nodes;
}

function defaultLinks(graph) {
  return graph.links;
}

function find(nodeById, id) {
  var node = nodeById.get(id);
  if (!node) throw new Error("missing: " + id);
  return node;
}

export default function Sankey() {
  var x0 = 0, y0 = 0, x1 = 1, y1 = 1, // extent
      dx = 24, // nodeWidth
      py = 8, // nodePadding
      id = defaultId,
      align = justify,
      sort,
      nodes = defaultNodes,
      links = defaultLinks,
      iterations = 6;

  function sankey() {
    var graph = {nodes: nodes.apply(null, arguments), links: links.apply(null, arguments)};
    computeNodeLinks(graph);
    computeNodeValues(graph);
    computeNodeDepths(graph);
    computeNodeBreadths(graph);
    computeLinkBreadths(graph);
    return graph;
  }

  sankey.update = function(graph) {
    computeLinkBreadths(graph);
    return graph;
  };

  sankey.nodeId = function(_) {
    return arguments.length ? (id = typeof _ === "function" ? _ : constant(_), sankey) : id;
  };

  sankey.nodeAlign = function(_) {
    return arguments.length ? (align = typeof _ === "function" ? _ : constant(_), sankey) : align;
  };

  sankey.nodeSort = function(_) {
    return arguments.length ? (sort = _, sankey) : sort;
  };

  sankey.nodeWidth = function(_) {
    return arguments.length ? (dx = +_, sankey) : dx;
  };

  sankey.nodePadding = function(_) {
    return arguments.length ? (py = +_, sankey) : py;
  };

  sankey.nodes = function(_) {
    return arguments.length ? (nodes = typeof _ === "function" ? _ : constant(_), sankey) : nodes;
  };

  sankey.links = function(_) {
    return arguments.length ? (links = typeof _ === "function" ? _ : constant(_), sankey) : links;
  };

  sankey.size = function(_) {
    return arguments.length ? (x0 = y0 = 0, x1 = +_[0], y1 = +_[1], sankey) : [x1 - x0, y1 - y0];
  };

  sankey.extent = function(_) {
    return arguments.length ? (x0 = +_[0][0], x1 = +_[1][0], y0 = +_[0][1], y1 = +_[1][1], sankey) : [[x0, y0], [x1, y1]];
  };

  sankey.iterations = function(_) {
    return arguments.length ? (iterations = +_, sankey) : iterations;
  };

  // Populate the sourceLinks and targetLinks for each node.
  // Also, if the source and target are not objects, assume they are indices.
  function computeNodeLinks(graph) {
    graph.nodes.forEach(function(node, i) {
      node.index = i;
      node.sourceLinks = [];
      node.targetLinks = [];
    });
    var nodeById = map(graph.nodes, id);
    graph.links.forEach(function(link, i) {
      link.index = i;
      var source = link.source, target = link.target;
      if (typeof source !== "object") source = link.source = find(nodeById, source);
      if (typeof target !== "object") target = link.target = find(nodeById, target);
      source.sourceLinks.push(link);
      target.targetLinks.push(link);
    });
  }

  // Compute the value (size) of each node by summing the associated links.
  function computeNodeValues(graph) {
    graph.nodes.forEach(function(node) {
      node.value = Math.max(
        sum(node.sourceLinks, value),
        sum(node.targetLinks, value)
      );
    });
  }

  // Iteratively assign the depth (x-position) for each node.
  // Nodes are assigned the maximum depth of incoming neighbors plus one;
  // nodes with no incoming links are assigned depth zero, while
  // nodes with no outgoing links are assigned the maximum depth.
  function computeNodeDepths(graph) {
    var nodes, next, x, n = graph.nodes.length;

    for (nodes = graph.nodes, next = [], x = 0; nodes.length; ++x, nodes = next, next = []) {
      if (x > n) throw new Error("circular link");
      nodes.forEach(function(node) {
        node.depth = x;
        node.sourceLinks.forEach(function(link) {
          if (next.indexOf(link.target) < 0) {
            next.push(link.target);
          }
        });
      });
    }

    for (nodes = graph.nodes, next = [], x = 0; nodes.length; ++x, nodes = next, next = []) {
      if (x > n) throw new Error("circular link");
      nodes.forEach(function(node) {
        node.height = x;
        node.targetLinks.forEach(function(link) {
          if (next.indexOf(link.source) < 0) {
            next.push(link.source);
          }
        });
      });
    }

    var kx = (x1 - x0 - dx) / (x - 1);
    graph.nodes.forEach(function(node) {
      node.layer = Math.max(0, Math.min(x - 1, Math.floor(align.call(null, node, x))));
      node.x1 = (node.x0 = x0 + node.layer * kx) + dx;
    });
  }

  function computeNodeBreadths(graph) {
    var columns = nest()
        .key(function(d) { return d.x0; })
        .sortKeys(ascending)
        .entries(graph.nodes)
        .map(function(d) { return d.values; });

    //
    initializeNodeBreadth();
    resolveCollisionsTopToBottom(1);
    for (var alpha = 0.95, n = iterations; n > 0; --n, alpha *= 0.95) {
      relaxRightToLeft(alpha);
      resolveCollisionsTopToBottom(1);
      relaxLeftToRight(alpha);
      resolveCollisionsBottomToTop(1);
    }

    function initializeNodeBreadth() {
      var ky = min(columns, function(nodes) {
        return (y1 - y0 - (nodes.length - 1) * py) / sum(nodes, value);
      });

      columns.forEach(function(nodes) {
        if (sort != null) nodes.sort(sort);
        nodes.forEach(function(node, i) {
          node.y1 = (node.y0 = i) + node.value * ky;
        });
      });

      graph.links.forEach(function(link) {
        link.width = link.value * ky;
      });
    }

    function relaxLeftToRight(alpha) {
      columns.forEach(function(nodes) {
        nodes.forEach(function(node) {
          let y = node.y0;
          for (const {target, width, value} of node.sourceLinks.sort(ascendingTargetBreadth)) {
            if (value > 0) {
              let dy = target.y0;
              for (const {source, width} of target.targetLinks) {
                if (source === node) break;
                dy += width;
              }
              dy = (y - dy) * Math.pow(alpha, target.layer - node.layer - 0.5) * value / target.value;
              target.y0 += dy;
              target.y1 += dy;
            }
            y += width;
          }
        });
      });
    }

    function relaxRightToLeft(alpha) {
      columns.slice().reverse().forEach(function(nodes) {
        nodes.forEach(function(node) {
          let y = node.y0;
          for (const {source, width, value} of node.targetLinks.sort(ascendingSourceBreadth)) {
            if (value > 0) {
              let dy = source.y0;
              for (const {target, width} of source.sourceLinks) {
                if (target === node) break;
                dy += width;
              }
              dy = (y - dy) * Math.pow(alpha, node.layer - source.layer - 0.5) * value / source.value;
              source.y0 += dy;
              source.y1 += dy;
            }
            y += width;
          }
        });
      });
    }

    function resolveCollisionsTopToBottom(alpha) {
      columns.forEach(function(nodes) {
        var node,
            dy,
            y = y0,
            n = nodes.length,
            i;

        // Push any overlapping nodes down.
        if (sort === undefined) nodes.sort(ascendingBreadth);
        for (i = 0; i < n; ++i) {
          node = nodes[i];
          dy = (y - node.y0) * alpha;
          if (dy > 0) node.y0 += dy, node.y1 += dy;
          y = node.y1 + py;
        }

        // If the bottommost node goes outside the bounds, push it back up.
        dy = (y - py - y1) * alpha;
        if (dy > 0) {
          y = (node.y0 -= dy), node.y1 -= dy;

          // Push any overlapping nodes back up.
          for (i = n - 2; i >= 0; --i) {
            node = nodes[i];
            dy = (node.y1 + py - y) * alpha;
            if (dy > 0) node.y0 -= dy, node.y1 -= dy;
            y = node.y0;
          }
        }
      });
    }

    function resolveCollisionsBottomToTop(alpha) {
      columns.forEach(function(nodes) {
        var node,
            dy,
            y = y1,
            n = nodes.length,
            i;

        // Push any overlapping nodes up.
        if (sort === undefined) nodes.sort(ascendingBreadth);
        for (i = n - 1; i >= 0; --i) {
          node = nodes[i];
          dy = (y - node.y1) * alpha;
          if (dy < 0) node.y0 += dy, node.y1 += dy;
          y = node.y0 - py;
        }

        // If the topmost node goes outside the bounds, push it back down.
        dy = (y + py - y0) * alpha;
        if (dy < 0) {
          node.y0 -= dy, y = (node.y1 -= dy);

          // Push any overlapping nodes back down.
          for (i = 1; i < n; ++i) {
            node = nodes[i];
            dy = (node.y0 - py - y) * alpha;
            if (dy < 0) node.y0 -= dy, node.y1 -= dy;
            y = node.y1;
          }
        }
      });
    }
  }

  function computeLinkBreadths(graph) {
    graph.nodes.forEach(function(node) {
      node.sourceLinks.sort(ascendingTargetBreadth);
      node.targetLinks.sort(ascendingSourceBreadth);
    });
    graph.nodes.forEach(function(node) {
      var y0 = node.y0, y1 = y0;
      node.sourceLinks.forEach(function(link) {
        link.y0 = y0 + link.width / 2, y0 += link.width;
      });
      node.targetLinks.forEach(function(link) {
        link.y1 = y1 + link.width / 2, y1 += link.width;
      });
    });
  }

  return sankey;
}
<|endoftext|>


<commit_before>function sceneFromLines(lines) {
	var createComponent = {
		"#": function(col, row) {
			Crafty.e("Wall").Wall(col, row);
		},
		">": function(col, row) {
			Crafty.e("Player1").Snake(col, row, "right", 1);
		},
		"o": function(col, row) {
			Crafty.e("PointItem, LengthIncrease").PointItem(col, row);
		},
		" ": function(col, row) {},
	};
	lines.forEach(function(line, row) {
		line.split("").forEach(function(char, col) {
			if (char === " "){
				return;
			};
			createComponent[char](col, row);
		});
	});
	Crafty.e("Score");
};

Crafty.scene("SetUp", function() {
	Crafty.e("RestartOnSpace");
	Crafty.e("Delay").delay(function() {
		Crafty.scene("MainMenu");
	}, 1000);
});

Crafty.scene("Stage1", function() {
	Crafty.e("BorderWalls");
	var pi = Crafty.e("PointItem, Neumann, LengthIncrease").PointItem(6, 2)
	pi.attr("randomMask", [
		"#########################",
		"#                       #",
		"# >                     #",
		"#                       #",
		"#                       #",
		"# o o ooo o   o   ooo o #",
		"# o o o   o   o   o o o #",
		"# ooo ooo o   o   o o o #",
		"# o o o   o   o   o o   #",
		"# o o ooo ooo ooo ooo o #",
		"#                       #",
		"#                       #",
		"#                       #",
		"#                       #",
		"#########################",
	]);
	Crafty.e("Player1").Snake(2, 2, "right", 5);
	Crafty.e("Score");
	Crafty.e("Objective").Objective(
		"Play until it's visible",
		function() {return false;}
	).bind("NoFreeCellsLeft", function() {
		this.condition = function() {return true;}
	});
});

Crafty.scene("Stage2", function() {
	sceneFromLines([
		"#########################",
		"#                       #",
		"#>ooo o  o  o  o  o ooo #",
		"# o   oo o o o o o  o   #",
		"# ooo o oo ooo oo   ooo #",
		"#   o o  o o o o o  o   #",
		"# ooo o  o o o o  o ooo #",
		"#                       #",
		"#   o   o  o  ooo ooo   #",
		"#   oo oo o o   o o     #",
		"#   o o o ooo  o  ooo   #",
		"#   o   o o o o   o     #",
		"#   o   o o o ooo ooo   #",
		"#                       #",
		"#########################",
	]);
	Crafty.e("Objective").Objective(
		"Collect all point items",
		function(snake) {
			return Crafty("PointItem").length === 0;
		}
	);
});

Crafty.scene("Stage3", function() {
	Crafty.e("BorderWalls");
	Crafty.e("PointItem, Neumann, LengthIncrease").PointItem(8, 4);
	Crafty.e("PointItem, Neumann, SpeedIncrease").PointItem(10, 4);
	Crafty.e("Player1").Snake(3, 4, "right", 5);
	Crafty.e("Score");
	Crafty.e("Objective").Objective(
		"Reach a length of 10 and a speed of 5",
		function(snake) {
			return snake.maxLength >= 10 && snake.speed() >= 5;
		}
	);
});

Crafty.scene("TwoPlayerMode", function() {
	Crafty.e("BorderWalls");
	Crafty.e("PointItem, Reappearing, LengthIncrease").randomMove();
	Crafty.e("PointItem, Reappearing, SpeedIncrease").randomMove();
	Crafty.e("PointItem, Reappearing, Decrease").randomMove();
	Crafty.e("Player1").Snake(1, 1, "right", 5);
	Crafty.e("Player2").Snake(Game.cols-2, Game.rows-2, "left", 5);
	Crafty.e("Objective").Objective(
		"Reach a score of 10 before the other player",
		function(snake) {
			return snake.score >= 10;
		}
	);
	Crafty.e("Score");
});

Crafty.scene("MainMenu", function() {
	one_player_stage_select = [
		[2, 2, "Stage1", "Stage 1"],
		[2, 4, "Stage2", "Stage 2"],
		[2, 6, "Stage3", "Stage 3"],
	];
	Crafty.e("MenuPoints").MenuPoints([
		[12, 2, one_player_stage_select, "One Player Mode"],
		[12, 4, "TwoPlayerMode", "Two Player Mode"],
	]);
	Crafty.e("Player1").Snake(2, 4, "right", 5);
});

<commit_msg>Welcome to... stage<commit_after>function sceneFromLines(lines) {
	var createComponent = {
		"#": function(col, row) {
			Crafty.e("Wall").Wall(col, row);
		},
		">": function(col, row) {
			Crafty.e("Player1").Snake(col, row, "right", 1);
		},
		"o": function(col, row) {
			Crafty.e("PointItem, LengthIncrease").PointItem(col, row);
		},
		"s": function(col, row) {
			Crafty.e("PointItem, SpeedIncrease").PointItem(col, row);
		},
		" ": function(col, row) {},
	};
	lines.forEach(function(line, row) {
		line.split("").forEach(function(char, col) {
			if (char === " "){
				return;
			};
			createComponent[char](col, row);
		});
	});
	Crafty.e("Score");
};

Crafty.scene("SetUp", function() {
	Crafty.e("RestartOnSpace");
	Crafty.e("Delay").delay(function() {
		Crafty.scene("MainMenu");
	}, 1000);
});

Crafty.scene("Stage1", function() {
	Crafty.e("BorderWalls");
	var pi = Crafty.e("PointItem, Neumann, LengthIncrease").PointItem(6, 2)
	pi.attr("randomMask", [
		"#########################",
		"#                       #",
		"# >                     #",
		"#                       #",
		"#                       #",
		"# o o ooo o   o   ooo o #",
		"# o o o   o   o   o o o #",
		"# ooo ooo o   o   o o o #",
		"# o o o   o   o   o o   #",
		"# o o ooo ooo ooo ooo o #",
		"#                       #",
		"#                       #",
		"#                       #",
		"#                       #",
		"#########################",
	]);
	Crafty.e("Player1").Snake(2, 2, "right", 5);
	Crafty.e("Score");
	Crafty.e("Objective").Objective(
		"Play until it's visible",
		function() {return false;}
	).bind("NoFreeCellsLeft", function() {
		this.condition = function() {return true;}
	});
	// Bonus: show the whole text sans one cell (no snake overlap)
});

Crafty.scene("Stage3", function() {
	sceneFromLines([
		"#########################",
		"#                       #",
		"#>ooo o  o  o  o  o ooo #",
		"# o   oo o o o o o  o   #",
		"# ooo o oo ooo oo   ooo #",
		"#   o o  o o o o o  o   #",
		"# ooo o  o o o o  o ooo #",
		"# ##################### #",
		"#   o   o  o  ooo ooo   #",
		"#   oo oo o o   o o     #",
		"#   o o o ooo  o  ooo   #",
		"#   o   o o o o   o     #",
		"#   o   o o o ooo ooo   #",
		"#                       #",
		"#########################",
	]);
	Crafty.e("Objective").Objective(
		"Collect all point items",
		function(snake) {
			return Crafty("PointItem").length === 0;
		}
	);
	// Bonus: collect in the fastest was possible
});

Crafty.scene("Stage2", function() {
	sceneFromLines([
		"#########################",
		"#                       #",
		"# #   #oo# ssooo#   #oo #",
		"# #   #o # s o o## ##o  #",
		"# # # #oo# s o o# # #oo #",
		"# ## ##o # s o o#   #o  #",
		"# #   #oo##ssooo#   #oo #",
		"# >                     #",
		"#        ooo sss        #",
		"#         o  s s        #",
		"#         o  s s        #",
		"#         o  s s        #",
		"#         o  sss        #",
		"#                       #",
		"#########################",
	]);
	Crafty.e("Score");
	Crafty.e("Objective").Objective(
		"Collect all point items",
		function(snake) {
			return Crafty("PointItem").length === 0;
		}
	);
	// Bonus: collect all yellow at max speed
});

Crafty.scene("TwoPlayerMode", function() {
	Crafty.e("BorderWalls");
	Crafty.e("PointItem, Reappearing, LengthIncrease").randomMove();
	Crafty.e("PointItem, Reappearing, SpeedIncrease").randomMove();
	Crafty.e("PointItem, Reappearing, Decrease").randomMove();
	Crafty.e("Player1").Snake(1, 1, "right", 5);
	Crafty.e("Player2").Snake(Game.cols-2, Game.rows-2, "left", 5);
	Crafty.e("Objective").Objective(
		"Reach a score of 10 before the other player",
		function(snake) {
			return snake.score >= 10;
		}
	);
	Crafty.e("Score");
});

Crafty.scene("MainMenu", function() {
	one_player_stage_select = [
		[2, 2, "Stage1", "Stage 1"],
		[2, 4, "Stage2", "Stage 2"],
		[2, 6, "Stage3", "Stage 3"],
	];
	Crafty.e("MenuPoints").MenuPoints([
		[12, 2, one_player_stage_select, "One Player Mode"],
		[12, 4, "TwoPlayerMode", "Two Player Mode"],
	]);
	Crafty.e("Player1").Snake(2, 4, "right", 5);
});

<|endoftext|>


<commit_before>"use strict";

let passages = {
  "Backwards Alphabet": "Z Y X W V U T S R Q P O N M L K J I H G F E D C B A",

  "Final sentence of Washington's Inagural Speech": "Having thus imparted to you my sentiments as they have been awakened by the occasion which brings us together, I shall take my present leave; but not without resorting once more to the benign Parent of the Human Race in humble supplication that, since He has been pleased to favor the American people with opportunities for deliberating in perfect tranquillity, and dispositions for deciding with unparalleled unanimity on a form of government for the security of their union and the advancement of their happiness, so His divine blessing may be equally conspicuous in the enlarged views, the temperate consultations, and the wise measures on which the success of this Government must depend.",

  "Jeff Goldblum's Creation Story from Jurassic Park": "God creates dinosaurs. God destroys dinosaurs. God creates man. Man destroys God. Man creates dinosaurs.",

  "First Sentence of the Gettysburg Address": "Fourscore and seven years ago our fathers brought forth on this continent a new nation, conceived in liberty and dedicated to the proposition that all men are created equal.",

  "Viper's Top Gun Speech": "Gentlemen, you are the top 1% of all naval aviators -- the elite, the BEST of the best. We'll make you better. Fly at least two combat missions a day, attend classes in between, and evaluations of your performance. Now in each combat sequence you're going to meet a different challenge. Every encounter is going to be much more difficult. We're going to teach you to fly the F-14 right to the edge of the envelop, faster than you've ever flown before -- and more dangerous. Now, we don't make policy here, gentlemen. Elected officials, civilians, do that. We are the instruments of that policy. And although we're not at war, we must always act as though we are at war."
};

module.exports = passages;<commit_msg>Restore Independence Day Quote as final passage<commit_after>"use strict";

let passages = {
  "Backwards Alphabet": "Z Y X W V U T S R Q P O N M L K J I H G F E D C B A",

  "Final sentence of Washington's Inagural Speech": "Having thus imparted to you my sentiments as they have been awakened by the occasion which brings us together, I shall take my present leave; but not without resorting once more to the benign Parent of the Human Race in humble supplication that, since He has been pleased to favor the American people with opportunities for deliberating in perfect tranquillity, and dispositions for deciding with unparalleled unanimity on a form of government for the security of their union and the advancement of their happiness, so His divine blessing may be equally conspicuous in the enlarged views, the temperate consultations, and the wise measures on which the success of this Government must depend.",

  "Jeff Goldblum's Creation Story from Jurassic Park": "God creates dinosaurs. God destroys dinosaurs. God creates man. Man destroys God. Man creates dinosaurs.",

  "First Sentence of the Gettysburg Address": "Fourscore and seven years ago our fathers brought forth on this continent a new nation, conceived in liberty and dedicated to the proposition that all men are created equal.",

  "Viper's Top Gun Speech": "Gentlemen, you are the top 1% of all naval aviators -- the elite, the BEST of the best. We'll make you better. Fly at least two combat missions a day, attend classes in between, and evaluations of your performance. Now in each combat sequence you're going to meet a different challenge. Every encounter is going to be much more difficult. We're going to teach you to fly the F-14 right to the edge of the envelop, faster than you've ever flown before -- and more dangerous. Now, we don't make policy here, gentlemen. Elected officials, civilians, do that. We are the instruments of that policy. And although we're not at war, we must always act as though we are at war.",

  "Independence Day Speech": "Good morning. In less than an hour, aircraft from here will join others from around the world. And you will be launching the largest aerial battle in the history of mankind. Mankind; that word should have new meaning for all of us today. We can’t be consumed by our petty differences anymore. We will be united in our common interests. Perhaps it’s fate that today is the 4th of July and you will once again be fighting for our freedom. Not from tyranny, oppression, or persecution, but from annihilation. We’re fighting for our right to live, to exist. And should we win the day, the 4th of July will no longer be known as an American holiday, but as the day when the world declared in one voice: ‘We will not go quietly into the night!’ ‘We will not vanish without a fight!’ ‘We’re going to live on!’ ‘We’re going to survive!’ Today we celebrate our Independence Day!"

};

module.exports = passages;<|endoftext|>


<commit_before>/**
* LICENSE MIT
* (C) Daniel Zelisko
* http://github.com/danielzzz/node-ping
*
* a simple wrapper for ping
*
*/

var sys   = require('util'),
    spawn = require('child_process').spawn;

function probe(addr, cb) {
        var ls    = spawn('/bin/ping', ['-n', '-w 2', '-c 1', addr]);
        /*ls.stdout.on('data', function (data) {
          //sys.print('stdout: ' + data);
        });

        ls.stderr.on('data', function (data) {
          //sys.print('stderr: ' + data);
        });*/

        ls.on('exit', function (code) {
            var result = (code === 0 ? true : false);
            cb && cb(result);
        });
}

exports.probe = probe;

<commit_msg>updated with default paths for windows and mac<commit_after>/**
* LICENSE MIT
* (C) Daniel Zelisko
* http://github.com/danielzzz/node-ping
*
* a simple wrapper for ping
*
*/

var sys   = require('util'),
    cp = require('child_process');
    os = require('os');

function probe(addr, cb) {
        var p = os.platform();
        var ls = null;

        if (p == 'linux') {
            //linux
            ls = cp.spawn('/bin/ping', ['-n', '-w 2', '-c 1', addr]);
        } else if (p.match(/^win/)) {
            //windows
            var ls = spawn('C:/windows/system32/ping.exe', ['-n', '1', '-w', '5000', addr]);

        } else if (p == 'darwin') {
            //mac ox
            var ls = spawn('/sbin/ping', ['-n', '-t 2', '-c 1', addr]);
        }

        ls.on('error', function(e) {
            throw new Error('ping.probe: there was an error while executing the ping program. check the path or permissions...');
        });

        /*ls.stdout.on('data', function (data) {
          //sys.print('stdout: ' + data);
        });

        ls.stderr.on('data', function (data) {
          //sys.print('stderr: ' + data);
        });*/

        ls.on('exit', function (code) {
            var result = (code === 0 ? true : false);
            cb && cb(result);
        });
}

exports.probe = probe;
<|endoftext|>


<commit_before>module.exports = {
  selector: [
    '.source.css.scss',
    '.source.sass',
    '.source.css.embedded.html', // .source.css.embedded.html is a temporary hack to support inline
    '.source.sass.embedded.html',
    '.source.css.scss.embedded.html'
  ],
  id: 'aligner-scss', // package name
  config: {
    ':-enabled': {
      title: 'Enable aligning :',
      type: 'boolean',
      "default": true
    },
    ':-alignment': {
      title: 'Padding for :',
      description: 'Pad left or right of the character',
      type: 'string',
      "default": 'right'
    },
    ':-leftSpace': {
      title: 'Left space for :',
      description: 'Add 1 whitespace to the left',
      type: 'boolean',
      "default": false
    },
    ':-rightSpace': {
      title: 'Right space for :',
      description: 'Add 1 whitespace to the right',
      type: 'boolean',
      "default": true
    },
    '!-enabled': {
      title: 'Enable aligning !',
      type: 'boolean',
      "default": false
    },
    '!-alignment': {
      title: 'Padding for !',
      description: 'Pad left or right of the character',
      type: 'string',
      "default": 'left'
    },
    '!-leftSpace': {
      title: 'Left space for !',
      description: 'Add 1 whitespace to the left',
      type: 'boolean',
      "default": true
    },
    '!-rightSpace': {
      title: 'Right space for !',
      description: 'Add 1 whitespace to the right',
      type: 'boolean',
      "default": false
    },
    '{-enabled': {
      title: 'Enable aligning {',
      type: 'boolean',
      "default": false
    },
    '{-alignment': {
      title: 'Padding for {',
      description: 'Pad left or right of the character',
      type: 'string',
      "default": 'left'
    },
    '{-leftSpace': {
      title: 'Left space for {',
      description: 'Add 1 whitespace to the left',
      type: 'boolean',
      "default": true
    },
    '{-rightSpace': {
      title: 'Right space for {',
      description: 'Add 1 whitespace to the right',
      type: 'boolean',
      "default": true
    }
  },
  privateConfig: {
    '!-scope': 'punctuation',
    ':-scope': 'key-value|property-name|operator',
    '{-scope': 'property-list.begin'
  }
};
<commit_msg>Update all alignment options to have a dropdown<commit_after>module.exports = {
  selector: [
    '.source.css.scss',
    '.source.sass',
    '.source.css.embedded.html', // .source.css.embedded.html is a temporary hack to support inline
    '.source.sass.embedded.html',
    '.source.css.scss.embedded.html'
  ],
  id: 'aligner-scss', // package name
  config: {
    ':-enabled': {
      title: 'Enable aligning :',
      type: 'boolean',
      "default": true
    },
    ':-alignment': {
      title: 'Padding for :',
      description: 'Pad left or right of the character',
      type: 'string',
      "default": 'right',
      enum: ['left', 'right']
    },
    ':-leftSpace': {
      title: 'Left space for :',
      description: 'Add 1 whitespace to the left',
      type: 'boolean',
      "default": false
    },
    ':-rightSpace': {
      title: 'Right space for :',
      description: 'Add 1 whitespace to the right',
      type: 'boolean',
      "default": true
    },
    '!-enabled': {
      title: 'Enable aligning !',
      type: 'boolean',
      "default": false
    },
    '!-alignment': {
      title: 'Padding for !',
      description: 'Pad left or right of the character',
      type: 'string',
      "default": 'left',
      enum: ['left', 'right']
    },
    '!-leftSpace': {
      title: 'Left space for !',
      description: 'Add 1 whitespace to the left',
      type: 'boolean',
      "default": true
    },
    '!-rightSpace': {
      title: 'Right space for !',
      description: 'Add 1 whitespace to the right',
      type: 'boolean',
      "default": false
    },
    '{-enabled': {
      title: 'Enable aligning {',
      type: 'boolean',
      "default": false
    },
    '{-alignment': {
      title: 'Padding for {',
      description: 'Pad left or right of the character',
      type: 'string',
      "default": 'left',
      enum: ['left', 'right']
    },
    '{-leftSpace': {
      title: 'Left space for {',
      description: 'Add 1 whitespace to the left',
      type: 'boolean',
      "default": true
    },
    '{-rightSpace': {
      title: 'Right space for {',
      description: 'Add 1 whitespace to the right',
      type: 'boolean',
      "default": true
    }
  },
  privateConfig: {
    '!-scope': 'punctuation',
    ':-scope': 'key-value|property-name|operator',
    '{-scope': 'property-list.begin'
  }
};
<|endoftext|>


<commit_before>/*
 * Class: FlightDeck
 * Initializes all needed functionality
 */

var FlightDeck = new Class({
	Implements: [Options, Events],
	options: {
		menu_el: 'UI_Editor_Menu',
		try_in_browser_class: 'XPI_test'
		//user: ''
	},
	initialize: function() {
		this.uri = new URI();
		if (this.uri.getData('redirect','fragment')) {
			window.location.href = this.uri.getData('redirect', 'fragment');
		}
		this.warning = this.error = this.message = {
			'alert': function(title, message) {
				alert(title+"\n"+message);
			}
		};
		this.editors = [];
		this.parseTooltips();
		this.createActionSections();
		this.parseTestButtons.bind(this).delay(10);
		this.addEvent('xpi_installed', this.whenXpiInstalled);
		this.addEvent('xpi_uninstalled', this.whenXpiUninstalled);
		if (!this.isAddonInstalled()) $log('FD: No Addon Builder Helper')
	},

	parseTooltips: function() {
		this.tips = new Tips({
			fixed: false,
			className: 'UI_tooltip',
			offset: {
				x: 0,
				y: 16
			}
		});

		$$('div.UI_tooltip_source').each(function(tipSource){
			tipSource.hide();
			var target = $(tipSource.get('data-tooltip-for'));
			target.set('title', '');
			target.store('tip:title', tipSource.get('data-tooltip-title'));
			target.store('tip:text', tipSource.get('html'));
			this.tips.attach(target);
		}, this);
	},

	setURIRedirect: function(url) {
		// change the URL add #/path/to/saved/revision
		if (this.uri.get('directory') != url) {
			this.uri.setData({'redirect': url}, false, 'fragment');
			this.uri.go();
		}
	},

	whenXpiInstalled: function(name) {
		this.parseTestButtons();
		this.message.alert('Add-ons Builder', '{name} installed'.substitute({'name': (name, 'Add-on').pick()}));
		// remove SDK from disk
		if (this.rm_xpi_url) {
			new Request.JSON({
				url: this.rm_xpi_url,
				onSuccess: function() {
					this.fireEvent('sdk_removed');
				}.bind(this)
			}).send();
			this.rm_xpi_url = undefined;
		}
	},

	whenXpiUninstalled: function() {
		this.parseTestButtons();
		this.message.alert('Add-ons Builder', 'Add-on uninstalled');
	},

	/*
	Method: whenAddonInstalled
	create listener for a callback function
	 */
	whenAddonInstalled: function(callback) {
		var removeListener = function() {
			document.body.removeEventListener('addonbuilderhelperstart', callback, false);
		}
		document.body.addEventListener('addonbuilderhelperstart', callback, false);
		(function() {
			$log('FD: Warning: not listening to addonbuilderhelperstart, is Helper installed?');
			removeListener();
		}).delay(100000);
	},

	parseTestButtons: function() {
		var installed = (this.isAddonInstalled()) ? this.isXpiInstalled() : false;
		if (installed) {
			$$('.{try_in_browser_class} a'.substitute(this.options)).each(function(test_button){
				if (installed && installed.installedID == test_button.get('rel')) {
					test_button.getParent('li').addClass('pressed');
				} else {
					test_button.getParent('li').removeClass('pressed');
				}
			}, this);
		}
	},

	/*
	 * Method: testXPI
	 */
	testXPI: function(response) {
		if (response.stderr) {
			fd.error.alert('Error in building Add-on XPI', response.stderr);
			return;
		}
		this.rm_xpi_url = response.rm_xpi_url;
		this.installXPI(response.test_xpi_url, response.name);
	},

	isXpiInstalled: function() {
		return window.mozFlightDeck.send({cmd:'isInstalled'});
	},

	/*
	 * Method: hideEditors
	 */
	hideEditors: function() {
		this.editors.each(function(ed){ ed.hide(); });
	},
	/*
	 * Method: installXPI
	 */
	installXPI: function(url, name) {
		if (fd.alertIfNoAddOn()) {
			$log('FD: installing ' + name + ' - ' + url);
			new Request({
				url: url,
				headers: {'Content-Type': 'text/plain; charset=x-user-defined'},
				onSuccess: function(responseText) {
					var result = window.mozFlightDeck.send({cmd: "install", contents: responseText});
					if (result && result.success) {
						this.fireEvent('xpi_installed', name);
					} else {
						if (result) $log(result);
						this.warning.alert(
							'Add-ons Builder',
							'Wrong response from Add-ons Helper. Please let us know'
						);
					}
				}.bind(this)
			}).send();
		}
	},
	uninstallXPI: function(jid) {
		$log('FD: uninstalling ' + jid);
		var result = window.mozFlightDeck.send({cmd:'uninstall'});
		if (result.success) this.fireEvent('xpi_uninstalled');
	},
	/*
	 * Method: enableMenuButtons
	 */
	enableMenuButtons: function() {
		$$('.' + this.options.menu_el + ' li').each(function(menuItem){
			if (menuItem.hasClass('disabled')){
				menuItem.removeClass('disabled');
			}
		});
	},

	isAddonInstalled: function() {
		return (window.mozFlightDeck) ? true : false;
	},

	/*
	 * Method: alertIfNoAddOn
	 */
	alertIfNoAddOn: function(callback, text, title) {
		if (this.isAddonInstalled()) return true;
		text = (text,
				"To test this add-on, please install the Add-ons Builder Helper add-on".substitute(settings)).pick();
		title = (title, "Install Add-ons Builder Helper").pick();
		fd.warning.alert(title, text);
		return false;
	},
	/*
	 * Method: createActionSections
	 */
	createActionSections: function(){
		$$('.UI_Editor_Menu_Separator').each(function(separator){
			separator.getPrevious('li').addClass('UI_Section_Close');
			separator.getNext('li').addClass('UI_Section_Open');
		});

		var UI_Editor_Menu_Button = $$('.UI_Editor_Menu_Button');

		if (UI_Editor_Menu_Button.length === 1){
			UI_Editor_Menu_Button[0].addClass('UI_Single');
		}
	}
});

/*
 * Add delay do Spinner
 */

Spinner = Class.refactor(Spinner, {
    options: { delay: 400 },
});

/*
 * Default onFailure in all Requests
 */

Request = Class.refactor(Request, {
	options: {
		onFailure: function(xhr) {
            if (this.options.addOnFailure) {
              this.options.addOnFailure();
            }
			fd.error.alert(
				'Error {status}'.substitute(xhr),
				'{statusText}
{responseText}'.substitute(xhr)
				);
		}
	},
    initialize: function(options) {
      this.previous(options);
      // It happened to be unnecessary
      //if (csrfmiddlewaretoken && (this.options.method == 'post' || this.options.method == 'POST')) {
      //  this.options.data['csrfmiddlewaretoken'] = csrfmiddlewaretoken;
      //}

    },
  // overloading processScripts to *not* execute JS responses
	processScripts: function(text){
		if (this.options.evalResponse) return Browser.exec(text);
		return text.stripScripts(this.options.evalScripts);
	},
});

/*
 * Inspired by
 * http://github.com/jeresig/sizzle/commit/7631f9c3f85e5fa72ac51532399cb593c2cdc71f
 * and this http://github.com/jeresig/sizzle/commit/5716360040a440041da19823964f96d025ca734b
 * and then http://dev.jquery.com/ticket/4512
 */

Element.implement({

	isHidden: function(){
		var w = this.offsetWidth, h = this.offsetHeight,
		force = (this.tagName.toLowerCase() === 'tr');
		return (w===0 && h===0 && !force)
			? true
			: (w!==0 && h!==0 && !force) ? false : this.getStyle('display') === 'none';
	},
	isVisible: function(){
		return !this.isHidden();
	},
	getSiblings: function(match,nocache) {
		return this.getParent().getChildren(match,nocache).erase(this);
	}

});

/*
	Add $name mutator - specifies the type of the created Class
	Usage:
		var C = new Class({$name = 'sometype', inititate: function() {}});
		var c = new C();
		alert(typeOf(c)); // 'sometype'
 */
Class.Mutators.$name = function(name){ this.implement('$family', {name: name}); };

/*
 * Add validation for alphanum + "-_."
 */
Form.Validator.addAllThese([
	['validate-alphanum_plus', {
		errorMsg: 	'Please use only letters (a-z),
'+
					'numbers (0-9) or \"_.-\" only in this field.
'+
					'No spaces or other characters are allowed.',
		test: function(element){
			return Form.Validator.getValidator('IsEmpty').test(element) ||  (/^[a-zA-Z0-9._\-]+$/).test(element.get('value'));
		}
	}],
	['validate-alphanum_plus_space', {
		errorMsg: 	'Please use only letters (a-z),
'+
					'numbers (0-9) spaces or \"_().-\" only in this field.
'+
					'No other characters are allowed.',
		test: function(element){
			return Form.Validator.getValidator('IsEmpty').test(element) ||  (/^[a-zA-Z0-9\ _\(\).\-]+$/).test(element.get('value'));
		}
	}]
]);

(function(){
	var html_symbols = ['&','"','<','>','','','','','','','','','','','','','','','','','','','','','','','','','','','','','',''],
	html_names = ['&','"','<','>','¡','¢','£','¤','¥','¦','§','¨','©','ª','«','¬','®','¯','°','±','²','³','´','µ','¶','·','¸','¹','º','»','¼','½','¾','¿'],
	js_symbols = ['\\(','\\)','\\{','\\}'],
	html_number = ['\(','\)','\{','\}'];

	String.implement({
		escapeHTML: function() {
			text = this;
			html_symbols.each(function(symbol, i){
				text = text.replace(new RegExp(symbol, 'g'), html_names[i]);
			});
			return text;
		},
		escapeJS: function() {
			text = this;
			js_symbols.each(function(symbol, i){
				text = text.replace(new RegExp(symbol, 'g'), html_number[i]);
			});
			return text;
		},
		escapeAll: function() {
			return this.escapeHTML()//.escapeJS();
		}
	});
})();

/*
	Listen to an event fired when Extension is installed
	This wasn't working
window.addEvent('load', function() {
	if (window.mozFlightDeck) {
		window.mozFlightDeck.whenMessaged(function(data) {
			// This gets called when one of our extensions has been installed
			// successfully, or failed somehow.
			fd.message.alert('Add-ons Builder', 'Add-on {msg}'.substitute(data));
			// log to console result of isInstalled command
			$log('sending isInstalled to window.mozFlightDeck');
			$log(fd.isXpiInstalled());
		});
	}
});
 */
<commit_msg>picl is an array function<commit_after>/*
 * Class: FlightDeck
 * Initializes all needed functionality
 */

var FlightDeck = new Class({
	Implements: [Options, Events],
	options: {
		menu_el: 'UI_Editor_Menu',
		try_in_browser_class: 'XPI_test'
		//user: ''
	},
	initialize: function() {
		this.uri = new URI();
		if (this.uri.getData('redirect','fragment')) {
			window.location.href = this.uri.getData('redirect', 'fragment');
		}
		this.warning = this.error = this.message = {
			'alert': function(title, message) {
				alert(title+"\n"+message);
			}
		};
		this.editors = [];
		this.parseTooltips();
		this.createActionSections();
		this.parseTestButtons.bind(this).delay(10);
		this.addEvent('xpi_installed', this.whenXpiInstalled);
		this.addEvent('xpi_uninstalled', this.whenXpiUninstalled);
		if (!this.isAddonInstalled()) $log('FD: No Addon Builder Helper')
	},

	parseTooltips: function() {
		this.tips = new Tips({
			fixed: false,
			className: 'UI_tooltip',
			offset: {
				x: 0,
				y: 16
			}
		});

		$$('div.UI_tooltip_source').each(function(tipSource){
			tipSource.hide();
			var target = $(tipSource.get('data-tooltip-for'));
			target.set('title', '');
			target.store('tip:title', tipSource.get('data-tooltip-title'));
			target.store('tip:text', tipSource.get('html'));
			this.tips.attach(target);
		}, this);
	},

	setURIRedirect: function(url) {
		// change the URL add #/path/to/saved/revision
		if (this.uri.get('directory') != url) {
			this.uri.setData({'redirect': url}, false, 'fragment');
			this.uri.go();
		}
	},

	whenXpiInstalled: function(name) {
		this.parseTestButtons();
		this.message.alert('Add-ons Builder', '{name} installed'.substitute({'name': [name, 'Add-on'].pick()}));
		// remove SDK from disk
		if (this.rm_xpi_url) {
			new Request.JSON({
				url: this.rm_xpi_url,
				onSuccess: function() {
					this.fireEvent('sdk_removed');
				}.bind(this)
			}).send();
			this.rm_xpi_url = undefined;
		}
	},

	whenXpiUninstalled: function() {
		this.parseTestButtons();
		this.message.alert('Add-ons Builder', 'Add-on uninstalled');
	},

	/*
	Method: whenAddonInstalled
	create listener for a callback function
	 */
	whenAddonInstalled: function(callback) {
		var removeListener = function() {
			document.body.removeEventListener('addonbuilderhelperstart', callback, false);
		}
		document.body.addEventListener('addonbuilderhelperstart', callback, false);
		(function() {
			$log('FD: Warning: not listening to addonbuilderhelperstart, is Helper installed?');
			removeListener();
		}).delay(100000);
	},

	parseTestButtons: function() {
		var installed = (this.isAddonInstalled()) ? this.isXpiInstalled() : false;
		if (installed) {
			$$('.{try_in_browser_class} a'.substitute(this.options)).each(function(test_button){
				if (installed && installed.installedID == test_button.get('rel')) {
					test_button.getParent('li').addClass('pressed');
				} else {
					test_button.getParent('li').removeClass('pressed');
				}
			}, this);
		}
	},

	/*
	 * Method: testXPI
	 */
	testXPI: function(response) {
		if (response.stderr) {
			fd.error.alert('Error in building Add-on XPI', response.stderr);
			return;
		}
		this.rm_xpi_url = response.rm_xpi_url;
		this.installXPI(response.test_xpi_url, response.name);
	},

	isXpiInstalled: function() {
		return window.mozFlightDeck.send({cmd:'isInstalled'});
	},

	/*
	 * Method: hideEditors
	 */
	hideEditors: function() {
		this.editors.each(function(ed){ ed.hide(); });
	},
	/*
	 * Method: installXPI
	 */
	installXPI: function(url, name) {
		if (fd.alertIfNoAddOn()) {
			$log('FD: installing ' + name + ' - ' + url);
			new Request({
				url: url,
				headers: {'Content-Type': 'text/plain; charset=x-user-defined'},
				onSuccess: function(responseText) {
					var result = window.mozFlightDeck.send({cmd: "install", contents: responseText});
					if (result && result.success) {
						this.fireEvent('xpi_installed', name);
					} else {
						if (result) $log(result);
						this.warning.alert(
							'Add-ons Builder',
							'Wrong response from Add-ons Helper. Please let us know'
						);
					}
				}.bind(this)
			}).send();
		}
	},
	uninstallXPI: function(jid) {
		$log('FD: uninstalling ' + jid);
		var result = window.mozFlightDeck.send({cmd:'uninstall'});
		if (result.success) this.fireEvent('xpi_uninstalled');
	},
	/*
	 * Method: enableMenuButtons
	 */
	enableMenuButtons: function() {
		$$('.' + this.options.menu_el + ' li').each(function(menuItem){
			if (menuItem.hasClass('disabled')){
				menuItem.removeClass('disabled');
			}
		});
	},

	isAddonInstalled: function() {
		return (window.mozFlightDeck) ? true : false;
	},

	/*
	 * Method: alertIfNoAddOn
	 */
	alertIfNoAddOn: function(callback, text, title) {
		if (this.isAddonInstalled()) return true;
		text = [text,
				"To test this add-on, please install the Add-ons Builder Helper add-on".substitute(settings)].pick();
		title = [title, "Install Add-ons Builder Helper"].pick();
		fd.warning.alert(title, text);
		return false;
	},
	/*
	 * Method: createActionSections
	 */
	createActionSections: function(){
		$$('.UI_Editor_Menu_Separator').each(function(separator){
			separator.getPrevious('li').addClass('UI_Section_Close');
			separator.getNext('li').addClass('UI_Section_Open');
		});

		var UI_Editor_Menu_Button = $$('.UI_Editor_Menu_Button');

		if (UI_Editor_Menu_Button.length === 1){
			UI_Editor_Menu_Button[0].addClass('UI_Single');
		}
	}
});

/*
 * Add delay do Spinner
 */

Spinner = Class.refactor(Spinner, {
    options: { delay: 400 },
});

/*
 * Default onFailure in all Requests
 */

Request = Class.refactor(Request, {
	options: {
		onFailure: function(xhr) {
            if (this.options.addOnFailure) {
              this.options.addOnFailure();
            }
			fd.error.alert(
				'Error {status}'.substitute(xhr),
				'{statusText}
{responseText}'.substitute(xhr)
				);
		}
	},
    initialize: function(options) {
      this.previous(options);
      // It happened to be unnecessary
      //if (csrfmiddlewaretoken && (this.options.method == 'post' || this.options.method == 'POST')) {
      //  this.options.data['csrfmiddlewaretoken'] = csrfmiddlewaretoken;
      //}

    },
  // overloading processScripts to *not* execute JS responses
	processScripts: function(text){
		if (this.options.evalResponse) return Browser.exec(text);
		return text.stripScripts(this.options.evalScripts);
	},
});

/*
 * Inspired by
 * http://github.com/jeresig/sizzle/commit/7631f9c3f85e5fa72ac51532399cb593c2cdc71f
 * and this http://github.com/jeresig/sizzle/commit/5716360040a440041da19823964f96d025ca734b
 * and then http://dev.jquery.com/ticket/4512
 */

Element.implement({

	isHidden: function(){
		var w = this.offsetWidth, h = this.offsetHeight,
		force = (this.tagName.toLowerCase() === 'tr');
		return (w===0 && h===0 && !force)
			? true
			: (w!==0 && h!==0 && !force) ? false : this.getStyle('display') === 'none';
	},
	isVisible: function(){
		return !this.isHidden();
	},
	getSiblings: function(match,nocache) {
		return this.getParent().getChildren(match,nocache).erase(this);
	}

});

/*
	Add $name mutator - specifies the type of the created Class
	Usage:
		var C = new Class({$name = 'sometype', inititate: function() {}});
		var c = new C();
		alert(typeOf(c)); // 'sometype'
 */
Class.Mutators.$name = function(name){ this.implement('$family', {name: name}); };

/*
 * Add validation for alphanum + "-_."
 */
Form.Validator.addAllThese([
	['validate-alphanum_plus', {
		errorMsg: 	'Please use only letters (a-z),
'+
					'numbers (0-9) or \"_.-\" only in this field.
'+
					'No spaces or other characters are allowed.',
		test: function(element){
			return Form.Validator.getValidator('IsEmpty').test(element) ||  (/^[a-zA-Z0-9._\-]+$/).test(element.get('value'));
		}
	}],
	['validate-alphanum_plus_space', {
		errorMsg: 	'Please use only letters (a-z),
'+
					'numbers (0-9) spaces or \"_().-\" only in this field.
'+
					'No other characters are allowed.',
		test: function(element){
			return Form.Validator.getValidator('IsEmpty').test(element) ||  (/^[a-zA-Z0-9\ _\(\).\-]+$/).test(element.get('value'));
		}
	}]
]);

(function(){
	var html_symbols = ['&','"','<','>','','','','','','','','','','','','','','','','','','','','','','','','','','','','','',''],
	html_names = ['&','"','<','>','¡','¢','£','¤','¥','¦','§','¨','©','ª','«','¬','®','¯','°','±','²','³','´','µ','¶','·','¸','¹','º','»','¼','½','¾','¿'],
	js_symbols = ['\\(','\\)','\\{','\\}'],
	html_number = ['\(','\)','\{','\}'];

	String.implement({
		escapeHTML: function() {
			text = this;
			html_symbols.each(function(symbol, i){
				text = text.replace(new RegExp(symbol, 'g'), html_names[i]);
			});
			return text;
		},
		escapeJS: function() {
			text = this;
			js_symbols.each(function(symbol, i){
				text = text.replace(new RegExp(symbol, 'g'), html_number[i]);
			});
			return text;
		},
		escapeAll: function() {
			return this.escapeHTML()//.escapeJS();
		}
	});
})();

/*
	Listen to an event fired when Extension is installed
	This wasn't working
window.addEvent('load', function() {
	if (window.mozFlightDeck) {
		window.mozFlightDeck.whenMessaged(function(data) {
			// This gets called when one of our extensions has been installed
			// successfully, or failed somehow.
			fd.message.alert('Add-ons Builder', 'Add-on {msg}'.substitute(data));
			// log to console result of isInstalled command
			$log('sending isInstalled to window.mozFlightDeck');
			$log(fd.isXpiInstalled());
		});
	}
});
 */
<|endoftext|>


<commit_before>// Copyright 2016 Chronicled
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

var Promise = require('bluebird');
var Web3 = require('web3');
var ProviderEngine = require('web3-provider-engine');
var CacheSubprovider = require('web3-provider-engine/subproviders/cache.js');
var FixtureSubprovider = require('web3-provider-engine/subproviders/fixture.js');
var FiltersSubprovider = require('web3-provider-engine/subproviders/filters.js');
// vm subprovider is poorly implemented, not fully-compatible with the geth, and breaks browser compatibility.
// var VmSubprovider = require('web3-provider-engine/subproviders/vm.js');
var RpcSubprovider = require('web3-provider-engine/subproviders/rpc.js');
var HookedWalletSubprovider = require('web3-provider-engine/subproviders/hooked-wallet.js');
var NonceSubprovider = require('web3-provider-engine/subproviders/nonce-tracker.js');
var wallet = require('eth-lightwallet');

var ConsumerSdk = require('./consumer.js');
var RegistrantSdk = require('./registrant.js');
var RegistrarSdk = require('./registrar.js');

// Fix webpack bitcore error
delete global._bitcore;

/* global Uint8Array */
var pwDerivedKey = new Uint8Array([215,152,86,175,5,168,43,177,135,97,218,89,136,5,110,93,193,114,94,197,247,212,127,83,200,150,255,124,17,245,91,10]);

var registryAddress = '0x07315477a3f2887eeb4a1b6ac61e34850755d1ae';
var registryABI = [{"constant":true,"inputs":[],"name":"registrarAddress","outputs":[{"name":"","type":"address"}],"type":"function"},{"constant":false,"inputs":[{"name":"_ids","type":"bytes32[]"},{"name":"_data","type":"bytes32[]"},{"name":"_schemaIndex","type":"uint88"}],"name":"createThing","outputs":[{"name":"","type":"bool"}],"type":"function"},{"constant":true,"inputs":[{"name":"_id","type":"bytes32[]"}],"name":"thingExist","outputs":[{"name":"","type":"bool"}],"type":"function"},{"constant":true,"inputs":[{"name":"","type":"uint256"}],"name":"schemas","outputs":[{"name":"","type":"bytes"}],"type":"function"},{"constant":false,"inputs":[],"name":"discontinue","outputs":[],"type":"function"},{"constant":false,"inputs":[{"name":"_schema","type":"bytes"}],"name":"createSchema","outputs":[{"name":"","type":"uint256"}],"type":"function"},{"constant":true,"inputs":[{"name":"","type":"bytes32"}],"name":"idToThing","outputs":[{"name":"","type":"uint256"}],"type":"function"},{"constant":false,"inputs":[{"name":"_id","type":"bytes32[]"},{"name":"_newIds","type":"bytes32[]"}],"name":"addIdentities","outputs":[{"name":"","type":"bool"}],"type":"function"},{"constant":false,"inputs":[{"name":"_registrarAddress","type":"address"}],"name":"configure","outputs":[{"name":"","type":"bool"}],"type":"function"},{"constant":false,"inputs":[{"name":"_ids","type":"bytes32[]"},{"name":"_idsPerThing","type":"uint16[]"},{"name":"_data","type":"bytes32[]"},{"name":"_dataLength","type":"uint16[]"},{"name":"_schemaIndex","type":"uint88"}],"name":"createThings","outputs":[],"type":"function"},{"constant":false,"inputs":[{"name":"_id","type":"bytes32[]"},{"name":"_isValid","type":"bool"}],"name":"setThingValid","outputs":[{"name":"","type":"bool"}],"type":"function"},{"constant":false,"inputs":[{"name":"_id","type":"bytes32[]"},{"name":"_data","type":"bytes32[]"},{"name":"_schemaIndex","type":"uint88"}],"name":"updateThingData","outputs":[{"name":"","type":"bool"}],"type":"function"},{"constant":false,"inputs":[{"name":"_id","type":"bytes32[]"}],"name":"deleteThing","outputs":[{"name":"","type":"bool"}],"type":"function"},{"constant":true,"inputs":[],"name":"getSchemasLenght","outputs":[{"name":"","type":"uint256"}],"type":"function"},{"constant":true,"inputs":[{"name":"_id","type":"bytes32[]"}],"name":"getThing","outputs":[{"name":"","type":"bytes32[]"},{"name":"","type":"bytes32[]"},{"name":"","type":"uint88"},{"name":"","type":"bytes"},{"name":"","type":"address"},{"name":"","type":"bool"}],"type":"function"},{"constant":true,"inputs":[{"name":"","type":"uint256"}],"name":"things","outputs":[{"name":"ownerAddress","type":"address"},{"name":"schemaIndex","type":"uint88"},{"name":"isValid","type":"bool"}],"type":"function"},{"inputs":[],"type":"constructor"},{"anonymous":false,"inputs":[{"indexed":false,"name":"ids","type":"bytes32[]"},{"indexed":true,"name":"owner","type":"address"}],"name":"Created","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"ids","type":"bytes32[]"},{"indexed":true,"name":"owner","type":"address"},{"indexed":false,"name":"isValid","type":"bool"}],"name":"Updated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"ids","type":"bytes32[]"},{"indexed":true,"name":"owner","type":"address"}],"name":"Deleted","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"code","type":"uint256"},{"indexed":false,"name":"reference","type":"bytes32[]"}],"name":"Error","type":"event"}];

var registrarAddress = '0xb98fdd97f3da105e6b166461afac208981bc786b';
var registrarABI = [{"constant":true,"inputs":[],"name":"getRegistrants","outputs":[{"name":"","type":"address[]"}],"type":"function"},{"constant":false,"inputs":[{"name":"_registrant","type":"address"},{"name":"_data","type":"bytes"}],"name":"add","outputs":[{"name":"","type":"bool"}],"type":"function"},{"constant":false,"inputs":[{"name":"_registrant","type":"address"},{"name":"_data","type":"bytes"},{"name":"_active","type":"bool"}],"name":"edit","outputs":[{"name":"","type":"bool"}],"type":"function"},{"constant":true,"inputs":[{"name":"_registrant","type":"address"}],"name":"isActiveRegistrant","outputs":[{"name":"","type":"bool"}],"type":"function"},{"constant":false,"inputs":[],"name":"discontinue","outputs":[],"type":"function"},{"constant":true,"inputs":[],"name":"registrar","outputs":[{"name":"","type":"address"}],"type":"function"},{"constant":true,"inputs":[{"name":"","type":"address"}],"name":"registrantIndex","outputs":[{"name":"","type":"uint256"}],"type":"function"},{"constant":true,"inputs":[{"name":"","type":"uint256"}],"name":"registrants","outputs":[{"name":"addr","type":"address"},{"name":"data","type":"bytes"},{"name":"active","type":"bool"}],"type":"function"},{"constant":false,"inputs":[{"name":"_registrar","type":"address"}],"name":"setNextRegistrar","outputs":[{"name":"","type":"bool"}],"type":"function"},{"inputs":[],"type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"name":"registrant","type":"address"},{"indexed":false,"name":"registrar","type":"address"},{"indexed":false,"name":"data","type":"bytes"}],"name":"Created","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"registrant","type":"address"},{"indexed":false,"name":"registrar","type":"address"},{"indexed":false,"name":"data","type":"bytes"},{"indexed":false,"name":"active","type":"bool"}],"name":"Updated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"code","type":"uint256"}],"name":"Error","type":"event"}];

var gasLimit = 1400000;

function extend(child, parent) {
  for(var prop in parent.prototype) {
    child.prototype[prop] = parent.prototype[prop];
  }
}

/**
 * Instantiates Sdk.
 * @constructor
 * @param {string} [rpcUrl='https://node.ambisafe.co'] geth node url.
 * @param {string} [userType='consumer'] Open Registry role to construct. One of 'consumer', 'registrant' or 'registrar'.
 * @param {string} [secret] private key or mnemonic to sign transactions with. Required only for "write" operation for 'registrant' and 'registrar' roles.
 * @param {Object} [contracts] contract addresses and ABI, defaults to official contracts.
 * @param {function} [finalCallback] callback that will be called with SDK instance as the only parameter.
 * @returns {Sdk} that uses this provider to communicate with blockchain.
 */
function Provider(rpcUrl, userType, secret, contracts, params, finalCallback) {
  var self = this;

  var engine = new ProviderEngine();
  var web3 = new Web3(engine);
  var addr;

  engine.addProvider(new FixtureSubprovider({
    web3_clientVersion: 'ProviderEngine/v0.0.0/javascript',
    net_listening: true,
    eth_hashrate: '0x00',
    eth_mining: false,
    eth_syncing: true
  }));

  // cache layer
  engine.addProvider(new CacheSubprovider());

  // pending nonce
  engine.addProvider(new NonceSubprovider());

  // vm
  // engine.addProvider(new VmSubprovider());

  this.params = params || {};
  this.shims = {};

  if (secret) {
    var ks;
    if (secret && secret.length > 70) {
      ks = new wallet.keystore(secret, pwDerivedKey);
      ks.generateNewAddress(pwDerivedKey, 1);
    }
    else if (secret) {
      ks = new wallet.keystore();
      ks.addPriv = function(privkeyHex) {
        var privKey = new Buffer(privkeyHex.replace('0x', ''), 'hex');
        var encPrivKey = wallet.keystore._encryptKey(privKey, pwDerivedKey);
        var address = wallet.keystore._computeAddressFromPrivKey(privKey);
        this.ksData["m/0'/0'/0'"].encPrivKeys[address] = encPrivKey;
        this.ksData["m/0'/0'/0'"].addresses.push(address);
      };
      ks.isDerivedKeyCorrect = function(pwDerivedKey) {
        if (!this.encSeed)
          return true;

        /* global KeyStore */
        var paddedSeed = KeyStore._decryptString(this.encSeed, pwDerivedKey);
        if (paddedSeed.length > 0) {
          return true;
        }
        return false;
      };
      ks.addPriv(secret);
    }
    addr = '0x' + ks.getAddresses()[0];

    engine.addProvider(new HookedWalletSubprovider({
      getAccounts: function(cb) {
        cb(null, [addr]);
      },
      approveTransaction: function(txParams, cb) {
        cb(null, true);
      },
      signTransaction: function(txData, cb) {
        // Static gas price
        var gasPrice = self.params.gasPrice || self.web3.toWei(30, 'shannon');
        txData.gasPrice = '0x' + self.web3.toBigNumber(gasPrice).toString(16);   // txData.gasPrice = parseInt(txData.gasPrice, 16);
        txData.gasLimit = '0x' + self.web3.toBigNumber(gasLimit).toString(16);
        txData.nonce = parseInt(txData.nonce, 16);
        if (self.shims[txData.to]) {
          if (typeof(self.shims[txData.to]) === 'function') {
            txData = self.shims[txData.to](txData);
          }
          else {
            txData.to = self.shims[txData.to];
          }
        }

        var tx = wallet.txutils.createContractTx(addr, txData);
        var signed = '0x' + wallet.signing.signTx(ks, pwDerivedKey, tx.tx, addr);
        cb(null, signed);
      }
    }));
  }
  else {
    // Dummy address to be able to execute CALL requests
    addr = '0x0000000000000000000000000000000000000001';
  }

  engine.addProvider(new FiltersSubprovider());

  engine.addProvider(new RpcSubprovider({
    rpcUrl: rpcUrl || 'http://localhost:8545'
  }));

  engine.on('error', function(err){
    console.error(err.stack);
  });

  // var firstBlock = true;
  // engine.on('block', function(block){
  //   if (firstBlock){
  //     firstBlock = false;
  //     instantiateSdk();
  //   }
  // });

  engine.start();

  this.web3 = Promise.promisifyAll(web3);
  this.web3.eth = Promise.promisifyAll(this.web3.eth);
  this.address = addr;
  this.registryAddress = registryAddress;
  this.registrarAddress = registrarAddress;
  this.registryABI = registryABI;
  this.registrarABI = registrarABI;
  if (contracts) {
    this.registryAddress = contracts.registryAddress || this.registryAddress;
    this.registryABI = contracts.registryABI || this.registryABI;
    this.registrarAddress = contracts.registrarAddress || this.registrarAddress;
    this.registrarABI = contracts.registrarABI || this.registrarABI;
  }

  this.instantiateSdk(userType || 'consumer');
  if (finalCallback) {
    finalCallback(this.sdk);
  }

  return this.sdk;
}

/**
 * Instantiates Sdk for specific user role.
 * @function
 * @param {string} userType Open Registry role to construct. One of 'consumer', 'registrant' or 'registrar'.
 * @returns {Sdk} that uses this provider to communicate with blockchain.
 */
Provider.prototype.instantiateSdk = function(userType) {
  var Sdk;

  switch (userType) {
    case 'consumer':
      this.sdk = new ConsumerSdk(this);
    break;
    case 'registrant':
      Sdk = RegistrantSdk;
      extend(Sdk, ConsumerSdk);
      this.sdk = new Sdk(this);
    break;
    case 'registrar':
      Sdk = RegistrarSdk;
      extend(Sdk, ConsumerSdk);
      extend(Sdk, RegistrantSdk);
      this.sdk = new Sdk(this);
    break;
  }

  return this.sdk;
};

/**
 * Get Registry contract instance.
 * @function
 * @returns {Contract} that allows interaction with Registry.
 */
Provider.prototype.getRegistry = function() {
  return Promise.promisifyAll(this.web3.eth.contract(this.registryABI).at(this.registryAddress));
};

/**
 * Get Registrar contract instance.
 * @function
 * @returns {Contract} that allows interaction with Registrar.
 */
Provider.prototype.getRegistrar = function() {
  return Promise.promisifyAll(this.web3.eth.contract(this.registrarABI).at(this.registrarAddress));
};

/**
 * Get web3 instance.
 * @function
 * @returns {Web3} that is using this provider to communicate with blockchain.
 */
Provider.prototype.getWeb3 = function() {
  return this.web3;
};

/**
 * Get ethereum account address.
 * @function
 * @returns {string} that is used to send transactions to blockchain.
 */
Provider.prototype.getAddress = function() {
  return this.address;
};

/**
 * Adds proxy contract to send transactions to instead of talking directly to Registry/Registrar.
 * @function
 * @param {string} targetAddress that needs to be proxied.
 * @param {string} shimAddress that will serve as proxy.
 */
Provider.prototype.addShim = function(targetAddress, shimAddressOrFunction) {
  this.shims[targetAddress] = shimAddressOrFunction;
};

module.exports = Provider;
<commit_msg>Proper gas<commit_after>// Copyright 2016 Chronicled
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

var Promise = require('bluebird');
var Web3 = require('web3');
var ProviderEngine = require('web3-provider-engine');
var CacheSubprovider = require('web3-provider-engine/subproviders/cache.js');
var FixtureSubprovider = require('web3-provider-engine/subproviders/fixture.js');
var FiltersSubprovider = require('web3-provider-engine/subproviders/filters.js');
// vm subprovider is poorly implemented, not fully-compatible with the geth, and breaks browser compatibility.
// var VmSubprovider = require('web3-provider-engine/subproviders/vm.js');
var RpcSubprovider = require('web3-provider-engine/subproviders/rpc.js');
var HookedWalletSubprovider = require('web3-provider-engine/subproviders/hooked-wallet.js');
var NonceSubprovider = require('web3-provider-engine/subproviders/nonce-tracker.js');
var wallet = require('eth-lightwallet');

var ConsumerSdk = require('./consumer.js');
var RegistrantSdk = require('./registrant.js');
var RegistrarSdk = require('./registrar.js');

// Fix webpack bitcore error
delete global._bitcore;

/* global Uint8Array */
var pwDerivedKey = new Uint8Array([215,152,86,175,5,168,43,177,135,97,218,89,136,5,110,93,193,114,94,197,247,212,127,83,200,150,255,124,17,245,91,10]);

var registryAddress = '0x07315477a3f2887eeb4a1b6ac61e34850755d1ae';
var registryABI = [{"constant":true,"inputs":[],"name":"registrarAddress","outputs":[{"name":"","type":"address"}],"type":"function"},{"constant":false,"inputs":[{"name":"_ids","type":"bytes32[]"},{"name":"_data","type":"bytes32[]"},{"name":"_schemaIndex","type":"uint88"}],"name":"createThing","outputs":[{"name":"","type":"bool"}],"type":"function"},{"constant":true,"inputs":[{"name":"_id","type":"bytes32[]"}],"name":"thingExist","outputs":[{"name":"","type":"bool"}],"type":"function"},{"constant":true,"inputs":[{"name":"","type":"uint256"}],"name":"schemas","outputs":[{"name":"","type":"bytes"}],"type":"function"},{"constant":false,"inputs":[],"name":"discontinue","outputs":[],"type":"function"},{"constant":false,"inputs":[{"name":"_schema","type":"bytes"}],"name":"createSchema","outputs":[{"name":"","type":"uint256"}],"type":"function"},{"constant":true,"inputs":[{"name":"","type":"bytes32"}],"name":"idToThing","outputs":[{"name":"","type":"uint256"}],"type":"function"},{"constant":false,"inputs":[{"name":"_id","type":"bytes32[]"},{"name":"_newIds","type":"bytes32[]"}],"name":"addIdentities","outputs":[{"name":"","type":"bool"}],"type":"function"},{"constant":false,"inputs":[{"name":"_registrarAddress","type":"address"}],"name":"configure","outputs":[{"name":"","type":"bool"}],"type":"function"},{"constant":false,"inputs":[{"name":"_ids","type":"bytes32[]"},{"name":"_idsPerThing","type":"uint16[]"},{"name":"_data","type":"bytes32[]"},{"name":"_dataLength","type":"uint16[]"},{"name":"_schemaIndex","type":"uint88"}],"name":"createThings","outputs":[],"type":"function"},{"constant":false,"inputs":[{"name":"_id","type":"bytes32[]"},{"name":"_isValid","type":"bool"}],"name":"setThingValid","outputs":[{"name":"","type":"bool"}],"type":"function"},{"constant":false,"inputs":[{"name":"_id","type":"bytes32[]"},{"name":"_data","type":"bytes32[]"},{"name":"_schemaIndex","type":"uint88"}],"name":"updateThingData","outputs":[{"name":"","type":"bool"}],"type":"function"},{"constant":false,"inputs":[{"name":"_id","type":"bytes32[]"}],"name":"deleteThing","outputs":[{"name":"","type":"bool"}],"type":"function"},{"constant":true,"inputs":[],"name":"getSchemasLenght","outputs":[{"name":"","type":"uint256"}],"type":"function"},{"constant":true,"inputs":[{"name":"_id","type":"bytes32[]"}],"name":"getThing","outputs":[{"name":"","type":"bytes32[]"},{"name":"","type":"bytes32[]"},{"name":"","type":"uint88"},{"name":"","type":"bytes"},{"name":"","type":"address"},{"name":"","type":"bool"}],"type":"function"},{"constant":true,"inputs":[{"name":"","type":"uint256"}],"name":"things","outputs":[{"name":"ownerAddress","type":"address"},{"name":"schemaIndex","type":"uint88"},{"name":"isValid","type":"bool"}],"type":"function"},{"inputs":[],"type":"constructor"},{"anonymous":false,"inputs":[{"indexed":false,"name":"ids","type":"bytes32[]"},{"indexed":true,"name":"owner","type":"address"}],"name":"Created","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"ids","type":"bytes32[]"},{"indexed":true,"name":"owner","type":"address"},{"indexed":false,"name":"isValid","type":"bool"}],"name":"Updated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"ids","type":"bytes32[]"},{"indexed":true,"name":"owner","type":"address"}],"name":"Deleted","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"code","type":"uint256"},{"indexed":false,"name":"reference","type":"bytes32[]"}],"name":"Error","type":"event"}];

var registrarAddress = '0xb98fdd97f3da105e6b166461afac208981bc786b';
var registrarABI = [{"constant":true,"inputs":[],"name":"getRegistrants","outputs":[{"name":"","type":"address[]"}],"type":"function"},{"constant":false,"inputs":[{"name":"_registrant","type":"address"},{"name":"_data","type":"bytes"}],"name":"add","outputs":[{"name":"","type":"bool"}],"type":"function"},{"constant":false,"inputs":[{"name":"_registrant","type":"address"},{"name":"_data","type":"bytes"},{"name":"_active","type":"bool"}],"name":"edit","outputs":[{"name":"","type":"bool"}],"type":"function"},{"constant":true,"inputs":[{"name":"_registrant","type":"address"}],"name":"isActiveRegistrant","outputs":[{"name":"","type":"bool"}],"type":"function"},{"constant":false,"inputs":[],"name":"discontinue","outputs":[],"type":"function"},{"constant":true,"inputs":[],"name":"registrar","outputs":[{"name":"","type":"address"}],"type":"function"},{"constant":true,"inputs":[{"name":"","type":"address"}],"name":"registrantIndex","outputs":[{"name":"","type":"uint256"}],"type":"function"},{"constant":true,"inputs":[{"name":"","type":"uint256"}],"name":"registrants","outputs":[{"name":"addr","type":"address"},{"name":"data","type":"bytes"},{"name":"active","type":"bool"}],"type":"function"},{"constant":false,"inputs":[{"name":"_registrar","type":"address"}],"name":"setNextRegistrar","outputs":[{"name":"","type":"bool"}],"type":"function"},{"inputs":[],"type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"name":"registrant","type":"address"},{"indexed":false,"name":"registrar","type":"address"},{"indexed":false,"name":"data","type":"bytes"}],"name":"Created","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"registrant","type":"address"},{"indexed":false,"name":"registrar","type":"address"},{"indexed":false,"name":"data","type":"bytes"},{"indexed":false,"name":"active","type":"bool"}],"name":"Updated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"code","type":"uint256"}],"name":"Error","type":"event"}];

const DEFAULT_GAS_LIMIT = 1400000;

function extend(child, parent) {
  for(var prop in parent.prototype) {
    child.prototype[prop] = parent.prototype[prop];
  }
}

/**
 * Instantiates Sdk.
 * @constructor
 * @param {string} [rpcUrl='https://node.ambisafe.co'] geth node url.
 * @param {string} [userType='consumer'] Open Registry role to construct. One of 'consumer', 'registrant' or 'registrar'.
 * @param {string} [secret] private key or mnemonic to sign transactions with. Required only for "write" operation for 'registrant' and 'registrar' roles.
 * @param {Object} [contracts] contract addresses and ABI, defaults to official contracts.
 * @param {function} [finalCallback] callback that will be called with SDK instance as the only parameter.
 * @returns {Sdk} that uses this provider to communicate with blockchain.
 */
function Provider(rpcUrl, userType, secret, contracts, params, finalCallback) {
  var self = this;

  var engine = new ProviderEngine();
  var web3 = new Web3(engine);
  var addr;

  engine.addProvider(new FixtureSubprovider({
    web3_clientVersion: 'ProviderEngine/v0.0.0/javascript',
    net_listening: true,
    eth_hashrate: '0x00',
    eth_mining: false,
    eth_syncing: true
  }));

  // cache layer
  engine.addProvider(new CacheSubprovider());

  // pending nonce
  engine.addProvider(new NonceSubprovider());

  // vm
  // engine.addProvider(new VmSubprovider());

  this.params = params || {};
  this.params.gasLimit = this.params.gasLimit || DEFAULT_GAS_LIMIT;
  this.shims = {};

  if (secret) {
    var ks;
    if (secret && secret.length > 70) {
      ks = new wallet.keystore(secret, pwDerivedKey);
      ks.generateNewAddress(pwDerivedKey, 1);
    }
    else if (secret) {
      ks = new wallet.keystore();
      ks.addPriv = function(privkeyHex) {
        var privKey = new Buffer(privkeyHex.replace('0x', ''), 'hex');
        var encPrivKey = wallet.keystore._encryptKey(privKey, pwDerivedKey);
        var address = wallet.keystore._computeAddressFromPrivKey(privKey);
        this.ksData["m/0'/0'/0'"].encPrivKeys[address] = encPrivKey;
        this.ksData["m/0'/0'/0'"].addresses.push(address);
      };
      ks.isDerivedKeyCorrect = function(pwDerivedKey) {
        if (!this.encSeed)
          return true;

        /* global KeyStore */
        var paddedSeed = KeyStore._decryptString(this.encSeed, pwDerivedKey);
        if (paddedSeed.length > 0) {
          return true;
        }
        return false;
      };
      ks.addPriv(secret);
    }
    addr = '0x' + ks.getAddresses()[0];

    engine.addProvider(new HookedWalletSubprovider({
      getAccounts: function(cb) {
        cb(null, [addr]);
      },
      approveTransaction: function(txParams, cb) {
        cb(null, true);
      },
      signTransaction: function(txData, cb) {
        // Static gas price
        var gasPrice = self.params.gasPrice || self.web3.toWei(20, 'shannon');
        txData.gasPrice = '0x' + self.web3.toBigNumber(gasPrice).toString(16);   // txData.gasPrice = parseInt(txData.gasPrice, 16);
        txData.gasLimit = '0x' + self.web3.toBigNumber(txData.gas || txData.gasLimit || self.params.gasLimit).toString(16);

        txData.nonce = parseInt(txData.nonce, 16);
        if (self.shims[txData.to]) {
          if (typeof(self.shims[txData.to]) === 'function') {
            txData = self.shims[txData.to](txData);
          }
          else {
            txData.to = self.shims[txData.to];
          }
        }

        var tx = wallet.txutils.createContractTx(addr, txData);
        var signed = '0x' + wallet.signing.signTx(ks, pwDerivedKey, tx.tx, addr);
        cb(null, signed);
      }
    }));
  }
  else {
    // Dummy address to be able to execute CALL requests
    addr = '0x0000000000000000000000000000000000000001';
  }

  engine.addProvider(new FiltersSubprovider());

  engine.addProvider(new RpcSubprovider({
    rpcUrl: rpcUrl || 'http://localhost:8545'
  }));

  engine.on('error', function(err){
    console.error(err.stack);
  });

  // var firstBlock = true;
  // engine.on('block', function(block){
  //   if (firstBlock){
  //     firstBlock = false;
  //     instantiateSdk();
  //   }
  // });

  engine.start();

  this.web3 = Promise.promisifyAll(web3);
  this.web3.eth = Promise.promisifyAll(this.web3.eth);
  this.address = addr;
  this.registryAddress = registryAddress;
  this.registrarAddress = registrarAddress;
  this.registryABI = registryABI;
  this.registrarABI = registrarABI;
  if (contracts) {
    this.registryAddress = contracts.registryAddress || this.registryAddress;
    this.registryABI = contracts.registryABI || this.registryABI;
    this.registrarAddress = contracts.registrarAddress || this.registrarAddress;
    this.registrarABI = contracts.registrarABI || this.registrarABI;
  }

  this.instantiateSdk(userType || 'consumer');
  if (finalCallback) {
    finalCallback(this.sdk);
  }

  return this.sdk;
}

/**
 * Instantiates Sdk for specific user role.
 * @function
 * @param {string} userType Open Registry role to construct. One of 'consumer', 'registrant' or 'registrar'.
 * @returns {Sdk} that uses this provider to communicate with blockchain.
 */
Provider.prototype.instantiateSdk = function(userType) {
  var Sdk;

  switch (userType) {
    case 'consumer':
      this.sdk = new ConsumerSdk(this);
    break;
    case 'registrant':
      Sdk = RegistrantSdk;
      extend(Sdk, ConsumerSdk);
      this.sdk = new Sdk(this);
    break;
    case 'registrar':
      Sdk = RegistrarSdk;
      extend(Sdk, ConsumerSdk);
      extend(Sdk, RegistrantSdk);
      this.sdk = new Sdk(this);
    break;
  }

  return this.sdk;
};

/**
 * Get Registry contract instance.
 * @function
 * @returns {Contract} that allows interaction with Registry.
 */
Provider.prototype.getRegistry = function() {
  return Promise.promisifyAll(this.web3.eth.contract(this.registryABI).at(this.registryAddress));
};

/**
 * Get Registrar contract instance.
 * @function
 * @returns {Contract} that allows interaction with Registrar.
 */
Provider.prototype.getRegistrar = function() {
  return Promise.promisifyAll(this.web3.eth.contract(this.registrarABI).at(this.registrarAddress));
};

/**
 * Get web3 instance.
 * @function
 * @returns {Web3} that is using this provider to communicate with blockchain.
 */
Provider.prototype.getWeb3 = function() {
  return this.web3;
};

/**
 * Get ethereum account address.
 * @function
 * @returns {string} that is used to send transactions to blockchain.
 */
Provider.prototype.getAddress = function() {
  return this.address;
};

/**
 * Adds proxy contract to send transactions to instead of talking directly to Registry/Registrar.
 * @function
 * @param {string} targetAddress that needs to be proxied.
 * @param {string} shimAddress that will serve as proxy.
 */
Provider.prototype.addShim = function(targetAddress, shimAddressOrFunction) {
  this.shims[targetAddress] = shimAddressOrFunction;
};

module.exports = Provider;
<|endoftext|>