Spaces:
Running on CPU Upgrade
Running on CPU Upgrade
| import * as d3 from "d3"; | |
| import * as R from 'ramda' | |
| import 'd3-selection-multi' | |
| import {d3S, D3Sel} from "../etc/Util"; | |
| import { VComponent } from "./VisComponent"; | |
| import { SimpleEventHandler } from "../etc/SimpleEventHandler"; | |
| import * as tp from "../etc/types" | |
| import '../etc/xd3' | |
| // Helpers | |
| const currMatchIdx = (elem) => +(<Element>elem.parentNode).getAttribute('matchidx') | |
| const currRowNum = (elem) => +(<Element>elem.parentNode).getAttribute('rownum') | |
| const backgroundColor = x => `rgba(128, 0, 150, ${0.6*x})` | |
| export class CorpusInspector extends VComponent<tp.FaissSearchResults[]>{ | |
| css_name = 'corpus-inspector'; | |
| _current: {}; | |
| _data: tp.FaissSearchResults[]; // The passed data | |
| static events = { | |
| rowMouseOver: "CorpusInspector_rowMouseOver", | |
| rowMouseOut: "CorpusInspector_rowMouseOut", | |
| rowClick: "CorpusInspector_rowClick", | |
| rowDblClick: "CorpusInspector_rowDblClick", | |
| cellMouseOver: "CorpusInspector_cellMouseOver", | |
| cellMouseOut: "CorpusInspector_cellMouseOut", | |
| cellClick: "CorpusInspector_cellClick", | |
| cellDblClick: "CorpusInspector_cellDblClick", | |
| } | |
| options = { | |
| showNext: false | |
| } | |
| // COMPONENTS | |
| inspectorRows: D3Sel | |
| inspectorCells: D3Sel | |
| scaler = d3.scalePow().range([0,0.9]).exponent(2) | |
| constructor(d3Parent: D3Sel, eventHandler?:SimpleEventHandler, options: {} = {}) { | |
| super(d3Parent, eventHandler) | |
| this.superInitHTML(options) | |
| this._init() | |
| } | |
| private createRows() { | |
| const data = this._data | |
| this.inspectorRows = this.base.selectAll(".inspector-row") | |
| .data(data) | |
| .join('div') | |
| .classed('inspector-row', true) | |
| .attrs({ | |
| matchIdx: d => d.index, | |
| rowNum: (d, i) => i, | |
| }) | |
| .on("mouseover", (d, i) => { | |
| this.eventHandler.trigger(CorpusInspector.events.rowMouseOver, {}) | |
| }) | |
| } | |
| private addTooltip() { | |
| this.inspectorCells = this.inspectorCells | |
| .classed('celltooltip', true) | |
| .append('span') | |
| .classed('tooltiptext', true) | |
| .html((d, i, n) => { | |
| const entityStr = d.is_ent ? "<br>Entity" : "" | |
| const att = (<Element>n[i].parentNode).getAttribute('att').slice(0, 7) | |
| const attStr = `<br>Attention: ${att}` | |
| return `POS: ${d.pos.toLowerCase()}<br>DEP: ${d.dep.toLowerCase()}` + entityStr + attStr | |
| }) | |
| } | |
| private createCells() { | |
| const self = this | |
| this.inspectorCells = this.inspectorRows.selectAll('.inspector-cell') | |
| .data((d:tp.FaissSearchResults) => d.tokens) | |
| .join('div') | |
| .classed('inspector-cell', true) | |
| .attr('index-offset', (d, i, n:HTMLElement[]) => { | |
| const matchIdx = currMatchIdx(n[i]) | |
| return i - matchIdx | |
| }) | |
| .attrs({ | |
| pos: d => d.pos.toLowerCase(), | |
| dep: d => d.dep.toLowerCase(), | |
| is_ent: d => d.is_ent | |
| }) | |
| .text(d => d.token.replace("\u0120", " ")) | |
| .classed('matched-cell', d => d.is_match) | |
| .classed('next-cell', function(d) { | |
| return self.showNext() && d.is_next_word | |
| }) | |
| .classed('gray-cell', function(d, i) { | |
| const idx = +currMatchIdx(this) | |
| return self.showNext() && i > idx | |
| }) | |
| // Highlight the cells appropriately | |
| this.inspectorCells.each((d,i,n) => { | |
| const idx = currMatchIdx(n[i]) | |
| if (i == idx) { | |
| const att = d.inward | |
| const maxAtt = +d3.max(att) | |
| const currRow = currRowNum(n[i]) | |
| const scaler = self.scaler.domain([0, maxAtt]) | |
| d3.selectAll(`.inspector-row[rownum='${currRow}']`) | |
| .selectAll(`.inspector-cell`) | |
| .style('background', (d, i) => { | |
| return backgroundColor(scaler(att[i])) | |
| }) | |
| .attr('att', (d, i) => att[i]) | |
| } | |
| }) | |
| self.addTooltip() | |
| } | |
| private updateData() { | |
| this.createRows() | |
| this.createCells() | |
| } | |
| _init() {} | |
| _wrangle(data: tp.FaissSearchResults[]) { | |
| this._data = data | |
| return data; | |
| } | |
| _render(data: tp.FaissSearchResults[]) { | |
| // Remember that this._data is defined in wrangle which should always be called before render | |
| // as is defined in the update function | |
| this.updateData() | |
| } | |
| showNext(): boolean | |
| showNext(v:boolean): this | |
| showNext(v?) { | |
| if (v == null) return this.options.showNext | |
| this.options.showNext = v | |
| return this | |
| } | |
| } |