<commit_before>/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */

'use strict'

var getTarget;
var getTargetFunction;
var targetFunctions;
var saveTargetInfo;

var matchLabel;
var getAnchors;
var createLabel;

(function() {
  var log = getLog('target');

  /* Store information about the DOM node */
  saveTargetInfo = function _saveTargetInfo(target, recording) {
    var allFeatures = ["tagName", "className",
      "left", "bottom", "right", "top", "width", "height",
      "font-size", "font-family", "font-style", "font-weight", "color",
      "background-color",
      "preceding-text",
      "xpath",
      "anchor",
      "snapshot"];

    // if (recording == RecordState.RECORDING) {
    //   allFeatures.push('branch');
    // }

    var targetInfo = {};
    for (var i = 0, ii = allFeatures.length; i < ii; ++i) {
      var feature = allFeatures[i];
      switch (feature) {
        case "xpath":
          targetInfo[feature] = nodeToXPath(target);
          break;
        case "snapshot":
          targetInfo[feature] = snapshotNode(target);
          break;
        case "branch":
          targetInfo[feature] = snapshotBranch(target);
          break;
        case "preceding-text":
          targetInfo[feature] = $(target).prev().text();
          break;
        case "top":
        case "right":
        case "bottom":
        case "left":
        case "width":
        case "height":
          if (target.getBoundingClientRect) {
            var rect = target.getBoundingClientRect();
            targetInfo[feature] = rect[feature];
          }
          break;
        case "tagName":
        case "className":
          targetInfo[feature] = target[feature];
          break;
        case "anchor":
          targetInfo[feature] = getAnchors(target);
          break;
        default:
          var style = getComputedStyle(target, null);
          if (style)
            targetInfo[feature] = style.getPropertyValue(feature);
      }
    }
    return targetInfo;
  };

  createLabel = function(e) {
    var tagName = e.tagName;
    var label = {tagName: tagName};

    // list of tags taken from
    // https://developer.mozilla.org/en-US/docs/Web/HTML/Element
    // these all add visual clues to the
    if (["A", "BUTTON", "LABEL", "OPTION"].indexOf(tagName) != -1) {
      label.textContent = e.textContent;
    } else if (["H1", "H2", "H3", "H4", "H5", "H6", "DIV", "SPAN"].
        indexOf(tagName) != -1) {
      var textContent = e.textContent;
      if (textContent.length < 20)
        label.textContent = textContent;
    } else if (["IMG"].indexOf(tagName) != -1) {
      if (e.alt)
        label.alt = e.alt;
    }
    return label;
  };

  var getUniqueSubtree =  function(element, otherNodes) {
    function checkContains(subtree, otherNodes) {
      for (var i = 0, ii = otherNodes.length; i < ii; ++i) {
        if (element.contains(otherNodes[i]))
          return true;
      }
      return false;
    }

    var e = element;
    var p = e.parentElement;
    while (e != document.body && checkContains(p, otherNodes)) {
      e = p;
      p = e.parentElement;
    }
    return e;
  };

  getAnchors = function(target) {
  // function getAnchors(target) {
    var label = createLabel(target);
    // sometimes we try to find anchors of the head element, which means the
    // body doesnt exist
    if (document.body)
      var otherConflictElements = matchLabel(label, document.body);
    else
      var otherConflictElements = [];

    return {
      label: label,
      numConflicts: otherConflictElements.length
    }
  };

  matchLabel = function(label, root) {
//  function findMatchingElements(label, root) {
    function evaluateXPath(aNode, aExpr) {
      var xpe = new XPathEvaluator();
      var nsResolver = xpe.createNSResolver(aNode.ownerDocument == null ?
        aNode.documentElement : aNode.ownerDocument.documentElement);
      var result = xpe.evaluate(aExpr, aNode, nsResolver, 0, null);
      var found = [];
      var res;
      while (res = result.iterateNext())
        found.push(res);
      return found;
    }

    var query = nodeToXPath(root.parentElement);
    var tagName = label.tagName;
    query += '//' + tagName;
    if (label.textContent)
      query += '[text()="' + label.textContent + '"]';

    var matches = evaluateXPath(document, query);
    if (label.alt)
      matches = matches.filter(function(e) { return e.alt == label.alt; });

    return matches;
  };

  /* The following functions are different implementations to take a target
   * info object, and convert it to a list of possible DOM nodes */

  function getTargetSimple(targetInfo) {
    return xPathToNodes(targetInfo.xpath);
  }

  function getTargetSuffix(targetInfo) {

    function helper(xpath) {
      var index = 0;
      while (xpath[index] == '/')
        index++;

      if (index > 0)
        xpath = xpath.slice(index);

      var targets = xPathToNodes('//' + xpath);

      if (targets.length > 0) {
        return targets;
      }

      /* If we're here, we failed to find the child. Try dropping
       * steadily larger prefixes of the xpath until some portion works.
       * Gives up if only three levels left in xpath. */
      if (xpath.split('/').length < 4) {
        /* No more prefixes to reasonably remove, so give up */
        return [];
      }

      var index = xpath.indexOf('/');
      xpathSuffix = xpath.slice(index + 1);
      return helper(xpathSuffix);
    }

    return helper(targetInfo.xpath);
  }

  function getTargetText(targetInfo) {
    var text = targetInfo.snapshot.prop.innerText;
    if (text) {
      return xPathToNodes('//*[text()="' + text + '"]');
    }
    return [];
  }

  function getTargetSearch(targetInfo) {
    /* search over changes to the ancesters (replacing each ancestor with a
     * star plus changes such as adding or removing ancestors) */

    function helper(xpathSplit, index) {
      if (index == 0)
        return [];

      var targets;

      if (index < xpathSplit.length - 1) {
        var clone = xpathSplit.slice(0);
        var xpathPart = clone[index];

        clone[index] = '*';
        targets = xPathToNodes(clone.join('/'));
        if (targets.length > 0)
          return targets;

        clone.splice(index, 0, xpathPart);
        targets = xPathToNodes(clone.join('/'));
        if (targets.length > 0)
          return targets;
      }

      targets = xPathToNodes(xpathSplit.join('/'));
      if (targets.length > 0)
        return targets;

      return helper(xpathSplit, index - 1);
    }

    var split = targetInfo.xpath.split('/');
    return helper(split, split.length - 1);
  }

  function getTargetClass(targetInfo) {
    var className = targetInfo.snapshot.prop.className;
    if (className) {
      //xPathToNodes("//*[@class='" + className + "']");

      var classes = className.trim().replace(':', '\\:').split(' ');
      var selector = '';
      for (var i = 0, ii = classes.length; i < ii; ++i) {
        var className = classes[i];
        if (className)
          selector += '.' + classes[i];
      }

      return $.makeArray($(selector));
    }
    return [];
  }

  function getTargetId(targetInfo) {
    var id = targetInfo.snapshot.prop.id;
    if (id) {
      var selector = '#' + id.trim().replace(':', '\\:');
      return $.makeArray($(selector));
    }
    return [];
  }

  function getTargetComposite(targetInfo) {
    var targets = [];
    var metaInfo = [];

    for (var strategy in targetFunctions) {
      try {
        var strategyTargets = targetFunctions[strategy](targetInfo);
        for (var i = 0, ii = strategyTargets.length; i < ii; ++i) {
          var t = strategyTargets[i];
          var targetIndex = targets.indexOf(t);
          if (targetIndex == -1) {
            targets.push(t);
            metaInfo.push([strategy]);
          } else {
            metaInfo[targetIndex].push(strategy);
          }
        }
      } catch (e) {}
    }

    var maxStrategies = 0;
    var maxTargets = [];
    for (var i = 0, ii = targets.length; i < ii; ++i) {
      var numStrategies = metaInfo[i].length;
      if (numStrategies == maxStrategies) {
        maxTargets.push(targets[i]);
      } else if (numStrategies > maxStrategies) {
        maxTargets = [targets[i]];
        maxStrategies = numStrategies;
      }
    }

    return maxTargets;
  }

  /* Set the target function */
  getTargetFunction = getTargetComposite;

  /* Given the target info, produce a single target DOM node. May get several
   * possible candidates, and would just return the first candidate. */
  getTarget = function(targetInfo) {
    var targets = getTargetFunction(targetInfo);
    if (!targets) {
      log.warn('No target found');
      return null;
    } else if (targets.length > 1) {
      log.warn('Multiple targets found:', targets);
      return null;
    } else {
      return targets[0];
    }
  };

  /* List of all target functions. Used for benchmarking */
  targetFunctions = {
    simple: getTargetSimple,
    suffix: getTargetSuffix,
    text: getTargetText,
    class: getTargetClass,
    id: getTargetId,
    search: getTargetSearch
  };

})();
<commit_msg>Adding in sarahs node addressing algorithms<commit_after>/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */

'use strict'

var getTarget;
var getTargetFunction;
var targetFunctions;
var saveTargetInfo;

var matchLabel;
var getAnchors;
var createLabel;

(function() {
  var log = getLog('target');

  function getFeatures(element) {
    var info = {};
    info.xpath = nodeToXPath(element);
    for (var prop in element) {
      if (element.hasOwnProperty(prop)) {
        var val = element[prop];
        if (val !== null && typeof val === 'object'){
          val = val.toString();
        }
        info[prop] = val;
      }
    }
    var text = element.textContent;
    info.textContent = text;
    var trimmedText = text.trim();
    info.firstWord = trimmedText.slice(0,trimmedText.indexOf(" "));
    info.lastWord = trimmedText.slice(trimmedText.lastIndexOf(" "),trimmedText.length);
    var colonIndex = trimmedText.indexOf(":")
      if (colonIndex > -1){
        info.preColonText = trimmedText.slice(0,colonIndex);
      }
    var children = element.childNodes;
    var l = children.length;
    for (var i = 0; i< l; i++){
      var childText = children[i].textContent;
      info["child"+i+"text"] = childText;
      info["lastChild"+(l-i)+"text"] = childText;
    }
    var prev = element.previousElementSibling;
    if (prev !== null){
      info.previousElementSiblingText = prev.textContent;
    }
    var boundingBox = element.getBoundingClientRect();
    for (var prop in boundingBox) {
      if (boundingBox.hasOwnProperty(prop)) {
        info[prop] = boundingBox.prop;
      }
    }
    var style = window.getComputedStyle(element, null);
    for (var i = 0; i < style.length; i++) {
      var prop = style[i];
      info[prop] = style.getPropertyValue(prop);
    }
    return info;
  }

  /* Store information about the DOM node */
  saveTargetInfo = function _saveTargetInfo(target, recording) {
    var allFeatures = ["tagName", "className",
      "left", "bottom", "right", "top", "width", "height",
      "font-size", "font-family", "font-style", "font-weight", "color",
      "background-color",
      "preceding-text",
      "xpath",
      "anchor",
      "snapshot"];

    // if (recording == RecordState.RECORDING) {
    //   allFeatures.push('branch');
    // }

    var targetInfo = {};
    for (var i = 0, ii = allFeatures.length; i < ii; ++i) {
      var feature = allFeatures[i];
      switch (feature) {
        case "xpath":
          targetInfo[feature] = nodeToXPath(target);
          break;
        case "snapshot":
          targetInfo[feature] = snapshotNode(target);
          break;
        case "branch":
          targetInfo[feature] = snapshotBranch(target);
          break;
        case "preceding-text":
          targetInfo[feature] = $(target).prev().text();
          break;
        case "top":
        case "right":
        case "bottom":
        case "left":
        case "width":
        case "height":
          if (target.getBoundingClientRect) {
            var rect = target.getBoundingClientRect();
            targetInfo[feature] = rect[feature];
          }
          break;
        case "tagName":
        case "className":
          targetInfo[feature] = target[feature];
          break;
        case "anchor":
          targetInfo[feature] = getAnchors(target);
          break;
        default:
          var style = getComputedStyle(target, null);
          if (style)
            targetInfo[feature] = style.getPropertyValue(feature);
      }
    }
    return targetInfo;
  };

//  createLabel = function(e) {
//    var tagName = e.tagName;
//    var label = {tagName: tagName};
//
//    // list of tags taken from
//    // https://developer.mozilla.org/en-US/docs/Web/HTML/Element
//    // these all add visual clues to the
//    if (["A", "BUTTON", "LABEL", "OPTION"].indexOf(tagName) != -1) {
//      label.textContent = e.textContent;
//    } else if (["H1", "H2", "H3", "H4", "H5", "H6", "DIV", "SPAN"].
//        indexOf(tagName) != -1) {
//      var textContent = e.textContent;
//      if (textContent.length < 20)
//        label.textContent = textContent;
//    } else if (["IMG"].indexOf(tagName) != -1) {
//      if (e.alt)
//        label.alt = e.alt;
//    }
//    return label;
//  };
//
//  var getUniqueSubtree =  function(element, otherNodes) {
//    function checkContains(subtree, otherNodes) {
//      for (var i = 0, ii = otherNodes.length; i < ii; ++i) {
//        if (element.contains(otherNodes[i]))
//          return true;
//      }
//      return false;
//    }
//
//    var e = element;
//    var p = e.parentElement;
//    while (e != document.body && checkContains(p, otherNodes)) {
//      e = p;
//      p = e.parentElement;
//    }
//    return e;
//  };
//
//  getAnchors = function(target) {
//  // function getAnchors(target) {
//    var label = createLabel(target);
//    // sometimes we try to find anchors of the head element, which means the
//    // body doesnt exist
//    if (document.body)
//      var otherConflictElements = matchLabel(label, document.body);
//    else
//      var otherConflictElements = [];
//
//    return {
//      label: label,
//      numConflicts: otherConflictElements.length
//    }
//  };
//
//  matchLabel = function(label, root) {
////  function findMatchingElements(label, root) {
//    function evaluateXPath(aNode, aExpr) {
//      var xpe = new XPathEvaluator();
//      var nsResolver = xpe.createNSResolver(aNode.ownerDocument == null ?
//        aNode.documentElement : aNode.ownerDocument.documentElement);
//      var result = xpe.evaluate(aExpr, aNode, nsResolver, 0, null);
//      var found = [];
//      var res;
//      while (res = result.iterateNext())
//        found.push(res);
//      return found;
//    }
//
//    var query = nodeToXPath(root.parentElement);
//    var tagName = label.tagName;
//    query += '//' + tagName;
//    if (label.textContent)
//      query += '[text()="' + label.textContent + '"]';
//
//    var matches = evaluateXPath(document, query);
//    if (label.alt)
//      matches = matches.filter(function(e) { return e.alt == label.alt; });
//
//    return matches;
//  };

  /* The following functions are different implementations to take a target
   * info object, and convert it to a list of possible DOM nodes */

  function getTargetSimple(targetInfo) {
    return xPathToNodes(targetInfo.xpath);
  }

  function getTargetSuffix(targetInfo) {

    function helper(xpath) {
      var index = 0;
      while (xpath[index] == '/')
        index++;

      if (index > 0)
        xpath = xpath.slice(index);

      var targets = xPathToNodes('//' + xpath);

      if (targets.length > 0) {
        return targets;
      }

      /* If we're here, we failed to find the child. Try dropping
       * steadily larger prefixes of the xpath until some portion works.
       * Gives up if only three levels left in xpath. */
      if (xpath.split('/').length < 4) {
        /* No more prefixes to reasonably remove, so give up */
        return [];
      }

      var index = xpath.indexOf('/');
      xpathSuffix = xpath.slice(index + 1);
      return helper(xpathSuffix);
    }

    return helper(targetInfo.xpath);
  }

  function getTargetText(targetInfo) {
    var text = targetInfo.snapshot.prop.innerText;
    if (text) {
      return xPathToNodes('//*[text()="' + text + '"]');
    }
    return [];
  }

  function getTargetSearch(targetInfo) {
    /* search over changes to the ancesters (replacing each ancestor with a
     * star plus changes such as adding or removing ancestors) */

    function helper(xpathSplit, index) {
      if (index == 0)
        return [];

      var targets;

      if (index < xpathSplit.length - 1) {
        var clone = xpathSplit.slice(0);
        var xpathPart = clone[index];

        clone[index] = '*';
        targets = xPathToNodes(clone.join('/'));
        if (targets.length > 0)
          return targets;

        clone.splice(index, 0, xpathPart);
        targets = xPathToNodes(clone.join('/'));
        if (targets.length > 0)
          return targets;
      }

      targets = xPathToNodes(xpathSplit.join('/'));
      if (targets.length > 0)
        return targets;

      return helper(xpathSplit, index - 1);
    }

    var split = targetInfo.xpath.split('/');
    return helper(split, split.length - 1);
  }

  function getTargetClass(targetInfo) {
    var className = targetInfo.snapshot.prop.className;
    if (className) {
      //xPathToNodes("//*[@class='" + className + "']");

      var classes = className.trim().replace(':', '\\:').split(' ');
      var selector = '';
      for (var i = 0, ii = classes.length; i < ii; ++i) {
        var className = classes[i];
        if (className)
          selector += '.' + classes[i];
      }

      return $.makeArray($(selector));
    }
    return [];
  }

  function getTargetId(targetInfo) {
    var id = targetInfo.snapshot.prop.id;
    if (id) {
      var selector = '#' + id.trim().replace(':', '\\:');
      return $.makeArray($(selector));
    }
    return [];
  }

  function getTargetComposite(targetInfo) {
    var targets = [];
    var metaInfo = [];

    for (var strategy in targetFunctions) {
      try {
        var strategyTargets = targetFunctions[strategy](targetInfo);
        for (var i = 0, ii = strategyTargets.length; i < ii; ++i) {
          var t = strategyTargets[i];
          var targetIndex = targets.indexOf(t);
          if (targetIndex == -1) {
            targets.push(t);
            metaInfo.push([strategy]);
          } else {
            metaInfo[targetIndex].push(strategy);
          }
        }
      } catch (e) {}
    }

    var maxStrategies = 0;
    var maxTargets = [];
    for (var i = 0, ii = targets.length; i < ii; ++i) {
      var numStrategies = metaInfo[i].length;
      if (numStrategies == maxStrategies) {
        maxTargets.push(targets[i]);
      } else if (numStrategies > maxStrategies) {
        maxTargets = [targets[i]];
        maxStrategies = numStrategies;
      }
    }

    return maxTargets;
  }

  function getAllSimilarityCandidates(targetInfo){
    var tagName = "*";
    if (targetInfo.nodeName){
      tagName = targetInfo.nodeName;
    }
    return document.getElementsByTagName(tagName);
  }

  var getTargetForSimilarity = function(targetInfo) {
    var candidates = getAllSimilarityCandidates(targetInfo);
    var bestScore = -1;
    var bestNode = null;
    for (var i = 0, ii = candidates.length; i < ii; ++i){
      var info = getFeatures(candidates[i]);
      var similarityCount = 0;
      for (var prop in targetInfo) {
        if (targetInfo.hasOwnProperty(prop)) {
          if (targetInfo[prop] === info[prop]){
            similarityCount += 1;
          }
        }
      }
      if (similarityCount > bestScore){
        bestScore = similarityCount;
        bestNode = candidates[i];
      }
    }
    return bestNode;
  };

  /* Set the target function */
  getTargetFunction = getTargetComposite;

  /* Given the target info, produce a single target DOM node. May get several
   * possible candidates, and would just return the first candidate. */
  getTarget = function(targetInfo) {
    var targets = getTargetFunction(targetInfo);
    if (!targets) {
      log.warn('No target found');
      return null;
    } else if (targets.length > 1) {
      log.warn('Multiple targets found:', targets);
      return null;
    } else {
      return targets[0];
    }
  };

  /* List of all target functions. Used for benchmarking */
  targetFunctions = {
    simple: getTargetSimple,
    suffix: getTargetSuffix,
    text: getTargetText,
    class: getTargetClass,
    id: getTargetId,
    search: getTargetSearch
  };

})();
<|endoftext|>


<commit_before>var map;
var ajaxRequest;
var plotlist;
var plotlayers=[];

function getParameterByName(name) {
    name = name.replace(/[\[]/, "\\[").replace(/[\]]/, "\\]");
    var regex = new RegExp("[\\?&]" + name + "=([^&#]*)"),
        results = regex.exec(location.search);
    return results === null ? "" : decodeURIComponent(results[1].replace(/\+/g, " "));
}

function httpGet(theUrl)
{
    if (window.XMLHttpRequest)
    {// code for IE7+, Firefox, Chrome, Opera, Safari
        xmlhttp=new XMLHttpRequest();
    }
    else
    {// code for IE6, IE5
        xmlhttp=new ActiveXObject("Microsoft.XMLHTTP");
    }
    xmlhttp.onreadystatechange=function()
    {
        if (xmlhttp.readyState==4 && xmlhttp.status==200)
        {
            return xmlhttp.responseText;
        }
    }
    xmlhttp.open("GET", theUrl, false );
    xmlhttp.send();
}

function initmap() {
    var mapOptions = {zoomControl: false};
	var localLayerName = getParameterByName("layer");
	if (!localLayerName){
	    localLayerName = "201112";
        mapOptions = {zoomControl: true};  // show zoom control if query string not given (likely to be a user not autobot)
	};
    var localTileLayerUrl = 'http://10.143.166.17:8000/tiles/' + localLayerName + '/{z}/{x}/{y}.png';
	var localTileLayer = new L.TileLayer(localTileLayerUrl, {minZoom: 8, maxZoom: 12, attribution: "safecast.org", tms: true});

	// set up the map with mapOptions (toggles zoom control)
	map = new L.Map('map', mapOptions);

	// create the tile layer with correct attribution
	//var osmUrl='http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png';
	//var osmAttrib='Map data © OpenStreetMap contributors';
	//var osm = new L.TileLayer(osmUrl, {minZoom: 8, maxZoom: 12, attribution: osmAttrib});

    L.control.scale({metric: true, imperial: false}).addTo(map);

    // add legend
    //var legend = L.control({position: 'bottomright'});
    //legend.onAdd = function (map) {
    //    var legendDivInnerHTML = httpGet("http://10.143.166.17:8000/legend/");
    //    var div = L.DomUtil.create("div", "info legend");
    //    div.innerHTML += legendDivInnerHTML;
    //    return div;
    //};
    //legend.addTo(map);

    // add layername label
    var info = L.control();

    info.onAdd = function (map) {
        this._div = L.DomUtil.create('div', 'info'); // create a div with a class "info"
        this.update();
        return this._div;
    };
    // method that we will use to update the control based on feature properties passed (update function ote used atm)
    info.update = function (props) {
        // parse localLayerName to int year and month to create display timeline overlay
        var selectedLayerYear = parseInt(localLayerName.substring(0, 4));
        // workaround for phantomjs parseInt issue
        var selectedLayerMonth = parseInt(localLayerName.substring(4));
        if (localLayerName.substring(4, 5) == "0"){
            selectedLayerMonth = parseInt(localLayerName.substring(5));
        };
        this._div.innerHTML = getTimeline(selectedLayerYear, selectedLayerMonth);
    };
    info.addTo(map);

	// start the map in Fukushima
	map.setView(new L.LatLng(37.435793, 140.735437), 9);
	//map.addLayer(osm);
	map.addLayer(localTileLayer);
}<commit_msg>attempting to add static legend to map.<commit_after>var map;
var ajaxRequest;
var plotlist;
var plotlayers=[];

function getParameterByName(name) {
    name = name.replace(/[\[]/, "\\[").replace(/[\]]/, "\\]");
    var regex = new RegExp("[\\?&]" + name + "=([^&#]*)"),
        results = regex.exec(location.search);
    return results === null ? "" : decodeURIComponent(results[1].replace(/\+/g, " "));
}

function httpGet(theUrl)
{
    if (window.XMLHttpRequest)
    {// code for IE7+, Firefox, Chrome, Opera, Safari
        xmlhttp=new XMLHttpRequest();
    }
    else
    {// code for IE6, IE5
        xmlhttp=new ActiveXObject("Microsoft.XMLHTTP");
    }
    xmlhttp.onreadystatechange=function()
    {
        if (xmlhttp.readyState==4 && xmlhttp.status==200)
        {
            return xmlhttp.responseText;
        }
    }
    xmlhttp.open("GET", theUrl, false );
    xmlhttp.send();
}

function initmap() {
    var mapOptions = {zoomControl: false};
	var localLayerName = getParameterByName("layer");
	if (!localLayerName){
	    localLayerName = "201112";
        mapOptions = {zoomControl: true};  // show zoom control if query string not given (likely to be a user not autobot)
	};
    var localTileLayerUrl = 'http://10.143.166.17:8000/tiles/' + localLayerName + '/{z}/{x}/{y}.png';
	var localTileLayer = new L.TileLayer(localTileLayerUrl, {minZoom: 8, maxZoom: 12, attribution: "safecast.org", tms: true});

	// set up the map with mapOptions (toggles zoom control)
	map = new L.Map('map', mapOptions);

	// create the tile layer with correct attribution
	//var osmUrl='http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png';
	//var osmAttrib='Map data © OpenStreetMap contributors';
	//var osm = new L.TileLayer(osmUrl, {minZoom: 8, maxZoom: 12, attribution: osmAttrib});

    L.control.scale({metric: true, imperial: false}).addTo(map);

    // add legend
    var legend = L.control({position: 'bottomright'});
    legend.onAdd = function (map) {
        var div = L.DomUtil.create("div", "info legend");
        div.innerHTML += '3.99 +
';
        div.innerHTML += '3.2 ~ 3.99
';
        div.innerHTML += '2.41 ~ 3.2
';
        div.innerHTML += '1.61 ~ 2.41
';
        div.innerHTML += '0.82 ~ 1.61
';
        div.innerHTML += '0.03 ~ 0.82
';
        return div;
    };
    legend.addTo(map);

    // add layername label
    var info = L.control();

    info.onAdd = function (map) {
        this._div = L.DomUtil.create('div', 'info'); // create a div with a class "info"
        this.update();
        return this._div;
    };
    // method that we will use to update the control based on feature properties passed (update function ote used atm)
    info.update = function (props) {
        // parse localLayerName to int year and month to create display timeline overlay
        var selectedLayerYear = parseInt(localLayerName.substring(0, 4));
        // workaround for phantomjs parseInt issue
        var selectedLayerMonth = parseInt(localLayerName.substring(4));
        if (localLayerName.substring(4, 5) == "0"){
            selectedLayerMonth = parseInt(localLayerName.substring(5));
        };
        this._div.innerHTML = getTimeline(selectedLayerYear, selectedLayerMonth);
    };
    info.addTo(map);

	// start the map in Fukushima
	map.setView(new L.LatLng(37.435793, 140.735437), 9);
	//map.addLayer(osm);
	map.addLayer(localTileLayer);
}<|endoftext|>


<commit_before>// Copyright 2020 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.

const MAPSKEY = config.MAPS_KEY
let neighborhood = [null , null];
let currentCategory = "all";

window.onscroll = stickyControlBar;

/* Scroll function so that the control bar sticks to the top of the page */
function stickyControlBar() {
    let controlBarWrapper = document.getElementById("control-bar-message-wrapper");
    let taskListDiv = document.getElementById("tasks-list");

    if (window.innerWidth > 1203) {
        const OFFSET = 190;
        if (window.pageYOffset >= OFFSET || document.body.scrollTop >= OFFSET || document.documentElement.scrollTop >= OFFSET) {
            controlBarWrapper.style.position = "fixed";
            taskListDiv.style.marginTop = "165px";
        } else {
            controlBarWrapper.style.position = "relative";
            taskListDiv.style.marginTop = "auto";
        }
    }
}

/* Calls addUIClickHandlers and getUserNeighborhood once page has loaded */
if (document.readyState === 'loading') {
    // adds on load event listeners if document hasn't yet loaded
    document.addEventListener('DOMContentLoaded', addUIClickHandlers)
    document.addEventListener('DOMContentLoaded', getUserNeighborhood);
} else {
    // if DOMContentLoaded has already fired, it simply calls the functions
    addUIClickHandlers();
    getUserNeighborhood();
}

/* Function adds all the necessary UI 'click' event listeners*/
function addUIClickHandlers() {
    // adds showCreateTaskModal and closeCreateTaskModal click events for the add task button
    if (document.body.contains(document.getElementById("addtaskbutton"))) {
        document.getElementById("addtaskbutton").addEventListener("click", showCreateTaskModal);
    	document.getElementById("close-addtask-button").addEventListener("click", closeCreateTaskModal);
    }

    // adds filterTasksBy click event listener to category buttons
    const categoryButtons = document.getElementsByClassName("categories");
    for (let i = 0; i < categoryButtons.length; i++) {
        categoryButtons[i].addEventListener("click", function(e) {
            filterTasksBy(e.target.id);
        });
    }
    // adds showTopScoresModal click event
    document.getElementById("topscore-button").addEventListener("click", showTopScoresModal);
    document.getElementById("close-topscore-button").addEventListener("click", closeTopScoresModal);
}

/* Function filters tasks by categories and styles selected categories */
function filterTasksBy(category) {
    currentCategory = category;

    // only fetches tasks if user's neighborhood has been retrieved
    if (userNeighborhoodIsKnown()) {
        fetchTasks(category)
            .then(response => displayTasks(response));
    }
	// Unhighlights and resets styling for all category buttons
    const categoryButtons = document.getElementsByClassName("categories");
    for (let i = 0; i < categoryButtons.length; i++){
        let button = categoryButtons[i];
        if (document.getElementById(category) != button) {
            button.style.backgroundColor = "rgb(76, 175, 80)";
        	button.addEventListener("mouseover", function() {
                button.style.backgroundColor = "rgb(62, 142, 65)";
            });
            button.addEventListener("mouseout", function() {
                button.style.backgroundColor = "rgb(76, 175, 80)"
            });
        } else {
            button.style.backgroundColor = "rgb(62, 142, 65)";
            button.addEventListener("mouseover", function() {
                button.style.backgroundColor = "rgb(62, 142, 65)";
            });
            button.addEventListener("mouseout", function() {
                button.style.backgroundColor = "rgb(62, 142, 65)"
            });
        }
    }
}

/* Function that display the help out overlay */
function helpOut(element) {
    const task = element.closest(".task");
    const overlay = task.getElementsByClassName("help-overlay");
    overlay[0].style.display = "block";
}

/* Function sends a fetch request to the edit task servlet when the user
offers to help out, edits the task's status and helper properties, and
then reloads the task list */
function confirmHelp(element) {
    const task = element.closest(".task");
    const url = "/tasks/edit?task-id=" + task.dataset.key + "&action=helpout";
    const request = new Request(url, {method: "POST"});
    fetch(request).then((response) => {
        // checks if another user has already claimed the task
        if (response.status == 409) {
            window.alert
                ("We're sorry, but the task you're trying to help with has already been claimed by another user.");
            window.location.href = '/';
        }
        // fetches tasks again if user's current neighborhood was successfully retrieved and stored
        else if (userNeighborhoodIsKnown()) {
            fetchTasks(currentCategory).then(response => displayTasks(response));
        }
    });
}

/* Function that hides the help out overlay */
function exitHelp(element) {
	element.closest(".help-overlay").style.display = "none";
}

/* Leonard's implementation of the Add Task modal */
function showCreateTaskModal() {
    var modal = document.getElementById("createTaskModalWrapper");
    modal.style.display = "block";
}
function closeCreateTaskModal() {
    var modal = document.getElementById("createTaskModalWrapper");
    modal.style.display = "none";
}

/* Function that calls the loadTopScorersBy functions
   and then shows the top scores modal */
function showTopScoresModal() {
    loadTopScorersBy("world");
    if (userNeighborhoodIsKnown()){
      loadTopScorersBy("neighborhood");
    }
    document.getElementById("topScoresModalWrapper").style.display = "block";
}

/* Function closes the top scores modal */
function closeTopScoresModal() {
    document.getElementById("topScoresModalWrapper").style.display = "none";
}

/* Function loads the data for the top scorers table */
function loadTopScorersBy(location) {
    let url = "/account?action=topscorers";
    if (location === "neighborhood") {
      url += "&zipcode=" + neighborhood[0] + "&country=" + neighborhood[1];
    }
    fetch(url)
      .then(response => response.json())
      .then(users => {
        // Inserts Nickname and Points for every top scorer
        for (let i = 0; i < users.length; i++) {
          let points = users[i].points;
          let nickname = users[i].nickname;
          let rowId = location + (i + 1);
          let row = document.getElementById(rowId);
          let rowNickname = row.getElementsByClassName("topscore-nickname")[0];
          let rowScore = row.getElementsByClassName("topscore-score")[0];
          rowNickname.innerText = nickname;
          rowScore.innerText = points;
          // Adds different styling if row includes current user
          if (users[i].isCurrentUser) {
            row.style.fontWeight = "bold";
            row.setAttribute("title", "Congratulations, you made it to the Top Scorers Board!");
          }
        }
    });
}
// If the user clicks outside of the modals, closes the modals directly
window.onclick = function(event) {
    var createTaskModal = document.getElementById("createTaskModalWrapper");
    if (event.target == createTaskModal) {
        createTaskModal.style.display = "none";
    }
    var topScoresModal = document.getElementById("topScoresModalWrapper");
    if (event.target == topScoresModal) {
        topScoresModal.style.display = "none";
    }
}

/* Function dynamically adds Maps API and
begins the processes of retrieving the user's neighborhood*/
function getUserNeighborhood() {
    const script = document.createElement("script");
    script.type = "text/javascript";
    script.src =  "https://maps.googleapis.com/maps/api/js?key=" + MAPSKEY + "&callback=initialize&language=en";
    script.defer = true;
    script.async = true;
    document.head.appendChild(script);

    // Once the Maps API script has dynamically loaded it gets the user location,
    // waits until it gets an answer and then calls toNeighborhood passing the location
    // as an argument, updates the global neighborhood variable and then calls
    // fetchTasks and displayTasks
	window.initialize = function () {
        getUserLocation().then(location => toNeighborhood(location))
        	.then(() => fetchTasks())
            .then((response) => displayTasks(response))
            .catch(() => {
                console.error("User location and/or neighborhood could not be retrieved");
                document.getElementById("location-missing-message").style.display = "block";
            });
	}
}

/* Function that returns a promise to get and return the user's location */
function getUserLocation() {
    return new Promise((resolve, reject) => {
        if (navigator.geolocation) {
            navigator.geolocation.getCurrentPosition(function(position) {
                var location = {lat: position.coords.latitude, lng: position.coords.longitude};
                resolve(location);
            }, function(err) {
                // Check to see if this failed because we're in an insecure
                // context, such as a local dev environment that isn't
                // http://localhost (https://developer.mozilla.org/en-US/docs/Web/Security/Secure_Contexts).
                if (err.code === 1 && !window.isSecureContext) {
                    if (config.LOCAL_DEV_LAT_LNG) {
                        resolve(config.LOCAL_DEV_LAT_LNG);
                    } else {
                        reject("User location failed");
                    }
                } else {
                    reject("User location failed");
                }
            });
        } else {
            reject("User location is not supported by this browser");
        }
    });
}

/* Function that returns a promise to return a neighborhood
array that includes the postal code and country */
function toNeighborhood(latlng) {
	return new Promise((resolve, reject) => {
        const geocoder = new google.maps.Geocoder;
        geocoder.geocode({"location": latlng}, function(results, status) {
            if (status == "OK") {
                if (results[0]) {
                    const result = results[0]
                    let zipCode = "";
                    let country ="";
                    for (let i = result.address_components.length - 1; i >= 0; i--) {
                        let component = result.address_components[i];
                        if ((zipCode == "") && (component.types.indexOf("postal_code") >= 0 )) {
                            zipCode = component.long_name;
                        }
                        if ((country == "") && (component.types.indexOf("country") >= 0 )) {
                            country = component.long_name;
                        }
                        if (zipCode != "" && country != "") break;
                    }
                    neighborhood = [zipCode, country];
                    resolve([zipCode, country]);
                } else reject("Couldn't get neighborhood");
            } else reject("Couldn't get neighborhood");
        });
    });
}

/* Fetches tasks from servlet by neighborhood and category */
function fetchTasks(category) {
    let url = "/tasks?zipcode=" + neighborhood[0]+ "&country=" + neighborhood[1];
    if (category !== undefined && category != "all") {
        url += "&category=" + category;
    }
    return fetch(url);
}

/* Displays the tasks received from the server response */
function displayTasks(response) {
    response.json().then(html => {
        if (html){
            document.getElementById("no-tasks-message").style.display = "none";
            document.getElementById("tasks-message").style.display = "block";
            document.getElementById("tasks-list").innerHTML = html;
            document.getElementById("tasks-list").style.display = "block";
            addTasksClickHandlers();
        } else {
            document.getElementById("no-tasks-message").style.display = "block";
            document.getElementById("tasks-message").style.display = "none";
            document.getElementById("tasks-list").style.display = "none";
        }
    });
}

/* Function adds all the necessary tasks 'click' event listeners*/
function addTasksClickHandlers() {

    // adds confirmHelp click event listener to confirm help buttons
    const confirmHelpButtons = document.getElementsByClassName("confirm-help");
    for (let i = 0; i < confirmHelpButtons.length; i++){
        confirmHelpButtons[i].addEventListener("click", function(e) {
            confirmHelp(e.target);
        });

    }
    // adds exitHelp click event listener to exit help buttons
    const exitHelpButtons = document.getElementsByClassName("exit-help");
    for (let i = 0; i < exitHelpButtons.length; i++) {
        exitHelpButtons[i].addEventListener("click", function(e) {
            exitHelp(e.target);
        });
    }
    // adds helpOut click event listener to help out buttons
    const helpOutButtons = document.getElementsByClassName("help-out");
        for (let i = 0; i < helpOutButtons.length; i++) {
            if (!helpOutButtons[i].classList.contains("disable-help")) {
                helpOutButtons[i].addEventListener("click", function(e) {
                    helpOut(e.target);
                });
            }
        }
}

/* Helper function that determines if the current user's neighborhood is known */
function userNeighborhoodIsKnown() {
  return (neighborhood[0] !== null && neighborhood[1] !== null);
}
<commit_msg>add comments to explain numbers used in scrolling function<commit_after>// Copyright 2020 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.

const MAPSKEY = config.MAPS_KEY
let neighborhood = [null , null];
let currentCategory = "all";

window.onscroll = stickyControlBar;

/* Scroll function so that the control bar sticks to the top of the page */
function stickyControlBar() {
    let controlBarWrapper = document.getElementById("control-bar-message-wrapper");
    let taskListDiv = document.getElementById("tasks-list");

    // Scrolling behavior in screens smaller than 1204 will result in overlapping DOM elements
    // therefore this scrolling function only applies to screens as big or bigger than that
    if (window.innerWidth >= 1204) {
        const OFFSET = 190; //Distance from top of page to top of control (categories) bar
        if (window.pageYOffset >= OFFSET || document.body.scrollTop >= OFFSET || document.documentElement.scrollTop >= OFFSET) {
            controlBarWrapper.style.position = "fixed";
            // adjust task list container so it appears like it's in the same position
            // after controlBarWrapper's position is changed to 'fixed'
            taskListDiv.style.marginTop = "165px";
        } else {
            controlBarWrapper.style.position = "relative";
            taskListDiv.style.marginTop = "auto";
        }
    }
}

/* Calls addUIClickHandlers and getUserNeighborhood once page has loaded */
if (document.readyState === 'loading') {
    // adds on load event listeners if document hasn't yet loaded
    document.addEventListener('DOMContentLoaded', addUIClickHandlers)
    document.addEventListener('DOMContentLoaded', getUserNeighborhood);
} else {
    // if DOMContentLoaded has already fired, it simply calls the functions
    addUIClickHandlers();
    getUserNeighborhood();
}

/* Function adds all the necessary UI 'click' event listeners*/
function addUIClickHandlers() {
    // adds showCreateTaskModal and closeCreateTaskModal click events for the add task button
    if (document.body.contains(document.getElementById("addtaskbutton"))) {
        document.getElementById("addtaskbutton").addEventListener("click", showCreateTaskModal);
    	document.getElementById("close-addtask-button").addEventListener("click", closeCreateTaskModal);
    }

    // adds filterTasksBy click event listener to category buttons
    const categoryButtons = document.getElementsByClassName("categories");
    for (let i = 0; i < categoryButtons.length; i++) {
        categoryButtons[i].addEventListener("click", function(e) {
            filterTasksBy(e.target.id);
        });
    }
    // adds showTopScoresModal click event
    document.getElementById("topscore-button").addEventListener("click", showTopScoresModal);
    document.getElementById("close-topscore-button").addEventListener("click", closeTopScoresModal);
}

/* Function filters tasks by categories and styles selected categories */
function filterTasksBy(category) {
    currentCategory = category;

    // only fetches tasks if user's neighborhood has been retrieved
    if (userNeighborhoodIsKnown()) {
        fetchTasks(category)
            .then(response => displayTasks(response));
    }
	// Unhighlights and resets styling for all category buttons
    const categoryButtons = document.getElementsByClassName("categories");
    for (let i = 0; i < categoryButtons.length; i++){
        let button = categoryButtons[i];
        if (document.getElementById(category) != button) {
            button.style.backgroundColor = "rgb(76, 175, 80)";
        	button.addEventListener("mouseover", function() {
                button.style.backgroundColor = "rgb(62, 142, 65)";
            });
            button.addEventListener("mouseout", function() {
                button.style.backgroundColor = "rgb(76, 175, 80)"
            });
        } else {
            button.style.backgroundColor = "rgb(62, 142, 65)";
            button.addEventListener("mouseover", function() {
                button.style.backgroundColor = "rgb(62, 142, 65)";
            });
            button.addEventListener("mouseout", function() {
                button.style.backgroundColor = "rgb(62, 142, 65)"
            });
        }
    }
}

/* Function that display the help out overlay */
function helpOut(element) {
    const task = element.closest(".task");
    const overlay = task.getElementsByClassName("help-overlay");
    overlay[0].style.display = "block";
}

/* Function sends a fetch request to the edit task servlet when the user
offers to help out, edits the task's status and helper properties, and
then reloads the task list */
function confirmHelp(element) {
    const task = element.closest(".task");
    const url = "/tasks/edit?task-id=" + task.dataset.key + "&action=helpout";
    const request = new Request(url, {method: "POST"});
    fetch(request).then((response) => {
        // checks if another user has already claimed the task
        if (response.status == 409) {
            window.alert
                ("We're sorry, but the task you're trying to help with has already been claimed by another user.");
            window.location.href = '/';
        }
        // fetches tasks again if user's current neighborhood was successfully retrieved and stored
        else if (userNeighborhoodIsKnown()) {
            fetchTasks(currentCategory).then(response => displayTasks(response));
        }
    });
}

/* Function that hides the help out overlay */
function exitHelp(element) {
	element.closest(".help-overlay").style.display = "none";
}

/* Leonard's implementation of the Add Task modal */
function showCreateTaskModal() {
    var modal = document.getElementById("createTaskModalWrapper");
    modal.style.display = "block";
}
function closeCreateTaskModal() {
    var modal = document.getElementById("createTaskModalWrapper");
    modal.style.display = "none";
}

/* Function that calls the loadTopScorersBy functions
   and then shows the top scores modal */
function showTopScoresModal() {
    loadTopScorersBy("world");
    if (userNeighborhoodIsKnown()){
      loadTopScorersBy("neighborhood");
    }
    document.getElementById("topScoresModalWrapper").style.display = "block";
}

/* Function closes the top scores modal */
function closeTopScoresModal() {
    document.getElementById("topScoresModalWrapper").style.display = "none";
}

/* Function loads the data for the top scorers table */
function loadTopScorersBy(location) {
    let url = "/account?action=topscorers";
    if (location === "neighborhood") {
      url += "&zipcode=" + neighborhood[0] + "&country=" + neighborhood[1];
    }
    fetch(url)
      .then(response => response.json())
      .then(users => {
        // Inserts Nickname and Points for every top scorer
        for (let i = 0; i < users.length; i++) {
          let points = users[i].points;
          let nickname = users[i].nickname;
          let rowId = location + (i + 1);
          let row = document.getElementById(rowId);
          let rowNickname = row.getElementsByClassName("topscore-nickname")[0];
          let rowScore = row.getElementsByClassName("topscore-score")[0];
          rowNickname.innerText = nickname;
          rowScore.innerText = points;
          // Adds different styling if row includes current user
          if (users[i].isCurrentUser) {
            row.style.fontWeight = "bold";
            row.setAttribute("title", "Congratulations, you made it to the Top Scorers Board!");
          }
        }
    });
}
// If the user clicks outside of the modals, closes the modals directly
window.onclick = function(event) {
    var createTaskModal = document.getElementById("createTaskModalWrapper");
    if (event.target == createTaskModal) {
        createTaskModal.style.display = "none";
    }
    var topScoresModal = document.getElementById("topScoresModalWrapper");
    if (event.target == topScoresModal) {
        topScoresModal.style.display = "none";
    }
}

/* Function dynamically adds Maps API and
begins the processes of retrieving the user's neighborhood*/
function getUserNeighborhood() {
    const script = document.createElement("script");
    script.type = "text/javascript";
    script.src =  "https://maps.googleapis.com/maps/api/js?key=" + MAPSKEY + "&callback=initialize&language=en";
    script.defer = true;
    script.async = true;
    document.head.appendChild(script);

    // Once the Maps API script has dynamically loaded it gets the user location,
    // waits until it gets an answer and then calls toNeighborhood passing the location
    // as an argument, updates the global neighborhood variable and then calls
    // fetchTasks and displayTasks
	window.initialize = function () {
        getUserLocation().then(location => toNeighborhood(location))
        	.then(() => fetchTasks())
            .then((response) => displayTasks(response))
            .catch(() => {
                console.error("User location and/or neighborhood could not be retrieved");
                document.getElementById("location-missing-message").style.display = "block";
            });
	}
}

/* Function that returns a promise to get and return the user's location */
function getUserLocation() {
    return new Promise((resolve, reject) => {
        if (navigator.geolocation) {
            navigator.geolocation.getCurrentPosition(function(position) {
                var location = {lat: position.coords.latitude, lng: position.coords.longitude};
                resolve(location);
            }, function(err) {
                // Check to see if this failed because we're in an insecure
                // context, such as a local dev environment that isn't
                // http://localhost (https://developer.mozilla.org/en-US/docs/Web/Security/Secure_Contexts).
                if (err.code === 1 && !window.isSecureContext) {
                    if (config.LOCAL_DEV_LAT_LNG) {
                        resolve(config.LOCAL_DEV_LAT_LNG);
                    } else {
                        reject("User location failed");
                    }
                } else {
                    reject("User location failed");
                }
            });
        } else {
            reject("User location is not supported by this browser");
        }
    });
}

/* Function that returns a promise to return a neighborhood
array that includes the postal code and country */
function toNeighborhood(latlng) {
	return new Promise((resolve, reject) => {
        const geocoder = new google.maps.Geocoder;
        geocoder.geocode({"location": latlng}, function(results, status) {
            if (status == "OK") {
                if (results[0]) {
                    const result = results[0]
                    let zipCode = "";
                    let country ="";
                    for (let i = result.address_components.length - 1; i >= 0; i--) {
                        let component = result.address_components[i];
                        if ((zipCode == "") && (component.types.indexOf("postal_code") >= 0 )) {
                            zipCode = component.long_name;
                        }
                        if ((country == "") && (component.types.indexOf("country") >= 0 )) {
                            country = component.long_name;
                        }
                        if (zipCode != "" && country != "") break;
                    }
                    neighborhood = [zipCode, country];
                    resolve([zipCode, country]);
                } else reject("Couldn't get neighborhood");
            } else reject("Couldn't get neighborhood");
        });
    });
}

/* Fetches tasks from servlet by neighborhood and category */
function fetchTasks(category) {
    let url = "/tasks?zipcode=" + neighborhood[0]+ "&country=" + neighborhood[1];
    if (category !== undefined && category != "all") {
        url += "&category=" + category;
    }
    return fetch(url);
}

/* Displays the tasks received from the server response */
function displayTasks(response) {
    response.json().then(html => {
        if (html){
            document.getElementById("no-tasks-message").style.display = "none";
            document.getElementById("tasks-message").style.display = "block";
            document.getElementById("tasks-list").innerHTML = html;
            document.getElementById("tasks-list").style.display = "block";
            addTasksClickHandlers();
        } else {
            document.getElementById("no-tasks-message").style.display = "block";
            document.getElementById("tasks-message").style.display = "none";
            document.getElementById("tasks-list").style.display = "none";
        }
    });
}

/* Function adds all the necessary tasks 'click' event listeners*/
function addTasksClickHandlers() {

    // adds confirmHelp click event listener to confirm help buttons
    const confirmHelpButtons = document.getElementsByClassName("confirm-help");
    for (let i = 0; i < confirmHelpButtons.length; i++){
        confirmHelpButtons[i].addEventListener("click", function(e) {
            confirmHelp(e.target);
        });

    }
    // adds exitHelp click event listener to exit help buttons
    const exitHelpButtons = document.getElementsByClassName("exit-help");
    for (let i = 0; i < exitHelpButtons.length; i++) {
        exitHelpButtons[i].addEventListener("click", function(e) {
            exitHelp(e.target);
        });
    }
    // adds helpOut click event listener to help out buttons
    const helpOutButtons = document.getElementsByClassName("help-out");
        for (let i = 0; i < helpOutButtons.length; i++) {
            if (!helpOutButtons[i].classList.contains("disable-help")) {
                helpOutButtons[i].addEventListener("click", function(e) {
                    helpOut(e.target);
                });
            }
        }
}

/* Helper function that determines if the current user's neighborhood is known */
function userNeighborhoodIsKnown() {
  return (neighborhood[0] !== null && neighborhood[1] !== null);
}
<|endoftext|>


<commit_before>ngActionCable.factory("ActionCableChannel",function ($q, ActionCableController, ActionCableWebsocket){
  return function(channelName, channelParams){
    this._websocketControllerActions= function(){
      var _channelParamsString= JSON.stringify(this.channelParams);
      ActionCableController.actions[this.channelName]= ActionCableController.actions[this.channelName] || {};
      ActionCableController.actions[this.channelName][_channelParamsString]= ActionCableController.actions[this.channelName][_channelParamsString] || [];
      return ActionCableController.actions[this.channelName][_channelParamsString];
    }

    this._subscriptionCount= function(){
      return this.callbacks.length;
    }

    this.channelName= channelName;
    this.channelParams= channelParams || {};
    this.onMessageCallback= null;
    this.callbacks= this._websocketControllerActions();

    this.subscribe= function(cb){
      var request;
      if (!(typeof(cb)==="function")) {
        console.error("0x01 Callback function was not defined on subscribe(). ActionCable channel: '"+this.channelName+"', params: '"+this.channelParams+"'");
        return $q.reject();
      };
      if (this.onMessageCallback) {
        console.error("0x02 This ActionCableChannel instance is already subscribed. ActionCable channel: '"+this.channelName+"', params: '"+this.channelParams+"'");
        return $q.reject();
      };
      if (this._subscriptionCount() === 0) { request= ActionCableWebsocket.subscribe(this.channelName, this.channelParams); };
      this._addMessageCallback(cb);
      return (request || $q.resolve());
    }
    this.unsubscribe= function(){
      var request;
      this._removeMessageCallback();
      if (this._subscriptionCount() === 0) { request= ActionCableWebsocket.unsubscribe(this.channelName, this.channelParams); };
      return (request || $q.resolve());
     }
    this.send= function(message, action){
      if (!this.onMessageCallback) {
        console.error("0x03 You need to subscribe before you can send a message. ActionCable channel: '"+this.channelName+"', params: '"+this.channelParams+"'");
        return $q.reject();
      };
      return ActionCableWebsocket.send(this.channelName, this.channelParams, message, action);
    }

    this._addMessageCallback= function(cb){
      this.onMessageCallback= cb;
      this.callbacks.push(this.onMessageCallback);
    }

    this._removeMessageCallback= function(){
      for(var i=0; i<this.callbacks.length; i++){
        if (this.callbacks[i]===this.onMessageCallback) {
          this.callbacks.splice(i, 1);
          this.onMessageCallback= null;
          return true;
        }
      }
      console.log("Callbacks:"); console.log(this.callbacks);
      console.log("onMessageCallback:"); console.log(this.onMessageCallback);
      throw "can't find onMessageCallback in callbacks array to remove"
    }
  }
});
<commit_msg>reuses _channelParamsString for logging<commit_after>ngActionCable.factory("ActionCableChannel",function ($q, ActionCableController, ActionCableWebsocket){
  return function(channelName, channelParams){
    this._websocketControllerActions= function(){
      ActionCableController.actions[this.channelName]= ActionCableController.actions[this.channelName] || {};
      ActionCableController.actions[this.channelName][this._channelParamsString]= ActionCableController.actions[this.channelName][this._channelParamsString] || [];
      return ActionCableController.actions[this.channelName][this._channelParamsString];
    }

    this._subscriptionCount= function(){
      return this.callbacks.length;
    }

    this.channelName= channelName;
    this.channelParams= channelParams || {};
    this._channelParamsString= JSON.stringify(this.channelParams);
    this.onMessageCallback= null;
    this.callbacks= this._websocketControllerActions();

    this.subscribe= function(cb){
      var request;
      if (!(typeof(cb)==="function")) {
        console.error("0x01 Callback function was not defined on subscribe(). ActionCable channel: '"+this.channelName+"', params: '"+this._channelParamsString+"'");
        return $q.reject();
      };
      if (this.onMessageCallback) {
        console.error("0x02 This ActionCableChannel instance is already subscribed. ActionCable channel: '"+this.channelName+"', params: '"+this._channelParamsString+"'");
        return $q.reject();
      };
      if (this._subscriptionCount() === 0) { request= ActionCableWebsocket.subscribe(this.channelName, this.channelParams); };
      this._addMessageCallback(cb);
      return (request || $q.resolve());
    }
    this.unsubscribe= function(){
      var request;
      this._removeMessageCallback();
      if (this._subscriptionCount() === 0) { request= ActionCableWebsocket.unsubscribe(this.channelName, this.channelParams); };
      return (request || $q.resolve());
     }
    this.send= function(message, action){
      if (!this.onMessageCallback) {
        console.error("0x03 You need to subscribe before you can send a message. ActionCable channel: '"+this.channelName+"', params: '"+this._channelParamsString+"'");
        return $q.reject();
      };
      return ActionCableWebsocket.send(this.channelName, this.channelParams, message, action);
    }

    this._addMessageCallback= function(cb){
      this.onMessageCallback= cb;
      this.callbacks.push(this.onMessageCallback);
    }

    this._removeMessageCallback= function(){
      for(var i=0; i<this.callbacks.length; i++){
        if (this.callbacks[i]===this.onMessageCallback) {
          this.callbacks.splice(i, 1);
          this.onMessageCallback= null;
          return true;
        }
      }
      console.log("Callbacks:"); console.log(this.callbacks);
      console.log("onMessageCallback:"); console.log(this.onMessageCallback);
      throw "can't find onMessageCallback in callbacks array to remove"
    }
  }
});
<|endoftext|>


<commit_before>'use strict'

angular.module('antismash.ui.bacterial.as_start', ['ngFileUpload'])
    .controller('AsStartCtrl', ['$state', '$window', 'Upload',
        function ($state, $window, Upload) {
            var vm = this;

            vm.valid_endings = '.gbk,.gb,.gbff,.emb,.embl,.fa,.fasta,.fna';

            // Defaullt values
            vm.submission = {};
            vm.extra_features = [
                { id: 'knownclusterblast', description: 'KnownClusterBlast', default: true },
                { id: 'clusterblast', description: 'ClusterBlast', default: false },
                { id: 'subclusterblast', description: 'SubClusterBlast', default: true },
                { id: 'smcogs', description: 'smCoG analysis', default: true },
                { id: 'asf', description: 'ActiveSiteFinder', default: true },
                { id: 'fullhmmer', description: 'Whole-genome PFAM analysis', default: false },
                { id: 'cassis', description: 'Cluster-border prediction based on transcription factor binding sites (CASSIS)', default: false},
            ];

            for (var i = 0; i < vm.extra_features.length; i++) {
                var feature = vm.extra_features[i];
                vm.submission[feature.id] = feature.default;
            }

            vm.cf_threshold = 0.6;

            vm.submit = function (form) {
                if (vm.upload_file) {
                    vm.submission.seq = vm.file;
                } else {
                    vm.submission.ncbi = vm.ncbi;
                }

                if (vm.email) {
                    vm.submission.email = vm.email;
                }

                if (vm.clusterfinder) {
                    vm.submission.inclusive = true;
                    vm.submission.cf_threshold = vm.cf_threshold;
                    vm.submission.cf_cdsnr = vm.cf_cdsnr;
                    vm.submission.cf_npfams = vm.cf_npfams;
                }

                Upload.upload({
                    url: '/api/v1.0/submit',
                    data: vm.submission,
                }).then(function (resp) {
                    $state.go('show.job', { id: resp.data.id });
                }, function (resp) {
                    console.log(resp);
                }, function (evt) {
                    var progressPercentage = parseInt(100.0 * evt.loaded / evt.total);
                    if (!evt.config.data.seq) {
                        return;
                    }
                    console.log('progress: ' + progressPercentage + '% ' + evt.config.data.seq.name);
                });
            }

            vm.validJob = function () {
                if (vm.upload_file) {
                    if (!vm.file) {
                        return false;
                    }
                } else {
                    if (!vm.ncbi) {
                        return false;
                    }
                }
                return true;
            }

            vm.allOff = function () {
                for (var i = 0; i < vm.extra_features.length; i++) {
                    var feature = vm.extra_features[i];
                    vm.submission[feature.id] = false;
                }
            }

            vm.allOn = function () {
                for (var i = 0; i < vm.extra_features.length; i++) {
                    var feature = vm.extra_features[i];
                    vm.submission[feature.id] = true;
                }
            }

            vm.loadSampleInput = function () {
                vm.upload_file = false;
                vm.ncbi = "Y16952";
            }

            vm.openSampleOutput = function () {
                $window.location.href = "/upload/example/index.html";
            }

            vm.loadJob = function () {
                console.log(vm.job_id);
                if (vm.job_id.substr(0, 8).toLowerCase() == 'bacteria') {
                    $state.go('show.job', { id: vm.job_id });
                }
                else if (vm.job_id.substr(0, 5).toLowerCase() == 'fungi') {
                    $window.location.href = "http://fungismash.secondarymetabolites.org/#!/show/job/" + vm.job_id;
                }
                else if (vm.job_id.substr(0, 6).toLowerCase() == 'plants') {
                    $window.location.href = "http://plantismash.secondarymetabolites.org/#!/show/job/" + vm.job_id;
                }
            }
        }]);
<commit_msg>start: Use A. fumigatus chr 1 as example input<commit_after>'use strict'

angular.module('antismash.ui.bacterial.as_start', ['ngFileUpload'])
    .controller('AsStartCtrl', ['$state', '$window', 'Upload',
        function ($state, $window, Upload) {
            var vm = this;

            vm.valid_endings = '.gbk,.gb,.gbff,.emb,.embl,.fa,.fasta,.fna';

            // Defaullt values
            vm.submission = {};
            vm.extra_features = [
                { id: 'knownclusterblast', description: 'KnownClusterBlast', default: true },
                { id: 'clusterblast', description: 'ClusterBlast', default: false },
                { id: 'subclusterblast', description: 'SubClusterBlast', default: true },
                { id: 'smcogs', description: 'smCoG analysis', default: true },
                { id: 'asf', description: 'ActiveSiteFinder', default: true },
                { id: 'fullhmmer', description: 'Whole-genome PFAM analysis', default: false },
                { id: 'cassis', description: 'Cluster-border prediction based on transcription factor binding sites (CASSIS)', default: false},
            ];

            for (var i = 0; i < vm.extra_features.length; i++) {
                var feature = vm.extra_features[i];
                vm.submission[feature.id] = feature.default;
            }

            vm.cf_threshold = 0.6;

            vm.submit = function (form) {
                if (vm.upload_file) {
                    vm.submission.seq = vm.file;
                } else {
                    vm.submission.ncbi = vm.ncbi;
                }

                if (vm.email) {
                    vm.submission.email = vm.email;
                }

                if (vm.clusterfinder) {
                    vm.submission.inclusive = true;
                    vm.submission.cf_threshold = vm.cf_threshold;
                    vm.submission.cf_cdsnr = vm.cf_cdsnr;
                    vm.submission.cf_npfams = vm.cf_npfams;
                }

                Upload.upload({
                    url: '/api/v1.0/submit',
                    data: vm.submission,
                }).then(function (resp) {
                    $state.go('show.job', { id: resp.data.id });
                }, function (resp) {
                    console.log(resp);
                }, function (evt) {
                    var progressPercentage = parseInt(100.0 * evt.loaded / evt.total);
                    if (!evt.config.data.seq) {
                        return;
                    }
                    console.log('progress: ' + progressPercentage + '% ' + evt.config.data.seq.name);
                });
            }

            vm.validJob = function () {
                if (vm.upload_file) {
                    if (!vm.file) {
                        return false;
                    }
                } else {
                    if (!vm.ncbi) {
                        return false;
                    }
                }
                return true;
            }

            vm.allOff = function () {
                for (var i = 0; i < vm.extra_features.length; i++) {
                    var feature = vm.extra_features[i];
                    vm.submission[feature.id] = false;
                }
            }

            vm.allOn = function () {
                for (var i = 0; i < vm.extra_features.length; i++) {
                    var feature = vm.extra_features[i];
                    vm.submission[feature.id] = true;
                }
            }

            vm.loadSampleInput = function () {
                vm.upload_file = false;
                vm.ncbi = "NC_007194";
            }

            vm.openSampleOutput = function () {
                $window.location.href = "/upload/example/index.html";
            }

            vm.loadJob = function () {
                console.log(vm.job_id);
                if (vm.job_id.substr(0, 8).toLowerCase() == 'bacteria') {
                    $state.go('show.job', { id: vm.job_id });
                }
                else if (vm.job_id.substr(0, 5).toLowerCase() == 'fungi') {
                    $window.location.href = "http://fungismash.secondarymetabolites.org/#!/show/job/" + vm.job_id;
                }
                else if (vm.job_id.substr(0, 6).toLowerCase() == 'plants') {
                    $window.location.href = "http://plantismash.secondarymetabolites.org/#!/show/job/" + vm.job_id;
                }
            }
        }]);
<|endoftext|>


<commit_before>'use strict';

(function () {

  var app = angular.module('antismash.db.ui.stats', [

  ]);

  app.controller('StatsController', ['$http', '$state',
    function ($http, $state) {

      var vm = this;
      vm.general_stats = {
        'num_clusters': '?',
        'top_secmet_species': '?',
        'top_secmet_taxon': '?',
        'top_secmet_taxon_count': '?',
        'num_genomes': '?',
        'num_sequences': '?',
        'top_seq_taxon': '?',
        'top_seq_taxon_count': '?'
      };
      vm.sec_met_clusters = [];
      vm.queryTopSpecies = queryTopSpecies;

      function queryTopSpecies() {
        $state.go('query', {search_string: vm.general_stats.top_seq_species});
      }

      $http.get('/api/v1.0/stats').then(function(response){
        vm.general_stats = {
          'num_clusters': response.data.num_clusters,
          'top_secmet_species': response.data.top_secmet_species,
          'top_secmet_taxon': response.data.top_secmet_taxon,
          'top_secmet_taxon_count': response.data.top_secmet_taxon_count,
          'top_secmet_acc': response.data.top_secmet_acc,
          'num_genomes': response.data.num_genomes,
          'num_sequences': response.data.num_sequences,
          'top_seq_taxon': response.data.top_seq_taxon,
          'top_seq_taxon_count': response.data.top_seq_taxon_count,
          'top_seq_species': response.data.top_seq_species,
        };
        vm.sec_met_clusters = response.data.clusters;
      })
    }]);

})();
<commit_msg>stats: Search top seq taxon by taxid, and make output nicer<commit_after>'use strict';

(function () {

  var app = angular.module('antismash.db.ui.stats', [

  ]);

  app.controller('StatsController', ['$http', '$state',
    function ($http, $state) {

      var vm = this;
      vm.general_stats = {
        'num_clusters': '?',
        'top_secmet_species': '?',
        'top_secmet_taxon': '?',
        'top_secmet_taxon_count': '?',
        'num_genomes': '?',
        'num_sequences': '?',
        'top_seq_taxon': '?',
        'top_seq_taxon_count': '?'
      };
      vm.sec_met_clusters = [];
      vm.queryTopSpecies = queryTopSpecies;

      function queryTopSpecies() {
        $state.go('query', {search_string: '[taxid]' + vm.general_stats.top_seq_taxon});
      }

      $http.get('/api/v1.0/stats').then(function(response){
        vm.general_stats = {
          'num_clusters': response.data.num_clusters,
          'top_secmet_species': response.data.top_secmet_species,
          'top_secmet_taxon': response.data.top_secmet_taxon,
          'top_secmet_taxon_count': response.data.top_secmet_taxon_count,
          'top_secmet_acc': response.data.top_secmet_acc,
          'num_genomes': response.data.num_genomes,
          'num_sequences': response.data.num_sequences,
          'top_seq_taxon': response.data.top_seq_taxon,
          'top_seq_taxon_count': response.data.top_seq_taxon_count,
          'top_seq_species': response.data.top_seq_species.replace(/Unclassified/, 'sp.'),
        };
        vm.sec_met_clusters = response.data.clusters;
      })
    }]);

})();
<|endoftext|>


<commit_before>
// The AquariYumYum Disc Horse
module.exports = {
  title: "The AquariYumYum Disc Horse",
  pages: [
    [
      {cat: "What do you think of Jerry's aquarium stones?"},
      {robot: "The other day, I was talking to Weezops and bot said 'There is a funky aquarium store near planet Kokorall'."}
    ],
    [
      {scene: "Jerry the fish is present in his aquarium."},
      {cat: "We should jet over and check it out!"},
      {robot: "Totally"},
      {jerry: "Glub Bloop!"}
    ],
    [
      {scene: "KitSpace (the ship...) is heading toward Kokorall now."},
      {robot: "Did you check the water filter?"},
      {cat: "Well... uh, not really. I just sort of took a look at the water and it was clean."},
      {robot: "The aquaponic garden is sensitive to changes. Sometimes a regular eye can't see very small things in the water."},
      {cat: "Do you mean nutrient levels?"},
      {robot: "That and bacteria. The plants might not grow the way we expect if something else is taking their nutrients"},
      {jerry: "Bloopeee!!!!"},
    ],
    [
      {scene: "Cat gets behind the controls as they slow the ship for landing"},
      {cat: "We're here! Let's scoot to that aqarium."},
      {scene: "Robot puts out his wheels and Cat hugs him as they take off"}
    ],
    [
      {robot: "Dude! Did you see that? Was that an Distillavator V9?"},
      {cat: "Best water distiller around. It can convert dirty water to clean water; like saltwater, or bacteria-filled water, or pee-fi--"},
      {robot: "There's the store"}
    ],
    [
      {scene: "Cat and Robot arrive at a very simple looking aqarium store. It looks like it's full of water."},
      {cat: "Uh, how can we get in there?"},
      {owner: "You simply slip in!"},
      {cat: "But won't water come out when we come in?"},
      {owner: "Oh you don't need to worry about displacment. Look at all my other stores!"},
      {scene: "Some are only part full"},
    ],
    [
      {owner: "Yep, always busy at this store, so the other stores work together to help out."},
      {robot: "When our bodies come into your store, our weight pushes down on the water."},
      {owner: "That's right, and all of the stores push up on the air a little bit to let you in."},
      {cat: "This is soo cool."},
      {robot: "Uh, displacement and boyancy?"},
      {cat: "(turns robot to look) This"}
    ],
    [
      {scene: "A set of green sparkle rocks are in front of them"},
      {robot: "*mesmerized*. Yeh. Sparkle."},
      {cat: "Right?! How much are these sparkly-magic-things?"},
      {owner: "The elumenhairy electric-hairy-sparkle-stones? A difficult-discourse."},
      {cat: "Disc Horse?"},
      {owner: "Discourse. It means a conversation or debate."}
    ],
    [
      {scene: "Just then, a friendly-sized purple and light-blue orb entered the tank"},
      {robot: "Wow, this is enough stones for Jerry's whole TankTrench region!"},
      {cat: "Or a mountain around the LeeGo Statue"}
    ],
    [
      {scene: "Robot and Cat are walking out of the store toward their ship"},
      {orb: "Can you give me your elemnhairy stones?"},
      {cat: "Oh hi! We just got these from AquariYumYum. It's that building--"},
      {orb: "They have no more stones. Can you give me yours?"},
      {cat: "Uh, would you like to help with my difficult discourse problem?"}
    ],
    [
      {scene: "Robot stops driving and comes back to listen."},
      {orb: "No. Can't you just give me some stones?"},
      {scene: "Things look a little tense"},
      {cat: "I remember the owner saying all the stores work together. Let's head to another store."}
    ],
    [
      {scene: "Robot, Cat and Orb head to one of the other aquariums and enter"},
      {orb: "I've gotten the last of this stone. There is none for you"},
      {scene: "Orb isn't emoting much"},
      {robot: "Beautiful!"},
      {cat: "Good find Orb!"}
    ],
    [
      {scene: "Orb looks surprised"},
      {orb: "Yeah I saw them and liked them"},
      {cat: "What did you like about them?"},
      {orb: "I. I felt good seeing them. My inner orb swelled with good feelings."},
      {robot: "Is the water going to spill?"}
    ],
    [
      {scene: "Cat and robot are heading back to their ship"},
      {orb: "It's hard to outroll an orb. Can I share some of my stones with you? Without that difficult conversation, this wouldn't have happend."},
      {robot: "The difficult-discourse!"},
      {cat: "*accepts orbs stones*"}
    ],
    [
      {cat: "Let me share some of my stones. We can remember this when we see each other's stones in our aquariums."},
      {jerry: "Glooby Glip Glubboo"}
    ],
    [
      {scene: "Jerry's amazeballs tank"}
    ]
  ]
}; // exports
<commit_msg>Eke hairy should be elumenhairy<commit_after>
// The AquariYumYum Disc Horse
module.exports = {
  title: "The AquariYumYum Disc Horse",
  pages: [
    [
      {cat: "What do you think of Jerry's aquarium stones?"},
      {robot: "The other day, I was talking to Weezops and bot said 'There is a funky aquarium store near planet Kokorall'."}
    ],
    [
      {scene: "Jerry the fish is present in his aquarium."},
      {cat: "We should jet over and check it out!"},
      {robot: "Totally"},
      {jerry: "Glub Bloop!"}
    ],
    [
      {scene: "KitSpace (the ship...) is heading toward Kokorall now."},
      {robot: "Did you check the water filter?"},
      {cat: "Well... uh, not really. I just sort of took a look at the water and it was clean."},
      {robot: "The aquaponic garden is sensitive to changes. Sometimes a regular eye can't see very small things in the water."},
      {cat: "Do you mean nutrient levels?"},
      {robot: "That and bacteria. The plants might not grow the way we expect if something else is taking their nutrients"},
      {jerry: "Bloopeee!!!!"},
    ],
    [
      {scene: "Cat gets behind the controls as they slow the ship for landing"},
      {cat: "We're here! Let's scoot to that aqarium."},
      {scene: "Robot puts out his wheels and Cat hugs him as they take off"}
    ],
    [
      {robot: "Dude! Did you see that? Was that an Distillavator V9?"},
      {cat: "Best water distiller around. It can convert dirty water to clean water; like saltwater, or bacteria-filled water, or pee-fi--"},
      {robot: "There's the store"}
    ],
    [
      {scene: "Cat and Robot arrive at a very simple looking aqarium store. It looks like it's full of water."},
      {cat: "Uh, how can we get in there?"},
      {owner: "You simply slip in!"},
      {cat: "But won't water come out when we come in?"},
      {owner: "Oh you don't need to worry about displacment. Look at all my other stores!"},
      {scene: "Some are only part full"},
    ],
    [
      {owner: "Yep, always busy at this store, so the other stores work together to help out."},
      {robot: "When our bodies come into your store, our weight pushes down on the water."},
      {owner: "That's right, and all of the stores push up on the air a little bit to let you in."},
      {cat: "This is soo cool."},
      {robot: "Uh, displacement and boyancy?"},
      {cat: "(turns robot to look) This"}
    ],
    [
      {scene: "A set of green sparkle rocks are in front of them"},
      {robot: "*mesmerized*. Yeh. Sparkle."},
      {cat: "Right?! How much are these sparkly-magic-things?"},
      {owner: "The elumenhairy electric-hairy-sparkle-stones? A difficult-discourse."},
      {cat: "Disc Horse?"},
      {owner: "Discourse. It means a conversation or debate."}
    ],
    [
      {scene: "Just then, a friendly-sized purple and light-blue orb entered the tank"},
      {robot: "Wow, this is enough stones for Jerry's whole TankTrench region!"},
      {cat: "Or a mountain around the LeeGo Statue"}
    ],
    [
      {scene: "Robot and Cat are walking out of the store toward their ship"},
      {orb: "Can you give me your elumenhairy stones?"},
      {cat: "Oh hi! We just got these from AquariYumYum. It's that building--"},
      {orb: "They have no more stones. Can you give me yours?"},
      {cat: "Uh, would you like to help with my difficult discourse problem?"}
    ],
    [
      {scene: "Robot stops driving and comes back to listen."},
      {orb: "No. Can't you just give me some stones?"},
      {scene: "Things look a little tense"},
      {cat: "I remember the owner saying all the stores work together. Let's head to another store."}
    ],
    [
      {scene: "Robot, Cat and Orb head to one of the other aquariums and enter"},
      {orb: "I've gotten the last of this stone. There is none for you"},
      {scene: "Orb isn't emoting much"},
      {robot: "Beautiful!"},
      {cat: "Good find Orb!"}
    ],
    [
      {scene: "Orb looks surprised"},
      {orb: "Yeah I saw them and liked them"},
      {cat: "What did you like about them?"},
      {orb: "I. I felt good seeing them. My inner orb swelled with good feelings."},
      {robot: "Is the water going to spill?"}
    ],
    [
      {scene: "Cat and robot are heading back to their ship"},
      {orb: "It's hard to outroll an orb. Can I share some of my stones with you? Without that difficult conversation, this wouldn't have happend."},
      {robot: "The difficult-discourse!"},
      {cat: "*accepts orbs stones*"}
    ],
    [
      {cat: "Let me share some of my stones. We can remember this when we see each other's stones in our aquariums."},
      {jerry: "Glooby Glip Glubboo"}
    ],
    [
      {scene: "Jerry's amazeballs tank"}
    ]
  ]
}; // exports
<|endoftext|>


<commit_before>'use strict';

const { emitPerfEvent } = require('../../perf');

// Event performance events related to the current request,
// e.g. how long each middleware lasted.
const perfEvent = async function (
  { error, runOpts, mInput },
  nextLayer,
  { measures },
) {
  // Do not report if an exception was thrown
  if (error) { return; }

  // Total request time, stopped just before the response is sent
  await emitPerfEvent({ mInput, phase: 'request', measures, runOpts });
};

module.exports = {
  perfEvent,
};
<commit_msg>Add overall request measure<commit_after>'use strict';

const { emitPerfEvent } = require('../../perf');

// Event performance events related to the current request,
// e.g. how long each middleware lasted.
const perfEvent = async function (
  { error, runOpts, mInput, respPerf },
  nextLayer,
  { measures },
) {
  // Do not report if an exception was thrown
  if (error) { return; }

  const measuresA = [...measures, respPerf];
  await emitPerfEvent({
    mInput,
    phase: 'request',
    measures: measuresA,
    runOpts,
  });
};

module.exports = {
  perfEvent,
};
<|endoftext|>


<commit_before>'use strict';

var debug = require('debug')('formio:middleware:mergeFormHandler');
var Q = require('q');
var _ = require('lodash');

/**
 * The mergeFormHandler middleware.
 *
 * This middleware is used for merging multiple modifications made to a form by different people.
 *
 * @param router
 * @returns {Function}
 *
 * @TODO: Uniquify all key:type pairs in the update form, to prohibit invalid merges.
 */
module.exports = function(router) {
  return function(req, res, next) {
    if (req.method !== 'PUT' || !req.formId || !_.has(req, 'body.modified') || !_.has(req, 'body.components')) {
      debug('Skipping');
      return next();
    }

    var util = router.formio.util;
    var cache = router.formio.cache;
    cache.loadCurrentForm(req, function(err, form) {
      if (err || !form) {
        var msg = err || 'No form was contained in the current request.';
        debug(msg);
        return next(msg);
      }

      // If both times are the same, continue as usual, because no outside modifications have been made since.
      var current = new Date();
      var stable = new Date(_.get(form, 'modified', current.getTime())).getTime();
      var local = new Date(_.get(req, 'body.modified'), current.getTime()).getTime();
      if (stable === local) {
        return next();
      }

      // Other modifications have been made since this session started, merge all changes.
      // If no components are present in the update, just merge form property changes.
      if (!_.has(req, 'body.components')) {
        // Update any form properties, using the latest update last.
        req.body = _.assign(form, req.body);
        return next();
      }

      // If the update contains components, deep merge all the components.
      var _components = {stable: _.cloneDeep(form.components), local: _.cloneDeep(req.body.components)};
      var _map = {stable: {}, local: {}};
      var _path = {stable: {}, local: {}};

      // Build the new component list using the oldest components first.
      var final = _.cloneDeep(_components.stable);
      var ignoreTypes = ['panel'];
      var ignoredComponents = [];
      ['stable', 'local'].forEach(function(type) {
        util.eachComponent(_components[type], function(component, path) {
          // If this component has components, and is in the ignore types, flag all its children, so it isnt merged 2x.
          if (ignoreTypes.indexOf(component.type) !== -1) {
            var container = _.intersection(['components', 'rows', 'columns'], Object.keys(component));
            util.eachComponent(component[container], function(subcomponent) {
              ignoredComponents.push(subcomponent.key);
            }, true);
          }

          debug('path (' + component.key + '): ' + path);
          // Update the map and path references for this type on a per component basis.
          _map[type][component.key] = component;
          _path[type][component.key] = path;
        }, true);
      });

      // The list of components with duplicate keys, but different types.
      var collisions = [];

      // Compute which keys are new in the local copy and merge them into the existing form.
      var difference = _.difference(Object.keys(_map.local), Object.keys(_map.stable));
      var parents = {};
      var newComponents = [];
      difference.forEach(function(key) {
        // Add this new local component to the list of new components.
        var component = _map.local[key];
        newComponents.push(component);

        // Determine this components parent component path.
        var path = _path.local[key];
        debug(path);
        if (path === key && ignoredComponents.indexOf(key) === -1) {
          parents[''] = parents[''] || [];
          parents[''].push(key);
        }
        else if (path.indexOf('.') !== -1) {
          path = path.split('.');
          var last = path.pop();
          if (last === key) {
            last = path.pop();
          }

          parents[last] = parents[last] || [];
          parents[last].push(key);
        }
      });
      debug('parents:');
      debug(parents);

      var newComponents = _.filter(_map.local, function(item) {
        return difference.indexOf(item.key) !== -1
      });
      debug('newComponents:');
      debug(newComponents);

      // Build the new component list using the oldest components first.
      util.eachComponent(final, function(component, path) {
        // Check to see if the stable component has local modifications.
        var modifications = _.get(_map.local, component.key);

        // Check to see if the stable component
        var subComponents = _.get(_path.stable, '');
        if (!modifications) {
          return;
        }

        // If the types are different, this is a component collision, take care of this after.
        if (modifications.type !== component.type) {
          collisions.push(modifications);
          delete _map.local[component.key];
          return;
        }

        // Remove any subcomponents, because they will be individually merged.
        modifications = _.omit(modifications, ['components', 'rows', 'columns']);
        component = _.assign(component, modifications);

        // Check to see if this component has new subcomponents.
        var container = _.intersection(['components', 'rows', 'columns'], Object.keys(component));
        if (container && _.has(parents, component.key)) {
          component[container] = component[container].concat(parents[component.key])
        }
      }, true);

      // Add totally new components to the form, without clobbering other form modifications.
      // @TODO: Make insertion in the correct place.
      if (parents.hasOwnProperty('') && parents[''].length > 0) {
        parents[''].forEach(function(key) {
          final.push(_map.local[key]);
        });
      }

      // Update the final components payload to include all changes.
      debug('final');
      debug(final);
      req.body.components = final;
      return next();
    });
  };
};
<commit_msg>Fixing time comparison issue, where the time was not defaulting correctly.<commit_after>'use strict';

var debug = require('debug')('formio:middleware:mergeFormHandler');
var Q = require('q');
var _ = require('lodash');

/**
 * The mergeFormHandler middleware.
 *
 * This middleware is used for merging multiple modifications made to a form by different people.
 *
 * @param router
 * @returns {Function}
 *
 * @TODO: Uniquify all key:type pairs in the update form, to prohibit invalid merges.
 */
module.exports = function(router) {
  return function(req, res, next) {
    if (req.method !== 'PUT' || !req.formId || !_.has(req, 'body.modified') || !_.has(req, 'body.components')) {
      debug('Skipping');
      return next();
    }

    var util = router.formio.util;
    var cache = router.formio.cache;
    cache.loadCurrentForm(req, function(err, form) {
      if (err || !form) {
        var msg = err || 'No form was contained in the current request.';
        debug(msg);
        return next(msg);
      }

      // If both times are the same, continue as usual, because no outside modifications have been made since.
      var current = new Date();
      var stable = new Date(_.get(form, 'modified', current.getTime())).getTime();
      var local = new Date(_.get(req, 'body.modified', current.getTime())).getTime();
      if (stable === local) {
        debug('skipping - up to date');
        return next();
      }

      // If the update contains components, deep merge all the components.
      var _components = {stable: _.cloneDeep(form.components), local: _.cloneDeep(req.body.components)};
      var _map = {stable: {}, local: {}};
      var _path = {stable: {}, local: {}};

      // Build the new component list using the oldest components first.
      var final = _.cloneDeep(_components.stable);
      var ignoreTypes = ['panel'];
      var ignoredComponents = [];
      ['stable', 'local'].forEach(function(type) {
        util.eachComponent(_components[type], function(component, path) {
          // If this component has components, and is in the ignore types, flag all its children, so it isnt merged 2x.
          if (ignoreTypes.indexOf(component.type) !== -1) {
            var container = _.intersection(['components', 'rows', 'columns'], Object.keys(component));
            util.eachComponent(component[container], function(subcomponent) {
              ignoredComponents.push(subcomponent.key);
            }, true);
          }

          debug('path (' + component.key + '): ' + path);
          // Update the map and path references for this type on a per component basis.
          _map[type][component.key] = component;
          _path[type][component.key] = path;
        }, true);
      });

      // The list of components with duplicate keys, but different types.
      var collisions = [];

      // Compute which keys are new in the local copy and merge them into the existing form.
      var difference = _.difference(Object.keys(_map.local), Object.keys(_map.stable));
      var parents = {};
      var newComponents = [];
      difference.forEach(function(key) {
        // Add this new local component to the list of new components.
        var component = _map.local[key];
        newComponents.push(component);

        // Determine this components parent component path.
        var path = _path.local[key];
        debug(path);
        if (path === key && ignoredComponents.indexOf(key) === -1) {
          parents[''] = parents[''] || [];
          parents[''].push(key);
        }
        else if (path.indexOf('.') !== -1) {
          path = path.split('.');
          var last = path.pop();
          if (last === key) {
            last = path.pop();
          }

          parents[last] = parents[last] || [];
          parents[last].push(key);
        }
      });
      debug('parents:');
      debug(parents);

      var newComponents = _.filter(_map.local, function(item) {
        return difference.indexOf(item.key) !== -1
      });
      debug('newComponents:');
      debug(newComponents);

      // Build the new component list using the oldest components first.
      util.eachComponent(final, function(component, path) {
        // Check to see if the stable component has local modifications.
        var modifications = _.get(_map.local, component.key);

        // Check to see if the stable component
        var subComponents = _.get(_path.stable, '');
        if (!modifications) {
          return;
        }

        // If the types are different, this is a component collision, take care of this after.
        if (modifications.type !== component.type) {
          collisions.push(modifications);
          delete _map.local[component.key];
          return;
        }

        // Remove any subcomponents, because they will be individually merged.
        modifications = _.omit(modifications, ['components', 'rows', 'columns']);
        component = _.assign(component, modifications);

        // Check to see if this component has new subcomponents.
        var container = _.intersection(['components', 'rows', 'columns'], Object.keys(component));
        if (container && _.has(parents, component.key)) {
          component[container] = component[container].concat(parents[component.key])
        }
      }, true);

      // Add totally new components to the form, without clobbering other form modifications.
      // @TODO: Make insertion in the correct place.
      if (parents.hasOwnProperty('') && parents[''].length > 0) {
        parents[''].forEach(function(key) {
          final.push(_map.local[key]);
        });
      }

      // Update the final components payload to include all changes.
      debug('final');
      debug(final);
      req.body.components = final;
      return next();
    });
  };
};
<|endoftext|>