mirror of
https://github.com/ail-project/ail-framework.git
synced 2024-11-26 07:47:17 +00:00
chg: [user-account] show chord diagram, Numbers of Messages Posted by Chat
This commit is contained in:
parent
38da8054ef
commit
33bf42538f
7 changed files with 325 additions and 134 deletions
|
@ -465,6 +465,31 @@ def get_user_account_nb_all_week_messages(user_id, chats, subchannels):
|
|||
nb_day += 1
|
||||
return stats
|
||||
|
||||
def get_user_account_chats_chord(subtype, user_id):
|
||||
nb = {}
|
||||
user_account = UsersAccount.UserAccount(user_id, subtype)
|
||||
for chat_g_id in user_account.get_chats():
|
||||
c_subtype, c_id = chat_g_id.split(':', 1)
|
||||
chat = Chats.Chat(c_id, c_subtype)
|
||||
nb[f'chat:{chat_g_id}'] = len(chat.get_user_messages(user_id))
|
||||
|
||||
user_account_gid = user_account.get_global_id() # # #
|
||||
chord = {'meta': {}, 'data': []}
|
||||
label = get_chat_user_account_label(user_account_gid)
|
||||
if label:
|
||||
chord['meta'][user_account_gid] = label
|
||||
else:
|
||||
chord['meta'][user_account_gid] = user_account_gid
|
||||
|
||||
for chat_g_id in nb:
|
||||
label = get_chat_user_account_label(chat_g_id)
|
||||
if label:
|
||||
chord['meta'][chat_g_id] = label
|
||||
else:
|
||||
chord['meta'][chat_g_id] = chat_g_id
|
||||
chord['data'].append({'source': user_account_gid, 'target': chat_g_id, 'value': nb[chat_g_id]})
|
||||
return chord
|
||||
|
||||
def _get_chat_card_meta_options():
|
||||
return {'created_at', 'icon', 'info', 'nb_participants', 'origin_link', 'subchannels', 'tags_safe', 'threads', 'translation', 'username'}
|
||||
|
||||
|
|
|
@ -297,6 +297,15 @@ def objects_user_account():
|
|||
ail_tags=Tag.get_modal_add_tags(user_account['id'], user_account['type'], user_account['subtype']),
|
||||
translation_languages=languages, translation_target=target)
|
||||
|
||||
@chats_explorer.route("/objects/user-account_chats_chord_json", methods=['GET']) # TODO API
|
||||
@login_required
|
||||
@login_read_only
|
||||
def objects_user_account_chats_chord_json():
|
||||
subtype = request.args.get('subtype')
|
||||
user_id = request.args.get('id')
|
||||
json_graph = chats_viewer.get_user_account_chats_chord(subtype, user_id)
|
||||
return jsonify(json_graph)
|
||||
|
||||
@chats_explorer.route("/objects/user-account/chat", methods=['GET'])
|
||||
@login_required
|
||||
@login_read_only
|
||||
|
|
127
var/www/static/js/d3/chord_directed_diagram.js
vendored
Normal file
127
var/www/static/js/d3/chord_directed_diagram.js
vendored
Normal file
|
@ -0,0 +1,127 @@
|
|||
//Requirement: - D3v7
|
||||
// - jquery
|
||||
|
||||
// container_id = #container_id{"meta": {obj_gid: label, ...}, "data": [{"source": source, "target": target, "value": value}, ...]}
|
||||
// tooltip = d3 tooltip object
|
||||
|
||||
// TODO: - Mouseover object
|
||||
|
||||
const create_directed_chord_diagram = (container_id, data, fct_mouseover, fct_mouseout, options) => {
|
||||
|
||||
function getMaxCharsToShow(angle, radius) {
|
||||
const approximateCharWidth = 7; // Approximate width of a character in pixels
|
||||
const arcLength = angle * radius; // Length of the arc
|
||||
return Math.floor(arcLength / approximateCharWidth); // Maximum number of characters that can fit
|
||||
}
|
||||
|
||||
const width = 900;
|
||||
const height = width;
|
||||
const innerRadius = Math.min(width, height) * 0.5 - 20;
|
||||
const outerRadius = innerRadius + 6;
|
||||
|
||||
const labels_meta = data.meta
|
||||
data = data.data
|
||||
|
||||
// Compute a dense matrix from the weighted links in data.
|
||||
var names = Array.from(d3.union(data.flatMap(d => [d.source, d.target])));
|
||||
const index = new Map(names.map((name, i) => [name, i]));
|
||||
const matrix = Array.from(index, () => new Array(names.length).fill(0));
|
||||
for (const {source, target, value} of data) matrix[index.get(source)][index.get(target)] += value;
|
||||
|
||||
const chord = d3.chordDirected()
|
||||
.padAngle(12 / innerRadius)
|
||||
.sortSubgroups(d3.descending)
|
||||
.sortChords(d3.descending);
|
||||
|
||||
const arc = d3.arc()
|
||||
.innerRadius(innerRadius)
|
||||
.outerRadius(outerRadius);
|
||||
|
||||
const ribbon = d3.ribbonArrow()
|
||||
.radius(innerRadius - 0.5)
|
||||
.padAngle(1 / innerRadius);
|
||||
|
||||
const colors = d3.schemeCategory10;
|
||||
|
||||
const svg = d3.select(container_id)
|
||||
.append("svg")
|
||||
.attr("width", width)
|
||||
.attr("height", height)
|
||||
.attr("viewBox", [-width / 2, -height / 2, width, height])
|
||||
.attr("style", "width: 100%; height: auto; font: 10px sans-serif;");
|
||||
|
||||
const chords = chord(matrix);
|
||||
|
||||
const textId = `text-${Math.random().toString(36).substring(2, 15)}`;
|
||||
|
||||
svg.append("path")
|
||||
.attr("id", textId)
|
||||
.attr("fill", "none")
|
||||
.attr("d", d3.arc()({outerRadius, startAngle: 0, endAngle: 2 * Math.PI}));
|
||||
|
||||
svg.append("g")
|
||||
.attr("fill-opacity", 0.75)
|
||||
.selectAll("path")
|
||||
.data(chords)
|
||||
.enter()
|
||||
.append("path")
|
||||
.attr("d", ribbon)
|
||||
.attr("fill", d => colors[d.target.index])
|
||||
.style("mix-blend-mode", "multiply")
|
||||
.append("title")
|
||||
.text(d => `${labels_meta[names[d.source.index]]}
|
||||
=> ${d.source.value} Messages
|
||||
${labels_meta[names[d.target.index]]}`);
|
||||
|
||||
const g = svg.append("g")
|
||||
.selectAll("g")
|
||||
.data(chords.groups)
|
||||
.enter()
|
||||
.append("g");
|
||||
|
||||
g.append("path")
|
||||
.attr("d", arc)
|
||||
.attr("fill", d => colors[d.index])
|
||||
.attr("stroke", "#fff")
|
||||
.on("mouseover", function(event, d) {
|
||||
fct_mouseover(event, d, names[d.index], labels_meta[names[d.index]]);
|
||||
})
|
||||
.on("mouseout", fct_mouseout);
|
||||
|
||||
g.each(function(d) {
|
||||
const group = d3.select(this);
|
||||
const angle = d.endAngle - d.startAngle;
|
||||
const text = labels_meta[names[d.index]];
|
||||
const maxCharsToShow = getMaxCharsToShow(angle, outerRadius);
|
||||
|
||||
let displayedText
|
||||
if (maxCharsToShow <= 1) {
|
||||
displayedText = text[0];
|
||||
} else {
|
||||
displayedText = text.length > maxCharsToShow ? text.slice(0, maxCharsToShow - 1) + "…" : text;
|
||||
}
|
||||
|
||||
let info_text = "OUT: " + d3.sum(matrix[d.index]) + " ; IN: " + d3.sum(matrix, row => row[d.index]) + " Messages"
|
||||
|
||||
if (displayedText) {
|
||||
group.append("text")
|
||||
.attr("dy", -3)
|
||||
.append("textPath")
|
||||
.attr("xlink:href", `#${textId}`)
|
||||
.attr("startOffset", d.startAngle * outerRadius)
|
||||
.on("mouseover", function(event, d) {
|
||||
fct_mouseover(event, d, names[d.index], labels_meta[names[d.index]], info_text);
|
||||
})
|
||||
.on("mouseout", fct_mouseout)
|
||||
.text(displayedText);
|
||||
}
|
||||
});
|
||||
|
||||
//return svg.node();
|
||||
}
|
||||
|
||||
|
||||
|
||||
// return svg ???
|
||||
// return svg
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
// sanitise str_to_sanitize
|
||||
function sanitize_text(str_to_sanitize) {
|
||||
return $("<span\>").text(str_to_sanitize).html()
|
||||
};
|
||||
}
|
||||
|
|
|
@ -12,12 +12,14 @@
|
|||
|
||||
<!-- JS -->
|
||||
<script src="{{ url_for('static', filename='js/jquery.js')}}"></script>
|
||||
<script src="{{ url_for('static', filename='js/helper.js')}}"></script>
|
||||
<script src="{{ url_for('static', filename='js/popper.min.js')}}"></script>
|
||||
<script src="{{ url_for('static', filename='js/bootstrap4.min.js')}}"></script>
|
||||
<script src="{{ url_for('static', filename='js/jquery.dataTables.min.js')}}"></script>
|
||||
<script src="{{ url_for('static', filename='js/dataTables.bootstrap.min.js')}}"></script>
|
||||
<script src="{{ url_for('static', filename='js/d3.min.js')}}"></script>
|
||||
<script src="{{ url_for('static', filename='js/d3.v7.min.js') }}"></script>
|
||||
<script src="{{ url_for('static', filename='js/d3/heatmap_week_hour.js')}}"></script>
|
||||
<script src="{{ url_for('static', filename='js/d3/chord_directed_diagram.js')}}"></script>
|
||||
|
||||
|
||||
</head>
|
||||
|
@ -34,6 +36,9 @@
|
|||
|
||||
{% include 'chats_explorer/card_user_account.html' %}
|
||||
|
||||
<span class="mt-3">
|
||||
{% include 'objects/image/block_blur_img_slider.html' %}
|
||||
</span>
|
||||
{% with translate_url=url_for('chats_explorer.objects_user_account', subtype=meta['subtype']), obj_id=meta['id'] %}
|
||||
{% include 'chats_explorer/block_translation.html' %}
|
||||
{% endwith %}
|
||||
|
@ -42,6 +47,9 @@
|
|||
<h4 class="mx-5 mt-2 text-secondary">User All Messages:</h4>
|
||||
<div id="heatmapweekhourall"></div>
|
||||
|
||||
<h4>Numbers of Messages Posted by Chat:</h4>
|
||||
<div id="chord_user_chats" style="max-width: 900px"></div>
|
||||
|
||||
<h4>User Chats:</h4>
|
||||
{% for meta_chats in meta['chats'] %}
|
||||
<div class="my-2">
|
||||
|
@ -58,6 +66,8 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
{% include 'objects/tooltip_ail_objects.html' %}
|
||||
<script>
|
||||
$(document).ready(function(){
|
||||
$("#page-Decoded").addClass("active");
|
||||
|
@ -77,26 +87,15 @@ d3.json("{{ url_for('chats_explorer.user_account_messages_stats_week_all') }}?su
|
|||
create_heatmap_week_hour('#heatmapweekhourall', data);
|
||||
})
|
||||
|
||||
function toggle_sidebar(){
|
||||
if($('#nav_menu').is(':visible')){
|
||||
$('#nav_menu').hide();
|
||||
$('#side_menu').removeClass('border-right')
|
||||
$('#side_menu').removeClass('col-lg-2')
|
||||
$('#core_content').removeClass('col-lg-10')
|
||||
}else{
|
||||
$('#nav_menu').show();
|
||||
$('#side_menu').addClass('border-right')
|
||||
$('#side_menu').addClass('col-lg-2')
|
||||
$('#core_content').addClass('col-lg-10')
|
||||
}
|
||||
}
|
||||
let url = "{{ url_for('chats_explorer.objects_user_account_chats_chord_json') }}?subtype={{ meta["subtype"] }}&id={{ meta["id"] }}"
|
||||
d3.json(url).then(function(data) {
|
||||
create_directed_chord_diagram('#chord_user_chats', data, mouseover_tooltip_ail_obj, mouseout_tooltip_ail_obj);
|
||||
});
|
||||
|
||||
|
||||
</script>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</body>
|
||||
|
||||
</html>
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
<script src="{{ url_for('static', filename='js/popper.min.js')}}"></script>
|
||||
<script src="{{ url_for('static', filename='js/bootstrap4.min.js')}}"></script>
|
||||
<script src="{{ url_for('static', filename='js/d3.v7.min.js') }}"></script>
|
||||
<script src="{{ url_for('static', filename='js/d3/chord_directed_diagram.js')}}"></script>
|
||||
|
||||
<style>
|
||||
.icon_legend {
|
||||
|
@ -128,6 +129,10 @@
|
|||
{% include 'correlation/metadata_card_item.html' %}
|
||||
{% endif %}
|
||||
|
||||
<div class="my-2">
|
||||
{% include 'objects/image/block_blur_img_slider.html' %}
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-xl-10">
|
||||
|
||||
|
@ -220,7 +225,7 @@
|
|||
</div>
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="checkbox" value="True" id="relationship_in_Check" name="relationship_in_Check" {%if "in" in dict_object["relationships"]%}checked{%endif%}>
|
||||
<label class="form-check-label" for="relationship_in_Check">Forward</label>
|
||||
<label class="form-check-label" for="relationship_in_Check">In</label>
|
||||
</div>
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="checkbox" value="True" id="relationship_mention_Check" name="relationship_mention_Check" {%if "mention" in dict_object["relationships"]%}checked{%endif%}>
|
||||
|
@ -357,6 +362,10 @@
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
{% include 'objects/tooltip_ail_objects.html' %}
|
||||
<script>
|
||||
|
||||
var all_graph = {};
|
||||
|
@ -369,8 +378,7 @@ $(document).ready(function(){
|
|||
|
||||
let url = "{{ url_for('correlation.relationships_chord_graph_json') }}?id={{ dict_object["correlation_id"] }}&type={{ dict_object["object_type"] }}{% if 'type_id' in dict_object["metadata"] %}&subtype={{ dict_object["metadata"]["type_id"] }}{% endif %}"
|
||||
d3.json(url).then(function(data) {
|
||||
const diagram = createDirectedChordDiagram(data);
|
||||
document.getElementById('chart_test').appendChild(diagram);
|
||||
create_directed_chord_diagram('#chart_test', data, mouseover_tooltip_ail_obj, mouseout_tooltip_ail_obj);
|
||||
});
|
||||
|
||||
|
||||
|
@ -796,119 +804,6 @@ all_graph.onResize = function () {
|
|||
|
||||
window.all_graph = all_graph;
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
function getMaxCharsToShow(angle, radius) {
|
||||
const approximateCharWidth = 7; // Approximate width of a character in pixels
|
||||
const arcLength = angle * radius; // Length of the arc
|
||||
return Math.floor(arcLength / approximateCharWidth); // Maximum number of characters that can fit
|
||||
}
|
||||
|
||||
function createDirectedChordDiagram(data) {
|
||||
const width = 900;
|
||||
const height = width;
|
||||
const innerRadius = Math.min(width, height) * 0.5 - 20;
|
||||
const outerRadius = innerRadius + 6;
|
||||
|
||||
const labels_meta = data.meta
|
||||
data = data.data
|
||||
|
||||
// Compute a dense matrix from the weighted links in data.
|
||||
var names = Array.from(d3.union(data.flatMap(d => [d.source, d.target])));
|
||||
const index = new Map(names.map((name, i) => [name, i]));
|
||||
const matrix = Array.from(index, () => new Array(names.length).fill(0));
|
||||
for (const {source, target, value} of data) matrix[index.get(source)][index.get(target)] += value;
|
||||
|
||||
const chord = d3.chordDirected()
|
||||
.padAngle(12 / innerRadius)
|
||||
.sortSubgroups(d3.descending)
|
||||
.sortChords(d3.descending);
|
||||
|
||||
const arc = d3.arc()
|
||||
.innerRadius(innerRadius)
|
||||
.outerRadius(outerRadius);
|
||||
|
||||
const ribbon = d3.ribbonArrow()
|
||||
.radius(innerRadius - 0.5)
|
||||
.padAngle(1 / innerRadius);
|
||||
|
||||
const colors = d3.schemeCategory10;
|
||||
|
||||
const svg = d3.create("svg")
|
||||
.attr("width", width)
|
||||
.attr("height", height)
|
||||
.attr("viewBox", [-width / 2, -height / 2, width, height])
|
||||
.attr("style", "width: 100%; height: auto; font: 10px sans-serif;");
|
||||
|
||||
const chords = chord(matrix);
|
||||
|
||||
const textId = `text-${Math.random().toString(36).substring(2, 15)}`;
|
||||
|
||||
svg.append("path")
|
||||
.attr("id", textId)
|
||||
.attr("fill", "none")
|
||||
.attr("d", d3.arc()({outerRadius, startAngle: 0, endAngle: 2 * Math.PI}));
|
||||
|
||||
svg.append("g")
|
||||
.attr("fill-opacity", 0.75)
|
||||
.selectAll("path")
|
||||
.data(chords)
|
||||
.enter()
|
||||
.append("path")
|
||||
.attr("d", ribbon)
|
||||
.attr("fill", d => colors[d.target.index])
|
||||
.style("mix-blend-mode", "multiply")
|
||||
.append("title")
|
||||
.text(d => `${labels_meta[names[d.target.index]]}
|
||||
Forwarded ${d.source.value} Messages From
|
||||
${labels_meta[names[d.source.index]]}`);
|
||||
|
||||
const g = svg.append("g")
|
||||
.selectAll("g")
|
||||
.data(chords.groups)
|
||||
.enter()
|
||||
.append("g");
|
||||
|
||||
g.append("path")
|
||||
.attr("d", arc)
|
||||
.attr("fill", d => colors[d.index])
|
||||
.attr("stroke", "#fff")
|
||||
.on("mouseover", function(event, d) {
|
||||
mouseovered(event, d, names[d.index], labels_meta[names[d.index]]);
|
||||
})
|
||||
.on("mouseout", mouseouted);
|
||||
|
||||
g.each(function(d) {
|
||||
const group = d3.select(this);
|
||||
const angle = d.endAngle - d.startAngle;
|
||||
const text = labels_meta[names[d.index]];
|
||||
const maxCharsToShow = getMaxCharsToShow(angle, outerRadius);
|
||||
|
||||
let displayedText
|
||||
if (maxCharsToShow <= 1) {
|
||||
displayedText = text[0];
|
||||
} else {
|
||||
displayedText = text.length > maxCharsToShow ? text.slice(0, maxCharsToShow - 1) + "…" : text;
|
||||
}
|
||||
|
||||
let info_text = "OUT: " + d3.sum(matrix[d.index]) + " ; IN: " + d3.sum(matrix, row => row[d.index]) + " Messages"
|
||||
|
||||
if (displayedText) {
|
||||
group.append("text")
|
||||
.attr("dy", -3)
|
||||
.append("textPath")
|
||||
.attr("xlink:href", `#${textId}`)
|
||||
.attr("startOffset", d.startAngle * outerRadius)
|
||||
.on("mouseover", function(event, d) {
|
||||
mouseovered(event, d, names[d.index], labels_meta[names[d.index]], info_text);
|
||||
})
|
||||
.on("mouseout", mouseouted)
|
||||
.text(displayedText);
|
||||
}
|
||||
});
|
||||
|
||||
return svg.node();
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
|
136
var/www/templates/objects/tooltip_ail_objects.html
Normal file
136
var/www/templates/objects/tooltip_ail_objects.html
Normal file
|
@ -0,0 +1,136 @@
|
|||
<script>
|
||||
let tooltip_ail_obj = d3.select("body").append("div")
|
||||
//.attr("class", "tooltip_graph")
|
||||
.attr("id", "tooltip_graph")
|
||||
.style("opacity", 0)
|
||||
.style("position", "absolute")
|
||||
.style("text-align", "center")
|
||||
.style("padding", "2px")
|
||||
.style("font", "12px sans-serif")
|
||||
.style("background", "#ebf4fb")
|
||||
.style("border", "2px solid #b7ddf2")
|
||||
.style("border-radius", "8px")
|
||||
.style("pointer-events", "none")
|
||||
.style("color", "#000000");
|
||||
|
||||
function mouseover_tooltip_ail_obj(event, d, obj_gid, obj_label, additional_text) { /// div var/const tooltip tooltip_ail_obj
|
||||
|
||||
let d3_pageX = event.pageX;
|
||||
let d3_pageY = event.pageY;
|
||||
|
||||
if (d.popover) {
|
||||
tooltip_ail_obj.html(d.popover)
|
||||
.style("left", (d3_pageX) + "px")
|
||||
.style("top", (d3_pageY - 28) + "px");
|
||||
|
||||
tooltip_ail_obj.transition()
|
||||
.duration(200)
|
||||
.style("opacity", 1);
|
||||
blur_images();
|
||||
} else {
|
||||
|
||||
let pop_header = "<div class=\"card text-white\" style=\"max-width: 25rem;\"><div class=\"card-header bg-dark pb-0 border-white\"><h6>"
|
||||
if (obj_label) {
|
||||
pop_header = pop_header + sanitize_text(obj_label)
|
||||
} else if (obj_gid) {
|
||||
pop_header = pop_header + sanitize_text(obj_gid)
|
||||
} else {
|
||||
pop_header = pop_header + sanitize_text(d.text)
|
||||
}
|
||||
pop_header = pop_header + "</h6></div>"
|
||||
let spinner = "<div class=\"card-body bg-dark pt-0\"><div class=\"spinner-border text-warning\" role=\"status\"></div> Loading...</div>"
|
||||
|
||||
tooltip_ail_obj.html(pop_header + spinner)
|
||||
.style("left", (d3_pageX) + "px")
|
||||
.style("top", (d3_pageY - 28) + "px");
|
||||
|
||||
tooltip_ail_obj.transition()
|
||||
.duration(200)
|
||||
.style("opacity", 1);
|
||||
|
||||
let description_url = "{{ url_for('correlation.get_description') }}?object_id="
|
||||
if (obj_gid) {
|
||||
description_url = description_url + obj_gid
|
||||
} else {
|
||||
description_url = description_url + d.id
|
||||
}
|
||||
|
||||
let desc
|
||||
$.getJSON(description_url, function(data){
|
||||
desc = pop_header + "<div class=\"card-body bg-dark pb-1 pt-2\"><dl class=\"row py-0 my-0\">"
|
||||
Object.keys(data).forEach(function(key) {
|
||||
if (key==="status") {
|
||||
desc = desc + "<dt class=\"col-sm-3 px-0\">status</dt><dd class=\"col-sm-9 px-0\"><div class=\"badge badge-pill badge-light flex-row-reverse\" style=\"color:"
|
||||
if (data["status"]) {
|
||||
desc = desc + "Green"
|
||||
} else {
|
||||
desc = desc + "Red"
|
||||
}
|
||||
desc = desc + ";\"><i class=\"fas "
|
||||
if (data["status"]) {
|
||||
desc = desc + "fa-check-circle\"></i>UP"
|
||||
} else {
|
||||
desc = desc + "fa-times-circle\"></i>DOWN"
|
||||
}
|
||||
desc = desc + "</div></dd>"
|
||||
} else if (key!=="tags" && key!=="id" && key!="img" && key!=="icon" && key!=="svg_icon" && key!=="link" && key!=="type" && key!=="tags_safe") {
|
||||
if (data[key]) {
|
||||
if ((key==="first_seen" || key==="last_seen") && data[key].length===8) {
|
||||
let date = sanitize_text(data[key])
|
||||
desc = desc + "<dt class=\"col-sm-3 px-0\">" + sanitize_text(key) + "</dt><dd class=\"col-sm-9 px-0\">" + date.slice(0, 4) + "-" + date.slice(4, 6) + "-" + date.slice(6, 8) + "</dd>"
|
||||
} else {
|
||||
desc = desc + "<dt class=\"col-sm-3 px-0\">" + sanitize_text(key) + "</dt><dd class=\"col-sm-9 px-0\">" + sanitize_text(data[key]) + "</dd>"
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
desc = desc + "</dl>"
|
||||
|
||||
if (data["tags"]) {
|
||||
data["tags"].forEach(function(tag) {
|
||||
desc = desc + "<span class=\"badge badge-warning\">"+ sanitize_text(tag) +"</span>";
|
||||
});
|
||||
}
|
||||
|
||||
if (data["img"]) {
|
||||
if (data["tags_safe"]) {
|
||||
if (data["type"] === "screenshot") {
|
||||
desc = desc + "<img src={{ url_for('objects_item.screenshot', filename="") }}"
|
||||
} else {
|
||||
desc = desc + "<img src={{ url_for('objects_image.image', filename="") }}"
|
||||
}
|
||||
desc = desc + data["img"] +" class=\"object_image\" id=\"tooltip_screenshot_correlation\" style=\"filter: blur(5px);max-width: 200px;\"/>";
|
||||
} else {
|
||||
desc = desc + "<span class=\"my-2 fa-stack fa-4x\"><i class=\"fas fa-stack-1x fa-image\"></i><i class=\"fas fa-stack-2x fa-ban\" style=\"color:Red\"></i></span>";
|
||||
}
|
||||
}
|
||||
if (additional_text) {
|
||||
desc = desc + "<hr><div>" + sanitize_text(additional_text) + "</div>"
|
||||
}
|
||||
|
||||
desc = desc + "</div></div>"
|
||||
tooltip_ail_obj.html(desc)
|
||||
.style("left", (d3_pageX) + "px")
|
||||
.style("top", (d3_pageY - 28) + "px");
|
||||
d.popover = desc
|
||||
|
||||
if (data["img"]) {
|
||||
blur_images();
|
||||
}
|
||||
|
||||
})
|
||||
.fail(function(error) {
|
||||
desc = pop_header + "<div class=\"card-body bg-dark pt-0\"><i class=\"fas fa-3x fa-times text-danger\"></i>"+ error.statusText +"</div>"
|
||||
tooltip_ail_obj.html(desc)
|
||||
.style("left", (d3_pageX) + "px")
|
||||
.style("top", (d3_pageY - 28) + "px");
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function mouseout_tooltip_ail_obj(event, d) {
|
||||
tooltip_ail_obj.transition()
|
||||
.duration(500)
|
||||
.style("opacity", 0);
|
||||
}
|
||||
</script>
|
Loading…
Reference in a new issue