<commit_before>"use strict";

/*global module, require*/

var d3 = require("d3"),
    floatingDialogue = require("floating-dialogue"),
    helpers = require("../helpers.js"),
    callbacks = helpers.callbackHandler,
    identity = helpers.identity,
    rounded = helpers.rounded;

/*
 Make an info panel for a polygon on the map.

 Call it with a container which it will make the panel inside.
 */
module.exports = function() {
    var dialogue = floatingDialogue(
	d3.select(
	    document.createElement("div")
	)
	    .classed("results", true))
	    .close()
	    .resize()
	    .drag()
	    .hide(),

	table = dialogue.content().append("table")
	    .classed("results-table", true),
	tHead = table.append("thead"),
	headers = tHead.append("tr")
	    .classed("headers", true),
	tBody = table.append("tbody"),
	rowClickHandler = callbacks(),
	headClickHandler = callbacks(),
	rowHoverHandler = callbacks(),
	resetHandler = callbacks(),
	extraRow,
	gotSize = false;

    dialogue.el().append("span")
	.classed("reset-results", true)
	.text("RESET")
	.on("click", resetHandler);

    var maybeNumber = function(n) {
	var num = parseFloat(n);
	if (isNaN(num) || !isFinite(n)) {
	    return n;
	} else {
	    return num;
	}
    };

    var sort = function(head, body, ordering) {
	var indices = ordering.properties.map(function(p){
	    return head.indexOf(p);
	});

	var len = indices.length;
	body.sort(function(a, b){
	    for (var i = 0; i < len; i++) {
		var direction = ordering.reverse[i] ? -1 : 1;
		var j = indices[i];
		var first = maybeNumber(a[j]);
		var second = maybeNumber(b[j]);

		if (first > second) {
		    return 1 * direction;
		} else if (first < second) {
		    return -1 * direction;
		} else {
		    // No-op, we'll carry on looping.
		}
	    }
	    return 0;
	});
    };

    var setColumnWidths = function(cellSelection) {
	tBody.select("tr")
	    .selectAll("td")
	    .each(function(d, i) {
		var width = d3.select(this).style("width");
		d3.select(cellSelection[0][i])
		    .style("min-width", width)
		    .style("max-width", width);
	    });
    };

    var module = {

	/*
	 Update the info with the given data.
	 head should be a 1D array.
	 body should be a 2D array.
	 */
	info : function(head, data, ordering) {
	    sort(head, data, ordering);

	    var h = headers.selectAll("th").data(head);
	    h.exit().remove();
	    h.enter().append("th");
	    h.html(identity)
		.on("click", function(d, i) {
		    var column = data.map(function(row) {
			return row[i];
		    });

		    headClickHandler(d, column);
		});
	    h.each(function(d, i){
		var j = ordering.properties.indexOf(d);
		this.sorted = (j >= 0);
		this.reverse = ordering.reverse[j];
	    })
		.classed("sorted", function(d, i){
		    return this.sorted;
		})
		.classed("reverse", function(d, i){
		    return this.reverse;
		});

	    var tr = tBody.selectAll("tr").data(
		data,
		function(d, i) {
		    return d[0];
		}
	    );
	    tr.enter().append("tr");
	    tr.exit().remove();
	    tr
		.on("click", rowClickHandler)
		.on("mouseenter", rowHoverHandler)
		.order();

	    var td = tr.selectAll("td").data(function(d, i){
		return d;
	    });

	    td.enter().append("td");
	    td.exit().remove();
	    td.html(rounded);

	    setColumnWidths(h);

	    if (!gotSize) {
		/*
		 Setting the size here allows us to autoresize the table's containing element to the content the first time,
		 but still provides scrollbars if it overflows the max-height.
		 */

		gotSize = true;
		dialogue.el().style("height", dialogue.el().node().offsetHeight + "px");
		dialogue.el().style("width", dialogue.el().node().offsetWidth + "px");
	    }
	},

	headerClicked : headClickHandler.add,
	rowClicked : rowClickHandler.add,
	rowHovered: rowHoverHandler.add,
	resetClicked: resetHandler.add,

	setExtraRow: function(row) {
	    if (extraRow) {
		extraRow.remove();
	    }

	    extraRow = d3.select(row.node().cloneNode(true));

	    setColumnWidths(
		extraRow
		    .classed("extra-row", true)
		    .selectAll("td"));

	    tHead.node().appendChild(extraRow.node());
	},

	rows: function() {
	    return tBody.selectAll("tr");
	},

	cells: function() {
	    return tBody.selectAll("tr").selectAll("td");
	},

	dialogue: function() {
	    return dialogue;
	},

	el: function() {
	    return dialogue.el();
	},

	tbody: function() {
	    return tBody;
	},

	addToContainer: function(container) {
	    container.node().appendChild(
		dialogue.el().node());
	}
    };

    return module;
};
<commit_msg>Results table's header x-position and size now syncs up with the body more consistently.<commit_after>"use strict";

/*global module, require*/

var d3 = require("d3"),
    floatingDialogue = require("floating-dialogue"),
    helpers = require("../helpers.js"),
    callbacks = helpers.callbackHandler,
    identity = helpers.identity,
    rounded = helpers.rounded;

/*
 Make an info panel for a polygon on the map.

 Call it with a container which it will make the panel inside.
 */
module.exports = function() {
    var dialogue = floatingDialogue(
	d3.select(
	    document.createElement("div")
	)
	    .classed("results", true))
	    .close()
	    .resize()
	    .drag()
	    .hide(),

	table = dialogue.content().append("table")
	    .classed("results-table", true),
	tHead = table.append("thead"),
	headers = tHead.append("tr")
	    .classed("headers", true),
	tBody = table.append("tbody")
	    .on("scroll", function(d, i) {
		/*
		 Scroll the header with the body.
		 I would prefer to set scrollLeft on the header, but this only works if it has its own scrollbar.
		 Instead we have this negative-margin hack.
		 */
		tHead.style("margin-left", (-this.scrollLeft) + "px");
	    }),
	rowClickHandler = callbacks(),
	headClickHandler = callbacks(),
	rowHoverHandler = callbacks(),
	resetHandler = callbacks(),
	extraRow,
	gotSize = false;

    dialogue.el().append("span")
	.classed("reset-results", true)
	.text("RESET")
	.on("click", resetHandler);

    var maybeNumber = function(n) {
	var num = parseFloat(n);
	if (isNaN(num) || !isFinite(n)) {
	    return n;
	} else {
	    return num;
	}
    };

    var sort = function(head, body, ordering) {
	var indices = ordering.properties.map(function(p){
	    return head.indexOf(p);
	});

	var len = indices.length;
	body.sort(function(a, b){
	    for (var i = 0; i < len; i++) {
		var direction = ordering.reverse[i] ? -1 : 1;
		var j = indices[i];
		var first = maybeNumber(a[j]);
		var second = maybeNumber(b[j]);

		if (first > second) {
		    return 1 * direction;
		} else if (first < second) {
		    return -1 * direction;
		} else {
		    // No-op, we'll carry on looping.
		}
	    }
	    return 0;
	});
    };

    var setColumnWidths = function(headers) {
	var widths = [],
	    getWidth = function(d, i) {
		return widths[i];
	    },
	    styleIt = function(selection) {
		selection.style("min-width", getWidth);
		selection.style("max-width", getWidth);
	    };

	tBody.select("tr")
	    .selectAll("td")
	    .each(function(d, i) {
		widths[i] = d3.select(this).style("width");
	    });

	styleIt(headers);

	styleIt(
	    tBody.selectAll("tr")
		.selectAll("td"));
    };

    var module = {

	/*
	 Update the info with the given data.
	 head should be a 1D array.
	 body should be a 2D array.
	 */
	info : function(head, data, ordering) {
	    sort(head, data, ordering);

	    var h = headers.selectAll("th").data(head);
	    h.exit().remove();
	    h.enter().append("th");
	    h.html(identity)
		.on("click", function(d, i) {
		    var column = data.map(function(row) {
			return row[i];
		    });

		    headClickHandler(d, column);
		});
	    h.each(function(d, i){
		var j = ordering.properties.indexOf(d);
		this.sorted = (j >= 0);
		this.reverse = ordering.reverse[j];
	    })
		.classed("sorted", function(d, i){
		    return this.sorted;
		})
		.classed("reverse", function(d, i){
		    return this.reverse;
		});

	    var tr = tBody.selectAll("tr").data(
		data,
		function(d, i) {
		    return d[0];
		}
	    );
	    tr.enter().append("tr");
	    tr.exit().remove();
	    tr
		.on("click", rowClickHandler)
		.on("mouseenter", rowHoverHandler)
		.order();

	    var td = tr.selectAll("td").data(function(d, i){
		return d;
	    });

	    td.enter().append("td");
	    td.exit().remove();
	    td.html(rounded);

	    setColumnWidths(h);

	    if (!gotSize) {
		/*
		 Setting the size here allows us to autoresize the table's containing element to the content the first time,
		 but still provides scrollbars if it overflows the max-height.
		 */

		gotSize = true;
		dialogue.el().style("height", dialogue.el().node().offsetHeight + "px");
		dialogue.el().style("width", dialogue.el().node().offsetWidth + "px");
	    }
	},

	headerClicked : headClickHandler.add,
	rowClicked : rowClickHandler.add,
	rowHovered: rowHoverHandler.add,
	resetClicked: resetHandler.add,

	setExtraRow: function(row) {
	    if (extraRow) {
		extraRow.remove();
	    }

	    extraRow = d3.select(row.node().cloneNode(true));

	    setColumnWidths(
		extraRow
		    .classed("extra-row", true)
		    .selectAll("td"));

	    tHead.node().appendChild(extraRow.node());
	},

	rows: function() {
	    return tBody.selectAll("tr");
	},

	cells: function() {
	    return tBody.selectAll("tr").selectAll("td");
	},

	dialogue: function() {
	    return dialogue;
	},

	el: function() {
	    return dialogue.el();
	},

	tbody: function() {
	    return tBody;
	},

	addToContainer: function(container) {
	    container.node().appendChild(
		dialogue.el().node());
	}
    };

    return module;
};
<|endoftext|>


<commit_before>$(function () {
    $('#spending-averages').highcharts({
        chart: {
            type: 'bar'
        },
        title: {
            text: 'Your Spending Percentages'
        },
        subtitle: {
            text: 'Compared to National Averages'
        },
        xAxis: {
            categories: ['Housing', 'Transportation', 'Food', 'Phone', 'Misc', 'Savings'],
            title: {
                text: 'Categories'
            }
        },
        yAxis: {
            min: 0,
            title: {
                text: 'Population (millions)',
                align: 'high'
            },
            labels: {
                overflow: 'justify'
            }
        },
        tooltip: {
            valueSuffix: ' millions'
        },
        plotOptions: {
            bar: {
                dataLabels: {
                    enabled: false
                }
            }
        },
        legend: {
            layout: 'vertical',
            align: 'right',
            verticalAlign: 'top',
            x: -40,
            y: 80,
            floating: true,
            borderWidth: 1,
            backgroundColor: ((Highcharts.theme && Highcharts.theme.legendBackgroundColor) || '#FFFFFF'),
            shadow: true
        },
        credits: {
            enabled: false
        },
        series: [{
            name: 'Your spending',
            data: [107, 31, 635, 203, 5]
        }, {
            name: 'Average Spending',
            data: [35, 15, 7, 3, 30, 10]
        }]
    });
});<commit_msg>Fix chart labels<commit_after>$(function () {
    $('#spending-averages').highcharts({
        chart: {
            type: 'bar'
        },
        title: {
            text: 'Your Spending Percentages'
        },
        subtitle: {
            text: 'Compared to National Averages'
        },
        xAxis: {
            categories: ['Housing', 'Transportation', 'Food', 'Phone', 'Misc', 'Savings'],
            title: {
                text: 'Categories'
            }
        },
        yAxis: {
            min: 0,
            title: {
                text: 'Percentage',
                align: 'high'
            },
            labels: {
                overflow: 'justify'
            }
        },
        tooltip: {
            valueSuffix: ' millions'
        },
        plotOptions: {
            bar: {
                dataLabels: {
                    enabled: false
                }
            }
        },
        legend: {
            layout: 'vertical',
            align: 'right',
            verticalAlign: 'top',
            x: -40,
            y: 80,
            floating: true,
            borderWidth: 1,
            backgroundColor: ((Highcharts.theme && Highcharts.theme.legendBackgroundColor) || '#FFFFFF'),
            shadow: true
        },
        credits: {
            enabled: false
        },
        series: [{
            name: 'Your spending',
            data: [27, 31, 35, 20, 5]
        }, {
            name: 'Average Spending',
            data: [35, 15, 7, 3, 30, 10]
        }]
    });
});<|endoftext|>


<commit_before>elation.require(['engine.things.label'], function() {
  elation.component.add('engine.things.label2d', function() {
    this.postinit = function() {
      elation.engine.things.janustext.extendclass.postinit.call(this);
      this.defineProperties({
        font: { type: 'string', default: 'sans-serif' },
        fontSize: { type: 'float', default: 64 },
        color: { type: 'color', default: 0xffffff },
      });
      this.properties.size = 1;
      this.properties.thickness = .11;
      this.properties.align = 'center';
    }
    this.createTextGeometry = function(text) {
      var labelgen = this.getAssetWrapper();
      var aspect = labelgen.getAspectRatio(text);
      var label = labelgen.getLabel(text);
      var height = 1 / aspect;
      var geometry = new THREE.PlaneBufferGeometry(1, height);
      geometry.applyMatrix(new THREE.Matrix4().makeTranslation(0, height/2, .02));
      return geometry;
    }
    this.createTextMaterial = function(text) {
      var labelgen = this.getAssetWrapper();
      var label = labelgen.getLabel(this.properties.text);
      var material = new THREE.MeshBasicMaterial({
        map: label,
        transparent: true,
        alphaTest: 0.6
      });
      return material;
    }
    this.setText = function(text) {
      this.properties.text = text;
      if (text.indexOf && text.indexOf('\n') != -1) {
        this.setMultilineText(text);
      } else {
        this.objects['3d'].geometry = this.createTextGeometry(text);
      }
      this.objects['3d'].material = this.material = this.createTextMaterial(text);
      this.refresh();
    }
    this.getAssetWrapper = function() {
      var color = (this.properties.color instanceof THREE.Color ? this.properties.color : new THREE.Color(this.properties.color));
      if (!this.labelgen) {
        var genname = ['label2d', this.properties.font, this.properties.fontSize, color.getHexString()].join('_');
        var asset = elation.engine.assets.find('labelgen', genname);
        if (!asset) {
          asset = elation.engine.assets.get({
            assettype: 'labelgen',
            assetname: genname,
            color: '#' + color.getHexString(),
            outline: 4
          });
        }
        this.labelgen = asset;
      }
      return this.labelgen;
    }
  }, elation.engine.things.label);
});

<commit_msg>Fixed inheritence<commit_after>elation.require(['engine.things.label'], function() {
  elation.component.add('engine.things.label2d', function() {
    this.postinit = function() {
      elation.engine.things.label2d.extendclass.postinit.call(this);
      this.defineProperties({
        font: { type: 'string', default: 'sans-serif' },
        fontSize: { type: 'float', default: 64 },
        color: { type: 'color', default: 0xffffff },
      });
      this.properties.size = 1;
      this.properties.thickness = .11;
      this.properties.align = 'center';
    }
    this.createTextGeometry = function(text) {
      var labelgen = this.getAssetWrapper();
      var aspect = labelgen.getAspectRatio(text);
      var label = labelgen.getLabel(text);
      var height = 1 / aspect;
      var geometry = new THREE.PlaneBufferGeometry(1, height);
      geometry.applyMatrix(new THREE.Matrix4().makeTranslation(0, height/2, .02));
      return geometry;
    }
    this.createTextMaterial = function(text) {
      var labelgen = this.getAssetWrapper();
      var label = labelgen.getLabel(this.properties.text);
      var material = new THREE.MeshBasicMaterial({
        map: label,
        transparent: true,
        alphaTest: 0.6
      });
      return material;
    }
    this.setText = function(text) {
      this.properties.text = text;
      if (text.indexOf && text.indexOf('\n') != -1) {
        this.setMultilineText(text);
      } else {
        this.objects['3d'].geometry = this.createTextGeometry(text);
      }
      this.objects['3d'].material = this.material = this.createTextMaterial(text);
      this.refresh();
    }
    this.getAssetWrapper = function() {
      var color = (this.properties.color instanceof THREE.Color ? this.properties.color : new THREE.Color(this.properties.color));
      if (!this.labelgen) {
        var genname = ['label2d', this.properties.font, this.properties.fontSize, color.getHexString()].join('_');
        var asset = elation.engine.assets.find('labelgen', genname);
        if (!asset) {
          asset = elation.engine.assets.get({
            assettype: 'labelgen',
            assetname: genname,
            color: '#' + color.getHexString(),
            outline: 4
          });
        }
        this.labelgen = asset;
      }
      return this.labelgen;
    }
  }, elation.engine.things.label);
});

<|endoftext|>


<commit_before>var APP = (function() {
    /**
     * Creates an Array of DOM nodes that match the selector
     * @param selector {string} CSS selector - selector to match against
     * @return {array} Array of DOM nodes
     */
    function getDomNodeArray(selector) {
        var nodes = Array.prototype.slice.apply(document.querySelectorAll(selector));
        if (!nodes) {
            nodes = [];
        }
        return nodes;
    }

    /*
    Adding guests
     */
    var newGuest = document.querySelector('input.new-guest');
    var people = document.querySelector('.attendees');
    newGuest.onkeydown = function(evt) {
        if (evt.keyIdentifier === "Enter") {
            var enteredPerson = document.createElement('div');
            var deletePerson = document.createElement('button');
            deletePerson.innerHTML = "-";
            deletePerson.parent = enteredPerson;

            deletePerson.onclick = function() {
                people.removeChild(this.parent);
            };
            enteredPerson.innerHTML = newGuest.value;
            enteredPerson.value = newGuest.value; // for easy access later
            enteredPerson.appendChild(deletePerson);

            people.appendChild(enteredPerson);
            newGuest.value = "";
        }
    };
    /*
    Adding functionality to hide/show guest in card-detail preview
     */
    var attendeesCheckbox = document.getElementById('attendees-list');
    var guestsDisplayContainer = document.querySelector('.card-detail.guests');
    attendeesCheckbox.onclick = function() {
      if (this.checked === true) {
        guestsDisplayContainer.classList.remove('hidden')
      } else {
        guestsDisplayContainer.classList.add('hidden');
      }
    }

    /*
    class="model model-*" Refer to inputs by the name after class model-*
     */
    function Model() {
        var self = this;

        function Binding(elem, value) {
            elem = elem || null;
            value = value || null;

            this.elem = elem;
            this.value = value;
            this.hasChanged = false;
            this.oninput = function() {};
        }

        // pulls values from elems of class="model model-*" to create Model's raw info and set up 1-way binding
        getDomNodeArray('.model').forEach(function(elem) {
            elem.classList.forEach(function(className) {
                var possiblyMatch = className.match(/model\-/g);
                if (possiblyMatch) {
                    // create the Model value
                    var name = className.slice(6);
                    var value = elem.value;

                    // set default values
                    if (name.indexOf("Month") > -1) {
                        value = value - 1;
                    } else if (name.indexOf("Time" > -1)) {
                        value = value || '00:00';
                    }

                    self[name] = self[name] || new Binding(elem, value);
                    elem.binding = elem.binding || self[name];

                    // bind data oninput
                    elem.oninput = function() {
                        self[name].hasChanged = true;
                        self[name] = self[name] || new Binding(elem, value);
                        self[name].value = elem.value;

                        // for callbacks
                        self[name].oninput();
                    };
                }
            });
        });
    };

    /*
    To create a placeholder effect. Assumes that display element starts with 'placeholder' class
     */
    function displayWithPlaceholder(inputBinding, displayElem, placeholder) {
        if (displayElem.classList.contains('placeholder')) {
            displayElem.classList.remove('placeholder');
        };
        if (inputBinding.value === "") {
            inputBinding.value = placeholder;
            // let the model know that input has gone back to default
            inputBinding.hasChanged = false;

            displayElem.classList.add('placeholder');
        }
        displayElem.innerHTML = inputBinding.value;
    };
    var model = new Model();

    // Update the view oninput
    model.title.oninput = function() {
        var titleDisplay = document.querySelector('.title-display');
        displayWithPlaceholder(model.title, titleDisplay, "Untitled Event");
    };

    model.type.oninput = function() {
        var typeDisplay = document.querySelector('.card-detail-actual.what');
        displayWithPlaceholder(model.type, typeDisplay, "Description");
    };

    model.host.oninput = function() {
        var hostDisplay = document.querySelector('.card-detail-actual.who');
        displayWithPlaceholder(model.host, hostDisplay, "Host");
    };

    model.starttime.oninput = function() {
        var startTimeDisplay = document.querySelector('.card-detail-actual.starttime');
        displayWithPlaceholder(model.starttime, startTimeDisplay, "Start Time");
    };

    model.endtime.oninput = function() {
        var endTimeDisplay = document.querySelector('.card-detail-actual.endtime');
        displayWithPlaceholder(model.endtime, endTimeDisplay, "End Time");
    };

    function validate() {
        var self = this;
        var allGood = false;
        var errorMessage = "Please correct the following errors:
";
        var people = getDomNodeArray('.people>div');

        var validations = [{
            errorMessage: "Please include a title.",
            validationMethod: function() {
                return model.title.hasChanged;
            }
        }, {
            errorMessage: "Please include a description.",
            validationMethod: function() {
                return model.description.hasChanged;
            }
        }, {
            errorMessage: "Please include guests.",
            validationMethod: function() {
                if (people.length > 0) {
                    return true;
                } else {
                    return false;
                }
            }
        }, {
            errorMessage: "Please include valid email addresses.",
            validationMethod: function() {
                var areReal = false;
                var emailRegex = new RegExp("^[-a-z0-9~!$%^&*_=+}{\'?]+(\.[-a-z0-9~!$%^&*_=+}{\'?]+)*@([a-z0-9_][-a-z0-9_]*(\.[-a-z0-9_]+)*\.(aero|arpa|biz|com|coop|edu|gov|info|int|mil|museum|name|net|org|pro|travel|mobi|[a-z][a-z])|([0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}))(:[0-9]{1,5})?$");
                people.forEach(function(guest, index) {
                    var result = emailRegex.exec(guest.value);
                    if (result && result['index'] === 0) {
                        if (index === 0) {
                            areReal = true;
                        } else {
                            areReal = areReal && true;
                        }
                    } else {
                        areReal = areReal && false;
                    }
                });
                return areReal;
            }
        }];

        validations.forEach(function(val, index) {
            if (val.validationMethod(self)) {
                if (index === 0) {
                    allGood = true;
                } else {
                    allGood = allGood && true;
                }
            } else {
                allGood = allGood && false;
                errorMessage = errorMessage + val.errorMessage + '
';
            }
        });
        errorMessage = errorMessage.trim();

        return {
            isValid: allGood,
            errorMessage: errorMessage
        }
    };

    var createButton = document.querySelector('button#create');
    createButton.onclick = function() {
        var validState = validate();

        if (!validState.isValid) {
            var errorMessage = document.querySelector('.error-message');
            errorMessage.innerHTML = validState.errorMessage;
        } else {
            alert("Valid form. Thanks for submitting!");
        }
    };

    /*
    Set up Google Maps autocomplete location input
     */
    var input = document.getElementById('location');
    var whereDisplay = document.querySelector('.card-detail-actual.where');
    var autocomplete = new google.maps.places.Autocomplete(input);

    autocomplete.addListener('place_changed', () => {
      let place = autocomplete.getPlace();
      displayWithPlaceholder({
          value: place.formatted_address
      }, whereDisplay, "Location");
    });
})();
<commit_msg>Add newly added guests to card-detail preview<commit_after>var APP = (function() {
    /**
     * Creates an Array of DOM nodes that match the selector
     * @param selector {string} CSS selector - selector to match against
     * @return {array} Array of DOM nodes
     */
    function getDomNodeArray(selector) {
        var nodes = Array.prototype.slice.apply(document.querySelectorAll(selector));
        if (!nodes) {
            nodes = [];
        }
        return nodes;
    }

    /*
    Adding guests
     */
    var newGuest = document.querySelector('input.new-guest');
    var people = document.querySelector('.attendees');
    var guestsDisplay = document.querySelector('.card-detail-actual.guests');
    newGuest.onkeydown = function(evt) {
        if (evt.keyIdentifier === "Enter") {
            let enteredPerson = document.createElement('div');
            let enteredPerson2 = document.createElement('div');
            let deletePerson = document.createElement('button');
            deletePerson.innerHTML = "-";
            deletePerson.parent = enteredPerson;

            deletePerson.onclick = function() {
                let dataAttr = this.parent.getAttribute('data-guest');
                let guestToRemove = document.querySelector(`[data-guest="${dataAttr}"]`);
                people.removeChild(this.parent);
                guestsDisplay.removeChild(guestToRemove); // remove from card-detail preview
            };
            enteredPerson.innerHTML = newGuest.value;
            enteredPerson.setAttribute('data-guest', newGuest.value); // for easy access later
            enteredPerson.appendChild(deletePerson);
            // add to card-detail preview
            enteredPerson2.innerHTML = newGuest.value;
            enteredPerson2.setAttribute('data-guest', newGuest.value); // for easy access later
            guestsDisplay.appendChild(enteredPerson2);

            people.appendChild(enteredPerson);
            newGuest.value = "";
        }
    };
    /*
    Adding functionality to hide/show guest in card-detail preview
     */
    var attendeesCheckbox = document.getElementById('attendees-list');
    var guestsDisplayContainer = document.querySelector('.card-detail.guests');
    attendeesCheckbox.onclick = function() {
      if (this.checked === true) {
        guestsDisplayContainer.classList.remove('hidden')
      } else {
        guestsDisplayContainer.classList.add('hidden');
      }
    }

    /*
    class="model model-*" Refer to inputs by the name after class model-*
     */
    function Model() {
        var self = this;

        function Binding(elem, value) {
            elem = elem || null;
            value = value || null;

            this.elem = elem;
            this.value = value;
            this.hasChanged = false;
            this.oninput = function() {};
        }

        // pulls values from elems of class="model model-*" to create Model's raw info and set up 1-way binding
        getDomNodeArray('.model').forEach(function(elem) {
            elem.classList.forEach(function(className) {
                var possiblyMatch = className.match(/model\-/g);
                if (possiblyMatch) {
                    // create the Model value
                    var name = className.slice(6);
                    var value = elem.value;

                    // set default values
                    if (name.indexOf("Month") > -1) {
                        value = value - 1;
                    } else if (name.indexOf("Time" > -1)) {
                        value = value || '00:00';
                    }

                    self[name] = self[name] || new Binding(elem, value);
                    elem.binding = elem.binding || self[name];

                    // bind data oninput
                    elem.oninput = function() {
                        self[name].hasChanged = true;
                        self[name] = self[name] || new Binding(elem, value);
                        self[name].value = elem.value;

                        // for callbacks
                        self[name].oninput();
                    };
                }
            });
        });
    };

    /*
    To create a placeholder effect. Assumes that display element starts with 'placeholder' class
     */
    function displayWithPlaceholder(inputBinding, displayElem, placeholder) {
        if (displayElem.classList.contains('placeholder')) {
            displayElem.classList.remove('placeholder');
        };
        if (inputBinding.value === "") {
            inputBinding.value = placeholder;
            // let the model know that input has gone back to default
            inputBinding.hasChanged = false;

            displayElem.classList.add('placeholder');
        }
        displayElem.innerHTML = inputBinding.value;
    };
    var model = new Model();

    // Update the view oninput
    model.title.oninput = function() {
        var titleDisplay = document.querySelector('.title-display');
        displayWithPlaceholder(model.title, titleDisplay, "Untitled Event");
    };

    model.type.oninput = function() {
        var typeDisplay = document.querySelector('.card-detail-actual.what');
        displayWithPlaceholder(model.type, typeDisplay, "Description");
    };

    model.host.oninput = function() {
        var hostDisplay = document.querySelector('.card-detail-actual.who');
        displayWithPlaceholder(model.host, hostDisplay, "Host");
    };

    model.starttime.oninput = function() {
        var startTimeDisplay = document.querySelector('.card-detail-actual.starttime');
        displayWithPlaceholder(model.starttime, startTimeDisplay, "Start Time");
    };

    model.endtime.oninput = function() {
        var endTimeDisplay = document.querySelector('.card-detail-actual.endtime');
        displayWithPlaceholder(model.endtime, endTimeDisplay, "End Time");
    };

    function validate() {
        var self = this;
        var allGood = false;
        var errorMessage = "Please correct the following errors:
";
        var people = getDomNodeArray('.people>div');

        var validations = [{
            errorMessage: "Please include a title.",
            validationMethod: function() {
                return model.title.hasChanged;
            }
        }, {
            errorMessage: "Please include a description.",
            validationMethod: function() {
                return model.description.hasChanged;
            }
        }, {
            errorMessage: "Please include guests.",
            validationMethod: function() {
                if (people.length > 0) {
                    return true;
                } else {
                    return false;
                }
            }
        }, {
            errorMessage: "Please include valid email addresses.",
            validationMethod: function() {
                var areReal = false;
                var emailRegex = new RegExp("^[-a-z0-9~!$%^&*_=+}{\'?]+(\.[-a-z0-9~!$%^&*_=+}{\'?]+)*@([a-z0-9_][-a-z0-9_]*(\.[-a-z0-9_]+)*\.(aero|arpa|biz|com|coop|edu|gov|info|int|mil|museum|name|net|org|pro|travel|mobi|[a-z][a-z])|([0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}))(:[0-9]{1,5})?$");
                people.forEach(function(guest, index) {
                    var result = emailRegex.exec(guest.value);
                    if (result && result['index'] === 0) {
                        if (index === 0) {
                            areReal = true;
                        } else {
                            areReal = areReal && true;
                        }
                    } else {
                        areReal = areReal && false;
                    }
                });
                return areReal;
            }
        }];

        validations.forEach(function(val, index) {
            if (val.validationMethod(self)) {
                if (index === 0) {
                    allGood = true;
                } else {
                    allGood = allGood && true;
                }
            } else {
                allGood = allGood && false;
                errorMessage = errorMessage + val.errorMessage + '
';
            }
        });
        errorMessage = errorMessage.trim();

        return {
            isValid: allGood,
            errorMessage: errorMessage
        }
    };

    var createButton = document.querySelector('button#create');
    createButton.onclick = function() {
        var validState = validate();

        if (!validState.isValid) {
            var errorMessage = document.querySelector('.error-message');
            errorMessage.innerHTML = validState.errorMessage;
        } else {
            alert("Valid form. Thanks for submitting!");
        }
    };

    /*
    Set up Google Maps autocomplete location input
     */
    var input = document.getElementById('location');
    var whereDisplay = document.querySelector('.card-detail-actual.where');
    var autocomplete = new google.maps.places.Autocomplete(input);

    autocomplete.addListener('place_changed', () => {
      let place = autocomplete.getPlace();
      displayWithPlaceholder({
          value: place.formatted_address
      }, whereDisplay, "Location");
    });
})();
<|endoftext|>


<commit_before>var net = require('net');
var EventEmitter = require('events').EventEmitter;
var util = require('util');
var ltx = require('ltx');
var StreamParser = require('./stream_parser');
var starttls = require('../starttls');

var NS_XMPP_TLS = exports.NS_XMPP_TLS = 'urn:ietf:params:xml:ns:xmpp-tls';
var NS_STREAM = exports.NS_STREAM = 'http://etherx.jabber.org/streams';
var NS_XMPP_STREAMS = 'urn:ietf:params:xml:ns:xmpp-streams';

/**
 Base class for connection-based streams.

 The socket parameter is optional for incoming connections.

 A note on events: this base class will emit 'rawStanza' and leaves
 'stanza' to Client & Component. Therefore we won't confuse the
 user with stanzas before authentication has finished.
*/

var MAX_RECONNECT_DELAY = 30 * 1000;

function Connection(socket) {
    EventEmitter.call(this);

    this.charset = 'UTF-8';
    this.xmlns = { stream: NS_STREAM };

    this.socket = socket || new net.Socket();
    this.reconnectDelay = 0;

    this.setupStream();

    this.mixins = [];
}

util.inherits(Connection, EventEmitter);
exports.Connection = Connection;

// Defaults
Connection.prototype.charset = 'UTF-8';
Connection.prototype.allowTLS = true;

/**
 Used by both the constructor and by reinitialization in setSecure().
*/
Connection.prototype.setupStream = function() {
    var self = this;
    this.reconnectDelay = 0;
    this.socket.addListener('data', function(data) {
        self.onData(data);
    });
    this.socket.addListener('end', function() {
        self.onEnd();
    });
    this.socket.addListener('error', function() {
	/* unhandled errors may throw up in node, preventing a reconnect */
        self.onEnd();
    });
    this.socket.addListener('close', function() {
	self.onClose();
    });
    var proxyEvent = function(event) {
        self.socket.addListener(event, function() {
	    var args = Array.prototype.slice.call(arguments);
	    args.unshift(event);
	    self.emit.apply(self, args);
        });
    };
    proxyEvent('data');  // let them sniff unparsed XML
    proxyEvent('drain');
    proxyEvent('close');

    /**
     * This is optimized for continuous TCP streams. If your "socket"
     * actually transports frames (WebSockets) and you can't have
     * stanzas split across those, use:
     *     cb(el.toString());
     */
    if (!this.socket.serializeStanza) {
        this.socket.serializeStanza = function(el, cb) {
            // Continuously write out
            el.write(function(s) {
                cb(s);
            });
        };
    }
};

/** Climbs the stanza up if a child was passed,
    but you can send strings and buffers too.

    Returns whether the socket flushed data.
*/
Connection.prototype.send = function(stanza) {
    var self = this;
    var flushed = true;
    if (!this.socket.writable) {
        this.socket.end();
        return;
    }

    if (stanza.root) {
        var el = this.rmStreamNs(stanza.root());
        this.socket.serializeStanza(el, function(s) {
            flushed = self.socket.write(s);
        });
    } else {
        flushed = this.socket.write(stanza);
    }
    return flushed;
};

Connection.prototype.startParser = function() {
    var self = this;
    this.parser = new StreamParser.StreamParser(this.charset, this.maxStanzaSize);

    this.parser.addListener('start', function(attrs) {
        self.streamAttrs = attrs;
        /* We need those xmlns often, store them extra */
        self.streamNsAttrs = {};
        for(var k in attrs) {
        if (k == 'xmlns' ||
            k.substr(0, 6) == 'xmlns:')
                self.streamNsAttrs[k] = attrs[k];
        }

        /* Notify in case we don't wait for <stream:features/>
           (Component or non-1.0 streams)
         */
        self.emit('streamStart', attrs);
    });
    this.parser.addListener('stanza', function(stanza) {
        self.onStanza(self.addStreamNs(stanza));
    });
    this.parser.addListener('error', function(e) {
        self.error(e.condition || 'internal-server-error', e.message);
    });
    this.parser.addListener('end', function() {
        self.stopParser();
        self.end();
    });
};

Connection.prototype.stopParser = function() {
    /* No more events, please (may happen however) */
    if(this.parser) {
        this.parser.stop();
        /* Get GC'ed */
        delete this.parser;
    }
};

Connection.prototype.startStream = function() {
    var attrs = {};
    for(var k in this.xmlns) {
        if (this.xmlns.hasOwnProperty(k)) {
            if (!k)
                attrs.xmlns = this.xmlns[k];
            else
                attrs['xmlns:' + k] = this.xmlns[k];
        }
    }
    if (this.xmppVersion)
        attrs.version = this.xmppVersion;
    if (this.streamTo)
        attrs.to = this.streamTo;
    if (this.streamId)
        attrs.id = this.streamId;

    var el = new ltx.Element('stream:stream', attrs);
    // make it non-empty to cut the closing tag
    el.t(' ');
    var s = el.toString();
    this.send(s.substr(0, s.indexOf(' </stream:stream>')));

    this.streamOpened = true;
};

Connection.prototype.onData = function(data) {
    if (this.parser)
        this.parser.write(data);
};

Connection.prototype.setSecure = function(credentials, isServer) {
    var self = this;
    this.stopParser();

    // Remove old event listeners
    this.socket.removeAllListeners('data');
    // retain socket 'end' listeners because ssl layer doesn't support it
    this.socket.removeAllListeners('drain');
    this.socket.removeAllListeners('close');

    var ct = starttls(this.socket, credentials || this.credentials, isServer, function() {
	self.isSecure = true;
	if (!isServer)
	    // Clients start <stream:stream>, servers reply
	    self.startStream();
        self.startParser();
    });
    ct.on('close', function() {
	self.onClose();
    });

    // The socket is now the cleartext stream
    this.socket = ct;

    // Attach new listeners on the cleartext stream
    this.setupStream();
};

/**
 * This is not an event listener, but takes care of the TLS handshake
 * before 'rawStanza' events are emitted to the derived classes.
 */
Connection.prototype.onStanza = function(stanza) {
    if (stanza.is('error', NS_STREAM)) {
        /* TODO: extract error text */
        this.emit('error', stanza);
    } else if (stanza.is('features', NS_STREAM) &&
               this.allowTLS &&
	       !this.isSecure &&
               stanza.getChild('starttls', NS_XMPP_TLS)) {
        /* Signal willingness to perform TLS handshake */
        this.send(new ltx.Element('starttls', { xmlns: NS_XMPP_TLS }));
    } else if (this.allowTLS &&
               stanza.is('proceed', NS_XMPP_TLS)) {
        /* Server is waiting for TLS handshake */
        this.setSecure();
    } else {
        this.emit('rawStanza', stanza);
    }
};

/**
 * Add stream xmlns to a stanza
 *
 * Does not add our default xmlns as it is different for
 * C2S/S2S/Component connections.
 */
Connection.prototype.addStreamNs = function(stanza) {
    for(var attr in this.streamNsAttrs) {
        if (!stanza.attrs[attr] &&
	    !(attr === 'xmlns' &&
	      this.streamNsAttrs[attr] === this.xmlns['']))
            stanza.attrs[attr] = this.streamNsAttrs[attr];
    }
    return stanza;
};

/**
 * Remove superfluous xmlns that were aleady declared in
 * our <stream:stream>
 */
Connection.prototype.rmStreamNs = function(stanza) {
    for(var prefix in this.xmlns) {
        var attr = prefix ? 'xmlns:'+prefix : 'xmlns';
        if (stanza.attrs[attr] == this.xmlns[prefix])
            delete stanza.attrs[attr];
    }
    return stanza;
};

/**
 * Connection has been ended by remote, we will not get any incoming
 * 'data' events. Alternatively, used for 'error' event.
 */
Connection.prototype.onEnd = function() {
    this.stopParser();
    if(this.socket)
        this.socket.end();
};

/**
 * XMPP-style end connection for user
 */
Connection.prototype.end = function() {
    if (this.socket.writable) {
        if (this.streamOpened) {
            this.socket.write('</stream:stream>');
            delete this.streamOpened;
	    /* wait for being called again upon 'end' from other side */
        } else {
            this.socket.end();
        }
    }
};

Connection.prototype.onClose = function() {
    if (!this.socket)
	/* A reconnect may have already been scheduled */
	return;

    delete this.socket;
    if (this.reconnect) {
	var self = this;
	setTimeout(function() {
	    self.socket = new net.Stream();
	    self.setupStream();
	    self.reconnect();
	}, this.reconnectDelay);
	console.log("Reconnect in", this.reconnectDelay);
	this.reconnectDelay += Math.ceil(Math.random() * 2000);
	if (this.reconnectDelay > MAX_RECONNECT_DELAY)
	    this.reconnectDelay = MAX_RECONNECT_DELAY;
    }
};

/**
 * End connection with stream error.
 * Emits 'error' event too.
 *
 * @param {String} condition XMPP error condition, see RFC3920 4.7.3. Defined Conditions
 * @param {String} text Optional error message
 */
Connection.prototype.error = function(condition, message) {
    this.emit('error', new Error(message));

    if (!this.socket.writable)
        return;

    if(!this.streamOpened)
        this.startStream(); /* RFC 3920, 4.7.1 stream-level errors rules */

    var e = new ltx.Element('stream:error');
    e.c(condition, { xmlns: NS_XMPP_STREAMS });
    if (message)
        e.c('text', { xmlns: NS_XMPP_STREAMS,
                      'xml:lang': 'en' }).
        t(message);

    this.send(e);
    this.end();
};

/**
 * Adds a mixin to this Connection for implementing higher-level functionality.
 *
 * A mixin is a module that exports a single function, taking the
 * Connection as its value. The module can perform its own stanza-handling,
 * event emission and other functionality. Note that mixins can only be
 * added once.
*/
Connection.prototype.addMixin = function(mixin, mixinArgs) {
    if(this.mixins.indexOf(mixin) == -1) {
        mixin(this, mixinArgs);
        this.mixins = this.mixins.concat(mixin);
    }
};
<commit_msg>we can use a more elegant way so we don't need to check for existence<commit_after>var net = require('net');
var EventEmitter = require('events').EventEmitter;
var util = require('util');
var ltx = require('ltx');
var StreamParser = require('./stream_parser');
var starttls = require('../starttls');

var NS_XMPP_TLS = exports.NS_XMPP_TLS = 'urn:ietf:params:xml:ns:xmpp-tls';
var NS_STREAM = exports.NS_STREAM = 'http://etherx.jabber.org/streams';
var NS_XMPP_STREAMS = 'urn:ietf:params:xml:ns:xmpp-streams';

/**
 Base class for connection-based streams.

 The socket parameter is optional for incoming connections.

 A note on events: this base class will emit 'rawStanza' and leaves
 'stanza' to Client & Component. Therefore we won't confuse the
 user with stanzas before authentication has finished.
*/

var MAX_RECONNECT_DELAY = 30 * 1000;

function Connection(socket) {
    EventEmitter.call(this);

    this.charset = 'UTF-8';
    this.xmlns = { stream: NS_STREAM };

    this.socket = socket || new net.Socket();
    this.reconnectDelay = 0;

    this.setupStream();

    this.mixins = [];
}

util.inherits(Connection, EventEmitter);
exports.Connection = Connection;

// Defaults
Connection.prototype.charset = 'UTF-8';
Connection.prototype.allowTLS = true;

/**
 Used by both the constructor and by reinitialization in setSecure().
*/
Connection.prototype.setupStream = function() {
    var self = this;
    this.reconnectDelay = 0;
    this.socket.addListener('data', function(data) {
        self.onData(data);
    });
    this.socket.addListener('end', function() {
        self.onEnd();
    });
    this.socket.addListener('error', function() {
	/* unhandled errors may throw up in node, preventing a reconnect */
        self.onEnd();
    });
    this.socket.addListener('close', function() {
	self.onClose();
    });
    var proxyEvent = function(event) {
        self.socket.addListener(event, function() {
	    var args = Array.prototype.slice.call(arguments);
	    args.unshift(event);
	    self.emit.apply(self, args);
        });
    };
    proxyEvent('data');  // let them sniff unparsed XML
    proxyEvent('drain');
    proxyEvent('close');

    /**
     * This is optimized for continuous TCP streams. If your "socket"
     * actually transports frames (WebSockets) and you can't have
     * stanzas split across those, use:
     *     cb(el.toString());
     */
    if (!this.socket.serializeStanza) {
        this.socket.serializeStanza = function(el, cb) {
            // Continuously write out
            el.write(function(s) {
                cb(s);
            });
        };
    }
};

/** Climbs the stanza up if a child was passed,
    but you can send strings and buffers too.

    Returns whether the socket flushed data.
*/
Connection.prototype.send = function(stanza) {
    var self = this;
    var flushed = true;
    if (!this.socket.writable) {
        this.socket.end();
        return;
    }

    if (stanza.root) {
        var el = this.rmStreamNs(stanza.root());
        this.socket.serializeStanza(el, function(s) {
            flushed = self.socket.write(s);
        });
    } else {
        flushed = this.socket.write(stanza);
    }
    return flushed;
};

Connection.prototype.startParser = function() {
    var self = this;
    this.parser = new StreamParser.StreamParser(this.charset, this.maxStanzaSize);

    this.parser.addListener('start', function(attrs) {
        self.streamAttrs = attrs;
        /* We need those xmlns often, store them extra */
        self.streamNsAttrs = {};
        for(var k in attrs) {
        if (k == 'xmlns' ||
            k.substr(0, 6) == 'xmlns:')
                self.streamNsAttrs[k] = attrs[k];
        }

        /* Notify in case we don't wait for <stream:features/>
           (Component or non-1.0 streams)
         */
        self.emit('streamStart', attrs);
    });
    this.parser.addListener('stanza', function(stanza) {
        self.onStanza(self.addStreamNs(stanza));
    });
    this.parser.addListener('error', function(e) {
        self.error(e.condition || 'internal-server-error', e.message);
    });
    this.parser.addListener('end', function() {
        self.stopParser();
        self.end();
    });
};

Connection.prototype.stopParser = function() {
    /* No more events, please (may happen however) */
    if(this.parser) {
        this.parser.stop();
        /* Get GC'ed */
        delete this.parser;
    }
};

Connection.prototype.startStream = function() {
    var attrs = {};
    for(var k in this.xmlns) {
        if (this.xmlns.hasOwnProperty(k)) {
            if (!k)
                attrs.xmlns = this.xmlns[k];
            else
                attrs['xmlns:' + k] = this.xmlns[k];
        }
    }
    if (this.xmppVersion)
        attrs.version = this.xmppVersion;
    if (this.streamTo)
        attrs.to = this.streamTo;
    if (this.streamId)
        attrs.id = this.streamId;

    var el = new ltx.Element('stream:stream', attrs);
    // make it non-empty to cut the closing tag
    el.t(' ');
    var s = el.toString();
    this.send(s.substr(0, s.indexOf(' </stream:stream>')));

    this.streamOpened = true;
};

Connection.prototype.onData = function(data) {
    if (this.parser)
        this.parser.write(data);
};

Connection.prototype.setSecure = function(credentials, isServer) {
    var self = this;
    this.stopParser();

    // Remove old event listeners
    this.socket.removeAllListeners('data');
    // retain socket 'end' listeners because ssl layer doesn't support it
    this.socket.removeAllListeners('drain');
    this.socket.removeAllListeners('close');

    var ct = starttls(this.socket, credentials || this.credentials, isServer, function() {
	self.isSecure = true;
	if (!isServer)
	    // Clients start <stream:stream>, servers reply
	    self.startStream();
        self.startParser();
    });
    ct.on('close', function() {
	self.onClose();
    });

    // The socket is now the cleartext stream
    this.socket = ct;

    // Attach new listeners on the cleartext stream
    this.setupStream();
};

/**
 * This is not an event listener, but takes care of the TLS handshake
 * before 'rawStanza' events are emitted to the derived classes.
 */
Connection.prototype.onStanza = function(stanza) {
    if (stanza.is('error', NS_STREAM)) {
        /* TODO: extract error text */
        this.emit('error', stanza);
    } else if (stanza.is('features', NS_STREAM) &&
               this.allowTLS &&
	       !this.isSecure &&
               stanza.getChild('starttls', NS_XMPP_TLS)) {
        /* Signal willingness to perform TLS handshake */
        this.send(new ltx.Element('starttls', { xmlns: NS_XMPP_TLS }));
    } else if (this.allowTLS &&
               stanza.is('proceed', NS_XMPP_TLS)) {
        /* Server is waiting for TLS handshake */
        this.setSecure();
    } else {
        this.emit('rawStanza', stanza);
    }
};

/**
 * Add stream xmlns to a stanza
 *
 * Does not add our default xmlns as it is different for
 * C2S/S2S/Component connections.
 */
Connection.prototype.addStreamNs = function(stanza) {
    for(var attr in this.streamNsAttrs) {
        if (!stanza.attrs[attr] &&
	    !(attr === 'xmlns' &&
	      this.streamNsAttrs[attr] === this.xmlns['']))
            stanza.attrs[attr] = this.streamNsAttrs[attr];
    }
    return stanza;
};

/**
 * Remove superfluous xmlns that were aleady declared in
 * our <stream:stream>
 */
Connection.prototype.rmStreamNs = function(stanza) {
    for(var prefix in this.xmlns) {
        var attr = prefix ? 'xmlns:'+prefix : 'xmlns';
        if (stanza.attrs[attr] == this.xmlns[prefix])
            delete stanza.attrs[attr];
    }
    return stanza;
};

/**
 * Connection has been ended by remote, we will not get any incoming
 * 'data' events. Alternatively, used for 'error' event.
 */
Connection.prototype.onEnd = function() {
    this.stopParser();
    this.socket.end();
};

/**
 * XMPP-style end connection for user
 */
Connection.prototype.end = function() {
    if (this.socket.writable) {
        if (this.streamOpened) {
            this.socket.write('</stream:stream>');
            delete this.streamOpened;
	    /* wait for being called again upon 'end' from other side */
        } else {
            this.socket.end();
        }
    }
};

Connection.prototype.onClose = function() {
    if (!this.socket)
	/* A reconnect may have already been scheduled */
	return;

    delete this.socket;
    if (this.reconnect) {
	var self = this;
	setTimeout(function() {
	    self.socket = new net.Stream();
	    self.setupStream();
	    self.reconnect();
	}, this.reconnectDelay);
	console.log("Reconnect in", this.reconnectDelay);
	this.reconnectDelay += Math.ceil(Math.random() * 2000);
	if (this.reconnectDelay > MAX_RECONNECT_DELAY)
	    this.reconnectDelay = MAX_RECONNECT_DELAY;
    }
};

/**
 * End connection with stream error.
 * Emits 'error' event too.
 *
 * @param {String} condition XMPP error condition, see RFC3920 4.7.3. Defined Conditions
 * @param {String} text Optional error message
 */
Connection.prototype.error = function(condition, message) {
    this.emit('error', new Error(message));

    if (!this.socket.writable)
        return;

    if(!this.streamOpened)
        this.startStream(); /* RFC 3920, 4.7.1 stream-level errors rules */

    var e = new ltx.Element('stream:error');
    e.c(condition, { xmlns: NS_XMPP_STREAMS });
    if (message)
        e.c('text', { xmlns: NS_XMPP_STREAMS,
                      'xml:lang': 'en' }).
        t(message);

    this.send(e);
    this.end();
};

/**
 * Adds a mixin to this Connection for implementing higher-level functionality.
 *
 * A mixin is a module that exports a single function, taking the
 * Connection as its value. The module can perform its own stanza-handling,
 * event emission and other functionality. Note that mixins can only be
 * added once.
*/
Connection.prototype.addMixin = function(mixin, mixinArgs) {
    if(this.mixins.indexOf(mixin) == -1) {
        mixin(this, mixinArgs);
        this.mixins = this.mixins.concat(mixin);
    }
};
<|endoftext|>


<commit_before>//
// Mocha is a test runner https://mochajs.org/
//
// Chai is an assertion library http://chaijs.com/
//

// expect is behavior driven development (bdd) style assertions
const expect = chai.expect;

describe(`Mocha+Chai Inclass Fetch Exercise for "${inclass.author}"`, () => {
    const baseURL = 'https://webdev-dummy.herokuapp.com'
    const sample = `${baseURL}/sample`

    let longest = null

    const findLargest = (done) => {
        if (!longest) {
            try {
                inclass.countWords(sample).then(r => {
                    Object.keys(r).forEach(key => {
                        if (!longest) longest = key
                        if (r[key] > r[longest]) {
                            longest = key
                        }
                    })
                })
                .then(done)
            } catch (e) {
                done()
            }
        } else {
            done()
        }
    }

	beforeEach((done) => findLargest(done))

    it('author should be defined', () => {
        expect(inclass.author).to.be.ok
    })

	it('countWords should count the number of words in each article', (done) => {
        inclass.countWords(sample)
        .then(r => {
            expect(Object.keys(r).length).to.equal(15)
            Object.keys(r).forEach(key => {
                expect(r[key]).to.be.above(0)
            })
            done()
        })
        .catch(done)
	})

    it('countWords should pass through errors', (done) => {
        inclass.countWords(`${baseURL}/badURL`)
        .then(r => {
            done(new Error('Received response instead of error ${r}'))
        })
        .catch(e => {
            done()
        })
    })

    it('getLargest should give me the id of the article with the most number of words', (done) => {
        inclass.getLargest(sample)
        .then(r => { expect(parseInt(r)).to.equal(parseInt(longest)); done() })
        .catch(done)
    })

    it('countWordsSafe should catch all errors and return an empty object', (done) => {
        inclass.countWordsSafe(`${baseURL}/badURL`)
        .then(r => { expect(Object.keys(r).length).to.equal(0); done() })
        .catch(done)
    })

    it('countWordsSafe should count the number of words in each article', (done) => {
        inclass.countWordsSafe(sample)
        .then(r => {
            expect(Object.keys(r).length).to.equal(15)
            Object.keys(r).forEach(key => {
                expect(r[key]).to.be.above(0)
            })
            done()
        })
        .catch(done)
    })

})
<commit_msg>update<commit_after>//
// Mocha is a test runner https://mochajs.org/
//
// Chai is an assertion library http://chaijs.com/
//

// expect is behavior driven development (bdd) style assertions
const expect = chai.expect;

describe(`Mocha+Chai Inclass Fetch Exercise for "${inclass.author}"`, () => {
    const baseURL = 'https://webdev-dummy.herokuapp.com'
    const sample = `${baseURL}/sample`

    let longest = null

    const findLargest = (done) => {
        if (!longest) {
            try {
                inclass.countWords(sample).then(r => {
                    Object.keys(r).forEach(key => {
                        if (!longest) longest = key
                        if (r[key] > r[longest]) {
                            longest = key
                        }
                    })
                })
                .then(done)
            } catch (e) {
                done()
            }
        } else {
            done()
        }
    }

	beforeEach((done) => findLargest(done))

    it('author should be defined', () => {
        expect(inclass.author).to.be.ok
    })

	it('countWords should count the number of words in each article', (done) => {
        inclass.countWords(sample)
        .then(r => {
            expect(Object.keys(r).length).to.equal(15)
            Object.keys(r).forEach(key => {
                expect(r[key]).to.be.above(0)
            })
            done()
        })
        .catch(done)
	})

    it('countWords should pass through errors', (done) => {
        inclass.countWords(`${baseURL}/badURL`)
        .then(r => {
            done(new Error(`Received response instead of error ${r}`))
        })
        .catch(e => {
            done()
        })
    })

    it('getLargest should give me the id of the article with the most number of words', (done) => {
        inclass.getLargest(sample)
        .then(r => { expect(parseInt(r)).to.equal(parseInt(longest)); done() })
        .catch(done)
    })

    it('countWordsSafe should catch all errors and return an empty object', (done) => {
        inclass.countWordsSafe(`${baseURL}/badURL`)
        .then(r => { expect(Object.keys(r).length).to.equal(0); done() })
        .catch(done)
    })

    it('countWordsSafe should count the number of words in each article', (done) => {
        inclass.countWordsSafe(sample)
        .then(r => {
            expect(Object.keys(r).length).to.equal(15)
            Object.keys(r).forEach(key => {
                expect(r[key]).to.be.above(0)
            })
            done()
        })
        .catch(done)
    })

})
<|endoftext|>


<commit_before>console.info("welcome to my Auction Trader live auction website :)");
console.info("my Auction Trader is an Aucion Overdrive company, All Rights Reserved");
//alert("This is a BETA version of my Auction Trader, please do not bid yet!");
<commit_msg>add console alert to pre prod<commit_after>console.info("welcome to my Auction Trader live auction website :)");
console.info("my Auction Trader is an Aucion Overdrive company, All Rights Reserved");
alert("You are viewing a PRE-PRODUCTION release of My Auction Trader, please do not place any bids!");
<|endoftext|>


<commit_before>import deferrable from 'golike-defer'
import find from 'lodash/find'
import gte from 'lodash/gte'
import isEmpty from 'lodash/isEmpty'
import lte from 'lodash/lte'

import {
  forEach,
  mapToArray,
  noop,
  parseSize,
  pCatch
} from '../../utils'

import {
  isVmHvm,
  isVmRunning,
  makeEditObject,
  NULL_REF
} from '../utils'

export default {
  // TODO: clean up on error.
  @deferrable.onFailure
  async createVm ($onFailure, templateId, {
    name_label, // deprecated
    nameLabel = name_label, // eslint-disable-line camelcase

    clone = true,
    installRepository = undefined,
    vdis = undefined,
    vifs = undefined,
    existingVdis = undefined,

    coreOs = false,
    cloudConfig = undefined,

    ...props
  } = {}, checkLimits) {
    const installMethod = (() => {
      if (installRepository == null) {
        return 'none'
      }

      try {
        installRepository = this.getObject(installRepository)
        return 'cd'
      } catch (_) {
        return 'network'
      }
    })()
    const template = this.getObject(templateId)

    // Clones the template.
    const vmRef = await this[clone ? '_cloneVm' : '_copyVm'](template, nameLabel)
    $onFailure(() => this.deleteVm(vmRef)::pCatch(noop))

    // TODO: copy BIOS strings?

    // Removes disks from the provision XML, we will create them by
    // ourselves.
    await this.call('VM.remove_from_other_config', vmRef, 'disks')::pCatch(noop)

    // Creates the VDIs and executes the initial steps of the
    // installation.
    await this.call('VM.provision', vmRef)

    let vm = await this._getOrWaitObject(vmRef)

    // Set VMs params.
    await this._editVm(vm, props, checkLimits)

    // Sets boot parameters.
    {
      const isHvm = isVmHvm(vm)

      if (isHvm) {
        if (!isEmpty(vdis) || installMethod === 'network') {
          const { HVM_boot_params: bootParams } = vm
          let order = bootParams.order
          if (order) {
            order = 'n' + order.replace('n', '')
          } else {
            order = 'ncd'
          }

          this._setObjectProperties(vm, {
            HVM_boot_params: { ...bootParams, order }
          })
        }
      } else { // PV
        if (vm.PV_bootloader === 'eliloader') {
          if (installMethod === 'network') {
            // TODO: normalize RHEL URL?

            await this._updateObjectMapProperty(vm, 'other_config', {
              'install-repository': installRepository
            })
          } else if (installMethod === 'cd') {
            await this._updateObjectMapProperty(vm, 'other_config', {
              'install-repository': 'cdrom'
            })
          }
        }
      }
    }

    let nVbds = vm.VBDs.length
    let hasBootableDisk = !!find(vm.$VBDs, 'bootable')

    // Inserts the CD if necessary.
    if (installMethod === 'cd') {
      // When the VM is started, if PV, the CD drive will become not
      // bootable and the first disk bootable.
      await this._insertCdIntoVm(installRepository, vm, {
        bootable: true
      })
      hasBootableDisk = true

      ++nVbds
    }

    // Modify existing (previous template) disks if necessary
    existingVdis && await Promise.all(mapToArray(existingVdis, async ({ size, $SR: srId, ...properties }, userdevice) => {
      const vbd = find(vm.$VBDs, { userdevice })
      if (!vbd) {
        return
      }
      const vdi = vbd.$VDI
      await this._setObjectProperties(vdi, properties)

      // if the disk is bigger
      if (
        size != null &&
        size > vdi.virtual_size
      ) {
        await this.resizeVdi(vdi.$id, size)
      }
      // if another SR is set, move it there
      if (srId) {
        await this.moveVdi(vdi.$id, srId)
      }
    }))

    // Creates the user defined VDIs.
    //
    // TODO: set vm.suspend_SR
    if (!isEmpty(vdis)) {
      const devices = await this.call('VM.get_allowed_VBD_devices', vm.$ref)
      await Promise.all(mapToArray(vdis, (vdiDescription, i) => {
        ++nVbds

        return this._createVdi(
          vdiDescription.size, // FIXME: Should not be done in Xapi.
          {
            name_label: vdiDescription.name_label,
            name_description: vdiDescription.name_description,
            sr: vdiDescription.sr || vdiDescription.SR
          }
        )
          .then(ref => this._getOrWaitObject(ref))
          .then(vdi => this._createVbd(vm, vdi, {
            // Either the CD or the 1st disk is bootable (only useful for PV VMs)
            bootable: !(hasBootableDisk || i),
            userdevice: devices[i]
          }))
      }))
    }

    // Destroys the VIFs cloned from the template.
    await Promise.all(mapToArray(vm.$VIFs, vif => this._deleteVif(vif)))

    // Creates the VIFs specified by the user.
    let nVifs = 0
    if (vifs) {
      const devices = await this.call('VM.get_allowed_VIF_devices', vm.$ref)
      await Promise.all(mapToArray(vifs, (vif, index) => {
        ++nVifs

        return this._createVif(
          vm,
          this.getObject(vif.network),
          {
            ipv4_allowed: vif.ipv4_allowed,
            ipv6_allowed: vif.ipv6_allowed,
            device: devices[index],
            locking_mode: isEmpty(vif.ipv4_allowed) && isEmpty(vif.ipv6_allowed) ? 'network_default' : 'locked',
            mac: vif.mac,
            mtu: vif.mtu
          }
        )
      }))
    }

    // TODO: Assign VGPUs.

    if (cloudConfig != null) {
      // Refresh the record.
      vm = await this._waitObjectState(vm.$id, vm => vm.VBDs.length === nVbds)

      // Find the SR of the first VDI.
      let srRef
      forEach(vm.$VBDs, vbd => {
        let vdi
        if (
          vbd.type === 'Disk' &&
          (vdi = vbd.$VDI)
        ) {
          srRef = vdi.SR
          return false
        }
      })

      const method = coreOs
        ? 'createCoreOsCloudInitConfigDrive'
        : 'createCloudInitConfigDrive'
      await this[method](vm.$id, srRef, cloudConfig)

      ++nVbds
    }

    // wait for the record with all the VBDs and VIFs
    return this._waitObjectState(vm.$id, vm =>
      vm.VBDs.length === nVbds &&
      vm.VIFs.length === nVifs
    )
  },

  // High level method to edit a VM.
  //
  // Params do not correspond directly to XAPI props.
  _editVm: makeEditObject({
    affinityHost: {
      get: 'affinity',
      set (value, vm) {
        return this._setObjectProperty(
          vm,
          'affinity',
          value ? this.getObject(value).$ref : NULL_REF
        )
      }
    },

    autoPoweron: {
      set (value, vm) {
        return Promise.all([
          this._updateObjectMapProperty(vm, 'other_config', {
            autoPoweron: value ? 'true' : null
          }),
          value && this.setPoolProperties({
            autoPoweron: true
          })
        ])
      }
    },

    CPUs: 'cpus',
    cpus: {
      addToLimits: true,

      // Current value may have constraints with other values.
      //
      // If the other value is not set and the constraint is not
      // respected, the other value is changed first.
      constraints: {
        cpusStaticMax: gte
      },

      get: vm => +vm.VCPUs_at_startup,
      set: [
        'VCPUs_at_startup',
        function (value, vm) {
          return isVmRunning(vm) && this._set('VCPUs_number_live', value)
        }
      ]
    },

    cpuCap: {
      get: vm => vm.VCPUs_params.cap && +vm.VCPUs_params.cap,
      set (cap, vm) {
        return this._updateObjectMapProperty(vm, 'VCPUs_params', { cap })
      }
    },

    cpusMax: 'cpusStaticMax',
    cpusStaticMax: {
      constraints: {
        cpus: lte
      },
      get: vm => +vm.VCPUs_max,
      set: 'VCPUs_max'
    },

    cpuWeight: {
      get: vm => vm.VCPUs_params.weight && +vm.VCPUs_params.weight,
      set (weight, vm) {
        return this._updateObjectMapProperty(vm, 'VCPUs_params', { weight })
      }
    },

    highAvailability: {
      set (ha, vm) {
        return this.call('VM.set_ha_restart_priority', vm.$ref, ha ? 'restart' : '')
      }
    },

    memoryMin: {
      constraints: {
        memoryMax: gte
      },
      get: vm => +vm.memory_dynamic_min,
      preprocess: parseSize,
      set: 'memory_dynamic_min'
    },

    memory: 'memoryMax',
    memoryMax: {
      addToLimits: true,
      limitName: 'memory',
      constraints: {
        memoryMin: lte,
        memoryStaticMax: gte
      },
      get: vm => +vm.memory_dynamic_max,
      preprocess: parseSize,
      set: 'memory_dynamic_max'
    },

    memoryStaticMax: {
      constraints: {
        memoryMax: lte
      },
      get: vm => +vm.memory_static_max,
      preprocess: parseSize,
      set: 'memory_static_max'
    },

    nameDescription: true,

    nameLabel: true,

    PV_args: true,

    tags: true
  }),

  async editVm (id, props, checkLimits) {
    return /* await */ this._editVm(this.getObject(id), props, checkLimits)
  },

  async revertVm (snapshotId, snapshotBefore = true) {
    const snapshot = this.getObject(snapshotId)
    if (snapshotBefore) {
      await this._snapshotVm(snapshot.$snapshot_of)
    }
    return this.call('VM.revert', snapshot.$ref)
  },

  async resumeVm (vmId) {
    // the force parameter is always true
    return this.call('VM.resume', this.getObject(vmId).$ref, false, true)
  }
}
<commit_msg>feat(vm.revertSnapshot): restore power state (#526)<commit_after>import deferrable from 'golike-defer'
import find from 'lodash/find'
import gte from 'lodash/gte'
import isEmpty from 'lodash/isEmpty'
import lte from 'lodash/lte'

import {
  forEach,
  mapToArray,
  noop,
  parseSize,
  pCatch
} from '../../utils'

import {
  isVmHvm,
  isVmRunning,
  makeEditObject,
  NULL_REF
} from '../utils'

export default {
  // TODO: clean up on error.
  @deferrable.onFailure
  async createVm ($onFailure, templateId, {
    name_label, // deprecated
    nameLabel = name_label, // eslint-disable-line camelcase

    clone = true,
    installRepository = undefined,
    vdis = undefined,
    vifs = undefined,
    existingVdis = undefined,

    coreOs = false,
    cloudConfig = undefined,

    ...props
  } = {}, checkLimits) {
    const installMethod = (() => {
      if (installRepository == null) {
        return 'none'
      }

      try {
        installRepository = this.getObject(installRepository)
        return 'cd'
      } catch (_) {
        return 'network'
      }
    })()
    const template = this.getObject(templateId)

    // Clones the template.
    const vmRef = await this[clone ? '_cloneVm' : '_copyVm'](template, nameLabel)
    $onFailure(() => this.deleteVm(vmRef)::pCatch(noop))

    // TODO: copy BIOS strings?

    // Removes disks from the provision XML, we will create them by
    // ourselves.
    await this.call('VM.remove_from_other_config', vmRef, 'disks')::pCatch(noop)

    // Creates the VDIs and executes the initial steps of the
    // installation.
    await this.call('VM.provision', vmRef)

    let vm = await this._getOrWaitObject(vmRef)

    // Set VMs params.
    await this._editVm(vm, props, checkLimits)

    // Sets boot parameters.
    {
      const isHvm = isVmHvm(vm)

      if (isHvm) {
        if (!isEmpty(vdis) || installMethod === 'network') {
          const { HVM_boot_params: bootParams } = vm
          let order = bootParams.order
          if (order) {
            order = 'n' + order.replace('n', '')
          } else {
            order = 'ncd'
          }

          this._setObjectProperties(vm, {
            HVM_boot_params: { ...bootParams, order }
          })
        }
      } else { // PV
        if (vm.PV_bootloader === 'eliloader') {
          if (installMethod === 'network') {
            // TODO: normalize RHEL URL?

            await this._updateObjectMapProperty(vm, 'other_config', {
              'install-repository': installRepository
            })
          } else if (installMethod === 'cd') {
            await this._updateObjectMapProperty(vm, 'other_config', {
              'install-repository': 'cdrom'
            })
          }
        }
      }
    }

    let nVbds = vm.VBDs.length
    let hasBootableDisk = !!find(vm.$VBDs, 'bootable')

    // Inserts the CD if necessary.
    if (installMethod === 'cd') {
      // When the VM is started, if PV, the CD drive will become not
      // bootable and the first disk bootable.
      await this._insertCdIntoVm(installRepository, vm, {
        bootable: true
      })
      hasBootableDisk = true

      ++nVbds
    }

    // Modify existing (previous template) disks if necessary
    existingVdis && await Promise.all(mapToArray(existingVdis, async ({ size, $SR: srId, ...properties }, userdevice) => {
      const vbd = find(vm.$VBDs, { userdevice })
      if (!vbd) {
        return
      }
      const vdi = vbd.$VDI
      await this._setObjectProperties(vdi, properties)

      // if the disk is bigger
      if (
        size != null &&
        size > vdi.virtual_size
      ) {
        await this.resizeVdi(vdi.$id, size)
      }
      // if another SR is set, move it there
      if (srId) {
        await this.moveVdi(vdi.$id, srId)
      }
    }))

    // Creates the user defined VDIs.
    //
    // TODO: set vm.suspend_SR
    if (!isEmpty(vdis)) {
      const devices = await this.call('VM.get_allowed_VBD_devices', vm.$ref)
      await Promise.all(mapToArray(vdis, (vdiDescription, i) => {
        ++nVbds

        return this._createVdi(
          vdiDescription.size, // FIXME: Should not be done in Xapi.
          {
            name_label: vdiDescription.name_label,
            name_description: vdiDescription.name_description,
            sr: vdiDescription.sr || vdiDescription.SR
          }
        )
          .then(ref => this._getOrWaitObject(ref))
          .then(vdi => this._createVbd(vm, vdi, {
            // Either the CD or the 1st disk is bootable (only useful for PV VMs)
            bootable: !(hasBootableDisk || i),
            userdevice: devices[i]
          }))
      }))
    }

    // Destroys the VIFs cloned from the template.
    await Promise.all(mapToArray(vm.$VIFs, vif => this._deleteVif(vif)))

    // Creates the VIFs specified by the user.
    let nVifs = 0
    if (vifs) {
      const devices = await this.call('VM.get_allowed_VIF_devices', vm.$ref)
      await Promise.all(mapToArray(vifs, (vif, index) => {
        ++nVifs

        return this._createVif(
          vm,
          this.getObject(vif.network),
          {
            ipv4_allowed: vif.ipv4_allowed,
            ipv6_allowed: vif.ipv6_allowed,
            device: devices[index],
            locking_mode: isEmpty(vif.ipv4_allowed) && isEmpty(vif.ipv6_allowed) ? 'network_default' : 'locked',
            mac: vif.mac,
            mtu: vif.mtu
          }
        )
      }))
    }

    // TODO: Assign VGPUs.

    if (cloudConfig != null) {
      // Refresh the record.
      vm = await this._waitObjectState(vm.$id, vm => vm.VBDs.length === nVbds)

      // Find the SR of the first VDI.
      let srRef
      forEach(vm.$VBDs, vbd => {
        let vdi
        if (
          vbd.type === 'Disk' &&
          (vdi = vbd.$VDI)
        ) {
          srRef = vdi.SR
          return false
        }
      })

      const method = coreOs
        ? 'createCoreOsCloudInitConfigDrive'
        : 'createCloudInitConfigDrive'
      await this[method](vm.$id, srRef, cloudConfig)

      ++nVbds
    }

    // wait for the record with all the VBDs and VIFs
    return this._waitObjectState(vm.$id, vm =>
      vm.VBDs.length === nVbds &&
      vm.VIFs.length === nVifs
    )
  },

  // High level method to edit a VM.
  //
  // Params do not correspond directly to XAPI props.
  _editVm: makeEditObject({
    affinityHost: {
      get: 'affinity',
      set (value, vm) {
        return this._setObjectProperty(
          vm,
          'affinity',
          value ? this.getObject(value).$ref : NULL_REF
        )
      }
    },

    autoPoweron: {
      set (value, vm) {
        return Promise.all([
          this._updateObjectMapProperty(vm, 'other_config', {
            autoPoweron: value ? 'true' : null
          }),
          value && this.setPoolProperties({
            autoPoweron: true
          })
        ])
      }
    },

    CPUs: 'cpus',
    cpus: {
      addToLimits: true,

      // Current value may have constraints with other values.
      //
      // If the other value is not set and the constraint is not
      // respected, the other value is changed first.
      constraints: {
        cpusStaticMax: gte
      },

      get: vm => +vm.VCPUs_at_startup,
      set: [
        'VCPUs_at_startup',
        function (value, vm) {
          return isVmRunning(vm) && this._set('VCPUs_number_live', value)
        }
      ]
    },

    cpuCap: {
      get: vm => vm.VCPUs_params.cap && +vm.VCPUs_params.cap,
      set (cap, vm) {
        return this._updateObjectMapProperty(vm, 'VCPUs_params', { cap })
      }
    },

    cpusMax: 'cpusStaticMax',
    cpusStaticMax: {
      constraints: {
        cpus: lte
      },
      get: vm => +vm.VCPUs_max,
      set: 'VCPUs_max'
    },

    cpuWeight: {
      get: vm => vm.VCPUs_params.weight && +vm.VCPUs_params.weight,
      set (weight, vm) {
        return this._updateObjectMapProperty(vm, 'VCPUs_params', { weight })
      }
    },

    highAvailability: {
      set (ha, vm) {
        return this.call('VM.set_ha_restart_priority', vm.$ref, ha ? 'restart' : '')
      }
    },

    memoryMin: {
      constraints: {
        memoryMax: gte
      },
      get: vm => +vm.memory_dynamic_min,
      preprocess: parseSize,
      set: 'memory_dynamic_min'
    },

    memory: 'memoryMax',
    memoryMax: {
      addToLimits: true,
      limitName: 'memory',
      constraints: {
        memoryMin: lte,
        memoryStaticMax: gte
      },
      get: vm => +vm.memory_dynamic_max,
      preprocess: parseSize,
      set: 'memory_dynamic_max'
    },

    memoryStaticMax: {
      constraints: {
        memoryMax: lte
      },
      get: vm => +vm.memory_static_max,
      preprocess: parseSize,
      set: 'memory_static_max'
    },

    nameDescription: true,

    nameLabel: true,

    PV_args: true,

    tags: true
  }),

  async editVm (id, props, checkLimits) {
    return /* await */ this._editVm(this.getObject(id), props, checkLimits)
  },

  async revertVm (snapshotId, snapshotBefore = true) {
    const snapshot = this.getObject(snapshotId)
    if (snapshotBefore) {
      await this._snapshotVm(snapshot.$snapshot_of)
    }
    await this.call('VM.revert', snapshot.$ref)
    if (snapshot.snapshot_info['power-state-at-snapshot'] === 'Running') {
      const vm = snapshot.$snapshot_of
      if (vm.power_state === 'Halted') {
        this.startVm(vm.$id)::pCatch(noop)
      } else if (vm.power_state === 'Suspended') {
        this.resumeVm(vm.$id)::pCatch(noop)
      }
    }
  },

  async resumeVm (vmId) {
    // the force parameter is always true
    return this.call('VM.resume', this.getObject(vmId).$ref, false, true)
  }
}
<|endoftext|>