<commit_before>(function(red, $) {
var cjs = red.cjs, _ = red._;
var origin = window.location.protocol + "//" + window.location.host;

$.widget("red.editor", {

	options: {
		debug_ready: false,
		debug_env: false,
		command_box: false,
		server_window: window.opener
	}

	, _create: function() {
		if(this.options.command_box) {
			this.command_box = $("<input />")	.prependTo(this.element.parent())
												.focus()
												.on("keydown", _.bind(function(event) {
													if(event.keyCode === 13) {
														var val = this.command_box.val();
														this.command_box.val("");
														this.on_input_command(val);
													}
												}, this));
		}

		this.env = red.create("environment");
		if(this.option("debug_env")) {
			window.env = this.env;
		}
		var root_pointer = this.env.get_root_pointer();
		this.root = root_pointer.root();
		this.element.dom_output({
			root: root_pointer
		});

		this.client_socket = new red.ProgramStateClient({
			ready_func: this.option("debug_ready"),
			server_window: this.option("server_window")
		}).on("message", function(data) {
			if(data.type === "color") {
				var color = data.value;
				$("html").css({
					"border-bottom": "10px solid " + color,
					height: "100%",
					"box-sizing": "border-box"
				});
			}
		}, this).on("root_loaded", function(root) {
			this.root.set("external_root", root, {literal: true});
			this.root.set("external_root_pointer", red.create("pointer", {stack: [root]}), {literal: true});
			this.load_viewer();
		}, this);
	}

	, load_viewer: function() {
		this.env

// ===== BEGIN EDITOR =====

.top()
.set("stringify_value", "function(v) {\n" +
"if(red._.isUndefined(v)) { return '(undefined)'; }\n" +
"else if(red._.isNull(v)) { return '(null)'; }\n" +
"else if(red._.isNumber(v) || red._.isBoolean(v)) { return v+''; }\n" +
"else if(red._.isString(v)) { return '\"' + v + '\"'; }\n" +
"else if(red._.isFunction(v)) { return '(func)'; }\n" +
"else if(v instanceof red.Pointer) { return stringify_value(v.points_at()); }\n" +
"else if(v instanceof red.PointerValue) { return stringify_value(v.get_pointer()); }\n" +
"else if(v instanceof red.Dict) { return v.uid; }\n" +
"return v;\n" +
"\n" +
"}")
.set("ambiguous_view", "<stateful>")
.cd("ambiguous_view")
	.set("(protos)", "INIT", "(obj instanceof red.StatefulObj ? [dict_view] : (obj instanceof red.Dict ? [dict_view] : (obj instanceof red.StatefulProp ? [stateful_prop_view] : (obj instanceof red.Cell ? [cell_view] : []))))")
	.set("obj", "INIT", "pointer ? pointer.points_at() : false")
	.up()
.set("cell_view", "<stateful>")
.cd("cell_view")
	.rename_state("INIT", "idle")
	.add_state("editing")
	.add_transition("idle", "editing", "on('mousedown', this)")
	.add_transition("editing", "idle", "on('keydown', this).when_eq('keyCode', 13)") //enter
	.add_transition("editing", "idle", "on('keydown', this).when_eq('keyCode', 27)") //esc
	.set("(protos)", "idle", "[dom]")
	.set("text")
	.set("text", "idle", "obj.get_str()")
	.set("tag")
	.set("tag", "idle", "'span'")
	.set("tag", "editing", "'input'")
	.set("attr", "<dict>")
	.cd("attr")
		.set("type", "editing", "'text'")
		.set("value", "idle->editing", "obj.get_str()")
		.up()
	.on_state("editing -0> idle", "function(event) {\n" +
		"var text = event.target.value;\n" +
		"var command = new red.ChangeCellCommand({\n" +
			"cell: obj,\n" +
			"str: text\n" +
		"});\n" +
		"post_command(command);\n" +
		"\n" +
	"}")
	.up()
.set("stateful_prop_view", "<stateful>")
.cd("stateful_prop_view")
	.set("(protos)", "INIT", "[dom]")
	.set("tag")
	.set("tag", "INIT", "'span'")
	.set("children", "<dict>")
	.cd("children")
		.set("cview", "<stateful>")
		.cd("cview")
			.set("(manifestations)", "parent.parent.obj.get_value_specs(pointer)")
			.set("(protos)", "[ambiguous_view]")
			.set("obj", "basis.value")
			.up()
		.up()
	.up()
.set("statechart_view", "<stateful>")
.cd("statechart_view")
	.set("(protos)", "INIT", "[dom]")
	.set("tag")
	.set("tag", "INIT", "'div'")
	.set("children", "<dict>")
	.cd("children")
		.set("root_state", "<stateful>")
		.cd("root_state")
			.set("state", "INIT", "statechart")
			.set("(protos)", "INIT", "[state_view]")
			.up()
		.up()
	.up()
.set("state_view", "<stateful>")
.cd("state_view")
	.set("(protos)", "INIT", "[dom]")
	.set("tag")
	.set("tag", "INIT", "'span'")
	.set("children", "<dict>")
	.cd("children")
		.set("name_view", "<dict>")
		.cd("name_view")
			.set("(protos)", "[dom]")
			.set("tag")
			.set("tag", "'span'")
			.set("text")
			.set("text", "state.get_name('parent')")
			.up()
		.set("substate_view", "<stateful>")
		.cd("substate_view")
			.set("(manifestations)", "parent.parent.state.get_substates()")
			.set("state", "INIT", "basis")
			.set("(protos)", "INIT", "state ? [state_view] : []")
			.up()
		.up()
	.up()
.set("dict_view", "<stateful>")
.cd("dict_view")
	.set("(protos)", "INIT", "[dom]")
	.set("dict", "INIT", "pointer.points_at()")
	.set("children", "<dict>")
	.cd("children")
		.set("scv", "<stateful>")
		.cd("scv")
			.set("(protos)", "INIT", "dict instanceof red.StatefulObj ? [statechart_view] : []")
			.set("statechart", "INIT", "dict.get_own_statechart()")
			.up()
		.set("child_view", "<stateful>")
		.cd("child_view")
			.set("(manifestations)", "dict.get_prop_names(pointer)")
			.set("(protos)", "INIT", "[dom]")
			.set("name", "INIT", "basis")
			.set("value", "INIT", "dict.prop_val(name, pointer)")
			.set("prop_pointer", "INIT", "dict.get_prop_pointer(name, pointer)")
			.set("children", "<dict>")
			.cd("children")
				.set("prop_ops", "<stateful>")
				.cd("prop_ops")
					.set("(protos)", "INIT", "[dom]")
					.set("tag", "<stateful_prop>")
					.set("tag", "INIT", "'span'")
					.set("children", "<dict>")
					.cd("children")
						.set("remove", "<stateful>")
						.cd("remove")
							.set("(protos)", "INIT", "[dom]")
							.add_transition("INIT", "INIT", "on('click', this)")
							.set("tag")
							.set("tag", "INIT", "'a'")
							.set("text")
							.set("text", "INIT", "'(-)'")
							.set("attr", "<dict>")
							.cd("attr")
								.set("href", "'javascript:void(0)'")
								.up()
							.on_state("INIT -> INIT", "function() {\n" +
							"var command = new red.UnsetPropCommand({\n" +
								"parent: dict,\n" +
								"name: name\n" +
							"});\n" +
							"post_command(command);\n" +
							"\n" +
							"}")
							.up()
						.up()
					.up()
				.set("prop_name_view", "<stateful>")
				.cd("prop_name_view")
					.set("(protos)", "INIT", "[dom]")
					.set("tag", "<stateful_prop>")
					.set("tag", "INIT", "'span'")
					.set("text", "<stateful_prop>")
					.set("text", "INIT", "name")
					.set("attr", "<dict>")
					.cd("attr")
						.set("class", "INIT", "'prop_name'")
						.up()
					.set("css", "<dict>")
					.cd("css")
						.set("display", "'inline-block'")
						.set("width", "'90px'")
						.up()
					.up()
				.set("prop_value_view", "<stateful>")
				.cd("prop_value_view")
					.set("(protos)", "INIT", "[dom]")
					.set("tag", "<stateful_prop>")
					.set("tag", "INIT", "'span'")
					.set("text", "<stateful_prop>")
					.set("text", "INIT", "stringify_value(value)")
					.set("attr", "<dict>")
					.cd("attr")
						.set("class", "INIT", "'prop_value'")
						.up()
					.up()
				.set("prop_src_view", "<stateful>")
				.cd("prop_src_view")
					.set("pointer", "<stateful_prop>")
					.set("pointer", "INIT", "parent.parent.prop_pointer")
					.set("(protos)", "INIT", "[ambiguous_view]")
					.set("css", "<dict>")
					.cd("css")
						.set("padding-left", "'25px'")
						.up()
					.up()
				.up()
			.up()
		.up()
	.up()
.cd("children")
	.set("obj", "<stateful>")
	.cd("obj")
		.set("pointer", "<stateful_prop>")
		.set("pointer", "INIT", "external_root_pointer")
		.set("(protos)", "INIT", "ambiguous_view")
		.set("css", "<dict>")
		.cd("css")
			.set("font-family", "'Courier New, monospace'")
			.set("font-size", "'0.8em'")
			.up()
		.up()
	.up()
.set("post_command", _.bind(function(command) {
	this.client_socket.post_command(command);
}, this))
;

// ===== END EDITOR =====
		if(this.option("debug_env")) {
			this.env.print_on_return = true;
		}
	}

	, _destroy: function() {
		this.remove_message_listener();
		this.client_socket.destroy();
		if(this.command_box) {
			this.command_box.remove();
		}
	}

	, on_input_command: function(value) {
		var tokens = aware_split(value).map(function(token) {
			return token.trim();
		});
		var command;
		if((["undo", "redo", "reset"]).indexOf(tokens[0]) >= 0) {
			command = tokens[0];
			this.client_socket.post_command(command);
		} else {
			var external_env = this.client_socket.get_external_env();
			command = external_env[tokens[0]].apply(external_env, _.rest(tokens));
			if(command instanceof red.Command) {
				this.client_socket.post_command(command);
			}
		}
	}
});

}(red, jQuery));
<commit_msg>added add property button<commit_after>(function(red, $) {
var cjs = red.cjs, _ = red._;
var origin = window.location.protocol + "//" + window.location.host;

$.widget("red.editor", {

	options: {
		debug_ready: false,
		debug_env: false,
		command_box: false,
		server_window: window.opener
	}

	, _create: function() {
		if(this.options.command_box) {
			this.command_box = $("<input />")	.prependTo(this.element.parent())
												.focus()
												.on("keydown", _.bind(function(event) {
													if(event.keyCode === 13) {
														var val = this.command_box.val();
														this.command_box.val("");
														this.on_input_command(val);
													}
												}, this));
		}

		this.env = red.create("environment");
		if(this.option("debug_env")) {
			window.env = this.env;
		}
		var root_pointer = this.env.get_root_pointer();
		this.root = root_pointer.root();
		this.element.dom_output({
			root: root_pointer
		});

		this.client_socket = new red.ProgramStateClient({
			ready_func: this.option("debug_ready"),
			server_window: this.option("server_window")
		}).on("message", function(data) {
			if(data.type === "color") {
				var color = data.value;
				$("html").css({
					"border-bottom": "10px solid " + color,
					height: "100%",
					"box-sizing": "border-box"
				});
			}
		}, this).on("root_loaded", function(root) {
			this.root.set("external_root", root, {literal: true});
			this.root.set("external_root_pointer", red.create("pointer", {stack: [root]}), {literal: true});
			this.load_viewer();
		}, this);
	}

	, load_viewer: function() {
		this.env

// ===== BEGIN EDITOR =====

.top()
.set("stringify_value", "function(v) {\n" +
"if(red._.isUndefined(v)) { return '(undefined)'; }\n" +
"else if(red._.isNull(v)) { return '(null)'; }\n" +
"else if(red._.isNumber(v) || red._.isBoolean(v)) { return v+''; }\n" +
"else if(red._.isString(v)) { return '\"' + v + '\"'; }\n" +
"else if(red._.isFunction(v)) { return '(func)'; }\n" +
"else if(v instanceof red.Pointer) { return stringify_value(v.points_at()); }\n" +
"else if(v instanceof red.PointerValue) { return stringify_value(v.get_pointer()); }\n" +
"else if(v instanceof red.Dict) { return v.uid; }\n" +
"return v;\n" +
"\n" +
"}")
.set("ambiguous_view", "<stateful>")
.cd("ambiguous_view")
	.set("(protos)", "INIT", "(obj instanceof red.StatefulObj ? [dict_view] : (obj instanceof red.Dict ? [dict_view] : (obj instanceof red.StatefulProp ? [stateful_prop_view] : (obj instanceof red.Cell ? [cell_view] : []))))")
	.set("obj", "INIT", "pointer ? pointer.points_at() : false")
	.up()
.set("cell_view", "<stateful>")
.cd("cell_view")
	.rename_state("INIT", "idle")
	.add_state("editing")
	.add_transition("idle", "editing", "on('mousedown', this)")
	.add_transition("editing", "idle", "on('keydown', this).when_eq('keyCode', 13)") //enter
	.add_transition("editing", "idle", "on('keydown', this).when_eq('keyCode', 27)") //esc
	.set("(protos)", "idle", "[dom]")
	.set("text")
	.set("text", "idle", "obj.get_str()")
	.set("tag")
	.set("tag", "idle", "'span'")
	.set("tag", "editing", "'input'")
	.set("attr", "<dict>")
	.cd("attr")
		.set("type", "editing", "'text'")
		.set("value", "idle->editing", "obj.get_str()")
		.up()
	.on_state("editing -0> idle", "function(event) {\n" +
		"var text = event.target.value;\n" +
		"var command = new red.ChangeCellCommand({\n" +
			"cell: obj,\n" +
			"str: text\n" +
		"});\n" +
		"post_command(command);\n" +
		"\n" +
	"}")
	.up()
.set("stateful_prop_view", "<stateful>")
.cd("stateful_prop_view")
	.set("(protos)", "INIT", "[dom]")
	.set("tag")
	.set("tag", "INIT", "'span'")
	.set("children", "<dict>")
	.cd("children")
		.set("cview", "<stateful>")
		.cd("cview")
			.set("(manifestations)", "parent.parent.obj.get_value_specs(pointer)")
			.set("(protos)", "[ambiguous_view]")
			.set("obj", "basis.value")
			.up()
		.up()
	.up()
.set("statechart_view", "<stateful>")
.cd("statechart_view")
	.set("(protos)", "INIT", "[dom]")
	.set("tag")
	.set("tag", "INIT", "'div'")
	.set("children", "<dict>")
	.cd("children")
		.set("root_state", "<stateful>")
		.cd("root_state")
			.set("state", "INIT", "statechart")
			.set("(protos)", "INIT", "[state_view]")
			.up()
		.up()
	.up()
.set("state_view", "<stateful>")
.cd("state_view")
	.set("(protos)", "INIT", "[dom]")
	.set("tag")
	.set("tag", "INIT", "'span'")
	.set("children", "<dict>")
	.cd("children")
		.set("name_view", "<dict>")
		.cd("name_view")
			.set("(protos)", "[dom]")
			.set("tag")
			.set("tag", "'span'")
			.set("text")
			.set("text", "state.get_name('parent')")
			.up()
		.set("substate_view", "<stateful>")
		.cd("substate_view")
			.set("(manifestations)", "parent.parent.state.get_substates()")
			.set("state", "INIT", "basis")
			.set("(protos)", "INIT", "state ? [state_view] : []")
			.up()
		.up()
	.up()
.set("dict_view", "<stateful>")
.cd("dict_view")
	.set("(protos)", "INIT", "[dom]")
	.set("dict", "INIT", "pointer.points_at()")
	.set("children", "<dict>")
	.cd("children")
		.set("scv", "<stateful>")
		.cd("scv")
			.set("(protos)", "INIT", "dict instanceof red.StatefulObj ? [statechart_view] : []")
			.set("statechart", "INIT", "dict.get_own_statechart()")
			.up()
		.set("child_view", "<stateful>")
		.cd("child_view")
			.set("(manifestations)", "dict.get_prop_names(pointer)")
			.set("(protos)", "INIT", "[dom]")
			.set("name", "INIT", "basis")
			.set("value", "INIT", "dict.prop_val(name, pointer)")
			.set("prop_pointer", "INIT", "dict.get_prop_pointer(name, pointer)")
			.set("children", "<dict>")
			.cd("children")
				.set("prop_ops", "<stateful>")
				.cd("prop_ops")
					.set("(protos)", "INIT", "[dom]")
					.set("tag", "<stateful_prop>")
					.set("tag", "INIT", "'span'")
					.set("children", "<dict>")
					.cd("children")
						.set("remove", "<stateful>")
						.cd("remove")
							.set("(protos)", "INIT", "[dom]")
							.add_transition("INIT", "INIT", "on('click', this)")
							.set("tag")
							.set("tag", "INIT", "'a'")
							.set("text")
							.set("text", "INIT", "'(-)'")
							.set("attr", "<dict>")
							.cd("attr")
								.set("href", "'javascript:void(0)'")
								.up()
							.on_state("INIT -> INIT", "function() {\n" +
							"var command = new red.UnsetPropCommand({\n" +
								"parent: dict,\n" +
								"name: name\n" +
							"});\n" +
							"post_command(command);\n" +
							"\n" +
							"}")
							.up()
						.up()
					.up()
				.set("prop_name_view", "<stateful>")
				.cd("prop_name_view")
					.set("(protos)", "INIT", "[dom]")
					.set("tag", "<stateful_prop>")
					.set("tag", "INIT", "'span'")
					.set("text", "<stateful_prop>")
					.set("text", "INIT", "name")
					.set("attr", "<dict>")
					.cd("attr")
						.set("class", "INIT", "'prop_name'")
						.up()
					.set("css", "<dict>")
					.cd("css")
						.set("display", "'inline-block'")
						.set("width", "'90px'")
						.up()
					.up()
				.set("prop_value_view", "<stateful>")
				.cd("prop_value_view")
					.set("(protos)", "INIT", "[dom]")
					.set("tag", "<stateful_prop>")
					.set("tag", "INIT", "'span'")
					.set("text", "<stateful_prop>")
					.set("text", "INIT", "stringify_value(value)")
					.set("attr", "<dict>")
					.cd("attr")
						.set("class", "INIT", "'prop_value'")
						.up()
					.up()
				.set("prop_src_view", "<stateful>")
				.cd("prop_src_view")
					.set("pointer", "<stateful_prop>")
					.set("pointer", "INIT", "parent.parent.prop_pointer")
					.set("(protos)", "INIT", "[ambiguous_view]")
					.set("css", "<dict>")
					.cd("css")
						.set("padding-left", "'25px'")
						.up()
					.up()
				.up()
			.up()
		.set("dict_ops", "<stateful>")
		.cd("dict_ops")
			.set("(protos)", "INIT", "[dom]")
			.add_transition("INIT", "INIT", "on('click', this)")
			.set("tag")
			.set("tag", "INIT", "'a'")
			.set("text")
			.set("text", "INIT", "'(+ add property)'")
			.set("attr", "<dict>")
			.cd("attr")
				.set("href", "'javascript:void(0)'")
				.up()
			.on_state("INIT -> INIT", "function() {\n" +
			"var prop_name = 'prop_' + dict.get_prop_names(pointer).length;\n" +
			"var orig_prop_name = prop_name;\n" +
			"var i = 0;\n" +
			"while(dict._has_prop(prop_name, pointer)) {\n" +
			"prop_name = orig_prop_name+'_'+i;\n" +
			"i++\n" +
			"}\n" +
			"var command = new red.SetPropCommand({\n" +
			"parent: dict,\n" +
			"name: prop_name,\n" +
			"value: red.create('cell')\n" +
			"});\n" +
			"post_command(command);\n" +
			"}")
			.up()
		.up()
	.up()
.cd("children")
	.set("obj", "<stateful>")
	.cd("obj")
		.set("pointer", "<stateful_prop>")
		.set("pointer", "INIT", "external_root_pointer")
		.set("(protos)", "INIT", "ambiguous_view")
		.set("css", "<dict>")
		.cd("css")
			.set("font-family", "'Courier New, monospace'")
			.set("font-size", "'0.8em'")
			.up()
		.up()
	.up()
.set("post_command", _.bind(function(command) {
	this.client_socket.post_command(command);
}, this))
;

// ===== END EDITOR =====
		if(this.option("debug_env")) {
			this.env.print_on_return = true;
		}
	}

	, _destroy: function() {
		this.remove_message_listener();
		this.client_socket.destroy();
		if(this.command_box) {
			this.command_box.remove();
		}
	}

	, on_input_command: function(value) {
		var tokens = aware_split(value).map(function(token) {
			return token.trim();
		});
		var command;
		if((["undo", "redo", "reset"]).indexOf(tokens[0]) >= 0) {
			command = tokens[0];
			this.client_socket.post_command(command);
		} else {
			var external_env = this.client_socket.get_external_env();
			command = external_env[tokens[0]].apply(external_env, _.rest(tokens));
			if(command instanceof red.Command) {
				this.client_socket.post_command(command);
			}
		}
	}
});

}(red, jQuery));
<|endoftext|>


<commit_before>HNSpecial.settings.registerModule("infinite_scrolling", function () {
  function getThreshold() {
    return window.scrollY + button.getBoundingClientRect().bottom + 50; // getBoundingClientRect returns coordinates relative to the viewport
  }

  function getButton(context) {
    return _.$("td.title > a[href^='/x'], td.title > a[href^='news2']", context)[0];
  }

  function replaceButton(message) {
    button.textContent = message;
    button.nextSibling.remove(); // Remove the pause button
    _.replaceTag(button, "span");
    disabled = true;
  }

  function pauseLoading(e) {
    if (e) e.preventDefault();
    disabled = !disabled;
    pause.textContent = labels[disabled ? 1 : 0];
    checkScroll();
  }

  function checkScroll() {
    if (!disabled && window.scrollY + window.innerHeight > threshold) {
      loadLinks();

      var nextLoads = loads - 3;
      if (loads === 3 || (nextLoads > 0 && nextLoads % 5 === 0)) pauseLoading();
    }
  }

  function loadLinks() {
    if (loading) return;
    loading = true;
    loads++;

    var label = button.textContent;
    button.textContent = "Loading more items...";

    var last = button.parentElement.parentElement.previousSibling;
    var container = last.parentElement;
    var url = button.getAttribute("href");

    _.request(url, "GET", function (page) {
      var dummy = _.createElement("div");
      dummy.innerHTML = page;

      if (dummy.getElementsByClassName("title").length) {
        _.toArray(dummy.getElementsByTagName("a")).forEach(function (link) {
          if (_.isTitleLink(link)) {
            var row = link.parentElement.parentElement;
            var sub = row.nextSibling;
            var empty = sub.nextSibling;

            container.insertBefore(row, last);
            container.insertBefore(sub, last);
            container.insertBefore(empty, last);
          }
        });

        var newButton = getButton(dummy);

        if (newButton) {
          button.textContent = label;
          button.setAttribute("href", newButton.getAttribute("href"));
          threshold = getThreshold();
        } else {
          replaceButton("No more links to load.");
        }

        loading = false;
        HNSpecial.settings.emit("new links"); // Notify other modules about the presence of new links

      } else {
        replaceButton("Couldn't load the page. Please try refreshing.");
      }
    });
  }

  // Set up the thing
  var button = getButton();

  if (_.isListingPage() && button) {
    var threshold = getThreshold();

    var loading = false;
    var loads = 0;
    var disabled = false;

    var labels = ["Pause infinite scrolling", "Resume infinite scrolling"];

    var pause = _.createElement("a", {
      content: labels[0],
      classes: ["hnspecial-infinite-pause"],
      attributes: {
        "href": "#"
      }
    });
    pause.addEventListener("click", pauseLoading);
    button.parentElement.appendChild(pause);

    button.addEventListener("click", function (e) {
      e.preventDefault();
      loadLinks();
    });

    document.addEventListener("scroll", checkScroll);
  }
});
<commit_msg>[#5] move variable definition outside<commit_after>// The code in this module is a bit ugly. It could use a rewrite.
HNSpecial.settings.registerModule("infinite_scrolling", function () {
  function getThreshold() {
    return window.scrollY + button.getBoundingClientRect().bottom + 50; // getBoundingClientRect returns coordinates relative to the viewport
  }

  function getButton(context) {
    return _.$("td.title > a[href^='/x'], td.title > a[href^='news2']", context)[0];
  }

  function replaceButton(message) {
    button.textContent = message;
    button.nextSibling.remove(); // Remove the pause button
    _.replaceTag(button, "span");
    disabled = true;
  }

  function pauseLoading(e) {
    if (e) e.preventDefault();
    disabled = !disabled;
    pause.textContent = labels[disabled ? 1 : 0];
    checkScroll();
  }

  function checkScroll() {
    if (!disabled && window.scrollY + window.innerHeight > threshold) {
      loadLinks();

      nextLoads = loads - 3;
      if (loads === 3 || (nextLoads > 0 && nextLoads % 5 === 0)) pauseLoading();
    }
  }

  function loadLinks() {
    if (loading) return;
    loading = true;
    loads++;

    var label = button.textContent;
    button.textContent = "Loading more items...";

    var last = button.parentElement.parentElement.previousSibling;
    var container = last.parentElement;
    var url = button.getAttribute("href");

    _.request(url, "GET", function (page) {
      var dummy = _.createElement("div");
      dummy.innerHTML = page;

      if (dummy.getElementsByClassName("title").length) {
        _.toArray(dummy.getElementsByTagName("a")).forEach(function (link) {
          if (_.isTitleLink(link)) {
            var row = link.parentElement.parentElement;
            var sub = row.nextSibling;
            var empty = sub.nextSibling;

            container.insertBefore(row, last);
            container.insertBefore(sub, last);
            container.insertBefore(empty, last);
          }
        });

        var newButton = getButton(dummy);

        if (newButton) {
          button.textContent = label;
          button.setAttribute("href", newButton.getAttribute("href"));
          threshold = getThreshold();
        } else {
          replaceButton("No more links to load.");
        }

        loading = false;
        HNSpecial.settings.emit("new links"); // Notify other modules about the presence of new links

      } else {
        replaceButton("Couldn't load the page. Please try refreshing.");
      }
    });
  }

  // Set up the thing
  var button = getButton();

  if (_.isListingPage() && button) {
    var threshold = getThreshold();

    var loading = false;
    var loads = 0;
    var nextLoads;
    var disabled = false;

    var labels = ["Pause infinite scrolling", "Resume infinite scrolling"];

    var pause = _.createElement("a", {
      content: labels[0],
      classes: ["hnspecial-infinite-pause"],
      attributes: {
        "href": "#"
      }
    });
    pause.addEventListener("click", pauseLoading);
    button.parentElement.appendChild(pause);

    button.addEventListener("click", function (e) {
      e.preventDefault();
      loadLinks();
    });

    document.addEventListener("scroll", checkScroll);
  }
});
<|endoftext|>


<commit_before>'use strict'
import * as React from 'react';

//import * as _ from "lodash";

import Swipe from "components/swipe/swipe";

import "./list.less";

import {fetchPosts} from "components/common/fetch";
import NoOrder from "./noOrder";
import Info from "./info";
import { eventFun } from 'libs/util'

import Modal from "components/modal/index";
import PopUp from "components/popup/index";

const RebateStatus ={
    "-1":"记录订单",
    0:"待返宝券",
    1:"已返宝券",
    2:"返宝券被收回",
    3:"返宝券失败"
}
const RebateStatusShort={
    "-1":"记录",
    0:"待返",
    1:"已返",
    2:"",
    3:""
}

class OrderList extends React.Component {
    constructor(props) {
        super(props);
        this.state = {
            items:[],
            page:0,
            isLoading:true,
            oneHeight:false,
            isEnd:false,
        }

        this.getData = this.getData.bind(this);
        this.touchMove = this.touchMove.bind(this);

        this.handClick = this.handClick.bind(this);
        this.doDel = this.doDel.bind(this);
        this.toInfo = this.toInfo.bind(this);

    }
    componentWillMount() {

        this.getData(1);
    }
    getData(num){
        let {pageSize,url,searchParam,upData} = this.props;
        let {page,items,isLoading,isEnd} = this.state;
        if((page!==0&&isLoading===true)||(isEnd)){
            return;
        }
        this.setState({
            isLoading:true
        })
        let _this = this;
        let param = Object.assign({},searchParam,{page,size:pageSize})
        page += num;
        return fetchPosts(url,param,"GET").then((data)=>{
                console.log(data);
                if(data.responseCode===1000){
                    _this.setState({
                        isLoading:false,
                        page,
                        isEnd:data.data.length<pageSize?true:false,
                        items:items.concat(data.data)
                    });
                    upData(data);
                }else{
                     _this.setState({
                        isLoading:false,});
                }

         }).catch(function(){
                    _this.setState({
                        isLoading:false,});
         });
    }

    touchMove(that,args){
        console.log(that,args);
        let {items} = this.state;
        if(that.min-args[0]>-300){
            this.getData(1);
        }
    }
    doDel(id){
        let _this = this;
        let newId= Number(id);
        Modal.confirm("删除","确认删除吗？").then((data)=>{
            console.log(data);
            if(data!=="Ok"){
                return;
            }
            fetchPosts("/stuff/order/delOrder.do",{"id":id},"GET").then((data)=>{
                    if(data.responseCode===1000){
                    let {items} = _this.state;
                    let i = 0,j = items.length,$lis=[];
                    //debugger
                    while(i<j){
                        console.error(items[i])
                        if(items[i].id===newId){
                            items.splice(i,1);
                            break;
                        }
                        i+=1;
                    }
                    Modal.alert("删除","成功");
                    _this.setState({
                        items
                    })
                    }else{
                            Modal.alert("删除","失败");

                    }

            }).catch(function(error){
                    Modal.alert("删除","失败");
            });
        });

    }
    infoClose(){
        PopUp.hide();
    }
    toInfo(id){

            return fetchPosts("/stuff/order/userOrder.do",{id},"GET").then((data)=>{
                    if(data.responseCode===1000){
                    PopUp.show(
                            (<Info data={data.data} onClick={this.infoClose} />),{maskClosable:true}
                    );
                    }else{
                            Modal.alert("查看详情","失败");
                    }

            }).catch(function(){
                    Modal.alert("查看详情","失败");
            });

       // debugger;

       //PopUp.show(
    }
    handClick(event){

        let className = event.target.className;
        let id = event.target.dataset.id;
        console.log("className:",className,"id",id);
        //debugger
        if(className==='js_del'){
            this.doDel(id);
        }else if(className==='js_info'){

            this.toInfo(id);
        }
    }

    render() {

        let {items,isLoading,page,isEnd} = this.state;
        let i =0,j=items.length,$lis = [];
        while(i<j){
            console.log("----");
            let item = items[i],totalPrice=0,totalSb=0;
            i+=1;

            let l=0,m=item.item.length,$subItem=[];
            while(l<m){
                let subItem = item.item[l];
                l+=1;

                totalPrice = (totalPrice*100+subItem.finalPrice*subItem.stuffNum*100)/100;
                totalSb = (totalSb*100+Number(subItem.rebateValue)*100)/100

                //totalPrice += subItem.price*subItem.stuffNum;
                $subItem.push(

{subItem.name}

                        ￥{subItem.finalPrice}

                )
            }
            let rebateStatus = item.rebateStatus;
            let short = RebateStatusShort[rebateStatus];
            $lis.push(

{RebateStatus[rebateStatus]}

                        {$subItem}

共{item.stuffNum}件商品，合计:￥{totalPrice}
                        {rebateStatus<2&&short}
                        {rebateStatus<2&&({totalSb}宝券)}

                        {rebateStatus>0&&(<button data-id={item.id} {...eventFun('109', 'order_remove', item.id)} className="js_del">删除</button>)}
                        <button data-id={item.id} {...eventFun('109', 'order_rebate', item.id)} className="js_info">返券详情</button>

            )
        }

        let props = {
            property:"translateY",
            className:"my-order-list",
            tag:"ul",
            min:"auto",
            stopPro:false,
            vertical:true,
            touchMove:this.touchMove
            //step:200
        }
        if(j===0&&isLoading===false){
            return(
<NoOrder />
)
        }else if(j===0&&page===0){
            return (

)
        }
        //return ({})
        return (
                <Swipe {...props} onClick={this.handClick}>
                    {$lis}
                    {isLoading===true&&(
Loading
)}
                    {page>1&&isEnd===true&&(
已经没有更新了
)}
                </Swipe>
        )
    }
};

OrderList.defaultProps = {
    pageSize:20,
    url:"/stuff/order/list.do",
    searchParam:{}
}

module.exports = OrderList;
//<button data-id={item.id} className="js_del">删除</button><commit_msg>point 2.0 update<commit_after>'use strict'
import * as React from 'react';

//import * as _ from "lodash";

import Swipe from "components/swipe/swipe";

import "./list.less";

import {fetchPosts} from "components/common/fetch";
import NoOrder from "./noOrder";
import Info from "./info";
import { eventFun } from 'libs/util'

import Modal from "components/modal/index";
import PopUp from "components/popup/index";

const RebateStatus ={
    "-1":"记录订单",
    0:"待返宝券",
    1:"已返宝券",
    2:"返宝券被收回",
    3:"返宝券失败"
}
const RebateStatusShort={
    "-1":"记录",
    0:"待返",
    1:"已返",
    2:"",
    3:""
}

class OrderList extends React.Component {
    constructor(props) {
        super(props);
        this.state = {
            items:[],
            page:0,
            isLoading:true,
            oneHeight:false,
            isEnd:false,
        }

        this.getData = this.getData.bind(this);
        this.touchMove = this.touchMove.bind(this);

        this.handClick = this.handClick.bind(this);
        this.doDel = this.doDel.bind(this);
        this.toInfo = this.toInfo.bind(this);

    }
    componentWillMount() {

        this.getData(1);
    }
    getData(num){
        let {pageSize,url,searchParam,upData} = this.props;
        let {page,items,isLoading,isEnd} = this.state;
        if((page!==0&&isLoading===true)||(isEnd)){
            return;
        }
        this.setState({
            isLoading:true
        })
        let _this = this;
        let param = Object.assign({},searchParam,{page,size:pageSize})
        page += num;
        return fetchPosts(url,param,"GET").then((data)=>{
                console.log(data);
                if(data.responseCode===1000){
                    _this.setState({
                        isLoading:false,
                        page,
                        isEnd:data.data.length<pageSize?true:false,
                        items:items.concat(data.data)
                    });
                    upData(data);
                }else{
                     _this.setState({
                        isLoading:false,});
                }

         }).catch(function(){
                    _this.setState({
                        isLoading:false,});
         });
    }

    touchMove(that,args){
        console.log(that,args);
        let {items} = this.state;
        if(that.min-args[0]>-300){
            this.getData(1);
        }
    }
    doDel(id){
        let _this = this;
        let newId= Number(id);
        Modal.confirm("删除","确认删除吗？").then((data)=>{
            console.log(data);
            if(data!=="Ok"){
                return;
            }
            fetchPosts("/stuff/order/delOrder.do",{"id":id},"GET").then((data)=>{
                    if(data.responseCode===1000){
                    let {items} = _this.state;
                    let i = 0,j = items.length,$lis=[];
                    //debugger
                    while(i<j){
                        console.error(items[i])
                        if(items[i].id===newId){
                            items.splice(i,1);
                            break;
                        }
                        i+=1;
                    }
                    Modal.alert("删除","成功");
                    _this.setState({
                        items
                    })
                    }else{
                            Modal.alert("删除","失败");

                    }

            }).catch(function(error){
                    Modal.alert("删除","失败");
            });
        });

    }
    infoClose(){
        PopUp.hide();
    }
    toInfo(id){

            return fetchPosts("/stuff/order/userOrder.do",{id},"GET").then((data)=>{
                    if(data.responseCode===1000){
                    PopUp.show(
                            (<Info data={data.data} onClick={this.infoClose} />),{maskClosable:true}
                    );
                    }else{
                            Modal.alert("查看详情","失败");
                    }

            }).catch(function(){
                    Modal.alert("查看详情","失败");
            });

       // debugger;

       //PopUp.show(
    }
    handClick(event){

        let className = event.target.className;
        let id = event.target.dataset.id;
        console.log("className:",className,"id",id);
        //debugger
        if(className==='js_del'){
            this.doDel(id);
        }else if(className==='js_info'){

            this.toInfo(id);
        }
    }

    render() {

        let {items,isLoading,page,isEnd} = this.state;
        let i =0,j=items.length,$lis = [];
        while(i<j){
            console.log("----");
            let item = items[i],totalPrice=0,totalSb=0;
            i+=1;

            let l=0,m=item.item.length,$subItem=[];
            while(l<m){
                let subItem = item.item[l];
                l+=1;

                totalPrice = (totalPrice*100+subItem.finalPrice*subItem.stuffNum*100)/100;
                totalSb = (totalSb*100+Number(subItem.rebateValue)*100)/100

                //totalPrice += subItem.price*subItem.stuffNum;
                $subItem.push(

{subItem.name}

                        ￥{subItem.finalPrice}

                )
            }
            let rebateStatus = item.rebateStatus;
            let short = RebateStatusShort[rebateStatus];
            $lis.push(

{RebateStatus[rebateStatus]}

                        {$subItem}

共{item.stuffNum}件商品，合计:￥{totalPrice}
                        {rebateStatus<2&&short}
                        {rebateStatus<2&&({totalSb}宝券)}

                        {rebateStatus>0&&(<button data-id={item.id} {...eventFun('109', 'order_remove', item.id)} className="js_del">删除</button>)}
                        <button data-id={item.id} {...eventFun('109', 'order_rebate', item.id)} className="js_info">返券详情</button>

            )
        }

        let props = {
            property:"translateY",
            className:"my-order-list",
            tag:"ul",
            min:"auto",
            stopPro:false,
            vertical:true,
            touchMove:this.touchMove
            //step:200
        }
        if(j===0&&isLoading===false){
            return(
<NoOrder />
)
        }else if(j===0&&page===0){
            return (

)
        }
        //return ({})
        return (
                <Swipe {...props} onClick={this.handClick}>
                    {$lis}
                    {isLoading===true&&(
Loading
)}
                    {page>1&&isEnd===true&&(
已经没有更新了
)}
                </Swipe>
        )
    }
};

OrderList.defaultProps = {
    pageSize:20,
    url:"/stuff/order/list.do",
    searchParam:{}
}

module.exports = OrderList;
//<button data-id={item.id} className="js_del">删除</button><|endoftext|>


<commit_before>import React from 'react';

import NavMain from './NavMain';
import PageHeader from './PageHeader';
import PageFooter from './PageFooter';

const Page = React.createClass({
  render() {
    return (

          <NavMain activePage="getting-started" />

          <PageHeader
            title="Getting started"
            subTitle="An overview of React-Bootstrap and how to install and use." />

Setup

You can import the lib as AMD modules, CommonJS modules, or as a global JS script.

First add the Bootstrap CSS to your project; check here if you have not already done that. Then:

CommonJS

{`
  $ npm install react
  $ npm install react-bootstrap
                    `}

{`
  var Alert = require('react-bootstrap/lib/Alert');
  // or
  var Alert = require('react-bootstrap').Alert;
                    `}

AMD

{`
  $ bower install react
  $ bower install react-bootstrap
                    `}

{`
  define(['react-bootstrap/lib/Alert'], function(Alert) { ... });
  // or
  define(['react-bootstrap'], function(ReactBootstrap) { var Alert = ReactBootstrap.Alert; ... });
                    `}

Browser globals

The bower repo contains react-bootstrap.js and react-bootstrap.min.js with all components exported in the window.ReactBootstrap object.

{`

                    `}

Browser support

We aim to support all browsers supported by both React and Bootstrap.

React requires polyfills for non-ES5 capable browsers.

jQuery is currently required only for IE8 support for components which require reading element positions from the DOM: Popover and Tooltip when launched with OverlayTrigger. We would like to remove this dependency in future versions but for now, including the following snippet in your page should have you covered:

{`

                    `}

          <PageFooter />

      );
  },

  shouldComponentUpdate() {
    return false;
  }
});

export default Page;
<commit_msg>Getting Started: document concerns/recommendations on including Bootstrap<commit_after>import React from 'react';

import NavMain from './NavMain';
import PageHeader from './PageHeader';
import PageFooter from './PageFooter';

const Page = React.createClass({
  render() {
    return (

          <NavMain activePage="getting-started" />

          <PageHeader
            title="Getting started"
            subTitle="An overview of React-Bootstrap and how to install and use." />

Setup

You can import the lib as AMD modules, CommonJS modules, or as a global JS script.

First add the Bootstrap CSS to your project; check here if you have not already done that. Note that:

Because many folks use custom bootstrap themes, we do not directly depend on Bootstrap. It is up to you to to determine how you get and link to the bootstrap css and fonts.

React-Bootstrap doesn't really depend on any bootstrap version (unless there are big breaking changes in the main lib, which are pretty rare). Just have bootstrap in your dependencies and ensure your build can read your less/sass/scss entry point.

Then:

CommonJS

{`
  $ npm install react
  $ npm install react-bootstrap
                    `}

{`
  var Alert = require('react-bootstrap/lib/Alert');
  // or
  var Alert = require('react-bootstrap').Alert;
                    `}

AMD

{`
  $ bower install react
  $ bower install react-bootstrap
                    `}

{`
  define(['react-bootstrap/lib/Alert'], function(Alert) { ... });
  // or
  define(['react-bootstrap'], function(ReactBootstrap) { var Alert = ReactBootstrap.Alert; ... });
                    `}

Browser globals

The bower repo contains react-bootstrap.js and react-bootstrap.min.js with all components exported in the window.ReactBootstrap object.

{`

                    `}

Browser support

We aim to support all browsers supported by both React and Bootstrap.

React requires polyfills for non-ES5 capable browsers.

jQuery is currently required only for IE8 support for components which require reading element positions from the DOM: Popover and Tooltip when launched with OverlayTrigger. We would like to remove this dependency in future versions but for now, including the following snippet in your page should have you covered:

{`

                    `}

          <PageFooter />

      );
  },

  shouldComponentUpdate() {
    return false;
  }
});

export default Page;
<|endoftext|>


<commit_before>import React                  from 'react';
import { bindActionCreators } from 'redux';
import { connect }            from 'react-redux';
import { Link }               from 'react-router';
import goHomeActions          from 'actions/goHome';
import RaisedButton           from 'material-ui/lib/raised-button';

const mapStateToProps = (state) => ({
  goHome : state.goHome,
  routerState : state.router
});
const mapDispatchToProps = (dispatch) => ({
  actions : bindActionCreators(goHomeActions, dispatch)
});
export class ResumeView extends React.Component {
  static propTypes = {
    actions : React.PropTypes.object
  }

  render () {
    return (

is this thing on?

        <RaisedButton label='This button does nothing' onClick={this.props.actions.goHome} />

        <Link to='/'>but this link will take you back to the counter</Link>

    );
  }
}

export default connect(mapStateToProps, mapDispatchToProps)(ResumeView);
<commit_msg>Feat: Now has a linkedin button<commit_after>import React                  from 'react';
import { bindActionCreators } from 'redux';
import { connect }            from 'react-redux';
import { Link }               from 'react-router';
import goHomeActions          from 'actions/goHome';
import RaisedButton           from 'material-ui/lib/raised-button';

const mapStateToProps = (state) => ({
  goHome : state.goHome,
  routerState : state.router
});
const mapDispatchToProps = (dispatch) => ({
  actions : bindActionCreators(goHomeActions, dispatch)
});
export class ResumeView extends React.Component {
  static propTypes = {
    actions : React.PropTypes.object
  }
  authorize() {}
  render () {
    const linkedin = require('./Sign-in-Large---Default.png');
    return (

is this thing on?

        <RaisedButton label='This button does nothing' onClick={this.props.actions.goHome} />

        <Link to='/'>but this link will take you back to the counter</Link>

    );
  }
}

export default connect(mapStateToProps, mapDispatchToProps)(ResumeView);
<|endoftext|>


<commit_before>var inherits = require('inherits');
var EventEmitter = require('events').EventEmitter;
var bncode = require('bncode');

inherits(MetadataExtension, process.EventEmitter);
function MetadataExtension(wire) {
    if (!(this instanceof MetadataExtension)) return new MetadataExtension();

    this.send = function(info, trailer) {
        var buf = bncode.encode(info);
        if (Buffer.isBuffer(trailer))
            buf = Buffer.concat(buf, trailer);
        wire.extended('ut_metadata', buf);
    };

    wire.on('extended-handshake', function(info) {
        var n = 1;
        for(var k in info.m) {
            if (info.m[k] >= n) {
                n = info.m[k] + 1;
            }
        }
        info.m.ut_metadata = n;
    });
    wire.on('extended', function(ext, buf) {
        var info;
        if (ext === 'handshake' &&
            buf.m.ut_metadata
           ) {
            this.metadataSize = buf.metadata_size;
            if (typeof this.metadataSize === 'number')  {
                this.emit('enabled', this.metadataSize);
            }
        } else if (ext === 'ut_metadata') {
            var decoder = new bncode.decoder();
            for(var i = 0; i < buf.length; i++) {
                decoder.decode(buf.slice(i, i + 1));
                try {
                    info = decoder.result()[0];
                    break;
                } catch (e) {
                    if (e.message !== "not in consistent state. More bytes coming?")
                        throw e;
                }
            }

            var trailer = (i < buf.length) &&
                buf.slice(i + 1);
            this._onMessage(info, trailer);
        }
    }.bind(this));
}
module.exports = MetadataExtension;

MetadataExtension.prototype.chunkSize = 16384;

MetadataExtension.prototype._onMessage = function(info, trailer) {
    console.log("MetadataExtension._onMessage", info, trailer && trailer.length)
    switch(info.msg_type) {
    case 0:
        this.emit('request', info);
        break;
    case 1:
        console.log("emit data");
        this.emit('data', info, trailer);
        break;
    case 2:
        this.emit('reject', info);
        break;
    }
};

MetadataExtension.prototype.request = function(piece) {
    this.send({
        msg_type: 0,
        piece: piece
    });
};

MetadataExtension.prototype.data = function(piece, buf) {
    this.send({
        msg_type: 1,
        piece: piece
    }, buf);
};

MetadataExtension.prototype.reject = function(piece) {
    this.send({
        msg_type: 2,
        piece: piece
    });
};
<commit_msg>ext_ut_metadata: rm debug<commit_after>var inherits = require('inherits');
var EventEmitter = require('events').EventEmitter;
var bncode = require('bncode');

inherits(MetadataExtension, process.EventEmitter);
function MetadataExtension(wire) {
    if (!(this instanceof MetadataExtension)) return new MetadataExtension();

    this.send = function(info, trailer) {
        var buf = bncode.encode(info);
        if (Buffer.isBuffer(trailer))
            buf = Buffer.concat(buf, trailer);
        wire.extended('ut_metadata', buf);
    };

    wire.on('extended-handshake', function(info) {
        var n = 1;
        for(var k in info.m) {
            if (info.m[k] >= n) {
                n = info.m[k] + 1;
            }
        }
        info.m.ut_metadata = n;
    });
    wire.on('extended', function(ext, buf) {
        var info;
        if (ext === 'handshake' &&
            buf.m.ut_metadata
           ) {
            this.metadataSize = buf.metadata_size;
            if (typeof this.metadataSize === 'number')  {
                this.emit('enabled', this.metadataSize);
            }
        } else if (ext === 'ut_metadata') {
            var decoder = new bncode.decoder();
            for(var i = 0; i < buf.length; i++) {
                decoder.decode(buf.slice(i, i + 1));
                try {
                    info = decoder.result()[0];
                    break;
                } catch (e) {
                    if (e.message !== "not in consistent state. More bytes coming?")
                        throw e;
                }
            }

            var trailer = (i < buf.length) &&
                buf.slice(i + 1);
            this._onMessage(info, trailer);
        }
    }.bind(this));
}
module.exports = MetadataExtension;

MetadataExtension.prototype.chunkSize = 16384;

MetadataExtension.prototype._onMessage = function(info, trailer) {
    switch(info.msg_type) {
    case 0:
        this.emit('request', info);
        break;
    case 1:
        this.emit('data', info, trailer);
        break;
    case 2:
        this.emit('reject', info);
        break;
    }
};

MetadataExtension.prototype.request = function(piece) {
    this.send({
        msg_type: 0,
        piece: piece
    });
};

MetadataExtension.prototype.data = function(piece, buf) {
    this.send({
        msg_type: 1,
        piece: piece
    }, buf);
};

MetadataExtension.prototype.reject = function(piece) {
    this.send({
        msg_type: 2,
        piece: piece
    });
};
<|endoftext|>


<commit_before>/**
 * Copyright 2015 The Incremental DOM Authors. All Rights Reserved.
 *
 * 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 alignWithDOM = require('./alignment').alignWithDOM;
var updateAttribute = require('./attributes').updateAttribute;
var getData = require('./node_data').getData;
var getWalker = require('./walker').getWalker;
var traversal = require('./traversal'),
    firstChild = traversal.firstChild,
    nextSibling = traversal.nextSibling,
    parentNode = traversal.parentNode;

/**
 * The offset in the virtual element declaration where the attributes are
 * specified.
 * @const
 */
var ATTRIBUTES_OFFSET = 3;

/**
 * Builds an array of arguments for use with elementOpenStart, attr and
 * elementOpenEnd.
 * @type {Array<*>}
 * @const
 */
var argsBuilder = [];

/**
 * Verify if the script are running in production.
 * @type {boolean}
 * @const
 */
var IS_PRODUCTION = process.env.NODE_ENV === 'production';

if (!IS_PRODUCTION) {
  /**
   * Keeps track whether or not we are in an attributes declaration (after
   * elementOpenStart, but before elementOpenEnd).
   * @type {boolean}
   */
  var inAttributes = false;

  /** Makes sure that the caller is not where attributes are expected. */
  var assertNotInAttributes = function() {
    if (inAttributes) {
      throw new Error('Was not expecting a call to attr or elementOpenEnd, ' +
          'they must follow a call to elementOpenStart.');
    }
  };

  /** Makes sure that the caller is where attributes are expected. */
  var assertInAttributes = function() {
    if (!inAttributes) {
      throw new Error('Was expecting a call to attr or elementOpenEnd. ' +
          'elementOpenStart must be followed by zero or more calls to attr, ' +
          'then one call to elementOpenEnd.');
    }
  };

  /** Updates the state to being in an attribute declaration. */
  var setInAttributes = function() {
    inAttributes = true;
  };

  /** Updates the state to not being in an attribute declaration. */
  var setNotInAttributes = function() {
    inAttributes = false;
  };
}

/**
 * Checks to see if one or more attributes have changed for a given
 * Element. When no attributes have changed, this function is much faster than
 * checking each individual argument. When attributes have changed, the overhead
 * of this function is minimal.
 *
 * This function is called in the context of the Element and the arguments from
 * elementOpen-like function so that the arguments are not de-optimized.
 *
 * @this {Element} The Element to check for changed attributes.
 * @param {*} unused1
 * @param {*} unused2
 * @param {*} unused3
 * @param {...*} var_args Attribute name/value pairs of the dynamic attributes
 *     for the Element.
 * @return {boolean} True if the Element has one or more changed attributes,
 *     false otherwise.
 */
var hasChangedAttrs = function(unused1, unused2, unused3, var_args) {
  var data = getData(this);
  var attrsArr = data.attrsArr;
  var attrsChanged = false;
  var i = ATTRIBUTES_OFFSET;
  var j = 0;

  for (; i < arguments.length; i += 1, j += 1) {
    if (attrsArr[j] !== arguments[i]) {
      attrsChanged = true;
      break;
    }
  }

  for (; i < arguments.length; i += 1, j += 1) {
    attrsArr[j] = arguments[i];
  }

  if (j < attrsArr.length) {
    attrsChanged = true;
    attrsArr.length = j;
  }

  return attrsChanged;
};

/**
 * Updates the newAttrs object for an Element.
 *
 * This function is called in the context of the Element and the arguments from
 * elementOpen-like function so that the arguments are not de-optimized.
 *
 * @this {Element} The Element to update newAttrs for.
 * @param {*} unused1
 * @param {*} unused2
 * @param {*} unused3
 * @param {...*} var_args Attribute name/value pairs of the dynamic attributes
 *     for the Element.
 * @return {!Object<string, *>} The updated newAttrs object.
 */
var updateNewAttrs = function(unused1, unused2, unused3, var_args) {
  var node = this;
  var data = getData(node);
  var newAttrs = data.newAttrs;

  for (var attr in newAttrs) {
    newAttrs[attr] = undefined;
  }

  for (var i = ATTRIBUTES_OFFSET; i < arguments.length; i += 2) {
    newAttrs[arguments[i]] = arguments[i + 1];
  }

  return newAttrs;
};

/**
 * Updates the attributes for a given Element.
 * @param {!Element} node
 * @param {!Object<string,*>} newAttrs The new attributes for node
 */
var updateAttributes = function(node, newAttrs) {
  for (var attr in newAttrs) {
    updateAttribute(node, attr, newAttrs[attr]);
  }
};

/**
 * Declares a virtual Element at the current location in the document. This
 * corresponds to an opening tag and a elementClose tag is required.
 * @param {string} tag The element's tag.
 * @param {?string} key The key used to identify this element. This can be an
 *     empty string, but performance may be better if a unique value is used
 *     when iterating over an array of items.
 * @param {?Array<*>} statics An array of attribute name/value pairs of the
 *     static attributes for the Element. These will only be set once when the
 *     Element is created.
 * @param {...*} var_args Attribute name/value pairs of the dynamic attributes
 *     for the Element.
 */
var elementOpen = function(tag, key, statics, var_args) {
  if (!IS_PRODUCTION) {
    assertNotInAttributes();
  }

  var node = alignWithDOM(tag, key, statics);

  if (hasChangedAttrs.apply(node, arguments)) {
    var newAttrs = updateNewAttrs.apply(node, arguments);
    updateAttributes(node, newAttrs);
  }

  firstChild();
};

/**
 * Declares a virtual Element at the current location in the document. This
 * corresponds to an opening tag and a elementClose tag is required. This is
 * like elementOpen, but the attributes are defined using the attr function
 * rather than being passed as arguments. Must be folllowed by 0 or more calls
 * to attr, then a call to elementOpenEnd.
 * @param {string} tag The element's tag.
 * @param {?string} key The key used to identify this element. This can be an
 *     empty string, but performance may be better if a unique value is used
 *     when iterating over an array of items.
 * @param {?Array<*>} statics An array of attribute name/value pairs of the
 *     static attributes for the Element. These will only be set once when the
 *     Element is created.
 */
var elementOpenStart = function(tag, key, statics) {
  if (!IS_PRODUCTION) {
    assertNotInAttributes();
    setInAttributes();
  }

  argsBuilder[0] = tag;
  argsBuilder[1] = key;
  argsBuilder[2] = statics;
  argsBuilder.length = ATTRIBUTES_OFFSET;
};

/***
 * Defines a virtual attribute at this point of the DOM. This is only valid
 * when called between elementOpenStart and elementOpenEnd.
 *
 * @param {string} name
 * @param {*} value
 */
var attr = function(name, value) {
  if (!IS_PRODUCTION) {
    assertInAttributes();
  }

  argsBuilder.push(name, value);
};

/**
 * Closes an open tag started with elementOpenStart.
 */
var elementOpenEnd = function() {
  if (!IS_PRODUCTION) {
    assertInAttributes();
    setNotInAttributes();
  }

  elementOpen.apply(null, argsBuilder);
};

/**
 * Closes an open virtual Element.
 *
 * @param {string} tag The element's tag.
 */
var elementClose = function(tag) {
  if (!IS_PRODUCTION) {
    assertNotInAttributes();
  }

  parentNode();
  nextSibling();
};

/**
 * Declares a virtual Element at the current location in the document that has
 * no children.
 * @param {string} tag The element's tag.
 * @param {?string} key The key used to identify this element. This can be an
 *     empty string, but performance may be better if a unique value is used
 *     when iterating over an array of items.
 * @param {?Array<*>} statics An array of attribute name/value pairs of the
 *     static attributes for the Element. These will only be set once when the
 *     Element is created.
 * @param {...*} var_args Attribute name/value pairs of the dynamic attributes
 *     for the Element.
 */
var elementVoid = function(tag, key, statics, var_args) {
  if (!IS_PRODUCTION) {
    assertNotInAttributes();
  }

  elementOpen.apply(null, arguments);
  elementClose.apply(null, arguments);
};

/**
 * Declares a virtual Text at this point in the document.
 *
 * @param {string} value The text of the Text.
 */
var text = function(value) {
  if (!IS_PRODUCTION) {
    assertNotInAttributes();
  }

  var node = alignWithDOM('#text', null, value);
  var data = getData(node);

  if (data.text !== value) {
    node.data = value;
    data.text = value;
  }

  nextSibling();
};

/** */
module.exports = {
  elementOpenStart: elementOpenStart,
  elementOpenEnd: elementOpenEnd,
  elementOpen: elementOpen,
  elementVoid: elementVoid,
  elementClose: elementClose,
  text: text,
  attr: attr
};

<commit_msg>Avoid elementOpenStart attributes memory leak<commit_after>/**
 * Copyright 2015 The Incremental DOM Authors. All Rights Reserved.
 *
 * 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 alignWithDOM = require('./alignment').alignWithDOM;
var updateAttribute = require('./attributes').updateAttribute;
var getData = require('./node_data').getData;
var getWalker = require('./walker').getWalker;
var traversal = require('./traversal'),
    firstChild = traversal.firstChild,
    nextSibling = traversal.nextSibling,
    parentNode = traversal.parentNode;

/**
 * The offset in the virtual element declaration where the attributes are
 * specified.
 * @const
 */
var ATTRIBUTES_OFFSET = 3;

/**
 * Builds an array of arguments for use with elementOpenStart, attr and
 * elementOpenEnd.
 * @type {Array<*>}
 * @const
 */
var argsBuilder = [];

/**
 * Verify if the script are running in production.
 * @type {boolean}
 * @const
 */
var IS_PRODUCTION = process.env.NODE_ENV === 'production';

if (!IS_PRODUCTION) {
  /**
   * Keeps track whether or not we are in an attributes declaration (after
   * elementOpenStart, but before elementOpenEnd).
   * @type {boolean}
   */
  var inAttributes = false;

  /** Makes sure that the caller is not where attributes are expected. */
  var assertNotInAttributes = function() {
    if (inAttributes) {
      throw new Error('Was not expecting a call to attr or elementOpenEnd, ' +
          'they must follow a call to elementOpenStart.');
    }
  };

  /** Makes sure that the caller is where attributes are expected. */
  var assertInAttributes = function() {
    if (!inAttributes) {
      throw new Error('Was expecting a call to attr or elementOpenEnd. ' +
          'elementOpenStart must be followed by zero or more calls to attr, ' +
          'then one call to elementOpenEnd.');
    }
  };

  /** Updates the state to being in an attribute declaration. */
  var setInAttributes = function() {
    inAttributes = true;
  };

  /** Updates the state to not being in an attribute declaration. */
  var setNotInAttributes = function() {
    inAttributes = false;
  };
}

/**
 * Checks to see if one or more attributes have changed for a given
 * Element. When no attributes have changed, this function is much faster than
 * checking each individual argument. When attributes have changed, the overhead
 * of this function is minimal.
 *
 * This function is called in the context of the Element and the arguments from
 * elementOpen-like function so that the arguments are not de-optimized.
 *
 * @this {Element} The Element to check for changed attributes.
 * @param {*} unused1
 * @param {*} unused2
 * @param {*} unused3
 * @param {...*} var_args Attribute name/value pairs of the dynamic attributes
 *     for the Element.
 * @return {boolean} True if the Element has one or more changed attributes,
 *     false otherwise.
 */
var hasChangedAttrs = function(unused1, unused2, unused3, var_args) {
  var data = getData(this);
  var attrsArr = data.attrsArr;
  var attrsChanged = false;
  var i = ATTRIBUTES_OFFSET;
  var j = 0;

  for (; i < arguments.length; i += 1, j += 1) {
    if (attrsArr[j] !== arguments[i]) {
      attrsChanged = true;
      break;
    }
  }

  for (; i < arguments.length; i += 1, j += 1) {
    attrsArr[j] = arguments[i];
  }

  if (j < attrsArr.length) {
    attrsChanged = true;
    attrsArr.length = j;
  }

  return attrsChanged;
};

/**
 * Updates the newAttrs object for an Element.
 *
 * This function is called in the context of the Element and the arguments from
 * elementOpen-like function so that the arguments are not de-optimized.
 *
 * @this {Element} The Element to update newAttrs for.
 * @param {*} unused1
 * @param {*} unused2
 * @param {*} unused3
 * @param {...*} var_args Attribute name/value pairs of the dynamic attributes
 *     for the Element.
 * @return {!Object<string, *>} The updated newAttrs object.
 */
var updateNewAttrs = function(unused1, unused2, unused3, var_args) {
  var node = this;
  var data = getData(node);
  var newAttrs = data.newAttrs;

  for (var attr in newAttrs) {
    newAttrs[attr] = undefined;
  }

  for (var i = ATTRIBUTES_OFFSET; i < arguments.length; i += 2) {
    newAttrs[arguments[i]] = arguments[i + 1];
  }

  return newAttrs;
};

/**
 * Updates the attributes for a given Element.
 * @param {!Element} node
 * @param {!Object<string,*>} newAttrs The new attributes for node
 */
var updateAttributes = function(node, newAttrs) {
  for (var attr in newAttrs) {
    updateAttribute(node, attr, newAttrs[attr]);
  }
};

/**
 * Declares a virtual Element at the current location in the document. This
 * corresponds to an opening tag and a elementClose tag is required.
 * @param {string} tag The element's tag.
 * @param {?string} key The key used to identify this element. This can be an
 *     empty string, but performance may be better if a unique value is used
 *     when iterating over an array of items.
 * @param {?Array<*>} statics An array of attribute name/value pairs of the
 *     static attributes for the Element. These will only be set once when the
 *     Element is created.
 * @param {...*} var_args Attribute name/value pairs of the dynamic attributes
 *     for the Element.
 */
var elementOpen = function(tag, key, statics, var_args) {
  if (!IS_PRODUCTION) {
    assertNotInAttributes();
  }

  var node = alignWithDOM(tag, key, statics);

  if (hasChangedAttrs.apply(node, arguments)) {
    var newAttrs = updateNewAttrs.apply(node, arguments);
    updateAttributes(node, newAttrs);
  }

  firstChild();
};

/**
 * Declares a virtual Element at the current location in the document. This
 * corresponds to an opening tag and a elementClose tag is required. This is
 * like elementOpen, but the attributes are defined using the attr function
 * rather than being passed as arguments. Must be folllowed by 0 or more calls
 * to attr, then a call to elementOpenEnd.
 * @param {string} tag The element's tag.
 * @param {?string} key The key used to identify this element. This can be an
 *     empty string, but performance may be better if a unique value is used
 *     when iterating over an array of items.
 * @param {?Array<*>} statics An array of attribute name/value pairs of the
 *     static attributes for the Element. These will only be set once when the
 *     Element is created.
 */
var elementOpenStart = function(tag, key, statics) {
  if (!IS_PRODUCTION) {
    assertNotInAttributes();
    setInAttributes();
  }

  argsBuilder[0] = tag;
  argsBuilder[1] = key;
  argsBuilder[2] = statics;
};

/***
 * Defines a virtual attribute at this point of the DOM. This is only valid
 * when called between elementOpenStart and elementOpenEnd.
 *
 * @param {string} name
 * @param {*} value
 */
var attr = function(name, value) {
  if (!IS_PRODUCTION) {
    assertInAttributes();
  }

  argsBuilder.push(name, value);
};

/**
 * Closes an open tag started with elementOpenStart.
 */
var elementOpenEnd = function() {
  if (!IS_PRODUCTION) {
    assertInAttributes();
    setNotInAttributes();
  }

  elementOpen.apply(null, argsBuilder);
  argsBuilder.length = 0;
};

/**
 * Closes an open virtual Element.
 *
 * @param {string} tag The element's tag.
 */
var elementClose = function(tag) {
  if (!IS_PRODUCTION) {
    assertNotInAttributes();
  }

  parentNode();
  nextSibling();
};

/**
 * Declares a virtual Element at the current location in the document that has
 * no children.
 * @param {string} tag The element's tag.
 * @param {?string} key The key used to identify this element. This can be an
 *     empty string, but performance may be better if a unique value is used
 *     when iterating over an array of items.
 * @param {?Array<*>} statics An array of attribute name/value pairs of the
 *     static attributes for the Element. These will only be set once when the
 *     Element is created.
 * @param {...*} var_args Attribute name/value pairs of the dynamic attributes
 *     for the Element.
 */
var elementVoid = function(tag, key, statics, var_args) {
  if (!IS_PRODUCTION) {
    assertNotInAttributes();
  }

  elementOpen.apply(null, arguments);
  elementClose.apply(null, arguments);
};

/**
 * Declares a virtual Text at this point in the document.
 *
 * @param {string} value The text of the Text.
 */
var text = function(value) {
  if (!IS_PRODUCTION) {
    assertNotInAttributes();
  }

  var node = alignWithDOM('#text', null, value);
  var data = getData(node);

  if (data.text !== value) {
    node.data = value;
    data.text = value;
  }

  nextSibling();
};

/** */
module.exports = {
  elementOpenStart: elementOpenStart,
  elementOpenEnd: elementOpenEnd,
  elementOpen: elementOpen,
  elementVoid: elementVoid,
  elementClose: elementClose,
  text: text,
  attr: attr
};

<|endoftext|>


<commit_before>const fs = require("fs");
const dir = require("node-dir");
const path = require("path");

const Emittery = require('emittery');
const async = require("async");
const Web3 = require("web3");

const expect = require("truffle-expect");
const Deployer = require("truffle-deployer");
const Require = require("truffle-require");

const ResolverIntercept = require("./resolverintercept");
const Reporter = require("./reporter/reporter");

const util = require('util');

class Migration {

  constructor(file, reporter, options){
    this.file = path.resolve(file);
    this.reporter = reporter;
    this.number = parseInt(path.basename(file));
    this.emitter = new Emittery();
    this.isFirst = false;
    this.isLast = false;
    this.options = options || {};

  }

  async run(options, callback) {
    const self = this;
    const logger = options.logger;
    const resolver = new ResolverIntercept(options.resolver);
    const web3 = new Web3();
    web3.setProvider(options.provider);

    // Initial context.
    const context = {
      web3: web3
    };

    const deployer = new Deployer({
      logger: {
        log: function(msg) {
          logger.log(msg);
        },
        error: function(msg){
          logger.error(msg);
        }
      },
      network: options.network,
      network_id: options.network_id,
      provider: options.provider,
      basePath: path.dirname(self.file)
    });

    this.reporter.migration = self;
    this.reporter.deployer = deployer;
    this.reporter.listen();

    const file = path.relative(options.migrations_directory, self.file)

    const preMigrationsData = {
      file: file,
      isFirst: this.isFirst,
      network: options.network,
      networkId: options.network_id,
    }

    await self.emitter.emit('preMigrate', preMigrationsData);

    var finish = async function(migrateFn) {
      try {
        await deployer.start();

        if (migrateFn && migrateFn.then !== undefined){
          await deployer.then(() => migrateFn);
        }

        if (options.save === false) return;

        const Migrations = resolver.require("./Migrations.sol");

        if (Migrations && Migrations.isDeployed()) {
          await self.emitter.emit('saveMigration', self.number);
          const migrations = await Migrations.deployed();
          await migrations.setCompleted(self.number);
        }

        await self.emitter.emit('postMigrate', self.isLast);
        await options.artifactor.saveAll(resolver.contracts());
        deployer.finish();

        if (self.isLast){
          self.emitter.clearListeners();
        }
        // Prevent errors thrown in the callback from triggering the below catch()
        process.nextTick(callback);
      } catch(e) {
        const payload = {
          type: 'migrateErr',
          error: e
        };

        await self.emitter.emit('error', payload);
        deployer.finish();
        callback(e);
      };
    };

    try {
      const accounts = await web3.eth.getAccounts();
      const requireOptions = {
        file: self.file,
        context: context,
        resolver: resolver,
        args: [deployer],
      }

      Require.file(requireOptions, async (err, fn) => {
        if (err) return callback(err);

        const unRunnable = !fn || !fn.length || fn.length == 0;

        if (unRunnable){
          const msg = `Migration ${self.file} invalid or does not take any parameters`;
          return callback(new Error(msg));
        }

        const migrateFn = fn(deployer, options.network, accounts);
        await finish(migrateFn);
      });

    } catch(err){
      callback(err)
    }
  }
}

const Migrate = {
  Migration: Migration,
  reporter: new Reporter(),

  assemble: function(options, callback) {
    dir.files(options.migrations_directory, function(err, files) {
      if (err) return callback(err);

      options.allowed_extensions = options.allowed_extensions || /^\.(js|es6?)$/;

      let migrations = files
        .filter(file => isNaN(parseInt(path.basename(file))) == false)
        .filter(file => path.extname(file).match(options.allowed_extensions) != null)
        .map(file => new Migration(file, Migrate.reporter, options));

      // Make sure to sort the prefixes as numbers and not strings.
      migrations = migrations.sort((a, b) => {
        if (a.number > b.number) return 1;
        if (a.number < b.number) return -1;
        return 0;
      });

      callback(null, migrations);
    });
  },

  run: function(options, callback) {
    const self = this;

    expect.options(options, [
      "working_directory",
      "migrations_directory",
      "contracts_build_directory",
      "provider",
      "artifactor",
      "resolver",
      "network",
      "network_id",
      "logger",
      "from", // address doing deployment
    ]);

    if (options.reset == true) {
      return this.runAll(options, callback);
    }

    self.lastCompletedMigration(options, function(err, last_migration) {
      if (err) return callback(err);

      // Don't rerun the last completed migration.
      self.runFrom(last_migration + 1, options, callback);
    });
  },

  runFrom: function(number, options, callback) {
    const self = this;

    this.assemble(options, function(err, migrations) {
      if (err) return callback(err);

      while (migrations.length > 0) {
        if (migrations[0].number >= number) break;
        migrations.shift();
      }

      if (options.to) {
        migrations = migrations.filter(migration => migration.number <= options.to);
      }

      self.runMigrations(migrations, options, callback);
    });
  },

  runAll: function(options, callback) {
    this.runFrom(0, options, callback);
  },

  runMigrations: function(migrations, options, callback) {
    // Perform a shallow clone of the options object
    // so that we can override the provider option without
    // changing the original options object passed in.
    const clone = {};

    Object.keys(options).forEach(function(key) {
      clone[key] = options[key];
    });

    if (options.quiet) {
      clone.logger = {
        log: function() {}
      }
    };

    clone.provider = this.wrapProvider(options.provider, clone.logger);
    clone.resolver = this.wrapResolver(options.resolver, clone.provider);

    // Make migrations aware of their position in sequence
    const total = migrations.length;
    if(total){
      migrations[0].isFirst = true;
      migrations[total - 1].isLast = true;
    }

    async.eachSeries(migrations, function(migration, finished) {
      migration.run(clone, function(err) {
        if (err) return finished(err);
        finished();
      });
    }, callback);
  },

  wrapProvider: function(provider) {
    return {
      send: function(payload, callback) {
        provider.send(payload, function(err, result) {
          (err)
            ? callback(err)
            : callback(err, result);
        });
      }
    };
  },

  wrapResolver: function(resolver, provider) {
    return {
      require: function(import_path, search_path) {
        const abstraction = resolver.require(import_path, search_path);
        abstraction.setProvider(provider);
        return abstraction;
      },
      resolve: resolver.resolve
    }
  },

  lastCompletedMigration: function(options, callback) {
    let Migrations;

    try {
      Migrations = options.resolver.require("Migrations");
    } catch (e) {
      const message = `Could not find built Migrations contract: ${e.message}`;
      return callback(new Error());
    }

    if (Migrations.isDeployed() == false) {
      return callback(null, 0);
    }

    const migrations = Migrations.deployed();

    Migrations.deployed().then(function(migrations) {
      // Two possible Migrations.sol's (lintable/unlintable)
      return (migrations.last_completed_migration)
        ? migrations.last_completed_migration.call()
        : migrations.lastCompletedMigration.call();

    }).then(function(completed_migration) {
      callback(null, parseInt(completed_migration));
    }).catch(callback);
  },

  needsMigrating: function(options, callback) {
    const self = this;

    if (options.reset == true) {
      return callback(null, true);
    }

    this.lastCompletedMigration(options, function(err, number) {
      if (err) return callback(err);

      self.assemble(options, function(err, migrations) {
        if (err) return callback(err);

        while (migrations.length > 0) {
          if (migrations[0].number >= number) break;
          migrations.shift();
        }

        callback(null, migrations.length > 1);
      });
    });
  }
};

module.exports = Migrate;
<commit_msg>Restructure Migration callbacks as class methods<commit_after>const fs = require("fs");
const dir = require("node-dir");
const path = require("path");

const Emittery = require('emittery');
const async = require("async");
const Web3 = require("web3");

const expect = require("truffle-expect");
const Deployer = require("truffle-deployer");
const Require = require("truffle-require");

const ResolverIntercept = require("./resolverintercept");
const Reporter = require("./reporter/reporter");

const util = require('util');

class Migration {

  constructor(file, reporter, options){
    this.file = path.resolve(file);
    this.reporter = reporter;
    this.number = parseInt(path.basename(file));
    this.emitter = new Emittery();
    this.isFirst = false;
    this.isLast = false;
    this.options = options || {};
  }

  // ------------------------------------- Private -------------------------------------------------
  /**
   * Loads & validates migration, then runs it.
   * @param  {Object}   options  config and command-line
   * @param  {Object}   context  web3
   * @param  {Object}   deployer truffle module
   * @param  {Object}   resolver truffle module
   * @param  {Function} callback
   */
  async _load(options, context, deployer, resolver, callback){
    const self = this;

    // Load assets and run `execute`
    try {
      const accounts = await context.web3.eth.getAccounts();
      const requireOptions = {
        file: self.file,
        context: context,
        resolver: resolver,
        args: [deployer],
      }

      Require.file(requireOptions, async (err, fn) => {
        if (err) return callback(err);

        const unRunnable = !fn || !fn.length || fn.length == 0;

        if (unRunnable){
          const msg = `Migration ${self.file} invalid or does not take any parameters`;
          return callback(new Error(msg));
        }

        // `migrateFn` might be sync or async. We negotiate that difference in
        // `execute` through the deployer API.
        const migrateFn = fn(deployer, options.network, accounts);
        await self._deploy(options, deployer, resolver, migrateFn, callback);
      });

    } catch(err){
      callback(err)
    }
  }

  /**
   * Initiates deployer sequence, then manages migrations info
   * publication to chain / artifact saving.
   * @param  {Object}   options     config and command-line
   * @param  {Object}   deployer    truffle module
   * @param  {Object}   resolver    truffle module
   * @param  {[type]}   migrateFn   module.exports of a migrations.js
   */
  async _deploy(options, deployer, resolver, migrateFn, callback) {
    const self = this;

    try {
      await deployer.start();

      // Allow migrations method to be async and
      // deploy to use await
      if (migrateFn && migrateFn.then !== undefined){
        await deployer.then(() => migrateFn);
      }

      // Migrate without saving
      if (options.save === false) return;

      // Write migrations record to chain
      const Migrations = resolver.require("./Migrations.sol");

      if (Migrations && Migrations.isDeployed()) {
        await self.emitter.emit('saveMigration', self.number);
        const migrations = await Migrations.deployed();
        await migrations.setCompleted(self.number);
      }

      // Save artifacts to local filesystem
      await self.emitter.emit('postMigrate', self.isLast);
      await options.artifactor.saveAll(resolver.contracts());
      deployer.finish();

      // Cleanup
      if (self.isLast){
        self.emitter.clearListeners();
      }
      // Prevent errors thrown in the callback from triggering the below catch()
      process.nextTick(callback);
    } catch(e) {
      const payload = {
        type: 'migrateErr',
        error: e
      };

      await self.emitter.emit('error', payload);
      deployer.finish();
      callback(e);
    };
  }

  // ------------------------------------- Public -------------------------------------------------
  /**
   * Instantiates a deployer, connects this migration and its deployer to the reporter
   * and launches a migration file's deployment sequence
   * @param  {Object}   options  config and command-line
   * @param  {Function} callback
   */
  async run(options, callback) {
    const self = this;
    const logger = options.logger;
    const resolver = new ResolverIntercept(options.resolver);
    const web3 = new Web3();
    web3.setProvider(options.provider);

    // Initial context.
    const context = {
      web3: web3
    };

    // Instantiate a Deployer
    const deployer = new Deployer({
      logger: logger,
      network: options.network,
      network_id: options.network_id,
      provider: options.provider,
      basePath: path.dirname(self.file)
    });

    // Connect reporter to this migration
    self.reporter.migration = self;
    self.reporter.deployer = deployer;
    self.reporter.listen();

    // Get file path and emit pre-migration event
    const file = path.relative(options.migrations_directory, self.file)

    const preMigrationsData = {
      file: file,
      isFirst: self.isFirst,
      network: options.network,
      networkId: options.network_id,
    }

    await self.emitter.emit('preMigrate', preMigrationsData);
    await self._load(options, context, deployer, resolver, callback);
  }
}

const Migrate = {
  Migration: Migration,
  reporter: new Reporter(),

  assemble: function(options, callback) {
    dir.files(options.migrations_directory, function(err, files) {
      if (err) return callback(err);

      options.allowed_extensions = options.allowed_extensions || /^\.(js|es6?)$/;

      let migrations = files
        .filter(file => isNaN(parseInt(path.basename(file))) == false)
        .filter(file => path.extname(file).match(options.allowed_extensions) != null)
        .map(file => new Migration(file, Migrate.reporter, options));

      // Make sure to sort the prefixes as numbers and not strings.
      migrations = migrations.sort((a, b) => {
        if (a.number > b.number) return 1;
        if (a.number < b.number) return -1;
        return 0;
      });

      callback(null, migrations);
    });
  },

  run: function(options, callback) {
    const self = this;

    expect.options(options, [
      "working_directory",
      "migrations_directory",
      "contracts_build_directory",
      "provider",
      "artifactor",
      "resolver",
      "network",
      "network_id",
      "logger",
      "from", // address doing deployment
    ]);

    if (options.reset == true) {
      return this.runAll(options, callback);
    }

    self.lastCompletedMigration(options, function(err, last_migration) {
      if (err) return callback(err);

      // Don't rerun the last completed migration.
      self.runFrom(last_migration + 1, options, callback);
    });
  },

  runFrom: function(number, options, callback) {
    const self = this;

    this.assemble(options, function(err, migrations) {
      if (err) return callback(err);

      while (migrations.length > 0) {
        if (migrations[0].number >= number) break;
        migrations.shift();
      }

      if (options.to) {
        migrations = migrations.filter(migration => migration.number <= options.to);
      }

      self.runMigrations(migrations, options, callback);
    });
  },

  runAll: function(options, callback) {
    this.runFrom(0, options, callback);
  },

  runMigrations: function(migrations, options, callback) {
    // Perform a shallow clone of the options object
    // so that we can override the provider option without
    // changing the original options object passed in.
    const clone = {};

    Object.keys(options).forEach(function(key) {
      clone[key] = options[key];
    });

    if (options.quiet) {
      clone.logger = {
        log: function() {}
      }
    };

    clone.provider = this.wrapProvider(options.provider, clone.logger);
    clone.resolver = this.wrapResolver(options.resolver, clone.provider);

    // Make migrations aware of their position in sequence
    const total = migrations.length;
    if(total){
      migrations[0].isFirst = true;
      migrations[total - 1].isLast = true;
    }

    async.eachSeries(migrations, function(migration, finished) {
      migration.run(clone, function(err) {
        if (err) return finished(err);
        finished();
      });
    }, callback);
  },

  wrapProvider: function(provider) {
    return {
      send: function(payload, callback) {
        provider.send(payload, function(err, result) {
          (err)
            ? callback(err)
            : callback(err, result);
        });
      }
    };
  },

  wrapResolver: function(resolver, provider) {
    return {
      require: function(import_path, search_path) {
        const abstraction = resolver.require(import_path, search_path);
        abstraction.setProvider(provider);
        return abstraction;
      },
      resolve: resolver.resolve
    }
  },

  lastCompletedMigration: function(options, callback) {
    let Migrations;

    try {
      Migrations = options.resolver.require("Migrations");
    } catch (e) {
      const message = `Could not find built Migrations contract: ${e.message}`;
      return callback(new Error());
    }

    if (Migrations.isDeployed() == false) {
      return callback(null, 0);
    }

    const migrations = Migrations.deployed();

    Migrations.deployed().then(function(migrations) {
      // Two possible Migrations.sol's (lintable/unlintable)
      return (migrations.last_completed_migration)
        ? migrations.last_completed_migration.call()
        : migrations.lastCompletedMigration.call();

    }).then(function(completed_migration) {
      callback(null, parseInt(completed_migration));
    }).catch(callback);
  },

  needsMigrating: function(options, callback) {
    const self = this;

    if (options.reset == true) {
      return callback(null, true);
    }

    this.lastCompletedMigration(options, function(err, number) {
      if (err) return callback(err);

      self.assemble(options, function(err, migrations) {
        if (err) return callback(err);

        while (migrations.length > 0) {
          if (migrations[0].number >= number) break;
          migrations.shift();
        }

        callback(null, migrations.length > 1);
      });
    });
  }
};

module.exports = Migrate;
<|endoftext|>