<commit_before>/**
 * Takes the recipe info of a recipe as a JS object and the div that the recipe
 * card will be added to. Creates only one recipe card. For the method to work,
 * recipeInfo is expected to have the following data saved as properties,
 * regardless of the explicit property name:
 * a recipe name, a recipe description, and a recipe
 */
createRecipeCard = (divID, recipeInfo) => {
  let docDiv = document.getElementById(divID);

  let recipeDiv = createElement("div", "", {"class": "recipe-card"});
  let imageDiv = createElement("div", "", {"class": "image-div"});
  let textDiv = createElement("div", "", {"class": "text-div"});

  const id = recipeInfo["id"];
  const name = recipeInfo["name"];
  const idToken = getIdToken();

  const addToPlannerButton = createElement("button", " Planner", {"class": "card-button action-button bottom more-left add-to-planner-btn fas"});
  addToPlannerButton.addEventListener('click', () => manageList("add", id, name, idToken, "planner"));
  setIcon(addToPlannerButton, id, idToken, "planner");

  const addToCookbookButton = createElement("button", " Cookbook", {"class": "card-button action-button bottom more-right add-to-cookbook-btn fas"});
  addToCookbookButton.addEventListener('click', () => manageList("add", id, name, idToken, "cookbook"));
  setIcon(addToCookbookButton, id, idToken, "cookbook");

  const removeFromPlannerButton = createElement("button", " Planner ", {"class": "card-button action-button bottom more-left fa fa-remove remove-from-planner-btn"});
  removeFromPlannerButton.addEventListener('click', () => manageList("remove", id, name, idToken, 'planner'));
  removeFromPlannerButton.style.display = "none";

  const removeFromToCookbookButton = createElement("button", " Cookbook ", {"class": "card-button action-button bottom more-right fa fa-remove remove-from-cookbook-btn"});
  removeFromToCookbookButton.addEventListener('click', () => manageList("remove", id, name, idToken, 'cookbook'));
  removeFromToCookbookButton.style.display = "none";

  let elementsToAddToImageDiv = [
    createImage(recipeInfo["name"], recipeInfo["imageBlobKey"]),
    addToPlannerButton, addToCookbookButton, removeFromPlannerButton, removeFromToCookbookButton,
  ];
  elementsToAddToImageDiv.forEach(elem => imageDiv.appendChild(elem));
  let elementsToAddToTextDiv = [
    createElement("button", recipeInfo["name"],
      {"class": "recipe-card-name",
      "onClick": "redirectRecipePage(" + recipeInfo["id"].toString() + ")"}),
    createElement("p", recipeInfo["id"], {"class": "hidden recipe-id"}),
    createElement("p", recipeInfo["description"], {"class": "recipe-card-description"}),
  ];
  elementsToAddToTextDiv.forEach(elem => textDiv.appendChild(elem));

  let allElementsToAdd = [imageDiv, textDiv];
  allElementsToAdd.forEach(elem => recipeDiv.appendChild(elem));
  docDiv.appendChild(recipeDiv);
}

/**
 * Takes the id for a recipe in Datastore, and redirects to the recipe page template
 * with the recipe ID as a search param.
 */
function redirectRecipePage(id) {
  window.location.assign("/pages/RecipePageTemplate.jsp?id=" + id.toString());
}

/**
 * Function for loading the recipe template page based on the id in the URL
 * search params.
 */
async function fillRecipeTemplate() {
  const recipeId = (new URL(document.location)).searchParams.get("id");

  // Gets the servlet data for the recipe
  // TODO: put this code in an if statement to handle recipe not found scenarios
  let response = await fetch("/search-id?recipeId=" + recipeId.toString());
  let responseText = await response.text();
  let recipeInfo = JSON.parse(responseText);

  let detailsSection = document.getElementById("details");
  let imageSection = document.getElementById("image-container");
  let descriptionSection = document.getElementById("description");
  let ingredientSection = document.getElementById("ingredients");
  let stepsSection = document.getElementById("steps");

  addRecipeInfo(recipeInfo, detailsSection);
  imageSection.appendChild(createImage(recipeInfo["name"], recipeInfo["imageBlobKey"]));
  descriptionSection.appendChild(createElement(
      "p", recipeInfo["description"], {"id": "description-text"}));
  addAsList(recipeInfo["ingredients"], ingredientSection, "Ingredients");
  addAsList(recipeInfo["steps"], stepsSection, "Steps");
}

/**
 * Creates the HTML for the information about the recipe and adds it to the div.
 * Current implementation adds the recipe name and the author.
 * Takes the recipe object and div (not div name) being used.
 */
addRecipeInfo = (recipeObj, infoDiv) => {
  let elementsForDiv = [
    createElement("h1", recipeObj["name"], {"id": "recipe-name"})/*,
    createElement("h3", recipeObj["author"], {"id": "recipe-author"})*/
    // Uncomment once the backend can handle sending author info too
  ];

  // Adds all the recipe info to the passed div
  elementsForDiv.forEach(elem => infoDiv.appendChild(elem));
}

/**
 * Creates the HTML for some part of the recipe which needs to shown as a list
 * (ingredients, steps, etc.) and adds it to the given div.
 * Takes a list and div (not div name) being used.
 */
addAsList = (listObj, listDiv, listName) => {
  // Constructs the base of how all item created in the function will be
  // named for CSS
  let cssRef = listName.toLowerCase() + "-list";

  // Creates the title for the list, and the list element
  let listForDiv = createElement("ol", "", {"id": cssRef})
  let listTitle = createElement("h2", listName, {"id": cssRef + "-title"});

  // Goes through all the objects in listObj and adds them as items to listForDiv
  listObj.forEach(item => listForDiv.appendChild(
    createElement("li", item, {"class": cssRef + "-item"})));

  listDiv.appendChild(listTitle);
  listDiv.appendChild(listForDiv);
}

/**
 * Takes an object which can be represented as a string or a string, an html
 * tag, and the html tag options stored as an object. Returns the reference to the
 * constructed html element.
 * Default values are set for object and tag options for instances where these aren't
 * needed, such as when making a new container div.
 */
createElement = (htmlTag, object = "", tagOptions = {}) => {
  // Create an HTML element with the given tag
  let htmlElement = document.createElement(htmlTag.toLowerCase());

  // If the object parameter is not null or an empty string, it
  // creates a textnode and adds it to the HTML element
  if (object !== null && object !== "") {
    let htmlText = document.createTextNode(object);
    htmlElement.appendChild(htmlText);
  }

  // If the tagOptions object is not null or empty, it goes through each
  // option and adds the attribute to the new HTML element.
  if (tagOptions !== null && Object.keys(tagOptions).length > 0) {
    for (opt in tagOptions) {
      htmlElement.setAttribute(opt, tagOptions[opt]);
    }
  }
  return htmlElement;
}

/**
 * Takes two objects which represent the name and the blobkey
 * Returns the reference to the constructed image element.
 */
createImage = (name, blobkey) => {
  let imageElement = document.createElement('img');
  imageElement.alt = name + " image";
  imageElement.className = "recipe-card-image";
  imageElement.src = "/serve?blobkey="+blobkey;
  return imageElement;
}

/**
 * Sets the icon of the button to a check if already added to list
 * and a plus otherwise
 */
function setIcon(button, id, idToken, type) {
  // checks if the current recipe is in the user's planner
  fetch("/manage-list?id=" + id + "&idToken=" + idToken + "&type=" + type)
      .then(response => response.text()).then((contains) => {
    if ((/true/i).test(contains)) {
      button.classList.add("fa-check");
    } else {
      button.classList.add("fa-plus");
    }
  });
}

// lets the user know if they have already added a recipe to a particular list
// otherwise adds the recipe
function manageList(action, id, name, idToken, type) {
  fetch("/manage-list?id=" + id + "&idToken=" + idToken + "&type=" + type)
      .then(response => response.text()).then((contains) => {
    if ((/true/i).test(contains)) {
      if (action == "add") {
        // if the user is trying to add a recipe and the list already contains it
        // alerts the user that the recipe is already in the list
        alert("You have already added the " + name + " recipe to your " + type);
      } else {
        // if the user is trying to remove a precipe and the list contains it
        // asks the user to confirm that they wish to remove the recipe
        const confirmedRemove = confirm("Are you sure you want to remove the " + name + " recipe from your " + type + "?");
        // tells the server to remove the recipe's key from the user's cookbook/planner
        if (confirmedRemove) {
          const params = new URLSearchParams();
          params.append("action", action);
          params.append("id", id);
          params.append("idToken", getIdToken());
          params.append("type", type);
          fetch("/manage-list", {method: "POST", body: params});
          if (document.URL.includes("UserPage")) {
            location.reload();
          }
        }
      }
    } else {
      if (action == "add") {
        // if the user is trying to add a recipe not already in the list
        const params = new URLSearchParams();
        params.append("action", action);
        params.append("id", id);
        params.append("idToken", getIdToken());
        params.append("type", type);
        fetch("/manage-list", {method: "POST", body: params});
        if (document.URL.includes("UserPage")) {
          location.reload();
        }
      }
    }
  });
}
<commit_msg>started<commit_after>/**
 * Takes the recipe info of a recipe as a JS object and the div that the recipe
 * card will be added to. Creates only one recipe card. For the method to work,
 * recipeInfo is expected to have the following data saved as properties,
 * regardless of the explicit property name:
 * a recipe name, a recipe description, and a recipe
 */
createRecipeCard = (divID, recipeInfo) => {
  let docDiv = document.getElementById(divID);

  let recipeDiv = createElement("div", "", {"class": "recipe-card"});
  let imageDiv = createElement("div", "", {"class": "image-div"});
  let textDiv = createElement("div", "", {"class": "text-div"});

  const id = recipeInfo["id"];
  const name = recipeInfo["name"];

  const addToPlannerButton = createElement("button", " Planner", {"class": "card-button action-button bottom more-left add-to-planner-btn fas"});
  addToPlannerButton.addEventListener('click', () => manageList("add", id, name, "planner"));
  setIcon(addToPlannerButton, id, idToken, "planner");

  const addToCookbookButton = createElement("button", " Cookbook", {"class": "card-button action-button bottom more-right add-to-cookbook-btn fas"});
  addToCookbookButton.addEventListener('click', () => manageList("add", id, name, "cookbook"));
  setIcon(addToCookbookButton, id, idToken, "cookbook");

  const removeFromPlannerButton = createElement("button", " Planner ", {"class": "card-button action-button bottom more-left fa fa-remove remove-from-planner-btn"});
  removeFromPlannerButton.addEventListener('click', () => manageList("remove", id, name, 'planner'));
  removeFromPlannerButton.style.display = "none";

  const removeFromToCookbookButton = createElement("button", " Cookbook ", {"class": "card-button action-button bottom more-right fa fa-remove remove-from-cookbook-btn"});
  removeFromToCookbookButton.addEventListener('click', () => manageList("remove", id, name, 'cookbook'));
  removeFromToCookbookButton.style.display = "none";

  let elementsToAddToImageDiv = [
    createImage(recipeInfo["name"], recipeInfo["imageBlobKey"]),
    addToPlannerButton, addToCookbookButton, removeFromPlannerButton, removeFromToCookbookButton,
  ];
  elementsToAddToImageDiv.forEach(elem => imageDiv.appendChild(elem));
  let elementsToAddToTextDiv = [
    createElement("button", recipeInfo["name"],
      {"class": "recipe-card-name",
      "onClick": "redirectRecipePage(" + recipeInfo["id"].toString() + ")"}),
    createElement("p", recipeInfo["id"], {"class": "hidden recipe-id"}),
    createElement("p", recipeInfo["description"], {"class": "recipe-card-description"}),
  ];
  elementsToAddToTextDiv.forEach(elem => textDiv.appendChild(elem));

  let allElementsToAdd = [imageDiv, textDiv];
  allElementsToAdd.forEach(elem => recipeDiv.appendChild(elem));
  docDiv.appendChild(recipeDiv);
}

/**
 * Takes the id for a recipe in Datastore, and redirects to the recipe page template
 * with the recipe ID as a search param.
 */
function redirectRecipePage(id) {
  window.location.assign("/pages/RecipePageTemplate.jsp?id=" + id.toString());
}

/**
 * Function for loading the recipe template page based on the id in the URL
 * search params.
 */
async function fillRecipeTemplate() {
  const recipeId = (new URL(document.location)).searchParams.get("id");

  // Gets the servlet data for the recipe
  // TODO: put this code in an if statement to handle recipe not found scenarios
  let response = await fetch("/search-id?recipeId=" + recipeId.toString());
  let responseText = await response.text();
  let recipeInfo = JSON.parse(responseText);

  let detailsSection = document.getElementById("details");
  let imageSection = document.getElementById("image-container");
  let descriptionSection = document.getElementById("description");
  let ingredientSection = document.getElementById("ingredients");
  let stepsSection = document.getElementById("steps");

  addRecipeInfo(recipeInfo, detailsSection);
  imageSection.appendChild(createImage(recipeInfo["name"], recipeInfo["imageBlobKey"]));
  descriptionSection.appendChild(createElement(
      "p", recipeInfo["description"], {"id": "description-text"}));
  addAsList(recipeInfo["ingredients"], ingredientSection, "Ingredients");
  addAsList(recipeInfo["steps"], stepsSection, "Steps");
}

/**
 * Creates the HTML for the information about the recipe and adds it to the div.
 * Current implementation adds the recipe name and the author.
 * Takes the recipe object and div (not div name) being used.
 */
addRecipeInfo = (recipeObj, infoDiv) => {
  let elementsForDiv = [
    createElement("h1", recipeObj["name"], {"id": "recipe-name"})/*,
    createElement("h3", recipeObj["author"], {"id": "recipe-author"})*/
    // Uncomment once the backend can handle sending author info too
  ];

  // Adds all the recipe info to the passed div
  elementsForDiv.forEach(elem => infoDiv.appendChild(elem));
}

/**
 * Creates the HTML for some part of the recipe which needs to shown as a list
 * (ingredients, steps, etc.) and adds it to the given div.
 * Takes a list and div (not div name) being used.
 */
addAsList = (listObj, listDiv, listName) => {
  // Constructs the base of how all item created in the function will be
  // named for CSS
  let cssRef = listName.toLowerCase() + "-list";

  // Creates the title for the list, and the list element
  let listForDiv = createElement("ol", "", {"id": cssRef})
  let listTitle = createElement("h2", listName, {"id": cssRef + "-title"});

  // Goes through all the objects in listObj and adds them as items to listForDiv
  listObj.forEach(item => listForDiv.appendChild(
    createElement("li", item, {"class": cssRef + "-item"})));

  listDiv.appendChild(listTitle);
  listDiv.appendChild(listForDiv);
}

/**
 * Takes an object which can be represented as a string or a string, an html
 * tag, and the html tag options stored as an object. Returns the reference to the
 * constructed html element.
 * Default values are set for object and tag options for instances where these aren't
 * needed, such as when making a new container div.
 */
createElement = (htmlTag, object = "", tagOptions = {}) => {
  // Create an HTML element with the given tag
  let htmlElement = document.createElement(htmlTag.toLowerCase());

  // If the object parameter is not null or an empty string, it
  // creates a textnode and adds it to the HTML element
  if (object !== null && object !== "") {
    let htmlText = document.createTextNode(object);
    htmlElement.appendChild(htmlText);
  }

  // If the tagOptions object is not null or empty, it goes through each
  // option and adds the attribute to the new HTML element.
  if (tagOptions !== null && Object.keys(tagOptions).length > 0) {
    for (opt in tagOptions) {
      htmlElement.setAttribute(opt, tagOptions[opt]);
    }
  }
  return htmlElement;
}

/**
 * Takes two objects which represent the name and the blobkey
 * Returns the reference to the constructed image element.
 */
createImage = (name, blobkey) => {
  let imageElement = document.createElement('img');
  imageElement.alt = name + " image";
  imageElement.className = "recipe-card-image";
  imageElement.src = "/serve?blobkey="+blobkey;
  return imageElement;
}

/**
 * Sets the icon of the button to a check if already added to list
 * and a plus otherwise
 */
function setIcon(button, id, idToken, type) {
  // checks if the current recipe is in the user's planner
  fetch("/manage-list?id=" + id + "&idToken=" + idToken + "&type=" + type)
      .then(response => response.text()).then((contains) => {
    if ((/true/i).test(contains)) {
      button.classList.add("fa-check");
    } else {
      button.classList.add("fa-plus");
    }
  });
}

// lets the user know if they have already added a recipe to a particular list
// otherwise adds the recipe
function manageList(action, id, name, type) {
  let idToken;
  confirmUser().then((googleUser) => {
    idToken = getIdToken();
    fetch("/manage-list?id=" + id + "&idToken=" + idToken + "&type=" + type)
        .then(response => response.text()).then((contains) => {
      if ((/true/i).test(contains)) {
        if (action == "add") {
          // if the user is trying to add a recipe and the list already contains it
          // alerts the user that the recipe is already in the list
          alert("You have already added the " + name + " recipe to your " + type);
        } else {
          // if the user is trying to remove a precipe and the list contains it
          // asks the user to confirm that they wish to remove the recipe
          const confirmedRemove = confirm("Are you sure you want to remove the " + name + " recipe from your " + type + "?");
          // tells the server to remove the recipe's key from the user's cookbook/planner
          if (confirmedRemove) {
            const params = new URLSearchParams();
            params.append("action", action);
            params.append("id", id);
            params.append("idToken", getIdToken());
            params.append("type", type);
            fetch("/manage-list", {method: "POST", body: params});
            if (document.URL.includes("UserPage")) {
              location.reload();
            }
          }
        }
      } else {
        if (action == "add") {
          // if the user is trying to add a recipe not already in the list
          const params = new URLSearchParams();
          params.append("action", action);
          params.append("id", id);
          params.append("idToken", getIdToken());
          params.append("type", type);
          fetch("/manage-list", {method: "POST", body: params});
          if (document.URL.includes("UserPage")) {
            location.reload();
          }
        }
      }
    });
  });
}
<|endoftext|>


<commit_before>const {join} = require('path');

const checkCacheExpiry = require('../utils/check-cache-expiry');

test(
    'Check cache for missing file',
    () => {

        expect(checkCacheExpiry(join(
            __dirname,
            '/mocks/missing-file.txt'
        ))).rejects.toThrowError(/ENOENT/u);

    }
);

test(
    'Check cache for existing file',
    () => {

        expect(checkCacheExpiry(join(
            __dirname,
            '/mocks/cache.txt'
        ))).resolves.toBeTruthy();

    }
);

test(
    'Check cache for expired file',
    () => {

        expect(checkCacheExpiry(
            join(
                __dirname,
                '/mocks/cache.txt'
            ),
            -1
        )).rejects.toThrowError(/expired/u);

    }
);
<commit_msg>Touch cache file before running each test.<commit_after>const {utimesSync} = require('fs');
const {join} = require('path');

const checkCacheExpiry = require('../utils/check-cache-expiry');

beforeEach(() => {

    const time = new Date();

    utimesSync(
        join(
            __dirname,
            '/mocks/cache.txt'
        ),
        time,
        time
    );

});

test(
    'Check cache for missing file',
    () => {

        expect(checkCacheExpiry(join(
            __dirname,
            '/mocks/missing-file.txt'
        ))).rejects.toThrowError(/ENOENT/u);

    }
);

test(
    'Check cache for existing file',
    () => {

        expect(checkCacheExpiry(join(
            __dirname,
            '/mocks/cache.txt'
        ))).resolves.toBeTruthy();

    }
);

test(
    'Check cache for expired file',
    () => {

        expect(checkCacheExpiry(
            join(
                __dirname,
                '/mocks/cache.txt'
            ),
            -1
        )).rejects.toThrowError(/expired/u);

    }
);
<|endoftext|>


<commit_before>function setupFragmentEditor() {
	var $window = $(window);
	var $editorFrame = $("#editor-frame");

	$("#new-fragment-editor-trigger, .new-fragment-editor-trigger-on-toolbar").click(function (e) {
		onClickFragmentEditorTrigger($editorFrame, $window, e, MSG.label_new_fragment, true);
    });

    $editorFrame.resizable({
        alsoResize: "#markItUpFragment-content-editor, #fragment-content-editor",
        minWidth: 581,
        minHeight: 234,
    });

	$editorFrame.draggable({
    	handle:"#editor-title-bar"
    	, cursor:"move"
    });

    $("#editor-title-bar").dblclick(function() {
    	toggleFragmentEditor($editorFrame, $(this));
    });

    $("#fragment-content-editor").markItUp(markItUpSettings, {});
}

function toggleFragmentEditor(frame, bar) {
	var target = bar.next();
	var ph = frame.data("prev-h");
	frame.data("prev-h", frame.height());
	if (target.is(":visible")) {
		var w = frame.width();
		if (frame.hasClass("ui-resizable"))
			frame.resizable("disable");
		target.hide();
		frame.width(w);
		frame.data("min-h", frame.css("min-height"));
		frame.css("min-height", bar.height());
		frame.height(bar.height());
	}
	else {
		if (frame.hasClass("ui-resizable"))
			frame.resizable("enable");
		target.show();
		frame.height(ph);
		frame.css("min-height", frame.data("min-h"));
	}
}

function onClickFragmentEditorTrigger($editorFrame, $window, e, title, forNewFragment) {
	var editorHeight = Math.max($editorFrame.height(), 696);
    var top = ($window.height() - editorHeight) / 2 + $window.scrollTop() + "px";

    if (forNewFragment) {
    	var el = $("#fragment-editor-form\\:title-input");
        el.val(null);
        el.watermark(MSG.label_title);

        el = $("#fragment-editor-form\\:tags-input");
        el.val(null);
        el.watermark(MSG.how_to_input_tags);

        $("#fragment-content-editor").val(null);
    }

    $editorFrame.lightbox_me({
        centered:false
        , showOverlay:false
        , lightboxSpeed:"slow"
        , closeSelector:"#editor-frame-close-button"
        , closeEsc:false
        , modalCSS:{ position:"absolute", top:top, left:"30%" }
        , onClose:clearFragmentEditor
    });

    var titleBar = $("#editor-title-bar");
    if (titleBar.next().is(":visible") == false) {
    	toggleFragmentEditor($editorFrame, titleBar);
    }

    $("#editor-title").text(title);

    e.preventDefault();
}

function clearFragmentEditor() {
    $("#fragment-content-editor").val(null);
    $("#fragment-editor-form\\:id-placeholder-for-fragment").val(null);

    // If the preview pop-up is open, close it
    var frgOverlay = $("#fragment-overlay");
    if (frgOverlay.find(".fragment-header").length == 0) {
        frgOverlay.trigger("close");
    }

    // [NOTE] without the following code,
    // jQuery UI Autocomplete object won't disapper even after its parent is closed
    $("#fragment-editor-form\\:tags-input").autocomplete("widget").hide();
}

function parseMarkdown(inputText) {
    return marked(inputText);
}

function openFragmentEditorForEdit(event) {
	var menu = $("#frg-context-menu");
	var target = menu.data("target-frg");
	var fragmentId = target.attr("_fid");
	var panelId = findPanel(target);

	$("#fragment-editor-form\\:id-placeholder-for-fragment").val(fragmentId);

    var el = target.find(".-cvz-data-title");
    $("#fragment-editor-form\\:title-input").val(el.text());

    el = target.find(".-cvz-data-tags");
    $("#fragment-editor-form\\:tags-input").val(el.text());

    el = target.find(".-cvz-data-content");
    $("#fragment-content-editor").val(el.text());

    var $window = $(window);
	var $editorFrame = $("#editor-frame");
    onClickFragmentEditorTrigger($editorFrame, $window, event, MSG.label_edit, false);
}

function prepareFragmentContent() {
    var $editor = $("#fragment-content-editor");
    var srcContent = $editor.val();
    $("#fragment-editor-form\\:content-placeholder").val(srcContent);
    return srcContent;
}

function previewFragment() {
	$("#fragment-overlay").lightbox_me({
        centered:false
        , showOverlay:false
        , lightboxSpeed:"fast"
        , closeSelector:"#fragment-overlay-close-button"
        , closeEsc:true
        , modalCSS:{ position:"fixed", bottom: '2%', right: '2%' }
    });

	$("#fragment-overlay-title").text(MSG.label_preview);

	var srcContent = prepareFragmentContent();
    var outputHtml = translateFragmentContent(srcContent);
    postprocessFragmentContent($("#fragment-overlay-content").html(outputHtml));
}

function autocompleteForTypingTags() {
	var tagSuggestions = [];
    $("#tag-palette-flat .each-tag").each(function (index, value) {
        tagSuggestions.push($(value).find(".each-tag-name").text());
    });

    var split = function (input) {
        return input.trim().split(/\,\s*/);
    }

    function alreadyTyped(typedTags, suggestion) {
    	for (var i=0; i<typedTags.length; ++i) {
    		if (typedTags[i] == suggestion) return true;
    	}
    	return false;
    }

    $("#fragment-editor-form\\:tags-input, #fragment-group-form\\:search-panel\\:tag-keywords").autocomplete({
        source: function(req, res) {
            var tags = split(req.term);
            if (tags.length && tags[tags.length-1] == "") {
                tags.pop();
            }
            var typed = tags[tags.length-1];
            tags.pop();
            var prefix = "";
            for (var i=0; i<tags.length; ++i) {
                prefix += tags[i] + ",";
            }
            var output = [];
            for (var i=0; i<tagSuggestions.length; ++i) {
                var suggestion = tagSuggestions[i];
                if (suggestion.indexOf(typed) == 0 && ! alreadyTyped(tags, suggestion)) {
                    output.push(suggestion);
                }
            }
        	console.log(output);
            res(output);
        },
        focus: function () {
            // prevent value inserted on focus
            return false;
        },
        select: function (event, ui) {
            var terms = split(this.value);
            // remove the current input
            terms.pop();
            // add the selected item
            terms.push(ui.item.value);
            // add placeholder to get the comma-and-space at the end
            this.value = terms.join(", ") + ", ";
            return false;
        },
        autoFocus: true,
    });
}
<commit_msg>[delete] debug code<commit_after>function setupFragmentEditor() {
	var $window = $(window);
	var $editorFrame = $("#editor-frame");

	$("#new-fragment-editor-trigger, .new-fragment-editor-trigger-on-toolbar").click(function (e) {
		onClickFragmentEditorTrigger($editorFrame, $window, e, MSG.label_new_fragment, true);
    });

    $editorFrame.resizable({
        alsoResize: "#markItUpFragment-content-editor, #fragment-content-editor",
        minWidth: 581,
        minHeight: 234,
    });

	$editorFrame.draggable({
    	handle:"#editor-title-bar"
    	, cursor:"move"
    });

    $("#editor-title-bar").dblclick(function() {
    	toggleFragmentEditor($editorFrame, $(this));
    });

    $("#fragment-content-editor").markItUp(markItUpSettings, {});
}

function toggleFragmentEditor(frame, bar) {
	var target = bar.next();
	var ph = frame.data("prev-h");
	frame.data("prev-h", frame.height());
	if (target.is(":visible")) {
		var w = frame.width();
		if (frame.hasClass("ui-resizable"))
			frame.resizable("disable");
		target.hide();
		frame.width(w);
		frame.data("min-h", frame.css("min-height"));
		frame.css("min-height", bar.height());
		frame.height(bar.height());
	}
	else {
		if (frame.hasClass("ui-resizable"))
			frame.resizable("enable");
		target.show();
		frame.height(ph);
		frame.css("min-height", frame.data("min-h"));
	}
}

function onClickFragmentEditorTrigger($editorFrame, $window, e, title, forNewFragment) {
	var editorHeight = Math.max($editorFrame.height(), 696);
    var top = ($window.height() - editorHeight) / 2 + $window.scrollTop() + "px";

    if (forNewFragment) {
    	var el = $("#fragment-editor-form\\:title-input");
        el.val(null);
        el.watermark(MSG.label_title);

        el = $("#fragment-editor-form\\:tags-input");
        el.val(null);
        el.watermark(MSG.how_to_input_tags);

        $("#fragment-content-editor").val(null);
    }

    $editorFrame.lightbox_me({
        centered:false
        , showOverlay:false
        , lightboxSpeed:"slow"
        , closeSelector:"#editor-frame-close-button"
        , closeEsc:false
        , modalCSS:{ position:"absolute", top:top, left:"30%" }
        , onClose:clearFragmentEditor
    });

    var titleBar = $("#editor-title-bar");
    if (titleBar.next().is(":visible") == false) {
    	toggleFragmentEditor($editorFrame, titleBar);
    }

    $("#editor-title").text(title);

    e.preventDefault();
}

function clearFragmentEditor() {
    $("#fragment-content-editor").val(null);
    $("#fragment-editor-form\\:id-placeholder-for-fragment").val(null);

    // If the preview pop-up is open, close it
    var frgOverlay = $("#fragment-overlay");
    if (frgOverlay.find(".fragment-header").length == 0) {
        frgOverlay.trigger("close");
    }

    // [NOTE] without the following code,
    // jQuery UI Autocomplete object won't disapper even after its parent is closed
    $("#fragment-editor-form\\:tags-input").autocomplete("widget").hide();
}

function parseMarkdown(inputText) {
    return marked(inputText);
}

function openFragmentEditorForEdit(event) {
	var menu = $("#frg-context-menu");
	var target = menu.data("target-frg");
	var fragmentId = target.attr("_fid");
	var panelId = findPanel(target);

	$("#fragment-editor-form\\:id-placeholder-for-fragment").val(fragmentId);

    var el = target.find(".-cvz-data-title");
    $("#fragment-editor-form\\:title-input").val(el.text());

    el = target.find(".-cvz-data-tags");
    $("#fragment-editor-form\\:tags-input").val(el.text());

    el = target.find(".-cvz-data-content");
    $("#fragment-content-editor").val(el.text());

    var $window = $(window);
	var $editorFrame = $("#editor-frame");
    onClickFragmentEditorTrigger($editorFrame, $window, event, MSG.label_edit, false);
}

function prepareFragmentContent() {
    var $editor = $("#fragment-content-editor");
    var srcContent = $editor.val();
    $("#fragment-editor-form\\:content-placeholder").val(srcContent);
    return srcContent;
}

function previewFragment() {
	$("#fragment-overlay").lightbox_me({
        centered:false
        , showOverlay:false
        , lightboxSpeed:"fast"
        , closeSelector:"#fragment-overlay-close-button"
        , closeEsc:true
        , modalCSS:{ position:"fixed", bottom: '2%', right: '2%' }
    });

	$("#fragment-overlay-title").text(MSG.label_preview);

	var srcContent = prepareFragmentContent();
    var outputHtml = translateFragmentContent(srcContent);
    postprocessFragmentContent($("#fragment-overlay-content").html(outputHtml));
}

function autocompleteForTypingTags() {
	var tagSuggestions = [];
    $("#tag-palette-flat .each-tag").each(function (index, value) {
        tagSuggestions.push($(value).find(".each-tag-name").text());
    });

    var split = function (input) {
        return input.trim().split(/\,\s*/);
    }

    function alreadyTyped(typedTags, suggestion) {
    	for (var i=0; i<typedTags.length; ++i) {
    		if (typedTags[i] == suggestion) return true;
    	}
    	return false;
    }

    $("#fragment-editor-form\\:tags-input, #fragment-group-form\\:search-panel\\:tag-keywords").autocomplete({
        source: function(req, res) {
            var tags = split(req.term);
            if (tags.length && tags[tags.length-1] == "") {
                tags.pop();
            }
            var typed = tags[tags.length-1];
            tags.pop();
            var prefix = "";
            for (var i=0; i<tags.length; ++i) {
                prefix += tags[i] + ",";
            }
            var output = [];
            for (var i=0; i<tagSuggestions.length; ++i) {
                var suggestion = tagSuggestions[i];
                if (suggestion.indexOf(typed) == 0 && ! alreadyTyped(tags, suggestion)) {
                    output.push(suggestion);
                }
            }
            res(output);
        },
        focus: function () {
            // prevent value inserted on focus
            return false;
        },
        select: function (event, ui) {
            var terms = split(this.value);
            // remove the current input
            terms.pop();
            // add the selected item
            terms.push(ui.item.value);
            // add placeholder to get the comma-and-space at the end
            this.value = terms.join(", ") + ", ";
            return false;
        },
        autoFocus: true,
    });
}
<|endoftext|>


<commit_before>import { toQueryString, getUrlVars, sleep } from './utils.js';

window.visualize = visualize; // Add function to global scope.

/**
 * Get the simulation metadata encoded as a query string, iterate over all rounds and visualize
 * the real and estimated board state at each round (with a 1000 ms delay between two consecutive rounds).
 */
async function visualize() {
    const realBoardElementId = 'real-board-table';
    const estimatedBoardElementId = 'estimated-board-table';
    var simulation = getUrlVars();
    var mainHeader = document.getElementById('simulation-visualization-header');
    var roundHeader = document.getElementById('current-round-header');
    var delay;
    mainHeader.innerText = 'Visualizing Simulation\n' + simulation.id;
    var params = {};
    params['simulationId'] = simulation.id;
    for (var round = 0; round < simulation.roundsNum; round++) {
        roundHeader.innerText = 'Current Round: ' + round;
        params['round'] = round;

        // Request real board state.
        params['isReal'] = true;
        await fetchReadBoardState(params, realBoardElementId);

        // Request estimated board state.
        params['isReal'] = false;
        await fetchReadBoardState(params, estimatedBoardElementId);

        // Delay.
        delay = 1/document.getElementById('speed-controller').value;
        await sleep(delay);
    }
    displayStats(params);
}

/**
 * Fetch board state from servlet by given parameters, and update an HTML table corresponding to given
 * board element id to visualize the board's state.
 * @param {Object} params is an object storing parameters for http request.
 * @param {String} boardElementId is the id of the html table element to be updated.
 */
async function fetchReadBoardState(params, boardElementId) {
    const queryString = toQueryString(params);
    fetch(`/read-board-state?${queryString}`)
    .then(response => response.json())
    .then(boardState => visualizeBoardState(boardState.array, boardElementId));
}

/**
 * Given a board state and an id of an html table element, update the table to display the board state.
 * @param {String[][]} board is the board state to visualize.
 * @param {String} tableId is the id of the html table element to be updated.
 */
function visualizeBoardState(board, tableId) {

    // Set table and table body
    var boardTable = document.getElementById(tableId);
    var boardTableBody = document.createElement('tbody');

    // Change cells' classes according to board.
    board.forEach(function(rowData) {

        var row = document.createElement('tr');
        row.classList.add('board-tr')

        rowData.forEach(function(cellData) {
            var cell = document.createElement('td');
            cell.classList.add('board-td');
            updateCell(cell, cellData);
            row.appendChild(cell);
        });

        boardTableBody.appendChild(row);
    });

    // Clear last state of table (if table body exists).
    var oldTableBodies = boardTable.tBodies;
    if (oldTableBodies.length > 0) {
        boardTable.removeChild(oldTableBodies[0]);
    }

    // Append updated table body.
    boardTable.appendChild(boardTableBody);
}

/**
 * Update the cell according to the agents inside it.
 * @param {HTMLTableDataCellElement} cell is an HTML element from type <td>, representing a cell on the board.
 * @param {String[]} agents is a list of agents ids located inside the cell.
 */
function updateCell(cell, agents) {
    var beaconsIdsInsideCell = extractBeaconsIds(agents);
    cell.setAttribute('title', beaconsIdsInsideCell.join(","));

    var beaconToFocusOn = document.getElementById("beacon-id").value;
    var isBeaconToFocusOnInCell = beaconToFocusOn != "" && beaconsIdsInsideCell.includes(beaconToFocusOn);
    cell.classList.add(determineCellClass(agents, beaconsIdsInsideCell.length, isBeaconToFocusOnInCell));
}

/**
 * return the css class name representing the correct state of the cell (empty/contains beacons/observers only).
 * @param {String[]} agents a list of agents ids located in the same cell of the board.
 * @returns {String} the correct css class name.
 */
function determineCellClass(agents, numberOfBeacons, isBeaconToFocusOnInCell) {
    if (agents.length === 0) {
        return 'board-td-empty';
    }
    if (isBeaconToFocusOnInCell) {
        return 'board-td-contains-wanted-beacon';
    }
    if (numberOfBeacons > 0) {
       return 'board-td-contains-beacon';
    }
    return 'board-td-observers-only';
}

/**
 * Returns a sorted list of all the beacons' ids in the given list.
 * @param {String[]} agents is a list of agents ids located in the same cell of the board.
 */
function extractBeaconsIds(agents) {
    var beaconsIds = agents.filter(agent => agent.charAt(0) === 'B')
                    .map(agent => agent.slice(6));
    return beaconsIds.sort((a, b) => parseInt(a) - parseInt(b));
}

/**
 * Display the statistical data of a simulation.
 * @param {Object*} params is an object storing parameters for http request.
 */
function displayStats(params) {
    const queryString = toQueryString(params);
    fetch(`/read-stats?${queryString}`)
    .then(response => response.json())
    .then(stats => console.log(stats));
}<commit_msg>implement function for displaying stats.<commit_after>import { toQueryString, getUrlVars, sleep } from './utils.js';

window.visualize = visualize; // Add function to global scope.

/**
 * Get the simulation metadata encoded as a query string, iterate over all rounds and visualize
 * the real and estimated board state at each round (with a 1000 ms delay between two consecutive rounds).
 */
async function visualize() {
    const realBoardElementId = 'real-board-table';
    const estimatedBoardElementId = 'estimated-board-table';
    var simulation = getUrlVars();
    var mainHeader = document.getElementById('simulation-visualization-header');
    var roundHeader = document.getElementById('current-round-header');
    var delay;
    mainHeader.innerText = 'Visualizing Simulation\n' + simulation.id;
    var params = {};
    params['simulationId'] = simulation.id;
    for (var round = 0; round < simulation.roundsNum; round++) {
        roundHeader.innerText = 'Current Round: ' + round;
        params['round'] = round;

        // Request real board state.
        params['isReal'] = true;
        await fetchReadBoardState(params, realBoardElementId);

        // Request estimated board state.
        params['isReal'] = false;
        await fetchReadBoardState(params, estimatedBoardElementId);

        // Delay.
        delay = 1/document.getElementById('speed-controller').value;
        await sleep(delay);
    }
    displayStats(params);
}

/**
 * Fetch board state from servlet by given parameters, and update an HTML table corresponding to given
 * board element id to visualize the board's state.
 * @param {Object} params is an object storing parameters for http request.
 * @param {String} boardElementId is the id of the html table element to be updated.
 */
async function fetchReadBoardState(params, boardElementId) {
    const queryString = toQueryString(params);
    fetch(`/read-board-state?${queryString}`)
    .then(response => response.json())
    .then(boardState => visualizeBoardState(boardState.array, boardElementId));
}

/**
 * Given a board state and an id of an html table element, update the table to display the board state.
 * @param {String[][]} board is the board state to visualize.
 * @param {String} tableId is the id of the html table element to be updated.
 */
function visualizeBoardState(board, tableId) {

    // Set table and table body
    var boardTable = document.getElementById(tableId);
    var boardTableBody = document.createElement('tbody');

    // Change cells' classes according to board.
    board.forEach(function(rowData) {

        var row = document.createElement('tr');
        row.classList.add('board-tr')

        rowData.forEach(function(cellData) {
            var cell = document.createElement('td');
            cell.classList.add('board-td');
            updateCell(cell, cellData);
            row.appendChild(cell);
        });

        boardTableBody.appendChild(row);
    });

    // Clear last state of table (if table body exists).
    var oldTableBodies = boardTable.tBodies;
    if (oldTableBodies.length > 0) {
        boardTable.removeChild(oldTableBodies[0]);
    }

    // Append updated table body.
    boardTable.appendChild(boardTableBody);
}

/**
 * Update the cell according to the agents inside it.
 * @param {HTMLTableDataCellElement} cell is an HTML element from type <td>, representing a cell on the board.
 * @param {String[]} agents is a list of agents ids located inside the cell.
 */
function updateCell(cell, agents) {
    var beaconsIdsInsideCell = extractBeaconsIds(agents);
    cell.setAttribute('title', beaconsIdsInsideCell.join(","));

    var beaconToFocusOn = document.getElementById("beacon-id").value;
    var isBeaconToFocusOnInCell = beaconToFocusOn != "" && beaconsIdsInsideCell.includes(beaconToFocusOn);
    cell.classList.add(determineCellClass(agents, beaconsIdsInsideCell.length, isBeaconToFocusOnInCell));
}

/**
 * return the css class name representing the correct state of the cell (empty/contains beacons/observers only).
 * @param {String[]} agents a list of agents ids located in the same cell of the board.
 * @returns {String} the correct css class name.
 */
function determineCellClass(agents, numberOfBeacons, isBeaconToFocusOnInCell) {
    if (agents.length === 0) {
        return 'board-td-empty';
    }
    if (isBeaconToFocusOnInCell) {
        return 'board-td-contains-wanted-beacon';
    }
    if (numberOfBeacons > 0) {
       return 'board-td-contains-beacon';
    }
    return 'board-td-observers-only';
}

/**
 * Returns a sorted list of all the beacons' ids in the given list.
 * @param {String[]} agents is a list of agents ids located in the same cell of the board.
 */
function extractBeaconsIds(agents) {
    var beaconsIds = agents.filter(agent => agent.charAt(0) === 'B')
                    .map(agent => agent.slice(6));
    return beaconsIds.sort((a, b) => parseInt(a) - parseInt(b));
}

/**
 * Display the statistical data of a simulation.
 * @param {Object*} params is an object storing parameters for http request.
 */
function displayStats(params) {
    const queryString = toQueryString(params);
    fetch(`/read-stats?${queryString}`)
    .then(response => response.json())
    .then(stats => Object.keys(stats).forEach(kind => createStatsTable(stats[kind])))
    .then(() => document.getElementsByClassName('boards')[0].innerHTML = ''); // Remove boards.
}

/**
 * Create an HTML table element add fill it with statistical data.
 * @param {Object} stats is an object storing the simulations' stats of a specific kind.
 */
function createStatsTable(stats) {
    var table = document.createElement('table');
    var header = table.createTHead();
    var headerRow = header.insertRow(0);
    var dataRow = table.insertRow(1);
    var i = 0;
    Object.keys(stats).forEach(measure => {
        headerRow.insertCell(i).innerText = measure;
        dataRow.insertCell(i).innerText = stats[measure];
        i++;
    });
    document.body.appendChild(table);
}<|endoftext|>


<commit_before>'use strict'

const glob = require('glob');
const webpack = require('webpack')
const Uglify = require('uglifyjs-webpack-plugin')
const path = require('path')

const uglifyOptions = {
  uglifyOptions: {
    mangle: false,
    compress: false,
  },
}

module.exports = {
  // TODO: put all tests in a .js file that webpack can use as entry point
  entry: glob.sync('./test/*.spec.js'),
  output: {
    filename: './test/browser/bundle.js'
  },
  target: 'web',
  devtool: 'none',
  node: {
    console: false,
    process: 'mock',
    Buffer: true
  },
  plugins: [
    new webpack.DefinePlugin({
      'process.env': {
        'NODE_ENV': JSON.stringify(process.env.NODE_ENV)
      }
    }),
    // new Uglify(uglifyOptions),
  ],
  externals: {
    fs: '{}',
    rimraf: '{ sync: () => {} }',
  },
  resolve: {
    modules: [
      'node_modules',
      path.resolve(__dirname, '../node_modules')
    ]
  },
  resolveLoader: {
    modules: [
      'node_modules',
      path.resolve(__dirname, '../node_modules')
    ],
    moduleExtensions: ['-loader']
  },
  module: {
    rules: [
      {
        test: /\.json$/,
        loader: 'json-loader'
      },
      // For inlining the fixture keys in browsers tests
      {
        test: /userA|userB|userC|userD$/,
        loader: 'json-loader'
      }
    ]
  }
}
<commit_msg>fix(tests): adding source map to webpack tests<commit_after>'use strict'

const glob = require('glob');
const webpack = require('webpack')
const Uglify = require('uglifyjs-webpack-plugin')
const path = require('path')

const uglifyOptions = {
  uglifyOptions: {
    mangle: false,
    compress: false,
  },
}

module.exports = {
  // TODO: put all tests in a .js file that webpack can use as entry point
  entry: glob.sync('./test/*.spec.js'),
  output: {
    filename: './test/browser/bundle.js'
  },
  target: 'web',
  devtool: 'source-map',
  node: {
    console: false,
    process: 'mock',
    Buffer: true
  },
  plugins: [
    new webpack.DefinePlugin({
      'process.env': {
        'NODE_ENV': JSON.stringify(process.env.NODE_ENV)
      }
    }),
    // new Uglify(uglifyOptions),
  ],
  externals: {
    fs: '{}',
    rimraf: '{ sync: () => {} }',
  },
  resolve: {
    modules: [
      'node_modules',
      path.resolve(__dirname, '../node_modules')
    ]
  },
  resolveLoader: {
    modules: [
      'node_modules',
      path.resolve(__dirname, '../node_modules')
    ],
    moduleExtensions: ['-loader']
  },
  module: {
    rules: [
      {
        test: /\.json$/,
        loader: 'json-loader'
      },
      // For inlining the fixture keys in browsers tests
      {
        test: /userA|userB|userC|userD$/,
        loader: 'json-loader'
      }
    ]
  }
}
<|endoftext|>


<commit_before>const BaseError = require('./BaseError');

const SpurErrors = {
  BaseError,

  ValidationError: BaseError.extend(400, 'Validation Error', 'validation_error'),
  UnauthorizedError: BaseError.extend(401, 'Unauthorized Error', 'unauthorized_error'),
  ForbiddenError: BaseError.extend(403, 'Forbidden Error', 'forbidden_error'),
  NotFoundError: BaseError.extend(404, 'Not Found Error', 'not_found_error'),
  MethodNotAllowedError: BaseError.extend(405, 'Method Not Allowed', 'method_not_allowed_error'),
  RequestTimeoutError: BaseError.extend(408, 'Request Timeout Error', 'request_timeout_error'),
  AlreadyExistsError: BaseError.extend(409, 'Already Exists Error', 'already_exists_error'),
  InternalServerError: BaseError.extend(500, 'Internal Server Error', 'internal_server_error'),
  BadGatewayError: BaseError.extend(502, 'Bad Gateway Error', 'bad_gateway_error'),
  ServiceUnavailableError: BaseError.extend(503, 'Service Unavailable Error', 'service_unavailable_error'),

  errorByStatusCode(statusCode) {
    const errorNames = Object.keys(SpurErrors);
    let spurError;
    errorNames.forEach((name) => {
      const error = SpurErrors[name];
      if (error.statusCode === statusCode) {
        spurError = error;
      }
    });
    return spurError;
  }
};

module.exports = SpurErrors;
<commit_msg>feat(gateway timeout error): GO-366 Add gateway timeout error constructor<commit_after>const BaseError = require('./BaseError');

const SpurErrors = {
  BaseError,

  ValidationError: BaseError.extend(400, 'Validation Error', 'validation_error'),
  UnauthorizedError: BaseError.extend(401, 'Unauthorized Error', 'unauthorized_error'),
  ForbiddenError: BaseError.extend(403, 'Forbidden Error', 'forbidden_error'),
  NotFoundError: BaseError.extend(404, 'Not Found Error', 'not_found_error'),
  MethodNotAllowedError: BaseError.extend(405, 'Method Not Allowed', 'method_not_allowed_error'),
  RequestTimeoutError: BaseError.extend(408, 'Request Timeout Error', 'request_timeout_error'),
  AlreadyExistsError: BaseError.extend(409, 'Already Exists Error', 'already_exists_error'),
  InternalServerError: BaseError.extend(500, 'Internal Server Error', 'internal_server_error'),
  BadGatewayError: BaseError.extend(502, 'Bad Gateway Error', 'bad_gateway_error'),
  ServiceUnavailableError: BaseError.extend(503, 'Service Unavailable Error', 'service_unavailable_error'),
  GatewayTimeoutError: BaseError.extend(504, 'Gateway Timeout Error', 'gateway_timeout_error'),

  errorByStatusCode(statusCode) {
    const errorNames = Object.keys(SpurErrors);
    let spurError;
    errorNames.forEach((name) => {
      const error = SpurErrors[name];
      if (error.statusCode === statusCode) {
        spurError = error;
      }
    });
    return spurError;
  }
};

module.exports = SpurErrors;
<|endoftext|>


<commit_before>export const VERSION = '1.9.3'
export const CHANGELOG_URL =
  'https://github.com/hashicorp/vault/blob/main/CHANGELOG.md#193'

// HashiCorp officially supported package managers
export const packageManagers = [
  {
    label: 'Homebrew',
    commands: ['brew tap hashicorp/tap', 'brew install hashicorp/tap/vault'],
    os: 'darwin',
  },
  {
    label: 'Ubuntu/Debian',
    commands: [
      'curl -fsSL https://apt.releases.hashicorp.com/gpg | sudo apt-key add -',
      'sudo apt-add-repository "deb [arch=amd64] https://apt.releases.hashicorp.com $(lsb_release -cs) main"',
      'sudo apt-get update && sudo apt-get install vault',
    ],
    os: 'linux',
  },
  {
    label: 'CentOS/RHEL',
    commands: [
      'sudo yum install -y yum-utils',
      'sudo yum-config-manager --add-repo https://rpm.releases.hashicorp.com/RHEL/hashicorp.repo',
      'sudo yum -y install vault',
    ],
    os: 'linux',
  },
  {
    label: 'Fedora',
    commands: [
      'sudo dnf install -y dnf-plugins-core',
      'sudo dnf config-manager --add-repo https://rpm.releases.hashicorp.com/fedora/hashicorp.repo',
      'sudo dnf -y install vault',
    ],
    os: 'linux',
  },
  {
    label: 'Amazon Linux',
    commands: [
      'sudo yum install -y yum-utils',
      'sudo yum-config-manager --add-repo https://rpm.releases.hashicorp.com/AmazonLinux/hashicorp.repo',
      'sudo yum -y install vault',
    ],
    os: 'linux',
  },
  {
    label: 'Homebrew',
    commands: ['brew tap hashicorp/tap', 'brew install hashicorp/tap/vault'],
    os: 'linux',
  },
]
<commit_msg>Updating website for 1.9.4 (#14373)<commit_after>export const VERSION = '1.9.4'
export const CHANGELOG_URL =
  'https://github.com/hashicorp/vault/blob/main/CHANGELOG.md#194'

// HashiCorp officially supported package managers
export const packageManagers = [
  {
    label: 'Homebrew',
    commands: ['brew tap hashicorp/tap', 'brew install hashicorp/tap/vault'],
    os: 'darwin',
  },
  {
    label: 'Ubuntu/Debian',
    commands: [
      'curl -fsSL https://apt.releases.hashicorp.com/gpg | sudo apt-key add -',
      'sudo apt-add-repository "deb [arch=amd64] https://apt.releases.hashicorp.com $(lsb_release -cs) main"',
      'sudo apt-get update && sudo apt-get install vault',
    ],
    os: 'linux',
  },
  {
    label: 'CentOS/RHEL',
    commands: [
      'sudo yum install -y yum-utils',
      'sudo yum-config-manager --add-repo https://rpm.releases.hashicorp.com/RHEL/hashicorp.repo',
      'sudo yum -y install vault',
    ],
    os: 'linux',
  },
  {
    label: 'Fedora',
    commands: [
      'sudo dnf install -y dnf-plugins-core',
      'sudo dnf config-manager --add-repo https://rpm.releases.hashicorp.com/fedora/hashicorp.repo',
      'sudo dnf -y install vault',
    ],
    os: 'linux',
  },
  {
    label: 'Amazon Linux',
    commands: [
      'sudo yum install -y yum-utils',
      'sudo yum-config-manager --add-repo https://rpm.releases.hashicorp.com/AmazonLinux/hashicorp.repo',
      'sudo yum -y install vault',
    ],
    os: 'linux',
  },
  {
    label: 'Homebrew',
    commands: ['brew tap hashicorp/tap', 'brew install hashicorp/tap/vault'],
    os: 'linux',
  },
]
<|endoftext|>


<commit_before>const fs = require('fs-extra');
const {getDestDir} = require('./paths');
const reload = require('./reload');
const {createTask} = require('./task');

async function bundleLocale(/** @type {string} */filePath, {production}) {
    let file = await fs.readFile(filePath, 'utf8');
    file = file.replace(/^#.*?$/gm, '');

    const messages = {};

    const regex = /@([a-z0-9_]+)/ig;
    let match;
    while (match = regex.exec(file)) {
        const messageName = match[1];
        const messageStart = match.index + match[0].length;
        let messageEnd = file.indexOf('@', messageStart);
        if (messageEnd < 0) {
            messageEnd = file.length;
        }
        messages[messageName] = {
            message: file.substring(messageStart, messageEnd).trim()
        };
    }

    const fileName = filePath.substring(filePath.lastIndexOf('/') + 1);
    const locale = fileName.substring(0, fileName.lastIndexOf('.')).replace('-', '_');
    const json = `${JSON.stringify(messages, null, 4)}\n`;
    const getOutputPath = (dir) => `${dir}/_locales/${locale}/messages.json`;
    const chromeDir = getDestDir({production});
    const firefoxDir = getDestDir({production, firefox: true});
    await fs.outputFile(getOutputPath(chromeDir), json);
    await fs.outputFile(getOutputPath(firefoxDir), json);
}

async function bundleLocales({production}) {
    const localesSrcDir = 'src/_locales';
    const list = await fs.readdir(localesSrcDir);
    for (const name of list) {
        if (!name.endsWith('.config')) {
            continue;
        }
        await bundleLocale(`${localesSrcDir}/${name}`, {production});
    }
}

module.exports = createTask(
    'bundle-locales',
    bundleLocales,
).addWatcher(
    ['src/_locales/**/*.config'],
    async (changedFiles) => {
        for (const file of changedFiles) {
            await bundleLocale(file, {production});
        }
        reload({type: reload.FULL});
    },
);
<commit_msg>Fixed error when rebuilding locales<commit_after>const fs = require('fs-extra');
const {getDestDir} = require('./paths');
const reload = require('./reload');
const {createTask} = require('./task');

async function bundleLocale(/** @type {string} */filePath, {production}) {
    let file = await fs.readFile(filePath, 'utf8');
    file = file.replace(/^#.*?$/gm, '');

    const messages = {};

    const regex = /@([a-z0-9_]+)/ig;
    let match;
    while (match = regex.exec(file)) {
        const messageName = match[1];
        const messageStart = match.index + match[0].length;
        let messageEnd = file.indexOf('@', messageStart);
        if (messageEnd < 0) {
            messageEnd = file.length;
        }
        messages[messageName] = {
            message: file.substring(messageStart, messageEnd).trim()
        };
    }

    const fileName = filePath.substring(filePath.lastIndexOf('/') + 1);
    const locale = fileName.substring(0, fileName.lastIndexOf('.')).replace('-', '_');
    const json = `${JSON.stringify(messages, null, 4)}\n`;
    const getOutputPath = (dir) => `${dir}/_locales/${locale}/messages.json`;
    const chromeDir = getDestDir({production});
    const firefoxDir = getDestDir({production, firefox: true});
    await fs.outputFile(getOutputPath(chromeDir), json);
    await fs.outputFile(getOutputPath(firefoxDir), json);
}

async function bundleLocales({production}) {
    const localesSrcDir = 'src/_locales';
    const list = await fs.readdir(localesSrcDir);
    for (const name of list) {
        if (!name.endsWith('.config')) {
            continue;
        }
        await bundleLocale(`${localesSrcDir}/${name}`, {production});
    }
}

module.exports = createTask(
    'bundle-locales',
    bundleLocales,
).addWatcher(
    ['src/_locales/**/*.config'],
    async (changedFiles) => {
        for (const file of changedFiles) {
            await bundleLocale(file, {production: false});
        }
        reload({type: reload.FULL});
    },
);
<|endoftext|>


<commit_before>// Copyright 2019 Google LLC
//
// 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
//
//     https://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.

import { toQueryString, getUrlVars, sleep } from './utils.js';

// Add function to global scope.
window.visualize = startVisualization;

// The timeout variable.
var simulationTimeout;

// Holds the simulation's metadata.
var simulation;

// Holds the simulation's visualization state.
var state = {
    currentRound: 0,
    isPaused: false,
    isFinished: false,
}

/**
 * Update simulation to holds simulation's metadata.
 * Initialize the visualization of the first round.
*/
async function startVisualization() {
    simulation = getUrlVars();
    var mainHeader = document.getElementById('simulation-visualization-header');
    mainHeader.innerText = 'Visualizing Simulation\n' + simulation.id;

    // Define the pause-button behavior.
    var pauseButton = document.getElementById('pause-button');
    pauseButton.onclick = function() {
        if (pauseButton.innerHTML === "Pause") {
            pause();
        } else {
            resume();
        }
    }
    nextRound();
}

/** Visualize the next round of the simulation. */
async function nextRound() {
    var roundHeader = document.getElementById('current-round-header');
    roundHeader.innerText = 'Current Round: ' + state.currentRound;

    var params = {
        simulationId: simulation.id,
        round: state.currentRound,
    };

    // Request real board state.
    params['isReal'] = true;
    await fetchReadBoardState(params, 'real-board-table');

    // Request estimated board state.
    params['isReal'] = false;
    await fetchReadBoardState(params, 'estimated-board-table');

    // Update the simulation's state and the timeout.
    updateState();
    if (!state.isPaused && !state.isFinished) {
        var delay = 1/document.getElementById('speed-controller').value;
        simulationTimeout = setTimeout(nextRound, delay);
    }

    // Visualize the statistics if the simulation is over.
    if (state.isFinished) {
        resetOrDisplayStats();
    }
}

/** Allow the user to decide whether they want to display the statistics or visualize the simulation again. */
function resetOrDisplayStats() {
    var flowButtons = document.getElementById('flow-buttons');
    var resetButton = document.createElement("button");
    resetButton.innerHTML = "reset";
    resetButton.addEventListener("click", reset);
    flowButtons.appendChild(resetButton);

    var displayStatsButton = document.createElement("button");
    displayStatsButton.innerHTML = "display statistics";
    displayStatsButton.addEventListener("click", displayStats);
    flowButtons.appendChild(displayStatsButton);
}

/** Update the current round. */
function updateState() {
    state.currentRound += 1;
    if (state.currentRound >= simulation.roundsNum) {
        state.isFinished = true;
    }
}

/** Continue the visualization. */
function resume() {
    var pauseButton = document.getElementById('pause-button');
    pauseButton.innerHTML = "Pause";

    state.isPaused = false;
    var delay = 1/document.getElementById('speed-controller').value;
    simulationTimeout = setTimeout(nextRound, delay);
}

/** Stop the visualization. */
function pause() {
    var pauseButton = document.getElementById('pause-button');
    pauseButton.innerHTML = "Play";

    state.isPaused = true;
    clearTimeout(simulationTimeout);
}

/**
 * Fetch board state from servlet by given parameters, and update an HTML table corresponding to given
 * board element id to visualize the board's state.
 * @param {Object} params is an object storing parameters for http request.
 * @param {String} boardElementId is the id of the html table element to be updated.
 */
async function fetchReadBoardState(params, boardElementId) {
    const queryString = toQueryString(params);
    fetch(`/read-board-state?${queryString}`)
    .then(response => response.json())
    .then(boardState => visualizeBoardState(boardState.array, boardElementId));
}

/**
 * Given a board state and an id of an html table element, update the table to display the board state.
 * @param {String[][]} board is the board state to visualize.
 * @param {String} tableId is the id of the html table element to be updated.
 */
function visualizeBoardState(board, tableId) {

    // Set table and table body
    var boardTable = document.getElementById(tableId);
    var boardTableBody = document.createElement('tbody');

    // Change cells' classes according to board.
    board.forEach(function(rowData) {

        var row = document.createElement('tr');
        row.classList.add('board-tr')

        rowData.forEach(function(cellData) {
            var cell = document.createElement('td');
            cell.classList.add('board-td');
            updateCell(cell, cellData);
            row.appendChild(cell);
        });

        boardTableBody.appendChild(row);
    });

    // Clear last state of table (if table body exists).
    var oldTableBodies = boardTable.tBodies;
    if (oldTableBodies.length > 0) {
        boardTable.removeChild(oldTableBodies[0]);
    }

    // Append updated table body.
    boardTable.appendChild(boardTableBody);
}

/**
 * Update the cell according to the agents inside it.
 * @param {HTMLTableDataCellElement} cell is an HTML element from type <td>, representing a cell on the board.
 * @param {String[]} agents is a list of agents ids located inside the cell.
 */
function updateCell(cell, agents) {
    var beaconsIdsInsideCell = extractBeaconsIds(agents);
    cell.setAttribute('title', beaconsIdsInsideCell.join(","));

    var beaconToFocusOn = document.getElementById("beacon-id").value;
    var isBeaconToFocusOnInCell = beaconToFocusOn != "" && beaconsIdsInsideCell.includes(beaconToFocusOn);
    cell.classList.add(determineCellClass(agents, beaconsIdsInsideCell.length, isBeaconToFocusOnInCell));
}

/**
 * return the css class name representing the correct state of the cell (empty/contains beacons/observers only).
 * @param {String[]} agents a list of agents ids located in the same cell of the board.
 * @returns {String} the correct css class name.
 */
function determineCellClass(agents, numberOfBeacons, isBeaconToFocusOnInCell) {
    if (agents.length === 0) {
        return 'board-td-empty';
    }
    if (isBeaconToFocusOnInCell) {
        return 'board-td-contains-wanted-beacon';
    }
    if (numberOfBeacons > 0) {
       return 'board-td-contains-beacon';
    }
    return 'board-td-observers-only';
}

/**
 * Returns a sorted list of all the beacons' ids in the given list.
 * @param {String[]} agents is a list of agents ids located in the same cell of the board.
 */
function extractBeaconsIds(agents) {
    var beaconsIds = agents.filter(agent => agent.charAt(0) === 'B')
                    .map(agent => agent.slice(6));
    return beaconsIds.sort((a, b) => parseInt(a) - parseInt(b));
}

/** Display the statistical data of a simulation. */
function displayStats() {
    clearFlowButtons();
    clearVisualizationElements();
    var params = {simulationId: simulation.id};
    const queryString = toQueryString(params);

    fetch(`/read-stats?${queryString}`)
    .then(response => response.json())
    .then(stats => Object.keys(stats).forEach(kind => createStatsTable(kind, stats[kind])));

    fetch(`/read-observed-stats?${queryString}`)
    .then(response => response.text())
    .then(response => JSON.parse(response.replace(/\bNaN\b/g, "null")))
    .then(stats => createObservedStatsTable(stats.rowMap));
}

/**
 * Create an HTML table element and fill it with statistical data.
 * @param {String} kind is the statistical data kind (distance or beacon observed percent).
 * @param {Object} stats is an object storing the simulations' stats of a specific kind.
 */
function createStatsTable(kind, stats) {
    var title = document.createElement('h4');
    title.innerText = kind;
    var table = document.createElement('table');
    table.classList.add('stats-table');
    var header = table.createTHead();
    var headerRow = header.insertRow(0);
    var dataRow = table.insertRow(1);

    // For beacon observed stats add the prefix 'beacon' to each measure name.
    var prefix = kind === 'BeaconsObservedPercentStats' ? 'beacon #' : '';

    var i = 0;
    Object.keys(stats).forEach(measure => {
        headerRow.insertCell(i).innerText = prefix + measure;
        dataRow.insertCell(i).innerText = stats[measure].toFixed(3);
        i++;
    });
    document.body.appendChild(title);
    document.body.appendChild(table);
}

/**
 * Create an HTML table element and fill it with statistics of observed intervals.
 * @param {Object} stats is an object storing the observed statistics.
 */
function createObservedStatsTable(stats) {
    var title = document.createElement('h4');
    title.innerText = "Observed Statistics";

    var table = document.createElement('table');
    table.classList.add('stats-table');
    var measures = Object.keys(stats[Object.keys(stats)[0]]);
    addHeader(table, measures);
    addStatisticsRows(table, stats);

    document.body.appendChild(title);
    document.body.appendChild(table);
}

/**
 * Given a table and an object that contains the header properties, add a header based on the object's items.
 * @param {HTMLElement} table is the html table to be updated.
 * @param {Object} properties is an object that contains the header properties.
 */
function addHeader(table, properties) {
    var header = table.createTHead();
    var row = header.insertRow(0);

    row.insertCell(0).innerHTML = "beacon's Id";
    for (var i = 0; i < properties.length; i++) {
        row.insertCell(i + 1).innerHTML = properties[i];
    }
}

/**
 * Given a table and an object storing beacons' stats, add a row for each beacon.
 * @param {HTMLElement} table is the table to be updated.
 * @param {Object} stats is an object storing beacons' id and stats.
 */
function addStatisticsRows(table, stats) {
    var prefix = 'beacon #';
    var hasNoValue = "-";

    for (const id in stats) {
        var row = table.insertRow(-1);
        row.insertCell(0).innerHTML = prefix + id;
        var i = 1; // cell index (cell 0 is a button).
        for (const property in stats[id]) {
            var value = stats[id][property]
            row.insertCell(i++).innerHTML = (value !== null) ? value : hasNoValue;
        }
    }
}

/** Clear all HTML element used for visualization. */
function clearVisualizationElements() {
    document.getElementsByClassName('boards')[0].innerHTML = '';
    document.getElementsByClassName('choose-beacon')[0].innerHTML = '';
    document.getElementsByClassName('speed-controller')[0].innerHTML = '';
    document.getElementsByClassName('pause-button')[0].innerHTML = '';
    document.getElementsByClassName('legend')[0].innerHTML = '';
    document.getElementById('current-round-header').innerHTML = '';
}

/** Start the simulation from the first round. */
function reset() {
    clearFlowButtons();
    state.isFinished = false;
    state.currentRound = 0;
    nextRound();
}

/** Clear the buttons that allow the user to decide whether they want to display the statistics or visualize the simulation again. */
function clearFlowButtons() {
    var flowButtons = document.getElementById('flow-buttons');
    flowButtons.innerHTML = '';
}<commit_msg>Round the statistics.<commit_after>// Copyright 2019 Google LLC
//
// 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
//
//     https://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.

import { toQueryString, getUrlVars, sleep } from './utils.js';

// Add function to global scope.
window.visualize = startVisualization;

// The timeout variable.
var simulationTimeout;

// Holds the simulation's metadata.
var simulation;

// Holds the simulation's visualization state.
var state = {
    currentRound: 0,
    isPaused: false,
    isFinished: false,
}

/**
 * Update simulation to holds simulation's metadata.
 * Initialize the visualization of the first round.
*/
async function startVisualization() {
    simulation = getUrlVars();
    var mainHeader = document.getElementById('simulation-visualization-header');
    mainHeader.innerText = 'Visualizing Simulation\n' + simulation.id;

    // Define the pause-button behavior.
    var pauseButton = document.getElementById('pause-button');
    pauseButton.onclick = function() {
        if (pauseButton.innerHTML === "Pause") {
            pause();
        } else {
            resume();
        }
    }
    nextRound();
}

/** Visualize the next round of the simulation. */
async function nextRound() {
    var roundHeader = document.getElementById('current-round-header');
    roundHeader.innerText = 'Current Round: ' + state.currentRound;

    var params = {
        simulationId: simulation.id,
        round: state.currentRound,
    };

    // Request real board state.
    params['isReal'] = true;
    await fetchReadBoardState(params, 'real-board-table');

    // Request estimated board state.
    params['isReal'] = false;
    await fetchReadBoardState(params, 'estimated-board-table');

    // Update the simulation's state and the timeout.
    updateState();
    if (!state.isPaused && !state.isFinished) {
        var delay = 1/document.getElementById('speed-controller').value;
        simulationTimeout = setTimeout(nextRound, delay);
    }

    // Visualize the statistics if the simulation is over.
    if (state.isFinished) {
        resetOrDisplayStats();
    }
}

/** Allow the user to decide whether they want to display the statistics or visualize the simulation again. */
function resetOrDisplayStats() {
    var flowButtons = document.getElementById('flow-buttons');
    var resetButton = document.createElement("button");
    resetButton.innerHTML = "reset";
    resetButton.addEventListener("click", reset);
    flowButtons.appendChild(resetButton);

    var displayStatsButton = document.createElement("button");
    displayStatsButton.innerHTML = "display statistics";
    displayStatsButton.addEventListener("click", displayStats);
    flowButtons.appendChild(displayStatsButton);
}

/** Update the current round. */
function updateState() {
    state.currentRound += 1;
    if (state.currentRound >= simulation.roundsNum) {
        state.isFinished = true;
    }
}

/** Continue the visualization. */
function resume() {
    var pauseButton = document.getElementById('pause-button');
    pauseButton.innerHTML = "Pause";

    state.isPaused = false;
    var delay = 1/document.getElementById('speed-controller').value;
    simulationTimeout = setTimeout(nextRound, delay);
}

/** Stop the visualization. */
function pause() {
    var pauseButton = document.getElementById('pause-button');
    pauseButton.innerHTML = "Play";

    state.isPaused = true;
    clearTimeout(simulationTimeout);
}

/**
 * Fetch board state from servlet by given parameters, and update an HTML table corresponding to given
 * board element id to visualize the board's state.
 * @param {Object} params is an object storing parameters for http request.
 * @param {String} boardElementId is the id of the html table element to be updated.
 */
async function fetchReadBoardState(params, boardElementId) {
    const queryString = toQueryString(params);
    fetch(`/read-board-state?${queryString}`)
    .then(response => response.json())
    .then(boardState => visualizeBoardState(boardState.array, boardElementId));
}

/**
 * Given a board state and an id of an html table element, update the table to display the board state.
 * @param {String[][]} board is the board state to visualize.
 * @param {String} tableId is the id of the html table element to be updated.
 */
function visualizeBoardState(board, tableId) {

    // Set table and table body
    var boardTable = document.getElementById(tableId);
    var boardTableBody = document.createElement('tbody');

    // Change cells' classes according to board.
    board.forEach(function(rowData) {

        var row = document.createElement('tr');
        row.classList.add('board-tr')

        rowData.forEach(function(cellData) {
            var cell = document.createElement('td');
            cell.classList.add('board-td');
            updateCell(cell, cellData);
            row.appendChild(cell);
        });

        boardTableBody.appendChild(row);
    });

    // Clear last state of table (if table body exists).
    var oldTableBodies = boardTable.tBodies;
    if (oldTableBodies.length > 0) {
        boardTable.removeChild(oldTableBodies[0]);
    }

    // Append updated table body.
    boardTable.appendChild(boardTableBody);
}

/**
 * Update the cell according to the agents inside it.
 * @param {HTMLTableDataCellElement} cell is an HTML element from type <td>, representing a cell on the board.
 * @param {String[]} agents is a list of agents ids located inside the cell.
 */
function updateCell(cell, agents) {
    var beaconsIdsInsideCell = extractBeaconsIds(agents);
    cell.setAttribute('title', beaconsIdsInsideCell.join(","));

    var beaconToFocusOn = document.getElementById("beacon-id").value;
    var isBeaconToFocusOnInCell = beaconToFocusOn != "" && beaconsIdsInsideCell.includes(beaconToFocusOn);
    cell.classList.add(determineCellClass(agents, beaconsIdsInsideCell.length, isBeaconToFocusOnInCell));
}

/**
 * return the css class name representing the correct state of the cell (empty/contains beacons/observers only).
 * @param {String[]} agents a list of agents ids located in the same cell of the board.
 * @returns {String} the correct css class name.
 */
function determineCellClass(agents, numberOfBeacons, isBeaconToFocusOnInCell) {
    if (agents.length === 0) {
        return 'board-td-empty';
    }
    if (isBeaconToFocusOnInCell) {
        return 'board-td-contains-wanted-beacon';
    }
    if (numberOfBeacons > 0) {
       return 'board-td-contains-beacon';
    }
    return 'board-td-observers-only';
}

/**
 * Returns a sorted list of all the beacons' ids in the given list.
 * @param {String[]} agents is a list of agents ids located in the same cell of the board.
 */
function extractBeaconsIds(agents) {
    var beaconsIds = agents.filter(agent => agent.charAt(0) === 'B')
                    .map(agent => agent.slice(6));
    return beaconsIds.sort((a, b) => parseInt(a) - parseInt(b));
}

/** Display the statistical data of a simulation. */
function displayStats() {
    clearFlowButtons();
    clearVisualizationElements();
    var params = {simulationId: simulation.id};
    const queryString = toQueryString(params);

    fetch(`/read-stats?${queryString}`)
    .then(response => response.json())
    .then(stats => Object.keys(stats).forEach(kind => createStatsTable(kind, stats[kind])));

    fetch(`/read-observed-stats?${queryString}`)
    .then(response => response.text())
    .then(response => JSON.parse(response.replace(/\bNaN\b/g, "null")))
    .then(stats => createObservedStatsTable(stats.rowMap));
}

/**
 * Create an HTML table element and fill it with statistical data.
 * @param {String} kind is the statistical data kind (distance or beacon observed percent).
 * @param {Object} stats is an object storing the simulations' stats of a specific kind.
 */
function createStatsTable(kind, stats) {
    var title = document.createElement('h4');
    title.innerText = kind;
    var table = document.createElement('table');
    table.classList.add('stats-table');
    var header = table.createTHead();
    var headerRow = header.insertRow(0);
    var dataRow = table.insertRow(1);

    // For beacon observed stats add the prefix 'beacon' to each measure name.
    var prefix = kind === 'BeaconsObservedPercentStats' ? 'beacon #' : '';

    var i = 0;
    Object.keys(stats).forEach(measure => {
        headerRow.insertCell(i).innerText = prefix + measure;
        dataRow.insertCell(i).innerText = stats[measure].toFixed(3);
        i++;
    });
    document.body.appendChild(title);
    document.body.appendChild(table);
}

/**
 * Create an HTML table element and fill it with statistics of observed intervals.
 * @param {Object} stats is an object storing the observed statistics.
 */
function createObservedStatsTable(stats) {
    var title = document.createElement('h4');
    title.innerText = "Observed Statistics";

    var table = document.createElement('table');
    table.classList.add('stats-table');
    var measures = Object.keys(stats[Object.keys(stats)[0]]);
    addHeader(table, measures);
    addStatisticsRows(table, stats);

    document.body.appendChild(title);
    document.body.appendChild(table);
}

/**
 * Given a table and an object that contains the header properties, add a header based on the object's items.
 * @param {HTMLElement} table is the html table to be updated.
 * @param {Object} properties is an object that contains the header properties.
 */
function addHeader(table, properties) {
    var header = table.createTHead();
    var row = header.insertRow(0);

    row.insertCell(0).innerHTML = "beacon's Id";
    for (var i = 0; i < properties.length; i++) {
        row.insertCell(i + 1).innerHTML = properties[i];
    }
}

/**
 * Given a table and an object storing beacons' stats, add a row for each beacon.
 * @param {HTMLElement} table is the table to be updated.
 * @param {Object} stats is an object storing beacons' id and stats.
 */
function addStatisticsRows(table, stats) {
    var prefix = 'beacon #';
    var hasNoValue = "-";

    for (const id in stats) {
        var row = table.insertRow(-1);
        row.insertCell(0).innerHTML = prefix + id;
        var i = 1; // cell index (cell 0 is a button).
        for (const property in stats[id]) {
            var value = stats[id][property]
            row.insertCell(i++).innerHTML = (value !== null) ? value.toFixed(3) : hasNoValue;
        }
    }
}

/** Clear all HTML element used for visualization. */
function clearVisualizationElements() {
    document.getElementsByClassName('boards')[0].innerHTML = '';
    document.getElementsByClassName('choose-beacon')[0].innerHTML = '';
    document.getElementsByClassName('speed-controller')[0].innerHTML = '';
    document.getElementsByClassName('pause-button')[0].innerHTML = '';
    document.getElementsByClassName('legend')[0].innerHTML = '';
    document.getElementById('current-round-header').innerHTML = '';
}

/** Start the simulation from the first round. */
function reset() {
    clearFlowButtons();
    state.isFinished = false;
    state.currentRound = 0;
    nextRound();
}

/** Clear the buttons that allow the user to decide whether they want to display the statistics or visualize the simulation again. */
function clearFlowButtons() {
    var flowButtons = document.getElementById('flow-buttons');
    flowButtons.innerHTML = '';
}<|endoftext|>


<commit_before>"use strict";

import http from 'http';
import { Router } from 'express';
import list from './list';
import post from './post';
import put from './put';
import del from './delete';
import patch from './patch';
import get from './get';

const getRouter = (mongooseModel, { url, hooks, actions, options }) => {
  let resourceListURL = `/${url}`;
  let resourceURL = `${resourceListURL}\\(:id\\)`;

  let routes = [
    {
      method: 'post',
      url: resourceListURL,
      ctrl: post,
      hook: hooks.post,
    },
    {
      method: 'put',
      url: resourceURL,
      ctrl: put,
      hook: hooks.put,
    },
    {
      method: 'patch',
      url: resourceURL,
      controller: patch,
      config: hooks.patch,
    },
    {
      method: 'delete',
      url: resourceURL,
      ctrl: del,
      hook: hooks.delete,
    },
    {
      method: 'get',
      url: resourceURL,
      ctrl: get,
      hook: hooks.get,
    },
    {
      method: 'get',
      url: resourceListURL,
      ctrl: list,
      hook: hooks.list,
    },
  ];

  let router = Router();

  // add REST routes.
  routes.map((route) => {
    let { method, url, ctrl, hook } = route;
    router[method](url, (req, res, next) => {
      authorizePipe(req, res, hook.auth)
      .then(function() { return beforePipe(req, res, hook.before); })
      .then(function() { return ctrl(req, mongooseModel, options); })
      .then(function(result) { return respondPipe(req, res, result); })
      .then(function(data) { return afterPipe(req, res, hook.after, data); })
      .catch(function(err) { errorPipe(req, res, err); });
    });
  });

  // add ACTION routes.
  Object.keys(actions).map(function(url) {
    let action = actions[url];
    router.post(`${resourceURL}${url}`, (req, res, next) => {
      authorizePipe(req, res, action.auth).then(function() {
        action(req, res, next);
      }).catch(function(result) { errorPipe(req, res, result); });
    });
  });

  return router;
};

function authorizePipe(req, res, auth) {
  return new Promise((resolve, reject) => {
    if (auth !== undefined) {
      if (!auth(req, res)) {
        return reject({ status: 401 });
      }
    }
    resolve();
  });
}

function beforePipe(req, res, before) {
  return new Promise((resolve, reject) => {
    if (before) {
      before(req.body, req, res);
    }
    resolve();
  });
}

function respondPipe(req, res, result) {
  return new Promise((resolve, reject) => {
    let status = result.status || 200;
    let data = result.entity;
    res.status(status).jsonp(data);
    resolve(data);
  });
}

function afterPipe(req, res, after, data) {
  return new Promise((resolve, reject) => {
    if (after) {
      after(data, req.body, req, res);
    }
    resolve();
  });
}

function errorPipe(req, res, err) {
  return new Promise((resolve, reject) => {
    let status = err.status || 500;
    let text = err.text || err.message || http.STATUS_CODES[status];
    res.status(status).send(text);
  });
}

export default { getRouter };
<commit_msg>fixed delete will return undefined bug.<commit_after>"use strict";

import http from 'http';
import { Router } from 'express';
import list from './list';
import post from './post';
import put from './put';
import del from './delete';
import patch from './patch';
import get from './get';

const getRouter = (mongooseModel, { url, hooks, actions, options }) => {
  let resourceListURL = `/${url}`;
  let resourceURL = `${resourceListURL}\\(:id\\)`;

  let routes = [
    {
      method: 'post',
      url: resourceListURL,
      ctrl: post,
      hook: hooks.post,
    },
    {
      method: 'put',
      url: resourceURL,
      ctrl: put,
      hook: hooks.put,
    },
    {
      method: 'patch',
      url: resourceURL,
      controller: patch,
      config: hooks.patch,
    },
    {
      method: 'delete',
      url: resourceURL,
      ctrl: del,
      hook: hooks.delete,
    },
    {
      method: 'get',
      url: resourceURL,
      ctrl: get,
      hook: hooks.get,
    },
    {
      method: 'get',
      url: resourceListURL,
      ctrl: list,
      hook: hooks.list,
    },
  ];

  let router = Router();

  // add REST routes.
  routes.map((route) => {
    let { method, url, ctrl, hook } = route;
    router[method](url, (req, res, next) => {
      authorizePipe(req, res, hook.auth)
      .then(function() { return beforePipe(req, res, hook.before); })
      .then(function() { return ctrl(req, mongooseModel, options); })
      .then(function(result) { return respondPipe(req, res, result || {}); })
      .then(function(data) { return afterPipe(req, res, hook.after, data); })
      .catch(function(err) { errorPipe(req, res, err); });
    });
  });

  // add ACTION routes.
  Object.keys(actions).map(function(url) {
    let action = actions[url];
    router.post(`${resourceURL}${url}`, (req, res, next) => {
      authorizePipe(req, res, action.auth).then(function() {
        action(req, res, next);
      }).catch(function(result) { errorPipe(req, res, result); });
    });
  });

  return router;
};

function authorizePipe(req, res, auth) {
  return new Promise((resolve, reject) => {
    if (auth !== undefined) {
      if (!auth(req, res)) {
        return reject({ status: 401 });
      }
    }
    resolve();
  });
}

function beforePipe(req, res, before) {
  return new Promise((resolve, reject) => {
    if (before) {
      before(req.body, req, res);
    }
    resolve();
  });
}

function respondPipe(req, res, result) {
  return new Promise((resolve, reject) => {
    let status = result.status || 200;
    let data = result.entity;
    res.status(status).jsonp(data);
    resolve(data);
  });
}

function afterPipe(req, res, after, data) {
  return new Promise((resolve, reject) => {
    if (after) {
      after(data, req.body, req, res);
    }
    resolve();
  });
}

function errorPipe(req, res, err) {
  return new Promise((resolve, reject) => {
    let status = err.status || 500;
    let text = err.text || err.message || http.STATUS_CODES[status];
    res.status(status).send(text);
  });
}

export default { getRouter };
<|endoftext|>