<commit_before>var helpers = clientApp.helpers || {};

$.fn.gallery = function(result, index)  {
  if (result.visible) {
    var visible = eval(result.visible);
    if (!visible) return;
  }

  var contentKeys = Object.keys(result.contents);

  if (result.label) $(this).appendLabel(result.label);
  var gallerySelector = "gallery-" + index;
  $(this).append('

');

  var blobsArray = getGalleryImages(result.contents);

  var leftItems = "gallery-left-items-" + index;
  var rightItems = "gallery-right-items-" + index;
  renderGalleryBlobList(index, contentKeys, result, leftItems, 'left', blobsArray);

  var blobsContainerSelector = "gallery-blobs-container-" + index;
  $("." + gallerySelector).append('

');

  renderGalleryBlobList(index, contentKeys, result, rightItems, 'right', blobsArray);
  $("." + rightItems).addClass("gallery-item-list-right di-none");

  var imgContainerLeft = "gallery-blobs-left-" + index;
  var imgContainerRight = "gallery-blobs-right-" + index;
  $("." + blobsContainerSelector).append('

');
  $("." + blobsContainerSelector).append('

');

  var firstVisibleBlobSet = blobsArray[0];
  for (var i = 0; i < firstVisibleBlobSet.length; i++) {
    $("#" + imgContainerRight).append(firstVisibleBlobSet[i].clone());
    $("#" + imgContainerLeft).append(firstVisibleBlobSet[i].clone());
  }

  var allSrc = getAllSrc(blobsArray);
  $("." + gallerySelector).setGalleryMinHeight(allSrc);
  $(this).append("

");
  $("#gallery-" + index + "-zoom-container").appendZoom(index, leftItems);
  if (blobsArray.length > 1) $("." + leftItems).appendCompare(index, rightItems, imgContainerRight);
}

function getGalleryImages(contentArray) {
  var allSrc = [];
  var keys = Object.keys(contentArray);
  for (var i = 0; i < keys.length; i++) {
    var blobSetContent = contentArray[keys[i]];
    var visibleField = blobSetContent.hasOwnProperty('visible');
    if (!visibleField || (visibleField && eval(blobSetContent.visible))) {
      var imgField = blobSetContent.img;
      var repeat = blobSetContent.repeat !== undefined ? checkRepeat(blobSetContent.repeat) : 1;
      var blobs = [];
      for (var idx = 0; idx < repeat; idx++) {
        blobs = [];
        if (typeof(imgField) === 'string') {
          var img = '';
          blobs.push($(img));
        } else if (typeof (imgField) === 'object') {
          for (var k = 0; k < imgField.length; k++) {
            var img = '';
            blobs.push($(img));
          }
        }
        allSrc.push(blobs);
      }
    }
  }
  return allSrc;
}

function getAllSrc(blobsArray) {
  let allSrc = [];
  for (var i = 0; i < blobsArray.length; i++) {
    for (var j = 0; j < blobsArray[i].length; j++) {
      allSrc.push(blobsArray[i][j].attr('src'));
    }
  }
  return allSrc;
}

function checkRepeat(expression) {
  if (expression.indexOf('+') === -1) return parseInt(eval(expression));
  var expressionArray = expression.split('+');
  var repeat = 0;
  for (var i = 0; i < expressionArray.length; i++) {
    repeat += parseInt(eval(expressionArray[i]));
  }
  return repeat;
}

function renderGalleryBlobList(index, contentKeys, result, itemSelector, side, blobsArray) {
  $(".gallery-" + index).append('

');
  $("." + itemSelector).append('

');
  var contents = result.contents;
  var firstVisibleBlob = true;
  var itemIndex = 0;
  for (let i = 0; i < contentKeys.length; i++) {
    var visibleField = contents[contentKeys[i]].hasOwnProperty('visible');
    if (!visibleField || (visibleField && eval(contents[contentKeys[i]].visible))) {
      var content = result.contents[contentKeys[i]].img;
      if (typeof(content) === 'string') {
        content = [content];
      }
      if (firstVisibleBlob) {
        helpers.addToStorage('gallery-' + index + '-' + side, itemIndex);
        firstVisibleBlob = false;
      }
      var line = contents[contentKeys[i]];
      var repeat = line.repeat != undefined ? checkRepeat(line.repeat) : 1;
      for (var idx = 0; idx < repeat; idx++) {
        var text = getEvalText(contentKeys[i]);
        var spanId = 'gallery-' + index + '-item-' + side +'-'+ i + '-' + idx;
        try {
          title = eval(text)
        } catch (err) {
          title = text
        }
        $('#gallery-'+index+'-blobList-'+side).append('' + title + '');
        $('#' + spanId).addHoverFeatures(index, side, blobsArray[itemIndex], itemIndex);
        itemIndex++;
      }
    }
  }
  $(this).addMouseOut(index, side, blobsArray);
  $("." +itemSelector+ " span:first-child").addClass("gallery-item-selected");
}

$.fn.addMouseOut = function (galleryIndex, side, blobsArray) {
  var selector = '#gallery-blobs-' + side + '-' + galleryIndex;
  $("#gallery-" + galleryIndex + "-blobList-" + side).mouseout(function (event) {
    e = event.toElement || event.relatedTarget;
    if (e != null && (e.parentNode == this || e == this)) {
      return;
    }
    var selectedSrc = helpers.getFromStorage("gallery-" + galleryIndex + "-" + side);
    $(selector).empty();
    var blobs = blobsArray[selectedSrc];
    for (var i = 0; i < blobs.length; i++) {
      $(selector).append(blobs[i].clone());
    }
    $("#gallery-" + galleryIndex + "-zoom > input").adjustSize(galleryIndex);
    $(selector + " > img").addClass('gallery-' + galleryIndex + '-blob-' + side);
    setInterpolation(galleryIndex);
  });
}

function getEvalText(str) {
  if (str.indexOf('+') != -1) {
    return str;
  } else {
    return "\'"+str+"\'";
  }
}

$.fn.addHoverFeatures = function(galleryIndex, side, src, itemIndex) {
  var imgSelector = '.gallery-' +galleryIndex+ '-blob-' + side;
  var selector = '#gallery-blobs-' +side+ '-' + galleryIndex;
  $(this).mouseover(function() {
    $(selector).empty();
    for (var i = 0; i < src.length; i++) {
      $(selector).append($(src[i]));
    }
    $(selector + " > img").addClass('gallery-' +galleryIndex+ '-blob-'+side);
    var zoomValue = $("#gallery-" + galleryIndex + "-zoom > #editor-zoom").val();
    $("#gallery-" + galleryIndex + "-zoom > input").adjustSize(galleryIndex);
    setInterpolation(galleryIndex);
  });
  $(this).on('click', function() {
    var listSelector = ".gallery-" +side+ "-items-" + galleryIndex;
    $("#gallery-" + galleryIndex + "-blobList-" + side + " > .gallery-item-selected").toggleClass("gallery-item-selected");
    $(this).toggleClass("gallery-item-selected");
    helpers.addToStorage("gallery-" +galleryIndex + "-" + side, itemIndex);
  });
}

function setInterpolation(galleryIndex) {
  var zoomValue = $("#gallery-" + galleryIndex + "-zoom > #editor-zoom").val();
  helpers.checkInterpolation(zoomValue, ".gallery-" + galleryIndex + " img");
}

$.fn.appendZoom = function(index, leftItems) {
  var zoom = $("#zoom-container").clone();
  zoom.find('input').val('1.0');
  zoom.find('span').html('1x');
  var newZoomID= "gallery-" +index+ "-zoom";
  zoom.attr("id", newZoomID).appendTo($(this));
  zoom.removeClass("di-none");
  zoom.show();
  $("#" + newZoomID + " > .zoom-info > #editor-image-size").remove();
  $("#" + newZoomID + " > input").on('input', function() {
    var zoomLevel = $(this).val();
    let selector = ".gallery-" + index + " img";
    helpers.checkInterpolation(zoomLevel, selector);
    $(".gallery-" + index).css({ height: (parseInt($(".gallery-" + index).css('min-height')) || parseInt($(".gallery-" + index).css('height'))) * zoomLevel + "px" });
    $(this).adjustSize(index);
    $("#" + newZoomID + " > .zoom-info > #editor-zoom-value").html(zoomLevel + "x");
  });
  scrollSync(index);
}

$.fn.adjustSize = function(index) {
  var zoomLevel = $(this).val();
  $("#gallery-blobs-left-"+index + ", #gallery-blobs-right-"+index).children('img').each(function(i){
    if ($(this)[0].naturalHeight != 0 || $(this)[0].naturalWidth != 0) {
      $(this).height($(this)[0].naturalHeight * zoomLevel);
      $(this).width($(this)[0].naturalWidth * zoomLevel);
    }
  });
}

// Set gallery min-height to avoid jump loops.
$.fn.setGalleryMinHeight = function (sources) {
  var minHeight;
  var selector = $(this);
  if (!Array.isArray(sources)) {
    sources = Object.keys(sources).map(function (e) {
      return sources[e];
    });
  }
  for (let i = 0; i < sources.length; i++) {
    let tmpImg = new Image();
    tmpImg.src = sources[i];
    $(tmpImg).on("load", function () {
      minHeight = parseInt(selector.css("min-height"));
      if (minHeight < tmpImg.height) {
        minHeight = tmpImg.height < 580 ? tmpImg.height : 580;
        selector.css({ minHeight: minHeight + 20 + "px" });
      }
    });
  }
};

$.fn.appendCompare = function (galleryIndex, rightItems, imgContainerRight) {
  $(this).append("
<input type=checkbox id=compare-btn-gallery-" + galleryIndex + ">Compare
");
  $("#compare-btn-gallery-" + galleryIndex).on('click', function () {
    if ($(this).is(":checked")) {
      $("#gallery-blobs-left-" + galleryIndex).css({ "flex-basis": "50%" });
      $("#gallery-blobs-right-" + galleryIndex).css({ "flex-basis": "50%" });
      $(".gallery-blobs-container-" + galleryIndex).addClass('blobs-wrapper-compare');
    } else {
      $("#gallery-blobs-left-" + galleryIndex).css({ "flex-basis": "" });
      $("#gallery-blobs-right-" + galleryIndex).css({ "flex-basis": "" });
      $(".gallery-blobs-container-" + galleryIndex).removeClass("blobs-wrapper-compare");
    }
    $("#" + imgContainerRight).toggleClass("di-none");
    $(".gallery-" + galleryIndex).toggleClass("space-between");
    $("." + rightItems).toggleClass("di-none");
  });
}

$.fn.appendLabel = function (labelArray) {
  var html = "";
  for (var i = 0; i < labelArray.length; i++) html += labelArray[i];
  if (html.charAt(0) == "'") html = eval(html);
  $(this).html("
" + html + "
");
}

function scrollSync(index) {
  var isSyncingLeftScroll = false;
  var isSyncingRightScroll = false;
  var leftDiv = document.getElementById('gallery-blobs-left-' + index);
  var rightDiv = document.getElementById('gallery-blobs-right-' + index);
  $('#gallery-blobs-left-' + index).attachDragger();
  $('#gallery-blobs-right-' + index).attachDragger();

  leftDiv.onscroll = function() {
    if (!isSyncingLeftScroll) {
      isSyncingRightScroll = true;
      rightDiv.scrollTop = this.scrollTop;
      rightDiv.scrollLeft = this.scrollLeft;
    }
    isSyncingLeftScroll = false;
  }
  rightDiv.onscroll = function() {
    if (!isSyncingRightScroll) {
      isSyncingLeftScroll = true;
      leftDiv.scrollTop = this.scrollTop;
      leftDiv.scrollLeft = this.scrollLeft;
    }
    isSyncingRightScroll = false;
  }
}<commit_msg>[ClientApp/gallery] Adjust zoom value to fit images in the gallery container<commit_after>var helpers = clientApp.helpers || {};

$.fn.gallery = function (result, index) {
  if (result.visible) {
    var visible = eval(result.visible);
    if (!visible) return;
  }

  var contentKeys = Object.keys(result.contents);

  if (result.label) $(this).appendLabel(result.label);
  var gallerySelector = "gallery-" + index;
  $(this).append('

');

  var blobsArray = getGalleryImages(result.contents);

  var leftItems = "gallery-left-items-" + index;
  var rightItems = "gallery-right-items-" + index;
  renderGalleryBlobList(index, contentKeys, result, leftItems, 'left', blobsArray);

  var blobsContainerSelector = "gallery-blobs-container-" + index;
  $("." + gallerySelector).append('

');

  renderGalleryBlobList(index, contentKeys, result, rightItems, 'right', blobsArray);
  $("." + rightItems).addClass("gallery-item-list-right di-none");

  var imgContainerLeft = "gallery-blobs-left-" + index;
  var imgContainerRight = "gallery-blobs-right-" + index;
  $("." + blobsContainerSelector).append('

');
  $("." + blobsContainerSelector).append('

');

  var firstVisibleBlobSet = blobsArray[0];
  for (var i = 0; i < firstVisibleBlobSet.length; i++) {
    $("#" + imgContainerRight).append(firstVisibleBlobSet[i].clone());
    $("#" + imgContainerLeft).append(firstVisibleBlobSet[i].clone());
  }

  var allSrc = getAllSrc(blobsArray);
  $("." + gallerySelector).setGalleryMinHeight(allSrc);
  $(this).append("

");
  $("#gallery-" + index + "-zoom-container").appendZoom(index, leftItems);
  if (blobsArray.length > 1) $("." + leftItems).appendCompare(index, rightItems, imgContainerRight);
}

function getGalleryImages(contentArray) {
  var allSrc = [];
  var keys = Object.keys(contentArray);
  for (var i = 0; i < keys.length; i++) {
    var blobSetContent = contentArray[keys[i]];
    var visibleField = blobSetContent.hasOwnProperty('visible');
    if (!visibleField || (visibleField && eval(blobSetContent.visible))) {
      var imgField = blobSetContent.img;
      var repeat = blobSetContent.repeat !== undefined ? checkRepeat(blobSetContent.repeat) : 1;
      var blobs = [];
      for (var idx = 0; idx < repeat; idx++) {
        blobs = [];
        if (typeof (imgField) === 'string') {
          var img = '';
          blobs.push($(img));
        } else if (typeof (imgField) === 'object') {
          for (var k = 0; k < imgField.length; k++) {
            var img = '';
            blobs.push($(img));
          }
        }
        allSrc.push(blobs);
      }
    }
  }
  return allSrc;
}

function getAllSrc(blobsArray) {
  let allSrc = [];
  for (var i = 0; i < blobsArray.length; i++) {
    for (var j = 0; j < blobsArray[i].length; j++) {
      allSrc.push(blobsArray[i][j].attr('src'));
    }
  }
  return allSrc;
}

function checkRepeat(expression) {
  if (expression.indexOf('+') === -1) return parseInt(eval(expression));
  var expressionArray = expression.split('+');
  var repeat = 0;
  for (var i = 0; i < expressionArray.length; i++) {
    repeat += parseInt(eval(expressionArray[i]));
  }
  return repeat;
}

function renderGalleryBlobList(index, contentKeys, result, itemSelector, side, blobsArray) {
  $(".gallery-" + index).append('

');
  $("." + itemSelector).append('

');
  var contents = result.contents;
  var firstVisibleBlob = true;
  var itemIndex = 0;
  for (let i = 0; i < contentKeys.length; i++) {
    var visibleField = contents[contentKeys[i]].hasOwnProperty('visible');
    if (!visibleField || (visibleField && eval(contents[contentKeys[i]].visible))) {
      var content = result.contents[contentKeys[i]].img;
      if (typeof (content) === 'string') {
        content = [content];
      }
      if (firstVisibleBlob) {
        helpers.addToStorage('gallery-' + index + '-' + side, itemIndex);
        firstVisibleBlob = false;
      }
      var line = contents[contentKeys[i]];
      var repeat = line.repeat != undefined ? checkRepeat(line.repeat) : 1;
      for (var idx = 0; idx < repeat; idx++) {
        var text = getEvalText(contentKeys[i]);
        var spanId = 'gallery-' + index + '-item-' + side + '-' + i + '-' + idx;
        try {
          title = eval(text)
        } catch (err) {
          title = text
        }
        $('#gallery-' + index + '-blobList-' + side).append('' + title + '');
        $('#' + spanId).addHoverFeatures(index, side, blobsArray[itemIndex], itemIndex);
        itemIndex++;
      }
    }
  }
  $(this).addMouseOut(index, side, blobsArray);
  $("." + itemSelector + " span:first-child").addClass("gallery-item-selected");
}

$.fn.addMouseOut = function (galleryIndex, side, blobsArray) {
  var selector = '#gallery-blobs-' + side + '-' + galleryIndex;
  $("#gallery-" + galleryIndex + "-blobList-" + side).mouseout(function (event) {
    e = event.toElement || event.relatedTarget;
    if (e != null && (e.parentNode == this || e == this)) {
      return;
    }
    var selectedSrc = helpers.getFromStorage("gallery-" + galleryIndex + "-" + side);
    $(selector).empty();
    var blobs = blobsArray[selectedSrc];
    for (var i = 0; i < blobs.length; i++) {
      $(selector).append(blobs[i].clone());
    }
    $("#gallery-" + galleryIndex + "-zoom > input").adjustSize(galleryIndex);
    $(selector + " > img").addClass('gallery-' + galleryIndex + '-blob-' + side);
    setInterpolation(galleryIndex);
  });
}

function getEvalText(str) {
  if (str.indexOf('+') != -1) {
    return str;
  } else {
    return "\'" + str + "\'";
  }
}

$.fn.addHoverFeatures = function (galleryIndex, side, src, itemIndex) {
  var imgSelector = '.gallery-' + galleryIndex + '-blob-' + side;
  var selector = '#gallery-blobs-' + side + '-' + galleryIndex;
  $(this).mouseover(function () {
    $(selector).empty();
    for (var i = 0; i < src.length; i++) {
      $(selector).append($(src[i]));
    }
    $(selector + " > img").addClass('gallery-' + galleryIndex + '-blob-' + side);
    var zoomValue = $("#gallery-" + galleryIndex + "-zoom > #editor-zoom").val();
    $("#gallery-" + galleryIndex + "-zoom > input").adjustSize(galleryIndex);
    setInterpolation(galleryIndex);
  });
  $(this).on('click', function () {
    var listSelector = ".gallery-" + side + "-items-" + galleryIndex;
    $("#gallery-" + galleryIndex + "-blobList-" + side + " > .gallery-item-selected").toggleClass("gallery-item-selected");
    $(this).toggleClass("gallery-item-selected");
    helpers.addToStorage("gallery-" + galleryIndex + "-" + side, itemIndex);
  });
}

function setInterpolation(galleryIndex) {
  var zoomValue = $("#gallery-" + galleryIndex + "-zoom > #editor-zoom").val();
  helpers.checkInterpolation(zoomValue, ".gallery-" + galleryIndex + " img");
}

$.fn.appendZoom = function (index, leftItems) {
  var zoom = $("#zoom-container").clone();
  zoom.find('input').val('1.0');
  zoom.find('span').html('1x');
  var newZoomID = "gallery-" + index + "-zoom";
  zoom.attr("id", newZoomID).appendTo($(this));
  zoom.removeClass("di-none");
  zoom.show();

  getImagesWidth($("#gallery-blobs-left-" + index).children()).then(imagesTotalWidth => {
    imagesTotalWidth = imagesTotalWidth.reduce((total, num) => total += num);
    imagesContainerWidth = $("#gallery-blobs-left-" + index).width()
    if (imagesTotalWidth > imagesContainerWidth) {
      zoomAdjustedValue = ((imagesContainerWidth - $("#gallery-blobs-left-" + index).children().length * 10) / imagesTotalWidth).toFixed(2);
      zoomAdjustedValue = zoomAdjustedValue < 0.25 ? 0.25 : zoomAdjustedValue;
      updateZoomValue(zoom, zoomAdjustedValue);
      $("#" + newZoomID + " > input").adjustSize(index);
    }
  });

  $("#" + newZoomID + " > .zoom-info > #editor-image-size").remove();
  $("#" + newZoomID + " > input").on('input', function () {
    var zoomLevel = $(this).val();
    let selector = ".gallery-" + index + " img";
    helpers.checkInterpolation(zoomLevel, selector);
    $(".gallery-" + index).css({ height: (parseInt($(".gallery-" + index).css('min-height')) || parseInt($(".gallery-" + index).css('height'))) * zoomLevel + "px" });
    $(this).adjustSize(index);
    $("#" + newZoomID + " > .zoom-info > #editor-zoom-value").html(zoomLevel + "x");
  });
  scrollSync(index);
}

function updateZoomValue(zoom, value){
  zoom.find('input').val(value.toString());
  zoom.find('span').html(value.toString() + 'x');
}

function getImageWidthAfterLoad(image) {
  return new Promise(resolve => image.onload = () => resolve(image.width));
}

async function getImagesWidth(images) {
  return Promise.all(images.map(img => getImageWidthAfterLoad(images[img])));
}

$.fn.adjustSize = function (index) {
  var zoomLevel = $(this).val();
  $("#gallery-blobs-left-" + index + ", #gallery-blobs-right-" + index).children('img').each(function (i) {
    if ($(this)[0].naturalHeight != 0 || $(this)[0].naturalWidth != 0) {
      $(this).height($(this)[0].naturalHeight * zoomLevel);
      $(this).width($(this)[0].naturalWidth * zoomLevel);
    }
  });
}

// Set gallery min-height to avoid jump loops.
$.fn.setGalleryMinHeight = function (sources) {
  var minHeight;
  var selector = $(this);
  if (!Array.isArray(sources)) {
    sources = Object.keys(sources).map(function (e) {
      return sources[e];
    });
  }
  for (let i = 0; i < sources.length; i++) {
    let tmpImg = new Image();
    tmpImg.src = sources[i];
    $(tmpImg).on("load", function () {
      minHeight = parseInt(selector.css("min-height"));
      if (minHeight < tmpImg.height) {
        minHeight = tmpImg.height < 580 ? tmpImg.height : 580;
        selector.css({ minHeight: minHeight + 20 + "px" });
      }
    });
  }
};

$.fn.appendCompare = function (galleryIndex, rightItems, imgContainerRight) {
  $(this).append("
<input type=checkbox id=compare-btn-gallery-" + galleryIndex + ">Compare
");
  $("#compare-btn-gallery-" + galleryIndex).on('click', function () {
    if ($(this).is(":checked")) {
      $("#gallery-blobs-left-" + galleryIndex).css({ "flex-basis": "50%" });
      $("#gallery-blobs-right-" + galleryIndex).css({ "flex-basis": "50%" });
      $(".gallery-blobs-container-" + galleryIndex).addClass('blobs-wrapper-compare');
    } else {
      $("#gallery-blobs-left-" + galleryIndex).css({ "flex-basis": "" });
      $("#gallery-blobs-right-" + galleryIndex).css({ "flex-basis": "" });
      $(".gallery-blobs-container-" + galleryIndex).removeClass("blobs-wrapper-compare");
    }
    $("#" + imgContainerRight).toggleClass("di-none");
    $(".gallery-" + galleryIndex).toggleClass("space-between");
    $("." + rightItems).toggleClass("di-none");
  });
}

$.fn.appendLabel = function (labelArray) {
  var html = "";
  for (var i = 0; i < labelArray.length; i++) html += labelArray[i];
  if (html.charAt(0) == "'") html = eval(html);
  $(this).html("
" + html + "
");
}

function scrollSync(index) {
  var isSyncingLeftScroll = false;
  var isSyncingRightScroll = false;
  var leftDiv = document.getElementById('gallery-blobs-left-' + index);
  var rightDiv = document.getElementById('gallery-blobs-right-' + index);
  $('#gallery-blobs-left-' + index).attachDragger();
  $('#gallery-blobs-right-' + index).attachDragger();

  leftDiv.onscroll = function () {
    if (!isSyncingLeftScroll) {
      isSyncingRightScroll = true;
      rightDiv.scrollTop = this.scrollTop;
      rightDiv.scrollLeft = this.scrollLeft;
    }
    isSyncingLeftScroll = false;
  }
  rightDiv.onscroll = function () {
    if (!isSyncingRightScroll) {
      isSyncingLeftScroll = true;
      leftDiv.scrollTop = this.scrollTop;
      leftDiv.scrollLeft = this.scrollLeft;
    }
    isSyncingRightScroll = false;
  }
}<|endoftext|>


<commit_before><commit_msg>Updated descendants functions to find descendants properly. Survey now supports sub contingent questions.<commit_after><|endoftext|>


<commit_before>////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////Builds the HTML Table from ElectionIDs

function buildElectionTable() {

	 var configs = JSON.parse(configRaw);
	 var address = configs.address;
	 var electionConf = JSON.parse(electionConfigRaw);
	 var elections = electionConf.elections;

	 //don't use port 80 if it's not deployed
	 if(electionConf.deployment === false){
		 address = address+":"+electionConf["nginx-port"];
	 }

	 document.getElementById("elections").innerHTML = "";

	 var head$ = $('
');
	 head$.append($('').html(" Election IDs "));
	 head$.append($('').html(" Election Title "));
	 head$.append($('').html(" Election State "));
	 head$.append($('').html(" Starting Time "));
	 head$.append($('').html(" Ending Time "));
  $("#elections").append(head$);

  for (var i = 0 ; i < elections.length ; i++) {
 	 var elecID = elections[i].electionID;
 	 var elecStatus = 'waiting';

      var row$ = $('
');

      row$.append($('').html("   "+elections[i].electionID+"    "));
      row$.append($('').html("   "+elections[i].electionTitle+"   "));
      row$.append($('').html("   "+elecStatus+"   "));
      row$.append($('').html("   "+elections[i].startTime.substring(0, elections[i].startTime.length-9)+"   "));
      row$.append($('').html("   "+elections[i].endTime.substring(0, elections[i].endTime.length-9)+"   "));
      $("#elections").append(row$);

      getElectionStatus(elecID, function (eleID, stat){
 		 document.getElementById(eleID).innerHTML = stat;
 	 });
  }

  function getElectionStatus(eleID, callback) {
      // Detemine the status of the system: (not-yet) open/closed,
      // by quering the final mix server.
      // Depending on the state, either the voting tab or the
      // verification tab will be opened.
      //
      // The state is detemined in a (too?) simple way, by
      // checking if the final server has ready result.
      //
 	 var stat = 'what';
 	 var url = address+'/'+eleID+'/mix/03/status';
      $.get(url)
       .fail(function () {
          var stat = 'no response';
          callback(eleID, stat)
        })
       .done(function (result) {  // we have some response
          var stat = 'pending';
     	 if (result.status==='result ready'){
         	 stat = 'closed';
          }
          else {
         	 stat = 'ready';
          }
          callback(eleID, stat)
        });

  }

  // Returns a promise of the state of the final mix server
  // The promise resolves to true if the result is ready and
  // to false otherwise.
  // The promise is rejected if the final server is down of
  // works for a different election.
  //
  function resultOfFinalServerReady(eleID) {
 	 var url = address+'/'+eleID+'/mix/03/status';
      $.get(url)
       .fail(function () {
          return 'pending';
        })
       .done(function (result) {  // we have some response
          if (result.electionID.substring(0, 5).toUpperCase() !== electionID.toUpperCase()) {
              reject('wrong election ID')
          }
          else if (result.status==='result ready'){
         		 return 'closed';
          }
          else {return 'ready';}
        });
  }

  ////////////////////////////////////////////////////////////////
  ///////// Refresh State

	/* Refresh */
  var electionStates = window.setInterval(function() {
 	 for (var i = 0 ; i < elections.length ; i++) {
     	 var elecID = elections[i].electionID;
     	 getElectionStatus(elecID, function (eleID, stat){
     		 document.getElementById(eleID).innerHTML = stat;
     	 });
 	 }
  }, 1000);

  $('#welcome').show();

}<commit_msg>changed order of table columns<commit_after>////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////Builds the HTML Table from ElectionIDs

function buildElectionTable() {

	 var configs = JSON.parse(configRaw);
	 var address = configs.address;
	 var electionConf = JSON.parse(electionConfigRaw);
	 var elections = electionConf.elections;

	 //don't use port 80 if it's not deployed
	 if(electionConf.deployment === false){
		 address = address+":"+electionConf["nginx-port"];
	 }

	 document.getElementById("elections").innerHTML = "";

	 var head$ = $('
');
	 head$.append($('').html(" Election IDs "));
	 head$.append($('').html(" Election Title "));
	 head$.append($('').html(" Starting Time "));
	 head$.append($('').html(" Ending Time "));
	 head$.append($('').html(" Election State "));
  $("#elections").append(head$);

  for (var i = 0 ; i < elections.length ; i++) {
 	 var elecID = elections[i].electionID;
 	 var elecStatus = 'waiting';

      var row$ = $('
');

      row$.append($('').html("   "+elections[i].electionID+"    "));
      row$.append($('').html("   "+elections[i].electionTitle+"   "));
      row$.append($('').html("   "+elections[i].startTime.substring(0, elections[i].startTime.length-9)+"   "));
      row$.append($('').html("   "+elections[i].endTime.substring(0, elections[i].endTime.length-9)+"   "));
      row$.append($('').html("   "+elecStatus+"   "));
      $("#elections").append(row$);

      getElectionStatus(elecID, function (eleID, stat){
 		 document.getElementById(eleID).innerHTML = stat;
 	 });
  }

  function getElectionStatus(eleID, callback) {
      // Detemine the status of the system: (not-yet) open/closed,
      // by quering the final mix server.
      // Depending on the state, either the voting tab or the
      // verification tab will be opened.
      //
      // The state is detemined in a (too?) simple way, by
      // checking if the final server has ready result.
      //
 	 var stat = 'what';
 	 var url = address+'/'+eleID+'/mix/03/status';
      $.get(url)
       .fail(function () {
          var stat = 'no response';
          callback(eleID, stat)
        })
       .done(function (result) {  // we have some response
          var stat = 'pending';
     	 if (result.status==='result ready'){
         	 stat = 'closed';
          }
          else {
         	 stat = 'ready';
          }
          callback(eleID, stat)
        });

  }

  // Returns a promise of the state of the final mix server
  // The promise resolves to true if the result is ready and
  // to false otherwise.
  // The promise is rejected if the final server is down of
  // works for a different election.
  //
  function resultOfFinalServerReady(eleID) {
 	 var url = address+'/'+eleID+'/mix/03/status';
      $.get(url)
       .fail(function () {
          return 'pending';
        })
       .done(function (result) {  // we have some response
          if (result.electionID.substring(0, 5).toUpperCase() !== electionID.toUpperCase()) {
              reject('wrong election ID')
          }
          else if (result.status==='result ready'){
         		 return 'closed';
          }
          else {return 'ready';}
        });
  }

  ////////////////////////////////////////////////////////////////
  ///////// Refresh State

	/* Refresh */
  var electionStates = window.setInterval(function() {
 	 for (var i = 0 ; i < elections.length ; i++) {
     	 var elecID = elections[i].electionID;
     	 getElectionStatus(elecID, function (eleID, stat){
     		 document.getElementById(eleID).innerHTML = stat;
     	 });
 	 }
  }, 1000);

  $('#welcome').show();

}<|endoftext|>


<commit_before>/**
  Artist Graph Model

  Bridge between Spotify's models.Artist model and
  the vis.Graph object.
*/
var ArtistGraph = function(element, artist, config) {

  // the DOM element where the canvas should be put on
  // to be later on passed to the vis.Graph object
  this.element = element;

  // Spotify's models.Artist object
  this.artist = artist;
  // id of the node to be passed on to the vis.Graph object
  this.artist.nodeid = 1;

  // ArtistGraph Events
  this.events = {};
  // vis.Graph Events
  this.graphevents = {};

  // load branching value from config if present
  // otherwise, load ArtistGraph.DEFAULT_BRANCHING
  this.branching = (config && config.branching) ||
    ArtistGraph.DEFAULT_BRANCHING;
  // load depth value from config if present
  // otherwise, load ArtistGraph.DEFAULT_DEPTH
  this.depth = (config && config.depth) ||
    ArtistGraph.DEFAULT_DEPTH;

  // load treemode value from config if present
  // otherwise, load ArtistGraph.DEFAULT_TREEMODE
  if (config && typeof config.treemode !== 'undefined')
    this.treemode = config.treemode;
  else this.treemode = ArtistGraph.DEFAULT_TREEMODE;

  // options to be passed on to vis.Graph object
  this.options = (config && config.options) ||
    ArtistGraph.DEFAULT_OPTIONS;

  this.initGraph();

  // create the vis.Graph Object
  this.graph =
    new vis.Graph(this.element, this.data, this.options);
};

// Default values to be used to construct the graph
// if no configuration values are specified when
// constructing the ArtistGraph object
ArtistGraph.DEFAULT_BRANCHING = 4;
ArtistGraph.DEFAULT_DEPTH = 2;
ArtistGraph.DEFAULT_TREEMODE = true;
ArtistGraph.DEFAULT_OPTIONS = {};

// ArtistGraph.colors = {
//   node: {
//     border: "white",
//     background: "black",
//     highlight: {
//       border: "white",
//       background: "black"
//     }
//   },
//   edge: {
//     border: "white",
//     background: "black",
//     highlight: {
//       border: "white",
//       background: "black"
//     }
//   }
// };

ArtistGraph.prototype = {

  // initiates state properties of the graph
  initGraph: function() {
    // list of related artist of the main artist of the graph
    this.relatedArtists = [];

    // extra edges that are missing from the graph in treemode form
    this.extraEdges = [];

    // current id value for the vis.Graph's nodes
    this.currentNodeId = 1;

    // data object to be passed on to the vis.Graph object
    this.data = {
      nodes: [{
        id: this.currentNodeId,
        label: this.artist.name,
        fontColor: '#313336', // TODO refactor colors
        color: {
          background: '#dfe0e6',
          highlight: {
            border: '#7fb701'
          }
        },

        // artist and isLeaf are helper properties
        // for future reference
        artist: this.artist,
        // isLeaf simply indicates if the node is a leaf
        // in the graph or not
        isLeaf: false,
        // is this the root node?
        isRoot: true
      }],
      edges: []
    };
  },

  // alias to initGraph
  reset: function() {
    this.initGraph();
  },

  // Resets state variables and starts constructing the graph
  buildGraph: function(done) {
    // Current number of iterations (recursive calls)
    // done to construct the graph
    var currentIteration = 1;

    // Maximum number of iterations that will be performed
    // to construct the graph.
    this.maxIterations =
      (function lambda(i) {
      return Math.pow(this.branching, i) +
        (i < this.depth ? lambda.bind(this)(i + 1) : 0);
    }).bind(this)(0);

    // maxIterations is equal to:
    //
    //   d
    //   ∑ b^i
    //  i=0
    //
    //  which is the sum of the branching value to the power
    //  of i, given that i goes from zero to the depth value.
    //
    //   depth
    //    ___
    //   |
    //    \
    //    /    branching ^ i
    //   |___
    //   i = 0

    // start constructing the graph recursively
    this.expandNode(
      this.depth - 1,
      this.artist,
      iterationUpdate.bind(this)
    );

    function iterationUpdate() {
      // Update the number of iterations done and
      currentIteration += this.branching;

      // If the number of iterations done is enough to have the
      // full graph constructed, then stop recursion and
      // draw the final graph.
      if (currentIteration >= this.maxIterations) {
        this.drawGraph(true);
        if (done)
          done();
      }
    }
  },

  // Expands the node of the parent artist by this.branching.
  // It recursively decreases the depth parameter.
  // The update parameter is the callback to be called
  // after all the callbacks of the child nodes of the root node
  // have finished.
  expandNode: function(depth, parentArtist, done) {

    // after expanding, the node will stop being a leaf
    var node = this.getNode(parentArtist);

    if (node)
      node.isLeaf = false;

    // load the related artists property
    parentArtist.load('related').done(this, function(parentArtist) {
      // when done loading, load the current snapshot of the array
      // of artists, with this.branching length
      parentArtist.related
        .snapshot(0, this.branching).done(this, function(snapshot) {
          // when done loading, load name and uri properties
          // of each artist in the snapshot
          snapshot.loadAll(['name', 'uri'])
          // call forEachRelated on each artist
          .each(this, forEachRelated)
          // when done on each artist update the number of iterations
          .done(done);
        });
    });

    // Updates the graph given the artist parameter.
    // This function will be called on each child node
    // (artist parameter) of the root node.
    // note: this means that this function will be called
    //       exactly this.branching times.
    function forEachRelated(childArtist) {
      // Try to find repeated nodes in the graph
      // given the name of the artist is the same
      var duplicated = this.getNode(childArtist);

      // Is the artist node already in the graph?
      // If there is a duplicate then create an
      // edge between the two artists:
      // the child artist and parent artist
      if (duplicated) {

        // try to find repeated edges in the graph
        var edgeExists = _.findWhere(this.data.edges, {
          to: duplicated.id,
          from: parentArtist.nodeid
        });
        // try to find repeated edges (even if inverse)
        var inverseEdgeExists = _.findWhere(this.data.edges, {
          from: duplicated.id,
          to: parentArtist.nodeid
        });

        // if the edge we are trying to insert
        // does not exist in the graph yet AND
        // the two nodes connecting the edge are not the same one
        // then insert edge.
        //
        // The latter test was added after metadata errors were found:
        // sometimes, an artist would exist itself in its related
        // artists list, which created a edge that went from it to
        // itself.
        if (!edgeExists && !inverseEdgeExists &&
          childArtist.uri !== parentArtist.uri) {

          // Create the extra edge.
          var extraEdge = {
            from: parentArtist.nodeid,
            to: duplicated.id,
          };
          this.extraEdges.push(extraEdge);

          // The extra edge concept is related to the treemode of
          // the graph:
          // If treemode is ENABLED, then the extra edges
          // are NOT added to the graph. This causes the graph to
          // have less edges (only one pass through) and therefore
          // the graph creation algorithm is one of a tree creation
          // algorithm, which, as expected, creates a tree.
          //
          // Otherwise, if treemode is DISABLED, then all the
          // possible edges will be added to the vis.Graph object,
          // which means that the graph will not be a tree,
          // with a much higher number of edges.
          if (!this.treemode)
            this.data.edges.push(extraEdge);
        }
      }
      // if the node is new/unique to the graph
      else {

        // then add it to the list of nodes
        this.data.nodes.push({
          id: ++this.currentNodeId,
          label: childArtist.name,
          artist: childArtist,
          // if the depth value of the graph is zero
          // then this is most definitely a leaf node
          isLeaf: depth <= 0
        });

        // also create the edge to connect the new node to
        // its parent
        this.data.edges.push({
          from: parentArtist.nodeid,
          to: this.currentNodeId
        });

        this.relatedArtists.push(childArtist);

        childArtist.nodeid = this.currentNodeId;
      }

      // if a leaf node as not been reached, then continue
      // constructing the graph, now with this child node
      // as the root node
      if (depth > 0)
        this.expandNode(depth - 1, childArtist, done);
      // note: the condition to end the recursion is: if depth <= 0
    }

  },

  // what it says...
  drawGraph: function(debug) {
    // binds all the graph events previously declared to the object.
    this.bindAllGraphEvents();

    // sets the previously computed graph data
    // note: no animation starts at this point
    this.graph.setData(this.data, {
      disableStart: true
    });

    // starts the animation to draw the graph
    this.graph.start();

    // updates the tagsmenu UI component
    this.events.updateTagsMenu();

    // Debug information about the graph creation
    if (debug) {
      console.log('#### Stats for ' + this.artist.name);
      console.log('# iterations: ' + this.maxIterations);
      console.log('# nodes: ' + this.data.nodes.length);
      console.log('# edges: ' + this.data.edges.length);
    }

  },
  redrawGraph: function() {
    this.graph.redraw();
  },

  // Updates the graph with the given config object
  // it is expected that config is defined
  updateGraph: function(config, done) {
    this.branching = config.branching || this.branching;
    this.depth = config.depth || this.depth;

    if (typeof config.treemode != 'undefined')
      this.treemode = config.treemode;

    this.reset();
    this.buildGraph(done);
  },

  // Refresh vis.Graph's data objects
  updateData: function() {
    this.graph.nodesData.update(this.data.nodes);
    this.graph.edgesData.update(this.data.edges);

    this.events.updateTagsMenu();
  },
  updateNodes: function() {
    this.graph.nodesData.update(this.data.nodes);
  },

  // highlights the artist's node
  highlightNode: function(artist) {
    this.getNode(artist).color = {
      border: '#7fb701',
      background: '#313336'
    };
  },

  // Get the node of the given artist.
  // return undefined if not found.
  getNode: function(artist) {
    return _.findWhere(
      this.data.nodes, {
        label: artist.name
      }
    );
  },

  // Events

  // binds the previously saved vis.Graph's events to the
  // graph object
  bindAllGraphEvents: function() {
    for (var event in this.graphevents) {
      this.graph.on(event, this.graphevents[event]);
    }
  },

  // saves the given event, given the proper eventHandler.
  on: function(event, eventHandler) {
    this.events[event] = eventHandler;
  },

  // saves a vis.Graph event, given the proper eventHandler.
  onGraph: function(event, eventHandler) {
    this.graphevents[event] = eventHandler;
  }
};

ArtistGraph.prototype.constructor = ArtistGraph;

// Exports for the spotify's require system
require(['$api/models'], function(_models) {
  exports.ArtistGraph = ArtistGraph;
});<commit_msg>weird bugfix o0<commit_after>/**
  Artist Graph Model

  Bridge between Spotify's models.Artist model and
  the vis.Graph object.
*/
var ArtistGraph = function(element, artist, config) {

  // the DOM element where the canvas should be put on
  // to be later on passed to the vis.Graph object
  this.element = element;

  // Spotify's models.Artist object
  this.artist = artist;
  // id of the node to be passed on to the vis.Graph object
  this.artist.nodeid = 1;

  // ArtistGraph Events
  this.events = {};
  // vis.Graph Events
  this.graphevents = {};

  // load branching value from config if present
  // otherwise, load ArtistGraph.DEFAULT_BRANCHING
  this.branching = (config && config.branching) ||
    ArtistGraph.DEFAULT_BRANCHING;
  // load depth value from config if present
  // otherwise, load ArtistGraph.DEFAULT_DEPTH
  this.depth = (config && config.depth) ||
    ArtistGraph.DEFAULT_DEPTH;

  // load treemode value from config if present
  // otherwise, load ArtistGraph.DEFAULT_TREEMODE
  if (config && typeof config.treemode !== 'undefined')
    this.treemode = config.treemode;
  else this.treemode = ArtistGraph.DEFAULT_TREEMODE;

  // options to be passed on to vis.Graph object
  this.options = (config && config.options) ||
    ArtistGraph.DEFAULT_OPTIONS;

  this.initGraph();

  // create the vis.Graph Object
  this.graph =
    new vis.Graph(this.element, this.data, this.options);
};

// Default values to be used to construct the graph
// if no configuration values are specified when
// constructing the ArtistGraph object
ArtistGraph.DEFAULT_BRANCHING = 4;
ArtistGraph.DEFAULT_DEPTH = 2;
ArtistGraph.DEFAULT_TREEMODE = true;
ArtistGraph.DEFAULT_OPTIONS = {};

// ArtistGraph.colors = {
//   node: {
//     border: "white",
//     background: "black",
//     highlight: {
//       border: "white",
//       background: "black"
//     }
//   },
//   edge: {
//     border: "white",
//     background: "black",
//     highlight: {
//       border: "white",
//       background: "black"
//     }
//   }
// };

ArtistGraph.prototype = {

  // initiates state properties of the graph
  initGraph: function() {
    // list of related artist of the main artist of the graph
    this.relatedArtists = [];

    // extra edges that are missing from the graph in treemode form
    this.extraEdges = [];

    // current id value for the vis.Graph's nodes
    this.currentNodeId = 1;

    // data object to be passed on to the vis.Graph object
    this.data = {
      nodes: [{
        id: this.currentNodeId,
        label: this.artist.name,
        fontColor: '#313336', // TODO refactor colors
        color: {
          background: '#dfe0e6',
          highlight: {
            border: '#7fb701'
          }
        },

        // artist and isLeaf are helper properties
        // for future reference
        artist: this.artist,
        // isLeaf simply indicates if the node is a leaf
        // in the graph or not
        isLeaf: false,
        // is this the root node?
        isRoot: true
      }],
      edges: []
    };
  },

  // alias to initGraph
  reset: function() {
    this.initGraph();
  },

  // Resets state variables and starts constructing the graph
  buildGraph: function(done) {
    // Current number of iterations (recursive calls)
    // done to construct the graph
    var currentIteration = 1;

    // Maximum number of iterations that will be performed
    // to construct the graph.
    this.maxIterations =
      (function lambda(i) {
      return Math.pow(this.branching, i) +
        (i < this.depth ? lambda.bind(this)(i + 1) : 0);
    }).bind(this)(0);

    // maxIterations is equal to:
    //
    //   d
    //   ∑ b^i
    //  i=0
    //
    //  which is the sum of the branching value to the power
    //  of i, given that i goes from zero to the depth value.
    //
    //   depth
    //    ___
    //   |
    //    \
    //    /    branching ^ i
    //   |___
    //   i = 0

    // start constructing the graph recursively
    this.expandNode(
      this.depth - 1,
      this.artist,
      iterationUpdate.bind(this)
    );

    function iterationUpdate() {
      // Update the number of iterations done and
      currentIteration += this.branching;

      // If the number of iterations done is enough to have the
      // full graph constructed, then stop recursion and
      // draw the final graph.
      if (currentIteration >= this.maxIterations) {
        this.drawGraph(true);
        if (done)
          done();
      }
    }
  },

  // Expands the node of the parent artist by this.branching.
  // It recursively decreases the depth parameter.
  // The update parameter is the callback to be called
  // after all the callbacks of the child nodes of the root node
  // have finished.
  expandNode: function(depth, parentArtist, done) {

    // after expanding, the node will stop being a leaf
    var node = this.getNode(parentArtist);

    if (node)
      node.isLeaf = false;

    // load the related artists property
    parentArtist.load('related').done(this, function(parentArtist) {
      // when done loading, load the current snapshot of the array
      // of artists, with this.branching length
      parentArtist.related
        .snapshot(0, this.branching).done(this, function(snapshot) {
          // when done loading, load name and uri properties
          // of each artist in the snapshot
          snapshot.loadAll(['name', 'uri'])
          // call forEachRelated on each artist
          .each(this, forEachRelated)
          // when done on each artist update the number of iterations
          .done(done);
        });
    });

    // Updates the graph given the artist parameter.
    // This function will be called on each child node
    // (artist parameter) of the root node.
    // note: this means that this function will be called
    //       exactly this.branching times.
    function forEachRelated(childArtist) {
      // Try to find repeated nodes in the graph
      // given the name of the artist is the same
      var duplicated = this.getNode(childArtist);

      // Is the artist node already in the graph?
      // If there is a duplicate then create an
      // edge between the two artists:
      // the child artist and parent artist
      if (duplicated) {

        // try to find repeated edges in the graph
        var edgeExists = _.findWhere(this.data.edges, {
          to: duplicated.id,
          from: parentArtist.nodeid
        });
        // try to find repeated edges (even if inverse)
        var inverseEdgeExists = _.findWhere(this.data.edges, {
          from: duplicated.id,
          to: parentArtist.nodeid
        });

        // if the edge we are trying to insert
        // does not exist in the graph yet AND
        // the two nodes connecting the edge are not the same one
        // then insert edge.
        //
        // The latter test was added after metadata errors were found:
        // sometimes, an artist would exist itself in its related
        // artists list, which created a edge that went from it to
        // itself.
        if (!edgeExists && !inverseEdgeExists &&
          childArtist.uri !== parentArtist.uri) {

          // Create the extra edge.
          var extraEdge = {
            from: parentArtist.nodeid,
            to: duplicated.id,
          };
          this.extraEdges.push(extraEdge);

          // The extra edge concept is related to the treemode of
          // the graph:
          // If treemode is ENABLED, then the extra edges
          // are NOT added to the graph. This causes the graph to
          // have less edges (only one pass through) and therefore
          // the graph creation algorithm is one of a tree creation
          // algorithm, which, as expected, creates a tree.
          //
          // Otherwise, if treemode is DISABLED, then all the
          // possible edges will be added to the vis.Graph object,
          // which means that the graph will not be a tree,
          // with a much higher number of edges.
          if (!this.treemode)
            this.data.edges.push(extraEdge);
        }
      }
      // if the node is new/unique to the graph
      else {

        // then add it to the list of nodes
        this.data.nodes.push({
          id: ++this.currentNodeId,
          label: childArtist.name,
          artist: childArtist,
          // if the depth value of the graph is zero
          // then this is most definitely a leaf node
          isLeaf: depth <= 0
        });

        // also create the edge to connect the new node to
        // its parent
        this.data.edges.push({
          from: parentArtist.nodeid,
          to: this.currentNodeId
        });

        this.relatedArtists.push(childArtist);

        childArtist.nodeid = this.currentNodeId;
      }

      // if a leaf node as not been reached, then continue
      // constructing the graph, now with this child node
      // as the root node
      if (depth > 0)
        this.expandNode(depth - 1, childArtist, done);
      // note: the condition to end the recursion is: if depth <= 0
    }

  },

  // what it says...
  drawGraph: function(debug) {
    // binds all the graph events previously declared to the object.
    this.bindAllGraphEvents();

    // sets the previously computed graph data
    // note: no animation starts at this point
    this.graph.setData(this.data, {
      disableStart: true
    });

    // starts the animation to draw the graph
    this.graph.start();

    // updates the tagsmenu UI component
    this.events.updateTagsMenu();

    // Debug information about the graph creation
    if (debug) {
      console.log('#### Stats for ' + this.artist.name);
      console.log('# iterations: ' + this.maxIterations);
      console.log('# nodes: ' + this.data.nodes.length);
      console.log('# edges: ' + this.data.edges.length);
    }

  },
  redrawGraph: function() {
    this.graph.redraw();
  },

  // Updates the graph with the given config object
  // it is expected that config is defined
  updateGraph: function(config, done) {
    this.branching = config.branching || this.branching;
    this.depth = config.depth || this.depth;

    if (typeof config.treemode != 'undefined')
      this.treemode = config.treemode;

    this.reset();
    this.buildGraph(done);
  },

  // Refresh vis.Graph's data objects
  updateData: function() {
    this.graph.nodesData.update(this.data.nodes);
    this.graph.edgesData.update(this.data.edges);

    this.events.updateTagsMenu();
  },
  updateNodes: function() {
    this.graph.nodesData.update(this.data.nodes);
  },

  // highlights the artist's node
  highlightNode: function(artist) {
    this.getNode(artist).color = {
      border: '#7fb701',
      background: '#313336'
    };
  },

  // Get the node of the given artist.
  // return undefined if not found.
  getNode: function(artist) {
    return _.findWhere(
      this.data.nodes, {
        id: artist.nodeid
      }
    );
  },

  // Events

  // binds the previously saved vis.Graph's events to the
  // graph object
  bindAllGraphEvents: function() {
    for (var event in this.graphevents) {
      this.graph.on(event, this.graphevents[event]);
    }
  },

  // saves the given event, given the proper eventHandler.
  on: function(event, eventHandler) {
    this.events[event] = eventHandler;
  },

  // saves a vis.Graph event, given the proper eventHandler.
  onGraph: function(event, eventHandler) {
    this.graphevents[event] = eventHandler;
  }
};

ArtistGraph.prototype.constructor = ArtistGraph;

// Exports for the spotify's require system
require(['$api/models'], function(_models) {
  exports.ArtistGraph = ArtistGraph;
});<|endoftext|>


<commit_before>/***
|''Requires''|TiddlySpaceConfig TiddlySpaceUserControls chrjs|
!HTMLForm
<form action="#">
	<fieldset>
		<legend />

Space Name:

<input type="text" name="space" />

Space Selection:

				<select>
					<option></option>
				</select>

		<input type="submit" />
	</fieldset>
</form>
!Code
***/
//{{{
(function($) {

var host = config.extensions.tiddlyweb.host;
var currentSpace = config.extensions.tiddlyspace.currentSpace.name;

var macro = config.macros.TiddlySpaceSubscription = {
	formTemplate: store.getTiddlerText(tiddler.title + "##HTMLForm"),
	locale: {
		addPassiveLabel: "Add subscription",
		addActiveLabel: "Subscribe",
		passiveDesc: "Add subscription to current space",
		activeDesc: "Subscribe current space to existing space",
		addSuccess: "added subscription for %0 to %1",
		listError: "error retrieving subscriptions for space %0: %1",
		forbiddenError: "unauthorized to modify space %0",
		noSpaceError: "space %0 does not exist",
		conflictError: "space %0 is already subscribed to %1"
	},

	handler: function(place, macroName, params, wikifier, paramString, tiddler) {
		// passive mode means subscribing given space to current space
		// active mode means subscribing current space to given space
		var mode = params[0] || "list";
		var form = $(this.formTemplate).
			find(".annotation").hide().end();
		if(mode == "passive") {
			form.submit(function(ev) { return macro.onSubmit(this, mode); }).
				find("._active").remove().end().
				find("legend").text(this.locale.addPassiveLabel).end().
				find(".description").text(this.locale.passiveDesc).end().
				find("[type=submit]").val(this.locale.addPassiveLabel).end().
				appendTo(place);
		} else if(mode == "active") {
			form.submit(function(ev) { return macro.onSubmit(this, mode); }).
				find("._passive").remove().end().
				find("legend").text(this.locale.addActiveLabel).end().
				find(".description").text(this.locale.activeDesc).end().
				find("[type=submit]").val(this.locale.addActiveLabel).end().
				appendTo(place);
			this.populateSpaces(form);
		} else {
			var container = $("
").appendTo(place);
			this.listSubscriptions(container);
		}
	},
	listSubscriptions: function(container) {
		var recipe = new tiddlyweb.Recipe(currentSpace + "_public", host);
		recipe.get(function(recipe, status, xhr) {
			var subscriptions = $.map(recipe.recipe, function(item, i) { // TODO: refactor to canonicalize; move to TiddlySpaceConfig!?
				var arr = item[0].split("_public");
				return (arr[0] != currentSpace && arr[1] === "") ? arr[0] : null;
			});
			var items = $.map(subscriptions, function(item, i) { // TODO: DRY (cf. displayMembers)
				var btn = $('').text(item);
					//attr("title", macro.locale.delTooltip). // TODO
					//click(this.onClick); // TODO
				return $("
").append(btn)[0];
			});
			$("
").append(items).appendTo(container);
		}, function(xhr, error, exc) {
			displayMessage(macro.locale.listError.format([currentSpace, error]));
		});
	},
	populateSpaces: function(form) { // TODO: rename?
		$.ajax({ // TODO: add to model/space.js?
			url: host + "/spaces?mine=1",
			type: "GET",
			success: function(data, status, xhr) {
				var spaces = $.map(data, function(item, i) {
					return $("<option />", { value: item.name }).text(item.name)[0];
				});
				$("select", form).append(spaces);
			} // TODO: error handling?
		});
	},
	onSubmit: function(el, mode) {
		var form = $(el).closest("form");
		var selector = mode == "passive" ? "[name=space]" : "select";
		var space = form.find(selector).val();
		var provider = mode == "passive" ? space : currentSpace;
		var subscriber = mode == "passive" ? currentSpace : space;
		this.subscribe(provider, subscriber, function(xhr, error, exc) {
			var ctx = {
				msg: {
					403: macro.locale.forbiddenError.format([subscriber]),
					404: macro.locale.noSpaceError.format([subscriber]), // XXX: only relevant for passive mode? -- XXX: could also be provider!?
					409: macro.locale.conflictError.format([subscriber, provider]) // TODO: distinguish between cases non-existing and already subscribed
				},
				form: form,
				selector: selector
			};
			config.macros.TiddlySpaceLogin.displayError(xhr, error, exc, ctx);
		});
		return false;
	},
	subscribe: function(provider, subscriber, errback) {
		var data = { subscriptions: [provider] };
		$.ajax({ // TODO: add to model/space.js?
			url: host + "/spaces/" + subscriber,
			type: "POST",
			contentType: "application/json",
			data: $.toJSON(data),
			success: function(data, status, xhr) {
				displayMessage(macro.locale.addSuccess.format([provider,
					subscriber]));
			},
			error: errback
		});
	}
};

})(jQuery);
//}}}
<commit_msg>added client-side support for unsubscribing from spaces<commit_after>/***
|''Requires''|TiddlySpaceConfig TiddlySpaceUserControls chrjs|
!HTMLForm
<form action="#">
	<fieldset>
		<legend />

Space Name:

<input type="text" name="space" />

Space Selection:

				<select>
					<option></option>
				</select>

		<input type="submit" />
	</fieldset>
</form>
!Code
***/
//{{{
(function($) {

var host = config.extensions.tiddlyweb.host;
var currentSpace = config.extensions.tiddlyspace.currentSpace.name;

var macro = config.macros.TiddlySpaceSubscription = {
	formTemplate: store.getTiddlerText(tiddler.title + "##HTMLForm"),
	locale: {
		addPassiveLabel: "Add subscription",
		addActiveLabel: "Subscribe",
		passiveDesc: "Add subscription to current space",
		activeDesc: "Subscribe current space to existing space",
		addSuccess: "added subscription for %0 to %1",
		delPrompt: "Are you sure you want to remove subscription %0?",
		delTooltip: "click to remove subscription",
		delError: "error removing subscription %0: %1",
		listError: "error retrieving subscriptions for space %0: %1",
		forbiddenError: "unauthorized to modify space %0",
		noSpaceError: "space %0 does not exist",
		conflictError: "space %0 is already subscribed to %1"
	},

	handler: function(place, macroName, params, wikifier, paramString, tiddler) {
		// passive mode means subscribing given space to current space
		// active mode means subscribing current space to given space
		var mode = params[0] || "list";
		var form = $(this.formTemplate).
			find(".annotation").hide().end();
		if(mode == "passive") {
			form.submit(function(ev) { return macro.onSubmit(this, mode); }).
				find("._active").remove().end().
				find("legend").text(this.locale.addPassiveLabel).end().
				find(".description").text(this.locale.passiveDesc).end().
				find("[type=submit]").val(this.locale.addPassiveLabel).end().
				appendTo(place);
		} else if(mode == "active") {
			form.submit(function(ev) { return macro.onSubmit(this, mode); }).
				find("._passive").remove().end().
				find("legend").text(this.locale.addActiveLabel).end().
				find(".description").text(this.locale.activeDesc).end().
				find("[type=submit]").val(this.locale.addActiveLabel).end().
				appendTo(place);
			this.populateSpaces(form);
		} else {
			var container = $("
").appendTo(place);
			this.listSubscriptions(container);
		}
	},
	listSubscriptions: function(container) {
		var recipe = new tiddlyweb.Recipe(currentSpace + "_public", host);
		recipe.get(function(recipe, status, xhr) {
			var subscriptions = $.map(recipe.recipe, function(item, i) { // TODO: refactor to canonicalize; move to TiddlySpaceConfig!?
				var arr = item[0].split("_public");
				return (arr[0] != currentSpace && arr[1] === "") ? arr[0] : null;
			});
			var items = $.map(subscriptions, function(item, i) { // TODO: DRY (cf. displayMembers)
				var btn = $('').text(item).
					attr("title", macro.locale.delTooltip).
					click(macro.onClick);
				return $("
").append(btn)[0];
			});
			$("
").append(items).appendTo(container);
		}, function(xhr, error, exc) {
			displayMessage(macro.locale.listError.format([currentSpace, error]));
		});
	},
	populateSpaces: function(form) { // TODO: rename?
		$.ajax({ // TODO: add to model/space.js?
			url: host + "/spaces?mine=1",
			type: "GET",
			success: function(data, status, xhr) {
				var spaces = $.map(data, function(item, i) {
					return $("<option />", { value: item.name }).text(item.name)[0];
				});
				$("select", form).append(spaces);
			} // TODO: error handling?
		});
	},
	onSubmit: function(el, mode) {
		var form = $(el).closest("form");
		var selector = mode == "passive" ? "[name=space]" : "select";
		var space = form.find(selector).val();
		var provider = mode == "passive" ? space : currentSpace;
		var subscriber = mode == "passive" ? currentSpace : space;
		var callback = function(data, status, xhr) {
			displayMessage(macro.locale.addSuccess.format([provider, subscriber]));
			// TODO: refresh list or reload page
		};
		var errback = function(xhr, error, exc) {
			var ctx = {
				msg: {
					403: macro.locale.forbiddenError.format([subscriber]),
					404: macro.locale.noSpaceError.format([subscriber]), // XXX: only relevant for passive mode? -- XXX: could also be provider!?
					409: macro.locale.conflictError.format([subscriber, provider]) // TODO: distinguish between cases non-existing and already subscribed
				},
				form: form,
				selector: selector
			};
			config.macros.TiddlySpaceLogin.displayError(xhr, error, exc, ctx);
		};
		this.subscribe(provider, subscriber, callback, errback);
		return false;
	},
	onClick: function(ev) { // XXX: ambiguous; rename
		var btn = $(this);
		var provider = btn.text();
		var msg = macro.locale.delPrompt.format([provider]);
		var callback = function(data, status, xhr) {
			btn.closest("li").slideUp(function(ev) { $(this).remove(); });
		};
		var errback = function(xhr, error, exc) { // XXX: doesn't actually happen
			displayMessage(macro.locale.delError.format([username, error]));
		};
		if(confirm(msg)) {
			macro.subscribe(provider, currentSpace, callback, errback, true);
		}
	},
	subscribe: function(provider, subscriber, callback, errback, unsubscribe) {
		var data = {};
		var key = unsubscribe ? "unsubscriptions" : "subscriptions";
		data[key] = [provider];
		$.ajax({ // TODO: add to model/space.js?
			url: host + "/spaces/" + subscriber,
			type: "POST",
			contentType: "application/json",
			data: $.toJSON(data),
			success: callback,
			error: errback
		});
	}
};

})(jQuery);
//}}}
<|endoftext|>


<commit_before>var expect = require('expect');
var should = require('should');
var request = require('superagent');

var GrommetE2EUtils = require('../src/utils/test/GrommetE2EUtils');

var options = {
  desiredCapabilities: {
    browserName: 'phantomjs'
  }
};

if (process.env.TRAVIS) {
  options = GrommetE2EUtils.getSauceLabsCIOptions(process.env.SAUCE_USERNAME,
    process.env.SAUCE_ACCESS_KEY);
}

var browser = GrommetE2EUtils.getBrowserClient(options);

var requestData = {
  sauceUsername: process.env.SAUCE_USERNAME,
  sauceAccessKey: process.env.SAUCE_ACCESS_KEY,
  failed: false
};

describe('Docs website e2e', function() {
  this.timeout(50000);

  beforeEach(function(done) {
    GrommetE2EUtils.configureBrowser(browser, 'http://localhost:8000/docs/',
      requestData, done);
  });

  it('loads the title for the docs home page', function(done) {
    browser
      .getTitle(function(err, title) {
        if (err) {
          should.fail('Expected the title to be returned.', err);
        }
        expect(title).toBe('Grommet - User Experience for the Enterprise');
        done();
      });
  });

  it('downloads hpi general adobe illustrator sticker sheet', function(done) {
    var selector = '.header .menu a:nth-child(1)';

    browser.getText(selector, function (err, text) {
      if (err) {
        should.fail('Expected to find design link.', err);
      }
      expect(text).toBe('Design');
      var buttonSelector = '.background-color-index-neutral-1 a:nth-child(1)';
      browser.click(selector).getText(buttonSelector).then(function(text) {
        expect(text).toBe('Resources');

        var aiSelector = '#hpi #hpi-ai-general';
        browser.click(buttonSelector).getText(aiSelector).then(function(text) {
          expect(text).toBe('Download General Sticker Sheet');
          browser.getAttribute(aiSelector, 'href').then(function(linkPath) {
            request.get(linkPath).end(function(err, res) {
              if (err) {
                should.fail('Expected to send a request to ai asset.', err);
              }
              expect(res.status).toBe(200);
              done();
            });
          });
        });
      });
    });
  });

  it('downloads hpi general adobe illustrator pdf sticker sheet', function(done) {
    var selector = '.header .menu a:nth-child(1)';

    browser.getText(selector, function (err, text) {
      if (err) {
        should.fail('Expected to find design link.', err);
      }
      expect(text).toBe('Design');
      var buttonSelector = '.background-color-index-neutral-1 a:nth-child(1)';
      browser.click(selector).getText(buttonSelector).then(function(text) {
        expect(text).toBe('Resources');

        var pdfSelector = '#hpi #hpi-ai-general-pdf';
        browser.click(buttonSelector).getText(pdfSelector).then(function(text) {
          expect(text).toBe('pdf');
          browser.getAttribute(pdfSelector, 'href').then(function(linkPath) {
            request.get(linkPath).end(function(err, res) {
              if (err) {
                should.fail('Expected to send a request to pdf asset.', err);
              }
              expect(res.status).toBe(200);
              done();
            });
          });
        });
      });
    });
  });

  it('downloads hpe general adobe illustrator sticker sheet', function(done) {
    var selector = '.header .menu a:nth-child(1)';

    browser.getText(selector, function (err, text) {
      if (err) {
        should.fail('Expected to find design link.', err);
      }
      expect(text).toBe('Design');
      var buttonSelector = '.background-color-index-neutral-1 a:nth-child(1)';
      browser.click(selector).getText(buttonSelector).then(function(text) {
        expect(text).toBe('Resources');

        var aiSelector = '#hpe #hpe-ai-general';
        browser.click(buttonSelector).getText(aiSelector).then(function(text) {
          expect(text).toBe('Download General Sticker Sheet');
          browser.getAttribute(aiSelector, 'href').then(function(linkPath) {
            request.get(linkPath).end(function(err, res) {
              if (err) {
                should.fail('Expected to send a request to ai asset.', err);
              }
              expect(res.status).toBe(200);
              done();
            });
          });
        });
      });
    });
  });

  it('downloads hpe general adobe illustrator pdf sticker sheet', function(done) {
    var selector = '.header .menu a:nth-child(1)';

    browser.getText(selector, function (err, text) {
      if (err) {
        should.fail('Expected to find design link.', err);
      }
      expect(text).toBe('Design');
      var buttonSelector = '.background-color-index-neutral-1 a:nth-child(1)';
      browser.click(selector).getText(buttonSelector).then(function(text) {
        expect(text).toBe('Resources');

        var pdfSelector = '#hpe #hpe-ai-general-pdf';
        browser.click(buttonSelector).getText(pdfSelector).then(function(text) {
          expect(text).toBe('pdf');
          browser.getAttribute(pdfSelector, 'href').then(function(linkPath) {
            request.get(linkPath).end(function(err, res) {
              if (err) {
                should.fail('Expected to send a request to pdf asset.', err);
              }
              expect(res.status).toBe(200);
              done();
            });
          });
        });
      });
    });
  });

  it('downloads hpe classic adobe illustrator sticker sheet', function(done) {
    var selector = '.header .menu a:nth-child(1)';

    browser.getText(selector, function (err, text) {
      if (err) {
        should.fail('Expected to find design link.', err);
      }
      expect(text).toBe('Design');
      var buttonSelector = '.background-color-index-neutral-1 a:nth-child(1)';
      browser.click(selector).getText(buttonSelector).then(function(text) {
        expect(text).toBe('Resources');

        var aiSelector = '#hpe #hpe-ai-classic';
        browser.click(buttonSelector).getText(aiSelector).then(function(text) {
          expect(text).toBe('Download Classic Sticker Sheet');
          browser.getAttribute(aiSelector, 'href').then(function(linkPath) {
            request.get(linkPath).end(function(err, res) {
              if (err) {
                should.fail('Expected to send a request to ai asset.', err);
              }
              expect(res.status).toBe(200);
              done();
            });
          });
        });
      });
    });
  });

  it('downloads hpe classic adobe illustrator pdf sticker sheet', function(done) {
    var selector = '.header .menu a:nth-child(1)';

    browser.getText(selector, function (err, text) {
      if (err) {
        should.fail('Expected to find design link.', err);
      }
      expect(text).toBe('Design');
      var buttonSelector = '.background-color-index-neutral-1 a:nth-child(1)';
      browser.click(selector).getText(buttonSelector).then(function(text) {
        expect(text).toBe('Resources');

        var pdfSelector = '#hpe #hpe-ai-classic-pdf';
        browser.click(buttonSelector).getText(pdfSelector).then(function(text) {
          expect(text).toBe('pdf');
          browser.getAttribute(pdfSelector, 'href').then(function(linkPath) {
            request.get(linkPath).end(function(err, res) {
              if (err) {
                should.fail('Expected to send a request to pdf asset.', err);
              }
              expect(res.status).toBe(200);
              done();
            });
          });
        });
      });
    });
  });

  it('downloads hpe app-templates adobe illustrator sticker sheet', function(done) {
    var selector = '.header .menu a:nth-child(1)';

    browser.getText(selector, function (err, text) {
      if (err) {
        should.fail('Expected to find design link.', err);
      }
      expect(text).toBe('Design');
      var buttonSelector = '.background-color-index-neutral-1 a:nth-child(1)';
      browser.click(selector).getText(buttonSelector).then(function(text) {
        expect(text).toBe('Resources');

        var aiSelector = '#hpe #hpe-ai-app-templates';
        browser.click(buttonSelector).getText(aiSelector).then(function(text) {
          expect(text).toBe('Download App Templates Sticker Sheet');
          browser.getAttribute(aiSelector, 'href').then(function(linkPath) {
            request.get(linkPath).end(function(err, res) {
              if (err) {
                should.fail('Expected to send a request to ai asset.', err);
              }
              expect(res.status).toBe(200);
              done();
            });
          });
        });
      });
    });
  });

  it('downloads hpe app-templates adobe illustrator pdf sticker sheet', function(done) {
    var selector = '.header .menu a:nth-child(1)';

    browser.getText(selector, function (err, text) {
      if (err) {
        should.fail('Expected to find design link.', err);
      }
      expect(text).toBe('Design');
      var buttonSelector = '.background-color-index-neutral-1 a:nth-child(1)';
      browser.click(selector).getText(buttonSelector).then(function(text) {
        expect(text).toBe('Resources');

        var pdfSelector = '#hpe #hpe-ai-app-templates-pdf';
        browser.click(buttonSelector).getText(pdfSelector).then(function(text) {
          expect(text).toBe('pdf');
          browser.getAttribute(pdfSelector, 'href').then(function(linkPath) {
            request.get(linkPath).end(function(err, res) {
              if (err) {
                should.fail('Expected to send a request to pdf asset.', err);
              }
              expect(res.status).toBe(200);
              done();
            });
          });
        });
      });
    });
  });

  it('downloads hpe icons adobe illustrator sticker sheet', function(done) {
    var selector = '.header .menu a:nth-child(1)';

    browser.getText(selector, function (err, text) {
      if (err) {
        should.fail('Expected to find design link.', err);
      }
      expect(text).toBe('Design');
      var buttonSelector = '.background-color-index-neutral-1 a:nth-child(1)';
      browser.click(selector).getText(buttonSelector).then(function(text) {
        expect(text).toBe('Resources');

        var aiSelector = '#hpe #hpe-ai-icons';
        browser.click(buttonSelector).getText(aiSelector).then(function(text) {
          expect(text).toBe('Download Icons Sticker Sheet');
          browser.getAttribute(aiSelector, 'href').then(function(linkPath) {
            request.get(linkPath).end(function(err, res) {
              if (err) {
                should.fail('Expected to send a request to ai asset.', err);
              }
              expect(res.status).toBe(200);
              done();
            });
          });
        });
      });
    });
  });

  it('downloads hpe icons adobe illustrator pdf sticker sheet', function(done) {
    var selector = '.header .menu a:nth-child(1)';

    browser.getText(selector, function (err, text) {
      if (err) {
        should.fail('Expected to find design link.', err);
      }
      expect(text).toBe('Design');
      var buttonSelector = '.background-color-index-neutral-1 a:nth-child(1)';
      browser.click(selector).getText(buttonSelector).then(function(text) {
        expect(text).toBe('Resources');

        var pdfSelector = '#hpe #hpe-ai-icons-pdf';
        browser.click(buttonSelector).getText(pdfSelector).then(function(text) {
          expect(text).toBe('pdf');
          browser.getAttribute(pdfSelector, 'href').then(function(linkPath) {
            request.get(linkPath).end(function(err, res) {
              if (err) {
                should.fail('Expected to send a request to pdf asset.', err);
              }
              expect(res.status).toBe(200);
              done();
            });
          });
        });
      });
    });
  });

  it('downloads hpe sketch sticker sheet', function(done) {
    var selector = '.header .menu a:nth-child(1)';

    browser.getText(selector, function (err, text) {
      if (err) {
        should.fail('Expected to find design link.', err);
      }
      expect(text).toBe('Design');
      var buttonSelector = '.background-color-index-neutral-1 a:nth-child(1)';
      browser.click(selector).getText(buttonSelector).then(function(text) {
        expect(text).toBe('Resources');

        var sketchSelector = '#hpe #hpe-sk-general';
        browser.click(buttonSelector).getText(sketchSelector).then(function(text) {
          expect(text).toBe('Download Sticker Sheet');
          browser.getAttribute(sketchSelector, 'href').then(function(linkPath) {
            request.get(linkPath).end(function(err, res) {
              if (err) {
                should.fail('Expected to send a request to sketch asset.', err);
              }
              expect(res.status).toBe(200);
              done();
            });
          });
        });
      });
    });
  });

  it('downloads hpe axure stencils', function(done) {
    var selector = '.header .menu a:nth-child(1)';

    browser.getText(selector, function (err, text) {
      if (err) {
        should.fail('Expected to find design link.', err);
      }
      expect(text).toBe('Design');
      var buttonSelector = '.background-color-index-neutral-1 a:nth-child(1)';
      browser.click(selector).getText(buttonSelector).then(function(text) {
        expect(text).toBe('Resources');

        var axureSelector = '#hpe #hpe-ax-general';
        browser.click(buttonSelector).getText(axureSelector).then(function(text) {
          expect(text).toBe('Download Stencils');
          browser.getAttribute(axureSelector, 'href').then(function(linkPath) {
            request.get(linkPath).end(function(err, res) {
              if (err) {
                should.fail('Expected to send a request to axure asset.', err);
              }
              expect(res.status).toBe(200);
              done();
            });
          });
        });
      });
    });
  });

  it('downloads hpe balsamiq assets', function(done) {
    var selector = '.header .menu a:nth-child(1)';

    browser.getText(selector, function (err, text) {
      if (err) {
        should.fail('Expected to find design link.', err);
      }
      expect(text).toBe('Design');
      var buttonSelector = '.background-color-index-neutral-1 a:nth-child(1)';
      browser.click(selector).getText(buttonSelector).then(function(text) {
        expect(text).toBe('Resources');

        var balsamiqSelector = '#hpe #hpe-bq-general';
        browser.click(buttonSelector).getText(balsamiqSelector).then(function(text) {
          expect(text).toBe('Download Assets');
          browser.getAttribute(balsamiqSelector, 'href').then(function(linkPath) {
            request.get(linkPath).end(function(err, res) {
              if (err) {
                should.fail('Expected to send a request to balsamiq asset.', err);
              }
              expect(res.status).toBe(200);
              done();
            });
          });
        });
      });
    });
  });

  afterEach(function(done) {
    if (process.env.TRAVIS) {
      requestData.state = this.currentTest.state;
      GrommetE2EUtils.reportResultsToSauceLabs(done, requestData);
    } else {
      done();
    }
  });

  after(function(done) {
    browser.end(done);
  });
});
<commit_msg>updated link names to match updated page<commit_after>var expect = require('expect');
var should = require('should');
var request = require('superagent');

var GrommetE2EUtils = require('../src/utils/test/GrommetE2EUtils');

var options = {
  desiredCapabilities: {
    browserName: 'phantomjs'
  }
};

if (process.env.TRAVIS) {
  options = GrommetE2EUtils.getSauceLabsCIOptions(process.env.SAUCE_USERNAME,
    process.env.SAUCE_ACCESS_KEY);
}

var browser = GrommetE2EUtils.getBrowserClient(options);

var requestData = {
  sauceUsername: process.env.SAUCE_USERNAME,
  sauceAccessKey: process.env.SAUCE_ACCESS_KEY,
  failed: false
};

describe('Docs website e2e', function() {
  this.timeout(50000);

  beforeEach(function(done) {
    GrommetE2EUtils.configureBrowser(browser, 'http://localhost:8000/docs/',
      requestData, done);
  });

  it('loads the title for the docs home page', function(done) {
    browser
      .getTitle(function(err, title) {
        if (err) {
          should.fail('Expected the title to be returned.', err);
        }
        expect(title).toBe('Grommet - User Experience for the Enterprise');
        done();
      });
  });

  it('downloads hpi general adobe illustrator sticker sheet', function(done) {
    var selector = '.header .menu a:nth-child(1)';

    browser.getText(selector, function (err, text) {
      if (err) {
        should.fail('Expected to find design link.', err);
      }
      expect(text).toBe('Design');
      var buttonSelector = '.background-color-index-neutral-1 a:nth-child(1)';
      browser.click(selector).getText(buttonSelector).then(function(text) {
        expect(text).toBe('Resources');

        var aiSelector = '#hpi #hpi-ai-general';
        browser.click(buttonSelector).getText(aiSelector).then(function(text) {
          expect(text).toBe('General Sticker Sheet');
          browser.getAttribute(aiSelector, 'href').then(function(linkPath) {
            request.get(linkPath).end(function(err, res) {
              if (err) {
                should.fail('Expected to send a request to ai asset.', err);
              }
              expect(res.status).toBe(200);
              done();
            });
          });
        });
      });
    });
  });

  it('downloads hpi general adobe illustrator pdf sticker sheet', function(done) {
    var selector = '.header .menu a:nth-child(1)';

    browser.getText(selector, function (err, text) {
      if (err) {
        should.fail('Expected to find design link.', err);
      }
      expect(text).toBe('Design');
      var buttonSelector = '.background-color-index-neutral-1 a:nth-child(1)';
      browser.click(selector).getText(buttonSelector).then(function(text) {
        expect(text).toBe('Resources');

        var pdfSelector = '#hpi #hpi-ai-general-pdf';
        browser.click(buttonSelector).getText(pdfSelector).then(function(text) {
          expect(text).toBe('pdf');
          browser.getAttribute(pdfSelector, 'href').then(function(linkPath) {
            request.get(linkPath).end(function(err, res) {
              if (err) {
                should.fail('Expected to send a request to pdf asset.', err);
              }
              expect(res.status).toBe(200);
              done();
            });
          });
        });
      });
    });
  });

  it('downloads hpe general adobe illustrator sticker sheet', function(done) {
    var selector = '.header .menu a:nth-child(1)';

    browser.getText(selector, function (err, text) {
      if (err) {
        should.fail('Expected to find design link.', err);
      }
      expect(text).toBe('Design');
      var buttonSelector = '.background-color-index-neutral-1 a:nth-child(1)';
      browser.click(selector).getText(buttonSelector).then(function(text) {
        expect(text).toBe('Resources');

        var aiSelector = '#hpe #hpe-ai-general';
        browser.click(buttonSelector).getText(aiSelector).then(function(text) {
          expect(text).toBe('General Sticker Sheet');
          browser.getAttribute(aiSelector, 'href').then(function(linkPath) {
            request.get(linkPath).end(function(err, res) {
              if (err) {
                should.fail('Expected to send a request to ai asset.', err);
              }
              expect(res.status).toBe(200);
              done();
            });
          });
        });
      });
    });
  });

  it('downloads hpe general adobe illustrator pdf sticker sheet', function(done) {
    var selector = '.header .menu a:nth-child(1)';

    browser.getText(selector, function (err, text) {
      if (err) {
        should.fail('Expected to find design link.', err);
      }
      expect(text).toBe('Design');
      var buttonSelector = '.background-color-index-neutral-1 a:nth-child(1)';
      browser.click(selector).getText(buttonSelector).then(function(text) {
        expect(text).toBe('Resources');

        var pdfSelector = '#hpe #hpe-ai-general-pdf';
        browser.click(buttonSelector).getText(pdfSelector).then(function(text) {
          expect(text).toBe('pdf');
          browser.getAttribute(pdfSelector, 'href').then(function(linkPath) {
            request.get(linkPath).end(function(err, res) {
              if (err) {
                should.fail('Expected to send a request to pdf asset.', err);
              }
              expect(res.status).toBe(200);
              done();
            });
          });
        });
      });
    });
  });

  it('downloads hpe classic adobe illustrator sticker sheet', function(done) {
    var selector = '.header .menu a:nth-child(1)';

    browser.getText(selector, function (err, text) {
      if (err) {
        should.fail('Expected to find design link.', err);
      }
      expect(text).toBe('Design');
      var buttonSelector = '.background-color-index-neutral-1 a:nth-child(1)';
      browser.click(selector).getText(buttonSelector).then(function(text) {
        expect(text).toBe('Resources');

        var aiSelector = '#hpe #hpe-ai-classic';
        browser.click(buttonSelector).getText(aiSelector).then(function(text) {
          expect(text).toBe('Classic Sticker Sheet');
          browser.getAttribute(aiSelector, 'href').then(function(linkPath) {
            request.get(linkPath).end(function(err, res) {
              if (err) {
                should.fail('Expected to send a request to ai asset.', err);
              }
              expect(res.status).toBe(200);
              done();
            });
          });
        });
      });
    });
  });

  it('downloads hpe classic adobe illustrator pdf sticker sheet', function(done) {
    var selector = '.header .menu a:nth-child(1)';

    browser.getText(selector, function (err, text) {
      if (err) {
        should.fail('Expected to find design link.', err);
      }
      expect(text).toBe('Design');
      var buttonSelector = '.background-color-index-neutral-1 a:nth-child(1)';
      browser.click(selector).getText(buttonSelector).then(function(text) {
        expect(text).toBe('Resources');

        var pdfSelector = '#hpe #hpe-ai-classic-pdf';
        browser.click(buttonSelector).getText(pdfSelector).then(function(text) {
          expect(text).toBe('pdf');
          browser.getAttribute(pdfSelector, 'href').then(function(linkPath) {
            request.get(linkPath).end(function(err, res) {
              if (err) {
                should.fail('Expected to send a request to pdf asset.', err);
              }
              expect(res.status).toBe(200);
              done();
            });
          });
        });
      });
    });
  });

  it('downloads hpe app-templates adobe illustrator sticker sheet', function(done) {
    var selector = '.header .menu a:nth-child(1)';

    browser.getText(selector, function (err, text) {
      if (err) {
        should.fail('Expected to find design link.', err);
      }
      expect(text).toBe('Design');
      var buttonSelector = '.background-color-index-neutral-1 a:nth-child(1)';
      browser.click(selector).getText(buttonSelector).then(function(text) {
        expect(text).toBe('Resources');

        var aiSelector = '#hpe #hpe-ai-app-templates';
        browser.click(buttonSelector).getText(aiSelector).then(function(text) {
          expect(text).toBe('Application Templates');
          browser.getAttribute(aiSelector, 'href').then(function(linkPath) {
            request.get(linkPath).end(function(err, res) {
              if (err) {
                should.fail('Expected to send a request to ai asset.', err);
              }
              expect(res.status).toBe(200);
              done();
            });
          });
        });
      });
    });
  });

  it('downloads hpe app-templates adobe illustrator pdf sticker sheet', function(done) {
    var selector = '.header .menu a:nth-child(1)';

    browser.getText(selector, function (err, text) {
      if (err) {
        should.fail('Expected to find design link.', err);
      }
      expect(text).toBe('Design');
      var buttonSelector = '.background-color-index-neutral-1 a:nth-child(1)';
      browser.click(selector).getText(buttonSelector).then(function(text) {
        expect(text).toBe('Resources');

        var pdfSelector = '#hpe #hpe-ai-app-templates-pdf';
        browser.click(buttonSelector).getText(pdfSelector).then(function(text) {
          expect(text).toBe('pdf');
          browser.getAttribute(pdfSelector, 'href').then(function(linkPath) {
            request.get(linkPath).end(function(err, res) {
              if (err) {
                should.fail('Expected to send a request to pdf asset.', err);
              }
              expect(res.status).toBe(200);
              done();
            });
          });
        });
      });
    });
  });

  it('downloads hpe icons adobe illustrator sticker sheet', function(done) {
    var selector = '.header .menu a:nth-child(1)';

    browser.getText(selector, function (err, text) {
      if (err) {
        should.fail('Expected to find design link.', err);
      }
      expect(text).toBe('Design');
      var buttonSelector = '.background-color-index-neutral-1 a:nth-child(1)';
      browser.click(selector).getText(buttonSelector).then(function(text) {
        expect(text).toBe('Resources');

        var aiSelector = '#hpe #hpe-ai-icons';
        browser.click(buttonSelector).getText(aiSelector).then(function(text) {
          expect(text).toBe('Icon Sticker Sheet');
          browser.getAttribute(aiSelector, 'href').then(function(linkPath) {
            request.get(linkPath).end(function(err, res) {
              if (err) {
                should.fail('Expected to send a request to ai asset.', err);
              }
              expect(res.status).toBe(200);
              done();
            });
          });
        });
      });
    });
  });

  it('downloads hpe icons adobe illustrator pdf sticker sheet', function(done) {
    var selector = '.header .menu a:nth-child(1)';

    browser.getText(selector, function (err, text) {
      if (err) {
        should.fail('Expected to find design link.', err);
      }
      expect(text).toBe('Design');
      var buttonSelector = '.background-color-index-neutral-1 a:nth-child(1)';
      browser.click(selector).getText(buttonSelector).then(function(text) {
        expect(text).toBe('Resources');

        var pdfSelector = '#hpe #hpe-ai-icons-pdf';
        browser.click(buttonSelector).getText(pdfSelector).then(function(text) {
          expect(text).toBe('pdf');
          browser.getAttribute(pdfSelector, 'href').then(function(linkPath) {
            request.get(linkPath).end(function(err, res) {
              if (err) {
                should.fail('Expected to send a request to pdf asset.', err);
              }
              expect(res.status).toBe(200);
              done();
            });
          });
        });
      });
    });
  });

  it('downloads hpe sketch sticker sheet', function(done) {
    var selector = '.header .menu a:nth-child(1)';

    browser.getText(selector, function (err, text) {
      if (err) {
        should.fail('Expected to find design link.', err);
      }
      expect(text).toBe('Design');
      var buttonSelector = '.background-color-index-neutral-1 a:nth-child(1)';
      browser.click(selector).getText(buttonSelector).then(function(text) {
        expect(text).toBe('Resources');

        var sketchSelector = '#hpe #hpe-sk-general';
        browser.click(buttonSelector).getText(sketchSelector).then(function(text) {
          expect(text).toBe('Sketch Sticker Sheet');
          browser.getAttribute(sketchSelector, 'href').then(function(linkPath) {
            request.get(linkPath).end(function(err, res) {
              if (err) {
                should.fail('Expected to send a request to sketch asset.', err);
              }
              expect(res.status).toBe(200);
              done();
            });
          });
        });
      });
    });
  });

  it('downloads hpe axure stencils', function(done) {
    var selector = '.header .menu a:nth-child(1)';

    browser.getText(selector, function (err, text) {
      if (err) {
        should.fail('Expected to find design link.', err);
      }
      expect(text).toBe('Design');
      var buttonSelector = '.background-color-index-neutral-1 a:nth-child(1)';
      browser.click(selector).getText(buttonSelector).then(function(text) {
        expect(text).toBe('Resources');

        var axureSelector = '#hpe #hpe-ax-general';
        browser.click(buttonSelector).getText(axureSelector).then(function(text) {
          expect(text).toBe('Axure Stencils');
          browser.getAttribute(axureSelector, 'href').then(function(linkPath) {
            request.get(linkPath).end(function(err, res) {
              if (err) {
                should.fail('Expected to send a request to axure asset.', err);
              }
              expect(res.status).toBe(200);
              done();
            });
          });
        });
      });
    });
  });

  it('downloads hpe balsamiq assets', function(done) {
    var selector = '.header .menu a:nth-child(1)';

    browser.getText(selector, function (err, text) {
      if (err) {
        should.fail('Expected to find design link.', err);
      }
      expect(text).toBe('Design');
      var buttonSelector = '.background-color-index-neutral-1 a:nth-child(1)';
      browser.click(selector).getText(buttonSelector).then(function(text) {
        expect(text).toBe('Resources');

        var balsamiqSelector = '#hpe #hpe-bq-general';
        browser.click(buttonSelector).getText(balsamiqSelector).then(function(text) {
          expect(text).toBe('Balsamiq Assets');
          browser.getAttribute(balsamiqSelector, 'href').then(function(linkPath) {
            request.get(linkPath).end(function(err, res) {
              if (err) {
                should.fail('Expected to send a request to balsamiq asset.', err);
              }
              expect(res.status).toBe(200);
              done();
            });
          });
        });
      });
    });
  });

  afterEach(function(done) {
    if (process.env.TRAVIS) {
      requestData.state = this.currentTest.state;
      GrommetE2EUtils.reportResultsToSauceLabs(done, requestData);
    } else {
      done();
    }
  });

  after(function(done) {
    browser.end(done);
  });
});
<|endoftext|>