<commit_before>var capitalizeFirstLetter = null;
var sort = null;
var data = {};
var curKey = "";
var curOrder = "country";
var timestamp = null;
var prevNav = $("#nav-mobile").children().first();
function sortBtnClickHandler() {
  if (!$.isEmptyObject(data)) {
    curOrder === "percentage" ? curOrder = "country" : curOrder = "percentage";
    sort(data[curKey], curOrder);
  }
}
function navClickHandler(e) {
  if (!$.isEmptyObject(data)) {
    if (prevNav) {
      prevNav.removeClass("active");
    }
    var nav = $(e).parent();
    nav.addClass("active");
    prevNav = nav;
    curKey = e.innerText.toLowerCase();
    curOrder = "country";
    sort(data[curKey], "country");
    $("#timestamp").text(timestamp + ", " + capitalizeFirstLetter(curKey));
  }
}
$.ajax({
  url: "/awwwards-analysis/data/result (nominees).csv",
}).then(function(d, stat, xhr) {
  timestamp = xhr.getResponseHeader("Last-Modified");
  /* main */
  function analyze(error, nominees, honorable, developer, sotd, sotm, soty) {
    if (error) {
      console.log(error);
    }
    $("#preloader").addClass("hidden");
    $("#tip").removeClass("hidden");
    $("#tipTriangle").removeClass("hidden");
    function parseData(d) {
      return {
        country: d.Country,
        percentage: +d.Percentage,
        submission: +d.Submission,
        population: +d.Population,
      };
    }
    data["nominees"] = nominees.map(parseData);
    data["honorable mention"] = honorable.map(parseData);
    data["developer award"] = developer.map(parseData);
    data["site of the day"] = sotd.map(parseData);
    data["site of the month"] = sotm.map(parseData);
    data["site of the year"] = soty.map(parseData);
    curKey = "nominees";
    function numberWithCommas(x) {
      return x.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",");
    }
    capitalizeFirstLetter = function(string) {
      return str.replace(/\w\S*/g, function(text) { return text.charAt(0).toUpperCase() + text.substr(1).toLowerCase();});
    }
    sort = function(data, order) {
      if (order === "country") {
        data = data.sort(function(a, b) {
          if (a.country > b.country) {
            return 1;
          } else if (a.country < b.country) {
            return -1;
          }
          return 0;
        });
      } else {
        data = data.sort(function(a, b) {
          return b.percentage - a.percentage;
        });
      }
      update(data, false);
    }
    // Add responsive support
    window.addEventListener("resize", function(event) {
      update(data, false);
    });
    var prevIdx = 0;
    function mousemove(el, data, xScale, yScale, mapXCoordToCountry, mapCountryToIdx) {
      // Invert x coordinate
      var country = mapXCoordToCountry(d3.mouse(el)[0]);
      var x = xScale(country);
      var idx = mapCountryToIdx(country);
      var y = yScale(data[idx].percentage);
      // Calculating the boundary index to rotate the tip
      var xAxisInterval = width / data.length;
      let sum = 0, lastIdx = data.length - 1;
      while(sum < 190) {
        sum += xAxisInterval;
        lastIdx--;
      }
      g.select(".guideline")
          .transition()
          .duration(50)
          .attr("x1", x)
          .attr("x2", x);
      g.selectAll("circle")
          .filter(function(d, i) { return i === prevIdx })
          .transition()
          .duration(50)
          .style("fill", "white")
          .attr("r", 3.5);
      g.selectAll("circle")
          .filter(function(d, i) { return i === idx })
          .transition()
          .duration(50)
          .style("fill", "#0f747f")
          .attr("r", 5);
      g.selectAll(".x-axis-text")
          .filter(function(d, i) { return i === prevIdx })
          .transition()
          .duration(50)
          .style("font-size", "10px");
      g.selectAll(".x-axis-text")
          .filter(function(d, i) { return i === idx })
          .transition()
          .duration(50)
          .style("font-size", "12px");
      if (idx <= lastIdx) {
        d3.select("#tip")
            .transition()
            .duration(50)
            .style("top", y + margin.top + tipMargin.top + "px")
            .style("left", x + margin.left + tipMargin.left + "px");
        d3.select("#tipTriangle")
            .style("transform", "rotate(45deg)")
            .transition()
            .duration(50)
            .style("bottom", "-35px")
            .style("left", "-15px");
      } else {
        d3.select("#tip")
            .transition()
            .duration(50)
            .style("top", y + margin.top + tipMargin.top + "px")
            .style("left", x - margin.left + "px");
        d3.select("#tipTriangle")
            .style("transform", "rotate(-45deg)")
            .transition()
            .duration(50)
            .style("bottom", "-35px")
            .style("left", "173px");
      }
      d3.select("#country")
          .text(data[idx].country);
      d3.select("#percentage")
          .text(data[idx].percentage.toFixed(11));
      d3.select("#submission")
          .text(data[idx].submission);
      d3.select("#population")
          .text(numberWithCommas(data[idx].population));
      prevIdx = idx;
    }
    function update(data, initialize) {
      var xScale = d3.scalePoint().domain(data.map(function(d) { return d.country; })).range([0, width]);
      var yScale = d3.scaleLinear().domain(d3.extent(data, function(d) { return d.percentage; })).range([height, 0]);
      // Used for mapping x coordinate to ordinal data
      var mapXCoordToCountry = d3.scaleQuantize().domain(xScale.range()).range(xScale.domain());
      // Used for mapping ordinal data to data index
      var mapCountryToIdx = d3.scaleBand().domain(data.map(function(d) { return d.country; })).range([0, data.length]);
      // Set up area, line, gridline
      var area = d3.area()
          .x(function(d) { return xScale(d.country); })
          .y0(height)
          .y1(function(d) { return yScale(d.percentage); })
          .curve(d3.curveMonotoneX);
      var line = d3.line()
          .x(function(d) { return xScale(d.country); })
          .y(function(d) { return yScale(d.percentage); })
          .curve(d3.curveMonotoneX);

      if (initialize) {
        // Draw area
        g.append("path")
            .data([data])
            .attr("class", "area")
            .attr("d", area)
            .style("fill", "url(#gradient)");
        // Draw x-axis
        g.append("g")
            .attr("class", "axis x-axis")
            .attr("transform", "translate(0," + height + ")")
            .call(d3.axisBottom(xScale))
            .selectAll("text")
            .attr("class", "x-axis-text")
            .attr("dx", "-0.8em")
            .attr("dy", "0.15em")
            .style("transform", "rotate(-65deg)")
            .style("text-anchor", "end");
        // Remove x-axis domain
        // g.select(".x-axis")
        //     .selectAll(".domain")
        //     .remove();
        // Draw y-axis
        g.append("g")
            .attr("class", "axis y-axis")
            .call(d3.axisLeft(yScale))
            .selectAll("text")
            .attr("class", "y-axis-text")
        // Draw y-axis label
        g.select(".y-axis")
            .append("text")
            .attr("transform", "rotate(-90)")
            .style("text-anchor", "end")
            .attr("y", "-70")
            .text("Percentage (%)");
        g.select(".y-axis")
            .append("text")
            .attr("id", "timestamp")
            .style("text-anchor", "end")
            .attr("y", "-30")
            .attr("dx", "160")
            .text(timestamp + ", " + capitalizeFirstLetter(curKey));
        // Draw trendline
        g.append("path")
            .datum(data)
            .attr("class", "trendline")
            .attr("d", line);
        // Draw guideline
        g.append("line")
            .attr("class", "guideline")
            .attr("x1", 0)
            .attr("y1", 0)
            .attr("x2", 0)
            .attr("y2", height);
        // Draw circle
        g.selectAll("circle")
            .data(data)
          .enter()
            .append("circle")
            .attr("class", "circle")
            .attr("cx", function(d) { return xScale(d.country); })
            .attr("cy", function(d) { return yScale(d.percentage); })
            .attr("r", 3.5)
            .filter(function(d, i) { return i === 0 })
            .style("fill", "#0f747f")
            .attr("r", 5);
        // Draw a transparent layer for detecting mouse event
        g.append("rect")
            .attr("class", "overlay")
            .attr("width", width)
            .attr("height", height)
            .attr("opacity", 0)
            .on("mousemove", function () {
              mousemove(this, data, xScale, yScale, mapXCoordToCountry, mapCountryToIdx);
            });
      } else {
        g.select(".area")
            .data([data])
            .transition()
            .duration(500)
            .attr("d", area);
        g.select(".trendline")
            .datum(data)
            .transition()
            .duration(500)
            .attr("d", line);
        // Exit circles
        g.selectAll("circle")
            .data(data)
          .exit()
            .remove();
        // Enter circles
        g.selectAll("circle")
            .data(data)
          .enter()
            .append("circle")
            .attr("class", "circle")
            .attr("r", 3.5);
        // Update circles
        g.selectAll("circle")
            .data(data)
            .style("fill", "white")
            .attr("r", 3.5)
            .transition()
            .duration(500)
            .attr("cx", function(d) { return xScale(d.country); })
            .attr("cy", function(d) { return yScale(d.percentage); })
            .filter(function(d, i) { return i === 0 })
            .style("fill", "#0f747f")
            .attr("r", 5);
        g.selectAll(".x-axis")
            .transition()
            .duration(500)
            .call(d3.axisBottom(xScale))
            .selectAll("text")
            .attr("class", "x-axis-text")
            .attr("dx", "-0.8em")
            .attr("dy", "0.15em")
            .style("transform", "rotate(-65deg)")
            .style("text-anchor", "end");
            // .selectAll(".domain")
            // .remove();
        // Update x-axis texts
        g.selectAll(".x-axis-text")
            .style("font-size", "10px")
            .filter(function(d, i) { return i === 0 })
            .style("font-size", "12px");
        g.selectAll(".y-axis")
            .transition()
            .duration(500)
            .call(d3.axisLeft(yScale));
        g.select(".overlay")
            .on("mousemove", function () {
              mousemove(this, data, xScale, yScale, mapXCoordToCountry, mapCountryToIdx);
            });
        prevIdx = 0;
      }
      g.select(".guideline")
          .transition()
          .duration(500)
          .attr("x1", 0)
          .attr("x2", 0);
      d3.select("#tip")
          .transition()
          .duration(500)
          .style("top", yScale(data[0].percentage) + margin.top + tipMargin.top + "px")
          .style("left", xScale(data[0].country) + margin.left + tipMargin.left + "px");
      d3.select("#tipTriangle")
          .style("transform", "rotate(45deg)")
          .transition()
          .duration(50)
          .style("bottom", "-35px")
          .style("left", "-15px");
      d3.select("#country")
          .text(data[0].country);
      d3.select("#percentage")
          .text(data[0].percentage.toFixed(11));
      d3.select("#submission")
          .text(data[0].submission);
      d3.select("#population")
          .text(numberWithCommas(data[0].population));
    }
    // Set up svg
    var svg = d3.select("svg"),
        margin = {
          top: 80,
          right: 80,
          bottom: 135,
          left: 100,
        },
        width = parseInt(d3.select("#chart").style("width")) - margin.left - margin.right,
        height = parseInt(d3.select("#chart").style("height")) - margin.top - margin.bottom;
    var g = svg.append("g")
        .attr("transform", "translate(" + margin.left + "," + margin.top + ")");
    // Set up gradient color
    var gradient = svg.append("defs")
        .append("linearGradient")
        .attr("id", "gradient")
        .attr("x1", "0%")
        .attr("y1", "0%")
        .attr("x2", "0%")
        .attr("y2", "100%")
        .attr("spreadMethod", "pad");
    gradient.append("stop")
        .attr("offset", "0%")
        .attr("stop-color", "rgb(36,175,192)")
        .attr("stop-opacity", 1);
    gradient.append("stop")
        .attr("offset", "100%")
        .attr("stop-color", "rgb(206,254,226)")
        .attr("stop-opacity", 1);
    var tipMargin = {
      top: -60,
      left: 10,
    };
    update(data[curKey], true);
  }
  // Read data
  d3.queue()
    .defer(d3.csv, "./data/result (nominees).csv")
    .defer(d3.csv, "./data/result (honorable).csv")
    .defer(d3.csv, "./data/result (developer).csv")
    .defer(d3.csv, "./data/result (sites_of_the_day).csv")
    .defer(d3.csv, "./data/result (sites_of_the_month).csv")
    .defer(d3.csv, "./data/result (sites_of_the_year).csv")
    .await(analyze);
});
<commit_msg>Testing responsive support<commit_after>var capitalizeFirstLetter = null;
var sort = null;
var data = {};
var curKey = "";
var curOrder = "country";
var timestamp = null;
var prevNav = $("#nav-mobile").children().first();
function sortBtnClickHandler() {
  if (!$.isEmptyObject(data)) {
    curOrder === "percentage" ? curOrder = "country" : curOrder = "percentage";
    sort(data[curKey], curOrder);
  }
}
function navClickHandler(e) {
  if (!$.isEmptyObject(data)) {
    if (prevNav) {
      prevNav.removeClass("active");
    }
    var nav = $(e).parent();
    nav.addClass("active");
    prevNav = nav;
    curKey = e.innerText.toLowerCase();
    curOrder = "country";
    sort(data[curKey], "country");
    $("#timestamp").text(timestamp + ", " + capitalizeFirstLetter(curKey));
  }
}
$.ajax({
  url: "/awwwards-analysis/data/result (nominees).csv",
}).then(function(d, stat, xhr) {
  timestamp = xhr.getResponseHeader("Last-Modified");
  /* main */
  function analyze(error, nominees, honorable, developer, sotd, sotm, soty) {
    if (error) {
      console.log(error);
    }
    $("#preloader").addClass("hidden");
    $("#tip").removeClass("hidden");
    $("#tipTriangle").removeClass("hidden");
    function parseData(d) {
      return {
        country: d.Country,
        percentage: +d.Percentage,
        submission: +d.Submission,
        population: +d.Population,
      };
    }
    data["nominees"] = nominees.map(parseData);
    data["honorable mention"] = honorable.map(parseData);
    data["developer award"] = developer.map(parseData);
    data["site of the day"] = sotd.map(parseData);
    data["site of the month"] = sotm.map(parseData);
    data["site of the year"] = soty.map(parseData);
    curKey = "nominees";
    function numberWithCommas(x) {
      return x.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",");
    }
    capitalizeFirstLetter = function(string) {
      return str.replace(/\w\S*/g, function(text) { return text.charAt(0).toUpperCase() + text.substr(1).toLowerCase();});
    }
    sort = function(data, order) {
      if (order === "country") {
        data = data.sort(function(a, b) {
          if (a.country > b.country) {
            return 1;
          } else if (a.country < b.country) {
            return -1;
          }
          return 0;
        });
      } else {
        data = data.sort(function(a, b) {
          return b.percentage - a.percentage;
        });
      }
      update(data, false);
    }
    // Add responsive support
    window.addEventListener("resize", function(event) {
      update(data, false);
    });
    var prevIdx = 0;
    function mousemove(el, data, xScale, yScale, mapXCoordToCountry, mapCountryToIdx) {
      // Invert x coordinate
      var country = mapXCoordToCountry(d3.mouse(el)[0]);
      var x = xScale(country);
      var idx = mapCountryToIdx(country);
      var y = yScale(data[idx].percentage);
      // Calculating the boundary index to rotate the tip
      var xAxisInterval = width / data.length;
      let sum = 0, lastIdx = data.length - 1;
      while(sum < 190) {
        sum += xAxisInterval;
        lastIdx--;
      }
      g.select(".guideline")
          .transition()
          .duration(50)
          .attr("x1", x)
          .attr("x2", x);
      g.selectAll("circle")
          .filter(function(d, i) { return i === prevIdx })
          .transition()
          .duration(50)
          .style("fill", "white")
          .attr("r", 3.5);
      g.selectAll("circle")
          .filter(function(d, i) { return i === idx })
          .transition()
          .duration(50)
          .style("fill", "#0f747f")
          .attr("r", 5);
      g.selectAll(".x-axis-text")
          .filter(function(d, i) { return i === prevIdx })
          .transition()
          .duration(50)
          .style("font-size", "10px");
      g.selectAll(".x-axis-text")
          .filter(function(d, i) { return i === idx })
          .transition()
          .duration(50)
          .style("font-size", "12px");
      if (idx <= lastIdx) {
        d3.select("#tip")
            .transition()
            .duration(50)
            .style("top", y + margin.top + tipMargin.top + "px")
            .style("left", x + margin.left + tipMargin.left + "px");
        d3.select("#tipTriangle")
            .style("transform", "rotate(45deg)")
            .transition()
            .duration(50)
            .style("bottom", "-35px")
            .style("left", "-15px");
      } else {
        d3.select("#tip")
            .transition()
            .duration(50)
            .style("top", y + margin.top + tipMargin.top + "px")
            .style("left", x - margin.left + "px");
        d3.select("#tipTriangle")
            .style("transform", "rotate(-45deg)")
            .transition()
            .duration(50)
            .style("bottom", "-35px")
            .style("left", "173px");
      }
      d3.select("#country")
          .text(data[idx].country);
      d3.select("#percentage")
          .text(data[idx].percentage.toFixed(11));
      d3.select("#submission")
          .text(data[idx].submission);
      d3.select("#population")
          .text(numberWithCommas(data[idx].population));
      prevIdx = idx;
    }
    function update(data, initialize) {
      var xScale = d3.scalePoint().domain(data.map(function(d) { return d.country; })).range([0, width]);
      var yScale = d3.scaleLinear().domain(d3.extent(data, function(d) { return d.percentage; })).range([height, 0]);
      // Used for mapping x coordinate to ordinal data
      var mapXCoordToCountry = d3.scaleQuantize().domain(xScale.range()).range(xScale.domain());
      // Used for mapping ordinal data to data index
      var mapCountryToIdx = d3.scaleBand().domain(data.map(function(d) { return d.country; })).range([0, data.length]);
      // Set up area, line, gridline
      var area = d3.area()
          .x(function(d) { return xScale(d.country); })
          .y0(height)
          .y1(function(d) { return yScale(d.percentage); })
          .curve(d3.curveMonotoneX);
      var line = d3.line()
          .x(function(d) { return xScale(d.country); })
          .y(function(d) { return yScale(d.percentage); })
          .curve(d3.curveMonotoneX);

      if (initialize) {
        // Draw area
        g.append("path")
            .data([data])
            .attr("class", "area")
            .attr("d", area)
            .style("fill", "url(#gradient)");
        // Draw x-axis
        g.append("g")
            .attr("class", "axis x-axis")
            .attr("transform", "translate(0," + height + ")")
            .call(d3.axisBottom(xScale))
            .selectAll("text")
            .attr("class", "x-axis-text")
            .attr("dx", "-0.8em")
            .attr("dy", "0.15em")
            .style("transform", "rotate(-65deg)")
            .style("text-anchor", "end");
        // Remove x-axis domain
        // g.select(".x-axis")
        //     .selectAll(".domain")
        //     .remove();
        // Draw y-axis
        g.append("g")
            .attr("class", "axis y-axis")
            .call(d3.axisLeft(yScale))
            .selectAll("text")
            .attr("class", "y-axis-text")
        // Draw y-axis label
        g.select(".y-axis")
            .append("text")
            .attr("transform", "rotate(-90)")
            .style("text-anchor", "end")
            .attr("y", "-70")
            .text("Percentage (%)");
        g.select(".y-axis")
            .append("text")
            .attr("id", "timestamp")
            .style("text-anchor", "end")
            .attr("y", "-35")
            .attr("dx", "170")
            .text(timestamp + ", " + capitalizeFirstLetter(curKey));
        // Draw trendline
        g.append("path")
            .datum(data)
            .attr("class", "trendline")
            .attr("d", line);
        // Draw guideline
        g.append("line")
            .attr("class", "guideline")
            .attr("x1", 0)
            .attr("y1", 0)
            .attr("x2", 0)
            .attr("y2", height);
        // Draw circle
        g.selectAll("circle")
            .data(data)
          .enter()
            .append("circle")
            .attr("class", "circle")
            .attr("cx", function(d) { return xScale(d.country); })
            .attr("cy", function(d) { return yScale(d.percentage); })
            .attr("r", 3.5)
            .filter(function(d, i) { return i === 0 })
            .style("fill", "#0f747f")
            .attr("r", 5);
        // Draw a transparent layer for detecting mouse event
        g.append("rect")
            .attr("class", "overlay")
            .attr("width", width)
            .attr("height", height)
            .attr("opacity", 0)
            .on("mousemove", function () {
              mousemove(this, data, xScale, yScale, mapXCoordToCountry, mapCountryToIdx);
            });
      } else {
        g.select(".area")
            .data([data])
            .transition()
            .duration(500)
            .attr("d", area);
        g.select(".trendline")
            .datum(data)
            .transition()
            .duration(500)
            .attr("d", line);
        // Exit circles
        g.selectAll("circle")
            .data(data)
          .exit()
            .remove();
        // Enter circles
        g.selectAll("circle")
            .data(data)
          .enter()
            .append("circle")
            .attr("class", "circle")
            .attr("r", 3.5);
        // Update circles
        g.selectAll("circle")
            .data(data)
            .style("fill", "white")
            .attr("r", 3.5)
            .transition()
            .duration(500)
            .attr("cx", function(d) { return xScale(d.country); })
            .attr("cy", function(d) { return yScale(d.percentage); })
            .filter(function(d, i) { return i === 0 })
            .style("fill", "#0f747f")
            .attr("r", 5);
        g.selectAll(".x-axis")
            .transition()
            .duration(500)
            .call(d3.axisBottom(xScale))
            .selectAll("text")
            .attr("class", "x-axis-text")
            .attr("dx", "-0.8em")
            .attr("dy", "0.15em")
            .style("transform", "rotate(-65deg)")
            .style("text-anchor", "end");
            // .selectAll(".domain")
            // .remove();
        // Update x-axis texts
        g.selectAll(".x-axis-text")
            .style("font-size", "10px")
            .filter(function(d, i) { return i === 0 })
            .style("font-size", "12px");
        g.selectAll(".y-axis")
            .transition()
            .duration(500)
            .call(d3.axisLeft(yScale));
        g.select(".overlay")
            .on("mousemove", function () {
              mousemove(this, data, xScale, yScale, mapXCoordToCountry, mapCountryToIdx);
            });
        prevIdx = 0;
      }
      g.select(".guideline")
          .transition()
          .duration(500)
          .attr("x1", 0)
          .attr("x2", 0);
      d3.select("#tip")
          .transition()
          .duration(500)
          .style("top", yScale(data[0].percentage) + margin.top + tipMargin.top + "px")
          .style("left", xScale(data[0].country) + margin.left + tipMargin.left + "px");
      d3.select("#tipTriangle")
          .style("transform", "rotate(45deg)")
          .transition()
          .duration(50)
          .style("bottom", "-35px")
          .style("left", "-15px");
      d3.select("#country")
          .text(data[0].country);
      d3.select("#percentage")
          .text(data[0].percentage.toFixed(11));
      d3.select("#submission")
          .text(data[0].submission);
      d3.select("#population")
          .text(numberWithCommas(data[0].population));
    }
    // Set up svg
    var svg = d3.select("svg"),
        margin = {
          top: 80,
          right: 80,
          bottom: 135,
          left: 100,
        },
        width = parseInt(d3.select("#chart").style("width")) - margin.left - margin.right,
        height = parseInt(d3.select("#chart").style("height")) - margin.top - margin.bottom;
    var g = svg.append("g")
        .attr("transform", "translate(" + margin.left + "," + margin.top + ")");
    // Set up gradient color
    var gradient = svg.append("defs")
        .append("linearGradient")
        .attr("id", "gradient")
        .attr("x1", "0%")
        .attr("y1", "0%")
        .attr("x2", "0%")
        .attr("y2", "100%")
        .attr("spreadMethod", "pad");
    gradient.append("stop")
        .attr("offset", "0%")
        .attr("stop-color", "rgb(36,175,192)")
        .attr("stop-opacity", 1);
    gradient.append("stop")
        .attr("offset", "100%")
        .attr("stop-color", "rgb(206,254,226)")
        .attr("stop-opacity", 1);
    var tipMargin = {
      top: -60,
      left: 10,
    };
    update(data[curKey], true);
  }
  // Read data
  d3.queue()
    .defer(d3.csv, "./data/result (nominees).csv")
    .defer(d3.csv, "./data/result (honorable).csv")
    .defer(d3.csv, "./data/result (developer).csv")
    .defer(d3.csv, "./data/result (sites_of_the_day).csv")
    .defer(d3.csv, "./data/result (sites_of_the_month).csv")
    .defer(d3.csv, "./data/result (sites_of_the_year).csv")
    .await(analyze);
});
<|endoftext|>


<commit_before>
function Genre(name) {
    this.name = name
    this.items = []

    this.sortItems = function(items) {
        return _.sortBy(items, function(item){
            var badgesSortFactor = _.reduce(item.dict.awards, function(memo, award){
                return memo + ((award.isHighly) ? 10 : 1)
            }, 0)
            return badgesSortFactor
        }).reverse()
    }

    this.render = function(){
        this.items = this.sortItems(this.items)

        var genre$ = $(ich.genreTemplate({name:this.name}, true))

        var renderedItems = []
        var itemsWithoutActivity = []
        for (var i=0;i<this.items.length;i++){
            var thisItem = this.items[i]
            if (thisItem.dict.awards.length) {
                renderedItems.push(thisItem.render())
            }
            else {
                itemsWithoutActivity.push(thisItem.render())
            }
        }
        genre$.find("ul.items.active").append(renderedItems)
        genre$.find("ul.items.inactive").append(itemsWithoutActivity)
        if (itemsWithoutActivity.length) {
            genre$.find("h4.plus-more")
                .toggle(
                    function(){
                        $(this).addClass("showing").siblings("ul.items.inactive").slideDown()
                        $(this).find("span.show-hide").html("(hide)")
                    },
                    function() {
                        $(this).removeClass("showing").siblings("ul.items.inactive").slideUp()
                        $(this).find("span.show-hide").html("(show)")
                    }
                )
                .find("span.value")
                .html(itemsWithoutActivity.length)
        }
        else {
            genre$.find("h4.plus-more").hide()
        }

        return genre$
    }
    return true
}

function GenreList(items) {
    var itemGroupsByGenre = _.groupBy(items, function(item) {
        return item.dict.biblio.genre
    })
    this.genres = _.map(itemGroupsByGenre, function(items, genreName){
        var genre = new Genre(genreName)
        genre.items = items
        return genre
    })

    this.render = function(){

        var renderedGenres = _.chain(this.genres)
            .sortBy("name")
            .map(function(genre){
                return genre.render()
            })
            .value()

        $("div.genre").remove()
        $("div.tooltip").remove() // otherwise tooltips from removed badges stick around
        $("#metrics div.wrapper").append(renderedGenres)
    }
}

function Coll(collViews){
    this.views = collViews;
    this.id = null
    this.items = {}

    this.addItemsFromDicts = function(newItemDicts, alias_tiids) {
        for (var i=0; i<newItemDicts.length; i++) {
            var tiid = newItemDicts[i]["_id"]
            this.items[tiid] = new Item(newItemDicts[i], new ItemView($), $)
        }
    }

    this.create = function(aliases, title) {
        /*
        *   Make a new collection. On success, add it to the user's list of colls,
        *   and redirect to the webapp's /collection/<cid> report page.
         */

        // @todo: remove. this is no longer used, as coll being created on server.

        var that = this
        var requestObj = {
            aliases: aliases,
            title:title
        }

        console.log("making the collection now.")
        $.ajax({
                   url: api_root+'/collection',
                   type: "POST",
                   dataType: "json",
                   contentType: "application/json; charset=utf-8",
                   data:  JSON.stringify(requestObj),
                   success: function(data){
                       console.log("finished making the collection!")
                       var cid=data.collection._id

                       // you could pass this in, but you pretty much only want it to redirect:
                       var redirect = function(){location.href = "/collection/" + cid}
                   }
               })
    }

    this.read = function(interval, tries) {
        console.log("read")
        if (tries === undefined) {
            var tries = 0
        }

        var thisThing = this
        this.views.startUpdating()

        $.ajax({
            url: api_root+'/v1/collection/'+thisThing.id+'?key='+api_key,
            type: "GET",
            dataType: "json",
            contentType: "application/json; charset=utf-8",
            statusCode: {
               210: function(data){
                   console.log("still updating")
                   thisThing.title = data.title
                   thisThing.alias_tiids = data.alias_tiids
                   thisThing.addItemsFromDicts(data.items)

                   thisThing.render.call(thisThing)

                   if (tries > 120) { // give up after 1 minute...
                       console.log("failed to finish update; giving up after 1min.")
                   }
                   else {
                       setTimeout(function(){
                           thisThing.read(interval, tries+1)
                       }, 500)
                   }
               },
               200: function(data) {
                   console.log("done with updating")
                   thisThing.alias_tiids = data.alias_tiids
                   thisThing.title = data.title
                   thisThing.addItemsFromDicts(data.items)

                   thisThing.render.call(thisThing)
                   thisThing.views.finishUpdating(thisThing.items)

                   return false;
               }
            }
            });
    }

    this.updateTitle = function(newTitle){
        // implement later.
    }

    this.deleteItem = function(tiidToDelete, callbacks) {
        callbacks.start()
        this.update({"tiids": [tiidToDelete]}, "DELETE", callbacks.success)
        return false
    }

    this.addItems = function(itemsToAdd, callbacks) {
        if (!this.userCanEdit(callbacks)) return false
        this.update({"aliases": itemsToAdd}, "PUT", callbacks.onSuccess)
    }

    this.update = function(payload, httpType, onSuccess){

        var url = webapp_root+'/user/'+impactstoryUserId+'/products?'
            + 'api_admin_key='+api_key

        $.ajax({
                   url: url,
                   type: httpType,
                   dataType: "json",
                   contentType: "application/json; charset=utf-8",
                   data:  JSON.stringify(payload),
                   success: function(data){
                       console.log("finished updating the collection!")
                       onSuccess(data)
                   }
               })
    }

    this.refreshItemData = function() {
        var thisThing = this
        this.views.startUpdating()
        $.ajax({
            url: api_root+'/v1/collection/'+this.id+'?key='+api_key,
            type: "POST",
            success: function(data){
               console.log("updating.")
               thisThing.read(1000);
            }});
        }

    this.userCanEdit = function(callbacks){
        // @todo make it so this figures out  status based on vars loaded server-side

        if (!$("body").hasClass("logged-in")) {
            callbacks.onNotLoggedIn()
            return false
        }

        if (!$("body").hasClass("can-edit")) {
            callbacks.onNotOwner()
            return false
        }
        return true
    }

    this.render = function(){
        this.views.renderItems(this.items)
//        this.views.renderTitle(this.title)

    }
}

function CollViews() {
    this.startUpdating = function(){
        $("img.loading").remove()

        $("body.report h2").before(ajaxLoadImg)
        changeElemState(
            $("body.user-profile #num-items"),
            "working"
        )
    }

    this.badgesWeight = function(dict) {
        return _.reduce(dict.awards, function(memo, metric){
            return memo + (metric.isHighly) ? 10 : 1
        }, 0)
    }

    this.finishUpdating = function(items){
        // setup page header
        $("#page-header img").remove()
        changeElemState(
            $("body.user-profile #num-items"),
            "ready"
        )

        console.log("num items:", _.size(items))
        $("#num-items span.value").text(_.size(items))

        // setup item-level zooming
        $("ul.active li.item div.item-header").addClass("zoomable")
        $("a.item-expand-button")
            .show()
            .css({color: tiLinkColor})
            .fadeOut(1500, function(){
                $(this).removeAttr("style")
            })
    }

    this.renderItems = function(itemObjsDict) {
        var itemObjs = _.values(itemObjsDict)
        var genreList = new GenreList(itemObjs)
        genreList.render()

    }

    this.renderIsEditable = function(isEditable) {
        if (isEditable){
            $("div#report").addClass("editable")
        }
    }

    this.onEditNotOwner = function(){
        $("#edit-not-owner").modal("show")
    }

    this.onEditNotLoggedIn = function() {
        $("#edit-not-logged-in").modal("show")
    }
}

function CollController(coll, collViews) {
    this.coll = coll
    this.collViews = collViews

    this.collReportPageInit = function() {
        this.coll.id = reportId // global loaded by the server
        this.coll.render()
        this.coll.read(1000)
        var that = this

        // refresh all the items
        $("#update-report-button").click(function(){
            coll.refreshItemData();
            return false;
        })

        // show/hide all zoom divs
        $("div#num-items a").toggle(
            function(){
                $(this).html("(collapse all)")
                $("li.item").addClass("zoomed").find("div.zoom").show()
            },
            function(){
                $(this).html("(expand all)")
                $("li.item").removeClass("zoomed").find("div.zoom").hide()
            }
        )

        // show the import-products dialog when you ask for it
        $("#import-products-button").click(function(){
            var callbacks = {
                onNotLoggedIn: that.collViews.onEditNotLoggedIn,
                onNotOwner: that.collViews.onEditNotOwner
            }

            if (!that.coll.userCanEdit(callbacks)) return false
            $("#import-products-modal").modal("show")
            return false
        })

        // delete items
        $("a.item-delete-button").live("click", function(){
            var item$ = $(this).parents("li.item")

            var callbacks = {
                onNotLoggedIn: that.collViews.onEditNotLoggedIn,
                onNotOwner: that.collViews.onEditNotOwner
            }

            if (!that.coll.userCanEdit(callbacks)) return false

            var callbacks2 = {
                start: function() {item$.slideUp();},
                success: function(data){
                    console.log("deleted, done. blam.", data)
                    $("#num-items span.value").text(_.size(data.alias_tiids))
                },
                onNotLoggedIn: that.collViews.onEditNotLoggedIn,
                onNotOwner: that.collViews.onEditNotOwner
            }

            that.coll.deleteItem( item$.attr("id"), callbacks2)
            return false;
        })
    }

}
<commit_msg>add event for clicked refresh button<commit_after>
function Genre(name) {
    this.name = name
    this.items = []

    this.sortItems = function(items) {
        return _.sortBy(items, function(item){
            var badgesSortFactor = _.reduce(item.dict.awards, function(memo, award){
                return memo + ((award.isHighly) ? 10 : 1)
            }, 0)
            return badgesSortFactor
        }).reverse()
    }

    this.render = function(){
        this.items = this.sortItems(this.items)

        var genre$ = $(ich.genreTemplate({name:this.name}, true))

        var renderedItems = []
        var itemsWithoutActivity = []
        for (var i=0;i<this.items.length;i++){
            var thisItem = this.items[i]
            if (thisItem.dict.awards.length) {
                renderedItems.push(thisItem.render())
            }
            else {
                itemsWithoutActivity.push(thisItem.render())
            }
        }
        genre$.find("ul.items.active").append(renderedItems)
        genre$.find("ul.items.inactive").append(itemsWithoutActivity)
        if (itemsWithoutActivity.length) {
            genre$.find("h4.plus-more")
                .toggle(
                    function(){
                        $(this).addClass("showing").siblings("ul.items.inactive").slideDown()
                        $(this).find("span.show-hide").html("(hide)")
                    },
                    function() {
                        $(this).removeClass("showing").siblings("ul.items.inactive").slideUp()
                        $(this).find("span.show-hide").html("(show)")
                    }
                )
                .find("span.value")
                .html(itemsWithoutActivity.length)
        }
        else {
            genre$.find("h4.plus-more").hide()
        }

        return genre$
    }
    return true
}

function GenreList(items) {
    var itemGroupsByGenre = _.groupBy(items, function(item) {
        return item.dict.biblio.genre
    })
    this.genres = _.map(itemGroupsByGenre, function(items, genreName){
        var genre = new Genre(genreName)
        genre.items = items
        return genre
    })

    this.render = function(){

        var renderedGenres = _.chain(this.genres)
            .sortBy("name")
            .map(function(genre){
                return genre.render()
            })
            .value()

        $("div.genre").remove()
        $("div.tooltip").remove() // otherwise tooltips from removed badges stick around
        $("#metrics div.wrapper").append(renderedGenres)
    }
}

function Coll(collViews){
    this.views = collViews;
    this.id = null
    this.items = {}

    this.addItemsFromDicts = function(newItemDicts, alias_tiids) {
        for (var i=0; i<newItemDicts.length; i++) {
            var tiid = newItemDicts[i]["_id"]
            this.items[tiid] = new Item(newItemDicts[i], new ItemView($), $)
        }
    }

    this.create = function(aliases, title) {
        /*
        *   Make a new collection. On success, add it to the user's list of colls,
        *   and redirect to the webapp's /collection/<cid> report page.
         */

        // @todo: remove. this is no longer used, as coll being created on server.

        var that = this
        var requestObj = {
            aliases: aliases,
            title:title
        }

        console.log("making the collection now.")
        $.ajax({
                   url: api_root+'/collection',
                   type: "POST",
                   dataType: "json",
                   contentType: "application/json; charset=utf-8",
                   data:  JSON.stringify(requestObj),
                   success: function(data){
                       console.log("finished making the collection!")
                       var cid=data.collection._id

                       // you could pass this in, but you pretty much only want it to redirect:
                       var redirect = function(){location.href = "/collection/" + cid}
                   }
               })
    }

    this.read = function(interval, tries) {
        console.log("read")
        if (tries === undefined) {
            var tries = 0
        }

        var thisThing = this
        this.views.startUpdating()

        $.ajax({
            url: api_root+'/v1/collection/'+thisThing.id+'?key='+api_key,
            type: "GET",
            dataType: "json",
            contentType: "application/json; charset=utf-8",
            statusCode: {
               210: function(data){
                   console.log("still updating")
                   thisThing.title = data.title
                   thisThing.alias_tiids = data.alias_tiids
                   thisThing.addItemsFromDicts(data.items)

                   thisThing.render.call(thisThing)

                   if (tries > 120) { // give up after 1 minute...
                       console.log("failed to finish update; giving up after 1min.")
                   }
                   else {
                       setTimeout(function(){
                           thisThing.read(interval, tries+1)
                       }, 500)
                   }
               },
               200: function(data) {
                   console.log("done with updating")
                   thisThing.alias_tiids = data.alias_tiids
                   thisThing.title = data.title
                   thisThing.addItemsFromDicts(data.items)

                   thisThing.render.call(thisThing)
                   thisThing.views.finishUpdating(thisThing.items)

                   return false;
               }
            }
            });
    }

    this.updateTitle = function(newTitle){
        // implement later.
    }

    this.deleteItem = function(tiidToDelete, callbacks) {
        callbacks.start()
        this.update({"tiids": [tiidToDelete]}, "DELETE", callbacks.success)
        return false
    }

    this.addItems = function(itemsToAdd, callbacks) {
        if (!this.userCanEdit(callbacks)) return false
        this.update({"aliases": itemsToAdd}, "PUT", callbacks.onSuccess)
    }

    this.update = function(payload, httpType, onSuccess){

        var url = webapp_root+'/user/'+impactstoryUserId+'/products?'
            + 'api_admin_key='+api_key

        $.ajax({
                   url: url,
                   type: httpType,
                   dataType: "json",
                   contentType: "application/json; charset=utf-8",
                   data:  JSON.stringify(payload),
                   success: function(data){
                       console.log("finished updating the collection!")
                       onSuccess(data)
                   }
               })
    }

    this.refreshItemData = function() {
        var thisThing = this
        this.views.startUpdating()
        $.ajax({
            url: api_root+'/v1/collection/'+this.id+'?key='+api_key,
            type: "POST",
            success: function(data){
               console.log("updating.")
               thisThing.read(1000);
            }});
        }

    this.userCanEdit = function(callbacks){
        // @todo make it so this figures out  status based on vars loaded server-side

        if (!$("body").hasClass("logged-in")) {
            callbacks.onNotLoggedIn()
            return false
        }

        if (!$("body").hasClass("can-edit")) {
            callbacks.onNotOwner()
            return false
        }
        return true
    }

    this.render = function(){
        this.views.renderItems(this.items)
//        this.views.renderTitle(this.title)

    }
}

function CollViews() {
    this.startUpdating = function(){
        $("img.loading").remove()

        $("body.report h2").before(ajaxLoadImg)
        changeElemState(
            $("body.user-profile #num-items"),
            "working"
        )
    }

    this.badgesWeight = function(dict) {
        return _.reduce(dict.awards, function(memo, metric){
            return memo + (metric.isHighly) ? 10 : 1
        }, 0)
    }

    this.finishUpdating = function(items){
        // setup page header
        $("#page-header img").remove()
        changeElemState(
            $("body.user-profile #num-items"),
            "ready"
        )

        console.log("num items:", _.size(items))
        $("#num-items span.value").text(_.size(items))

        // setup item-level zooming
        $("ul.active li.item div.item-header").addClass("zoomable")
        $("a.item-expand-button")
            .show()
            .css({color: tiLinkColor})
            .fadeOut(1500, function(){
                $(this).removeAttr("style")
            })
    }

    this.renderItems = function(itemObjsDict) {
        var itemObjs = _.values(itemObjsDict)
        var genreList = new GenreList(itemObjs)
        genreList.render()

    }

    this.renderIsEditable = function(isEditable) {
        if (isEditable){
            $("div#report").addClass("editable")
        }
    }

    this.onEditNotOwner = function(){
        $("#edit-not-owner").modal("show")
    }

    this.onEditNotLoggedIn = function() {
        $("#edit-not-logged-in").modal("show")
    }
}

function CollController(coll, collViews) {
    this.coll = coll
    this.collViews = collViews

    this.collReportPageInit = function() {
        this.coll.id = reportId // global loaded by the server
        this.coll.render()
        this.coll.read(1000)
        var that = this

        // refresh all the items
        $("#update-report-button").click(function(){
            analytics.track("Clicked refresh button")
            coll.refreshItemData();
            return false;
        })

        // show/hide all zoom divs
        $("div#num-items a").toggle(
            function(){
                $(this).html("(collapse all)")
                $("li.item").addClass("zoomed").find("div.zoom").show()
            },
            function(){
                $(this).html("(expand all)")
                $("li.item").removeClass("zoomed").find("div.zoom").hide()
            }
        )

        // show the import-products dialog when you ask for it
        $("#import-products-button").click(function(){
            var callbacks = {
                onNotLoggedIn: that.collViews.onEditNotLoggedIn,
                onNotOwner: that.collViews.onEditNotOwner
            }

            if (!that.coll.userCanEdit(callbacks)) return false
            $("#import-products-modal").modal("show")
            return false
        })

        // delete items
        $("a.item-delete-button").live("click", function(){
            var item$ = $(this).parents("li.item")

            var callbacks = {
                onNotLoggedIn: that.collViews.onEditNotLoggedIn,
                onNotOwner: that.collViews.onEditNotOwner
            }

            if (!that.coll.userCanEdit(callbacks)) return false

            var callbacks2 = {
                start: function() {item$.slideUp();},
                success: function(data){
                    console.log("deleted, done. blam.", data)
                    $("#num-items span.value").text(_.size(data.alias_tiids))
                },
                onNotLoggedIn: that.collViews.onEditNotLoggedIn,
                onNotOwner: that.collViews.onEditNotOwner
            }

            that.coll.deleteItem( item$.attr("id"), callbacks2)
            return false;
        })
    }

}
<|endoftext|>


<commit_before>// Native
import Head from 'next/head'

// Packages
import moment from 'moment'

// Layouts
import Page from '../layouts/page'

// Components
import Link from '../components/link'
import Title from '../components/title'

// Data
import posts from '../data/essays'

const parseDate = date => moment(date, 'DD-MM-YYYY')

// Assign a URL to each post and
// sort them by date (most recent one first)
const preparePosts = () => posts.map(post => Object.assign({
  url: `/${parseDate(post.date).year()}/${post.id}`
}, post)).sort((a, b) => parseDate(b.date).diff(parseDate(a.date)))

export default () => (
  <Page>
    <Title/>

      <link rel="stylesheet" href="//fonts.googleapis.com/css?family=Cardo&text=Leo%27s%20Essays"/>

{`Leo's Essays`}

    <Link href="/">↩</Link>

      {
        preparePosts().map(post => (

            <Link href={post.url}>
              {post.title}
              {parseDate(post.date).format('MMMM Do YYYY')}
            </Link>

        ))
      }

  </Page>
)
<commit_msg>Perfected back button<commit_after>// Native
import Head from 'next/head'

// Packages
import moment from 'moment'

// Layouts
import Page from '../layouts/page'

// Components
import Link from '../components/link'
import Title from '../components/title'

// Data
import posts from '../data/essays'

const parseDate = date => moment(date, 'DD-MM-YYYY')

// Assign a URL to each post and
// sort them by date (most recent one first)
const preparePosts = () => posts.map(post => Object.assign({
  url: `/${parseDate(post.date).year()}/${post.id}`
}, post)).sort((a, b) => parseDate(b.date).diff(parseDate(a.date)))

export default () => (
  <Page>
    <Title/>

      <link rel="stylesheet" href="//fonts.googleapis.com/css?family=Cardo&text=Leo%27s%20Essays"/>

{`Leo's Essays`}

    <Link href="/">↩</Link>

      {
        preparePosts().map(post => (

            <Link href={post.url}>
              {post.title}
              {parseDate(post.date).format('MMMM Do YYYY')}
            </Link>

        ))
      }

  </Page>
)
<|endoftext|>


<commit_before>// ==UserScript==
// @name WaniKani hide mnemonics
// @namespace wkhidem
// @description Adds a possiblity to hide meaning and reading mnemonics during lessons and review.
// @version 1.2
// @author Niklas Barsk
// @include http://www.wanikani.com/review/session*
// @include http://www.wanikani.com/lesson/session*
// @require http://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js
// @require https://gist.github.com/raw/2625891/waitForKeyElements.js
// @require https://raw.github.com/meetselva/attrchange/master/attrchange.js
// @grant   GM_addStyle
// @run-at document-end
// @updateURL http://userscripts.org/scripts/source/184925.user.js
// ==/UserScript==

/*
 * This script is licensed under the MIT licence.
 */

// review/lessons quiz
waitForKeyElements("#item-info-meaning-mnemonic", init);

// The different types of lesson pages (non-quiz mode)
waitForKeyElements("#main-info.radical", initLesson);
waitForKeyElements("#main-info.kanji", initLesson);
waitForKeyElements("#main-info.vocabulary", initLesson);

function initLesson()
{
    init();

    // The lessons are loaded in batches of several items and when
    // switching page new data is updated via javascript and not by
    // loading a new web page. Set up a listener that listens to
    // changes to the main-info element and calls init() again
    // whenever it changes.
    $("#main-info").attrchange({
        trackValues: false,
        callback: function(e)
        {
            init();
        }});
}

function init()
{
    if (!sanityCheckPassed())
    {
        // Don't try to run the script if the HTML can't be parsed.
        console.warn("WaniKani hide mnemonics need to be updated to support the latest version of WaniKani.");
        return;
    }

    setCorrectText();
    setCorrectVisibility();

    if (isQuiz())
    {
        // Update visibility state when the "Show All Information" button is pressed.
        document.getElementById("all-info").addEventListener("click", setCorrectVisibility);
    }
}

/**
 * Set the correct text for the meaning and reading headers depending on
 * the current state.
 */
function setCorrectText()
{
    setCorrectTextFor("meaning");
    if (!isRadical())
    {
        setCorrectTextFor("reading");
    }
}

/**
 * Returns true if the mnemonic is hidden for the current character
 * and the given type.
 */
function isHidden(which)
{
    return localStorage.getItem(getStorageKey(which)) != null
}

/**
 * Set hidden status for the current character in the localStorage
 * for the give type.
 * @param "reading" or "meaning" depending on which key is desired.
 */
function setStorage(which)
{
    localStorage.setItem(getStorageKey(which), 0);
}

/**
 * Remove the stored information about the current character from
 * the localStorage for the give type.
 * @param "reading" or "meaning" depending on which key is desired.
 */
function clearStorage(which)
{
    localStorage.removeItem(getStorageKey(which));
}

/**
 * Get the key that the removed state for the current character is
 * stored under in the localStorage.
 * @param "reading" or "meaning" depending on which key is desired.
 */
function getStorageKey(which)
{
    var character = document.getElementById("character").textContent.trim();
    if (character == "")
    {
        // Radical with image instead of text.
        var src = document.getElementById("character").children[0].getAttribute("src");
        character = src.split("/").pop()
    }
    return getCharacterType() + "_" + character + "_" + which;
}

/**
 * Return the type of the character the page is for: a string containing
 * "vocabulary", "kanji" or "radical".
 */
function getCharacterType()
{
    if (isLesson())
    {
        return document.getElementById("main-info").className.trim();
    }
    else if (isReview())
    {
        return document.getElementById("character").className.trim();
    }
}

/**
 * Return true if the current page is for a radical.
 */
function isRadical()
{
    return getCharacterType() == "radical";
}

/**
 * Return true if the current page is for vocabulary.
 */
function isVocabulary()
{
    return getCharacterType() == "vocabulary";
}

/**
 * Returns true if the current page is a lesson.
 */
function isLesson()
{
    return document.URL.indexOf("lesson") != -1;
}

/**
 * Returns true if the current page is a review.
 */
function isReview()
{
    return document.URL.indexOf("review") != -1;
}

/**
 * Returns true if the user is currently doing a quiz.
 */
function isQuiz()
{
    if (isReview())
    {
        return true;
    }
    var mainInfo = document.getElementById("main-info");
    return mainInfo.parentElement.className == "quiz";
}

/**
 * Set the correct visibility of the reading and meaning sections.
 */
function setCorrectVisibility()
{
    if (isHidden("meaning"))
    {
        hide("meaning");
    }
    else
    {
        show("meaning");
    }

    if (!isRadical())
    {
        if (isHidden("reading"))
        {
            hide("reading");
        }
        else
        {
            show("reading");
        }
    }
}

/**
 * Hide the specified section.
 * @param which The section that should be hidden, either "reading" or "meaning".
 */
function hide(which)
{
    setStorage(which);
    setDisplayStyle(which, "none");
    setCorrectText();
}

/**
 * Show the specified section.
 * @param which The section that should be shown, either "reading" or "meaning".
 */
function show(which)
{
    clearStorage(which);
    setDisplayStyle(which, "");
    setCorrectText();
}

/**
 * Set the display style of the hidable section.
 * @param which The section that should be updated, either "reading" or "meaning".
 * @param display The new value of the display css property.
 */
function setDisplayStyle(which, display)
{
    var children = getHidableElements(which);
    for (i = 0; i < children.length; ++i)
    {
        children[i].style.display = display;
    }
}

/**
 * Returns an array with all elements that should be hidden or
 * shown when the hide/show link is clicked.
 * @param Specifies if it's the "reading" or "meaning" that should be hidden
 */
function getHidableElements(which)
{
    // return an array of items to hide/show
    var ret = [];
    if (isQuiz())
    {
        ret.push(getMnemonicContainer(which));
    }
    else
    {
        var children = getLearningContainer(which).children;
        for (i = 0; i < children.length - 2; ++i) // note section is last 2 elments.
        {
            ret.push(children[i]);
        }
    }
    return ret;
}

/**
 * Set the correct text for reading/meaning/note header with the apropriate
 * show/hide link depending on what the current state is.
 *
 * @param which Specifies which header should be updated, the "reading" or "meaning" header.
 * @param action Specifies what happens when the header is pressed, either "show" or "hide".
 * @param headerID The ID of the header that should be updated.
 * @param header The DOM element which should have its text updated.
 */
function textForHeader(which, action, header)
{
    // Add the show/hide link to the header.
    header.innerHTML = header.firstChild.textContent + getLinkHTML(which, action);

    // Set either hide(which) or show(which) as onclick handler for the new link.
    document.getElementById(getLinkId(which, action)).onclick = function() { eval(action)(which);}
}

/**
 * Get the HTML for the show/hide link.
 * @param which Specifies if the link is for "reading" or "meaning".
 * @param action Specifies if the link is "hide" or "show".
 */
function getLinkHTML(which, action)
{
    // Examples of what the html looks like:
    // (show original meaning)
    // (hide)

    var linkText = action;
    if (action == "show")
    {
        if (isVocabulary())
        {
            linkText += " original explanation";
        }
        else
        {
            linkText += " original mnemonic";
        }
    }

    return " (" + linkText + ")";
}

/**
 * Return the id of the show/hide link.
 */
function getLinkId(which, action)
{
    return action + "-" + which + "-" + getCharacterType();
}

/**
 * Set the correct text for the specified header depending on the current state.
 * @param which The header that should be updated, either "reading" or "meaning".
 */
function setCorrectTextFor(which)
{
    if (isHidden(which))
    {
        // Display the "show" link in the note.
        textForHeader(which, "show", getNoteHeader(which));
    }
    else
    {
        // Display the "hide" link in the header.
        textForHeader(which, "hide", getMnemonicHeader(which));

        // Make sure the default version of the Note header is displayed.
        var nh = getNoteHeader(which);
        nh.innerHTML = nh.firstChild.textContent;
    }
}

/**
 * Get the DOM element that contains the mnemonic.
 * @param which Specifies if the header for the reading or meaning should
 *              be returned. The parameter is ignored for radicals since
 *              they only have one mnemonic.
 */
function getMnemonicContainer(which)
{
    if (isRadical())
    {
        return document.getElementById("item-info-col2").children[0];
    }
    else
    {
        return document.getElementById("item-info-" + which + "-mnemonic");
    }
}

/**
 * Get the DOM element for the mnemonic header.
 * @param which Specifies if the header for the reading or meaning should
 *              be returned. The parameter is ignored for radicals since
 *              they only have one mnemonic.
 */
function getMnemonicHeader(which)
{
    if (isQuiz())
    {
        return getMnemonicContainer(which).children[0];
    }
    else
    {
        return getLearningContainer(which).children[0];
    }
}

/**
 * Get the DOM element for the user notes header.
 * @param which Specifies if the notes header for the reading or meaning
 *              should be returned.
 */
function getNoteHeader(which)
{
    if (isQuiz())
    {
        return document.getElementById("note-" + which).children[0];
    }
    else
    {
        var container = getLearningContainer(which);
        return container.children[container.children.length - 2];
    }
}

/**
 * Get the container element of the mnemonics and notes in the learning
 * part of lessons. There is no id available for the actual headers and
 * mnemonics like in the quiz so the container element is the closest
 * we get.
 *
 * @param which Specifies if it's the container for "reading" or "meaning"
 *              that is desired.
 */
function getLearningContainer(which)
{
    var id = "supplement-" + getCharacterType().substring(0,3) + "-";
    var className = "pure-u-3-4";
    if (isRadical())
    {
        id += "name";
        className = "pure-u-1"
    }
    else
    {
        id += which;
    }

    return document.getElementById(id).getElementsByClassName(className)[0];
}

/**
 * Return true if critical assumptions made about the HTML code holds.
 */
function sanityCheckPassed()
{
    try
    {
        ensureElementExists("character");

        // Make sure we can get a correct storage key
        var parts = getStorageKey("meaning").split("_");
        if (parts.length != 3 || parts[0] == "" ||
            parts[1] == ""    || parts[2] == "")
        {
            throw new Error("Unable to generate a correct storage key: " + key);
        }

        if (isLesson())
        {
            ensureElementExists("main-info");
        }

        if (isQuiz())
        {
            ensureElementExists("all-info");
            ensureElementExists("note-meaning");

            if (isRadical())
            {
                ensureElementExists("item-info-col2");
            }
            else
            {
                ensureElementExists("item-info-reading-mnemonic");
                ensureElementExists("item-info-meaning-mnemonic");
                ensureElementExists("note-reading");
            }
        }
        else // Lessons during learning
        {
            ensureElementExistsAndHasClass("supplement-voc-reading", "pure-u-3-4");
            ensureElementExistsAndHasClass("supplement-voc-meaning", "pure-u-3-4");
            ensureElementExistsAndHasClass("supplement-kan-reading", "pure-u-3-4");
            ensureElementExistsAndHasClass("supplement-kan-meaning", "pure-u-3-4");
            ensureElementExistsAndHasClass("supplement-rad-name", "pure-u-1");

            // Make sure assumptions in isQuiz() holds.
            var cn = document.getElementById("main-info").parentElement.className;
            if (cn != "" && cn != "quiz")
            {
                throw new Error("Parent of 'main-info' is neither empty nor \"quiz\"");
            }
        }
    }
    catch (e)
    {
        console.error(e.toString());
        return false;
    }
    return true;
}

/**
 * Throws an exception if the given id doesn't exist in the DOM tree.
 * @return the element if it exist
 */
function ensureElementExists(id)
{
    var element = document.getElementById(id);
    if (element == null)
    {
        throw new Error(id + " does not exist");
    }
    return element;
}

/**
 * Throws an exception if the given id doesn't exist in the DOM tree.
 */
function ensureElementExistsAndHasClass(id, className)
{
    var element = ensureElementExists(id);
    if (element.getElementsByClassName(className)[0] == null)
    {
        throw new Error(id + " does not contain any element with class: " + className);
    }
}
<commit_msg>Don't explictly show hidden headers during quiz mode.<commit_after>// ==UserScript==
// @name WaniKani hide mnemonics
// @namespace wkhidem
// @description Adds a possiblity to hide meaning and reading mnemonics during lessons and review.
// @version 1.2
// @author Niklas Barsk
// @include http://www.wanikani.com/review/session*
// @include http://www.wanikani.com/lesson/session*
// @require http://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js
// @require https://gist.github.com/raw/2625891/waitForKeyElements.js
// @require https://raw.github.com/meetselva/attrchange/master/attrchange.js
// @grant   GM_addStyle
// @run-at document-end
// @updateURL http://userscripts.org/scripts/source/184925.user.js
// ==/UserScript==

/*
 * This script is licensed under the MIT licence.
 */

// review/lessons quiz
waitForKeyElements("#item-info-meaning-mnemonic", init);

// The different types of lesson pages (non-quiz mode)
waitForKeyElements("#main-info.radical", initLesson);
waitForKeyElements("#main-info.kanji", initLesson);
waitForKeyElements("#main-info.vocabulary", initLesson);

function initLesson()
{
    init();

    // The lessons are loaded in batches of several items and when
    // switching page new data is updated via javascript and not by
    // loading a new web page. Set up a listener that listens to
    // changes to the main-info element and calls init() again
    // whenever it changes.
    $("#main-info").attrchange({
        trackValues: false,
        callback: function(e)
        {
            init();
        }});
}

function init()
{
    if (!sanityCheckPassed())
    {
        // Don't try to run the script if the HTML can't be parsed.
        console.warn("WaniKani hide mnemonics need to be updated to support the latest version of WaniKani.");
        return;
    }

    setCorrectText();
    setCorrectVisibility();

    if (isQuiz())
    {
        // Update visibility state when the "Show All Information" button is pressed.
        document.getElementById("all-info").addEventListener("click", setCorrectVisibility);
    }
}

/**
 * Set the correct text for the meaning and reading headers depending on
 * the current state.
 */
function setCorrectText()
{
    setCorrectTextFor("meaning");
    if (!isRadical())
    {
        setCorrectTextFor("reading");
    }
}

/**
 * Returns true if the mnemonic is hidden for the current character
 * and the given type.
 */
function isHidden(which)
{
    return localStorage.getItem(getStorageKey(which)) != null
}

/**
 * Set hidden status for the current character in the localStorage
 * for the give type.
 * @param "reading" or "meaning" depending on which key is desired.
 */
function setStorage(which)
{
    localStorage.setItem(getStorageKey(which), 0);
}

/**
 * Remove the stored information about the current character from
 * the localStorage for the give type.
 * @param "reading" or "meaning" depending on which key is desired.
 */
function clearStorage(which)
{
    localStorage.removeItem(getStorageKey(which));
}

/**
 * Get the key that the removed state for the current character is
 * stored under in the localStorage.
 * @param "reading" or "meaning" depending on which key is desired.
 */
function getStorageKey(which)
{
    var character = document.getElementById("character").textContent.trim();
    if (character == "")
    {
        // Radical with image instead of text.
        var src = document.getElementById("character").children[0].getAttribute("src");
        character = src.split("/").pop()
    }
    return getCharacterType() + "_" + character + "_" + which;
}

/**
 * Return the type of the character the page is for: a string containing
 * "vocabulary", "kanji" or "radical".
 */
function getCharacterType()
{
    if (isLesson())
    {
        return document.getElementById("main-info").className.trim();
    }
    else if (isReview())
    {
        return document.getElementById("character").className.trim();
    }
}

/**
 * Return true if the current page is for a radical.
 */
function isRadical()
{
    return getCharacterType() == "radical";
}

/**
 * Return true if the current page is for vocabulary.
 */
function isVocabulary()
{
    return getCharacterType() == "vocabulary";
}

/**
 * Returns true if the current page is a lesson.
 */
function isLesson()
{
    return document.URL.indexOf("lesson") != -1;
}

/**
 * Returns true if the current page is a review.
 */
function isReview()
{
    return document.URL.indexOf("review") != -1;
}

/**
 * Returns true if the user is currently doing a quiz.
 */
function isQuiz()
{
    if (isReview())
    {
        return true;
    }
    var mainInfo = document.getElementById("main-info");
    return mainInfo.parentElement.className == "quiz";
}

/**
 * Set the correct visibility of the reading and meaning sections.
 */
function setCorrectVisibility()
{
    if (isHidden("meaning"))
    {
        hide("meaning");
    }
    else if (!isQuiz())
    {
        show("meaning");
    }

    if (!isRadical())
    {
        if (isHidden("reading"))
        {
            hide("reading");
        }
        else if (!isQuiz())
        {
            show("reading");
        }
    }
}

/**
 * Hide the specified section.
 * @param which The section that should be hidden, either "reading" or "meaning".
 */
function hide(which)
{
    setStorage(which);
    setDisplayStyle(which, "none");
    setCorrectText();
}

/**
 * Show the specified section.
 * @param which The section that should be shown, either "reading" or "meaning".
 */
function show(which)
{
    clearStorage(which);
    setDisplayStyle(which, "");
    setCorrectText();
}

/**
 * Set the display style of the hidable section.
 * @param which The section that should be updated, either "reading" or "meaning".
 * @param display The new value of the display css property.
 */
function setDisplayStyle(which, display)
{
    var children = getHidableElements(which);
    for (i = 0; i < children.length; ++i)
    {
        children[i].style.display = display;
    }
}

/**
 * Returns an array with all elements that should be hidden or
 * shown when the hide/show link is clicked.
 * @param Specifies if it's the "reading" or "meaning" that should be hidden
 */
function getHidableElements(which)
{
    // return an array of items to hide/show
    var ret = [];
    if (isQuiz())
    {
        ret.push(getMnemonicContainer(which));
    }
    else
    {
        var children = getLearningContainer(which).children;
        for (i = 0; i < children.length - 2; ++i) // note section is last 2 elments.
        {
            ret.push(children[i]);
        }
    }
    return ret;
}

/**
 * Set the correct text for reading/meaning/note header with the apropriate
 * show/hide link depending on what the current state is.
 *
 * @param which Specifies which header should be updated, the "reading" or "meaning" header.
 * @param action Specifies what happens when the header is pressed, either "show" or "hide".
 * @param headerID The ID of the header that should be updated.
 * @param header The DOM element which should have its text updated.
 */
function textForHeader(which, action, header)
{
    // Add the show/hide link to the header.
    header.innerHTML = header.firstChild.textContent + getLinkHTML(which, action);

    // Set either hide(which) or show(which) as onclick handler for the new link.
    document.getElementById(getLinkId(which, action)).onclick = function() { eval(action)(which);}
}

/**
 * Get the HTML for the show/hide link.
 * @param which Specifies if the link is for "reading" or "meaning".
 * @param action Specifies if the link is "hide" or "show".
 */
function getLinkHTML(which, action)
{
    // Examples of what the html looks like:
    // (show original meaning)
    // (hide)

    var linkText = action;
    if (action == "show")
    {
        if (isVocabulary())
        {
            linkText += " original explanation";
        }
        else
        {
            linkText += " original mnemonic";
        }
    }

    return " (" + linkText + ")";
}

/**
 * Return the id of the show/hide link.
 */
function getLinkId(which, action)
{
    return action + "-" + which + "-" + getCharacterType();
}

/**
 * Set the correct text for the specified header depending on the current state.
 * @param which The header that should be updated, either "reading" or "meaning".
 */
function setCorrectTextFor(which)
{
    if (isHidden(which))
    {
        // Display the "show" link in the note.
        textForHeader(which, "show", getNoteHeader(which));
    }
    else
    {
        // Display the "hide" link in the header.
        textForHeader(which, "hide", getMnemonicHeader(which));

        // Make sure the default version of the Note header is displayed.
        var nh = getNoteHeader(which);
        nh.innerHTML = nh.firstChild.textContent;
    }
}

/**
 * Get the DOM element that contains the mnemonic.
 * @param which Specifies if the header for the reading or meaning should
 *              be returned. The parameter is ignored for radicals since
 *              they only have one mnemonic.
 */
function getMnemonicContainer(which)
{
    if (isRadical())
    {
        return document.getElementById("item-info-col2").children[0];
    }
    else
    {
        return document.getElementById("item-info-" + which + "-mnemonic");
    }
}

/**
 * Get the DOM element for the mnemonic header.
 * @param which Specifies if the header for the reading or meaning should
 *              be returned. The parameter is ignored for radicals since
 *              they only have one mnemonic.
 */
function getMnemonicHeader(which)
{
    if (isQuiz())
    {
        return getMnemonicContainer(which).children[0];
    }
    else
    {
        return getLearningContainer(which).children[0];
    }
}

/**
 * Get the DOM element for the user notes header.
 * @param which Specifies if the notes header for the reading or meaning
 *              should be returned.
 */
function getNoteHeader(which)
{
    if (isQuiz())
    {
        return document.getElementById("note-" + which).children[0];
    }
    else
    {
        var container = getLearningContainer(which);
        return container.children[container.children.length - 2];
    }
}

/**
 * Get the container element of the mnemonics and notes in the learning
 * part of lessons. There is no id available for the actual headers and
 * mnemonics like in the quiz so the container element is the closest
 * we get.
 *
 * @param which Specifies if it's the container for "reading" or "meaning"
 *              that is desired.
 */
function getLearningContainer(which)
{
    var id = "supplement-" + getCharacterType().substring(0,3) + "-";
    var className = "pure-u-3-4";
    if (isRadical())
    {
        id += "name";
        className = "pure-u-1"
    }
    else
    {
        id += which;
    }

    return document.getElementById(id).getElementsByClassName(className)[0];
}

/**
 * Return true if critical assumptions made about the HTML code holds.
 */
function sanityCheckPassed()
{
    try
    {
        ensureElementExists("character");

        // Make sure we can get a correct storage key
        var parts = getStorageKey("meaning").split("_");
        if (parts.length != 3 || parts[0] == "" ||
            parts[1] == ""    || parts[2] == "")
        {
            throw new Error("Unable to generate a correct storage key: " + key);
        }

        if (isLesson())
        {
            ensureElementExists("main-info");
        }

        if (isQuiz())
        {
            ensureElementExists("all-info");
            ensureElementExists("note-meaning");

            if (isRadical())
            {
                ensureElementExists("item-info-col2");
            }
            else
            {
                ensureElementExists("item-info-reading-mnemonic");
                ensureElementExists("item-info-meaning-mnemonic");
                ensureElementExists("note-reading");
            }
        }
        else // Lessons during learning
        {
            ensureElementExistsAndHasClass("supplement-voc-reading", "pure-u-3-4");
            ensureElementExistsAndHasClass("supplement-voc-meaning", "pure-u-3-4");
            ensureElementExistsAndHasClass("supplement-kan-reading", "pure-u-3-4");
            ensureElementExistsAndHasClass("supplement-kan-meaning", "pure-u-3-4");
            ensureElementExistsAndHasClass("supplement-rad-name", "pure-u-1");

            // Make sure assumptions in isQuiz() holds.
            var cn = document.getElementById("main-info").parentElement.className;
            if (cn != "" && cn != "quiz")
            {
                throw new Error("Parent of 'main-info' is neither empty nor \"quiz\"");
            }
        }
    }
    catch (e)
    {
        console.error(e.toString());
        return false;
    }
    return true;
}

/**
 * Throws an exception if the given id doesn't exist in the DOM tree.
 * @return the element if it exist
 */
function ensureElementExists(id)
{
    var element = document.getElementById(id);
    if (element == null)
    {
        throw new Error(id + " does not exist");
    }
    return element;
}

/**
 * Throws an exception if the given id doesn't exist in the DOM tree.
 */
function ensureElementExistsAndHasClass(id, className)
{
    var element = ensureElementExists(id);
    if (element.getElementsByClassName(className)[0] == null)
    {
        throw new Error(id + " does not contain any element with class: " + className);
    }
}
<|endoftext|>


<commit_before>(function(module) {

    "use strict";

    var webdavFS = require("webdav-fs"),
        TextDatasource = require("./TextDatasource.js");

    /**
     * WebDAV datasource for reading and writing remote archives
     * @class WebDAVDatasource
     * @augments TextDatasource
     */
    class WebDAVDatasource extends TextDatasource {

        /**
         * Constructor for the datasource
         * @param {string} endpoint URL for the WebDAV service (without resource path)
         * @param {string} webDAVPath Resource path on the WebDAV service
         * @param {string} username Username for the WebDAV service
         * @param {string} password Password for the WebDAV service
         */
        constructor(endpoint, webDAVPath, username, password) {
            var endpointLen = endpoint.length;
            endpoint = (endpoint[endpointLen - 1] === "/") ? endpoint : endpoint + "/";
            webDAVPath = (webDAVPath[0] === "/") ? webDAVPath : "/" + webDAVPath;
            this._wfs = webdavFS(endpoint, username, password);
            this._path = webDAVPath;
            super("");
        }

        /**
         * Load the archive using a password
         * @param {string} password The password for archive decryption
         * @returns {Promise.<Archive>} A promise resolving with the opened archive
         */
        load(password) {
            var wfs = this._wfs,
                filePath = this._path;
            return (new Promise(function(resolve, reject) {
                wfs.readFile(filePath, "utf8", function(error, data) {
                    if (error) {
                        (reject)(error);
                    } else {
                        (resolve)(data);
                    }
                });
            })).then((content) => {
                this.setContent(content);
                return super.load(password);
            }).catch(function(err) {
                var errorMsg = "Failed opening archive: " + err;
                console.error(errorMsg);
                throw err;
            });
        }

        /**
         * Save an archive with a password to the WebDAV service
         * @param {Archive} archive The archive to save
         * @param {string} password The password for encryption
         * @returns {Promise} A promise resolving when the save is complete
         */
        save(archive, password) {
            var wfs = this._wfs,
                filePath = this._path;
            return super
                .save(archive, password)
                .then(function(encrypted) {
                    return new Promise(function(resolve, reject) {
                        wfs.writeFile(filePath, encrypted, "utf8", function(error) {
                            if (error) {
                                (reject)(error);
                            } else {
                                (resolve)();
                            }
                        });
                    });
                });
        }

    }

    module.exports = WebDAVDatasource;

})(module);
<commit_msg>Fix webdav constructor<commit_after>(function(module) {

    "use strict";

    var webdavFS = require("webdav-fs"),
        TextDatasource = require("./TextDatasource.js");

    /**
     * WebDAV datasource for reading and writing remote archives
     * @class WebDAVDatasource
     * @augments TextDatasource
     */
    class WebDAVDatasource extends TextDatasource {

        /**
         * Constructor for the datasource
         * @param {string} endpoint URL for the WebDAV service (without resource path)
         * @param {string} webDAVPath Resource path on the WebDAV service
         * @param {string} username Username for the WebDAV service
         * @param {string} password Password for the WebDAV service
         */
        constructor(endpoint, webDAVPath, username, password) {
            var endpointLen = endpoint.length;
            endpoint = (endpoint[endpointLen - 1] === "/") ? endpoint : endpoint + "/";
            webDAVPath = (webDAVPath[0] === "/") ? webDAVPath : "/" + webDAVPath;
            super("");
            this._wfs = webdavFS(endpoint, username, password);
            this._path = webDAVPath;
        }

        /**
         * Load the archive using a password
         * @param {string} password The password for archive decryption
         * @returns {Promise.<Archive>} A promise resolving with the opened archive
         */
        load(password) {
            var wfs = this._wfs,
                filePath = this._path;
            return (new Promise(function(resolve, reject) {
                wfs.readFile(filePath, "utf8", function(error, data) {
                    if (error) {
                        (reject)(error);
                    } else {
                        (resolve)(data);
                    }
                });
            })).then((content) => {
                this.setContent(content);
                return super.load(password);
            }).catch(function(err) {
                var errorMsg = "Failed opening archive: " + err;
                console.error(errorMsg);
                throw err;
            });
        }

        /**
         * Save an archive with a password to the WebDAV service
         * @param {Archive} archive The archive to save
         * @param {string} password The password for encryption
         * @returns {Promise} A promise resolving when the save is complete
         */
        save(archive, password) {
            var wfs = this._wfs,
                filePath = this._path;
            return super
                .save(archive, password)
                .then(function(encrypted) {
                    return new Promise(function(resolve, reject) {
                        wfs.writeFile(filePath, encrypted, "utf8", function(error) {
                            if (error) {
                                (reject)(error);
                            } else {
                                (resolve)();
                            }
                        });
                    });
                });
        }

    }

    module.exports = WebDAVDatasource;

})(module);
<|endoftext|>


<commit_before>/**
 * @flow
 */

type ARIAAbstractRole =
  'command'
  | 'composite'
  | 'input'
  | 'landmark'
  | 'range'
  | 'roletype'
  | 'section'
  | 'sectionhead'
  | 'select'
  | 'structure'
  | 'widget'
  | 'window';

type ARIAWidgetRole =
  | 'button'
  | 'checkbox'
  | 'gridcell'
  | 'link'
  | 'menuitem'
  | 'menuitemcheckbox'
  | 'menuitemradio'
  | 'option'
  | 'progressbar'
  | 'radio'
  | 'scrollbar'
  | 'searchbox'
  | 'slider'
  | 'spinbutton'
  | 'switch'
  | 'tab'
  | 'tabpanel'
  | 'textbox'
  | 'treeitem';

type ARIACompositeWidgetRole =
  'combobox'
  | 'grid'
  | 'listbox'
  | 'menu'
  | 'menubar'
  | 'radiogroup'
  | 'tablist'
  | 'tree'
  | 'treegrid';

type ARIADocumentStructureRole =
  'application'
  | 'article'
  | 'blockquote'
  | 'caption'
  | 'cell'
  | 'columnheader'
  | 'definition'
  | 'deletion'
  | 'directory'
  | 'document'
  | 'emphasis'
  | 'feed'
  | 'figure'
  | 'generic'
  | 'group'
  | 'heading'
  | 'img'
  | 'insertion'
  | 'list'
  | 'listitem'
  | 'math'
  | 'meter'
  | 'none'
  | 'note'
  | 'paragraph'
  | 'presentation'
  | 'row'
  | 'rowgroup'
  | 'rowheader'
  | 'separator'
  | 'strong'
  | 'subscript'
  | 'superscript'
  | 'table'
  | 'term'
  | 'time'
  | 'toolbar'
  | 'tooltip';

type ARIALandmarkRole =
  'banner'
  | 'complementary'
  | 'contentinfo'
  | 'form'
  | 'main'
  | 'navigation'
  | 'region'
  | 'search';

type ARIALiveRegionRole =
  'alert'
  | 'log'
  | 'marquee'
  | 'status'
  | 'timer';

type ARIAWindowRole =
  'alertdialog'
  | 'dialog';

type ARIAUncategorizedRole =
  'code';

type ARIADPubRole =
  | 'doc-abstract'
  | 'doc-acknowledgments'
  | 'doc-afterword'
  | 'doc-appendix'
  | 'doc-backlink'
  | 'doc-biblioentry'
  | 'doc-bibliography'
  | 'doc-biblioref'
  | 'doc-chapter'
  | 'doc-colophon'
  | 'doc-conclusion'
  | 'doc-cover'
  | 'doc-credit'
  | 'doc-credits'
  | 'doc-dedication'
  | 'doc-endnote'
  | 'doc-endnotes'
  | 'doc-epigraph'
  | 'doc-epilogue'
  | 'doc-errata'
  | 'doc-example'
  | 'doc-footnote'
  | 'doc-foreword'
  | 'doc-glossary'
  | 'doc-glossref'
  | 'doc-index'
  | 'doc-introduction'
  | 'doc-noteref'
  | 'doc-notice'
  | 'doc-pagebreak'
  | 'doc-pagelist'
  | 'doc-part'
  | 'doc-preface'
  | 'doc-prologue'
  | 'doc-pullquote'
  | 'doc-qna'
  | 'doc-subtitle'
  | 'doc-tip'
  | 'doc-toc';

type ARIARole =
  ARIAWidgetRole
  | ARIACompositeWidgetRole
  | ARIADocumentStructureRole
  | ARIALandmarkRole
  | ARIALiveRegionRole
  | ARIAWindowRole
  | ARIAUncategorizedRole;

type ARIARoleDefintionKey =
  ARIAAbstractRole
  | ARIARole
  | ARIADPubRole;

type ARIARoleDefinition = {
  /* Abstract roles may not be used in HTML. */
  abstract: boolean,
  /* The concepts in related domains that inform behavior mappings. */
  baseConcepts: Array<ARIARoleRelation>,
  /* Child presentational roles strip child nodes of roles and flatten the
  * content to text. */
  childrenPresentational: boolean,
  /* aria-* properties and states disallowed on this role. */
  prohibitedProps: $Keys<ARIAPropertyMap>[],
  /* aria-* properties and states allowed on this role. */
  props: ARIAPropertyMap,
  /* The concepts in related domains that inform behavior mappings. */
  relatedConcepts: Array<ARIARoleRelation>,
  /* aria-* properties and states required on this role. */
  requiredProps: ARIAPropertyMap,
  /* An array or super class "stacks." Each stack contains a LIFO list of
  * strings correspond to a super class in the inheritance chain of this
  * role. Roles may have more than one inheritance chain, which is why
  * this property is an array of arrays and not a single array. */
  superClass: Array<Array<ARIAAbstractRole | ARIARole | ARIADPubRole>>,
};

type MapOfRoleDefinitions = Map<ARIARoleDefintionKey, ARIARoleDefinition>;

type ARIAState =
  'aria-busy'
  | 'aria-checked'
  | 'aria-disabled'
  | 'aria-expanded'
  | 'aria-grabbed'
  | 'aria-hidden'
  | 'aria-invalid'
  | 'aria-pressed'
  | 'aria-selected';

type ARIAProperty =
  'aria-activedescendant'
  | 'aria-atomic'
  | 'aria-autocomplete'
  | 'aria-colcount'
  | 'aria-colindex'
  | 'aria-colspan'
  | 'aria-controls'
  | 'aria-current'
  | 'aria-describedby'
  | 'aria-details'
  | 'aria-dropeffect'
  | 'aria-errormessage'
  | 'aria-flowto'
  | 'aria-haspopup'
  | 'aria-keyshortcuts'
  | 'aria-label'
  | 'aria-labelledby'
  | 'aria-level'
  | 'aria-live'
  | 'aria-modal'
  | 'aria-multiline'
  | 'aria-multiselectable'
  | 'aria-orientation'
  | 'aria-owns'
  | 'aria-placeholder'
  | 'aria-posinset'
  | 'aria-readonly'
  | 'aria-relevant'
  | 'aria-required'
  | 'aria-roledescription'
  | 'aria-rowcount'
  | 'aria-rowindex'
  | 'aria-rowspan'
  | 'aria-setsize'
  | 'aria-sort'
  | 'aria-valuemax'
  | 'aria-valuemin'
  | 'aria-valuenow'
  | 'aria-valuetext'
  | ARIAState;

type ARIAPropertyMap = {
  'aria-busy'?: mixed,
  'aria-checked'?: mixed,
  'aria-disabled'?: mixed,
  'aria-expanded'?: mixed,
  'aria-grabbed'?: mixed,
  'aria-hidden'?: mixed,
  'aria-invalid'?: mixed,
  'aria-pressed'?: mixed,
  'aria-selected'?: mixed,
  'aria-activedescendant'?: mixed,
  'aria-atomic'?: mixed,
  'aria-autocomplete'?: mixed,
  'aria-colcount'?: mixed,
  'aria-colindex'?: mixed,
  'aria-colspan'?: mixed,
  'aria-controls'?: mixed,
  'aria-current'?: ?ARIAPropertyCurrent,
  'aria-describedby'?: mixed,
  'aria-details'?: mixed,
  'aria-dropeffect'?: mixed,
  'aria-errormessage'?: mixed,
  'aria-flowto'?: mixed,
  'aria-haspopup'?: mixed,
  'aria-keyshortcuts'?: mixed,
  'aria-label'?: mixed,
  'aria-labelledby'?: mixed,
  'aria-level'?: mixed,
  'aria-live'?: mixed,
  'aria-modal'?: mixed,
  'aria-multiline'?: mixed,
  'aria-multiselectable'?: mixed,
  'aria-orientation'?: mixed,
  'aria-owns'?: mixed,
  'aria-placeholder'?: mixed,
  'aria-posinset'?: mixed,
  'aria-readonly'?: mixed,
  'aria-relevant'?: mixed,
  'aria-required'?: mixed,
  'aria-roledescription'?: mixed,
  'aria-rowcount'?: mixed,
  'aria-rowindex'?: mixed,
  'aria-rowspan'?: mixed,
  'aria-setsize'?: mixed,
  'aria-sort'?: mixed,
  'aria-valuemax'?: mixed,
  'aria-valuemin'?: mixed,
  'aria-valuenow'?: mixed,
  'aria-valuetext'?: mixed,
};

type ARIAPropertyDefinition = {
  type: 'string'
  | 'id'
  | 'idlist'
  | 'integer'
  | 'number'
  | 'boolean'
  | 'token'
  | 'tokenlist'
  | 'tristate',
  value? : Array<string | boolean>,
  allowundefined?: boolean,
};

type ARIAPropertyCurrent =
  'page'
  | 'step'
  | 'location'
  | 'date'
  | 'time'
  | 'true'
  | 'false'
  | true
  | false;

type ARIARoleRelation = {
  module?: string,
  concept?: ARIARoleRelationConcept,
};

/* The concept in a related domain that informs behavior mappings.
 * Related domains include: HTML, "Device Independence Delivery Unit", XForms,
 * and ARIA to name a few. */
type ARIARoleRelationConcept = {
  name: string,
  attributes?: Array<ARIARoleRelationConceptAttribute>,
  // These constraints are drawn from the mapping between ARIA and HTML:
  // https://www.w3.org/TR/html-aria
  constraints?: Array<
    'direct descendant of document'
    | 'direct descendant of ol, ul or menu'
    | 'direct descendant of details element with the open attribute defined'
    | 'descendant of table'
  >,
};

type ARIARoleRelationConceptAttribute = {
  name: string,
  value?: string | number,
  // These constraints are drawn from the mapping between ARIA and HTML:
  // https://www.w3.org/TR/html-aria
  constraints?: Array<
    'undefined' // The attribute does not exist on the node:
    | 'set' // The attribute has a value:
    | '>1'
  >,
};<commit_msg>chore: Seal AriaPropertMap type (#67)<commit_after>/**
 * @flow
 */

type ARIAAbstractRole =
  'command'
  | 'composite'
  | 'input'
  | 'landmark'
  | 'range'
  | 'roletype'
  | 'section'
  | 'sectionhead'
  | 'select'
  | 'structure'
  | 'widget'
  | 'window';

type ARIAWidgetRole =
  | 'button'
  | 'checkbox'
  | 'gridcell'
  | 'link'
  | 'menuitem'
  | 'menuitemcheckbox'
  | 'menuitemradio'
  | 'option'
  | 'progressbar'
  | 'radio'
  | 'scrollbar'
  | 'searchbox'
  | 'slider'
  | 'spinbutton'
  | 'switch'
  | 'tab'
  | 'tabpanel'
  | 'textbox'
  | 'treeitem';

type ARIACompositeWidgetRole =
  'combobox'
  | 'grid'
  | 'listbox'
  | 'menu'
  | 'menubar'
  | 'radiogroup'
  | 'tablist'
  | 'tree'
  | 'treegrid';

type ARIADocumentStructureRole =
  'application'
  | 'article'
  | 'blockquote'
  | 'caption'
  | 'cell'
  | 'columnheader'
  | 'definition'
  | 'deletion'
  | 'directory'
  | 'document'
  | 'emphasis'
  | 'feed'
  | 'figure'
  | 'generic'
  | 'group'
  | 'heading'
  | 'img'
  | 'insertion'
  | 'list'
  | 'listitem'
  | 'math'
  | 'meter'
  | 'none'
  | 'note'
  | 'paragraph'
  | 'presentation'
  | 'row'
  | 'rowgroup'
  | 'rowheader'
  | 'separator'
  | 'strong'
  | 'subscript'
  | 'superscript'
  | 'table'
  | 'term'
  | 'time'
  | 'toolbar'
  | 'tooltip';

type ARIALandmarkRole =
  'banner'
  | 'complementary'
  | 'contentinfo'
  | 'form'
  | 'main'
  | 'navigation'
  | 'region'
  | 'search';

type ARIALiveRegionRole =
  'alert'
  | 'log'
  | 'marquee'
  | 'status'
  | 'timer';

type ARIAWindowRole =
  'alertdialog'
  | 'dialog';

type ARIAUncategorizedRole =
  'code';

type ARIADPubRole =
  | 'doc-abstract'
  | 'doc-acknowledgments'
  | 'doc-afterword'
  | 'doc-appendix'
  | 'doc-backlink'
  | 'doc-biblioentry'
  | 'doc-bibliography'
  | 'doc-biblioref'
  | 'doc-chapter'
  | 'doc-colophon'
  | 'doc-conclusion'
  | 'doc-cover'
  | 'doc-credit'
  | 'doc-credits'
  | 'doc-dedication'
  | 'doc-endnote'
  | 'doc-endnotes'
  | 'doc-epigraph'
  | 'doc-epilogue'
  | 'doc-errata'
  | 'doc-example'
  | 'doc-footnote'
  | 'doc-foreword'
  | 'doc-glossary'
  | 'doc-glossref'
  | 'doc-index'
  | 'doc-introduction'
  | 'doc-noteref'
  | 'doc-notice'
  | 'doc-pagebreak'
  | 'doc-pagelist'
  | 'doc-part'
  | 'doc-preface'
  | 'doc-prologue'
  | 'doc-pullquote'
  | 'doc-qna'
  | 'doc-subtitle'
  | 'doc-tip'
  | 'doc-toc';

type ARIARole =
  ARIAWidgetRole
  | ARIACompositeWidgetRole
  | ARIADocumentStructureRole
  | ARIALandmarkRole
  | ARIALiveRegionRole
  | ARIAWindowRole
  | ARIAUncategorizedRole;

type ARIARoleDefintionKey =
  ARIAAbstractRole
  | ARIARole
  | ARIADPubRole;

type ARIARoleDefinition = {
  /* Abstract roles may not be used in HTML. */
  abstract: boolean,
  /* The concepts in related domains that inform behavior mappings. */
  baseConcepts: Array<ARIARoleRelation>,
  /* Child presentational roles strip child nodes of roles and flatten the
  * content to text. */
  childrenPresentational: boolean,
  /* aria-* properties and states disallowed on this role. */
  prohibitedProps: $Keys<ARIAPropertyMap>[],
  /* aria-* properties and states allowed on this role. */
  props: ARIAPropertyMap,
  /* The concepts in related domains that inform behavior mappings. */
  relatedConcepts: Array<ARIARoleRelation>,
  /* aria-* properties and states required on this role. */
  requiredProps: ARIAPropertyMap,
  /* An array or super class "stacks." Each stack contains a LIFO list of
  * strings correspond to a super class in the inheritance chain of this
  * role. Roles may have more than one inheritance chain, which is why
  * this property is an array of arrays and not a single array. */
  superClass: Array<Array<ARIAAbstractRole | ARIARole | ARIADPubRole>>,
};

type MapOfRoleDefinitions = Map<ARIARoleDefintionKey, ARIARoleDefinition>;

type ARIAState =
  'aria-busy'
  | 'aria-checked'
  | 'aria-disabled'
  | 'aria-expanded'
  | 'aria-grabbed'
  | 'aria-hidden'
  | 'aria-invalid'
  | 'aria-pressed'
  | 'aria-selected';

type ARIAProperty =
  'aria-activedescendant'
  | 'aria-atomic'
  | 'aria-autocomplete'
  | 'aria-colcount'
  | 'aria-colindex'
  | 'aria-colspan'
  | 'aria-controls'
  | 'aria-current'
  | 'aria-describedby'
  | 'aria-details'
  | 'aria-dropeffect'
  | 'aria-errormessage'
  | 'aria-flowto'
  | 'aria-haspopup'
  | 'aria-keyshortcuts'
  | 'aria-label'
  | 'aria-labelledby'
  | 'aria-level'
  | 'aria-live'
  | 'aria-modal'
  | 'aria-multiline'
  | 'aria-multiselectable'
  | 'aria-orientation'
  | 'aria-owns'
  | 'aria-placeholder'
  | 'aria-posinset'
  | 'aria-readonly'
  | 'aria-relevant'
  | 'aria-required'
  | 'aria-roledescription'
  | 'aria-rowcount'
  | 'aria-rowindex'
  | 'aria-rowspan'
  | 'aria-setsize'
  | 'aria-sort'
  | 'aria-valuemax'
  | 'aria-valuemin'
  | 'aria-valuenow'
  | 'aria-valuetext'
  | ARIAState;

// {| [AriaProperty]?: mixed |}
type ARIAPropertyMap = {|
  'aria-busy'?: mixed,
  'aria-checked'?: mixed,
  'aria-disabled'?: mixed,
  'aria-expanded'?: mixed,
  'aria-grabbed'?: mixed,
  'aria-hidden'?: mixed,
  'aria-invalid'?: mixed,
  'aria-pressed'?: mixed,
  'aria-selected'?: mixed,
  'aria-activedescendant'?: mixed,
  'aria-atomic'?: mixed,
  'aria-autocomplete'?: mixed,
  'aria-colcount'?: mixed,
  'aria-colindex'?: mixed,
  'aria-colspan'?: mixed,
  'aria-controls'?: mixed,
  'aria-current'?: ?ARIAPropertyCurrent,
  'aria-describedby'?: mixed,
  'aria-details'?: mixed,
  'aria-dropeffect'?: mixed,
  'aria-errormessage'?: mixed,
  'aria-flowto'?: mixed,
  'aria-haspopup'?: mixed,
  'aria-keyshortcuts'?: mixed,
  'aria-label'?: mixed,
  'aria-labelledby'?: mixed,
  'aria-level'?: mixed,
  'aria-live'?: mixed,
  'aria-modal'?: mixed,
  'aria-multiline'?: mixed,
  'aria-multiselectable'?: mixed,
  'aria-orientation'?: mixed,
  'aria-owns'?: mixed,
  'aria-placeholder'?: mixed,
  'aria-posinset'?: mixed,
  'aria-readonly'?: mixed,
  'aria-relevant'?: mixed,
  'aria-required'?: mixed,
  'aria-roledescription'?: mixed,
  'aria-rowcount'?: mixed,
  'aria-rowindex'?: mixed,
  'aria-rowspan'?: mixed,
  'aria-setsize'?: mixed,
  'aria-sort'?: mixed,
  'aria-valuemax'?: mixed,
  'aria-valuemin'?: mixed,
  'aria-valuenow'?: mixed,
  'aria-valuetext'?: mixed,
|};

type ARIAPropertyDefinition = {
  type: 'string'
  | 'id'
  | 'idlist'
  | 'integer'
  | 'number'
  | 'boolean'
  | 'token'
  | 'tokenlist'
  | 'tristate',
  value? : Array<string | boolean>,
  allowundefined?: boolean,
};

type ARIAPropertyCurrent =
  'page'
  | 'step'
  | 'location'
  | 'date'
  | 'time'
  | 'true'
  | 'false'
  | true
  | false;

type ARIARoleRelation = {
  module?: string,
  concept?: ARIARoleRelationConcept,
};

/* The concept in a related domain that informs behavior mappings.
 * Related domains include: HTML, "Device Independence Delivery Unit", XForms,
 * and ARIA to name a few. */
type ARIARoleRelationConcept = {
  name: string,
  attributes?: Array<ARIARoleRelationConceptAttribute>,
  // These constraints are drawn from the mapping between ARIA and HTML:
  // https://www.w3.org/TR/html-aria
  constraints?: Array<
    'direct descendant of document'
    | 'direct descendant of ol, ul or menu'
    | 'direct descendant of details element with the open attribute defined'
    | 'descendant of table'
  >,
};

type ARIARoleRelationConceptAttribute = {
  name: string,
  value?: string | number,
  // These constraints are drawn from the mapping between ARIA and HTML:
  // https://www.w3.org/TR/html-aria
  constraints?: Array<
    'undefined' // The attribute does not exist on the node:
    | 'set' // The attribute has a value:
    | '>1'
  >,
};<|endoftext|>