in src/core/main.ts [1405:1902]
function processTimeline(data, shouldDrawTimeline) {
// check for earliest and latest numerical dates before parsing
globals.earliest_date = d3.min(data, function (d) {
if (d.start_date instanceof Date) {
return d.start_date;
}
return +d.start_date;
});
globals.latest_start_date = d3.max(data, function (d) {
if (d.start_date instanceof Date) {
return d.start_date;
}
return +d.start_date;
});
globals.latest_end_date = d3.max(data, function (d) {
if (d.end_date instanceof Date) {
return d.end_date;
}
return +d.end_date;
});
// set flag for really epic time scales
if (globals.isNumber(globals.earliest_date)) {
if (globals.earliest_date < -9999 || d3.max([globals.latest_start_date, globals.latest_end_date]) > 10000) {
globals.date_granularity = "epochs";
}
}
//log("date_granularity after: " + globals.date_granularity);
parseDates(data); // parse all the date values, replace blank end_date values
// set annotation counter for each item
data.forEach(function (item) {
item.annotation_count = 0;
});
/**
---------------------------------------------------------------------------------------
PROCESS CATEGORIES OF EVENTS
---------------------------------------------------------------------------------------
**/
// determine event categories from data
globals.categories.domain(data.map(function (d) {
return d.category;
}));
globals.num_categories = globals.categories.domain().length;
globals.max_legend_item_width = 0;
globals.categories.domain().sort().forEach(function (item) {
var legend_dummy = document.createElement("span");
legend_dummy.id = "legend_dummy";
legend_dummy.style.fontSize = "12px";
legend_dummy.style.fill = "#fff";
legend_dummy.style.fontFamily = "Century Gothic";
legend_dummy.innerHTML = item;
document.querySelector(".timeline_storyteller").appendChild(legend_dummy);
var legend_dummy_width = legend_dummy.offsetWidth;
document.querySelector(".timeline_storyteller").removeChild(legend_dummy);
if (legend_dummy_width > globals.max_legend_item_width) {
globals.max_legend_item_width = legend_dummy_width;
}
});
//logEvent("# categories: " + globals.num_categories, "preprocessing");
var temp_palette;
// assign colour labels to categories if # categories < 12
if (globals.num_categories <= 20 && globals.num_categories >= 11) {
temp_palette = colorSchemes.schema5();
globals.categories.range(temp_palette);
temp_palette = undefined;
} else if (globals.num_categories <= 10 && globals.num_categories >= 3) {
temp_palette = colorSchemes.schema2();
globals.categories.range(temp_palette);
temp_palette = undefined;
} else if (globals.num_categories === 2) {
temp_palette = ["#E45641", "#44B3C2"];
globals.categories.range(temp_palette);
temp_palette = undefined;
} else {
temp_palette = ["#E45641"];
globals.categories.range(temp_palette);
temp_palette = undefined;
}
if (globals.use_custom_palette) {
globals.categories.range(globals.color_palette);
//logEvent("custom palette: " + globals.categories.range(), "color palette");
}
filter_div.append("input")
.attr({
type: "image",
name: "Hide filter panel",
id: "export_close_btn",
class: "img_btn_enabled",
src: imageUrls("close.png"),
height: 15,
width: 15,
title: "Hide filter panel"
})
.style("position", "absolute")
.style("top", "0px")
.style("left", "5px")
.style("margin-top", "5px")
.on("click", function () {
selectWithParent("#filter_div").style("display", "none");
//logEvent("hide filter panel", "export");
});
filter_div.append("text")
.attr("class", "menu_label filter_label")
.style("margin-right", "auto")
.text("Filter Options")
.style("cursor", "move")
.call(filterDrag);
filter_div.append("hr")
.attr("class", "menu_hr");
// filter type options
var filter_type_picker = filter_div.append("div")
.attr("id", "filter_type_picker")
.attr("class", "filter_div_section");
filter_type_picker.append("div")
.attr("class", "filter_div_header")
.append("text")
.attr("class", "menu_label filter_label")
.text("Filter Mode:");
var filter_type_rb = filter_type_picker.selectAll("g")
.data(["Emphasize", "Hide"])
.enter();
var filter_type_rb_label = filter_type_rb.append("label")
.attr("class", "menu_rb");
filter_type_rb_label.append("input")
.attr({
type: "radio",
name: "filter_type_rb",
value: function (d) {
return d;
}
})
.property("disabled", false)
.property("checked", function (d) {
return d === "Emphasize";
});
filter_type_rb_label.append("img")
.attr({
class: "img_btn_enabled",
height: 30,
width: 30,
title: function (d) {
return d;
},
src: function (d) {
return imageUrls(d === "Emphasize" ? "highlight.png" : "hide.png");
}
})
.style("margin-bottom", "0px");
filter_type_rb_label.append("span")
.attr("class", "option_rb_label")
.html(function (d) {
return d;
});
selectAllWithParent("#filter_type_picker input[name=filter_type_rb]").on("change", function () {
const newCategories = selectWithParent("#category_picker").select("option");
const newFacets = selectWithParent("#facet_picker").select("option");
const newSegments = selectWithParent("#segment_picker").select("option");
globals.filter_type = this.value;
selectWithParent("#filter_div").style("display", "inline");
//logEvent("filter type changed: " + this.value, "filter");
const isHide = globals.filter_type === "Hide";
if (!isHide) {
globals.active_data = globals.all_data;
}
const trigger_remove_filter =
globals.selected_categories[0].length !== 1 || globals.selected_categories[0][0].value !== "( All )" ||
globals.selected_facets[0].length !== 1 || globals.selected_facets[0][0].value !== "( All )" ||
globals.selected_segments[0].length !== 1 || globals.selected_segments[0][0].value !== "( All )";
if (trigger_remove_filter) {
const remove = globals.dispatch.remove;
const emphasize = globals.dispatch.Emphasize;
(isHide ? emphasize : remove).call(globals.dispatch, newCategories, newFacets, newSegments);
(isHide ? remove : emphasize).call(globals.dispatch, globals.selected_categories, globals.selected_facets, globals.selected_segments);
}
});
var category_filter = filter_div.append("div")
.attr("class", "filter_div_section");
var category_filter_header = category_filter.append("div")
.attr("class", "filter_div_header");
category_filter_header.append("text")
.attr("class", "menu_label filter_label")
.text("Category");
category_filter_header.append("label")
.attr("for", "category_picker")
.style("display", "block")
.style("margin-right", "100%")
.attr("id", "category_picker_label")
.append("img")
.attr({
name: "Filter by event category",
class: "filter_header_icon",
height: 30,
width: 30,
title: "Filter by event category",
src: imageUrls("categories.png")
});
var all_categories = ["( All )"];
category_filter.append("select")
.attr("class", "filter_select")
.attr("size", 8)
.attr("id", "category_picker")
.attr({
multiple: true
})
.on("change", function () {
instance._updateSelectedFilters(d3.select(this), "selected_categories");
})
.selectAll("option")
.data(all_categories.concat(globals.categories.domain().sort()))
.enter()
.append("option")
.text(function (d) { return d; })
.property("selected", function (d) {
return d === "( All )";
});
globals.selected_categories = selectWithParent("#category_picker")
.selectAll("option")
.filter(function () {
return this.selected;
});
/**
---------------------------------------------------------------------------------------
PROCESS FACETS
---------------------------------------------------------------------------------------
**/
// determine facets (separate timelines) from data
globals.facets.domain(data.map(function (d) {
return d.facet;
}));
globals.facets.domain().sort();
globals.num_facets = globals.facets.domain().length;
globals.total_num_facets = globals.num_facets;
globals.num_facet_cols = Math.ceil(Math.sqrt(globals.num_facets));
globals.num_facet_rows = Math.ceil(globals.num_facets / globals.num_facet_cols);
//logEvent("# facets: " + globals.num_facets, "preprocessing");
var facet_filter = filter_div.append("div")
.attr("class", "filter_div_section");
var facet_filter_header = facet_filter.append("div")
.attr("class", "filter_div_header");
facet_filter_header.append("text")
.attr("class", "menu_label filter_label")
.text("Facet");
facet_filter_header.append("label")
.attr("for", "facet_picker")
.style("display", "block")
.style("margin-right", "100%")
.attr("id", "facet_picker_label")
.append("img")
.attr({
name: "Filter by event facet",
class: "filter_header_icon",
height: 30,
width: 30,
title: "Filter by event facet",
src: imageUrls("facets.png")
});
var all_facets = ["( All )"];
facet_filter.append("select")
.attr("class", "filter_select")
.attr("size", 8)
.attr("id", "facet_picker")
.attr({
multiple: true
})
.on("change", function () {
instance._updateSelectedFilters(d3.select(this), "selected_facets");
})
.selectAll("option")
.data(all_facets.concat(globals.facets.domain().sort()))
.enter()
.append("option")
.text(function (d) { return d; })
.property("selected", function (d) {
return d === "( All )";
});
globals.selected_facets = selectWithParent("#facet_picker")
.selectAll("option")
.filter(function () {
return this.selected;
});
/**
---------------------------------------------------------------------------------------
PROCESS SEGMENTS
---------------------------------------------------------------------------------------
**/
// event sorting function
data.sort(compareAscending);
if (globals.date_granularity === "epochs") {
data.min_start_date = globals.earliest_date;
data.max_start_date = d3.max([globals.latest_start_date, globals.latest_end_date]);
data.max_end_date = d3.max([globals.latest_start_date, globals.latest_end_date]);
} else {
// determine the time domain of the data along a linear quantitative scale
data.min_start_date = d3.min(data, function (d) {
return d.start_date;
});
data.max_start_date = d3.max(data, function (d) {
return d.start_date;
});
data.max_end_date = d3.max(data, function (d) {
return time.minute.floor(d.end_date);
});
}
// determine the granularity of segments
globals.segment_granularity = getSegmentGranularity(data.min_start_date, data.max_end_date);
data.forEach(function (item) {
item.segment = getSegment(item.start_date);
});
var segment_list = getSegmentList(data.min_start_date, data.max_end_date);
globals.present_segments.domain(segment_list.map(function (d) {
return d;
}));
var segment_filter = filter_div.append("div")
.attr("class", "filter_div_section");
var segment_filter_header = segment_filter.append("div")
.attr("class", "filter_div_header");
segment_filter_header.append("text")
.attr("class", "menu_label filter_label")
.text("Segment");
segment_filter_header.append("label")
.attr("for", "segment_picker")
.style("display", "block")
.style("margin-right", "100%")
.attr("id", "segment_picker_label")
.append("img")
.attr({
name: "Filter by chronological segment",
class: "filter_header_icon",
height: 30,
width: 30,
title: "Filter by chronological segment",
src: imageUrls("segments.png")
});
var all_segments = ["( All )"];
segment_filter.append("select")
.attr("id", "segment_picker")
.attr("class", "filter_select")
.attr("size", 8)
.attr({
multiple: true
})
.on("change", function () {
instance._updateSelectedFilters(d3.select(this), "selected_segments");
})
.selectAll("option")
.data(all_segments.concat(globals.present_segments.domain().sort()))
.enter()
.append("option")
.text(function (d) { return d; })
.property("selected", function (d) {
return d === "( All )";
});
globals.selected_segments = selectWithParent("#segment_picker")
.selectAll("option")
.filter(function () {
return this.selected;
});
globals.all_data = data;
globals.active_data = globals.all_data;
measureTimeline(globals.active_data);
selectWithParent("#timeline_metadata").style("display", "inline");
selectWithParent("#timeline_metadata_contents")
.append("span")
.attr("class", "metadata_title")
.style("text-decoration", "underline")
.text("About this data:");
selectWithParent("#timeline_metadata_contents")
.append("div")
.attr("class", "timeline_metadata_contents_div")
.html("<p class='metadata_content'><img src='" + imageUrls("timeline.png") + "' width='36px' style='float: left; padding-right: 5px;'/><strong>Cardinality & extent</strong>: " +
globals.active_data.length + " unique events spanning " + globals.range_text + " <br><strong>Granularity</strong>: " + globals.segment_granularity + "</p>");
var category_metadata = selectWithParent("#timeline_metadata_contents")
.append("div")
.attr("class", "timeline_metadata_contents_div")
.style("border-top", "1px dashed #999");
var category_metadata_p = category_metadata
.append("p")
.attr("class", "metadata_content")
.html("<img src='" + imageUrls("categories.png") + "' width='36px' style='float: left; padding-right: 5px;'/><strong>Event categories</strong>: ( " + globals.num_categories + " ) <em><strong>Note</strong>: click on the swatches to assign custom colors to categories.</em><br>");
var category_metadata_element = category_metadata_p.selectAll(".category_element")
.data(globals.categories.domain().sort())
.enter()
.append("g")
.attr("class", "category_element");
category_metadata_element.append("div")
.attr("class", "colorpicker_wrapper")
.attr("filter", "url(#drop-shadow)")
.style("background-color", globals.categories)
.on("click", function (d, i) {
var colorEle = this;
instance._colorPicker.show(this, globals.categories(d), function (value) {
// Update the display
d3.select(colorEle).style("background-color", value);
instance.setCategoryColor(d, i, value);
});
});
// .append("input")
// .attr("type", "color")
// .attr("class", "colorpicker")
// .attr("value", globals.categories)
// .on("change", function (d, i) {
// });
category_metadata_element.append("span")
.attr("class", "metadata_content")
.style("float", "left")
.text(function (d) {
return " " + d + " ..";
});
category_metadata.append("p")
.html("<br>");
selectWithParent("#timeline_metadata_contents")
.append("div")
.attr("class", "timeline_metadata_contents_div")
.style("border-top", "1px dashed #999")
.html(
"<p class='metadata_content'><img src='" + imageUrls("facets.png") + "' width='36px' style='float: left; padding-right: 5px;'/><strong>Timeline facets</strong>: " +
((globals.facets.domain().length > 1) ? ("( " + globals.num_facets + " ) " + globals.facets.domain().slice(0, 30).join(" .. ")) : "(none)") + "</p>");
if (shouldDrawTimeline) {
drawTimeline(globals.active_data);
}
}