Fixed author and resource detail page (#59)
This commit is contained in:
Родитель
630fbc9d83
Коммит
18de6643a5
|
@ -87,7 +87,12 @@ For full license text, see LICENSE.txt file in the repo root or https://opensour
|
|||
<a href="{{.Url}}" target="_blank">{{.Text}}</a><br/>
|
||||
{{end}}
|
||||
|
||||
</div><div id="d3_here" class="svg-container" style='width: 100%; height:100%;'>
|
||||
</div>
|
||||
<div id="d3_here" class="svg-container" style='width: 100%; height:100%;'>
|
||||
|
||||
<div id="tooltip_container">
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<script src="https://d3js.org/d3.v5.js"></script>
|
||||
<script
|
||||
|
|
|
@ -15,6 +15,10 @@ html, body {
|
|||
width: 100%;
|
||||
}
|
||||
|
||||
#resource_container {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
#resource_event_table td, #resource_event_table th {
|
||||
border: 1px solid lightgrey;
|
||||
padding: 4px;
|
||||
|
|
|
@ -20,214 +20,218 @@ For full license text, see LICENSE.txt file in the repo root or https://opensour
|
|||
|
||||
</head>
|
||||
<body>
|
||||
<h2>Details</h2>
|
||||
<b>Name</b>: {{.Name}}<br>
|
||||
<b>Namespace</b>: {{.Namespace}}<br>
|
||||
<b>Kind</b>: {{.Kind}}<br><br>
|
||||
<div id="resource_container">
|
||||
|
||||
<a href="{{.SelfUrl}}" target="_blank">Open In New Tab</a><br>
|
||||
<h2>Details</h2>
|
||||
<b>Name</b>: {{.Name}}<br>
|
||||
<b>Namespace</b>: {{.Namespace}}<br>
|
||||
<b>Kind</b>: {{.Kind}}<br><br>
|
||||
|
||||
<a href="{{.SelfUrl}}" target="_blank">Open In New Tab</a><br>
|
||||
|
||||
|
||||
{{if ne (len .Links) 0 }}
|
||||
<h2>Links</h2>
|
||||
{{range .Links}}<a href="{{.Url}}" target="_blank">{{.Text}}</a><br>{{end}}
|
||||
{{end}}
|
||||
{{if ne (len .Links) 0 }}
|
||||
<h2>Links</h2>
|
||||
{{range .Links}}<a href="{{.Url}}" target="_blank">{{.Text}}</a><br>{{end}}
|
||||
{{end}}
|
||||
|
||||
<div id="resource_payload">
|
||||
<h2>Payloads from ${ getStartTime} to ${getEndTime}</h2>
|
||||
<table id="resource_event_table">
|
||||
<tr>
|
||||
<th>Payload Link</th>
|
||||
<th @click="sort('payloadTime')">PayloadTime ⇅</th>
|
||||
</tr>
|
||||
<tr v-for="res in sortedResPayloads">
|
||||
<td><a :href ="res.origValue | get_payload_url" target="_blank">Details</a></td>
|
||||
<td>${ res.payloadTime | get_formatted_time}</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<div id="resource_payload">
|
||||
<h2>Payloads from ${ getStartTime} to ${getEndTime}</h2>
|
||||
<table id="resource_event_table">
|
||||
<tr>
|
||||
<th>Payload Link</th>
|
||||
<th @click="sort('payloadTime')">PayloadTime ⇅</th>
|
||||
</tr>
|
||||
<tr v-for="res in sortedResPayloads">
|
||||
<td><a :href ="res.origValue | get_payload_url" target="_blank">Details</a></td>
|
||||
<td>${ res.payloadTime | get_formatted_time}</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
<script>
|
||||
new Vue({
|
||||
el: '#resource_payload',
|
||||
delimiters: ['${', '}'],
|
||||
data: {
|
||||
resPayload: [],
|
||||
currentSortBy: 'payloadTime',
|
||||
currentSortDirection: 'asc',
|
||||
startTime: ''
|
||||
new Vue({
|
||||
el: '#resource_payload',
|
||||
delimiters: ['${', '}'],
|
||||
data: {
|
||||
resPayload: [],
|
||||
currentSortBy: 'payloadTime',
|
||||
currentSortDirection: 'asc',
|
||||
startTime: ''
|
||||
},
|
||||
filters: {
|
||||
get_formatted_time(unixnano_time) {
|
||||
return new Date(unixnano_time/1000000).toISOString().split('T').join(' ');
|
||||
},
|
||||
filters: {
|
||||
get_formatted_time(unixnano_time) {
|
||||
return new Date(unixnano_time/1000000).toISOString().split('T').join(' ');
|
||||
},
|
||||
get_payload_url(value) {
|
||||
return "/debug/view/?k=" + value.payloadKey;
|
||||
},
|
||||
get_payload_url(value) {
|
||||
return "/debug/view/?k=" + value.payloadKey;
|
||||
},
|
||||
mounted() {
|
||||
NProgress.configure({
|
||||
easing: 'ease',
|
||||
minimum: 0.3,
|
||||
parent: '#resource_payload'
|
||||
});
|
||||
},
|
||||
mounted() {
|
||||
NProgress.configure({
|
||||
easing: 'ease',
|
||||
minimum: 0.3,
|
||||
parent: '#resource_payload'
|
||||
});
|
||||
|
||||
axios.interceptors.request.use(config => {
|
||||
NProgress.start();
|
||||
return config;
|
||||
});
|
||||
axios.interceptors.request.use(config => {
|
||||
NProgress.start();
|
||||
return config;
|
||||
});
|
||||
|
||||
axios.interceptors.response.use(response => {
|
||||
NProgress.done();
|
||||
return response;
|
||||
});
|
||||
axios.interceptors.response.use(response => {
|
||||
NProgress.done();
|
||||
return response;
|
||||
});
|
||||
|
||||
axios
|
||||
.get('{{.PayloadUrl}}')
|
||||
.then(response => {
|
||||
if (response.data) {
|
||||
this.resPayload = response.data.map(function (val) {
|
||||
return {
|
||||
payloadTime: val.payloadTime,
|
||||
origValue: val
|
||||
};
|
||||
});
|
||||
} else {
|
||||
console.log("No payloads found for period")
|
||||
}
|
||||
})
|
||||
},
|
||||
methods: {
|
||||
sort: function (sortBy) {
|
||||
if (sortBy === this.currentSortBy) {
|
||||
this.currentSortDirection = (this.currentSortDirection === 'asc') ? 'desc' : 'asc';
|
||||
axios
|
||||
.get('{{.PayloadUrl}}')
|
||||
.then(response => {
|
||||
if (response.data) {
|
||||
this.resPayload = response.data.map(function (val) {
|
||||
return {
|
||||
payloadTime: val.payloadTime,
|
||||
origValue: val
|
||||
};
|
||||
});
|
||||
} else {
|
||||
console.log("No payloads found for period")
|
||||
}
|
||||
this.currentSortBy = sortBy;
|
||||
},
|
||||
})
|
||||
},
|
||||
methods: {
|
||||
sort: function (sortBy) {
|
||||
if (sortBy === this.currentSortBy) {
|
||||
this.currentSortDirection = (this.currentSortDirection === 'asc') ? 'desc' : 'asc';
|
||||
}
|
||||
this.currentSortBy = sortBy;
|
||||
},
|
||||
computed: {
|
||||
sortedResPayloads: function () {
|
||||
return this.resPayload.sort((a, b) => {
|
||||
let direction = (this.currentSortDirection === 'asc') ? 1 : -1;
|
||||
if (a[this.currentSortBy] < b[this.currentSortBy]) return -1 * direction;
|
||||
if (a[this.currentSortBy] > b[this.currentSortBy]) return direction;
|
||||
return 0;
|
||||
});
|
||||
},
|
||||
getStartTime: function () {
|
||||
payloadTimeList = this.resPayload.map(function (val) {
|
||||
formattedTime = new Date(val.payloadTime/1000000).toISOString().split('T').join(' ');
|
||||
return formattedTime;
|
||||
});
|
||||
return payloadTimeList[0];
|
||||
},
|
||||
getEndTime: function () {
|
||||
payloadTimeList = this.resPayload.map(function (val) {
|
||||
formattedTime = new Date(val.payloadTime/1000000).toISOString().split('T').join(' ');
|
||||
return formattedTime;
|
||||
});
|
||||
return payloadTimeList[payloadTimeList.length-1];
|
||||
},
|
||||
}
|
||||
});
|
||||
},
|
||||
computed: {
|
||||
sortedResPayloads: function () {
|
||||
return this.resPayload.sort((a, b) => {
|
||||
let direction = (this.currentSortDirection === 'asc') ? 1 : -1;
|
||||
if (a[this.currentSortBy] < b[this.currentSortBy]) return -1 * direction;
|
||||
if (a[this.currentSortBy] > b[this.currentSortBy]) return direction;
|
||||
return 0;
|
||||
});
|
||||
},
|
||||
getStartTime: function () {
|
||||
payloadTimeList = this.resPayload.map(function (val) {
|
||||
formattedTime = new Date(val.payloadTime/1000000).toISOString().split('T').join(' ');
|
||||
return formattedTime;
|
||||
});
|
||||
return payloadTimeList[0];
|
||||
},
|
||||
getEndTime: function () {
|
||||
payloadTimeList = this.resPayload.map(function (val) {
|
||||
formattedTime = new Date(val.payloadTime/1000000).toISOString().split('T').join(' ');
|
||||
return formattedTime;
|
||||
});
|
||||
return payloadTimeList[payloadTimeList.length-1];
|
||||
},
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<div id="resource_events">
|
||||
<h2>Events</h2>
|
||||
<table id="resource_event_table">
|
||||
<tr>
|
||||
<th>Payload Link</th>
|
||||
<th @click="sort('message')">Message ⇅</th>
|
||||
<th @click="sort('reason')">Reason ⇅</th>
|
||||
<th @click="sort('source')">Source ⇅</th>
|
||||
<th @click="sort('count')">Count ⇅</th>
|
||||
<th @click="sort('firstSeen')">First seen ⇅</th>
|
||||
<th @click="sort('lastSeen')">Last seen ⇅</th>
|
||||
</tr>
|
||||
<tr v-for="resEvent in sortedResEvents">
|
||||
<td><a :href ="resEvent.origValue | get_payload_url" target="_blank">Details</a></td>
|
||||
<td>${ resEvent.message }</td>
|
||||
<td>${ resEvent.reason }</td>
|
||||
<td>${ resEvent.source }</td>
|
||||
<td>${ resEvent.count }</td>
|
||||
<td>${ resEvent.firstSeen | get_formatted_date }</td>
|
||||
<td>${ resEvent.lastSeen | get_formatted_date }</td>
|
||||
</tr>
|
||||
</table>
|
||||
<h2>Events</h2>
|
||||
<table id="resource_event_table">
|
||||
<tr>
|
||||
<th>Payload Link</th>
|
||||
<th @click="sort('message')">Message ⇅</th>
|
||||
<th @click="sort('reason')">Reason ⇅</th>
|
||||
<th @click="sort('source')">Source ⇅</th>
|
||||
<th @click="sort('count')">Count ⇅</th>
|
||||
<th @click="sort('firstSeen')">First seen ⇅</th>
|
||||
<th @click="sort('lastSeen')">Last seen ⇅</th>
|
||||
</tr>
|
||||
<tr v-for="resEvent in sortedResEvents">
|
||||
<td><a :href ="resEvent.origValue | get_payload_url" target="_blank">Details</a></td>
|
||||
<td>${ resEvent.message }</td>
|
||||
<td>${ resEvent.reason }</td>
|
||||
<td>${ resEvent.source }</td>
|
||||
<td>${ resEvent.count }</td>
|
||||
<td>${ resEvent.firstSeen | get_formatted_date }</td>
|
||||
<td>${ resEvent.lastSeen | get_formatted_date }</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
<script>
|
||||
new Vue({
|
||||
el: '#resource_events',
|
||||
delimiters: ['${', '}'],
|
||||
data: {
|
||||
resEvents: [],
|
||||
currentSortBy: 'firstSeen',
|
||||
currentSortDirection: 'asc'
|
||||
new Vue({
|
||||
el: '#resource_events',
|
||||
delimiters: ['${', '}'],
|
||||
data: {
|
||||
resEvents: [],
|
||||
currentSortBy: 'firstSeen',
|
||||
currentSortDirection: 'asc'
|
||||
},
|
||||
filters: {
|
||||
get_formatted_date(value) {
|
||||
return value.split('T').join(' ');
|
||||
},
|
||||
filters: {
|
||||
get_formatted_date(value) {
|
||||
return value.split('T').join(' ');
|
||||
},
|
||||
get_payload_url(value) {
|
||||
return "/debug/view/?k=" + value.eventKey;
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
NProgress.configure({
|
||||
easing: 'ease',
|
||||
minimum: 0.3,
|
||||
parent: '#resource_events'
|
||||
});
|
||||
|
||||
axios.interceptors.request.use(config => {
|
||||
NProgress.start();
|
||||
return config;
|
||||
});
|
||||
|
||||
axios.interceptors.response.use(response => {
|
||||
NProgress.done();
|
||||
return response;
|
||||
});
|
||||
|
||||
axios
|
||||
.get('{{.EventsUrl}}')
|
||||
.then(response => {
|
||||
if (response.data) {
|
||||
this.resEvents = response.data.map(function (val) {
|
||||
let parsedVal = JSON.parse(val.payload);
|
||||
return {
|
||||
message: parsedVal.message,
|
||||
source: parsedVal.source.host,
|
||||
reason: parsedVal.reason,
|
||||
count: parsedVal.count,
|
||||
firstSeen: parsedVal.firstTimestamp,
|
||||
lastSeen: parsedVal.lastTimestamp,
|
||||
origValue: val
|
||||
};
|
||||
});
|
||||
} else {
|
||||
console.log("No events found for period")
|
||||
}
|
||||
})
|
||||
},
|
||||
methods: {
|
||||
sort: function (sortBy) {
|
||||
if (sortBy === this.currentSortBy) {
|
||||
this.currentSortDirection = (this.currentSortDirection === 'asc') ? 'desc' : 'asc';
|
||||
}
|
||||
this.currentSortBy = sortBy;
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
sortedResEvents: function () {
|
||||
return this.resEvents.sort((a, b) => {
|
||||
let direction = (this.currentSortDirection === 'asc') ? 1 : -1;
|
||||
if (a[this.currentSortBy] < b[this.currentSortBy]) return -1 * direction;
|
||||
if (a[this.currentSortBy] > b[this.currentSortBy]) return direction;
|
||||
return 0;
|
||||
});
|
||||
}
|
||||
get_payload_url(value) {
|
||||
return "/debug/view/?k=" + value.eventKey;
|
||||
}
|
||||
});
|
||||
},
|
||||
mounted() {
|
||||
NProgress.configure({
|
||||
easing: 'ease',
|
||||
minimum: 0.3,
|
||||
parent: '#resource_events'
|
||||
});
|
||||
|
||||
axios.interceptors.request.use(config => {
|
||||
NProgress.start();
|
||||
return config;
|
||||
});
|
||||
|
||||
axios.interceptors.response.use(response => {
|
||||
NProgress.done();
|
||||
return response;
|
||||
});
|
||||
|
||||
axios
|
||||
.get('{{.EventsUrl}}')
|
||||
.then(response => {
|
||||
if (response.data) {
|
||||
this.resEvents = response.data.map(function (val) {
|
||||
let parsedVal = JSON.parse(val.payload);
|
||||
return {
|
||||
message: parsedVal.message,
|
||||
source: parsedVal.source.host,
|
||||
reason: parsedVal.reason,
|
||||
count: parsedVal.count,
|
||||
firstSeen: parsedVal.firstTimestamp,
|
||||
lastSeen: parsedVal.lastTimestamp,
|
||||
origValue: val
|
||||
};
|
||||
});
|
||||
} else {
|
||||
console.log("No events found for period")
|
||||
}
|
||||
})
|
||||
},
|
||||
methods: {
|
||||
sort: function (sortBy) {
|
||||
if (sortBy === this.currentSortBy) {
|
||||
this.currentSortDirection = (this.currentSortDirection === 'asc') ? 'desc' : 'asc';
|
||||
}
|
||||
this.currentSortBy = sortBy;
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
sortedResEvents: function () {
|
||||
return this.resEvents.sort((a, b) => {
|
||||
let direction = (this.currentSortDirection === 'asc') ? 1 : -1;
|
||||
if (a[this.currentSortBy] < b[this.currentSortBy]) return -1 * direction;
|
||||
if (a[this.currentSortBy] > b[this.currentSortBy]) return direction;
|
||||
return 0;
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
*/
|
||||
|
||||
const displayMaxX = document.documentElement.clientWidth;
|
||||
const displayMaxY = document.documentElement.clientHeight;
|
||||
|
||||
let topAxis, bottomAxis, xAxisScale, yAxisBand, data, theTime, axisTop, axisBottom, smallBarOffset;
|
||||
|
||||
|
@ -167,7 +168,6 @@ function processAndSortResources(result) {
|
|||
}).sort(cmpFn);
|
||||
return data
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function compareKind(a, b) {
|
||||
|
@ -196,15 +196,12 @@ function appendAxes(svg) {
|
|||
}
|
||||
|
||||
function renderTooltip() {
|
||||
tooltip = d3.select(
|
||||
document.createElement("div"))
|
||||
tooltip = d3.select("#tooltip_container")
|
||||
.append("div")
|
||||
.call(createTooltip);
|
||||
|
||||
document.querySelector("body").appendChild(tooltip.node());
|
||||
}
|
||||
|
||||
function bindMouseEvents(svg) {
|
||||
|
||||
svg.on("mousemove", function () {
|
||||
let [x, y] = d3.mouse(this);
|
||||
|
||||
|
@ -216,9 +213,9 @@ function bindMouseEvents(svg) {
|
|||
line.attr("transform", `translate(${x} 0)`);
|
||||
theTime = xAxisScale.invert(x);
|
||||
if (!detailedToolTipIsVisible) {
|
||||
tooltip
|
||||
.style("left", d3.event.pageX + "px")
|
||||
.style("top", d3.event.pageY + 20 + "px")
|
||||
let tooltipX = d3.event.pageX;
|
||||
let tooltipY = d3.event.pageY;
|
||||
positionTooltip(tooltipX, tooltipY);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
@ -246,34 +243,7 @@ function bindMouseEvents(svg) {
|
|||
))
|
||||
}
|
||||
}).on("click", function (d) {
|
||||
if (detailedToolTipIsVisible) {
|
||||
resourceBarHtml = getResourceBarContent(
|
||||
{
|
||||
title: d.text,
|
||||
kind: d.kind,
|
||||
namespace: d.namespace,
|
||||
time: theTime
|
||||
}
|
||||
);
|
||||
tooltip.html(resourceBarHtml);
|
||||
detailedToolTipIsVisible = false
|
||||
} else {
|
||||
let [x, y] = d3.mouse(this);
|
||||
$.ajax({
|
||||
url: "/resource",
|
||||
data: {
|
||||
click_time: xAxisScale.invert(x).getTime(),
|
||||
name: d.text,
|
||||
namespace: d.namespace,
|
||||
kind: d.kind,
|
||||
},
|
||||
success: function (result) {
|
||||
detailedToolTipIsVisible = true;
|
||||
tooltip.html(result);
|
||||
evalJSFromHtml(result);
|
||||
}
|
||||
});
|
||||
}
|
||||
showDetailedTooltip(d, d3.event, this);
|
||||
});
|
||||
// Intuitively 'd' should be the 'heatmap' element - but for whatever reason
|
||||
// the event binds correctly but 'd' is the resource element. Not sure why - I think
|
||||
|
@ -312,6 +282,8 @@ function bindMouseEvents(svg) {
|
|||
d3.select(this).attr("fill", severityColorGenFunc(thisOverlay.severity));
|
||||
tooltip.style("opacity", 0)
|
||||
}
|
||||
}).on("click", function (d) {
|
||||
showDetailedTooltip(d, d3.event, this);
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -351,7 +323,6 @@ function createResourceBar(d) {
|
|||
let w = Math.max(xAxisScale(d.end) - xAxisScale(d.start), 10);
|
||||
const isLabelRight = (sx > displayMaxX / 2 ? sx + w < displayMaxX : sx - w > 0);
|
||||
|
||||
|
||||
el
|
||||
.append("rect")
|
||||
.attr("x", sx)
|
||||
|
@ -385,6 +356,7 @@ function createResourceBar(d) {
|
|||
.attr("fill", d3.color(severityColorGenFunc(overlay.severity)))
|
||||
.attr("title", text)
|
||||
.attr("transform", `translate(0 ${-smallBarOffset})`)
|
||||
.style("cursor", "pointer")
|
||||
.classed("heatmap", true)
|
||||
.attr("index", n++)
|
||||
}
|
||||
|
@ -420,7 +392,8 @@ function createResourceBar(d) {
|
|||
.attr("x", isLabelRight ? sx - 5 : sx + w + 5)
|
||||
.attr("fill", "black")
|
||||
.style("text-anchor", isLabelRight ? "end" : "start")
|
||||
.style("dominant-baseline", "hanging");
|
||||
.style("dominant-baseline", "hanging")
|
||||
.style("font-size", "14");
|
||||
}
|
||||
|
||||
function evalJSFromHtml(html) {
|
||||
|
@ -432,6 +405,8 @@ function evalJSFromHtml(html) {
|
|||
}
|
||||
}
|
||||
|
||||
// I think the detailed tooltip should probably be moved to
|
||||
// a modal dialog - it's getting to be too large and unwieldy
|
||||
function createTooltip(el) {
|
||||
el
|
||||
.style("position", "absolute")
|
||||
|
@ -443,5 +418,75 @@ function createTooltip(el) {
|
|||
.style("padding", "10px")
|
||||
.style("line-height", "1.3")
|
||||
.style("z-index", 1)
|
||||
.style("font", "11px sans-serif")
|
||||
.style("font", "12px sans-serif")
|
||||
.style("max-height", "50%")
|
||||
.style("max-width", "50%")
|
||||
.style("overflow-y", "scroll")
|
||||
}
|
||||
|
||||
function positionTooltip(x, y) {
|
||||
let tooltipX = x;
|
||||
let tooltipY = y;
|
||||
|
||||
if (x > displayMaxX / 2) {
|
||||
tooltip.style("right", (displayMaxX - tooltipX) + "px");
|
||||
tooltip.style("left", "")
|
||||
} else {
|
||||
tooltip.style("left", tooltipX + "px");
|
||||
tooltip.style("right", "")
|
||||
}
|
||||
|
||||
if (y > displayMaxY / 2) {
|
||||
tooltip.style("bottom", (displayMaxY - tooltipY) + "px");
|
||||
tooltip.style("top", "")
|
||||
} else {
|
||||
// It looks really goofy if you don't. 20px is about the size of the mouse on a 1080 scaled display
|
||||
tooltip.style("top", tooltipY + "px");
|
||||
tooltip.style("bottom", "")
|
||||
}
|
||||
|
||||
if (detailedToolTipIsVisible) {
|
||||
tooltip.style("pointer-events", "auto")
|
||||
} else {
|
||||
tooltip.style("pointer-events", "none")
|
||||
}
|
||||
}
|
||||
|
||||
function showDetailedTooltip(d, event, parent) {
|
||||
let tooltipX = event.pageX;
|
||||
let tooltipY = event.pageY;
|
||||
if (detailedToolTipIsVisible) {
|
||||
let resourceBarHtml = getResourceBarContent(
|
||||
{
|
||||
title: d.text,
|
||||
kind: d.kind,
|
||||
namespace: d.namespace,
|
||||
time: theTime
|
||||
}
|
||||
);
|
||||
tooltip.html(resourceBarHtml);
|
||||
positionTooltip(tooltipX, tooltipY);
|
||||
detailedToolTipIsVisible = false
|
||||
} else {
|
||||
let [x, y] = d3.mouse(parent);
|
||||
|
||||
let tooltipX = event.pageX;
|
||||
let tooltipY = event.pageY;
|
||||
|
||||
$.ajax({
|
||||
url: "/resource",
|
||||
data: {
|
||||
click_time: xAxisScale.invert(x).getTime(),
|
||||
name: d.text,
|
||||
namespace: d.namespace,
|
||||
kind: d.kind,
|
||||
},
|
||||
success: function (result) {
|
||||
detailedToolTipIsVisible = true;
|
||||
tooltip.html(result);
|
||||
evalJSFromHtml(result);
|
||||
positionTooltip(tooltipX, tooltipY)
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
Загрузка…
Ссылка в новой задаче