TimelineStoryteller.prototype._loadAnnotations = function()

in src/main.js [4606:4753]


TimelineStoryteller.prototype._loadAnnotations = function (scene, scene_index) {
  this.clearCanvas();

  this._prevTransitioning = false;
  const that = this;

  log("Loading Annotations");
  if (this._currentSceneIndex !== scene_index) {
    return;
  }

  // is the legend expanded in this scene?
  globals.legend_expanded = scene.s_legend_expanded;
  if (scene.s_legend_expanded) {
    this.expandLegend();
  } else {
    this.collapseLegend();
  }

  /**
   * Creates a mapper, that adds a type property
   * @param {string} type The type of the item
   * @returns {object} An object with the type and item properties
   */
  function mapWithType(type) {
    return function (item) {
      return {
        id: item.id,
        type,
        item
      };
    };
  }

  this._pruneAnnotations();

  var captionAnnos = globals.caption_list.map(mapWithType("caption"));
  var imageAnnos = globals.image_list.map(mapWithType("image"));
  var textAnnos = globals.annotation_list.map(mapWithType("annotation"));

  // TODO: this would be better if the scenes had a more generic property called "annotations", that have a list of all the
  // annotations that had a "type" property

  // These are are technically annotations, just different types, so concat them all together
  const allAnnos = captionAnnos.concat(imageAnnos).concat(textAnnos);

  let nextId = getHighestId(allAnnos);
  allAnnos
    .filter(function (anno) { // Filter out annotations not on this scene
      // Basically maps the type to scene.s_images or scene.s_annotations or scene.s_captions
      var sceneList = scene["s_" + anno.type + "s"];

      for (var i = 0; i < sceneList.length; i++) { // eslint-disable-line no-shadow
        // Basically the id property in the scene, so image_id or caption_id or annotation_id
        if (sceneList[i][anno.type + "_id"] === anno.item.id) {
          return true;
        }
      }
    })

    // We sort the annotations by z-order, and add the annotations in that order
    // this is important cause with svgs, the order in which elements are added dictates their z-index
    .sort(function (a, b) { return (a.item.z_index || 0) - (b.item.z_index || 0); })

    // Iterate through all of our annotations
    .forEach(function (anno) {
      // Make a copy so existing scenes do not get modified
      const item = Object.assign({}, anno.item);
      item.id = ++nextId;

      if (anno.type === "caption") {
        addCaption(item.caption_text, item.caption_width * 1.1, item.x_rel_pos, item.y_rel_pos, item);
      } else if (anno.type === "image") {
        addImage(that._timeline_vis, item.i_url, item.x_rel_pos, item.y_rel_pos, item.i_width, item.i_height, item);
      } else {
        var itemSel = selectWithParent("#event_g" + item.item_index).select("rect.event_span");
        var itemEle = itemSel[0][0].__data__,
          item_x_pos = 0,
          item_y_pos = 0;

        if (scene.s_representation !== "Radial") {
          item_x_pos = itemEle.rect_x_pos + itemEle.rect_offset_x + globals.padding.left + globals.unit_width * 0.5;
          item_y_pos = itemEle.rect_y_pos + itemEle.rect_offset_y + globals.padding.top + globals.unit_width * 0.5;
        } else {
          item_x_pos = itemEle.path_x_pos + itemEle.path_offset_x + globals.padding.left;
          item_y_pos = itemEle.path_y_pos + itemEle.path_offset_y + globals.padding.top;
        }

        const { element } = annotateEvent(that._timeline_vis, item.content_text, item_x_pos, item_y_pos, item.x_offset, item.y_offset, item.x_anno_offset, item.y_anno_offset, item.label_width, item.item_index, item);
        element
          .transition()
          .duration(that.options.animations ? 50 : 0)
          .style("opacity", 1)
          .each(function () {
            // If after running the transition, the scene has changed, then hide this annotation.
            if (that._currentSceneIndex !== scene_index) {
              this.style.opacity = 0;
            }
          });
      }
      if (anno.type === "caption") {
        globals.caption_list.push(item);
      } else if (anno.type === "image") {
        globals.image_list.push(item);
      } else {
        globals.annotation_list.push(item);
      }
    });

  // Set read-only state for annotations in playback mode
  d3.selectAll(".annotation_control, .annotation_drag_area, .image_drag_area, .caption_drag_area")
    .style("display", globals.playback_mode ? "none" : "");

  // toggle selected events in the scene
  this._main_svg.selectAll(".timeline_event_g")[0].forEach(function (event) {
    if (scene.s_selections.indexOf(event.__data__.event_id) !== -1) {
      event.__data__.selected = true;
      selectWithParent("#event_g" + event.__data__.event_id)
        .selectAll(".event_span")
        .attr("filter", "url(#drop-shadow)")
        .style("z-index", 1)
        .style("stroke", "#f00")
        .style("stroke-width", "1.25px");
      selectWithParent("#event_g" + event.__data__.event_id)
        .selectAll(".event_span_component")
        .style("z-index", 1)
        .style("stroke", "#f00")
        .style("stroke-width", "1px");
    } else {
      event.__data__.selected = false;
      selectWithParent("#event_g" + event.__data__.event_id)
        .selectAll(".event_span")
        .attr("filter", "none")
        .style("stroke", "#fff")
        .style("stroke-width", "0.25px");
      selectWithParent("#event_g" + event.__data__.event_id)
        .selectAll(".event_span_component")
        .style("stroke", "#fff")
        .style("stroke-width", "0.25px");
    }
  });
  if (this._timeline_vis.tl_representation() !== "Curve") {
    selectWithParent("#timecurve").style("visibility", "hidden");
  } else {
    selectWithParent("#timecurve").style("visibility", "visible");
  }
  this._main_svg.style("visibility", "visible");
};