function updateNavigationStepper()

in src/core/main.ts [1995:2229]


  function updateNavigationStepper() {
    var STEPPER_STEP_WIDTH = 50;

    var navigation_step_svg = selectWithParent("#stepper_svg");

    var navigation_step = navigation_step_svg.selectAll(".framePoint")
      .data(globals.scenes);

    navigation_step.exit().transition()
      .delay(1000)
      .remove();

    var navigation_step_update = navigation_step.transition()
      .duration(instance.options.animations ? 1000 : 0);

    var navigation_step_enter = navigation_step.enter()
      .append("g")
      .attr("class", "framePoint")
      .attr("id", function (d) {
        return "frame" + d.s_order;
      })
      .attr("transform", function (d) {
        return "translate(" + (d.s_order * STEPPER_STEP_WIDTH + d.s_order * 5) + ",0)";
      })
      .style("cursor", "pointer");

    navigation_step_update.attr("transform", function (d) {
      return "translate(" + (d.s_order * STEPPER_STEP_WIDTH + d.s_order * 5) + ",0)";
    })
      .attr("id", function (d) {
        return "frame" + d.s_order;
      });

    navigation_step_enter.append("title")
      .text(function (d) {
        return "Scene " + (d.s_order + 1);
      });

    navigation_step_update.select("title")
      .text(function (d) {
        return "Scene " + (d.s_order + 1);
      });

    function changeSceneClickHandler(d) {
      instance._currentSceneIndex = d.s_order;
      changeScene(instance._currentSceneIndex);
    }

    navigation_step_enter.append("rect")
      .attr("fill", "white")
      .attr("width", STEPPER_STEP_WIDTH)
      .attr("height", STEPPER_STEP_WIDTH)
      .style("stroke", function (d) {
        return d.s_order === instance._currentSceneIndex ? "#f00" : "#ccc";
      })
      .style("stroke-width", "3px")
      .on("click", changeSceneClickHandler);

    navigation_step_update.select("rect")
      .style("stroke", function (d) {
        return d.s_order === instance._currentSceneIndex ? "#f00" : "#ccc";
      });

    if (isIE11) {
      navigation_step_enter.append("svg:text")
        .attr("x", 25)
        .attr("y", 25)
        .attr("font-size", "20px")
        .attr("text-anchor", "middle")
        .attr("alignment-baseline", "central")
        .attr("style", "cursor:pointer")
        .text(function (d) {
          return (d.s_order + 1);
        })
        .on("click", changeSceneClickHandler);

      navigation_step_update.select("text")
        .text(function (d) {
          return (d.s_order + 1);
        });
    } else {
      navigation_step_enter.append("svg:image")
        .attr("xlink:href", function (d) {
          return d.s_src;
        })
        .attr("x", 2)
        .attr("y", 2)
        .attr("width", STEPPER_STEP_WIDTH - 4)
        .attr("height", STEPPER_STEP_WIDTH - 4)
        .on("click", changeSceneClickHandler);
    }

    var navigation_step_delete = navigation_step_enter.append("g")
      .attr("class", "scene_delete")
      .style("opacity", 0);

    navigation_step_delete.append("svg:image")
      .attr("class", "annotation_control annotation_delete")

      .attr("title", "Delete Scene")
      .attr("x", STEPPER_STEP_WIDTH - 17)
      .attr("y", 2)
      .attr("width", 15)
      .attr("height", 15)
      .attr("xlink:href", imageUrls("delete.png"));

    navigation_step_delete.append("rect")
      .attr("title", "Delete Scene")
      .attr("x", STEPPER_STEP_WIDTH - 17)
      .attr("y", 2)
      .attr("width", 15)
      .attr("height", 15)
      .on("mouseover", function () {
        d3.select(this).style("stroke", "#f00");
      })
      .on("mouseout", function () {
        d3.select(this).style("stroke", "#ccc");
      })
      .on("click", function (d) {
        selectWithParent("#frame" + d.s_order).remove();
        selectAllWithParent(".frame_hover").remove();
        // delete current scene unless image or caption div is open
        //logEvent("scene " + (d.s_order + 1) + " deleted.", "deletion");

        var j;
        for (j = 0; j < globals.scenes.length; j++) {
          if (globals.scenes[j].s_order === d.s_order) {
            globals.scenes.splice(j, 1);
          }
        }

        for (j = 0; j < globals.scenes.length; j++) {
          if (globals.scenes[j].s_order > d.s_order) {
            globals.scenes[j].s_order--;
          }
        }

        if (instance._currentSceneIndex > d.s_order) {
          instance._currentSceneIndex--;
        }

        updateNavigationStepper();

        instance._dispatch.stateChanged();

        if (instance._currentSceneIndex === d.s_order) { // is current scene to be deleted?
          if (instance._currentSceneIndex === globals.scenes.length - 1) { // is it the final scene?
            instance._currentSceneIndex = 0; // set current scene to first scene
          } else { // current scene is not the last scene
            instance._currentSceneIndex--; // set current scene to previous scene
            if (instance._currentSceneIndex < 0) { // did you delete the first scene?
              instance._currentSceneIndex = globals.scenes.length - 1; // set current to last scene
            }
          }

          if (globals.scenes.length === 0) { // are there no more scenes left?
            instance._currentSceneIndex = -1; // set current scene to -1
          } else {
            changeScene(instance._currentSceneIndex);
          }
        }
      })
      .append("title")
      .text("Delete Scene");

    if (!isIE11) {
      navigation_step_svg.selectAll(".framePoint")
        .on("mouseover", function () {
          const popupSize = 300;
          const frameRect = this.getBoundingClientRect();
          const relativeParentRect = selectWithParent(".timeline_storyteller-container").node().getBoundingClientRect();
          const offscreenAmount = (frameRect.right + popupSize) - relativeParentRect.right;

          // If we're offscreen, then adjust the position to take the offsceen amount into account
          const x_pos = frameRect.left - relativeParentRect.left - (offscreenAmount > 0 ? offscreenAmount : 0);
          const y_pos = frameRect.top - relativeParentRect.top;

          var img_src = d3.select(this).select("image").attr("href");

          d3.select(this).select("rect")
            .style("stroke", "#666");

          d3.select(this).select(".scene_delete")
            .style("opacity", 1);

          selectWithParent().append("div")
            .attr("class", "frame_hover")
            .style("left", `${x_pos}px`)
            .style("top", `${y_pos - popupSize - 20}px`)
            .append("svg")
            .style("padding", "0px")
            .style("width", `${popupSize}px`)
            .style("height", `${popupSize}px`)
            .append("svg:image")
            .attr("xlink:href", img_src)
            .attr("x", 2)
            .attr("y", 2)
            .attr("width", 296)
            .attr("height", 296);
        })
        .on("mouseout", function (d) {
          d3.select(this).select(".scene_delete")
            .style("opacity", 0);

          if (d.s_order === instance._currentSceneIndex) {
            d3.select(this).select("rect")
              .style("stroke", function () {
                return "#f00";
              });
          } else {
            d3.select(this).select("rect")
              .style("stroke", function () {
                return "#ccc";
              });
          }

          selectAllWithParent(".frame_hover").remove();
        });
    }

    navigation_step_svg.attr("width", (globals.scenes.length + 1) * (STEPPER_STEP_WIDTH + 5));

    const total = (globals.scenes || []).length;
    const sceneIdx = instance._currentSceneIndex;
    selectWithParent("#prev_scene_btn")
      // Always show 1 if at the beginning
      .attr("title", total > 1 ? `Scene ${sceneIdx === 0 ? total : sceneIdx} of ${total}` : "Previous Scene")
      .classed("img_btn_disabled", total < 2)
      .classed("img_btn_enabled", total > 1);

    selectWithParent("#next_scene_btn")
      .attr("title", total > 1 ? `Scene ${sceneIdx === total - 1 ? 1 : sceneIdx + 2} of ${total}` : "Next Scene")
      .classed("img_btn_disabled", total < 2)
      .classed("img_btn_enabled", total > 1);
  }