summvis / resources /summvis.js
cbensimon's picture
cbensimon HF staff
Initial commit
6124176 unverified
$(document).ready(
function () {
// Define global variables
let isDragging = false;
let saveDragPos;
let rtime;
let timeout = false;
let delta = 200;
let disableScrollEvent = false;
let annotateLexical = false;
let annotateSemantic = false;
let annotateNovel = false;
let annotateEntities = false;
// Define functions
function clamp(number, min, max) {
return Math.max(min, Math.min(number, max));
}
function hasScroll() {
const el = $(".display .main-doc");
return el.prop("scrollHeight") > el.prop("clientHeight");
}
function scrollBy(delta) {
const proxyDoc = $(".display .proxy-doc");
const proxyScroll = proxyDoc.find(".proxy-scroll");
const currentTop = parseFloat(proxyScroll.css("top"));
const newTop = clamp(currentTop + delta, 0, proxyDoc.innerHeight() - proxyScroll.innerHeight());
proxyScroll.css("top", newTop);
const mainDoc = $(".display .main-doc");
const scaleY = mainDoc[0].scrollHeight / proxyDoc.innerHeight();
mainDoc.scrollTop(newTop * scaleY)
}
function getSpanId(el) {
return getSpanIds(el)[0]
}
function getSpanIds(el) {
return el.attr("class").split(/\s+/).filter(function (x) {
return x.startsWith("span-")
});
}
function createProxy() {
const mainDoc = $(".display .main-doc");
const proxyDoc = $(".display .proxy-doc");
const proxyHeight = proxyDoc.innerHeight();
const proxyWidth = proxyDoc.innerWidth();
const scaleX = 0.8 * proxyWidth / mainDoc.innerWidth();
const scaleY = proxyHeight / mainDoc[0].scrollHeight;
const scrollTop = mainDoc.scrollTop();
const proxyScrollTop = scrollTop * scaleY;
const proxyScrollBottom = (scrollTop + mainDoc.innerHeight()) * scaleY;
const proxyScrollHeight = proxyScrollBottom - proxyScrollTop;
proxyDoc.empty();
// Loop through underlines in doc view and create associated proxy element
if (annotateLexical) {
$(".display .main-doc .token-underline").each(
function (index, value) {
const el = $(value);
const x = el.position().left;
const y = mainDoc.scrollTop() + el.position().top - mainDoc.position().top;
const newHeight = 3;
const color = el.css("border-bottom-color");
const proxyPadding = proxyDoc.innerWidth() - proxyDoc.width();
const newX = x * scaleX + proxyPadding / 2;
const newY = (y + el.height()) * scaleY - newHeight;
const newWidth = Math.min(
Math.max((el.width() * scaleX) + 1, 5),
proxyDoc.width() + proxyPadding / 2 - newX
);
let classes = "proxy-underline annotation-hidden " + getSpanIds(el).join(" ");
const proxyEl = $('<div/>', {
"class": classes,
"css": {
"position": "absolute",
"left": Math.round(newX),
"top": Math.round(newY),
"background-color": color,
"width": newWidth,
"height": newHeight,
}
}).appendTo(proxyDoc);
proxyEl.data(el.data());
}
);
}
// Loop through all active highlights in doc view and create associated proxy element
if (annotateSemantic) {
$(".display .main-doc .highlight").each(
function (index, value) {
const el = $(value);
const x = el.position().left;
const y = mainDoc.scrollTop() + el.position().top - mainDoc.position().top;
const newHeight = 5;
const color = el.css("background-color");
const proxyPadding = proxyDoc.innerWidth() - proxyDoc.width()
const newX = x * scaleX + proxyPadding / 2;
const newY = (y + el.height()) * scaleY - newHeight;
const newWidth = Math.min(
Math.max((el.width() * scaleX) + 1, 5),
proxyDoc.width() + proxyPadding / 2 - newX
);
const proxyEl = $('<div/>', {
"class": 'proxy-highlight annotation-hidden',
"css": {
"position": "absolute",
"left": Math.round(newX),
"top": Math.round(newY),
"background-color": color,
"width": newWidth,
"height": newHeight,
}
}).appendTo(proxyDoc);
// Copy data attributes
proxyEl.data(el.data());
// Set classes for matching
proxyEl.addClass(el.data("match-classes"))
}
);
}
$('<div/>', {
"class": 'proxy-scroll hidden',
"css": {
"top": proxyScrollTop,
"height": proxyScrollHeight,
}
}).appendTo(proxyDoc);
if (hasScroll()) {
$(".display .proxy-scroll").removeClass("hidden")
}
$(".display .proxy-doc")
.mousedown(function (event) {
saveDragPos = parseFloat(event.pageY);
isDragging = true;
event.preventDefault();
})
.mousemove(function (event) {
const dragPos = parseFloat(event.pageY);
if (isDragging) {
const distanceMoved = dragPos - saveDragPos;
scrollBy(distanceMoved);
saveDragPos = dragPos;
event.preventDefault();
}
})
.mouseup(function (event) {
isDragging = false;
})
.mouseenter(function () {
disableScrollEvent = true;
$(".display .proxy-scroll").addClass("hover")
})
.mouseleave(function () {
isDragging = false;
disableScrollEvent = false;
$(".display .proxy-scroll").removeClass("hover")
})
.on('wheel', function (event) {
scrollBy(event.originalEvent.deltaY / 4);
event.preventDefault();
});
// TODO: Handle user clicking in scroll region
$(".display .main-doc").scroll(function () {
if (disableScrollEvent) return;
$(".display .proxy-scroll")
.css(
"top", $(this).scrollTop() * scaleY
)
})
}
function resizeend() {
if (new Date() - rtime < delta) {
setTimeout(resizeend, delta);
} else {
timeout = false;
updateAnnotations();
toggleScrollbar();
}
}
function toggleScrollbar() {
if (hasScroll()) {
$(".display .proxy-scroll").removeClass("hidden");
} else {
$(".display .proxy-scroll").addClass("hidden");
}
}
function updateAnnotations() {
annotateSemantic = $("#option-semantic").hasClass("selected");
annotateLexical = $("#option-lexical").hasClass("selected");
annotateEntities = $("#option-entity").hasClass("selected");
annotateNovel = $("#option-novel").hasClass("selected");
if (annotateSemantic || annotateLexical) {
$(".summary-item").addClass("selectable")
} else {
$(".summary-item").removeClass("selectable")
}
if (annotateLexical) {
$(".underline").removeClass("annotation-hidden");
$(".summary-item").addClass("annotate-lexical");
} else {
$(".underline").addClass("annotation-hidden");
$(".summary-item").removeClass("annotate-lexical");
}
if (annotateSemantic) {
$(".highlight").removeClass("annotation-hidden");
} else {
$(".highlight").addClass("annotation-hidden");
}
if (annotateEntities) {
$(".summary-item").addClass("annotate-entities")
} else {
$(".summary-item").removeClass("annotate-entities")
}
if (annotateNovel) {
$(".summary-item").addClass("annotate-novel")
} else {
$(".summary-item").removeClass("annotate-novel")
}
createProxy();
if (annotateLexical) {
$(".proxy-underline").removeClass("annotation-hidden");
} else {
$(".proxy-underline").addClass("annotation-hidden");
}
if (annotateSemantic) {
$(".proxy-highlight").removeClass("annotation-hidden");
} else {
$(".proxy-highlight").addClass("annotation-hidden");
}
$(".summary-item .highlight").tooltip("disable");
if (annotateSemantic) {
$(".summary-item.selected .highlight").tooltip("enable")
}
}
function removeDocTooltips() {
$("[data-tooltip-timestamp]").tooltip("dispose").removeAttr("data-tooltip-timestamp");
}
function resetUnderlines() {
$('.annotation-invisible').removeClass("annotation-invisible");
$('.annotation-inactive').removeClass("annotation-inactive");
$('.temp-underline-color')
.each(function () {
$(this).css("border-color", $(this).data("primary-color"));
})
.removeClass("temp-underline-color")
$('.temp-proxy-underline-color')
.each(function () {
$(this).css("background-color", $(this).data("primary-color"));
})
.removeClass("temp-proxy-underline-color")
}
function showDocTooltip(el) {
const topDocHighlightId = $(el).data("top-doc-highlight-id");
const topDocSim = $(el).data("top-doc-sim");
const topHighlight = $(`.display .main-doc .highlight[data-highlight-id=${topDocHighlightId}]`);
if (!isViewable(topHighlight)) {
return;
}
topHighlight.tooltip({title: `Most similar (${topDocSim})`, trigger: "manual", container: "body"});
topHighlight.tooltip("show");
const tooltipTimestamp = Date.now();
// Do not use .data() method to set data attributes as they are not searchable
topHighlight.attr("data-tooltip-timestamp", tooltipTimestamp);
setTimeout(function () {
if (topHighlight.data("tooltip-timestamp") == tooltipTimestamp) {
topHighlight.tooltip("dispose").removeAttr("data-tooltip-timestamp");
}
}, 8000);
}
function highlightUnderlines() {
const spanId = getSpanId($(this));
const color = $(this).css("border-bottom-color");
// TODO Consolidate into single statement
$(`.summary-item.selected .underline.${spanId}`).removeClass("annotation-inactive");
$(`.doc .underline.${spanId}`)
.removeClass("annotation-inactive")
.each(function () {
$(this).css("border-bottom-color", color);
})
.addClass("temp-underline-color");
$(`.proxy-underline.${spanId}`)
.removeClass("annotation-inactive")
.each(function () {
$(this).css("background-color", color);
})
.addClass("temp-proxy-underline-color");
$(`.summary-item.selected .underline:not(.${spanId})`).addClass("annotation-inactive");
$(`.doc .underline:not(.${spanId})`).addClass("annotation-inactive");
$(`.proxy-underline:not(.${spanId})`).addClass("annotation-inactive");
$(".summary-item.selected .highlight:not(.annotation-hidden)").addClass("annotation-inactive");
}
function resetHighlights() {
removeDocTooltips();
$('.summary-item.selected .annotation-inactive').removeClass("annotation-inactive");
$('.summary-item.selected .annotation-invisible').removeClass("annotation-invisible");
$('.temp-highlight-color')
.each(function () {
$(this).css("background-color", $(this).data("primary-color"));
})
.removeClass("temp-highlight-color");
$('.highlight.selected').removeClass("selected");
$('.proxy-highlight.selected').removeClass("selected");
$('.summary-item [title]').removeAttr("title");
}
function highlightToken() {
const highlightId = $(this).data("highlight-id");
$(`.summary-item.selected .highlight:not(.summary-highlight-${highlightId})`).addClass("annotation-inactive");
$('.highlight.selected').removeClass("selected")
$('.proxy-highlight.selected').removeClass("selected")
const matchedDocHighlight = `.display .main-doc .summary-highlight-${highlightId}`;
const matchedProxyHighlight = `.proxy-doc .summary-highlight-${highlightId}`;
$(matchedDocHighlight + ", " + matchedProxyHighlight)
.each(function () {
const newHighlightColor = $(this).data(`color-${highlightId}`);
$(this).css("background-color", newHighlightColor);
$(this).addClass("selected");
})
.addClass("temp-highlight-color");
$(".underline").addClass("annotation-inactive");
$(".proxy-underline").addClass("annotation-invisible")
showDocTooltip(this);
$(this).addClass("selected");
$(this).removeClass("annotation-inactive");
$('.summary-item [title]').removeAttr("title");
if (!isViewable($(matchedDocHighlight))) {
$(this).attr("title", "Click to scroll to most similar word.")
}
}
function isViewable(el) {
const elTop = el.offset().top;
const elBottom = elTop + el.outerHeight();
const scrollRegion = $(".display .main-doc");
const scrollTop = scrollRegion.offset().top;
const scrollBottom = scrollTop + scrollRegion.outerHeight();
return elTop > scrollTop && elBottom < scrollBottom;
}
// Initialization
$(function () {
$('[data-toggle="tooltip"]').tooltip({
// 'boundary': '.summary-container'
trigger: 'hover'
})
})
updateAnnotations();
// Bind events
$(window).resize(function () {
rtime = new Date();
if (timeout === false) {
timeout = true;
setTimeout(resizeend, delta);
}
});
$(".summary-list").on(
"click",
".summary-item.selectable:not(.selected)",
function () {
const summary_index = $(this).data("index");
// Update summary items
$(".summary-item.selected").removeClass("selected")
$(this).addClass("selected")
// Update doc
// Show the version of document aligned with selected summary index
$(`.doc[data-index=${summary_index}]`).removeClass("nodisplay").addClass("display");
// Hide the version of document not aligned with selected summary index
$(`.doc[data-index!=${summary_index}]`).removeClass("display").addClass("nodisplay");
updateAnnotations();
}
);
$("#option-lexical").click(function () {
$(this).toggleClass("selected")
updateAnnotations()
});
$("#option-semantic").click(function () {
$(this).toggleClass("selected")
updateAnnotations()
});
$("#option-novel").click(function () {
$(this).toggleClass("selected")
updateAnnotations()
});
$("#option-entity").click(function () {
$(this).toggleClass("selected")
updateAnnotations()
});
const activeUnderlines = ".summary-item.selected .underline:not(.annotation-inactive):not(.annotation-hidden)";
$(".summary-list").on(
"mouseenter",
activeUnderlines,
function () {
highlightUnderlines.call(this);
}
);
$(".summary-list").on(
"mouseleave",
activeUnderlines,
resetUnderlines
);
$(".summary-list").on(
"click",
activeUnderlines,
function () {
// Find aligned underline in doc and scroll doc to that position
highlightUnderlines.call(this);
const mainDoc = $(".display .main-doc");
const spanId = getSpanId($(this));
const matchedUnderline = $(`.doc .underline.${spanId}`);
mainDoc.animate({
scrollTop: mainDoc.scrollTop() +
matchedUnderline.offset().top - mainDoc.offset().top - 60
},
300
)
}
);
const activeHighlights = ".summary-item.selected .highlight:not(.annotation-hidden):not(.matches-ngram), " +
".summary-item.selected:not(.annotate-lexical) .highlight:not(.annotation-hidden)";
$(".summary-list").on(
"mouseenter",
activeHighlights,
function () {
highlightToken.call(this);
})
$(".summary-list").on(
"mouseleave",
activeHighlights,
function () {
resetHighlights();
resetUnderlines();
}
);
$(".summary-list").on(
"click",
activeHighlights,
function () {
highlightToken.call(this);
// Find corresponding highlight in doc representing max similarity and scroll doc to that position
const topDocHighlightId = $(this).data("top-doc-highlight-id");
removeDocTooltips(topDocHighlightId);
const topDocHighlight = $(`.display .main-doc .highlight[data-highlight-id=${topDocHighlightId}]`);
const mainDoc = $(".display .main-doc");
const el = this;
mainDoc.animate({
scrollTop: mainDoc.scrollTop() +
topDocHighlight.offset().top - mainDoc.offset().top - 60
},
300,
function () {
setTimeout(
function () {
// If no other tooltips have since been displayed
if ($("[data-tooltip-timestamp]").length == 0) {
showDocTooltip(el);
} else {
console.log("Not showing tooltip because one already exists")
}
},
100
)
}
)
}
);
$(".summary-list").on(
"mouseleave",
".summary-item.selected .content",
function () {
resetHighlights();
resetUnderlines();
},
);
}
);